@gzl10/ts-helpers 4.2.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/CHANGELOG.md +320 -0
- package/README.md +233 -0
- package/USAGE-GUIDE.md +800 -0
- package/dist/browser/async.js +15 -0
- package/dist/browser/async.js.map +1 -0
- package/dist/browser/chunk-4O7ZPIJN.js +383 -0
- package/dist/browser/chunk-4O7ZPIJN.js.map +1 -0
- package/dist/browser/chunk-75XNTC34.js +60 -0
- package/dist/browser/chunk-75XNTC34.js.map +1 -0
- package/dist/browser/chunk-C3D7YZVE.js +299 -0
- package/dist/browser/chunk-C3D7YZVE.js.map +1 -0
- package/dist/browser/chunk-CZL6C2EI.js +452 -0
- package/dist/browser/chunk-CZL6C2EI.js.map +1 -0
- package/dist/browser/chunk-D4FZFIVA.js +240 -0
- package/dist/browser/chunk-D4FZFIVA.js.map +1 -0
- package/dist/browser/chunk-IL7NG7IC.js +72 -0
- package/dist/browser/chunk-IL7NG7IC.js.map +1 -0
- package/dist/browser/chunk-NSBPE2FW.js +17 -0
- package/dist/browser/chunk-NSBPE2FW.js.map +1 -0
- package/dist/browser/chunk-SLQVNPTH.js +27 -0
- package/dist/browser/chunk-SLQVNPTH.js.map +1 -0
- package/dist/browser/chunk-WG7ILCUB.js +195 -0
- package/dist/browser/chunk-WG7ILCUB.js.map +1 -0
- package/dist/browser/chunk-WJA4JDMZ.js +278 -0
- package/dist/browser/chunk-WJA4JDMZ.js.map +1 -0
- package/dist/browser/chunk-ZFVYLUTT.js +65 -0
- package/dist/browser/chunk-ZFVYLUTT.js.map +1 -0
- package/dist/browser/chunk-ZYTSVMTI.js +263 -0
- package/dist/browser/chunk-ZYTSVMTI.js.map +1 -0
- package/dist/browser/dates.js +78 -0
- package/dist/browser/dates.js.map +1 -0
- package/dist/browser/environment-detection.js +21 -0
- package/dist/browser/environment-detection.js.map +1 -0
- package/dist/browser/environment.js +34 -0
- package/dist/browser/environment.js.map +1 -0
- package/dist/browser/errors.js +18 -0
- package/dist/browser/errors.js.map +1 -0
- package/dist/browser/index.js +412 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/math.js +51 -0
- package/dist/browser/math.js.map +1 -0
- package/dist/browser/number.js +10 -0
- package/dist/browser/number.js.map +1 -0
- package/dist/browser/objects.js +31 -0
- package/dist/browser/objects.js.map +1 -0
- package/dist/browser/strings.js +80 -0
- package/dist/browser/strings.js.map +1 -0
- package/dist/browser/validation-core.js +54 -0
- package/dist/browser/validation-core.js.map +1 -0
- package/dist/browser/validation-crypto.js +28 -0
- package/dist/browser/validation-crypto.js.map +1 -0
- package/dist/browser/validators.js +98 -0
- package/dist/browser/validators.js.map +1 -0
- package/dist/cjs/async.js +86 -0
- package/dist/cjs/async.js.map +1 -0
- package/dist/cjs/dates.js +285 -0
- package/dist/cjs/dates.js.map +1 -0
- package/dist/cjs/environment-detection.js +84 -0
- package/dist/cjs/environment-detection.js.map +1 -0
- package/dist/cjs/environment.js +261 -0
- package/dist/cjs/environment.js.map +1 -0
- package/dist/cjs/errors.js +80 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.js +2035 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/math.js +388 -0
- package/dist/cjs/math.js.map +1 -0
- package/dist/cjs/number.js +37 -0
- package/dist/cjs/number.js.map +1 -0
- package/dist/cjs/objects.js +249 -0
- package/dist/cjs/objects.js.map +1 -0
- package/dist/cjs/strings.js +253 -0
- package/dist/cjs/strings.js.map +1 -0
- package/dist/cjs/validation.js +450 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/async.js +15 -0
- package/dist/esm/async.js.map +1 -0
- package/dist/esm/chunk-4O7ZPIJN.js +383 -0
- package/dist/esm/chunk-4O7ZPIJN.js.map +1 -0
- package/dist/esm/chunk-75XNTC34.js +60 -0
- package/dist/esm/chunk-75XNTC34.js.map +1 -0
- package/dist/esm/chunk-BDOBKBKA.js +72 -0
- package/dist/esm/chunk-BDOBKBKA.js.map +1 -0
- package/dist/esm/chunk-C3D7YZVE.js +299 -0
- package/dist/esm/chunk-C3D7YZVE.js.map +1 -0
- package/dist/esm/chunk-CZL6C2EI.js +452 -0
- package/dist/esm/chunk-CZL6C2EI.js.map +1 -0
- package/dist/esm/chunk-EBLSTOEC.js +263 -0
- package/dist/esm/chunk-EBLSTOEC.js.map +1 -0
- package/dist/esm/chunk-NSBPE2FW.js +17 -0
- package/dist/esm/chunk-NSBPE2FW.js.map +1 -0
- package/dist/esm/chunk-SLQVNPTH.js +27 -0
- package/dist/esm/chunk-SLQVNPTH.js.map +1 -0
- package/dist/esm/chunk-WG7ILCUB.js +195 -0
- package/dist/esm/chunk-WG7ILCUB.js.map +1 -0
- package/dist/esm/chunk-WJA4JDMZ.js +278 -0
- package/dist/esm/chunk-WJA4JDMZ.js.map +1 -0
- package/dist/esm/chunk-ZFVYLUTT.js +65 -0
- package/dist/esm/chunk-ZFVYLUTT.js.map +1 -0
- package/dist/esm/dates.js +78 -0
- package/dist/esm/dates.js.map +1 -0
- package/dist/esm/environment-detection.js +21 -0
- package/dist/esm/environment-detection.js.map +1 -0
- package/dist/esm/environment.js +34 -0
- package/dist/esm/environment.js.map +1 -0
- package/dist/esm/errors.js +18 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.js +380 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/math.js +51 -0
- package/dist/esm/math.js.map +1 -0
- package/dist/esm/number.js +10 -0
- package/dist/esm/number.js.map +1 -0
- package/dist/esm/objects.js +31 -0
- package/dist/esm/objects.js.map +1 -0
- package/dist/esm/strings.js +80 -0
- package/dist/esm/strings.js.map +1 -0
- package/dist/esm/validation.js +54 -0
- package/dist/esm/validation.js.map +1 -0
- package/dist/node/async.js +93 -0
- package/dist/node/async.js.map +1 -0
- package/dist/node/csv.js +102 -0
- package/dist/node/csv.js.map +1 -0
- package/dist/node/data.js +880 -0
- package/dist/node/data.js.map +1 -0
- package/dist/node/dates.js +324 -0
- package/dist/node/dates.js.map +1 -0
- package/dist/node/environment.js +278 -0
- package/dist/node/environment.js.map +1 -0
- package/dist/node/errors.js +89 -0
- package/dist/node/errors.js.map +1 -0
- package/dist/node/index.js +3151 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/json.js +107 -0
- package/dist/node/json.js.map +1 -0
- package/dist/node/math.js +413 -0
- package/dist/node/math.js.map +1 -0
- package/dist/node/number.js +42 -0
- package/dist/node/number.js.map +1 -0
- package/dist/node/objects.js +264 -0
- package/dist/node/objects.js.map +1 -0
- package/dist/node/strings.js +293 -0
- package/dist/node/strings.js.map +1 -0
- package/dist/node/tree.js +89 -0
- package/dist/node/tree.js.map +1 -0
- package/dist/node/validation-core.js +477 -0
- package/dist/node/validation-core.js.map +1 -0
- package/dist/node/validation-crypto.js +179 -0
- package/dist/node/validation-crypto.js.map +1 -0
- package/dist/node/validation.js +677 -0
- package/dist/node/validation.js.map +1 -0
- package/dist/node/validators.js +123 -0
- package/dist/node/validators.js.map +1 -0
- package/dist/node-esm/async.js +15 -0
- package/dist/node-esm/async.js.map +1 -0
- package/dist/node-esm/chunk-3YOF7NPT.js +299 -0
- package/dist/node-esm/chunk-3YOF7NPT.js.map +1 -0
- package/dist/node-esm/chunk-64TBXJQS.js +263 -0
- package/dist/node-esm/chunk-64TBXJQS.js.map +1 -0
- package/dist/node-esm/chunk-75XNTC34.js +60 -0
- package/dist/node-esm/chunk-75XNTC34.js.map +1 -0
- package/dist/node-esm/chunk-C4PKXIPB.js +278 -0
- package/dist/node-esm/chunk-C4PKXIPB.js.map +1 -0
- package/dist/node-esm/chunk-CMDFZME3.js +452 -0
- package/dist/node-esm/chunk-CMDFZME3.js.map +1 -0
- package/dist/node-esm/chunk-DZZPUYMP.js +74 -0
- package/dist/node-esm/chunk-DZZPUYMP.js.map +1 -0
- package/dist/node-esm/chunk-HTSEHRHI.js +195 -0
- package/dist/node-esm/chunk-HTSEHRHI.js.map +1 -0
- package/dist/node-esm/chunk-JCAUVOPH.js +27 -0
- package/dist/node-esm/chunk-JCAUVOPH.js.map +1 -0
- package/dist/node-esm/chunk-KBHE3K2F.js +505 -0
- package/dist/node-esm/chunk-KBHE3K2F.js.map +1 -0
- package/dist/node-esm/chunk-LYTET5NX.js +65 -0
- package/dist/node-esm/chunk-LYTET5NX.js.map +1 -0
- package/dist/node-esm/chunk-PZ5AY32C.js +10 -0
- package/dist/node-esm/chunk-PZ5AY32C.js.map +1 -0
- package/dist/node-esm/chunk-UKGXL2QO.js +383 -0
- package/dist/node-esm/chunk-UKGXL2QO.js.map +1 -0
- package/dist/node-esm/chunk-XAEYT23H.js +164 -0
- package/dist/node-esm/chunk-XAEYT23H.js.map +1 -0
- package/dist/node-esm/csv.js +63 -0
- package/dist/node-esm/csv.js.map +1 -0
- package/dist/node-esm/data.js +32 -0
- package/dist/node-esm/data.js.map +1 -0
- package/dist/node-esm/dates.js +78 -0
- package/dist/node-esm/dates.js.map +1 -0
- package/dist/node-esm/environment.js +34 -0
- package/dist/node-esm/environment.js.map +1 -0
- package/dist/node-esm/errors.js +18 -0
- package/dist/node-esm/errors.js.map +1 -0
- package/dist/node-esm/index.js +426 -0
- package/dist/node-esm/index.js.map +1 -0
- package/dist/node-esm/json.js +68 -0
- package/dist/node-esm/json.js.map +1 -0
- package/dist/node-esm/math.js +51 -0
- package/dist/node-esm/math.js.map +1 -0
- package/dist/node-esm/number.js +10 -0
- package/dist/node-esm/number.js.map +1 -0
- package/dist/node-esm/objects.js +31 -0
- package/dist/node-esm/objects.js.map +1 -0
- package/dist/node-esm/strings.js +80 -0
- package/dist/node-esm/strings.js.map +1 -0
- package/dist/node-esm/tree.js +8 -0
- package/dist/node-esm/tree.js.map +1 -0
- package/dist/node-esm/validation-core.js +54 -0
- package/dist/node-esm/validation-core.js.map +1 -0
- package/dist/node-esm/validation-crypto.js +26 -0
- package/dist/node-esm/validation-crypto.js.map +1 -0
- package/dist/node-esm/validation.js +606 -0
- package/dist/node-esm/validation.js.map +1 -0
- package/dist/node-esm/validators.js +98 -0
- package/dist/node-esm/validators.js.map +1 -0
- package/dist/types/async-C8gvbSG-.d.ts +453 -0
- package/dist/types/async.d.ts +1 -0
- package/dist/types/csv.d.ts +226 -0
- package/dist/types/data.d.ts +1561 -0
- package/dist/types/dates-hTiE0Z11.d.ts +298 -0
- package/dist/types/dates.d.ts +1 -0
- package/dist/types/environment-B8eLS7KT.d.ts +420 -0
- package/dist/types/environment-detection.d.ts +102 -0
- package/dist/types/environment.d.ts +1 -0
- package/dist/types/errors.d.ts +147 -0
- package/dist/types/index.d.ts +211 -0
- package/dist/types/json.d.ts +284 -0
- package/dist/types/math-BQ9Lwdp7.d.ts +2060 -0
- package/dist/types/math.d.ts +1 -0
- package/dist/types/number-CYnQfLWj.d.ts +44 -0
- package/dist/types/number.d.ts +1 -0
- package/dist/types/objects-BohS8GCS.d.ts +1185 -0
- package/dist/types/objects.d.ts +1 -0
- package/dist/types/strings-CiqRPYLL.d.ts +1349 -0
- package/dist/types/strings.d.ts +1 -0
- package/dist/types/tree.d.ts +284 -0
- package/dist/types/validation-core-DfHF8rCG.d.ts +238 -0
- package/dist/types/validation-crypto-browser.d.ts +56 -0
- package/dist/types/validation-crypto-node.d.ts +31 -0
- package/dist/types/validation.d.ts +1 -0
- package/dist/types/validators.d.ts +216 -0
- package/package.json +253 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/objects.ts"],"sourcesContent":["/**\n * Object manipulation and comparison utilities\n * Consolidated from primitives/object and array modules\n */\n\nimport crc32 from 'crc/crc32'\nimport * as lodash from 'lodash'\nconst {\n isArray,\n isArrayBuffer,\n isBoolean,\n isBuffer,\n isEqual,\n isNil,\n isNumber,\n isObject,\n isPlainObject,\n isString,\n isTypedArray,\n transform,\n} = lodash\nimport fastEqual from 'fast-deep-equal'\nimport { areDatesEqualWithTolerance, isDateTime, toDate } from './dates'\n\n// Native implementations to replace lodash functions\nconst nativeFilter = <T>(array: T[], predicate: Partial<T>): T[] => {\n return array.filter(item => {\n return Object.keys(predicate).every(key => item[key as keyof T] === predicate[key as keyof T])\n })\n}\n\nconst nativeAssign = <T extends object>(target: T, source: Partial<T>): T => {\n return Object.assign(target, source)\n}\n\nconst nativePick = <T extends object, K extends keyof T>(object: T, keys: K[]): Pick<T, K> => {\n const result = {} as Pick<T, K>\n keys.forEach(key => {\n if (key in object) {\n result[key] = object[key]\n }\n })\n return result\n}\n\nconst nativeRemove = <T>(array: T[], predicate: (item: T) => boolean): T[] => {\n const removed: T[] = []\n for (let i = array.length - 1; i >= 0; i--) {\n if (predicate(array[i])) {\n removed.unshift(array.splice(i, 1)[0])\n }\n }\n return removed\n}\n\n/**\n * Compares two objects utilizing lodash deep comparison\n * @deprecated Use `deepEqual` instead for consistency\n * @param data1 First object to compare\n * @param data2 Second object to compare\n * @returns True if objects are deeply equal, false otherwise\n */\nexport const comparator = (data1: any, data2: any) => isEqual(data1, data2)\n\n/**\n * Performs fast deep comparison between two values\n *\n * Compares values recursively including nested objects, arrays, dates, RegExp, and primitives.\n * Uses fast-deep-equal library for optimal performance (~5-10x faster than JSON.stringify).\n *\n * Comparison rules:\n * - Primitives: strict equality (===)\n * - Objects: recursive key-value comparison\n * - Arrays: length + element-by-element comparison\n * - Dates: compares timestamps\n * - RegExp: compares source and flags\n * - null/undefined: strict equality\n *\n * Use cases: Testing, change detection, cache invalidation, data validation\n *\n * @param data1 - First value to compare (any type)\n * @param data2 - Second value to compare (any type)\n * @returns True if values are deeply equal, false otherwise\n *\n * @example\n * ```typescript\n * // Primitive values\n * deepEqual(42, 42) // true\n * deepEqual('hello', 'hello') // true\n * deepEqual(null, null) // true\n * deepEqual(42, '42') // false (different types)\n *\n * // Objects\n * deepEqual({ a: 1, b: 2 }, { a: 1, b: 2 }) // true\n * deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }) // true (order doesn't matter)\n * deepEqual({ a: 1 }, { a: 1, b: undefined }) // false\n *\n * // Arrays\n * deepEqual([1, 2, 3], [1, 2, 3]) // true\n * deepEqual([1, 2, 3], [3, 2, 1]) // false (order matters)\n *\n * // Nested structures\n * deepEqual(\n * { user: { name: 'Alice', tags: ['admin'] } },\n * { user: { name: 'Alice', tags: ['admin'] } }\n * ) // true\n *\n * // Dates\n * deepEqual(new Date('2024-01-01'), new Date('2024-01-01')) // true\n * deepEqual(new Date('2024-01-01'), new Date('2024-01-02')) // false\n *\n * // RegExp\n * deepEqual(/test/gi, /test/gi) // true\n * deepEqual(/test/g, /test/i) // false\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Detect if form data changed\n * const originalData = {\n * name: 'John Doe',\n * email: 'john@example.com',\n * preferences: { theme: 'dark', notifications: true }\n * }\n *\n * const currentData = {\n * name: 'John Doe',\n * email: 'john.doe@example.com', // Changed!\n * preferences: { theme: 'dark', notifications: true }\n * }\n *\n * const hasChanges = !deepEqual(originalData, currentData)\n * if (hasChanges) {\n * console.log('⚠️ You have unsaved changes')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Cache invalidation\n * const cache = new Map<string, { params: any; result: any }>()\n *\n * function cachedApiCall(params: any) {\n * const cacheKey = 'api-call'\n * const cached = cache.get(cacheKey)\n *\n * // Check if cached params match current params\n * if (cached && deepEqual(cached.params, params)) {\n * console.log('✅ Cache hit')\n * return cached.result\n * }\n *\n * console.log('❌ Cache miss - fetching')\n * const result = fetchData(params)\n * cache.set(cacheKey, { params, result })\n * return result\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: React shouldComponentUpdate optimization\n * class UserProfile extends React.Component {\n * shouldComponentUpdate(nextProps: any) {\n * // Only re-render if props actually changed\n * return !deepEqual(this.props, nextProps)\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Testing\n * import { deepEqual } from '@g10/ts-helpers'\n *\n * test('API returns expected user structure', async () => {\n * const response = await api.getUser(123)\n *\n * const expected = {\n * id: 123,\n * name: 'Alice',\n * roles: ['user', 'admin'],\n * metadata: { lastLogin: expect.any(Date) }\n * }\n *\n * // Deep comparison ignoring date instance\n * expect(deepEqual(\n * { ...response, metadata: { ...response.metadata, lastLogin: null } },\n * { ...expected, metadata: { ...expected.metadata, lastLogin: null } }\n * )).toBe(true)\n * })\n * ```\n *\n * @see {@link calculateDifferences} for finding specific differences between objects\n * @see {@link comparator} for lodash-based comparison (deprecated)\n * @see {@link https://github.com/epoberezkin/fast-deep-equal fast-deep-equal library}\n */\nexport const deepEqual = (data1: any, data2: any) => fastEqual(data1, data2)\n\n/**\n * Converts an object to a human-readable string representation\n *\n * Transforms JSON object to clean, readable text by removing braces, quotes, and\n * adding spacing. Useful for logging, debugging, UI displays, and error messages.\n *\n * Transformation:\n * 1. Stringify object to JSON\n * 2. Remove braces `{}` and quotes `\"`\n * 3. Replace commas with comma+space for readability\n *\n * @param data - Object to format (must be JSON-serializable)\n * @returns Human-readable string representation\n *\n * @example\n * ```typescript\n * // Simple object\n * formatToReadableString({ name: 'Alice', age: 25 })\n * // 'name: Alice, age: 25'\n *\n * // Multiple properties\n * formatToReadableString({ id: 1, status: 'active', verified: true })\n * // 'id: 1, status: active, verified: true'\n *\n * // Nested objects (flattened)\n * formatToReadableString({ user: { name: 'Bob' }, role: 'admin' })\n * // 'user: name: Bob, role: admin'\n *\n * // Empty object\n * formatToReadableString({})\n * // ''\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Log request parameters\n * function logApiRequest(endpoint: string, params: any) {\n * const paramsStr = formatToReadableString(params)\n * console.log(`API Request: ${endpoint} | Params: ${paramsStr}`)\n * }\n *\n * logApiRequest('/users', { page: 1, limit: 20, role: 'admin' })\n * // API Request: /users | Params: page: 1, limit: 20, role: admin\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Display validation errors\n * const errors = { email: 'Invalid format', password: 'Too short' }\n * const message = `Validation failed: ${formatToReadableString(errors)}`\n * console.log(message)\n * // Validation failed: email: Invalid format, password: Too short\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Format metadata for display\n * const metadata = {\n * author: 'John Doe',\n * created: '2024-01-01',\n * tags: ['javascript', 'typescript']\n * }\n *\n * const metadataDisplay = formatToReadableString(metadata)\n * // 'author: John Doe, created: 2024-01-01, tags: javascript, typescript'\n * ```\n *\n * @see {@link JSON.stringify} for full JSON serialization\n */\nexport const formatToReadableString = (data: { [key: string]: any }): string =>\n JSON.stringify(data).replace(/[{}\"]/g, '').replace(/,/g, ', ')\n\n/**\n * Extracts only top-level primitive properties from an object\n *\n * Filters out nested objects, arrays, and functions, returning only primitive values\n * (string, number, boolean, null, undefined, symbol). Useful for serialization,\n * API payloads, and database operations.\n *\n * Included types: string, number, boolean, null, undefined, symbol\n * Excluded types: object, array, function\n *\n * @param obj - Object from which to extract properties (null/undefined returns {})\n * @returns New object containing only primitive top-level properties\n *\n * @example\n * ```typescript\n * // Mixed types object\n * const user = {\n * id: 123,\n * name: 'Alice',\n * email: 'alice@example.com',\n * age: 25,\n * active: true,\n * metadata: { lastLogin: '2024-01-01' }, // Excluded (object)\n * roles: ['admin', 'user'], // Excluded (array)\n * save: () => {} // Excluded (function)\n * }\n *\n * getShallowProperties(user)\n * // { id: 123, name: 'Alice', email: 'alice@example.com', age: 25, active: true }\n *\n * // Only primitives\n * const config = { host: 'localhost', port: 5432, ssl: false }\n * getShallowProperties(config)\n * // { host: 'localhost', port: 5432, ssl: false } (unchanged)\n *\n * // Null/undefined values preserved\n * const partial = { a: 1, b: null, c: undefined, d: { nested: true } }\n * getShallowProperties(partial)\n * // { a: 1, b: null, c: undefined }\n *\n * // Empty/null input\n * getShallowProperties({}) // {}\n * getShallowProperties(null) // {}\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Prepare data for SQL insert (exclude nested objects)\n * const formData = {\n * firstName: 'John',\n * lastName: 'Doe',\n * email: 'john@example.com',\n * age: 30,\n * address: { street: '123 Main St', city: 'NYC' }, // Excluded\n * preferences: { theme: 'dark' } // Excluded\n * }\n *\n * const insertData = getShallowProperties(formData)\n * // { firstName: 'John', lastName: 'Doe', email: 'john@example.com', age: 30 }\n *\n * // Now safe to insert into flat database table\n * await db.insert('users', insertData)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Extract searchable fields for indexing\n * const product = {\n * sku: 'ABC123',\n * name: 'Widget',\n * price: 29.99,\n * inStock: true,\n * category: { id: 1, name: 'Electronics' }, // Excluded\n * reviews: [{ rating: 5 }], // Excluded\n * images: ['img1.jpg', 'img2.jpg'] // Excluded\n * }\n *\n * const searchableFields = getShallowProperties(product)\n * // { sku: 'ABC123', name: 'Widget', price: 29.99, inStock: true }\n *\n * // Index only primitive fields for search\n * searchIndex.add(product.sku, searchableFields)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Extract flat properties for CSV export\n * const users = [\n * {\n * id: 1,\n * name: 'Alice',\n * email: 'alice@example.com',\n * profile: { bio: 'Developer' },\n * tags: ['admin']\n * },\n * {\n * id: 2,\n * name: 'Bob',\n * email: 'bob@example.com',\n * profile: { bio: 'Designer' },\n * tags: ['user']\n * }\n * ]\n *\n * const csvData = users.map(getShallowProperties)\n * // [\n * // { id: 1, name: 'Alice', email: 'alice@example.com' },\n * // { id: 2, name: 'Bob', email: 'bob@example.com' }\n * // ]\n * // Now safe to convert to CSV\n * ```\n *\n * @see {@link calculateDifferences} for comparing objects\n * @see {@link deepEqual} for deep comparison including nested objects\n */\nexport const getShallowProperties = (obj: any): any => {\n if (!obj) return {}\n const topLevelProps: any = {}\n for (const prop in obj) {\n if (Object.hasOwnProperty.call(obj, prop)) {\n const value = obj[prop]\n const type = typeof value\n // Include primitives: string, number, boolean, undefined, symbol, and null\n if ((type !== 'object' && type !== 'function') || value === null) {\n topLevelProps[prop] = value\n }\n }\n }\n return topLevelProps\n}\n\n/**\n * Calculates differences between two objects\n *\n * Compares two objects and returns only the properties that differ. Useful for\n * change tracking, audit logs, delta updates, and optimistic UI updates.\n *\n * Special features:\n * - Date comparison: Compares dates ignoring milliseconds (tolerance-based)\n * - Shallow comparison: Only checks top-level properties\n * - One-way diff: Returns changed properties from newObj (not deletions)\n *\n * @param oldObj - Original object to compare against (baseline)\n * @param newObj - New object to compare (current state)\n * @returns Object containing only properties that changed in newObj\n *\n * @example\n * ```typescript\n * // Simple property changes\n * const old = { name: 'Alice', age: 25, city: 'NYC' }\n * const updated = { name: 'Alice', age: 26, city: 'NYC' }\n *\n * calculateDifferences(old, updated)\n * // { age: 26 } - only changed property\n *\n * // Multiple changes\n * const old2 = { a: 1, b: 2, c: 3 }\n * const new2 = { a: 1, b: 999, c: 3 }\n * calculateDifferences(old2, new2)\n * // { b: 999 }\n *\n * // No changes\n * const old3 = { x: 10 }\n * const new3 = { x: 10 }\n * calculateDifferences(old3, new3)\n * // {} - empty object\n *\n * // Date comparison (ignores milliseconds)\n * const oldDate = { timestamp: new Date('2024-01-01T10:00:00.123Z') }\n * const newDate = { timestamp: new Date('2024-01-01T10:00:00.456Z') }\n * calculateDifferences(oldDate, newDate)\n * // {} - dates considered equal (same second)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Track form changes for audit log\n * const originalUser = {\n * name: 'John Doe',\n * email: 'john@example.com',\n * role: 'user',\n * lastLogin: new Date('2024-01-01')\n * }\n *\n * const updatedUser = {\n * name: 'John Doe',\n * email: 'john.doe@company.com', // Changed\n * role: 'admin', // Changed\n * lastLogin: new Date('2024-01-01')\n * }\n *\n * const changes = calculateDifferences(originalUser, updatedUser)\n * console.log('User changes:', changes)\n * // { email: 'john.doe@company.com', role: 'admin' }\n *\n * // Save to audit log\n * auditLog.push({\n * userId: user.id,\n * timestamp: new Date(),\n * changes: changes\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Optimistic UI updates (send only changed fields)\n * function updateUserProfile(userId: string, formData: any) {\n * const originalData = getCurrentUserData(userId)\n *\n * // Only send changed fields to API\n * const delta = calculateDifferences(originalData, formData)\n *\n * if (Object.keys(delta).length === 0) {\n * console.log('✅ No changes to save')\n * return\n * }\n *\n * // Send PATCH request with only changed fields\n * return api.patch(`/users/${userId}`, delta)\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Configuration change detection\n * const previousConfig = {\n * database: { host: 'localhost', port: 5432 },\n * cache: { ttl: 3600 },\n * features: { darkMode: true }\n * }\n *\n * const newConfig = {\n * database: { host: 'prod-db.example.com', port: 5432 },\n * cache: { ttl: 7200 },\n * features: { darkMode: true }\n * }\n *\n * const configChanges = calculateDifferences(previousConfig, newConfig)\n * // {\n * // database: { host: 'prod-db.example.com', port: 5432 },\n * // cache: { ttl: 7200 }\n * // }\n *\n * // Note: Returns entire nested object if any property changed\n * if (configChanges.database) {\n * console.log('⚠️ Database configuration changed - restart required')\n * }\n * ```\n *\n * @see {@link deepEqual} for full equality check\n * @see {@link getShallowProperties} for extracting only primitive properties\n */\nexport const calculateDifferences = (oldObj: any, newObj: any): any => {\n return transform(\n newObj,\n (result, value, key) => {\n const oldValue = oldObj[key]\n // If both values are dates, compare them ignoring milliseconds\n if (isDateTime(value) && isDateTime(oldValue)) {\n if (!areDatesEqualWithTolerance(toDate(value), toDate(oldValue))) {\n result[key] = value\n }\n } else if (!isEqual(value, oldValue)) {\n result[key] = value\n }\n },\n {}\n )\n}\n\n/**\n * Generates CRC32 hash from various input types\n *\n * Creates a 32-bit cyclic redundancy check (CRC32) hash as hexadecimal string.\n * Fast, deterministic fingerprinting for data integrity, caching, and change detection.\n *\n * Supported types:\n * - String: Direct hash\n * - Buffer/Uint8Array/ArrayBuffer: Binary hash\n * - Number: Converts to string then hash\n * - Boolean: 'true'/'false' then hash\n * - Object/Array: JSON.stringify then hash\n * - null/undefined: Hash of '-'\n *\n * Use cases: Content fingerprinting, ETags, cache keys, data deduplication\n *\n * ⚠️ NOTE: CRC32 is NOT cryptographically secure. Use for checksums, not security.\n *\n * @param str - Input data of any type (string, Buffer, object, array, primitive)\n * @returns CRC32 hash as hexadecimal string (8 characters)\n *\n * @example\n * ```typescript\n * // String inputs\n * generateCrcHash('hello') // '3610a686'\n * generateCrcHash('Hello') // 'f7d18982' (case-sensitive)\n * generateCrcHash('') // '0'\n *\n * // Numbers\n * generateCrcHash(123) // '884863d2' (hashes '123')\n * generateCrcHash(0) // 'f4dbdf21'\n *\n * // Booleans\n * generateCrcHash(true) // 'cc2c5c10' (hashes 'true')\n * generateCrcHash(false) // 'cc0c5c10' (hashes 'false')\n *\n * // Objects (deterministic)\n * generateCrcHash({ a: 1, b: 2 }) // Same hash for same object\n * generateCrcHash({ b: 2, a: 1 }) // Different hash (key order matters)\n *\n * // Arrays\n * generateCrcHash([1, 2, 3]) // Consistent hash\n *\n * // null/undefined\n * generateCrcHash(null) // '4e08bfb4' (hashes '-')\n * generateCrcHash(undefined) // '4e08bfb4' (hashes '-')\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate ETag for HTTP caching\n * function generateETag(content: string): string {\n * const hash = generateCrcHash(content)\n * return `\"${hash}\"`\n * }\n *\n * const html = '<html><body>Hello World</body></html>'\n * const etag = generateETag(html)\n * // \"a3c2f1b8\"\n *\n * // Client sends: If-None-Match: \"a3c2f1b8\"\n * // Server compares ETags, returns 304 Not Modified if match\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Content-based cache key\n * const cache = new Map<string, any>()\n *\n * function cacheApiResponse(params: any, response: any) {\n * const cacheKey = generateCrcHash(JSON.stringify(params))\n * cache.set(cacheKey, response)\n * }\n *\n * function getCachedResponse(params: any) {\n * const cacheKey = generateCrcHash(JSON.stringify(params))\n * return cache.get(cacheKey)\n * }\n *\n * // Same params = same hash = cache hit\n * cacheApiResponse({ userId: 123, page: 1 }, { data: [...] })\n * const cached = getCachedResponse({ userId: 123, page: 1 })\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Detect duplicate content\n * const seen = new Set<string>()\n *\n * function isDuplicate(content: string): boolean {\n * const hash = generateCrcHash(content)\n * if (seen.has(hash)) {\n * return true\n * }\n * seen.add(hash)\n * return false\n * }\n *\n * isDuplicate('Hello') // false (first time)\n * isDuplicate('World') // false\n * isDuplicate('Hello') // true (duplicate!)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: File versioning/fingerprinting\n * const fileContents = [\n * { path: 'app.js', content: 'console.log(\"v1\")' },\n * { path: 'style.css', content: 'body { color: red; }' }\n * ]\n *\n * const manifest = fileContents.map(file => ({\n * path: file.path,\n * hash: generateCrcHash(file.content),\n * url: `${file.path}?v=${generateCrcHash(file.content)}`\n * }))\n *\n * // [\n * // { path: 'app.js', hash: 'a3c2f1b8', url: 'app.js?v=a3c2f1b8' },\n * // { path: 'style.css', hash: 'b4d3e2c1', url: 'style.css?v=b4d3e2c1' }\n * // ]\n * // Cache-busting URLs change only when content changes\n * ```\n *\n * @see {@link deepEqual} for comparing objects for equality\n * @see {@link https://en.wikipedia.org/wiki/Cyclic_redundancy_check CRC32 Algorithm}\n */\nexport const generateCrcHash = (str: string | Buffer | Uint8Array | any): string => {\n if (isNil(str)) return crc32('-').toString(16)\n if (isString(str)) return crc32(str).toString(16)\n if (isBuffer(str)) return crc32(str).toString(16)\n if (isArrayBuffer(str)) return crc32(str).toString(16)\n if (isBoolean(str)) return crc32(str ? 'true' : 'false').toString(16)\n if (isNumber(str)) return crc32(str.toString()).toString(16)\n if (isArray(str) || isObject(str)) return generateCrcHash(JSON.stringify(str))\n return crc32(str || '').toString(16)\n}\n\n/**\n * Verifies if two variables have the same type\n *\n * Performs strict type comparison with special handling for arrays, typed arrays,\n * plain objects, and primitives. Uses runtime type checking for accurate validation.\n *\n * Type detection hierarchy:\n * 1. Arrays: Standard Array.isArray() check\n * 2. Typed Arrays: Int8Array, Uint8Array, Float32Array, etc. (checks exact typed array class)\n * 3. Plain Objects: Object literals (not class instances or built-in objects)\n * 4. Primitives: typeof comparison (string, number, boolean, undefined, symbol)\n *\n * Special features:\n * - Differentiates between arrays and typed arrays\n * - Typed array class matching (Int8Array ≠ Uint8Array)\n * - Plain object vs class instance detection\n * - null/undefined handling\n *\n * @param sourceTarget - First value to check type\n * @param destinationTarget - Second value to check type\n * @returns True if both values have the same type, false otherwise\n *\n * @example\n * ```typescript\n * // Basic types - Primitives\n * hasSameType('hello', 'world') // true (both string)\n * hasSameType(42, 100) // true (both number)\n * hasSameType(true, false) // true (both boolean)\n * hasSameType('hello', 42) // false (string vs number)\n * hasSameType(null, undefined) // false (different types)\n * ```\n *\n * @example\n * ```typescript\n * // Arrays and objects\n * hasSameType([1, 2, 3], [4, 5]) // true (both arrays)\n * hasSameType({ a: 1 }, { b: 2 }) // true (both plain objects)\n * hasSameType([1, 2], { a: 1 }) // false (array vs object)\n * hasSameType([], {}) // false (array vs object)\n * ```\n *\n * @example\n * ```typescript\n * // Typed arrays - Strict class matching\n * const int8 = new Int8Array([1, 2, 3])\n * const int8_2 = new Int8Array([4, 5])\n * const uint8 = new Uint8Array([1, 2, 3])\n * const float32 = new Float32Array([1.5, 2.5])\n *\n * hasSameType(int8, int8_2) // true (both Int8Array)\n * hasSameType(int8, uint8) // false (Int8Array vs Uint8Array)\n * hasSameType(uint8, float32) // false (Uint8Array vs Float32Array)\n * hasSameType(int8, [1, 2, 3]) // false (typed array vs regular array)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Type-safe data merging\n * function safeMerge<T>(source: T, updates: any): T | null {\n * if (!hasSameType(source, updates)) {\n * console.error('❌ Type mismatch: cannot merge incompatible types')\n * return null\n * }\n *\n * if (Array.isArray(source)) {\n * return [...source, ...updates] as T\n * }\n *\n * if (typeof source === 'object' && source !== null) {\n * return { ...source, ...updates }\n * }\n *\n * return updates // Replace primitive values\n * }\n *\n * // Valid merges\n * const user = { name: 'John', age: 30 }\n * const updated = safeMerge(user, { age: 31, city: 'NYC' })\n * // { name: 'John', age: 31, city: 'NYC' }\n *\n * // Type mismatch prevented\n * const invalid = safeMerge(user, [1, 2, 3])\n * // null (with error logged)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: API response validation\n * function validateResponseShape(expected: any, actual: any): boolean {\n * // Check if response has the same structure\n * if (!hasSameType(expected, actual)) {\n * console.error('Invalid response type:', {\n * expected: typeof expected,\n * actual: typeof actual\n * })\n * return false\n * }\n *\n * // Additional validation for objects/arrays\n * if (typeof expected === 'object' && expected !== null) {\n * for (const key of Object.keys(expected)) {\n * if (!hasSameType(expected[key], actual[key])) {\n * console.error(`Type mismatch at key \"${key}\"`)\n * return false\n * }\n * }\n * }\n *\n * return true\n * }\n *\n * const expectedUser = {\n * id: 0,\n * name: '',\n * active: false,\n * tags: [] as string[]\n * }\n *\n * const validResponse = {\n * id: 123,\n * name: 'Alice',\n * active: true,\n * tags: ['admin', 'verified']\n * }\n *\n * validateResponseShape(expectedUser, validResponse) // true\n *\n * const invalidResponse = {\n * id: '123', // ❌ string instead of number\n * name: 'Alice',\n * active: true,\n * tags: ['admin']\n * }\n *\n * validateResponseShape(expectedUser, invalidResponse) // false\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Buffer/TypedArray validation before processing\n * function processImageBuffer(buffer: Uint8Array | Uint8ClampedArray): void {\n * const validBufferTypes = [\n * new Uint8Array(0),\n * new Uint8ClampedArray(0)\n * ]\n *\n * const isValidType = validBufferTypes.some(validType =>\n * hasSameType(buffer, validType)\n * )\n *\n * if (!isValidType) {\n * throw new TypeError('Expected Uint8Array or Uint8ClampedArray for image data')\n * }\n *\n * // Safe to process buffer\n * console.log(`Processing ${buffer.length} bytes of image data`)\n * }\n *\n * // Valid buffers\n * const imageData = new Uint8ClampedArray([255, 0, 0, 255]) // RGBA pixel\n * processImageBuffer(imageData) // ✅ Works\n *\n * // Invalid buffer type\n * const float32Buffer = new Float32Array([1.0, 0.5])\n * processImageBuffer(float32Buffer) // ❌ TypeError\n * ```\n *\n * @example\n * ```typescript\n * // Edge cases\n * hasSameType(null, null) // true (both null)\n * hasSameType(undefined, undefined) // true (both undefined)\n * hasSameType(null, undefined) // false (different types)\n * hasSameType(NaN, NaN) // true (both number)\n * hasSameType(Infinity, -Infinity) // true (both number)\n *\n * // Class instances vs plain objects\n * class User { name = 'John' }\n * const userInstance = new User()\n * const plainUser = { name: 'John' }\n * hasSameType(userInstance, plainUser) // false (class instance vs plain object)\n *\n * // Functions\n * const fn1 = () => {}\n * const fn2 = function() {}\n * hasSameType(fn1, fn2) // true (both functions)\n * ```\n *\n * @see {@link deepEqual} for value comparison (not just type)\n * @see {@link isArray} for array checking\n * @see {@link isPlainObject} for plain object detection\n */\nexport const hasSameType = (sourceTarget: any, destinationTarget: any) => {\n if (isArray(sourceTarget) && isArray(destinationTarget)) return true\n if (isArray(sourceTarget) !== isArray(destinationTarget)) return false\n if (isTypedArray(sourceTarget) && isTypedArray(destinationTarget)) {\n if (\n Object.prototype.toString.call(sourceTarget) ===\n Object.prototype.toString.call(destinationTarget)\n )\n return true\n else return false\n }\n if (isTypedArray(sourceTarget) !== isTypedArray(destinationTarget)) return false\n if (isPlainObject(sourceTarget) && isPlainObject(destinationTarget)) return true\n\n // Check if one is object and the other is not\n if (isPlainObject(sourceTarget) !== isPlainObject(destinationTarget)) return false\n\n return typeof sourceTarget === typeof destinationTarget\n}\n\n/**\n * Updates array elements that match search criteria\n *\n * Finds all elements matching the search criteria and merges update object into them.\n * Mutates the original array. Useful for batch updates on filtered data.\n *\n * Algorithm:\n * 1. Filter array to find elements matching objSearch\n * 2. For each match, merge objUpd properties using Object.assign\n * 3. Return the mutated array\n *\n * @param data - Array to update (will be mutated)\n * @param objUpd - Object with properties to merge into matching elements\n * @param objSearch - Object with search criteria (all properties must match)\n * @returns The mutated array with updated elements\n *\n * @example\n * ```typescript\n * // Update all users with role 'user' to 'member'\n * const users = [\n * { id: 1, name: 'Alice', role: 'user', active: true },\n * { id: 2, name: 'Bob', role: 'admin', active: true },\n * { id: 3, name: 'Charlie', role: 'user', active: false }\n * ]\n *\n * updateArrayElementsBy(users, { role: 'member' }, { role: 'user' })\n * // users is now:\n * // [\n * // { id: 1, name: 'Alice', role: 'member', active: true },\n * // { id: 2, name: 'Bob', role: 'admin', active: true },\n * // { id: 3, name: 'Charlie', role: 'member', active: false }\n * // ]\n *\n * // Update multiple properties\n * const products = [\n * { sku: 'A1', status: 'draft', published: false },\n * { sku: 'A2', status: 'draft', published: false },\n * { sku: 'B1', status: 'active', published: true }\n * ]\n *\n * updateArrayElementsBy(\n * products,\n * { status: 'active', published: true },\n * { status: 'draft' }\n * )\n * // All draft products now active and published\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Bulk activate pending users\n * const pendingUsers = [\n * { email: 'user1@example.com', status: 'pending', verified: false },\n * { email: 'user2@example.com', status: 'pending', verified: false },\n * { email: 'user3@example.com', status: 'active', verified: true }\n * ]\n *\n * // Activate all pending users\n * updateArrayElementsBy(\n * pendingUsers,\n * { status: 'active', verified: true, activatedAt: new Date() },\n * { status: 'pending' }\n * )\n *\n * console.log(pendingUsers.filter(u => u.status === 'active').length)\n * // 3 (all users now active)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Mark completed tasks as archived\n * const tasks = [\n * { id: 1, title: 'Task 1', completed: true, archived: false },\n * { id: 2, title: 'Task 2', completed: false, archived: false },\n * { id: 3, title: 'Task 3', completed: true, archived: false }\n * ]\n *\n * updateArrayElementsBy(\n * tasks,\n * { archived: true, archivedAt: new Date() },\n * { completed: true }\n * )\n *\n * const archivedCount = tasks.filter(t => t.archived).length\n * console.log(`Archived ${archivedCount} completed tasks`)\n * ```\n *\n * @see {@link updateArrayElementById} for updating single element by ID\n * @see {@link deleteArrayElementsBy} for deleting elements by criteria\n */\nexport const updateArrayElementsBy = (data: any[], objUpd: any, objSearch: any) => {\n nativeFilter(data, objSearch).forEach(element => nativeAssign(element, objUpd))\n return data\n}\n\n/**\n * Updates a single array element by its ID field\n *\n * Finds element by matching ID field value and replaces entire element with new object.\n * Mutates the original array. Use for updating single records by primary key.\n *\n * Algorithm:\n * 1. Find index of element where element[idField] === objUpd[idField]\n * 2. If found, replace entire element with objUpd\n * 3. If not found, array remains unchanged\n *\n * @param data - Array to update (will be mutated)\n * @param objUpd - New object to replace the found element (must contain idField)\n * @param idField - Name of field to use as identifier (default: 'id')\n * @returns void (modifies array in-place)\n *\n * @example\n * ```typescript\n * // Update user by ID\n * const users = [\n * { id: 1, name: 'Alice', email: 'alice@example.com' },\n * { id: 2, name: 'Bob', email: 'bob@example.com' },\n * { id: 3, name: 'Charlie', email: 'charlie@example.com' }\n * ]\n *\n * updateArrayElementById(\n * users,\n * { id: 2, name: 'Robert', email: 'robert@example.com', verified: true },\n * 'id'\n * )\n * // users[1] is now: { id: 2, name: 'Robert', email: 'robert@example.com', verified: true }\n *\n * // ID not found - no change\n * updateArrayElementById(users, { id: 999, name: 'Unknown' }, 'id')\n * // users array unchanged\n *\n * // Custom ID field\n * const products = [\n * { sku: 'ABC123', name: 'Widget', price: 10 },\n * { sku: 'DEF456', name: 'Gadget', price: 20 }\n * ]\n *\n * updateArrayElementById(\n * products,\n * { sku: 'ABC123', name: 'Super Widget', price: 15 },\n * 'sku'\n * )\n * // products[0] is now: { sku: 'ABC123', name: 'Super Widget', price: 15 }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Update cached user after API response\n * const userCache = [\n * { id: 1, name: 'Alice', role: 'user' },\n * { id: 2, name: 'Bob', role: 'admin' },\n * ]\n *\n * async function updateUser(userId: number, updates: any) {\n * // API call\n * const response = await api.patch(`/users/${userId}`, updates)\n *\n * // Update local cache with full response\n * updateArrayElementById(userCache, response.data, 'id')\n *\n * console.log('✅ Cache updated')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Replace product in shopping cart\n * const cart = [\n * { productId: 'P1', name: 'Laptop', qty: 1, price: 1000 },\n * { productId: 'P2', name: 'Mouse', qty: 2, price: 25 }\n * ]\n *\n * function updateCartItem(updatedItem: any) {\n * updateArrayElementById(cart, updatedItem, 'productId')\n * recalculateTotal()\n * }\n *\n * // User changes laptop quantity\n * updateCartItem({ productId: 'P1', name: 'Laptop', qty: 2, price: 1000 })\n * ```\n *\n * @see {@link updateArrayElementsBy} for updating multiple elements by criteria\n * @see {@link deleteArrayElementsBy} for deleting elements\n */\nexport const updateArrayElementById = (data: any[], objUpd: any, idField: string) => {\n const index = data.findIndex(x => x[idField] === objUpd[idField])\n if (index !== -1) data.splice(index, 1, objUpd)\n}\n\n/**\n * Removes array elements that match search criteria\n *\n * Filters out elements matching all properties in the search object.\n * Mutates the original array. Useful for batch deletions based on criteria.\n *\n * Algorithm:\n * 1. Pick properties from each element matching objSearch keys\n * 2. Deep compare picked properties with objSearch\n * 3. Remove element if all properties match\n * 4. Return the mutated array\n *\n * @param data - Array to modify (will be mutated)\n * @param objSearch - Object with search criteria (all properties must match for deletion)\n * @returns The mutated array with matching elements removed\n *\n * @example\n * ```typescript\n * // Delete users with role 'guest'\n * const users = [\n * { id: 1, name: 'Alice', role: 'admin' },\n * { id: 2, name: 'Bob', role: 'guest' },\n * { id: 3, name: 'Charlie', role: 'guest' },\n * { id: 4, name: 'Dave', role: 'user' }\n * ]\n *\n * deleteArrayElementsBy(users, { role: 'guest' })\n * // users is now: [\n * // { id: 1, name: 'Alice', role: 'admin' },\n * // { id: 4, name: 'Dave', role: 'user' }\n * // ]\n *\n * // Multiple criteria (AND condition)\n * const tasks = [\n * { id: 1, status: 'done', archived: true },\n * { id: 2, status: 'done', archived: false },\n * { id: 3, status: 'pending', archived: false }\n * ]\n *\n * deleteArrayElementsBy(tasks, { status: 'done', archived: true })\n * // Removes only tasks that are BOTH done AND archived\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Clean up expired sessions\n * const sessions = [\n * { sessionId: 'abc', userId: 1, expired: false },\n * { sessionId: 'def', userId: 2, expired: true },\n * { sessionId: 'ghi', userId: 3, expired: true },\n * { sessionId: 'jkl', userId: 4, expired: false }\n * ]\n *\n * // Remove all expired sessions\n * deleteArrayElementsBy(sessions, { expired: true })\n *\n * console.log(`Active sessions: ${sessions.length}`)\n * // Active sessions: 2\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Remove cancelled orders from pending list\n * const orders = [\n * { orderId: 'O1', status: 'pending', cancelled: false },\n * { orderId: 'O2', status: 'pending', cancelled: true },\n * { orderId: 'O3', status: 'shipped', cancelled: false },\n * { orderId: 'O4', status: 'pending', cancelled: true }\n * ]\n *\n * // Clean up cancelled pending orders\n * deleteArrayElementsBy(orders, { status: 'pending', cancelled: true })\n *\n * // orders now only has valid pending and shipped orders\n * console.log(orders.length) // 2\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Clear failed upload queue\n * const uploadQueue = [\n * { fileId: 'F1', status: 'uploading', retries: 0 },\n * { fileId: 'F2', status: 'failed', retries: 3 },\n * { fileId: 'F3', status: 'failed', retries: 3 },\n * { fileId: 'F4', status: 'completed', retries: 0 }\n * ]\n *\n * // Remove permanently failed uploads (max retries reached)\n * deleteArrayElementsBy(uploadQueue, { status: 'failed', retries: 3 })\n *\n * console.log(`Remaining in queue: ${uploadQueue.length}`)\n * // Remaining in queue: 2 (uploading + completed)\n * ```\n *\n * @see {@link updateArrayElementsBy} for updating elements by criteria\n * @see {@link updateArrayElementById} for updating single element by ID\n */\nexport const deleteArrayElementsBy = (data: any[], objSearch: any) => {\n nativeRemove(data, x => deepEqual(nativePick(x, Object.keys(objSearch) as any), objSearch))\n return data\n}\n\n/**\n * Sets a value at a deep path in an object using dot notation\n *\n * Creates intermediate objects/arrays as needed. Supports nested paths\n * and array indices. Mutates the original object.\n *\n * @param obj - Object to modify (will be mutated)\n * @param path - Dot-notation path (e.g., 'database.connection.timeout')\n * @param value - Value to set at the path\n * @returns The modified object (same reference as input)\n *\n * @example\n * ```typescript\n * // Simple nested path\n * const config = {}\n * setDeepValue(config, 'database.host', 'localhost')\n * // config = { database: { host: 'localhost' } }\n *\n * // Multi-level nesting\n * const obj = {}\n * setDeepValue(obj, 'app.server.port', 3000)\n * // obj = { app: { server: { port: 3000 } } }\n *\n * // Array indices\n * const data = { users: [] }\n * setDeepValue(data, 'users.0.name', 'Alice')\n * // data = { users: [{ name: 'Alice' }] }\n *\n * // Overwriting existing values\n * const settings = { database: { host: 'old' } }\n * setDeepValue(settings, 'database.host', 'new')\n * // settings = { database: { host: 'new' } }\n *\n * // Complex paths\n * const complex = {}\n * setDeepValue(complex, 'features.authentication.oauth.providers.0', 'google')\n * // complex = { features: { authentication: { oauth: { providers: ['google'] } } } }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Dynamic configuration\n * const config = {}\n * const envVars = {\n * 'DATABASE_HOST': 'localhost',\n * 'DATABASE_PORT': '5432',\n * 'CACHE_TTL': '3600'\n * }\n *\n * // Convert flat env vars to nested config\n * setDeepValue(config, 'database.host', envVars.DATABASE_HOST)\n * setDeepValue(config, 'database.port', parseInt(envVars.DATABASE_PORT))\n * setDeepValue(config, 'cache.ttl', parseInt(envVars.CACHE_TTL))\n * // config = {\n * // database: { host: 'localhost', port: 5432 },\n * // cache: { ttl: 3600 }\n * // }\n * ```\n */\nexport function setDeepValue<T = any>(obj: T, path: string, value: any): T {\n if (!obj || typeof obj !== 'object') {\n throw new TypeError('setDeepValue: target must be an object')\n }\n\n if (!path || typeof path !== 'string') {\n throw new TypeError('setDeepValue: path must be a non-empty string')\n }\n\n const keys = path.split('.')\n let current: any = obj\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i]\n const nextKey = keys[i + 1]\n\n // Determine if next level should be an array or object\n const isNextArray = /^\\d+$/.test(nextKey)\n\n // Create intermediate level if it doesn't exist\n if (!(key in current) || typeof current[key] !== 'object') {\n current[key] = isNextArray ? [] : {}\n }\n\n current = current[key]\n }\n\n // Set the final value\n const lastKey = keys[keys.length - 1]\n current[lastKey] = value\n\n return obj\n}\n\n/**\n * Gets a value at a deep path in an object using dot notation\n *\n * Safely retrieves values from nested objects/arrays. Returns undefined\n * or a default value if path doesn't exist.\n *\n * @param obj - Object to read from\n * @param path - Dot-notation path (e.g., 'database.connection.timeout')\n * @param defaultValue - Value to return if path doesn't exist (default: undefined)\n * @returns Value at the path, or defaultValue if not found\n *\n * @example\n * ```typescript\n * // Simple nested access\n * const config = { database: { host: 'localhost' } }\n * getDeepValue(config, 'database.host') // 'localhost'\n *\n * // Non-existent path returns undefined\n * getDeepValue(config, 'database.port') // undefined\n *\n * // With default value\n * getDeepValue(config, 'database.port', 5432) // 5432\n *\n * // Array access\n * const data = { users: [{ name: 'Alice' }, { name: 'Bob' }] }\n * getDeepValue(data, 'users.0.name') // 'Alice'\n * getDeepValue(data, 'users.1.name') // 'Bob'\n *\n * // Deep paths\n * const app = { features: { auth: { enabled: true } } }\n * getDeepValue(app, 'features.auth.enabled') // true\n * getDeepValue(app, 'features.payments.enabled', false) // false (default)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Safe config access\n * const config = {\n * database: { host: 'localhost' },\n * cache: { ttl: 3600 }\n * }\n *\n * const dbHost = getDeepValue(config, 'database.host', '127.0.0.1')\n * const dbPort = getDeepValue(config, 'database.port', 5432)\n * const cacheTtl = getDeepValue(config, 'cache.ttl', 1800)\n * const redisHost = getDeepValue(config, 'redis.host', 'localhost')\n *\n * // All values are safely retrieved with fallbacks\n * ```\n */\nexport function getDeepValue<T = any>(obj: any, path: string, defaultValue?: T): T | undefined {\n if (!obj || typeof obj !== 'object') {\n return defaultValue\n }\n\n if (!path || typeof path !== 'string') {\n return defaultValue\n }\n\n const keys = path.split('.')\n let current = obj\n\n for (const key of keys) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return defaultValue\n }\n\n if (!(key in current)) {\n return defaultValue\n }\n\n current = current[key]\n }\n\n return current !== undefined ? current : defaultValue\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-NSBPE2FW.js";
|
|
4
|
+
|
|
5
|
+
// src/strings.ts
|
|
6
|
+
var strings_exports = {};
|
|
7
|
+
__export(strings_exports, {
|
|
8
|
+
capitalizeEachWord: () => capitalizeEachWord,
|
|
9
|
+
capitalizeFirst: () => capitalizeFirst,
|
|
10
|
+
cleanJsonChars: () => cleanJsonChars,
|
|
11
|
+
contains: () => contains,
|
|
12
|
+
countOccurrences: () => countOccurrences,
|
|
13
|
+
endsWith: () => endsWith,
|
|
14
|
+
ensureEndsWith: () => ensureEndsWith,
|
|
15
|
+
ensureStartsWith: () => ensureStartsWith,
|
|
16
|
+
envKeyToPath: () => envKeyToPath,
|
|
17
|
+
escapeHtmlChars: () => escapeHtmlChars,
|
|
18
|
+
isEmail: () => isEmail,
|
|
19
|
+
isEmpty: () => isEmpty,
|
|
20
|
+
matchPathPattern: () => matchPathPattern,
|
|
21
|
+
padEnd: () => padEnd,
|
|
22
|
+
padStart: () => padStart,
|
|
23
|
+
pathToEnvKey: () => pathToEnvKey,
|
|
24
|
+
removeAccents: () => removeAccents,
|
|
25
|
+
repeatString: () => repeatString,
|
|
26
|
+
replaceAllOccurrences: () => replaceAllOccurrences,
|
|
27
|
+
reverseString: () => reverseString,
|
|
28
|
+
sanitizeString: () => sanitizeString,
|
|
29
|
+
startsWith: () => startsWith,
|
|
30
|
+
stripFromEnd: () => stripFromEnd,
|
|
31
|
+
stripFromStart: () => stripFromStart,
|
|
32
|
+
toCamelCase: () => toCamelCase,
|
|
33
|
+
toKebabCase: () => toKebabCase,
|
|
34
|
+
toLowerCase: () => toLowerCase,
|
|
35
|
+
toPascalCase: () => toPascalCase,
|
|
36
|
+
toSnakeCase: () => toSnakeCase,
|
|
37
|
+
toUpperCase: () => toUpperCase,
|
|
38
|
+
toUrlSlug: () => toUrlSlug,
|
|
39
|
+
trim: () => trim,
|
|
40
|
+
trimEnd: () => trimEnd,
|
|
41
|
+
trimStart: () => trimStart,
|
|
42
|
+
truncateString: () => truncateString,
|
|
43
|
+
unescapeHtmlChars: () => unescapeHtmlChars,
|
|
44
|
+
unescapeUnicode: () => unescapeUnicode
|
|
45
|
+
});
|
|
46
|
+
var sanitizeString = (instr) => {
|
|
47
|
+
if (!instr) return "";
|
|
48
|
+
return addDash(instr.replace(/[^a-zA-Z0-9 ]/g, "")).toLowerCase();
|
|
49
|
+
};
|
|
50
|
+
function addDash(str) {
|
|
51
|
+
const str2 = str.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>{}[\]\\/]/gi, "");
|
|
52
|
+
return str2.replace(/ /g, "-");
|
|
53
|
+
}
|
|
54
|
+
var cleanJsonChars = (texto) => {
|
|
55
|
+
if (!texto) return "";
|
|
56
|
+
return texto.replace(/({|}|&|amp;|gt;)/g, "");
|
|
57
|
+
};
|
|
58
|
+
var truncateString = (str, maxlength = 80, suffix = "...") => {
|
|
59
|
+
if (!str) return "";
|
|
60
|
+
return str.length > maxlength ? str.substring(0, maxlength) + suffix : str;
|
|
61
|
+
};
|
|
62
|
+
var unescapeUnicode = (input) => {
|
|
63
|
+
return JSON.stringify(JSON.parse(`"${input}"`)).replace(/^"(.*)"$/, "$1");
|
|
64
|
+
};
|
|
65
|
+
var ensureEndsWith = (str, trailing) => {
|
|
66
|
+
return str.endsWith(trailing) ? str : `${str}${trailing}`;
|
|
67
|
+
};
|
|
68
|
+
var stripFromEnd = (str, trailing) => {
|
|
69
|
+
return str.endsWith(trailing) ? str.slice(0, -1 * trailing.length) : str;
|
|
70
|
+
};
|
|
71
|
+
var ensureStartsWith = (str, leading) => {
|
|
72
|
+
return str.startsWith(leading) ? str : `${leading}${str}`;
|
|
73
|
+
};
|
|
74
|
+
var stripFromStart = (str, leading) => {
|
|
75
|
+
return str.startsWith(leading) ? str.slice(leading.length) : str;
|
|
76
|
+
};
|
|
77
|
+
var toLowerCase = (str) => {
|
|
78
|
+
return str.toLowerCase();
|
|
79
|
+
};
|
|
80
|
+
var toUpperCase = (str) => {
|
|
81
|
+
return str.toUpperCase();
|
|
82
|
+
};
|
|
83
|
+
var capitalizeFirst = (str) => {
|
|
84
|
+
if (!str) return "";
|
|
85
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
86
|
+
};
|
|
87
|
+
var capitalizeEachWord = (str) => {
|
|
88
|
+
if (!str) return "";
|
|
89
|
+
return str.split(" ").map((word) => capitalizeFirst(word)).join(" ");
|
|
90
|
+
};
|
|
91
|
+
var toCamelCase = (str) => {
|
|
92
|
+
if (!str) return "";
|
|
93
|
+
const words = str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]+/).filter((word) => word.length > 0).map((word) => word.toLowerCase());
|
|
94
|
+
if (words.length === 0) return "";
|
|
95
|
+
return words[0] + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
96
|
+
};
|
|
97
|
+
var toSnakeCase = (str) => {
|
|
98
|
+
if (!str) return "";
|
|
99
|
+
return str.replace(/\W+/g, " ").split(/ |\B(?=[A-Z])/).map((word) => word.toLowerCase()).join("_");
|
|
100
|
+
};
|
|
101
|
+
var toKebabCase = (str) => {
|
|
102
|
+
if (!str) return "";
|
|
103
|
+
return str.replace(/[_\s]+/g, "-").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
104
|
+
};
|
|
105
|
+
var toPascalCase = (str) => {
|
|
106
|
+
if (!str) return "";
|
|
107
|
+
return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
|
|
108
|
+
};
|
|
109
|
+
var contains = (str, searchStr, caseSensitive = false) => {
|
|
110
|
+
if (caseSensitive) {
|
|
111
|
+
return str.includes(searchStr);
|
|
112
|
+
}
|
|
113
|
+
return str.toLowerCase().includes(searchStr.toLowerCase());
|
|
114
|
+
};
|
|
115
|
+
var startsWith = (str, searchStr, caseSensitive = false) => {
|
|
116
|
+
if (caseSensitive) {
|
|
117
|
+
return str.startsWith(searchStr);
|
|
118
|
+
}
|
|
119
|
+
return str.toLowerCase().startsWith(searchStr.toLowerCase());
|
|
120
|
+
};
|
|
121
|
+
var endsWith = (str, searchStr, caseSensitive = false) => {
|
|
122
|
+
if (caseSensitive) {
|
|
123
|
+
return str.endsWith(searchStr);
|
|
124
|
+
}
|
|
125
|
+
return str.toLowerCase().endsWith(searchStr.toLowerCase());
|
|
126
|
+
};
|
|
127
|
+
var padStart = (str, length, padChar = " ") => {
|
|
128
|
+
return str.padStart(length, padChar);
|
|
129
|
+
};
|
|
130
|
+
var padEnd = (str, length, padChar = " ") => {
|
|
131
|
+
if (str.length >= length) return str;
|
|
132
|
+
return str + padChar.repeat(length - str.length);
|
|
133
|
+
};
|
|
134
|
+
var trim = (str) => {
|
|
135
|
+
return str.trim();
|
|
136
|
+
};
|
|
137
|
+
var trimStart = (str) => {
|
|
138
|
+
return str.trimStart();
|
|
139
|
+
};
|
|
140
|
+
var trimEnd = (str) => {
|
|
141
|
+
return str.trimEnd();
|
|
142
|
+
};
|
|
143
|
+
var reverseString = (str) => {
|
|
144
|
+
return str.split("").reverse().join("");
|
|
145
|
+
};
|
|
146
|
+
var repeatString = (str, times) => {
|
|
147
|
+
return str.repeat(times);
|
|
148
|
+
};
|
|
149
|
+
var replaceAllOccurrences = (str, searchStr, replaceStr) => {
|
|
150
|
+
return str.split(searchStr).join(replaceStr);
|
|
151
|
+
};
|
|
152
|
+
var countOccurrences = (str, searchStr) => {
|
|
153
|
+
if (!searchStr) return 0;
|
|
154
|
+
let count = 0;
|
|
155
|
+
let position = 0;
|
|
156
|
+
while ((position = str.indexOf(searchStr, position)) !== -1) {
|
|
157
|
+
count++;
|
|
158
|
+
position += 1;
|
|
159
|
+
}
|
|
160
|
+
return count;
|
|
161
|
+
};
|
|
162
|
+
var isEmpty = (str) => {
|
|
163
|
+
return !str || str.trim().length === 0;
|
|
164
|
+
};
|
|
165
|
+
var isEmail = (str) => {
|
|
166
|
+
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
167
|
+
return re.test(str.toLowerCase());
|
|
168
|
+
};
|
|
169
|
+
var toUrlSlug = (str) => {
|
|
170
|
+
return str.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
171
|
+
};
|
|
172
|
+
var removeAccents = (str) => {
|
|
173
|
+
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
174
|
+
};
|
|
175
|
+
var escapeHtmlChars = (str) => {
|
|
176
|
+
const htmlEscapes = {
|
|
177
|
+
"&": "&",
|
|
178
|
+
"<": "<",
|
|
179
|
+
">": ">",
|
|
180
|
+
'"': """,
|
|
181
|
+
"'": "'"
|
|
182
|
+
};
|
|
183
|
+
return str.replace(/[&<>"']/g, (match) => htmlEscapes[match]);
|
|
184
|
+
};
|
|
185
|
+
var unescapeHtmlChars = (str) => {
|
|
186
|
+
const htmlUnescapes = {
|
|
187
|
+
"&": "&",
|
|
188
|
+
"<": "<",
|
|
189
|
+
">": ">",
|
|
190
|
+
""": '"',
|
|
191
|
+
"'": "'"
|
|
192
|
+
};
|
|
193
|
+
return str.replace(/&|<|>|"|'/g, (match) => htmlUnescapes[match]);
|
|
194
|
+
};
|
|
195
|
+
function matchPathPattern(path, pattern) {
|
|
196
|
+
if (!path || typeof path !== "string" || !pattern || typeof pattern !== "string") {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
if (path === pattern) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
if (!pattern.includes("*")) {
|
|
203
|
+
return path === pattern;
|
|
204
|
+
}
|
|
205
|
+
const regexPattern = pattern.split(".").map((segment) => {
|
|
206
|
+
if (segment === "*") {
|
|
207
|
+
return "[^.]+";
|
|
208
|
+
}
|
|
209
|
+
return segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
210
|
+
}).join("\\.");
|
|
211
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
212
|
+
return regex.test(path);
|
|
213
|
+
}
|
|
214
|
+
function envKeyToPath(envKey, prefix = "NX") {
|
|
215
|
+
if (!envKey || typeof envKey !== "string") {
|
|
216
|
+
return "";
|
|
217
|
+
}
|
|
218
|
+
let key = envKey.trim();
|
|
219
|
+
if (prefix && key.startsWith(`${prefix}_`)) {
|
|
220
|
+
key = key.substring(prefix.length + 1);
|
|
221
|
+
}
|
|
222
|
+
return key.toLowerCase().replace(/_/g, ".");
|
|
223
|
+
}
|
|
224
|
+
function pathToEnvKey(path, prefix = "NX") {
|
|
225
|
+
if (!path || typeof path !== "string") {
|
|
226
|
+
return "";
|
|
227
|
+
}
|
|
228
|
+
const key = path.trim().toUpperCase().replace(/\./g, "_");
|
|
229
|
+
if (key.length === 0) {
|
|
230
|
+
return "";
|
|
231
|
+
}
|
|
232
|
+
if (prefix && prefix.length > 0) {
|
|
233
|
+
return `${prefix.toUpperCase()}_${key}`;
|
|
234
|
+
}
|
|
235
|
+
return key;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export {
|
|
239
|
+
sanitizeString,
|
|
240
|
+
cleanJsonChars,
|
|
241
|
+
truncateString,
|
|
242
|
+
unescapeUnicode,
|
|
243
|
+
ensureEndsWith,
|
|
244
|
+
stripFromEnd,
|
|
245
|
+
ensureStartsWith,
|
|
246
|
+
stripFromStart,
|
|
247
|
+
toLowerCase,
|
|
248
|
+
toUpperCase,
|
|
249
|
+
capitalizeFirst,
|
|
250
|
+
capitalizeEachWord,
|
|
251
|
+
toCamelCase,
|
|
252
|
+
toSnakeCase,
|
|
253
|
+
toKebabCase,
|
|
254
|
+
toPascalCase,
|
|
255
|
+
contains,
|
|
256
|
+
startsWith,
|
|
257
|
+
endsWith,
|
|
258
|
+
padStart,
|
|
259
|
+
padEnd,
|
|
260
|
+
trim,
|
|
261
|
+
trimStart,
|
|
262
|
+
trimEnd,
|
|
263
|
+
reverseString,
|
|
264
|
+
repeatString,
|
|
265
|
+
replaceAllOccurrences,
|
|
266
|
+
countOccurrences,
|
|
267
|
+
isEmpty,
|
|
268
|
+
isEmail,
|
|
269
|
+
toUrlSlug,
|
|
270
|
+
removeAccents,
|
|
271
|
+
escapeHtmlChars,
|
|
272
|
+
unescapeHtmlChars,
|
|
273
|
+
matchPathPattern,
|
|
274
|
+
envKeyToPath,
|
|
275
|
+
pathToEnvKey,
|
|
276
|
+
strings_exports
|
|
277
|
+
};
|
|
278
|
+
//# sourceMappingURL=chunk-WJA4JDMZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/strings.ts"],"sourcesContent":["/**\n * String manipulation utilities\n * Consolidated from primitives/string module\n */\n\n/**\n * Sanitizes a string by removing special characters and converting to lowercase with dashes\n *\n * Produces clean, URL-friendly strings by removing special characters and normalizing whitespace.\n * Useful for slugs, IDs, CSS class names, and filename generation.\n *\n * Transformation rules:\n * 1. Remove all non-alphanumeric characters except spaces\n * 2. Remove additional punctuation (`~!@#$%^&*()_|+-=?;:'\",.<>{}[]\\\\/`)\n * 3. Replace spaces with dashes\n * 4. Convert to lowercase\n *\n * @param instr - Input string to sanitize (null returns empty string)\n * @returns Sanitized string in lowercase with dashes replacing spaces\n *\n * @example\n * ```typescript\n * // Basic sanitization\n * sanitizeString('Hello World!') // 'hello-world'\n * sanitizeString('user@example.com') // 'userexamplecom'\n * sanitizeString('Price: $19.99') // 'price-1999'\n *\n * // Remove accents and special chars\n * sanitizeString('Café Münchën') // 'caf-mnchen'\n * sanitizeString('北京 2024') // '2024'\n *\n * // Multiple spaces/dashes\n * sanitizeString('hello world') // 'hello---world'\n * sanitizeString('foo-bar_baz') // 'foo-barbaz'\n *\n * // Edge cases\n * sanitizeString(null) // ''\n * sanitizeString('') // ''\n * sanitizeString(' ') // '---'\n * sanitizeString('123') // '123'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate CSS class names from user input\n * function generateClassName(userInput: string): string {\n * return `custom-${sanitizeString(userInput)}`\n * }\n *\n * generateClassName('My Component!') // 'custom-my-component'\n * generateClassName('Button (Primary)') // 'custom-button-primary'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Clean product names for file export\n * const products = [\n * { name: 'T-Shirt (Blue)', sku: 'TS-001' },\n * { name: 'Shoes & Accessories', sku: 'SH-042' }\n * ]\n *\n * products.forEach(p => {\n * const filename = `${sanitizeString(p.name)}-${p.sku}.json`\n * console.log(filename)\n * // 't-shirt-blue-TS-001.json'\n * // 'shoes-accessories-SH-042.json'\n * })\n * ```\n *\n * @see {@link toUrlSlug} for more sophisticated URL slug generation with accent removal\n * @see {@link toKebabCase} for case conversion to kebab-case\n */\nexport const sanitizeString = (instr: string | null): string => {\n if (!instr) return ''\n return addDash(instr.replace(/[^a-zA-Z0-9 ]/g, '')).toLowerCase()\n}\n\nfunction addDash(str: string): string {\n const str2 = str.replace(/[`~!@#$%^&*()_|+\\-=?;:'\",.<>{}[\\]\\\\/]/gi, '')\n return str2.replace(/ /g, '-')\n}\n\n/**\n * Removes JSON-like characters and HTML entities from a string\n * @param texto Input string to clean\n * @returns String with JSON characters and HTML entities removed\n */\nexport const cleanJsonChars = (texto: string | null): string => {\n if (!texto) return ''\n return texto.replace(/({|}|&|amp;|gt;)/g, '')\n}\n\n/**\n * Truncates a string to a specified maximum length with optional suffix\n *\n * Shortens long strings for display in UI components, tables, previews, and tooltips.\n * Preserves readability while fitting space constraints.\n *\n * Behavior:\n * - If string length ≤ maxlength: returns original string unchanged\n * - If string length > maxlength: returns `str.substring(0, maxlength) + suffix`\n * - Suffix is included in addition to maxlength (not counted within limit)\n * - Null/empty strings return empty string\n *\n * @param str - String to truncate (null/empty returns '')\n * @param maxlength - Maximum length before truncation (default: 80 characters)\n * @param suffix - Suffix to append when truncated (default: '...')\n * @returns Truncated string with suffix if needed, original string if shorter\n *\n * @example\n * ```typescript\n * // Basic truncation (default 80 chars)\n * truncateString('Short text') // 'Short text' (no truncation)\n *\n * const long = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'\n * truncateString(long, 20) // 'Lorem ipsum dolor si...' (20 + 3 = 23 chars total)\n *\n * // Custom max length\n * truncateString('Hello World', 5) // 'Hello...'\n * truncateString('Hello World', 11) // 'Hello World' (no truncation)\n *\n * // Custom suffix\n * truncateString('Long text here', 8, '…') // 'Long tex…'\n * truncateString('Long text here', 8, ' [more]') // 'Long tex [more]'\n * truncateString('Long text here', 8, '') // 'Long tex' (no suffix)\n *\n * // Edge cases\n * truncateString('', 10) // ''\n * truncateString(null as any, 10) // '' (graceful handling)\n * truncateString('Hi', 0) // '...' (edge case: 0 maxlength)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Truncate product descriptions in table\n * const products = [\n * { name: 'Laptop', description: 'High-performance laptop with 16GB RAM and 512GB SSD' },\n * { name: 'Mouse', description: 'Wireless ergonomic mouse' }\n * ]\n *\n * products.forEach(p => {\n * console.log(`${p.name}: ${truncateString(p.description, 30)}`)\n * })\n * // Output:\n * // Laptop: High-performance laptop with 1...\n * // Mouse: Wireless ergonomic mouse\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Truncate user comments for preview\n * function renderCommentPreview(comment: string): string {\n * return `<div class=\"preview\">${truncateString(comment, 100)}</div>`\n * }\n *\n * renderCommentPreview('This is a very long comment that needs to be shortened for display...')\n * // '<div class=\"preview\">This is a very long comment that needs to be shortened for display in the UI without taking too m...</div>'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Email subject line truncation\n * function formatEmailSubject(subject: string): string {\n * // Gmail displays ~60 chars on desktop\n * return truncateString(subject, 60, '…')\n * }\n *\n * formatEmailSubject('Re: Important meeting about Q4 planning and budget review')\n * // 'Re: Important meeting about Q4 planning and budget review' (fits)\n *\n * formatEmailSubject('Re: [URGENT] Critical system outage affecting all production servers and databases')\n * // 'Re: [URGENT] Critical system outage affecting all production…'\n * ```\n *\n * @see {@link isEmpty} for checking empty strings\n */\nexport const truncateString = (str: string, maxlength = 80, suffix = '...'): string => {\n if (!str) return ''\n return str.length > maxlength ? str.substring(0, maxlength) + suffix : str\n}\n\n/**\n * Converts Unicode escape sequences to their actual characters\n * @param input String containing Unicode escape sequences\n * @returns String with escape sequences converted to actual characters\n */\nexport const unescapeUnicode = (input: string): string => {\n return JSON.stringify(JSON.parse(`\"${input}\"`)).replace(/^\"(.*)\"$/, '$1')\n}\n\n/**\n * Ensures a string ends with a specific suffix, adding it if not present\n * @param str Input string to check\n * @param trailing Suffix string to ensure is present at the end\n * @returns String guaranteed to end with the trailing string\n */\nexport const ensureEndsWith = (str: string, trailing: string): string => {\n return str.endsWith(trailing) ? str : `${str}${trailing}`\n}\n\n/**\n * Removes a specific suffix from the end of a string if present\n * @param str Input string to process\n * @param trailing Suffix string to remove from the end\n * @returns String with trailing suffix removed, original if suffix not found\n */\nexport const stripFromEnd = (str: string, trailing: string): string => {\n return str.endsWith(trailing) ? str.slice(0, -1 * trailing.length) : str\n}\n\n/**\n * Ensures a string starts with a specific prefix, adding it if not present\n * @param str Input string to check\n * @param leading Prefix string to ensure is present at the start\n * @returns String guaranteed to start with the leading string\n */\nexport const ensureStartsWith = (str: string, leading: string): string => {\n return str.startsWith(leading) ? str : `${leading}${str}`\n}\n\n/**\n * Removes a specific prefix from the start of a string if present\n * @param str Input string to process\n * @param leading Prefix string to remove from the start\n * @returns String with leading prefix removed, original if prefix not found\n */\nexport const stripFromStart = (str: string, leading: string): string => {\n return str.startsWith(leading) ? str.slice(leading.length) : str\n}\n\n/**\n * Converts all characters in a string to lowercase\n * @param str Input string to convert\n * @returns String with all characters in lowercase\n */\nexport const toLowerCase = (str: string): string => {\n return str.toLowerCase()\n}\n\n/**\n * Converts all characters in a string to uppercase\n * @param str Input string to convert\n * @returns String with all characters in uppercase\n */\nexport const toUpperCase = (str: string): string => {\n return str.toUpperCase()\n}\n\n/**\n * Capitalizes the first letter of a string and lowercases the rest\n * @param str Input string to capitalize\n * @returns String with first letter capitalized and rest in lowercase\n */\nexport const capitalizeFirst = (str: string): string => {\n if (!str) return ''\n return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase()\n}\n\n/**\n * Capitalizes the first letter of each word in a string\n * @param str Input string with words separated by spaces\n * @returns String with each word's first letter capitalized\n */\nexport const capitalizeEachWord = (str: string): string => {\n if (!str) return ''\n return str\n .split(' ')\n .map(word => capitalizeFirst(word))\n .join(' ')\n}\n\n/**\n * Converts a string to camelCase format\n *\n * Transforms strings from any case convention (kebab-case, snake_case, PascalCase, spaces)\n * to camelCase following JavaScript/TypeScript naming conventions.\n *\n * Algorithm:\n * 1. Detect word boundaries (spaces, hyphens, underscores, case transitions)\n * 2. Split into words and normalize to lowercase\n * 3. First word: keep lowercase\n * 4. Remaining words: capitalize first letter\n * 5. Join without separators\n *\n * Format: `firstWordSecondWordThirdWord`\n *\n * @param str - Input string in any case format (empty returns '')\n * @returns String in camelCase format\n *\n * @example\n * ```typescript\n * // From kebab-case\n * toCamelCase('hello-world') // 'helloWorld'\n * toCamelCase('user-profile-page') // 'userProfilePage'\n *\n * // From snake_case\n * toCamelCase('hello_world') // 'helloWorld'\n * toCamelCase('user_first_name') // 'userFirstName'\n *\n * // From PascalCase\n * toCamelCase('HelloWorld') // 'helloWorld'\n * toCamelCase('UserProfile') // 'userProfile'\n *\n * // From space-separated\n * toCamelCase('hello world') // 'helloWorld'\n * toCamelCase('First Name') // 'firstName'\n *\n * // Mixed formats\n * toCamelCase('hello-World_test') // 'helloWorldTest'\n * toCamelCase('API_Response-data') // 'apiResponseData'\n *\n * // Edge cases\n * toCamelCase('') // ''\n * toCamelCase('a') // 'a'\n * toCamelCase('UPPERCASE') // 'uppercase'\n * toCamelCase('123-test') // '123Test'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Convert database column names to JS properties\n * const dbColumns = ['user_id', 'first_name', 'last_name', 'created_at']\n *\n * const jsProperties = dbColumns.map(toCamelCase)\n * console.log(jsProperties)\n * // ['userId', 'firstName', 'lastName', 'createdAt']\n *\n * // Transform database row to JS object\n * function transformRow(row: Record<string, any>): Record<string, any> {\n * return Object.fromEntries(\n * Object.entries(row).map(([key, value]) => [toCamelCase(key), value])\n * )\n * }\n *\n * transformRow({ user_id: 123, first_name: 'Alice' })\n * // { userId: 123, firstName: 'Alice' }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Convert CSS property names to JS style properties\n * const cssProperties = [\n * 'background-color',\n * 'font-size',\n * 'margin-top',\n * 'border-bottom-width'\n * ]\n *\n * cssProperties.forEach(prop => {\n * const jsProp = toCamelCase(prop)\n * console.log(`${prop} → ${jsProp}`)\n * })\n * // background-color → backgroundColor\n * // font-size → fontSize\n * // margin-top → marginTop\n * // border-bottom-width → borderBottomWidth\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: API response transformation\n * interface ApiUser {\n * user_id: number\n * first_name: string\n * last_name: string\n * created_at: string\n * }\n *\n * function transformApiResponse(apiData: ApiUser) {\n * return {\n * userId: apiData.user_id,\n * firstName: apiData.first_name,\n * lastName: apiData.last_name,\n * createdAt: new Date(apiData.created_at)\n * }\n * }\n *\n * // Or generically:\n * function autoTransformKeys<T extends Record<string, any>>(obj: T) {\n * return Object.fromEntries(\n * Object.entries(obj).map(([k, v]) => [toCamelCase(k), v])\n * )\n * }\n * ```\n *\n * @see {@link toPascalCase} for PascalCase conversion (first letter uppercase)\n * @see {@link toSnakeCase} for snake_case conversion\n * @see {@link toKebabCase} for kebab-case conversion\n */\nexport const toCamelCase = (str: string): string => {\n if (!str) return ''\n\n // Split by word boundaries (spaces, hyphens, underscores, camelCase boundaries)\n const words = str\n .replace(/([a-z])([A-Z])/g, '$1 $2') // Split camelCase\n .split(/[-_\\s]+/) // Split by separators\n .filter(word => word.length > 0)\n .map(word => word.toLowerCase())\n\n if (words.length === 0) return ''\n\n // First word lowercase, rest capitalize first letter\n return (\n words[0] +\n words\n .slice(1)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join('')\n )\n}\n\n/**\n * Converts a string to snake_case format\n *\n * Transforms strings from any case convention to snake_case, commonly used in\n * Python, Ruby, database column names, and environment variables.\n *\n * Algorithm:\n * 1. Replace non-word characters with spaces\n * 2. Split on spaces and camelCase boundaries\n * 3. Convert all words to lowercase\n * 4. Join with underscores\n *\n * Format: `first_word_second_word_third_word`\n *\n * @param str - Input string in any case format (empty returns '')\n * @returns String in snake_case format\n *\n * @example\n * ```typescript\n * // From camelCase\n * toSnakeCase('helloWorld') // 'hello_world'\n * toSnakeCase('userFirstName') // 'user_first_name'\n *\n * // From PascalCase\n * toSnakeCase('HelloWorld') // 'hello_world'\n * toSnakeCase('UserProfile') // 'user_profile'\n *\n * // From kebab-case\n * toSnakeCase('hello-world') // 'hello_world'\n * toSnakeCase('user-profile-page') // 'user_profile_page'\n *\n * // From space-separated\n * toSnakeCase('hello world') // 'hello_world'\n * toSnakeCase('First Name') // 'first_name'\n *\n * // Mixed formats\n * toSnakeCase('helloWorld-test') // 'hello_world_test'\n * toSnakeCase('APIResponse') // 'a_p_i_response'\n *\n * // Edge cases\n * toSnakeCase('') // ''\n * toSnakeCase('a') // 'a'\n * toSnakeCase('UPPERCASE') // 'u_p_p_e_r_c_a_s_e'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Convert JS properties to database column names\n * const jsObject = {\n * userId: 123,\n * firstName: 'Alice',\n * lastName: 'Smith',\n * createdAt: new Date()\n * }\n *\n * const dbColumns = Object.keys(jsObject).map(toSnakeCase)\n * console.log(dbColumns)\n * // ['user_id', 'first_name', 'last_name', 'created_at']\n *\n * // Generate SQL INSERT statement\n * const columns = Object.keys(jsObject).map(toSnakeCase).join(', ')\n * const sql = `INSERT INTO users (${columns}) VALUES (?)`\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Environment variable generation\n * const configKeys = ['databaseHost', 'databasePort', 'apiBaseUrl']\n *\n * configKeys.forEach(key => {\n * const envVar = toSnakeCase(key).toUpperCase()\n * console.log(`${envVar}=value`)\n * })\n * // DATABASE_HOST=value\n * // DATABASE_PORT=value\n * // API_BASE_URL=value\n * ```\n *\n * @see {@link toCamelCase} for camelCase conversion\n * @see {@link toKebabCase} for kebab-case conversion\n * @see {@link toPascalCase} for PascalCase conversion\n */\nexport const toSnakeCase = (str: string): string => {\n if (!str) return ''\n return str\n .replace(/\\W+/g, ' ')\n .split(/ |\\B(?=[A-Z])/)\n .map(word => word.toLowerCase())\n .join('_')\n}\n\n/**\n * Converts a string to kebab-case format\n *\n * Transforms strings from any case convention to kebab-case (also called dash-case or hyphen-case).\n * Widely used in URLs, HTML attributes, CSS classes, and file names.\n *\n * Algorithm:\n * 1. Replace underscores and spaces with hyphens\n * 2. Insert hyphens before uppercase letters (handle camelCase)\n * 3. Convert all to lowercase\n * 4. Remove non-alphanumeric characters (except hyphens)\n * 5. Collapse multiple consecutive hyphens\n * 6. Remove leading/trailing hyphens\n *\n * Format: `first-word-second-word-third-word`\n *\n * @param str - Input string in any case format (empty returns '')\n * @returns String in kebab-case format\n *\n * @example\n * ```typescript\n * // From camelCase\n * toKebabCase('helloWorld') // 'hello-world'\n * toKebabCase('userFirstName') // 'user-first-name'\n *\n * // From PascalCase\n * toKebabCase('HelloWorld') // 'hello-world'\n * toKebabCase('UserProfile') // 'user-profile'\n *\n * // From snake_case\n * toKebabCase('hello_world') // 'hello-world'\n * toKebabCase('user_first_name') // 'user-first-name'\n *\n * // From space-separated\n * toKebabCase('hello world') // 'hello-world'\n * toKebabCase('First Name') // 'first-name'\n *\n * // Mixed formats\n * toKebabCase('helloWorld_test') // 'hello-world-test'\n * toKebabCase('API-Response') // 'api-response'\n *\n * // Edge cases\n * toKebabCase('') // ''\n * toKebabCase('a') // 'a'\n * toKebabCase('UPPERCASE') // 'uppercase'\n * toKebabCase('123Test') // '123-test'\n * toKebabCase('--multiple--dashes--') // 'multiple-dashes'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate URL slugs from page titles\n * const pageTitle = 'My Awesome Blog Post'\n * const urlSlug = toKebabCase(pageTitle)\n * const url = `https://example.com/blog/${urlSlug}`\n * // https://example.com/blog/my-awesome-blog-post\n *\n * // Multiple pages\n * const pages = [\n * { title: 'Getting Started', component: 'GettingStarted' },\n * { title: 'API Reference', component: 'ApiReference' },\n * { title: 'Best Practices', component: 'BestPractices' }\n * ]\n *\n * const routes = pages.map(p => ({\n * path: `/${toKebabCase(p.title)}`,\n * component: p.component\n * }))\n * // [\n * // { path: '/getting-started', component: 'GettingStarted' },\n * // { path: '/api-reference', component: 'ApiReference' },\n * // { path: '/best-practices', component: 'BestPractices' }\n * // ]\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: CSS class name generation\n * function generateClassName(componentName: string, modifier?: string): string {\n * const base = `component-${toKebabCase(componentName)}`\n * return modifier ? `${base}--${toKebabCase(modifier)}` : base\n * }\n *\n * generateClassName('UserProfile') // 'component-user-profile'\n * generateClassName('UserProfile', 'isActive') // 'component-user-profile--is-active'\n * generateClassName('ButtonPrimary', 'largeSize') // 'component-button-primary--large-size'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: HTML attribute generation\n * function generateDataAttribute(key: string, value: string): string {\n * return `data-${toKebabCase(key)}=\"${value}\"`\n * }\n *\n * generateDataAttribute('userId', '123') // 'data-user-id=\"123\"'\n * generateDataAttribute('testEnvironment', 'staging') // 'data-test-environment=\"staging\"'\n * ```\n *\n * @see {@link toCamelCase} for camelCase conversion\n * @see {@link toSnakeCase} for snake_case conversion\n * @see {@link toPascalCase} for PascalCase conversion\n * @see {@link toUrlSlug} for URL-safe slug generation with accent removal\n */\nexport const toKebabCase = (str: string): string => {\n if (!str) return ''\n return str\n .replace(/[_\\s]+/g, '-')\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2') // Handle numbers before capitals too\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n}\n\n/**\n * Converts a string to PascalCase format\n *\n * Transforms strings from any case convention to PascalCase (also called UpperCamelCase).\n * Commonly used for class names, component names, type names, and constructor functions\n * in JavaScript/TypeScript.\n *\n * Algorithm:\n * 1. Split on word boundaries (hyphens, underscores, spaces)\n * 2. Capitalize first letter of each word\n * 3. Join without separators\n * 4. Ensure first character is uppercase\n *\n * Format: `FirstWordSecondWordThirdWord`\n *\n * @param str - Input string in any case format (empty returns '')\n * @returns String in PascalCase format\n *\n * @example\n * ```typescript\n * // From camelCase\n * toPascalCase('helloWorld') // 'HelloWorld'\n * toPascalCase('userFirstName') // 'UserFirstName'\n *\n * // From kebab-case\n * toPascalCase('hello-world') // 'HelloWorld'\n * toPascalCase('user-profile-page') // 'UserProfilePage'\n *\n * // From snake_case\n * toPascalCase('hello_world') // 'HelloWorld'\n * toPascalCase('user_first_name') // 'UserFirstName'\n *\n * // From space-separated\n * toPascalCase('hello world') // 'HelloWorld'\n * toPascalCase('first name') // 'FirstName'\n *\n * // Mixed formats\n * toPascalCase('hello-World_test') // 'HelloWorldTest'\n * toPascalCase('api_response-data') // 'ApiResponseData'\n *\n * // Edge cases\n * toPascalCase('') // ''\n * toPascalCase('a') // 'A'\n * toPascalCase('UPPERCASE') // 'UPPERCASE'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate React component names\n * const componentNames = ['user-profile', 'navigation-bar', 'footer-links']\n *\n * componentNames.forEach(name => {\n * const pascalName = toPascalCase(name)\n * console.log(`export function ${pascalName}() { ... }`)\n * })\n * // export function UserProfile() { ... }\n * // export function NavigationBar() { ... }\n * // export function FooterLinks() { ... }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: TypeScript type name generation\n * interface GenerateTypeOptions {\n * name: string\n * fields: Array<{ name: string; type: string }>\n * }\n *\n * function generateTypeDefinition(options: GenerateTypeOptions): string {\n * const typeName = toPascalCase(options.name)\n * const fields = options.fields\n * .map(f => ` ${f.name}: ${f.type}`)\n * .join('\\n')\n *\n * return `interface ${typeName} {\\n${fields}\\n}`\n * }\n *\n * generateTypeDefinition({\n * name: 'user-profile',\n * fields: [\n * { name: 'userId', type: 'number' },\n * { name: 'name', type: 'string' }\n * ]\n * })\n * // interface UserProfile {\n * // userId: number\n * // name: string\n * // }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Class name generation for dynamic imports\n * async function loadService(serviceName: string) {\n * const className = toPascalCase(serviceName)\n * const module = await import(`./services/${serviceName}`)\n * return new module[className]()\n * }\n *\n * await loadService('user-service') // new UserService()\n * await loadService('payment-gateway') // new PaymentGateway()\n * ```\n *\n * @see {@link toCamelCase} for camelCase conversion (first letter lowercase)\n * @see {@link toSnakeCase} for snake_case conversion\n * @see {@link toKebabCase} for kebab-case conversion\n */\nexport const toPascalCase = (str: string): string => {\n if (!str) return ''\n return str\n .replace(/[-_\\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))\n .replace(/^./, char => char.toUpperCase())\n}\n\n/**\n * Checks if a string contains another string with optional case sensitivity\n * @param str String to search within\n * @param searchStr Substring to search for\n * @param caseSensitive Whether to perform case-sensitive search (default: false)\n * @returns True if the string contains the search string, false otherwise\n */\nexport const contains = (str: string, searchStr: string, caseSensitive = false): boolean => {\n if (caseSensitive) {\n return str.includes(searchStr)\n }\n return str.toLowerCase().includes(searchStr.toLowerCase())\n}\n\n/**\n * Checks if a string starts with another string with optional case sensitivity\n * @param str String to check\n * @param searchStr Prefix to search for\n * @param caseSensitive Whether to perform case-sensitive search (default: false)\n * @returns True if the string starts with the search string, false otherwise\n */\nexport const startsWith = (str: string, searchStr: string, caseSensitive = false): boolean => {\n if (caseSensitive) {\n return str.startsWith(searchStr)\n }\n return str.toLowerCase().startsWith(searchStr.toLowerCase())\n}\n\n/**\n * Checks if a string ends with another string with optional case sensitivity\n * @param str String to check\n * @param searchStr Suffix to search for\n * @param caseSensitive Whether to perform case-sensitive search (default: false)\n * @returns True if the string ends with the search string, false otherwise\n */\nexport const endsWith = (str: string, searchStr: string, caseSensitive = false): boolean => {\n if (caseSensitive) {\n return str.endsWith(searchStr)\n }\n return str.toLowerCase().endsWith(searchStr.toLowerCase())\n}\n\n/**\n * Pads a string at the start (left) with a specific character to reach target length\n * @param str String to pad\n * @param length Target length for the resulting string\n * @param padChar Character to use for padding (default: space)\n * @returns String padded to target length, original if already longer\n */\nexport const padStart = (str: string, length: number, padChar = ' '): string => {\n return str.padStart(length, padChar)\n}\n\n/**\n * Pads a string at the end (right) with a specific character to reach target length\n * @param str String to pad\n * @param length Target length for the resulting string\n * @param padChar Character to use for padding (default: space)\n * @returns String padded to target length, original if already longer\n */\nexport const padEnd = (str: string, length: number, padChar = ' '): string => {\n if (str.length >= length) return str\n return str + padChar.repeat(length - str.length)\n}\n\n/**\n * Removes leading and trailing whitespace from a string\n * @param str String to trim\n * @returns String with leading and trailing whitespace removed\n */\nexport const trim = (str: string): string => {\n return str.trim()\n}\n\n/**\n * Removes leading (start) whitespace from a string\n * @param str String to trim\n * @returns String with leading whitespace removed\n */\nexport const trimStart = (str: string): string => {\n return str.trimStart()\n}\n\n/**\n * Removes trailing (end) whitespace from a string\n * @param str String to trim\n * @returns String with trailing whitespace removed\n */\nexport const trimEnd = (str: string): string => {\n return str.trimEnd()\n}\n\n/**\n * Reverses the character order in a string\n * @param str String to reverse\n * @returns String with characters in reverse order\n */\nexport const reverseString = (str: string): string => {\n return str.split('').reverse().join('')\n}\n\n/**\n * Repeats a string a specified number of times\n * @param str String to repeat\n * @param times Number of times to repeat the string\n * @returns String repeated the specified number of times\n */\nexport const repeatString = (str: string, times: number): string => {\n return str.repeat(times)\n}\n\n/**\n * Replaces all occurrences of a substring with a replacement string\n * @param str String to search in\n * @param searchStr Substring to find and replace\n * @param replaceStr String to replace each occurrence with\n * @returns String with all occurrences replaced\n */\nexport const replaceAllOccurrences = (\n str: string,\n searchStr: string,\n replaceStr: string\n): string => {\n return str.split(searchStr).join(replaceStr)\n}\n\n/**\n * Counts the number of occurrences of a substring within a string\n * @param str String to search within\n * @param searchStr Substring to count occurrences of\n * @returns Number of times the substring appears in the string\n */\nexport const countOccurrences = (str: string, searchStr: string): number => {\n if (!searchStr) return 0\n let count = 0\n let position = 0\n\n while ((position = str.indexOf(searchStr, position)) !== -1) {\n count++\n position += 1 // Move only 1 position to allow overlapping matches\n }\n\n return count\n}\n\n/**\n * Checks if a string is empty, null, undefined, or contains only whitespace\n * @param str String to check (can be null or undefined)\n * @returns True if string is empty/null/undefined or only whitespace, false otherwise\n */\nexport const isEmpty = (str: string | null | undefined): boolean => {\n return !str || str.trim().length === 0\n}\n\n/**\n * Validates if a string is a properly formatted email address\n * @param str String to validate as email\n * @returns True if string matches email format, false otherwise\n */\nexport const isEmail = (str: string): boolean => {\n const re =\n /^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n return re.test(str.toLowerCase())\n}\n\n/**\n * Generates a URL-friendly slug from a string\n *\n * Creates clean, SEO-friendly URL slugs by normalizing strings for use in web addresses.\n * Removes special characters, normalizes whitespace, and converts to lowercase with hyphens.\n *\n * Algorithm:\n * 1. Convert to lowercase\n * 2. Trim leading/trailing whitespace\n * 3. Remove non-word characters (except spaces, hyphens, underscores)\n * 4. Replace spaces/underscores/multiple-hyphens with single hyphen\n * 5. Remove leading/trailing hyphens\n *\n * Format: `url-friendly-slug-text`\n *\n * ⚠️ NOTE: Does NOT remove accents. For accent removal, use {@link removeAccents} first.\n *\n * @param str - String to convert to URL slug (special chars removed)\n * @returns URL-friendly slug string in lowercase with hyphens\n *\n * @example\n * ```typescript\n * // Basic slug generation\n * toUrlSlug('Hello World') // 'hello-world'\n * toUrlSlug('My Blog Post Title') // 'my-blog-post-title'\n *\n * // Remove special characters\n * toUrlSlug('User: John Doe!') // 'user-john-doe'\n * toUrlSlug('Price: $19.99') // 'price-1999'\n * toUrlSlug('Hello @ World #2024') // 'hello-world-2024'\n *\n * // Normalize whitespace and separators\n * toUrlSlug('hello world') // 'hello-world'\n * toUrlSlug('hello_world_test') // 'hello-world-test'\n * toUrlSlug('---multiple---dashes---') // 'multiple-dashes'\n *\n * // Preserve numbers and hyphens\n * toUrlSlug('Article 123') // 'article-123'\n * toUrlSlug('ES6-Features') // 'es6-features'\n *\n * // Edge cases\n * toUrlSlug('') // ''\n * toUrlSlug(' ') // ''\n * toUrlSlug('123') // '123'\n * toUrlSlug('a-b-c') // 'a-b-c'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate blog post URL from title\n * function createBlogPostUrl(title: string, id: number): string {\n * const slug = toUrlSlug(title)\n * return `/blog/${id}/${slug}`\n * }\n *\n * createBlogPostUrl('10 Tips for TypeScript', 42)\n * // '/blog/42/10-tips-for-typescript'\n *\n * createBlogPostUrl('Getting Started with React!', 1)\n * // '/blog/1/getting-started-with-react'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate product URLs for e-commerce\n * interface Product {\n * id: string\n * name: string\n * category: string\n * }\n *\n * function getProductUrl(product: Product): string {\n * const categorySlug = toUrlSlug(product.category)\n * const nameSlug = toUrlSlug(product.name)\n * return `/products/${categorySlug}/${product.id}/${nameSlug}`\n * }\n *\n * getProductUrl({\n * id: 'SKU-123',\n * name: 'Wireless Mouse (Ergonomic)',\n * category: 'Computer Accessories'\n * })\n * // '/products/computer-accessories/SKU-123/wireless-mouse-ergonomic'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: SEO-friendly URLs with accent handling\n * function createSeoUrl(title: string): string {\n * // Remove accents BEFORE slug generation for better URL compatibility\n * return toUrlSlug(removeAccents(title))\n * }\n *\n * createSeoUrl('Café Münchën 2024')\n * // 'cafe-munchen-2024' (accents removed)\n *\n * // Without accent removal:\n * toUrlSlug('Café Münchën 2024')\n * // 'caf-mnchen-2024' (accents become invalid chars, get removed)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate unique slugs for duplicate titles\n * const existingSlugs = new Set(['hello-world', 'hello-world-1'])\n *\n * function generateUniqueSlug(title: string): string {\n * let slug = toUrlSlug(title)\n * let counter = 1\n *\n * while (existingSlugs.has(slug)) {\n * slug = `${toUrlSlug(title)}-${counter}`\n * counter++\n * }\n *\n * existingSlugs.add(slug)\n * return slug\n * }\n *\n * generateUniqueSlug('Hello World') // 'hello-world-2' (avoiding existing)\n * generateUniqueSlug('New Article') // 'new-article'\n * ```\n *\n * @see {@link toKebabCase} for case conversion to kebab-case\n * @see {@link removeAccents} for removing accents before slug generation\n * @see {@link sanitizeString} for basic string sanitization\n */\nexport const toUrlSlug = (str: string): string => {\n return str\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/[\\s_-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n\n/**\n * Removes accents and diacritical marks from characters in a string\n *\n * Uses Unicode normalization (NFD) to decompose accented characters, then removes\n * combining diacritical marks. Essential for search, sorting, URL slugs, and ASCII compatibility.\n *\n * Algorithm:\n * 1. Normalize string to NFD (Normalization Form Decomposed)\n * 2. Remove Unicode combining diacritical marks (U+0300–U+036F)\n * 3. Return ASCII-compatible base characters\n *\n * Supported diacritics: acute (´), grave (`), circumflex (^), tilde (~), umlaut (¨),\n * cedilla (¸), and many more.\n *\n * @param str - String containing accented characters (e.g., 'café', 'Münchën')\n * @returns String with accents removed to base ASCII characters (e.g., 'cafe', 'Munchen')\n *\n * @example\n * ```typescript\n * // Basic accent removal - Romance languages\n * removeAccents('café') // 'cafe'\n * removeAccents('naïve') // 'naive'\n * removeAccents('résumé') // 'resume'\n * removeAccents('à côté') // 'a cote'\n *\n * // Spanish accents\n * removeAccents('Español') // 'Espanol'\n * removeAccents('niño') // 'nino'\n * removeAccents('José María') // 'Jose Maria'\n *\n * // German umlauts\n * removeAccents('Münchën') // 'Munchen'\n * removeAccents('Köln') // 'Koln'\n * removeAccents('Zürich') // 'Zurich'\n *\n * // Portuguese\n * removeAccents('São Paulo') // 'Sao Paulo'\n * removeAccents('Brasília') // 'Brasilia'\n *\n * // French\n * removeAccents('Côte d'Ivoire') // 'Cote d'Ivoire'\n * removeAccents('Françoise') // 'Francoise'\n *\n * // Mixed\n * removeAccents('Crème brûlée') // 'Creme brulee'\n * removeAccents('Åre, Malmö') // 'Are, Malmo'\n *\n * // Edge cases\n * removeAccents('') // ''\n * removeAccents('ASCII text') // 'ASCII text' (unchanged)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Search normalization (case + accent insensitive)\n * function normalizeForSearch(text: string): string {\n * return removeAccents(text.toLowerCase().trim())\n * }\n *\n * const searchQuery = normalizeForSearch('Café') // 'cafe'\n * const productName = normalizeForSearch('CAFÉ PREMIUM') // 'cafe premium'\n *\n * if (productName.includes(searchQuery)) {\n * console.log('Match found!')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate SEO-friendly URLs\n * function createSeoUrl(title: string): string {\n * return toUrlSlug(removeAccents(title))\n * }\n *\n * createSeoUrl('Guía de Español')\n * // 'guia-de-espanol'\n *\n * createSeoUrl('Café Münchën 2024')\n * // 'cafe-munchen-2024'\n *\n * createSeoUrl('São Paulo: Best Restaurants')\n * // 'sao-paulo-best-restaurants'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Sort names alphabetically (accent-insensitive)\n * const names = ['Álvarez', 'Andersen', 'Ängel', 'Adams']\n *\n * const sorted = names.sort((a, b) =>\n * removeAccents(a).localeCompare(removeAccents(b))\n * )\n * // ['Adams', 'Álvarez', 'Andersen', 'Ängel']\n * // (sorted by: Adams, Alvarez, Andersen, Angel)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Filename sanitization\n * function sanitizeFilename(filename: string): string {\n * // Remove accents, then sanitize\n * const withoutAccents = removeAccents(filename)\n * return sanitizeString(withoutAccents)\n * }\n *\n * sanitizeFilename('Presentación_2024.pdf')\n * // 'presentacion-2024pdf'\n *\n * sanitizeFilename('Föräldrar & Barn.docx')\n * // 'foraldrar-barn-docx'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Email address generation\n * function generateEmail(firstName: string, lastName: string): string {\n * const first = removeAccents(firstName.toLowerCase())\n * const last = removeAccents(lastName.toLowerCase())\n * return `${first}.${last}@company.com`\n * }\n *\n * generateEmail('José', 'García')\n * // 'jose.garcia@company.com'\n *\n * generateEmail('François', 'Müller')\n * // 'francois.muller@company.com'\n * ```\n *\n * @see {@link toUrlSlug} for URL slug generation (combine with removeAccents for best results)\n * @see {@link sanitizeString} for string sanitization\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize String.normalize() Documentation}\n */\nexport const removeAccents = (str: string): string => {\n return str.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n}\n\n/**\n * Escapes HTML special characters to prevent XSS and display issues\n *\n * Converts dangerous HTML characters to their HTML entity equivalents, preventing\n * script injection (XSS) and ensuring proper text display in HTML contexts.\n *\n * Escaped characters:\n * - `&` → `&` (must be first to avoid double-escaping)\n * - `<` → `<` (prevents opening tags)\n * - `>` → `>` (prevents closing tags)\n * - `\"` → `"` (prevents attribute injection)\n * - `'` → `'` (prevents attribute injection)\n *\n * ⚠️ SECURITY WARNING: This provides basic XSS protection but is NOT a complete solution.\n * For production HTML sanitization, use DOMPurify or similar dedicated libraries.\n * This function is safe for:\n * - Displaying user input as plain text in HTML\n * - Escaping attribute values in HTML\n * - Simple content rendering\n *\n * NOT sufficient for:\n * - Rich HTML content (use DOMPurify)\n * - JavaScript context (use different escaping)\n * - URL context (use encodeURIComponent)\n * - CSS context (use CSS-specific escaping)\n *\n * @param str - String containing HTML characters to escape\n * @returns String with HTML characters safely escaped as HTML entities\n *\n * @example\n * ```typescript\n * // Basic escaping\n * escapeHtmlChars('<script>alert(\"XSS\")</script>')\n * // '<script>alert("XSS")</script>'\n *\n * escapeHtmlChars('5 < 10 & 10 > 5')\n * // '5 < 10 & 10 > 5'\n *\n * escapeHtmlChars('Say \"Hello\" & \\'Goodbye\\'')\n * // 'Say "Hello" & 'Goodbye''\n *\n * // Edge cases\n * escapeHtmlChars('') // ''\n * escapeHtmlChars('No special chars') // 'No special chars'\n * escapeHtmlChars('<') // '&lt;' (escapes already-escaped)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Safely display user comments in HTML\n * interface Comment {\n * author: string\n * content: string\n * }\n *\n * function renderComment(comment: Comment): string {\n * const safeAuthor = escapeHtmlChars(comment.author)\n * const safeContent = escapeHtmlChars(comment.content)\n *\n * return `\n * <div class=\"comment\">\n * <strong>${safeAuthor}</strong>\n * <p>${safeContent}</p>\n * </div>\n * `\n * }\n *\n * renderComment({\n * author: '<script>alert(\"XSS\")</script>',\n * content: 'Great post! <3'\n * })\n * // Safe HTML output with escaped tags\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate safe HTML attributes\n * function generateDataAttribute(key: string, value: string): string {\n * const safeKey = toKebabCase(key)\n * const safeValue = escapeHtmlChars(value)\n * return `data-${safeKey}=\"${safeValue}\"`\n * }\n *\n * generateDataAttribute('userInput', '<script>alert(1)</script>')\n * // 'data-user-input=\"<script>alert(1)</script>\"'\n *\n * generateDataAttribute('description', 'Product \"Premium\" & more')\n * // 'data-description=\"Product "Premium" & more\"'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Escape user search query for display\n * function displaySearchResults(query: string, results: any[]): string {\n * const safeQuery = escapeHtmlChars(query)\n *\n * return `\n * <div class=\"search-results\">\n * <h2>Results for: ${safeQuery}</h2>\n * <p>${results.length} results found</p>\n * </div>\n * `\n * }\n *\n * displaySearchResults('<img src=x onerror=alert(1)>', [])\n * // Safe display: \"Results for: <img src=x onerror=alert(1)>\"\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: CSV to HTML table with safe content\n * function csvRowToHtmlRow(cells: string[]): string {\n * const safeCells = cells.map(cell => escapeHtmlChars(cell))\n * const tdElements = safeCells.map(cell => `<td>${cell}</td>`).join('')\n * return `<tr>${tdElements}</tr>`\n * }\n *\n * csvRowToHtmlRow(['Alice', '<script>evil</script>', '25'])\n * // '<tr><td>Alice</td><td><script>evil</script></td><td>25</td></tr>'\n * ```\n *\n * @example\n * ```typescript\n * // Edge case: Preventing double-escaping\n * const userInput = 'Hello & Goodbye'\n * const escaped = escapeHtmlChars(userInput)\n * // 'Hello & Goodbye'\n *\n * const doubleEscaped = escapeHtmlChars(escaped)\n * // 'Hello &amp; Goodbye' ⚠️ Over-escaped!\n *\n * // Solution: Only escape once, track escaped state\n * interface SafeString {\n * value: string\n * isEscaped: boolean\n * }\n *\n * function safeEscape(str: string | SafeString): SafeString {\n * if (typeof str === 'object' && str.isEscaped) {\n * return str\n * }\n * const value = typeof str === 'string' ? str : str.value\n * return { value: escapeHtmlChars(value), isEscaped: true }\n * }\n * ```\n *\n * @see {@link unescapeHtmlChars} for reversing HTML entity escaping\n * @see {@link sanitizeString} for basic string sanitization\n * @see {@link https://owasp.org/www-community/attacks/xss/ OWASP XSS Prevention Cheat Sheet}\n * @see {@link https://github.com/cure53/DOMPurify DOMPurify for production HTML sanitization}\n */\nexport const escapeHtmlChars = (str: string): string => {\n const htmlEscapes: { [key: string]: string } = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n }\n return str.replace(/[&<>\"']/g, match => htmlEscapes[match])\n}\n\n/**\n * Converts HTML entities back to their original characters\n * @param str String containing HTML entities to unescape\n * @returns String with HTML entities converted back to original characters\n */\nexport const unescapeHtmlChars = (str: string): string => {\n const htmlUnescapes: { [key: string]: string } = {\n '&': '&',\n '<': '<',\n '>': '>',\n '"': '\"',\n ''': \"'\",\n }\n return str.replace(/&|<|>|"|'/g, match => htmlUnescapes[match])\n}\n\n/**\n * Checks if a path matches a wildcard pattern using dot notation\n *\n * Supports wildcards (*) to match any segment at that position.\n * Useful for configuration paths, routing, permissions, and feature flags.\n *\n * @param path - Dot-notation path to test (e.g., 'features.auth.enabled')\n * @param pattern - Pattern with optional wildcards (e.g., 'features.*', 'features.*.enabled')\n * @returns True if path matches pattern, false otherwise\n *\n * @example\n * ```typescript\n * // Exact match\n * matchPathPattern('features.auth', 'features.auth') // true\n *\n * // Wildcard at end\n * matchPathPattern('features.auth', 'features.*') // true\n * matchPathPattern('features.payments', 'features.*') // true\n * matchPathPattern('other.value', 'features.*') // false\n *\n * // Wildcard in middle\n * matchPathPattern('features.auth.enabled', 'features.*.enabled') // true\n * matchPathPattern('features.payments.enabled', 'features.*.enabled') // true\n * matchPathPattern('features.auth.disabled', 'features.*.enabled') // false\n *\n * // Multiple wildcards\n * matchPathPattern('app.features.auth.oauth', 'app.*.*.oauth') // true\n * matchPathPattern('app.features.auth.saml', 'app.*.*.oauth') // false\n *\n * // No wildcard\n * matchPathPattern('exact.path.match', 'exact.path.match') // true\n * matchPathPattern('exact.path.nomatch', 'exact.path.match') // false\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Feature flag matching\n * const enabledFeatures = [\n * 'features.auth.oauth',\n * 'features.auth.saml',\n * 'features.payments.stripe',\n * ]\n *\n * const hasAuth = enabledFeatures.some(f =>\n * matchPathPattern(f, 'features.auth.*')\n * ) // true\n *\n * const hasPayments = enabledFeatures.some(f =>\n * matchPathPattern(f, 'features.payments.*')\n * ) // true\n *\n * // Permission matching\n * const userPermissions = ['admin.users.read', 'admin.users.write']\n * const canManageUsers = userPermissions.some(p =>\n * matchPathPattern(p, 'admin.users.*')\n * ) // true\n * ```\n */\nexport function matchPathPattern(path: string, pattern: string): boolean {\n if (!path || typeof path !== 'string' || !pattern || typeof pattern !== 'string') {\n return false\n }\n\n // Exact match\n if (path === pattern) {\n return true\n }\n\n // No wildcard in pattern\n if (!pattern.includes('*')) {\n return path === pattern\n }\n\n // Convert pattern to regex\n // Escape special regex characters except *\n const regexPattern = pattern\n .split('.')\n .map(segment => {\n if (segment === '*') {\n return '[^.]+'\n }\n // Escape special regex chars\n return segment.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n })\n .join('\\\\.')\n\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(path)\n}\n\n/**\n * Converts an environment variable key to dot-notation path\n *\n * Transforms uppercase underscore-separated env var names to lowercase\n * dot-notation paths. Optionally removes a prefix.\n *\n * Common convention: ENV_VAR_NAME → env.var.name\n *\n * @param envKey - Environment variable key (e.g., 'NX_FEATURES_AUTH', 'APP_DATABASE_HOST')\n * @param prefix - Optional prefix to remove (e.g., 'NX', 'APP'). Default: 'NX'\n * @returns Dot-notation path in lowercase\n *\n * @example\n * ```typescript\n * // Default prefix (NX)\n * envKeyToPath('NX_FEATURES_AUTH') // 'features.auth'\n * envKeyToPath('NX_FEATURES_PAYMENTS') // 'features.payments'\n * envKeyToPath('NX_DATABASE_HOST') // 'database.host'\n *\n * // Custom prefix\n * envKeyToPath('APP_DATABASE_HOST', 'APP') // 'database.host'\n * envKeyToPath('MY_CONFIG_VALUE', 'MY') // 'config.value'\n *\n * // No prefix\n * envKeyToPath('DATABASE_HOST', '') // 'database.host'\n * envKeyToPath('FEATURES_AUTH', '') // 'features.auth'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Parse environment variables to config object\n * const envVars = {\n * 'APP_DATABASE_HOST': 'localhost',\n * 'APP_DATABASE_PORT': '5432',\n * 'APP_CACHE_TTL': '3600',\n * }\n *\n * const config = {}\n * Object.entries(envVars).forEach(([key, value]) => {\n * const path = envKeyToPath(key, 'APP')\n * // Use with setDeepValue: setDeepValue(config, path, value)\n * })\n * // Paths: 'database.host', 'database.port', 'cache.ttl'\n * ```\n */\nexport function envKeyToPath(envKey: string, prefix: string = 'NX'): string {\n if (!envKey || typeof envKey !== 'string') {\n return ''\n }\n\n let key = envKey.trim()\n\n // Remove prefix if provided and matches\n if (prefix && key.startsWith(`${prefix}_`)) {\n key = key.substring(prefix.length + 1)\n }\n\n // Convert to lowercase and replace underscores with dots\n return key.toLowerCase().replace(/_/g, '.')\n}\n\n/**\n * Converts a dot-notation path to environment variable key format\n *\n * Transforms lowercase dot-notation paths to uppercase underscore-separated\n * env var names. Optionally adds a prefix.\n *\n * Common convention: env.var.name → ENV_VAR_NAME\n *\n * @param path - Dot-notation path (e.g., 'features.auth', 'database.host')\n * @param prefix - Optional prefix to add (e.g., 'NX', 'APP'). Default: 'NX'\n * @returns Environment variable key in uppercase\n *\n * @example\n * ```typescript\n * // Default prefix (NX)\n * pathToEnvKey('features.auth') // 'NX_FEATURES_AUTH'\n * pathToEnvKey('features.payments') // 'NX_FEATURES_PAYMENTS'\n * pathToEnvKey('database.host') // 'NX_DATABASE_HOST'\n *\n * // Custom prefix\n * pathToEnvKey('database.host', 'APP') // 'APP_DATABASE_HOST'\n * pathToEnvKey('config.value', 'MY') // 'MY_CONFIG_VALUE'\n *\n * // No prefix\n * pathToEnvKey('database.host', '') // 'DATABASE_HOST'\n * pathToEnvKey('features.auth', '') // 'FEATURES_AUTH'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Generate .env file from config object\n * const config = {\n * database: { host: 'localhost', port: 5432 },\n * cache: { ttl: 3600 }\n * }\n *\n * const envVars = [\n * `${pathToEnvKey('database.host', 'APP')}=localhost`,\n * `${pathToEnvKey('database.port', 'APP')}=5432`,\n * `${pathToEnvKey('cache.ttl', 'APP')}=3600`,\n * ]\n * // Output:\n * // APP_DATABASE_HOST=localhost\n * // APP_DATABASE_PORT=5432\n * // APP_CACHE_TTL=3600\n * ```\n */\nexport function pathToEnvKey(path: string, prefix: string = 'NX'): string {\n if (!path || typeof path !== 'string') {\n return ''\n }\n\n // Convert dots to underscores and uppercase\n const key = path.trim().toUpperCase().replace(/\\./g, '_')\n\n // Return empty if path was just whitespace\n if (key.length === 0) {\n return ''\n }\n\n // Add prefix if provided\n if (prefix && prefix.length > 0) {\n return `${prefix.toUpperCase()}_${key}`\n }\n\n return key\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TsHelpersError
|
|
3
|
+
} from "./chunk-75XNTC34.js";
|
|
4
|
+
import {
|
|
5
|
+
__export
|
|
6
|
+
} from "./chunk-NSBPE2FW.js";
|
|
7
|
+
|
|
8
|
+
// src/async.ts
|
|
9
|
+
var async_exports = {};
|
|
10
|
+
__export(async_exports, {
|
|
11
|
+
handleOperation: () => handleOperation,
|
|
12
|
+
runBatch: () => runBatch,
|
|
13
|
+
sleep: () => sleep,
|
|
14
|
+
wait: () => wait
|
|
15
|
+
});
|
|
16
|
+
var sleep = (ms) => {
|
|
17
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
|
+
};
|
|
19
|
+
var wait = (ms) => sleep(ms);
|
|
20
|
+
async function runBatch(jobs, batchSize = 50) {
|
|
21
|
+
if (batchSize <= 0) {
|
|
22
|
+
throw new TsHelpersError("Batch size must be greater than 0", {
|
|
23
|
+
code: "INVALID_OPERATION" /* INVALID_OPERATION */,
|
|
24
|
+
data: { batchSize }
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const results = [];
|
|
28
|
+
const batches = Math.ceil(jobs.length / batchSize);
|
|
29
|
+
for (let i = 0; i < batches; i++) {
|
|
30
|
+
const batchStart = i * batchSize;
|
|
31
|
+
const batchEnd = batchStart + batchSize;
|
|
32
|
+
const batch = jobs.slice(batchStart, batchEnd);
|
|
33
|
+
const batchResults = await Promise.all(batch.map((job) => job));
|
|
34
|
+
results.push(...batchResults);
|
|
35
|
+
}
|
|
36
|
+
return results;
|
|
37
|
+
}
|
|
38
|
+
function handleOperation(target, operation, ...args) {
|
|
39
|
+
if (operation.includes("/")) {
|
|
40
|
+
const [parentOp, childOp] = operation.split("/");
|
|
41
|
+
if (!target[parentOp] || !target[parentOp][childOp]) {
|
|
42
|
+
throw new TsHelpersError(`Operation [${operation}] does not exist`, {
|
|
43
|
+
code: "INVALID_OPERATION" /* INVALID_OPERATION */,
|
|
44
|
+
data: { operation, parentOp, childOp }
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return target[parentOp][childOp](...args);
|
|
48
|
+
}
|
|
49
|
+
if (!target[operation]) {
|
|
50
|
+
throw new TsHelpersError(`Operation [${operation}] does not exist`, {
|
|
51
|
+
code: "INVALID_OPERATION" /* INVALID_OPERATION */,
|
|
52
|
+
data: { operation }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return target[operation](...args);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
sleep,
|
|
60
|
+
wait,
|
|
61
|
+
runBatch,
|
|
62
|
+
handleOperation,
|
|
63
|
+
async_exports
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=chunk-ZFVYLUTT.js.map
|