@sylphx/flow 1.0.1 → 1.0.3

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.
Files changed (229) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +10 -9
  3. package/src/commands/codebase-command.ts +168 -0
  4. package/src/commands/flow-command.ts +1137 -0
  5. package/src/commands/flow-orchestrator.ts +296 -0
  6. package/src/commands/hook-command.ts +444 -0
  7. package/src/commands/init-command.ts +92 -0
  8. package/src/commands/init-core.ts +322 -0
  9. package/src/commands/knowledge-command.ts +161 -0
  10. package/src/commands/run-command.ts +120 -0
  11. package/src/components/benchmark-monitor.tsx +331 -0
  12. package/src/components/reindex-progress.tsx +261 -0
  13. package/src/composables/functional/index.ts +14 -0
  14. package/src/composables/functional/useEnvironment.ts +171 -0
  15. package/src/composables/functional/useFileSystem.ts +139 -0
  16. package/src/composables/index.ts +5 -0
  17. package/src/composables/useEnv.ts +13 -0
  18. package/src/composables/useRuntimeConfig.ts +27 -0
  19. package/src/composables/useTargetConfig.ts +45 -0
  20. package/src/config/ai-config.ts +376 -0
  21. package/src/config/constants.ts +35 -0
  22. package/src/config/index.ts +27 -0
  23. package/src/config/rules.ts +43 -0
  24. package/src/config/servers.ts +371 -0
  25. package/src/config/targets.ts +126 -0
  26. package/src/core/agent-loader.ts +141 -0
  27. package/src/core/agent-manager.ts +174 -0
  28. package/src/core/ai-sdk.ts +603 -0
  29. package/src/core/app-factory.ts +381 -0
  30. package/src/core/builtin-agents.ts +9 -0
  31. package/src/core/command-system.ts +550 -0
  32. package/src/core/config-system.ts +550 -0
  33. package/src/core/connection-pool.ts +390 -0
  34. package/src/core/di-container.ts +155 -0
  35. package/src/core/error-handling.ts +519 -0
  36. package/src/core/formatting/bytes.test.ts +115 -0
  37. package/src/core/formatting/bytes.ts +64 -0
  38. package/src/core/functional/async.ts +313 -0
  39. package/src/core/functional/either.ts +109 -0
  40. package/src/core/functional/error-handler.ts +135 -0
  41. package/src/core/functional/error-types.ts +311 -0
  42. package/src/core/functional/index.ts +19 -0
  43. package/src/core/functional/option.ts +142 -0
  44. package/src/core/functional/pipe.ts +189 -0
  45. package/src/core/functional/result.ts +204 -0
  46. package/src/core/functional/validation.ts +138 -0
  47. package/src/core/headless-display.ts +96 -0
  48. package/src/core/index.ts +6 -0
  49. package/src/core/installers/file-installer.ts +303 -0
  50. package/src/core/installers/mcp-installer.ts +213 -0
  51. package/src/core/interfaces/index.ts +22 -0
  52. package/src/core/interfaces/repository.interface.ts +91 -0
  53. package/src/core/interfaces/service.interface.ts +133 -0
  54. package/src/core/interfaces.ts +129 -0
  55. package/src/core/loop-controller.ts +200 -0
  56. package/src/core/result.ts +351 -0
  57. package/src/core/rule-loader.ts +147 -0
  58. package/src/core/rule-manager.ts +240 -0
  59. package/src/core/service-config.ts +252 -0
  60. package/src/core/session-service.ts +121 -0
  61. package/src/core/state-detector.ts +389 -0
  62. package/src/core/storage-factory.ts +115 -0
  63. package/src/core/stream-handler.ts +288 -0
  64. package/src/core/target-manager.ts +161 -0
  65. package/src/core/type-utils.ts +427 -0
  66. package/src/core/unified-storage.ts +456 -0
  67. package/src/core/upgrade-manager.ts +300 -0
  68. package/src/core/validation/limit.test.ts +155 -0
  69. package/src/core/validation/limit.ts +46 -0
  70. package/src/core/validation/query.test.ts +44 -0
  71. package/src/core/validation/query.ts +20 -0
  72. package/src/db/auto-migrate.ts +322 -0
  73. package/src/db/base-database-client.ts +144 -0
  74. package/src/db/cache-db.ts +218 -0
  75. package/src/db/cache-schema.ts +75 -0
  76. package/src/db/database.ts +70 -0
  77. package/src/db/index.ts +252 -0
  78. package/src/db/memory-db.ts +153 -0
  79. package/src/db/memory-schema.ts +29 -0
  80. package/src/db/schema.ts +289 -0
  81. package/src/db/session-repository.ts +733 -0
  82. package/src/domains/codebase/index.ts +5 -0
  83. package/src/domains/codebase/tools.ts +139 -0
  84. package/src/domains/index.ts +8 -0
  85. package/src/domains/knowledge/index.ts +10 -0
  86. package/src/domains/knowledge/resources.ts +537 -0
  87. package/src/domains/knowledge/tools.ts +174 -0
  88. package/src/domains/utilities/index.ts +6 -0
  89. package/src/domains/utilities/time/index.ts +5 -0
  90. package/src/domains/utilities/time/tools.ts +291 -0
  91. package/src/index.ts +211 -0
  92. package/src/services/agent-service.ts +273 -0
  93. package/src/services/claude-config-service.ts +252 -0
  94. package/src/services/config-service.ts +258 -0
  95. package/src/services/evaluation-service.ts +271 -0
  96. package/src/services/functional/evaluation-logic.ts +296 -0
  97. package/src/services/functional/file-processor.ts +273 -0
  98. package/src/services/functional/index.ts +12 -0
  99. package/src/services/index.ts +13 -0
  100. package/src/services/mcp-service.ts +432 -0
  101. package/src/services/memory.service.ts +476 -0
  102. package/src/services/search/base-indexer.ts +156 -0
  103. package/src/services/search/codebase-indexer-types.ts +38 -0
  104. package/src/services/search/codebase-indexer.ts +647 -0
  105. package/src/services/search/embeddings-provider.ts +455 -0
  106. package/src/services/search/embeddings.ts +316 -0
  107. package/src/services/search/functional-indexer.ts +323 -0
  108. package/src/services/search/index.ts +27 -0
  109. package/src/services/search/indexer.ts +380 -0
  110. package/src/services/search/knowledge-indexer.ts +422 -0
  111. package/src/services/search/semantic-search.ts +244 -0
  112. package/src/services/search/tfidf.ts +559 -0
  113. package/src/services/search/unified-search-service.ts +888 -0
  114. package/src/services/smart-config-service.ts +385 -0
  115. package/src/services/storage/cache-storage.ts +487 -0
  116. package/src/services/storage/drizzle-storage.ts +581 -0
  117. package/src/services/storage/index.ts +15 -0
  118. package/src/services/storage/lancedb-vector-storage.ts +494 -0
  119. package/src/services/storage/memory-storage.ts +268 -0
  120. package/src/services/storage/separated-storage.ts +467 -0
  121. package/src/services/storage/vector-storage.ts +13 -0
  122. package/src/shared/agents/index.ts +63 -0
  123. package/src/shared/files/index.ts +99 -0
  124. package/src/shared/index.ts +32 -0
  125. package/src/shared/logging/index.ts +24 -0
  126. package/src/shared/processing/index.ts +153 -0
  127. package/src/shared/types/index.ts +25 -0
  128. package/src/targets/claude-code.ts +574 -0
  129. package/src/targets/functional/claude-code-logic.ts +185 -0
  130. package/src/targets/functional/index.ts +6 -0
  131. package/src/targets/opencode.ts +529 -0
  132. package/src/types/agent.types.ts +32 -0
  133. package/src/types/api/batch.ts +108 -0
  134. package/src/types/api/errors.ts +118 -0
  135. package/src/types/api/index.ts +55 -0
  136. package/src/types/api/requests.ts +76 -0
  137. package/src/types/api/responses.ts +180 -0
  138. package/src/types/api/websockets.ts +85 -0
  139. package/src/types/api.types.ts +9 -0
  140. package/src/types/benchmark.ts +49 -0
  141. package/src/types/cli.types.ts +87 -0
  142. package/src/types/common.types.ts +35 -0
  143. package/src/types/database.types.ts +510 -0
  144. package/src/types/mcp-config.types.ts +448 -0
  145. package/src/types/mcp.types.ts +69 -0
  146. package/src/types/memory-types.ts +63 -0
  147. package/src/types/provider.types.ts +28 -0
  148. package/src/types/rule.types.ts +24 -0
  149. package/src/types/session.types.ts +214 -0
  150. package/src/types/target-config.types.ts +295 -0
  151. package/src/types/target.types.ts +140 -0
  152. package/src/types/todo.types.ts +25 -0
  153. package/src/types.ts +40 -0
  154. package/src/utils/advanced-tokenizer.ts +191 -0
  155. package/src/utils/agent-enhancer.ts +114 -0
  156. package/src/utils/ai-model-fetcher.ts +19 -0
  157. package/src/utils/async-file-operations.ts +516 -0
  158. package/src/utils/audio-player.ts +345 -0
  159. package/src/utils/cli-output.ts +266 -0
  160. package/src/utils/codebase-helpers.ts +211 -0
  161. package/src/utils/console-ui.ts +79 -0
  162. package/src/utils/database-errors.ts +140 -0
  163. package/src/utils/debug-logger.ts +49 -0
  164. package/src/utils/error-handler.ts +53 -0
  165. package/src/utils/file-operations.ts +310 -0
  166. package/src/utils/file-scanner.ts +259 -0
  167. package/src/utils/functional/array.ts +355 -0
  168. package/src/utils/functional/index.ts +15 -0
  169. package/src/utils/functional/object.ts +279 -0
  170. package/src/utils/functional/string.ts +281 -0
  171. package/src/utils/functional.ts +543 -0
  172. package/src/utils/help.ts +20 -0
  173. package/src/utils/immutable-cache.ts +106 -0
  174. package/src/utils/index.ts +78 -0
  175. package/src/utils/jsonc.ts +158 -0
  176. package/src/utils/logger.ts +396 -0
  177. package/src/utils/mcp-config.ts +249 -0
  178. package/src/utils/memory-tui.ts +414 -0
  179. package/src/utils/models-dev.ts +91 -0
  180. package/src/utils/notifications.ts +169 -0
  181. package/src/utils/object-utils.ts +51 -0
  182. package/src/utils/parallel-operations.ts +487 -0
  183. package/src/utils/paths.ts +143 -0
  184. package/src/utils/process-manager.ts +155 -0
  185. package/src/utils/prompts.ts +120 -0
  186. package/src/utils/search-tool-builder.ts +214 -0
  187. package/src/utils/secret-utils.ts +179 -0
  188. package/src/utils/security.ts +537 -0
  189. package/src/utils/session-manager.ts +168 -0
  190. package/src/utils/session-title.ts +87 -0
  191. package/src/utils/settings.ts +182 -0
  192. package/src/utils/simplified-errors.ts +410 -0
  193. package/src/utils/sync-utils.ts +159 -0
  194. package/src/utils/target-config.ts +570 -0
  195. package/src/utils/target-utils.ts +394 -0
  196. package/src/utils/template-engine.ts +94 -0
  197. package/src/utils/test-audio.ts +71 -0
  198. package/src/utils/todo-context.ts +46 -0
  199. package/src/utils/token-counter.ts +288 -0
  200. package/dist/index.d.ts +0 -10
  201. package/dist/index.js +0 -59554
  202. package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
  203. package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
  204. package/dist/shared/chunk-25dwp0dp.js +0 -89
  205. package/dist/shared/chunk-3pjb6063.js +0 -208
  206. package/dist/shared/chunk-4d6ydpw7.js +0 -2854
  207. package/dist/shared/chunk-4wjcadjk.js +0 -225
  208. package/dist/shared/chunk-5j4w74t6.js +0 -30
  209. package/dist/shared/chunk-5j8m3dh3.js +0 -58
  210. package/dist/shared/chunk-5thh3qem.js +0 -91
  211. package/dist/shared/chunk-6g9xy73m.js +0 -252
  212. package/dist/shared/chunk-7eq34c42.js +0 -23
  213. package/dist/shared/chunk-c2gwgx3r.js +0 -115
  214. package/dist/shared/chunk-cjd3mk4c.js +0 -1320
  215. package/dist/shared/chunk-g5cv6703.js +0 -368
  216. package/dist/shared/chunk-hpkhykhq.js +0 -574
  217. package/dist/shared/chunk-m2322pdk.js +0 -122
  218. package/dist/shared/chunk-nd5fdvaq.js +0 -26
  219. package/dist/shared/chunk-pgd3m6zf.js +0 -108
  220. package/dist/shared/chunk-qk8n91hw.js +0 -494
  221. package/dist/shared/chunk-rkkn8szp.js +0 -16855
  222. package/dist/shared/chunk-t16rfxh0.js +0 -61
  223. package/dist/shared/chunk-t4fbfa5v.js +0 -19
  224. package/dist/shared/chunk-t77h86w6.js +0 -276
  225. package/dist/shared/chunk-v0ez4aef.js +0 -71
  226. package/dist/shared/chunk-v29j2r3s.js +0 -32051
  227. package/dist/shared/chunk-vfbc6ew5.js +0 -765
  228. package/dist/shared/chunk-vmeqwm1c.js +0 -204
  229. package/dist/shared/chunk-x66eh37x.js +0 -137
