@happyvertical/utils 0.80.0 → 0.80.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.
@@ -0,0 +1,1415 @@
1
+ import { createId, isCuid } from "@paralleldrive/cuid2";
2
+ import { add, format, isValid, parse, parseISO } from "date-fns";
3
+ import pluralize from "pluralize";
4
+ //#region src/config/env-config.ts
5
+ /**
6
+ * Convert snake_case to camelCase
7
+ * @param str - Snake case string
8
+ * @returns Camel case string
9
+ * @example
10
+ * toCamelCase('max_retries') → 'maxRetries'
11
+ * toCamelCase('api_key') → 'apiKey'
12
+ */
13
+ function toCamelCase(str) {
14
+ return str.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
15
+ }
16
+ /**
17
+ * Convert camelCase to SCREAMING_SNAKE_CASE
18
+ * @param str - Camel case string
19
+ * @returns Screaming snake case string
20
+ * @example
21
+ * toScreamingSnakeCase('maxRetries') → 'MAX_RETRIES'
22
+ * toScreamingSnakeCase('apiKey') → 'API_KEY'
23
+ */
24
+ function toScreamingSnakeCase(str) {
25
+ return str.replace(/([A-Z])/g, "_$1").toUpperCase().replace(/^_/, "");
26
+ }
27
+ /**
28
+ * Convert string value to specified type
29
+ * @param value - String value from environment variable
30
+ * @param type - Target type for conversion
31
+ * @returns Converted value
32
+ * @throws Error if JSON parsing fails
33
+ */
34
+ function convertType(value, type) {
35
+ switch (type) {
36
+ case "number": {
37
+ const num = Number(value);
38
+ if (Number.isNaN(num)) throw new Error(`Cannot convert "${value}" to number: result is NaN`);
39
+ return num;
40
+ }
41
+ case "boolean": return value.toLowerCase() === "true" || value === "1" || value.toLowerCase() === "yes";
42
+ case "json": try {
43
+ return JSON.parse(value);
44
+ } catch (error) {
45
+ throw new Error(`Cannot parse "${value}" as JSON: ${error instanceof Error ? error.message : "Unknown error"}`);
46
+ }
47
+ default: return value;
48
+ }
49
+ }
50
+ /**
51
+ * Load configuration from environment variables
52
+ *
53
+ * Scans environment variables matching the specified pattern and merges them
54
+ * with user-provided options. User options always take precedence over env vars.
55
+ *
56
+ * @template T - Type of configuration object
57
+ * @param userOptions - User-provided configuration (takes precedence)
58
+ * @param options - Configuration options for env loading
59
+ * @returns Merged configuration with env vars and user options
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * // Load HAVE_AI_* variables
64
+ * const config = loadEnvConfig({ provider: 'openai' }, {
65
+ * packageName: 'ai',
66
+ * schema: {
67
+ * provider: 'string',
68
+ * model: 'string',
69
+ * timeout: 'number',
70
+ * maxRetries: 'number'
71
+ * }
72
+ * });
73
+ * // Checks: HAVE_AI_PROVIDER, HAVE_AI_MODEL, HAVE_AI_TIMEOUT, HAVE_AI_MAX_RETRIES
74
+ *
75
+ * // Load SQLOO_* variables (backward compatibility)
76
+ * const sqlConfig = loadEnvConfig({}, {
77
+ * prefix: 'SQLOO',
78
+ * schema: {
79
+ * database: 'string',
80
+ * host: 'string',
81
+ * port: 'number'
82
+ * }
83
+ * });
84
+ * // Checks: SQLOO_DATABASE, SQLOO_HOST, SQLOO_PORT
85
+ *
86
+ * // Load without prefix
87
+ * const customConfig = loadEnvConfig({}, {
88
+ * prefix: '',
89
+ * schema: {
90
+ * OPENAI_API_KEY: 'string'
91
+ * }
92
+ * });
93
+ * // Checks: OPENAI_API_KEY
94
+ * ```
95
+ */
96
+ function loadEnvConfig(userOptions = {}, options = {}) {
97
+ const { prefix = "HAVE", packageName, schema = {}, transform = {}, allowUnknown = true } = options;
98
+ const envPrefix = packageName && prefix ? `${prefix}_${packageName.toUpperCase()}_` : prefix ? `${prefix}_` : "";
99
+ const config = { ...userOptions };
100
+ const fieldsToScan = allowUnknown ? /* @__PURE__ */ new Set([...Object.keys(schema), ...Object.keys(process.env).filter((key) => envPrefix && key.startsWith(envPrefix)).map((key) => {
101
+ return toCamelCase(key.slice(envPrefix.length));
102
+ })]) : new Set(Object.keys(schema));
103
+ for (const fieldName of fieldsToScan) {
104
+ if (fieldName in userOptions) continue;
105
+ const envVarName = envPrefix ? envPrefix + toScreamingSnakeCase(fieldName) : fieldName;
106
+ const envValue = process.env[envVarName];
107
+ if (envValue === void 0) continue;
108
+ const transformFn = transform[fieldName];
109
+ if (transformFn) {
110
+ try {
111
+ config[fieldName] = transformFn(envValue);
112
+ } catch (error) {
113
+ console.warn(`Failed to transform ${envVarName}="${envValue}":`, error instanceof Error ? error.message : error);
114
+ }
115
+ continue;
116
+ }
117
+ const fieldType = schema[fieldName];
118
+ if (fieldType) {
119
+ try {
120
+ config[fieldName] = convertType(envValue, fieldType);
121
+ } catch (error) {
122
+ console.warn(`Failed to convert ${envVarName}="${envValue}" to ${fieldType}:`, error instanceof Error ? error.message : error);
123
+ }
124
+ continue;
125
+ }
126
+ config[fieldName] = envValue;
127
+ }
128
+ return config;
129
+ }
130
+ //#endregion
131
+ //#region src/shared/code/extraction.ts
132
+ /**
133
+ * Code extraction utilities for parsing code from text (e.g., AI responses)
134
+ *
135
+ * Provides functions to extract code blocks, JSON data, and function definitions
136
+ * from markdown-formatted text or AI-generated responses.
137
+ */
138
+ /**
139
+ * Extracts a code block from markdown-formatted text
140
+ *
141
+ * @param text - The text containing markdown code blocks
142
+ * @param language - Optional language specifier to match (e.g., 'javascript', 'typescript', 'json')
143
+ * @returns The extracted code without markdown delimiters, or empty string if not found
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const code = extractCodeBlock(`
148
+ * Here's the function:
149
+ * \`\`\`javascript
150
+ * function hello() {
151
+ * return 'world';
152
+ * }
153
+ * \`\`\`
154
+ * `, 'javascript');
155
+ * // Returns: "function hello() {\n return 'world';\n}"
156
+ * ```
157
+ */
158
+ function extractCodeBlock(text, language) {
159
+ if (!text) return "";
160
+ const langPattern = language ? `${language}\\s*` : "(?:\\w+\\s*)?";
161
+ const codeBlockRegex = new RegExp(`\`\`\`${langPattern}\\r?\\n([\\s\\S]*?)\\r?\\n\`\`\``, "i");
162
+ const match = text.match(codeBlockRegex);
163
+ if (match?.[1]) return match[1].trim();
164
+ const inlineMatch = text.match(/`([^`]+)`/);
165
+ if (inlineMatch?.[1]) return inlineMatch[1].trim();
166
+ return "";
167
+ }
168
+ /**
169
+ * Extracts and parses JSON from text, handling markdown code blocks
170
+ *
171
+ * @param text - The text containing JSON data
172
+ * @returns The parsed JSON object
173
+ * @throws {SyntaxError} If the JSON is invalid
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * const data = extractJSON<{ name: string }>(`
178
+ * The result is:
179
+ * \`\`\`json
180
+ * {
181
+ * "name": "example"
182
+ * }
183
+ * \`\`\`
184
+ * `);
185
+ * // Returns: { name: "example" }
186
+ * ```
187
+ */
188
+ function extractJSON(text) {
189
+ if (!text) throw new SyntaxError("Cannot extract JSON from empty text");
190
+ let jsonText = extractCodeBlock(text, "json");
191
+ if (!jsonText) jsonText = extractCodeBlock(text);
192
+ if (!jsonText) {
193
+ const jsonObjectMatch = text.match(/\{[\s\S]*\}/);
194
+ const jsonArrayMatch = text.match(/\[[\s\S]*\]/);
195
+ if (jsonObjectMatch) jsonText = jsonObjectMatch[0];
196
+ else if (jsonArrayMatch) jsonText = jsonArrayMatch[0];
197
+ else jsonText = text.trim();
198
+ }
199
+ try {
200
+ return JSON.parse(jsonText);
201
+ } catch (error) {
202
+ throw new SyntaxError(`Failed to parse JSON: ${error instanceof Error ? error.message : "Unknown error"}`);
203
+ }
204
+ }
205
+ /**
206
+ * Extracts all code blocks from markdown-formatted text
207
+ *
208
+ * @param text - The text containing markdown code blocks
209
+ * @param language - Optional language specifier to filter by
210
+ * @returns Array of extracted code blocks
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const blocks = extractAllCodeBlocks(`
215
+ * \`\`\`javascript
216
+ * const a = 1;
217
+ * \`\`\`
218
+ *
219
+ * \`\`\`typescript
220
+ * const b: number = 2;
221
+ * \`\`\`
222
+ * `);
223
+ * // Returns: ["const a = 1;", "const b: number = 2;"]
224
+ * ```
225
+ */
226
+ function extractAllCodeBlocks(text, language) {
227
+ if (!text) return [];
228
+ const langPattern = language ? `${language}\\s*` : "(?:\\w+\\s*)?";
229
+ const codeBlockRegex = new RegExp(`\`\`\`${langPattern}\\r?\\n([\\s\\S]*?)\\r?\\n\`\`\``, "gi");
230
+ const blocks = [];
231
+ const matches = text.matchAll(codeBlockRegex);
232
+ for (const match of matches) if (match[1]) blocks.push(match[1].trim());
233
+ return blocks;
234
+ }
235
+ /**
236
+ * Extracts a specific function definition from code
237
+ *
238
+ * @param code - The code containing function definitions
239
+ * @param functionName - The name of the function to extract
240
+ * @returns The function definition, or empty string if not found
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * const code = `
245
+ * function foo() { return 1; }
246
+ * function bar() { return 2; }
247
+ * `;
248
+ *
249
+ * const fooFunc = extractFunctionDefinition(code, 'foo');
250
+ * // Returns: "function foo() { return 1; }"
251
+ * ```
252
+ */
253
+ function extractFunctionDefinition(code, functionName) {
254
+ if (!code || !functionName) return "";
255
+ const patterns = [
256
+ {
257
+ regex: new RegExp(`function\\s+${functionName}\\s*\\([^)]*\\)\\s*\\{`, "i"),
258
+ hasBraces: true
259
+ },
260
+ {
261
+ regex: new RegExp(`(?:const|let|var)\\s+${functionName}\\s*=\\s*function\\s*\\([^)]*\\)\\s*\\{`, "i"),
262
+ hasBraces: true
263
+ },
264
+ {
265
+ regex: new RegExp(`(?:const|let|var)\\s+${functionName}\\s*=\\s*\\([^)]*\\)\\s*=>\\s*\\{`, "i"),
266
+ hasBraces: true
267
+ },
268
+ {
269
+ regex: new RegExp(`(?:const|let|var)\\s+${functionName}\\s*=\\s*\\([^)]*\\)\\s*=>\\s*[^;]+;?`, "i"),
270
+ hasBraces: false
271
+ }
272
+ ];
273
+ for (const { regex, hasBraces } of patterns) {
274
+ const match = code.match(regex);
275
+ if (match && match.index !== void 0) {
276
+ const startIdx = match.index;
277
+ if (!hasBraces) return match[0].trim();
278
+ const braceIdx = code.indexOf("{", startIdx);
279
+ if (braceIdx === -1) continue;
280
+ let idx = braceIdx;
281
+ let depth = 0;
282
+ let endIdx = -1;
283
+ while (idx < code.length) {
284
+ const char = code[idx];
285
+ if (char === "{") depth++;
286
+ else if (char === "}") {
287
+ depth--;
288
+ if (depth === 0) {
289
+ endIdx = idx;
290
+ break;
291
+ }
292
+ }
293
+ idx++;
294
+ }
295
+ if (endIdx !== -1) return code.slice(startIdx, endIdx + 1).trim();
296
+ }
297
+ }
298
+ return "";
299
+ }
300
+ //#endregion
301
+ //#region src/shared/code/validation.ts
302
+ /**
303
+ * Default dangerous patterns that should be disallowed
304
+ */
305
+ var DANGEROUS_PATTERNS = [
306
+ /require\s*\(/i,
307
+ /import\s+/i,
308
+ /eval\s*\(/i,
309
+ /Function\s*\(/i,
310
+ /process\./i,
311
+ /fs\./i,
312
+ /child_process/i,
313
+ /__dirname/i,
314
+ /__filename/i,
315
+ /global\./i
316
+ ];
317
+ /**
318
+ * Validates code before execution in a sandbox
319
+ *
320
+ * @param code - The code to validate
321
+ * @param options - Validation options
322
+ * @returns Validation result with errors and warnings
323
+ *
324
+ * @example
325
+ * ```typescript
326
+ * const result = validateCode(`
327
+ * function parse(data) {
328
+ * return JSON.parse(data);
329
+ * }
330
+ * `, {
331
+ * allowedGlobals: ['JSON'],
332
+ * maxLength: 10000
333
+ * });
334
+ *
335
+ * if (!result.valid) {
336
+ * console.error('Code validation failed:', result.errors);
337
+ * }
338
+ * ```
339
+ */
340
+ function validateCode(code, options = {}) {
341
+ const { allowedGlobals, disallowedPatterns = DANGEROUS_PATTERNS, maxLength = 5e4, allowRequire = false, allowImport = false, allowEval = false, checkSyntax = true } = options;
342
+ const errors = [];
343
+ const warnings = [];
344
+ if (!code || code.trim().length === 0) {
345
+ errors.push("Code is empty");
346
+ return {
347
+ valid: false,
348
+ errors,
349
+ warnings
350
+ };
351
+ }
352
+ if (code.length > maxLength) errors.push(`Code exceeds maximum length (${code.length} > ${maxLength} characters)`);
353
+ const patternFlags = /* @__PURE__ */ new Map([
354
+ [DANGEROUS_PATTERNS[0], "require"],
355
+ [DANGEROUS_PATTERNS[1], "import"],
356
+ [DANGEROUS_PATTERNS[2], "eval"],
357
+ [DANGEROUS_PATTERNS[3], "eval"]
358
+ ]);
359
+ const effectivePatterns = disallowedPatterns.filter((_pattern, index) => {
360
+ const patternType = patternFlags.get(DANGEROUS_PATTERNS[index]);
361
+ if (patternType === "require" && allowRequire) return false;
362
+ if (patternType === "import" && allowImport) return false;
363
+ if (patternType === "eval" && allowEval) return false;
364
+ return true;
365
+ });
366
+ for (const pattern of effectivePatterns) if (pattern.test(code)) errors.push(`Code contains disallowed pattern: ${pattern.source.replace(/\\/g, "")}`);
367
+ if (allowedGlobals) {
368
+ const undeclaredVars = findUndeclaredVariables(code, allowedGlobals);
369
+ if (undeclaredVars.length > 0) warnings.push(`Potentially undeclared variables: ${undeclaredVars.join(", ")}`);
370
+ }
371
+ if (checkSyntax) {
372
+ const syntaxErrors = checkCodeSyntax(code);
373
+ errors.push(...syntaxErrors);
374
+ }
375
+ const stats = {
376
+ length: code.length,
377
+ lines: code.split("\n").length,
378
+ hasAsync: /\basync\b\s*(function|\([\w\s,={}[\]]*\)\s*=>|\w+\s*\()/m.test(code),
379
+ hasArrowFunctions: /=>/.test(code),
380
+ hasClasses: /\bclass\s+\w+/.test(code)
381
+ };
382
+ if (stats.lines > 100) warnings.push(`Code is long (${stats.lines} lines) - consider breaking into smaller functions`);
383
+ return {
384
+ valid: errors.length === 0,
385
+ errors,
386
+ warnings,
387
+ stats
388
+ };
389
+ }
390
+ /**
391
+ * Checks code syntax without executing it
392
+ *
393
+ * @param code - The code to check
394
+ * @returns Array of syntax error messages (empty if valid)
395
+ */
396
+ function checkCodeSyntax(code) {
397
+ const errors = [];
398
+ try {
399
+ new Function(code);
400
+ } catch (error) {
401
+ if (error instanceof SyntaxError) {
402
+ const message = error.message;
403
+ if (message.includes("await is only valid") && /\bawait\b/.test(code)) try {
404
+ new Function(`(async function() { ${code} })()`);
405
+ return errors;
406
+ } catch (asyncError) {
407
+ if (asyncError instanceof SyntaxError) errors.push(`Syntax error: ${asyncError.message}`);
408
+ }
409
+ else errors.push(`Syntax error: ${message}`);
410
+ }
411
+ }
412
+ return errors;
413
+ }
414
+ /**
415
+ * Finds potentially undeclared variables in code
416
+ *
417
+ * **Important Limitations:**
418
+ * This is a basic regex-based static analysis with known limitations:
419
+ * - Cannot detect variables in destructuring assignments
420
+ * - May miss variables declared in nested scopes
421
+ * - Cannot handle dynamic variable access (e.g., obj[varName])
422
+ * - Will have false positives for method calls and object properties
423
+ * - Does not understand scope chains or closures
424
+ *
425
+ * For production use cases requiring accurate variable analysis, consider using
426
+ * a proper AST parser like @babel/parser or acorn.
427
+ *
428
+ * @param code - The code to analyze
429
+ * @param allowedGlobals - List of allowed global variables
430
+ * @returns Array of potentially undeclared variable names (may include false positives)
431
+ */
432
+ function findUndeclaredVariables(code, allowedGlobals) {
433
+ const undeclaredVars = /* @__PURE__ */ new Set();
434
+ const matches = code.matchAll(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g);
435
+ const declaredVars = /* @__PURE__ */ new Set();
436
+ const declarations = code.matchAll(/\b(?:var|let|const|function)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g);
437
+ for (const match of declarations) if (match[1]) declaredVars.add(match[1]);
438
+ for (const match of matches) {
439
+ const identifier = match[1];
440
+ if (isJavaScriptKeyword(identifier)) continue;
441
+ if (declaredVars.has(identifier)) continue;
442
+ if (allowedGlobals.includes(identifier)) continue;
443
+ if (isCommonBuiltin(identifier)) continue;
444
+ undeclaredVars.add(identifier);
445
+ }
446
+ return Array.from(undeclaredVars);
447
+ }
448
+ /**
449
+ * Checks if a word is a JavaScript keyword
450
+ */
451
+ function isJavaScriptKeyword(word) {
452
+ return [
453
+ "break",
454
+ "case",
455
+ "catch",
456
+ "class",
457
+ "const",
458
+ "continue",
459
+ "debugger",
460
+ "default",
461
+ "delete",
462
+ "do",
463
+ "else",
464
+ "export",
465
+ "extends",
466
+ "finally",
467
+ "for",
468
+ "function",
469
+ "if",
470
+ "import",
471
+ "in",
472
+ "instanceof",
473
+ "let",
474
+ "new",
475
+ "return",
476
+ "super",
477
+ "switch",
478
+ "this",
479
+ "throw",
480
+ "try",
481
+ "typeof",
482
+ "var",
483
+ "void",
484
+ "while",
485
+ "with",
486
+ "yield",
487
+ "async",
488
+ "await"
489
+ ].includes(word);
490
+ }
491
+ /**
492
+ * Checks if a word is a common JavaScript built-in
493
+ */
494
+ function isCommonBuiltin(word) {
495
+ return [
496
+ "Array",
497
+ "Object",
498
+ "String",
499
+ "Number",
500
+ "Boolean",
501
+ "Date",
502
+ "Math",
503
+ "JSON",
504
+ "RegExp",
505
+ "Error",
506
+ "Map",
507
+ "Set",
508
+ "Promise",
509
+ "Symbol",
510
+ "undefined",
511
+ "null",
512
+ "true",
513
+ "false",
514
+ "console",
515
+ "parseInt",
516
+ "parseFloat",
517
+ "isNaN",
518
+ "isFinite",
519
+ "decodeURI",
520
+ "decodeURIComponent",
521
+ "encodeURI",
522
+ "encodeURIComponent"
523
+ ].includes(word);
524
+ }
525
+ /**
526
+ * Quick validation to check if code is safe for execution
527
+ * Returns true if code passes basic safety checks
528
+ *
529
+ * @param code - The code to check
530
+ * @returns true if code is safe, false otherwise
531
+ *
532
+ * @example
533
+ * ```typescript
534
+ * if (isSafeCode('return x + y')) {
535
+ * // Safe to execute
536
+ * }
537
+ * ```
538
+ */
539
+ function isSafeCode(code) {
540
+ return validateCode(code, {
541
+ maxLength: 5e4,
542
+ checkSyntax: true
543
+ }).valid;
544
+ }
545
+ //#endregion
546
+ //#region src/shared/logger.ts
547
+ /**
548
+ * Console-based logger implementation
549
+ *
550
+ * Routes all log messages to the appropriate console methods with optional context.
551
+ */
552
+ var ConsoleLogger = class {
553
+ debug(message, context) {
554
+ if (context) console.debug(message, context);
555
+ else console.debug(message);
556
+ }
557
+ info(message, context) {
558
+ if (context) console.info(message, context);
559
+ else console.info(message);
560
+ }
561
+ warn(message, context) {
562
+ if (context) console.warn(message, context);
563
+ else console.warn(message);
564
+ }
565
+ error(message, context) {
566
+ if (context) console.error(message, context);
567
+ else console.error(message);
568
+ }
569
+ };
570
+ /**
571
+ * No-operation logger that discards all log messages
572
+ *
573
+ * Useful for disabling logging in production or testing environments.
574
+ */
575
+ var NoOpLogger = class {
576
+ debug() {}
577
+ info() {}
578
+ warn() {}
579
+ error() {}
580
+ };
581
+ /** Global logger instance used throughout the SDK */
582
+ var globalLogger = new ConsoleLogger();
583
+ /**
584
+ * Replace the global logger with a custom implementation
585
+ *
586
+ * @param logger - Custom logger implementation
587
+ * @example
588
+ * ```typescript
589
+ * setLogger(new CustomLogger());
590
+ * ```
591
+ */
592
+ var setLogger = (logger) => {
593
+ globalLogger = logger;
594
+ };
595
+ /**
596
+ * Get the current global logger instance
597
+ *
598
+ * @returns Current logger implementation
599
+ * @example
600
+ * ```typescript
601
+ * const logger = getLogger();
602
+ * logger.info('Application started');
603
+ * ```
604
+ */
605
+ var getLogger = () => {
606
+ return globalLogger;
607
+ };
608
+ /**
609
+ * Disable all logging by switching to no-op logger
610
+ *
611
+ * @example
612
+ * ```typescript
613
+ * if (process.env.NODE_ENV === 'production') {
614
+ * disableLogging();
615
+ * }
616
+ * ```
617
+ */
618
+ var disableLogging = () => {
619
+ globalLogger = new NoOpLogger();
620
+ };
621
+ /**
622
+ * Enable console logging by switching to console logger
623
+ *
624
+ * @example
625
+ * ```typescript
626
+ * enableLogging(); // Re-enable after disabling
627
+ * ```
628
+ */
629
+ var enableLogging = () => {
630
+ globalLogger = new ConsoleLogger();
631
+ };
632
+ //#endregion
633
+ //#region src/shared/types.ts
634
+ /**
635
+ * Shared type definitions and interfaces for universal use
636
+ *
637
+ * This module provides standardized error classes and logging interfaces
638
+ * used throughout the HAVE SDK. All error classes extend BaseError to
639
+ * provide consistent error handling with context and timestamps.
640
+ */
641
+ /**
642
+ * Standardized error codes used across the HAVE SDK
643
+ *
644
+ * These codes provide consistent error categorization for better error handling
645
+ * and monitoring across all packages in the SDK.
646
+ */
647
+ var ErrorCode = /* @__PURE__ */ function(ErrorCode) {
648
+ /** Input validation failed */
649
+ ErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
650
+ /** API request/response error */
651
+ ErrorCode["API_ERROR"] = "API_ERROR";
652
+ /** File system operation error */
653
+ ErrorCode["FILE_ERROR"] = "FILE_ERROR";
654
+ /** Network connectivity or HTTP error */
655
+ ErrorCode["NETWORK_ERROR"] = "NETWORK_ERROR";
656
+ /** Database operation error */
657
+ ErrorCode["DATABASE_ERROR"] = "DATABASE_ERROR";
658
+ /** Data parsing or format error */
659
+ ErrorCode["PARSING_ERROR"] = "PARSING_ERROR";
660
+ /** Operation timeout error */
661
+ ErrorCode["TIMEOUT_ERROR"] = "TIMEOUT_ERROR";
662
+ /** Unspecified or unexpected error */
663
+ ErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
664
+ return ErrorCode;
665
+ }({});
666
+ /**
667
+ * Base error class providing standardized error handling across the HAVE SDK
668
+ *
669
+ * All custom errors extend this class to ensure consistent error structure,
670
+ * context preservation, and debugging capabilities.
671
+ *
672
+ * @example
673
+ * ```typescript
674
+ * throw new BaseError('Something went wrong', ErrorCode.UNKNOWN_ERROR, {
675
+ * userId: 123,
676
+ * operation: 'processData'
677
+ * });
678
+ * ```
679
+ */
680
+ var BaseError = class extends Error {
681
+ /** Error classification code */
682
+ code;
683
+ /** Additional context data for debugging */
684
+ context;
685
+ /** When the error occurred */
686
+ timestamp;
687
+ constructor(message, code = "UNKNOWN_ERROR", context) {
688
+ super(message);
689
+ this.name = this.constructor.name;
690
+ this.code = code;
691
+ this.context = context;
692
+ this.timestamp = /* @__PURE__ */ new Date();
693
+ Error.captureStackTrace?.(this, this.constructor);
694
+ }
695
+ /**
696
+ * Serializes the error to a JSON-compatible object
697
+ * @returns Object containing all error properties
698
+ */
699
+ toJSON() {
700
+ return {
701
+ name: this.name,
702
+ message: this.message,
703
+ code: this.code,
704
+ context: this.context,
705
+ timestamp: this.timestamp.toISOString(),
706
+ stack: this.stack
707
+ };
708
+ }
709
+ };
710
+ /**
711
+ * Error thrown when input validation fails
712
+ *
713
+ * @example
714
+ * ```typescript
715
+ * throw new ValidationError('Email format invalid', {
716
+ * email: 'invalid-email',
717
+ * field: 'userEmail'
718
+ * });
719
+ * ```
720
+ */
721
+ var ValidationError = class extends BaseError {
722
+ constructor(message, context) {
723
+ super(message, "VALIDATION_ERROR", context);
724
+ }
725
+ };
726
+ /**
727
+ * Error thrown for API-related failures
728
+ *
729
+ * @example
730
+ * ```typescript
731
+ * throw new ApiError('HTTP 404 Not Found', {
732
+ * url: 'https://api.example.com/users/123',
733
+ * status: 404
734
+ * });
735
+ * ```
736
+ */
737
+ var ApiError = class extends BaseError {
738
+ constructor(message, context) {
739
+ super(message, "API_ERROR", context);
740
+ }
741
+ };
742
+ /**
743
+ * Error thrown for file system operation failures
744
+ *
745
+ * @example
746
+ * ```typescript
747
+ * throw new FileError('File not found', {
748
+ * path: '/path/to/missing/file.txt',
749
+ * operation: 'read'
750
+ * });
751
+ * ```
752
+ */
753
+ var FileError = class extends BaseError {
754
+ constructor(message, context) {
755
+ super(message, "FILE_ERROR", context);
756
+ }
757
+ };
758
+ /**
759
+ * Error thrown for network connectivity or HTTP failures
760
+ *
761
+ * @example
762
+ * ```typescript
763
+ * throw new NetworkError('Connection timeout', {
764
+ * host: 'example.com',
765
+ * timeout: 5000,
766
+ * attempt: 3
767
+ * });
768
+ * ```
769
+ */
770
+ var NetworkError = class extends BaseError {
771
+ constructor(message, context) {
772
+ super(message, "NETWORK_ERROR", context);
773
+ }
774
+ };
775
+ /**
776
+ * Error thrown for database operation failures
777
+ *
778
+ * @example
779
+ * ```typescript
780
+ * throw new DatabaseError('Connection failed', {
781
+ * database: 'production',
782
+ * query: 'SELECT * FROM users',
783
+ * errorCode: 'ECONNREFUSED'
784
+ * });
785
+ * ```
786
+ */
787
+ var DatabaseError = class extends BaseError {
788
+ constructor(message, context) {
789
+ super(message, "DATABASE_ERROR", context);
790
+ }
791
+ };
792
+ /**
793
+ * Error thrown for data parsing or format failures
794
+ *
795
+ * @example
796
+ * ```typescript
797
+ * throw new ParsingError('Invalid JSON format', {
798
+ * input: '{invalid json}',
799
+ * parser: 'JSON.parse',
800
+ * position: 1
801
+ * });
802
+ * ```
803
+ */
804
+ var ParsingError = class extends BaseError {
805
+ constructor(message, context) {
806
+ super(message, "PARSING_ERROR", context);
807
+ }
808
+ };
809
+ /**
810
+ * Error thrown when operations exceed their timeout duration
811
+ *
812
+ * @example
813
+ * ```typescript
814
+ * throw new TimeoutError('Operation timed out', {
815
+ * timeout: 5000,
816
+ * operation: 'fetchData',
817
+ * elapsedTime: 5234
818
+ * });
819
+ * ```
820
+ */
821
+ var TimeoutError = class extends BaseError {
822
+ constructor(message, context) {
823
+ super(message, "TIMEOUT_ERROR", context);
824
+ }
825
+ };
826
+ //#endregion
827
+ //#region src/shared/universal.ts
828
+ /**
829
+ * Universal utilities that work in both browser and Node.js environments
830
+ */
831
+ /**
832
+ * Generates a unique identifier using CUID2 (preferred) or UUID fallback
833
+ *
834
+ * CUID2 is more secure and collision-resistant than UUIDs, but UUID is provided
835
+ * as a fallback for RFC4122 compliance requirements.
836
+ *
837
+ * @param type - ID type: 'cuid2' (default) or 'uuid'
838
+ * @returns A unique identifier string
839
+ * @example
840
+ * ```typescript
841
+ * const id = makeId(); // CUID2: "ckx5f8h3z0000qzrmn831i7rn"
842
+ * const uuid = makeId('uuid'); // UUID: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
843
+ * ```
844
+ */
845
+ var makeId = (type = "cuid2") => {
846
+ if (type === "cuid2") return createId();
847
+ if (crypto?.randomUUID) return crypto.randomUUID();
848
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
849
+ const r = Math.random() * 16 | 0;
850
+ return (c === "x" ? r : r & 3 | 8).toString(16);
851
+ });
852
+ };
853
+ /**
854
+ * Generates a CUID2 identifier (collision-resistant, more secure than UUID)
855
+ *
856
+ * CUID2 provides better entropy and security compared to UUID v4, making it
857
+ * ideal for distributed systems and user-facing identifiers.
858
+ *
859
+ * @returns A CUID2 identifier string
860
+ * @example
861
+ * ```typescript
862
+ * const id = createId(); // "ckx5f8h3z0000qzrmn831i7rn"
863
+ * ```
864
+ */
865
+ var createId$1 = createId;
866
+ /**
867
+ * Converts a string to a URL-friendly slug
868
+ *
869
+ * Handles international characters, removes special characters, and replaces
870
+ * spaces with hyphens. Ampersands are converted to "-38-" for uniqueness.
871
+ *
872
+ * @param str - The string to convert to a slug
873
+ * @returns A URL-friendly slug string
874
+ * @example
875
+ * ```typescript
876
+ * makeSlug("My Example Title & Co."); // "my-example-title-38-co"
877
+ * makeSlug("Café España"); // "cafe-espana"
878
+ * ```
879
+ */
880
+ var makeSlug = (str) => {
881
+ const from = "àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìıİłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż+·/_,:;";
882
+ const to = "aaaaaaaaaacccddeeeeeeeegghiiiiiiiilmnnnnoooooooooprrsssssttuuuuuuuuuwxyyzzz--------------";
883
+ const textToCompare = new RegExp(from.split("").join("|").replace(/\+/g, "\\+"), "g");
884
+ return str.toString().toLowerCase().replace("&", "-38-").replace(/\s+/g, "-").replace(textToCompare, (c) => to.charAt(from.indexOf(c))).replace(/[&.]/g, "-").replace(/[^\w-º+]+/g, "").replace(/--+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
885
+ };
886
+ /**
887
+ * Extracts the filename from a URL's pathname
888
+ *
889
+ * Returns the last segment of the URL path. If no filename is found,
890
+ * defaults to 'index.html'.
891
+ *
892
+ * @param url - The URL to extract filename from
893
+ * @returns The filename from the URL
894
+ * @throws {TypeError} When URL is invalid
895
+ * @example
896
+ * ```typescript
897
+ * urlFilename("https://example.com/path/file.pdf"); // "file.pdf"
898
+ * urlFilename("https://example.com/path/"); // "index.html"
899
+ * ```
900
+ */
901
+ var urlFilename = (url) => {
902
+ const pathSegments = new URL(url).pathname.split("/");
903
+ return pathSegments[pathSegments.length - 1] || "index.html";
904
+ };
905
+ /**
906
+ * Converts a URL to a file path by joining hostname and pathname
907
+ *
908
+ * Creates a file system compatible path from a URL by combining the hostname
909
+ * with the pathname segments, useful for creating local file structures.
910
+ *
911
+ * @param url - The URL to convert to a path
912
+ * @returns A file path string with hostname and path segments
913
+ * @throws {TypeError} When URL is invalid
914
+ * @example
915
+ * ```typescript
916
+ * urlPath("https://example.com/path/to/resource"); // "example.com/path/to/resource"
917
+ * ```
918
+ */
919
+ var urlPath = (url) => {
920
+ const parsedUrl = new URL(url);
921
+ return [parsedUrl.hostname, ...parsedUrl.pathname.split("/").filter(Boolean)].join("/");
922
+ };
923
+ /**
924
+ * Creates a Promise that resolves after a specified duration
925
+ *
926
+ * Useful for adding delays in async functions or rate limiting operations.
927
+ *
928
+ * @param duration - Time to wait in milliseconds
929
+ * @returns Promise that resolves after the specified duration
930
+ * @example
931
+ * ```typescript
932
+ * await sleep(1000); // Wait 1 second
933
+ * console.log('1 second has passed');
934
+ * ```
935
+ */
936
+ var sleep = (duration) => {
937
+ return new Promise((resolve) => {
938
+ setTimeout(resolve, duration);
939
+ });
940
+ };
941
+ /**
942
+ * Repeatedly calls a function until it returns a defined value or times out
943
+ *
944
+ * Polls an async function at regular intervals until it returns a defined value
945
+ * or the timeout is reached. Useful for waiting for conditions to be met.
946
+ *
947
+ * @param it - Async function to poll
948
+ * @param options - Configuration options
949
+ * @param options.timeout - Maximum time to wait in milliseconds (0 = no timeout)
950
+ * @param options.delay - Time between polling attempts in milliseconds
951
+ * @returns Promise that resolves with the function result or rejects on timeout
952
+ * @throws {TimeoutError} When timeout is reached before function returns defined value
953
+ * @example
954
+ * ```typescript
955
+ * // Wait for a file to exist
956
+ * const fileExists = await waitFor(
957
+ * async () => {
958
+ * try {
959
+ * await fs.access('/path/to/file');
960
+ * return true;
961
+ * } catch {
962
+ * return undefined; // Keep polling
963
+ * }
964
+ * },
965
+ * { timeout: 10000, delay: 500 }
966
+ * );
967
+ * ```
968
+ */
969
+ function waitFor(it, { timeout = 0, delay = 1e3 } = {}) {
970
+ return new Promise((resolve, reject) => {
971
+ const beginTime = Date.now();
972
+ (async function waitATick() {
973
+ try {
974
+ const result = await it();
975
+ if (typeof result !== "undefined") return resolve(result);
976
+ if (timeout > 0) {
977
+ if (Date.now() > beginTime + timeout) return reject(new TimeoutError("Function call timed out", {
978
+ timeout,
979
+ delay,
980
+ elapsedTime: Date.now() - beginTime
981
+ }));
982
+ }
983
+ setTimeout(waitATick, delay);
984
+ } catch (error) {
985
+ reject(error);
986
+ }
987
+ })();
988
+ });
989
+ }
990
+ /**
991
+ * Type guard to check if a value is an array
992
+ *
993
+ * @param obj - Value to check
994
+ * @returns True if value is an array, with TypeScript type narrowing
995
+ * @example
996
+ * ```typescript
997
+ * if (isArray(data)) {
998
+ * // TypeScript knows data is unknown[]
999
+ * data.forEach(item => console.log(item));
1000
+ * }
1001
+ * ```
1002
+ */
1003
+ var isArray = (obj) => {
1004
+ return Array.isArray(obj);
1005
+ };
1006
+ /**
1007
+ * Type guard to check if a value is a plain object
1008
+ *
1009
+ * Checks for objects that are not null, arrays, or other non-plain object types.
1010
+ *
1011
+ * @param obj - Value to check
1012
+ * @returns True if value is a plain object, with TypeScript type narrowing
1013
+ * @example
1014
+ * ```typescript
1015
+ * if (isPlainObject(data)) {
1016
+ * // TypeScript knows data is Record<string, unknown>
1017
+ * Object.keys(data).forEach(key => console.log(key, data[key]));
1018
+ * }
1019
+ * ```
1020
+ */
1021
+ var isPlainObject = (obj) => {
1022
+ return typeof obj === "object" && obj !== null && !Array.isArray(obj);
1023
+ };
1024
+ /**
1025
+ * Checks if a string is a valid URL
1026
+ *
1027
+ * Uses the URL constructor to validate the string format. Returns false
1028
+ * for any string that cannot be parsed as a valid URL.
1029
+ *
1030
+ * @param url - String to validate as URL
1031
+ * @returns True if string is a valid URL
1032
+ * @example
1033
+ * ```typescript
1034
+ * isUrl("https://example.com"); // true
1035
+ * isUrl("not-a-url"); // false
1036
+ * ```
1037
+ */
1038
+ var isUrl = (url) => {
1039
+ try {
1040
+ new URL(url);
1041
+ return true;
1042
+ } catch {
1043
+ return false;
1044
+ }
1045
+ };
1046
+ /**
1047
+ * Converts a string to camelCase
1048
+ *
1049
+ * Handles kebab-case, snake_case, and space-separated strings.
1050
+ * Removes special characters and ensures proper camelCase formatting.
1051
+ *
1052
+ * @param str - String to convert
1053
+ * @returns camelCase formatted string
1054
+ * @example
1055
+ * ```typescript
1056
+ * camelCase("hello-world"); // "helloWorld"
1057
+ * camelCase("snake_case_string"); // "snakeCaseString"
1058
+ * camelCase("Some Title"); // "someTitle"
1059
+ * ```
1060
+ */
1061
+ var camelCase = (str) => {
1062
+ return str.toLowerCase().replace(/[-_]+/g, " ").replace(/[^\w\s]/g, "").replace(/\s(.)/g, (_, char) => char.toUpperCase()).replace(/\s/g, "").replace(/^(.)/, (_, char) => char.toLowerCase());
1063
+ };
1064
+ /**
1065
+ * Converts a string to snake_case
1066
+ *
1067
+ * Handles camelCase, kebab-case, and space-separated strings.
1068
+ * Converts to lowercase with underscore separators.
1069
+ *
1070
+ * @param str - String to convert
1071
+ * @returns snake_case formatted string
1072
+ * @example
1073
+ * ```typescript
1074
+ * snakeCase("helloWorld"); // "hello_world"
1075
+ * snakeCase("kebab-case-string"); // "kebab_case_string"
1076
+ * snakeCase("Some Title"); // "some_title"
1077
+ * ```
1078
+ */
1079
+ var snakeCase = (str) => {
1080
+ return str.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[-\s]+/g, "_");
1081
+ };
1082
+ /**
1083
+ * Recursively converts all object keys to camelCase
1084
+ *
1085
+ * Deeply traverses an object or array and converts all keys to camelCase.
1086
+ * Preserves the structure and values, only transforming key names.
1087
+ *
1088
+ * @param obj - Object or array to transform
1089
+ * @returns New object/array with camelCase keys
1090
+ * @example
1091
+ * ```typescript
1092
+ * keysToCamel({
1093
+ * user_name: "john",
1094
+ * user_details: { first_name: "John" }
1095
+ * }); // { userName: "john", userDetails: { firstName: "John" } }
1096
+ * ```
1097
+ */
1098
+ var keysToCamel = (obj) => {
1099
+ if (isPlainObject(obj)) {
1100
+ const n = {};
1101
+ Object.keys(obj).forEach((k) => {
1102
+ n[camelCase(k)] = keysToCamel(obj[k]);
1103
+ });
1104
+ return n;
1105
+ }
1106
+ if (isArray(obj)) return obj.map((i) => keysToCamel(i));
1107
+ return obj;
1108
+ };
1109
+ /**
1110
+ * Recursively converts all object keys to snake_case
1111
+ *
1112
+ * Deeply traverses an object or array and converts all keys to snake_case.
1113
+ * Preserves the structure and values, only transforming key names.
1114
+ *
1115
+ * @param obj - Object or array to transform
1116
+ * @returns New object/array with snake_case keys
1117
+ * @example
1118
+ * ```typescript
1119
+ * keysToSnake({
1120
+ * userName: "john",
1121
+ * userDetails: { firstName: "John" }
1122
+ * }); // { user_name: "john", user_details: { first_name: "John" } }
1123
+ * ```
1124
+ */
1125
+ var keysToSnake = (obj) => {
1126
+ if (isPlainObject(obj)) {
1127
+ const n = {};
1128
+ Object.keys(obj).forEach((k) => {
1129
+ n[snakeCase(k)] = keysToSnake(obj[k]);
1130
+ });
1131
+ return n;
1132
+ }
1133
+ if (isArray(obj)) return obj.map((i) => keysToSnake(i));
1134
+ return obj;
1135
+ };
1136
+ /**
1137
+ * Converts a domain string to camelCase
1138
+ *
1139
+ * Convenience function that applies camelCase conversion to domain strings.
1140
+ * Useful for converting API service names or domain identifiers.
1141
+ *
1142
+ * @param domain - Domain string to convert
1143
+ * @returns camelCase formatted domain string
1144
+ * @example
1145
+ * ```typescript
1146
+ * domainToCamel("api-service"); // "apiService"
1147
+ * domainToCamel("user_management"); // "userManagement"
1148
+ * ```
1149
+ */
1150
+ var domainToCamel = (domain) => camelCase(domain);
1151
+ /**
1152
+ * Creates a visual progress indicator by cycling through a sequence of characters
1153
+ *
1154
+ * Useful for creating animated progress indicators in CLI applications.
1155
+ * Cycles through provided characters or defaults to dot sequences.
1156
+ *
1157
+ * @param tick - Current tick state (null to start)
1158
+ * @param options - Configuration options
1159
+ * @param options.chars - Array of characters to cycle through
1160
+ * @returns Next character in the sequence
1161
+ * @example
1162
+ * ```typescript
1163
+ * let tick = null;
1164
+ * setInterval(() => {
1165
+ * tick = logTicker(tick);
1166
+ * process.stdout.write(`\rProcessing ${tick}`);
1167
+ * }, 500);
1168
+ * // Outputs: "Processing ." → "Processing .." → "Processing ..."
1169
+ * ```
1170
+ */
1171
+ var logTicker = (tick, options = {}) => {
1172
+ const { chars = [
1173
+ ".",
1174
+ "..",
1175
+ "..."
1176
+ ] } = options;
1177
+ if (tick) {
1178
+ const index = chars.indexOf(tick);
1179
+ return index + 1 >= chars.length ? chars[0] : chars[index + 1];
1180
+ }
1181
+ return chars[0];
1182
+ };
1183
+ /**
1184
+ * Parses an Amazon date string format (YYYYMMDDTHHMMSSZ) to a Date object
1185
+ *
1186
+ * Specifically handles the compact date format used by Amazon AWS services.
1187
+ * Validates the format and throws detailed errors for invalid inputs.
1188
+ *
1189
+ * @param dateStr - Amazon date string in format YYYYMMDDTHHMMSSZ
1190
+ * @returns Parsed Date object
1191
+ * @throws {ParsingError} When date string format is invalid
1192
+ * @example
1193
+ * ```typescript
1194
+ * parseAmazonDateString('20220223T215409Z');
1195
+ * // Returns: Date object for February 23, 2022, 21:54:09 UTC
1196
+ * ```
1197
+ */
1198
+ var parseAmazonDateString = (dateStr) => {
1199
+ const match = dateStr.match(/^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})([A-Z0-9]+)/);
1200
+ if (!match) throw new ParsingError("Could not parse Amazon date string", {
1201
+ dateString: dateStr,
1202
+ expectedFormat: "YYYYMMDDTHHMMSSZ"
1203
+ });
1204
+ const [matched, year, month, day, hour, minutes, seconds, timezone] = match;
1205
+ if (matched !== dateStr) throw new ParsingError("Invalid Amazon date string format", {
1206
+ dateString: dateStr,
1207
+ matched,
1208
+ expectedFormat: "YYYYMMDDTHHMMSSZ"
1209
+ });
1210
+ return /* @__PURE__ */ new Date(`${year}-${month}-${day}T${hour}:${minutes}:${seconds}${timezone}`);
1211
+ };
1212
+ /**
1213
+ * Extracts and parses a date from a string
1214
+ *
1215
+ * Intelligently extracts dates from filenames or text strings by looking for
1216
+ * common date patterns. Supports multiple formats including:
1217
+ * - ISO dates (2023-01-15, 2023/01/15)
1218
+ * - US dates (01/15/2023, 01-15-2023)
1219
+ * - Natural language (January 15, 2023, Oct 14 2025)
1220
+ * - Filenames with dates (Report_January_15_2023.pdf)
1221
+ *
1222
+ * @param str - String containing date information (filename, title, or text)
1223
+ * @returns Parsed Date object or null if no valid date found
1224
+ * @example
1225
+ * ```typescript
1226
+ * dateInString("Report_January_15_2023.pdf"); // Date(2023, 0, 15)
1227
+ * dateInString("Regular Council Meeting October 14, 2025"); // Date(2025, 9, 14)
1228
+ * dateInString("financial-report-dec-2023.pdf"); // Date(2023, 11, 1)
1229
+ * dateInString("2023-01-15"); // Date(2023, 0, 15)
1230
+ * dateInString("no-date-here.pdf"); // null
1231
+ * ```
1232
+ */
1233
+ var dateInString = (str) => {
1234
+ const cleanStr = str.toLowerCase();
1235
+ const underscoreMatch = str.match(/(\d{4})_(\d{1,2})_(\d{1,2})/);
1236
+ if (underscoreMatch) {
1237
+ const [, year, month, day] = underscoreMatch;
1238
+ const date = new Date(Number.parseInt(year, 10), Number.parseInt(month, 10) - 1, Number.parseInt(day, 10));
1239
+ if (!Number.isNaN(date.getTime())) return date;
1240
+ }
1241
+ const dotMatch = str.match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);
1242
+ if (dotMatch) {
1243
+ const [, month, day, year] = dotMatch;
1244
+ const date = new Date(Number.parseInt(year, 10), Number.parseInt(month, 10) - 1, Number.parseInt(day, 10));
1245
+ if (!Number.isNaN(date.getTime())) return date;
1246
+ }
1247
+ const isoMatch = cleanStr.match(/(\d{4})[-/](\d{1,2})[-/](\d{1,2})/);
1248
+ if (isoMatch) {
1249
+ const [, year, month, day] = isoMatch;
1250
+ const date = new Date(Number.parseInt(year, 10), Number.parseInt(month, 10) - 1, Number.parseInt(day, 10));
1251
+ if (!Number.isNaN(date.getTime())) return date;
1252
+ }
1253
+ const usMatch = cleanStr.match(/(\d{1,2})[-/](\d{1,2})[-/](\d{4})/);
1254
+ if (usMatch) {
1255
+ const [, month, day, year] = usMatch;
1256
+ const date = new Date(Number.parseInt(year, 10), Number.parseInt(month, 10) - 1, Number.parseInt(day, 10));
1257
+ if (!Number.isNaN(date.getTime())) return date;
1258
+ }
1259
+ const yearMatch = cleanStr.match(/20\d{2}/);
1260
+ if (!yearMatch) return null;
1261
+ const year = Number.parseInt(yearMatch[0], 10);
1262
+ const monthPatterns = {
1263
+ january: 1,
1264
+ jan: 1,
1265
+ february: 2,
1266
+ feb: 2,
1267
+ march: 3,
1268
+ mar: 3,
1269
+ april: 4,
1270
+ apr: 4,
1271
+ may: 5,
1272
+ june: 6,
1273
+ jun: 6,
1274
+ july: 7,
1275
+ jul: 7,
1276
+ august: 8,
1277
+ aug: 8,
1278
+ september: 9,
1279
+ sep: 9,
1280
+ sept: 9,
1281
+ october: 10,
1282
+ oct: 10,
1283
+ november: 11,
1284
+ nov: 11,
1285
+ december: 12,
1286
+ dec: 12
1287
+ };
1288
+ let foundMonth = null;
1289
+ let monthStart = -1;
1290
+ let monthName = "";
1291
+ for (const [name, monthNum] of Object.entries(monthPatterns)) {
1292
+ const monthIndex = cleanStr.indexOf(name);
1293
+ if (monthIndex !== -1) {
1294
+ foundMonth = monthNum;
1295
+ monthStart = monthIndex;
1296
+ monthName = name;
1297
+ break;
1298
+ }
1299
+ }
1300
+ if (!foundMonth) return null;
1301
+ const yearIndex = cleanStr.indexOf(yearMatch[0]);
1302
+ const beforeMonth = cleanStr.substring(Math.max(0, monthStart - 15), monthStart);
1303
+ const afterMonth = cleanStr.substring(monthStart + monthName.length, Math.min(cleanStr.length, monthStart + monthName.length + 15));
1304
+ const beforeYear = cleanStr.substring(Math.max(0, yearIndex - 15), yearIndex);
1305
+ const afterYear = cleanStr.substring(yearIndex + 4, Math.min(cleanStr.length, yearIndex + 19));
1306
+ const dayMatch = beforeMonth.match(/(?<!\d)(\d{1,2})\s*$/) || afterMonth.match(/^\s*(\d{1,2})(?!\d)/) || beforeYear.match(/(?<!\d)(\d{1,2})\s*$/) || afterYear.match(/^\s*(\d{1,2})(?!\d)/) || afterMonth.match(/[^\d](\d{1,2})(?!\d)/);
1307
+ const day = dayMatch ? Number.parseInt(dayMatch[1], 10) : 1;
1308
+ if (day < 1 || day > 31) return null;
1309
+ const date = new Date(year, foundMonth - 1, day);
1310
+ return !Number.isNaN(date.getTime()) ? date : null;
1311
+ };
1312
+ /**
1313
+ * Formats a date string into a human-readable format using the system locale
1314
+ *
1315
+ * Uses the Intl.DateTimeFormat API to create localized, human-readable date strings.
1316
+ * Automatically adapts to the user's system locale settings.
1317
+ *
1318
+ * @param dateString - ISO date string or any valid date string
1319
+ * @returns Human-readable date string in system locale
1320
+ * @example
1321
+ * ```typescript
1322
+ * prettyDate("2023-01-15T12:00:00Z"); // "January 15, 2023" (in English locale)
1323
+ * ```
1324
+ */
1325
+ var prettyDate = (dateString) => {
1326
+ const date = new Date(dateString);
1327
+ return new Intl.DateTimeFormat(void 0, {
1328
+ year: "numeric",
1329
+ month: "long",
1330
+ day: "numeric"
1331
+ }).format(date);
1332
+ };
1333
+ /**
1334
+ * String pluralization utilities using the pluralize library
1335
+ *
1336
+ * Re-exports the pluralize library function for word pluralization.
1337
+ * Handles English pluralization rules including irregular forms.
1338
+ *
1339
+ * @example
1340
+ * ```typescript
1341
+ * pluralizeWord("cat"); // "cats"
1342
+ * pluralizeWord("mouse"); // "mice"
1343
+ * singularize("cats"); // "cat"
1344
+ * isPlural("cats"); // true
1345
+ * isSingular("cat"); // true
1346
+ * ```
1347
+ */
1348
+ var pluralizeWord = pluralize;
1349
+ var singularize = pluralize.singular;
1350
+ var isPlural = pluralize.isPlural;
1351
+ var isSingular = pluralize.isSingular;
1352
+ /**
1353
+ * Enhanced date utilities using date-fns library
1354
+ *
1355
+ * Re-exports commonly used date-fns functions for date manipulation and formatting.
1356
+ * Provides more reliable date handling than native Date methods.
1357
+ *
1358
+ * @param date - Date object or ISO string to format
1359
+ * @param formatStr - Format string (defaults to 'yyyy-MM-dd')
1360
+ * @returns Formatted date string
1361
+ * @example
1362
+ * ```typescript
1363
+ * formatDate(new Date(), 'yyyy-MM-dd'); // "2023-01-15"
1364
+ * formatDate(new Date(), 'MM/dd/yyyy'); // "01/15/2023"
1365
+ * parseDate('2023-01-15'); // Date object
1366
+ * isValidDate(new Date()); // true
1367
+ * addInterval(new Date(), { days: 7 }); // Date 7 days from now
1368
+ * ```
1369
+ */
1370
+ var formatDate = (date, formatStr = "yyyy-MM-dd") => {
1371
+ return format(typeof date === "string" ? new Date(date) : date, formatStr);
1372
+ };
1373
+ /**
1374
+ * Parses a date string into a Date object
1375
+ *
1376
+ * When a format string is provided, uses date-fns `parse` with that format.
1377
+ * Otherwise falls back to `parseISO` for ISO 8601 strings.
1378
+ *
1379
+ * @param dateStr - The date string to parse
1380
+ * @param formatStr - Optional format pattern (e.g., 'MM/dd/yyyy', 'dd-MMM-yyyy')
1381
+ * @returns Parsed Date object
1382
+ * @example
1383
+ * ```typescript
1384
+ * parseDate('2023-01-15'); // ISO parse
1385
+ * parseDate('01/15/2023', 'MM/dd/yyyy'); // Custom format parse
1386
+ * ```
1387
+ */
1388
+ var parseDate = (dateStr, formatStr) => {
1389
+ if (formatStr) return parse(dateStr, formatStr, /* @__PURE__ */ new Date());
1390
+ return parseISO(dateStr);
1391
+ };
1392
+ var isValidDate = isValid;
1393
+ var addInterval = add;
1394
+ /**
1395
+ * Gets a temporary directory path (cross-platform)
1396
+ *
1397
+ * Creates a platform-appropriate temporary directory path under the .have-sdk namespace.
1398
+ * Uses environment variables in Node.js or falls back to /tmp.
1399
+ *
1400
+ * @param subfolder - Optional subfolder name within the temp directory
1401
+ * @returns Cross-platform temporary directory path
1402
+ * @example
1403
+ * ```typescript
1404
+ * getTempDirectory(); // "/tmp/.have-sdk" (Unix) or equivalent
1405
+ * getTempDirectory("cache"); // "/tmp/.have-sdk/cache"
1406
+ * ```
1407
+ */
1408
+ var getTempDirectory = (subfolder) => {
1409
+ const basePath = `${process?.env ? process.env.TMPDIR || process.env.TMP || process.env.TEMP || "/tmp" : "/tmp"}/.have-sdk`;
1410
+ return subfolder ? `${basePath}/${subfolder}` : basePath;
1411
+ };
1412
+ //#endregion
1413
+ export { ApiError as A, enableLogging as B, prettyDate as C, urlFilename as D, snakeCase as E, NetworkError as F, extractAllCodeBlocks as G, setLogger as H, ParsingError as I, extractJSON as J, extractCodeBlock as K, TimeoutError as L, DatabaseError as M, ErrorCode as N, urlPath as O, FileError as P, toScreamingSnakeCase as Q, ValidationError as R, pluralizeWord as S, sleep as T, isSafeCode as U, getLogger as V, validateCode as W, loadEnvConfig as X, convertType as Y, toCamelCase as Z, logTicker as _, domainToCamel as a, parseAmazonDateString as b, isArray as c, isPlural as d, isSingular as f, keysToSnake as g, keysToCamel as h, dateInString as i, BaseError as j, waitFor as k, isCuid as l, isValidDate as m, camelCase as n, formatDate as o, isUrl as p, extractFunctionDefinition as q, createId$1 as r, getTempDirectory as s, addInterval as t, isPlainObject as u, makeId as v, singularize as w, parseDate as x, makeSlug as y, disableLogging as z };
1414
+
1415
+ //# sourceMappingURL=universal-DOIuUel_.js.map