@@ -0,0 +1,427 @@
1
+ /**
2
+ * Type Utilities - 類型安全工具
3
+ * Functional approach to type safety and validation
4
+ */
5
+
6
+ import { z } from 'zod';
7
+
8
+ // Re-export unified Result type and utilities
9
+ export {
10
+ Result,
11
+ AsyncResult,
12
+ ok,
13
+ err,
14
+ isOk,
15
+ isErr,
16
+ map,
17
+ flatMap,
18
+ mapError,
19
+ getOrElse,
20
+ getOrElseLazy,
21
+ match,
22
+ unwrap,
23
+ tryCatch,
24
+ tryCatchAsync,
25
+ safeAsync,
26
+ safeSync,
27
+ all,
28
+ allAsync,
29
+ tap,
30
+ tapError,
31
+ type SuccessType,
32
+ type ErrorType,
33
+ type SafeResult,
34
+ } from './result.js';
35
+
36
+ /**
37
+ * Common validation schemas
38
+ */
39
+ export const Schemas = {
40
+ /** Non-empty string validation */
41
+ nonEmptyString: z.string().min(1, 'String cannot be empty'),
42
+
43
+ /** Optional non-empty string */
44
+ optionalNonEmptyString: z.string().min(1).optional(),
45
+
46
+ /** Positive number validation */
47
+ positiveNumber: z.number().positive('Number must be positive'),
48
+
49
+ /** Optional positive number */
50
+ optionalPositiveNumber: z.number().positive().optional(),
51
+
52
+ /** Array validation */
53
+ nonEmptyArray: z.array(z.any()).min(1, 'Array cannot be empty'),
54
+
55
+ /** Email validation */
56
+ email: z.string().email('Invalid email format'),
57
+
58
+ /** URL validation */
59
+ url: z.string().url('Invalid URL format'),
60
+
61
+ /** Object validation */
62
+ object: z.object({}).passthrough(),
63
+
64
+ /** Storage configuration */
65
+ storageConfig: z.object({
66
+ type: z.enum(['memory', 'cache', 'vector', 'drizzle']),
67
+ connectionString: z.string().optional(),
68
+ defaultTTL: z.number().positive().optional(),
69
+ maxCacheSize: z.number().positive().optional(),
70
+ vectorDimensions: z.number().positive().optional(),
71
+ storageDir: z.string().optional(),
72
+ }),
73
+
74
+ /** CLI options */
75
+ cliOptions: z.object({
76
+ target: z.string().optional(),
77
+ verbose: z.boolean().default(false),
78
+ dryRun: z.boolean().default(false),
79
+ clear: z.boolean().default(false),
80
+ mcp: z.union([z.array(z.string()), z.boolean(), z.null()]).default(null),
81
+ quiet: z.boolean().default(false),
82
+ agent: z.string().optional(),
83
+ }),
84
+ } as const;
85
+
86
+ /**
87
+ * Type guards
88
+ */
89
+ export const TypeGuards = {
90
+ /** Check if value is a non-empty string */
91
+ isNonEmptyString: (value: unknown): value is string => {
92
+ return typeof value === 'string' && value.length > 0;
93
+ },
94
+
95
+ /** Check if value is a positive number */
96
+ isPositiveNumber: (value: unknown): value is number => {
97
+ return typeof value === 'number' && value > 0;
98
+ },
99
+
100
+ /** Check if value is a non-empty array */
101
+ isNonEmptyArray: (value: unknown): value is unknown[] => {
102
+ return Array.isArray(value) && value.length > 0;
103
+ },
104
+
105
+ /** Check if value is an object */
106
+ isObject: (value: unknown): value is Record<string, unknown> => {
107
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
108
+ },
109
+
110
+ /** Check if value is a function */
111
+ isFunction: (value: unknown): value is Function => {
112
+ return typeof value === 'function';
113
+ },
114
+
115
+ /** Check if value is a Date */
116
+ isDate: (value: unknown): value is Date => {
117
+ return value instanceof Date && !isNaN(value.getTime());
118
+ },
119
+
120
+ /** Check if value is a Buffer */
121
+ isBuffer: (value: unknown): value is Buffer => {
122
+ return Buffer.isBuffer(value);
123
+ },
124
+ } as const;
125
+
126
+ /**
127
+ * Safe parsing utilities
128
+ */
129
+ export const SafeParse = {
130
+ /** Parse JSON safely */
131
+ json: <T = unknown>(str: string): Result<T> => {
132
+ return safeSync(() => JSON.parse(str) as T,
133
+ error => new Error(`Invalid JSON: ${error instanceof Error ? error.message : String(error)}`));
134
+ },
135
+
136
+ /** Parse number safely */
137
+ number: (str: string, radix = 10): Result<number> => {
138
+ return safeSync(() => {
139
+ const num = parseInt(str, radix);
140
+ if (isNaN(num)) throw new Error(`Invalid number: ${str}`);
141
+ return num;
142
+ }, error => new Error(`Failed to parse number: ${error instanceof Error ? error.message : String(error)}`));
143
+ },
144
+
145
+ /** Parse float safely */
146
+ float: (str: string): Result<number> => {
147
+ return safeSync(() => {
148
+ const num = parseFloat(str);
149
+ if (isNaN(num)) throw new Error(`Invalid float: ${str}`);
150
+ return num;
151
+ }, error => new Error(`Failed to parse float: ${error instanceof Error ? error.message : String(error)}`));
152
+ },
153
+
154
+ /** Parse boolean safely */
155
+ boolean: (str: string): Result<boolean> => {
156
+ const lower = str.toLowerCase();
157
+ if (['true', '1', 'yes', 'on'].includes(lower)) return ok(true);
158
+ if (['false', '0', 'no', 'off'].includes(lower)) return ok(false);
159
+ return err(new Error(`Invalid boolean: ${str}`));
160
+ },
161
+ } as const;
162
+
163
+ /**
164
+ * String utilities
165
+ */
166
+ export const StringUtils = {
167
+ /** Truncate string to max length */
168
+ truncate: (str: string, maxLength: number, suffix = '...'): string => {
169
+ if (str.length <= maxLength) return str;
170
+ return str.slice(0, maxLength - suffix.length) + suffix;
171
+ },
172
+
173
+ /** Convert to kebab-case */
174
+ kebabCase: (str: string): string => {
175
+ return str
176
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
177
+ .replace(/[\s_]+/g, '-')
178
+ .toLowerCase();
179
+ },
180
+
181
+ /** Convert to camelCase */
182
+ camelCase: (str: string): string => {
183
+ return str
184
+ .replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
185
+ .replace(/^[A-Z]/, char => char.toLowerCase());
186
+ },
187
+
188
+ /** Convert to PascalCase */
189
+ pascalCase: (str: string): string => {
190
+ const camel = StringUtils.camelCase(str);
191
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
192
+ },
193
+
194
+ /** Capitalize first letter */
195
+ capitalize: (str: string): string => {
196
+ return str.charAt(0).toUpperCase() + str.slice(1);
197
+ },
198
+
199
+ /** Check if string is empty or whitespace */
200
+ isEmpty: (str: string): boolean => {
201
+ return str.trim().length === 0;
202
+ },
203
+
204
+ /** Generate random string */
205
+ random: (length = 8): string => {
206
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
207
+ let result = '';
208
+ for (let i = 0; i < length; i++) {
209
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
210
+ }
211
+ return result;
212
+ },
213
+
214
+ /** Escape regex special characters */
215
+ escapeRegex: (str: string): string => {
216
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
217
+ },
218
+ } as const;
219
+
220
+ /**
221
+ * Array utilities
222
+ */
223
+ export const ArrayUtils = {
224
+ /** Check if array is empty */
225
+ isEmpty: <T>(arr: T[]): boolean => arr.length === 0,
226
+
227
+ /** Remove duplicates */
228
+ unique: <T>(arr: T[]): T[] => [...new Set(arr)],
229
+
230
+ /** Group array by key */
231
+ groupBy: <T, K extends string | number>(
232
+ arr: T[],
233
+ keyFn: (item: T) => K
234
+ ): Record<K, T[]> => {
235
+ return arr.reduce((groups, item) => {
236
+ const key = keyFn(item);
237
+ if (!groups[key]) groups[key] = [];
238
+ groups[key].push(item);
239
+ return groups;
240
+ }, {} as Record<K, T[]>);
241
+ },
242
+
243
+ /** Chunk array into smaller arrays */
244
+ chunk: <T>(arr: T[], size: number): T[][] => {
245
+ const chunks: T[][] = [];
246
+ for (let i = 0; i < arr.length; i += size) {
247
+ chunks.push(arr.slice(i, i + size));
248
+ }
249
+ return chunks;
250
+ },
251
+
252
+ /** Flatten nested arrays */
253
+ flatten: <T>(arr: (T | T[])[]): T[] => {
254
+ return arr.reduce<T[]>((flat, item) => {
255
+ return flat.concat(Array.isArray(item) ? ArrayUtils.flatten(item) : item);
256
+ }, []);
257
+ },
258
+
259
+ /** Pick random element */
260
+ sample: <T>(arr: T[]): T | undefined => {
261
+ return arr[Math.floor(Math.random() * arr.length)];
262
+ },
263
+
264
+ /** Shuffle array */
265
+ shuffle: <T>(arr: T[]): T[] => {
266
+ const shuffled = [...arr];
267
+ for (let i = shuffled.length - 1; i > 0; i--) {
268
+ const j = Math.floor(Math.random() * (i + 1));
269
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
270
+ }
271
+ return shuffled;
272
+ },
273
+ } as const;
274
+
275
+ /**
276
+ * Object utilities
277
+ */
278
+ export const ObjectUtils = {
279
+ /** Check if object is empty */
280
+ isEmpty: (obj: Record<string, unknown>): boolean => {
281
+ return Object.keys(obj).length === 0;
282
+ },
283
+
284
+ /** Pick specific keys from object */
285
+ pick: <T extends Record<string, unknown>, K extends keyof T>(
286
+ obj: T,
287
+ keys: K[]
288
+ ): Pick<T, K> => {
289
+ return keys.reduce((picked, key) => {
290
+ if (key in obj) {
291
+ picked[key] = obj[key];
292
+ }
293
+ return picked;
294
+ }, {} as Pick<T, K>);
295
+ },
296
+
297
+ /** Omit specific keys from object */
298
+ omit: <T extends Record<string, unknown>, K extends keyof T>(
299
+ obj: T,
300
+ keys: K[]
301
+ ): Omit<T, K> => {
302
+ const result = { ...obj };
303
+ keys.forEach(key => delete result[key]);
304
+ return result as Omit<T, K>;
305
+ },
306
+
307
+ /** Deep clone object */
308
+ deepClone: <T>(obj: T): T => {
309
+ return JSON.parse(JSON.stringify(obj));
310
+ },
311
+
312
+ /** Merge objects */
313
+ merge: <T extends Record<string, unknown>>(
314
+ ...objects: Partial<T>[]
315
+ ): T => {
316
+ return objects.reduce((merged, obj) => ({ ...merged, ...obj }), {} as T);
317
+ },
318
+
319
+ /** Get nested value from object */
320
+ get: (obj: any, path: string, defaultValue?: unknown): unknown => {
321
+ const keys = path.split('.');
322
+ let current = obj;
323
+
324
+ for (const key of keys) {
325
+ if (current == null || typeof current !== 'object') {
326
+ return defaultValue;
327
+ }
328
+ current = current[key];
329
+ }
330
+
331
+ return current !== undefined ? current : defaultValue;
332
+ },
333
+
334
+ /** Set nested value in object */
335
+ set: (obj: any, path: string, value: unknown): void => {
336
+ const keys = path.split('.');
337
+ let current = obj;
338
+
339
+ for (let i = 0; i < keys.length - 1; i++) {
340
+ const key = keys[i];
341
+ if (!(key in current) || typeof current[key] !== 'object') {
342
+ current[key] = {};
343
+ }
344
+ current = current[key];
345
+ }
346
+
347
+ current[keys[keys.length - 1]] = value;
348
+ },
349
+ } as const;
350
+
351
+ /**
352
+ * Function utilities
353
+ */
354
+ export const FunctionUtils = {
355
+ /** Debounce function */
356
+ debounce: <T extends (...args: any[]) => any>(
357
+ fn: T,
358
+ delay: number
359
+ ): ((...args: Parameters<T>) => void) => {
360
+ let timeoutId: NodeJS.Timeout;
361
+ return (...args: Parameters<T>) => {
362
+ clearTimeout(timeoutId);
363
+ timeoutId = setTimeout(() => fn(...args), delay);
364
+ };
365
+ },
366
+
367
+ /** Throttle function */
368
+ throttle: <T extends (...args: any[]) => any>(
369
+ fn: T,
370
+ delay: number
371
+ ): ((...args: Parameters<T>) => void) => {
372
+ let lastCall = 0;
373
+ return (...args: Parameters<T>) => {
374
+ const now = Date.now();
375
+ if (now - lastCall >= delay) {
376
+ lastCall = now;
377
+ fn(...args);
378
+ }
379
+ };
380
+ },
381
+
382
+ /** Memoize function */
383
+ memoize: <T extends (...args: any[]) => any>(
384
+ fn: T,
385
+ keyFn?: (...args: Parameters<T>) => string
386
+ ): T => {
387
+ const cache = new Map<string, ReturnType<T>>();
388
+
389
+ return ((...args: Parameters<T>) => {
390
+ const key = keyFn ? keyFn(...args) : JSON.stringify(args);
391
+
392
+ if (cache.has(key)) {
393
+ return cache.get(key);
394
+ }
395
+
396
+ const result = fn(...args);
397
+ cache.set(key, result);
398
+ return result;
399
+ }) as T;
400
+ },
401
+
402
+ /** Retry function */
403
+ retry: async <T>(
404
+ fn: () => Promise<T>,
405
+ maxAttempts = 3,
406
+ delay = 1000
407
+ ): Promise<T> => {
408
+ let lastError: Error;
409
+
410
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
411
+ try {
412
+ return await fn();
413
+ } catch (error) {
414
+ lastError = error instanceof Error ? error : new Error(String(error));
415
+
416
+ if (attempt === maxAttempts) {
417
+ throw lastError;
418
+ }
419
+
420
+ // Exponential backoff
421
+ await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, attempt - 1)));
422
+ }
423
+ }
424
+
425
+ throw lastError!;
426
+ },
427
+ } as const;