@talex-touch/utils 1.0.18 → 1.0.20

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 (80) hide show
  1. package/channel/index.ts +49 -1
  2. package/common/index.ts +2 -0
  3. package/common/search/gather.ts +45 -0
  4. package/common/search/index.ts +67 -0
  5. package/common/storage/constants.ts +16 -2
  6. package/common/storage/entity/index.ts +2 -1
  7. package/common/storage/entity/openers.ts +32 -0
  8. package/common/storage/entity/shortcut-settings.ts +22 -0
  9. package/common/storage/shortcut-storage.ts +58 -0
  10. package/common/utils/file.ts +62 -0
  11. package/common/{utils.ts → utils/index.ts} +14 -2
  12. package/common/utils/polling.ts +184 -0
  13. package/common/utils/task-queue.ts +108 -0
  14. package/common/utils/time.ts +374 -0
  15. package/core-box/README.md +8 -8
  16. package/core-box/builder/index.ts +6 -0
  17. package/core-box/builder/tuff-builder.example.ts.bak +258 -0
  18. package/core-box/builder/tuff-builder.ts +1162 -0
  19. package/core-box/index.ts +5 -2
  20. package/core-box/run-tests.sh +7 -0
  21. package/core-box/search.ts +1 -536
  22. package/core-box/tuff/index.ts +6 -0
  23. package/core-box/tuff/tuff-dsl.ts +1412 -0
  24. package/electron/clipboard-helper.ts +199 -0
  25. package/electron/env-tool.ts +36 -2
  26. package/electron/file-parsers/index.ts +8 -0
  27. package/electron/file-parsers/parsers/text-parser.ts +109 -0
  28. package/electron/file-parsers/registry.ts +92 -0
  29. package/electron/file-parsers/types.ts +58 -0
  30. package/electron/index.ts +3 -0
  31. package/eventbus/index.ts +0 -7
  32. package/index.ts +3 -1
  33. package/package.json +4 -29
  34. package/plugin/channel.ts +48 -16
  35. package/plugin/index.ts +194 -30
  36. package/plugin/log/types.ts +11 -0
  37. package/plugin/node/index.ts +4 -0
  38. package/plugin/node/logger-manager.ts +113 -0
  39. package/plugin/{log → node}/logger.ts +41 -7
  40. package/plugin/plugin-source.ts +74 -0
  41. package/plugin/preload.ts +5 -15
  42. package/plugin/providers/index.ts +2 -0
  43. package/plugin/providers/registry.ts +47 -0
  44. package/plugin/providers/types.ts +54 -0
  45. package/plugin/risk/index.ts +1 -0
  46. package/plugin/risk/types.ts +20 -0
  47. package/plugin/sdk/enum/bridge-event.ts +4 -0
  48. package/plugin/sdk/enum/index.ts +1 -0
  49. package/plugin/sdk/hooks/bridge.ts +68 -0
  50. package/plugin/sdk/hooks/index.ts +2 -1
  51. package/plugin/sdk/hooks/life-cycle.ts +2 -4
  52. package/plugin/sdk/index.ts +2 -0
  53. package/plugin/sdk/storage.ts +84 -0
  54. package/plugin/sdk/types.ts +2 -2
  55. package/plugin/sdk/window/index.ts +5 -3
  56. package/preload/index.ts +2 -0
  57. package/preload/loading.ts +15 -0
  58. package/preload/renderer.ts +41 -0
  59. package/renderer/hooks/arg-mapper.ts +79 -0
  60. package/renderer/hooks/index.ts +2 -0
  61. package/renderer/hooks/initialize.ts +198 -0
  62. package/renderer/index.ts +3 -0
  63. package/renderer/storage/app-settings.ts +2 -0
  64. package/renderer/storage/base-storage.ts +1 -0
  65. package/renderer/storage/openers.ts +11 -0
  66. package/renderer/touch-sdk/env.ts +106 -0
  67. package/renderer/touch-sdk/index.ts +108 -0
  68. package/renderer/touch-sdk/terminal.ts +85 -0
  69. package/renderer/touch-sdk/utils.ts +61 -0
  70. package/search/levenshtein-utils.ts +39 -0
  71. package/search/types.ts +16 -16
  72. package/types/index.ts +2 -1
  73. package/types/modules/base.ts +146 -0
  74. package/types/modules/index.ts +4 -0
  75. package/types/modules/module-lifecycle.ts +148 -0
  76. package/types/modules/module-manager.ts +99 -0
  77. package/types/modules/module.ts +112 -0
  78. package/types/touch-app-core.ts +16 -93
  79. package/core-box/types.ts +0 -384
  80. package/plugin/log/logger-manager.ts +0 -60
@@ -0,0 +1,108 @@
1
+ type ConsoleLike = Pick<typeof console, 'debug' | 'warn'>
2
+
3
+ export interface AdaptiveTaskQueueOptions {
4
+ /**
5
+ * 估算单个任务的执行耗时(毫秒)。用于动态计算每批处理量。
6
+ * 若不确定,可传入 1~2 之间的经验值。
7
+ */
8
+ estimatedTaskTimeMs?: number
9
+ /**
10
+ * 每次让出事件循环前的最长时间窗口(毫秒),默认约等于一帧的 17ms。
11
+ */
12
+ yieldIntervalMs?: number
13
+ /**
14
+ * 限制每批最大处理数量,避免估算误差导致批次过大。
15
+ */
16
+ maxBatchSize?: number
17
+ /**
18
+ * 自定义日志输出目标,默认使用 `console`。
19
+ */
20
+ logger?: ConsoleLike
21
+ /**
22
+ * 方便调试的标签,会出现在日志里。
23
+ */
24
+ label?: string
25
+ /**
26
+ * 每次批处理完成后触发,可用于自定义进度上报。
27
+ */
28
+ onYield?: (context: {
29
+ processed: number
30
+ total: number
31
+ batchSize: number
32
+ elapsedMs: number
33
+ }) => void
34
+ }
35
+
36
+ const DEFAULT_YIELD_INTERVAL_MS = 17
37
+
38
+ async function delay(ms: number): Promise<void> {
39
+ if (ms <= 0) return
40
+ await new Promise<void>((resolve) => setTimeout(resolve, ms))
41
+ }
42
+
43
+ /**
44
+ * 以“自适应批处理”方式执行一组任务。
45
+ *
46
+ * - 根据传入的任务量与预估耗时动态计算批大小
47
+ * - 每批处理后,会等待(默认 17ms)以释放事件循环,避免主线程卡顿
48
+ * - 支持在批处理完成时回调进度
49
+ */
50
+ export async function runAdaptiveTaskQueue<T>(
51
+ items: readonly T[],
52
+ handler: (item: T, index: number) => Promise<void> | void,
53
+ options: AdaptiveTaskQueueOptions = {}
54
+ ): Promise<void> {
55
+ const total = items.length
56
+ if (total === 0) return
57
+
58
+ const {
59
+ estimatedTaskTimeMs = 1,
60
+ yieldIntervalMs = DEFAULT_YIELD_INTERVAL_MS,
61
+ maxBatchSize,
62
+ logger = console,
63
+ label = 'AdaptiveTaskQueue',
64
+ onYield
65
+ } = options
66
+
67
+ const safeTaskMs = Math.max(estimatedTaskTimeMs, 0.1)
68
+ const computedBatchSize = Math.max(1, Math.floor(yieldIntervalMs / safeTaskMs))
69
+ const batchSize = maxBatchSize ? Math.min(maxBatchSize, computedBatchSize) : computedBatchSize
70
+
71
+ const currentPerformance = typeof globalThis !== 'undefined' ? (globalThis as any)?.performance : undefined
72
+ const now = () => (currentPerformance ? currentPerformance.now() : Date.now())
73
+
74
+
75
+ const startTime = now()
76
+
77
+ logger.debug?.(
78
+ `[${label}] Starting queue for ${total} item(s). batchSize=${batchSize}, estimated=${safeTaskMs.toFixed(2)}ms`
79
+ )
80
+
81
+ for (let index = 0; index < total; index++) {
82
+ await handler(items[index], index)
83
+
84
+ const processed = index + 1
85
+ if (processed % batchSize === 0 && processed < total) {
86
+ const beforeYield = now()
87
+ await delay(yieldIntervalMs)
88
+ const afterYield = now()
89
+ const elapsedMs = beforeYield - startTime
90
+ logger.debug?.(
91
+ `[${label}] Yielded after ${processed}/${total} item(s); wait ${(
92
+ afterYield - beforeYield
93
+ ).toFixed(1)}ms`
94
+ )
95
+ onYield?.({
96
+ processed,
97
+ total,
98
+ batchSize,
99
+ elapsedMs
100
+ })
101
+ }
102
+ }
103
+
104
+ const endTime = now()
105
+ logger.debug?.(
106
+ `[${label}] Completed ${total} item(s) in ${((endTime - startTime) / 1000).toFixed(2)}s`
107
+ )
108
+ }
@@ -0,0 +1,374 @@
1
+ /**
2
+ * Custom error class representing an operation timeout.
3
+ * This error is thrown when a Promise does not resolve within a specified time limit.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * try {
8
+ * await withTimeout(someLongRunningPromise(), 1000);
9
+ * } catch (error) {
10
+ * if (error instanceof TimeoutError) {
11
+ * console.error('Operation took too long!');
12
+ * }
13
+ * }
14
+ * ```
15
+ */
16
+ export class TimeoutError extends Error {
17
+ /**
18
+ * Creates an instance of TimeoutError.
19
+ * @param message - An optional message describing the timeout error. Defaults to 'Operation timed out'.
20
+ */
21
+ constructor(message: string = 'Operation timed out') {
22
+ super(message);
23
+ this.name = 'TimeoutError';
24
+ // Maintain proper stack trace for the error.
25
+ if (Error.captureStackTrace) {
26
+ Error.captureStackTrace(this, TimeoutError);
27
+ }
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Executes a promise and throws a TimeoutError if it does not resolve within the specified time.
33
+ * This is a robust alternative to a timeout that resolves with a special value,
34
+ * as it uses idiomatic error handling (`try...catch`).
35
+ *
36
+ * @template T The type of the promise's resolution value.
37
+ * @param promise - The promise to execute.
38
+ * @param ms - The timeout duration in milliseconds. If `ms` is 0 or negative, the original promise is returned without a timeout.
39
+ * @returns A promise that resolves with the result of the input promise, or rejects with a `TimeoutError`.
40
+ * @throws {TimeoutError} If the promise does not resolve within the specified time.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * import { withTimeout, TimeoutError } from '@talex-touch/utils';
45
+ *
46
+ * async function fetchDataWithTimeout() {
47
+ * try {
48
+ * const result = await withTimeout(fetch('https://api.example.com/data'), 3000); // 3-second timeout
49
+ * console.log('Data fetched:', await result.json());
50
+ * } catch (error) {
51
+ * if (error instanceof TimeoutError) {
52
+ * console.error('Fetch request timed out!');
53
+ * } else {
54
+ * console.error('Fetch failed:', error);
55
+ * }
56
+ * }
57
+ * }
58
+ *
59
+ * fetchDataWithTimeout();
60
+ * ```
61
+ */
62
+ export function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
63
+ if (ms <= 0) {
64
+ // If timeout is 0 or negative, return the original Promise without a timeout mechanism.
65
+ return promise;
66
+ }
67
+
68
+ // Create a promise that rejects in <ms> milliseconds
69
+ const timeout = new Promise<never>((_, reject) => {
70
+ const id = setTimeout(() => {
71
+ clearTimeout(id);
72
+ reject(new TimeoutError(`Promise timed out after ${ms} ms`));
73
+ }, ms);
74
+ });
75
+
76
+ // Race the input promise against the timeout promise
77
+ return Promise.race([promise, timeout]);
78
+ }
79
+
80
+ /**
81
+ * Represents a value that can either be a fixed type `T` or a function that dynamically
82
+ * computes `T` based on the current attempt number.
83
+ * @template T The type of the value.
84
+ */
85
+ type DynamicValue<T> = T | ((attempt: number) => T);
86
+
87
+ /**
88
+ * Defines a generic asynchronous function type that returns a Promise.
89
+ * @template T The type of the Promise's resolution value.
90
+ */
91
+ type AsyncFunction<T> = (...args: any[]) => Promise<T>;
92
+
93
+ /**
94
+ * Defines the options for configuring the retrier behavior.
95
+ */
96
+ export interface RetrierOptions {
97
+ /**
98
+ * The maximum number of retries.
99
+ * - If a `number`, it represents the fixed number of retries (e.g., `2` means 1 initial attempt + 2 retries = total 3 attempts).
100
+ * - If a `function`, it dynamically calculates the maximum retries based on the current attempt number (starting from 1).
101
+ * @defaultValue 2 (meaning 1 initial attempt + 2 retries, total 3 attempts)
102
+ */
103
+ maxRetries?: DynamicValue<number>;
104
+ /**
105
+ * The timeout duration for each individual attempt in milliseconds.
106
+ * - If a `number`, it represents a fixed timeout for each attempt.
107
+ * - If a `function`, it dynamically calculates the timeout based on the current attempt number.
108
+ * @defaultValue 5000 (5 seconds)
109
+ */
110
+ timeoutMs?: DynamicValue<number>;
111
+ /**
112
+ * Callback invoked at the beginning of each attempt.
113
+ * @param attempt - The current attempt number (starts from 1).
114
+ * @param error - The error from the previous attempt, if any (undefined for the first attempt).
115
+ */
116
+ onAttempt?: (attempt: number, error?: Error) => void;
117
+ /**
118
+ * Callback invoked upon successful completion of the operation.
119
+ * @template T The type of the successful result.
120
+ * @param result - The successful result of the operation.
121
+ * @param attempt - The attempt number on which the operation succeeded.
122
+ */
123
+ onSuccess?: <T>(result: T, attempt: number) => void;
124
+ /**
125
+ * Callback invoked when all retries have been exhausted and the operation ultimately fails.
126
+ * @param error - The final error that caused the operation to fail.
127
+ * @param attempt - The total number of attempts made before final failure.
128
+ */
129
+ onFailure?: (error: Error, attempt: number) => void;
130
+ /**
131
+ * Callback invoked before initiating a retry, indicating that a retry will occur.
132
+ * @param attempt - The attempt number that just failed and will lead to the next retry.
133
+ * @param error - The error that caused the current attempt to fail.
134
+ */
135
+ onRetry?: (attempt: number, error: Error) => void;
136
+ /**
137
+ * Callback invoked when a single attempt times out. Note that this is a specific type of failure that leads to a retry.
138
+ * @param attempt - The attempt number that timed out.
139
+ * @param ms - The timeout duration (in milliseconds) that was set for this specific attempt.
140
+ */
141
+ onTimeout?: (attempt: number, ms: number) => void;
142
+ /**
143
+ * A predicate function that determines whether a given error should trigger a retry.
144
+ * If this function returns `false`, the retrier will immediately stop and throw the error.
145
+ * By default, all errors will trigger a retry (returns `true`).
146
+ * @param error - The error that occurred.
147
+ * @param attempt - The current attempt number.
148
+ * @returns `true` if a retry should occur, `false` otherwise.
149
+ */
150
+ shouldRetry?: (error: Error, attempt: number) => boolean;
151
+ }
152
+
153
+ /**
154
+ * Creates a retrier factory function that can wrap any asynchronous method
155
+ * to add configurable retry logic with timeout and event callbacks.
156
+ *
157
+ * @param options - Configuration options for the retrier.
158
+ * @returns A higher-order function. This returned function takes an asynchronous method
159
+ * and returns a new, wrapped async method with the specified retry logic.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * import { createRetrier, TimeoutError } from '@talex-touch/utils';
164
+ *
165
+ * // Example of a flaky asynchronous task
166
+ * let globalCallCount = 0;
167
+ * async function flakyApiCall(data: string): Promise<string> {
168
+ * globalCallCount++;
169
+ * console.log(` [API] Attempt ${globalCallCount}: Calling API with ${data}...`);
170
+ * const delay = Math.random() * 1500 + 500; // 500ms to 2000ms
171
+ * await new Promise(resolve => setTimeout(resolve, delay));
172
+ *
173
+ * if (globalCallCount < 3) { // Fail first 2 attempts
174
+ * throw new Error(`API failed on attempt ${globalCallCount}`);
175
+ * }
176
+ * console.log(` [API] Attempt ${globalCallCount}: API call successful for ${data}!`);
177
+ * return `Data received: ${data} on attempt ${globalCallCount}`;
178
+ * }
179
+ *
180
+ * async function runCustomRetrier() {
181
+ * console.log('\n--- Running Custom Retrier Example ---');
182
+ * globalCallCount = 0; // Reset for example
183
+ *
184
+ * const myRetrier = createRetrier({
185
+ * maxRetries: 3, // Total 4 attempts
186
+ * timeoutMs: (attempt) => 1000 + (attempt * 500), // Increasing timeout: 1.5s, 2s, 2.5s, 3s
187
+ * onAttempt: (att, err) => console.log(`[Retrier] Attempt ${att} starting. Last err: ${err?.message || 'N/A'}`),
188
+ * onSuccess: (res, att) => console.log(`[Retrier] ✅ Success on attempt ${att}: ${res}`),
189
+ * onFailure: (err, att) => console.error(`[Retrier] ❌ Final failure after ${att} attempts: ${err.message}`),
190
+ * onRetry: (att, err) => console.warn(`[Retrier] 🔄 Retrying. Attempt ${att} failed with: ${err.message}`),
191
+ * onTimeout: (att, ms) => console.warn(`[Retrier] ⏰ Attempt ${att} timed out after ${ms}ms.`),
192
+ * shouldRetry: (error) => !(error instanceof TypeError), // Don't retry on TypeError
193
+ * });
194
+ *
195
+ * const wrappedFlakyApiCall = myRetrier(flakyApiCall);
196
+ *
197
+ * try {
198
+ * const result = await wrappedFlakyApiCall('item-X');
199
+ * console.log('Final Operation Result:', result);
200
+ * } catch (error: any) {
201
+ * console.error('Operation ultimately failed:', error.message);
202
+ * if (error instanceof TimeoutError) {
203
+ * console.error('Specifically: A timeout occurred.');
204
+ * }
205
+ * }
206
+ * }
207
+ *
208
+ * runCustomRetrier();
209
+ * ```
210
+ */
211
+ export function createRetrier(options: RetrierOptions = {}) {
212
+ const {
213
+ maxRetries = 2, // Default: 2 retries (total 3 attempts)
214
+ timeoutMs = 5000, // Default: 5 seconds timeout per attempt
215
+ onAttempt,
216
+ onSuccess,
217
+ onFailure,
218
+ onRetry,
219
+ onTimeout,
220
+ shouldRetry = () => true, // Default: retry on all errors
221
+ } = options;
222
+
223
+ /**
224
+ * Resolves a DynamicValue to its concrete value for the given attempt.
225
+ * @template T The type of the dynamic value.
226
+ * @param value - The DynamicValue to resolve.
227
+ * @param attempt - The current attempt number.
228
+ * @returns The concrete value.
229
+ */
230
+ const resolveDynamicValue = <T_Val>(value: DynamicValue<T_Val>, attempt: number): T_Val => {
231
+ return typeof value === 'function' ? (value as (attempt: number) => T_Val)(attempt) : value;
232
+ };
233
+
234
+ /**
235
+ * A higher-order function that takes an asynchronous function and returns a new function
236
+ * with retry capabilities applied.
237
+ *
238
+ * @template T The expected return type of the wrapped asynchronous function.
239
+ * @template Func The type of the asynchronous function to be wrapped.
240
+ * @param func - The original asynchronous function to be wrapped with retry logic.
241
+ * @returns A new asynchronous function that, when called, executes the original function
242
+ * with retry attempts and timeout handling as configured.
243
+ */
244
+ return function <T, Func extends AsyncFunction<T>>(func: Func): ((...args: Parameters<Func>) => Promise<T>) {
245
+ return async function (this: ThisParameterType<Func>, ...args: Parameters<Func>): Promise<T> {
246
+ let currentAttempt = 0;
247
+ let lastError: Error | undefined;
248
+
249
+ // Resolve maxRetries once or dynamically per attempt based on DynamicValue type
250
+ const calculatedMaxRetries = resolveDynamicValue(maxRetries, 1); // Calculate for the first attempt as starting point
251
+
252
+ // Loop through attempts up to the maximum allowed retries
253
+ while (currentAttempt <= calculatedMaxRetries) {
254
+ currentAttempt++;
255
+ onAttempt?.(currentAttempt, lastError); // Notify about current attempt, including previous error if any
256
+
257
+ // Resolve timeoutMs dynamically for the current attempt
258
+ const currentTimeoutMs = resolveDynamicValue(timeoutMs, currentAttempt);
259
+
260
+ try {
261
+ // Execute the original function with its original `this` context and arguments,
262
+ // wrapped by the `withTimeout` utility.
263
+ const result = await withTimeout(func.apply(this, args), currentTimeoutMs);
264
+ onSuccess?.(result, currentAttempt); // Notify success
265
+ return result; // Return the successful result
266
+ } catch (error: any) {
267
+ lastError = error; // Store the error for the next attempt's `onAttempt` callback
268
+
269
+ // Notify if the specific error was a timeout
270
+ if (error instanceof TimeoutError) {
271
+ onTimeout?.(currentAttempt, currentTimeoutMs);
272
+ } else {
273
+ // console.warn(`Attempt ${currentAttempt} failed with error:`, error.message); // Example internal logging
274
+ }
275
+
276
+ // Determine if a retry should proceed based on the `shouldRetry` predicate and max attempts
277
+ const shouldProceedRetry = shouldRetry(error, currentAttempt);
278
+
279
+ if (!shouldProceedRetry || currentAttempt > calculatedMaxRetries) {
280
+ onFailure?.(error, currentAttempt); // Notify final failure
281
+ throw error; // Throw the error if no more retries or `shouldRetry` returns false
282
+ }
283
+
284
+ onRetry?.(currentAttempt, error); // Notify that a retry will occur
285
+ // Optional: Add a delay here for exponential backoff or other retry strategies.
286
+ // e.g., `await new Promise(resolve => setTimeout(resolve, Math.pow(2, currentAttempt) * 100));`
287
+ }
288
+ }
289
+ // This line should ideally not be reached if the loop's conditions are correct and errors are always thrown.
290
+ throw new Error('Retrier exhausted all attempts without success. This is an unexpected state.');
291
+ };
292
+ };
293
+ }
294
+
295
+ /**
296
+ * A convenience function to apply default retry logic to an asynchronous method.
297
+ * It uses sensible default configurations:
298
+ * - `maxRetries`: 2 (meaning 1 initial attempt + 2 retries, total 3 attempts)
299
+ * - `timeoutMs`: 5000 (5 seconds timeout per attempt)
300
+ * - Includes default logging for attempts, success, failure, and retries.
301
+ *
302
+ * @template T The expected return type of the asynchronous function.
303
+ * @template Func The type of the asynchronous function to be wrapped.
304
+ * @param func - The asynchronous method to be wrapped with default retry logic.
305
+ * @returns A new asynchronous method that automatically includes the default retry behavior.
306
+ *
307
+ * @example
308
+ * ```ts
309
+ * import { useRetrier, TimeoutError } from './your-module'; // Assuming your module path
310
+ *
311
+ * // Example of an async operation that might fail randomly
312
+ * let operationCount = 0;
313
+ * async function unstableOperation(): Promise<string> {
314
+ * operationCount++;
315
+ * console.log(` [Op] Executing unstableOperation, attempt ${operationCount}.`);
316
+ * const success = Math.random() > 0.6; // 40% chance of success
317
+ * await new Promise(resolve => setTimeout(resolve, 800 + Math.random() * 500)); // 0.8s to 1.3s delay
318
+ *
319
+ * if (!success && operationCount < 3) { // Fail first 2 attempts often
320
+ * throw new Error(`Unstable operation failed on call ${operationCount}.`);
321
+ * }
322
+ * console.log(` [Op] Unstable operation succeeded on call ${operationCount}.`);
323
+ * return `Data from unstable operation (Call ${operationCount})`;
324
+ * }
325
+ *
326
+ * async function runUseRetrier() {
327
+ * console.log('\n--- Running UseRetrier Example ---');
328
+ * operationCount = 0; // Reset for example
329
+ *
330
+ * const reliableOperation = useRetrier(unstableOperation);
331
+ *
332
+ * try {
333
+ * const result = await reliableOperation();
334
+ * console.log('Final Result (UseRetrier):', result);
335
+ * } catch (error: any) {
336
+ * console.error('Operation failed completely (UseRetrier):', error.message);
337
+ * if (error instanceof TimeoutError) {
338
+ * console.error('Specifically: A timeout occurred for UseRetrier example.');
339
+ * }
340
+ * }
341
+ *
342
+ * // Another attempt, showing success
343
+ * operationCount = 0;
344
+ * try {
345
+ * const result = await reliableOperation();
346
+ * console.log('Final Result (UseRetrier, second run):', result);
347
+ * } catch (error: any) {
348
+ * console.error('Operation failed completely (UseRetrier, second run):', error.message);
349
+ * }
350
+ * }
351
+ *
352
+ * runUseRetrier();
353
+ * ```
354
+ */
355
+ export function useRetrier<T, Func extends AsyncFunction<T>>(func: Func): ((...args: Parameters<Func>) => Promise<T>) {
356
+ // Create a retrier instance with default options and common logging callbacks.
357
+ const defaultRetrier = createRetrier({
358
+ maxRetries: 2, // Default: 2 retries
359
+ timeoutMs: 5000, // Default: 5 seconds timeout
360
+ onAttempt: (attempt, error) => {
361
+ if (attempt > 1) {
362
+ console.log(`[UseRetrier Default] Attempt ${attempt} (after previous error: ${error?.message || 'None'})...`);
363
+ } else {
364
+ console.log(`[UseRetrier Default] Starting initial attempt ${attempt}...`);
365
+ }
366
+ },
367
+ onSuccess: (result, attempt) => console.log(`[UseRetrier Default] ✅ Succeeded on attempt ${attempt}. Result:`, result),
368
+ onFailure: (error, attempt) => console.error(`[UseRetrier Default] ❌ Failed after ${attempt} attempts. Final Error:`, error.message),
369
+ onRetry: (attempt, error) => console.warn(`[UseRetrier Default] 🔄 Will retry. Attempt ${attempt} failed with: ${error.message}`),
370
+ onTimeout: (attempt, ms) => console.warn(`[UseRetrier Default] ⏰ Attempt ${attempt} timed out after ${ms}ms.`),
371
+ });
372
+ // Use the created default retrier to wrap the provided function.
373
+ return defaultRetrier(func);
374
+ }
@@ -1,6 +1,6 @@
1
1
  # Core Box Package
2
2
 
3
- The Core Box package provides unified type definitions and utility functions for the Talex Touch search box system. This package contains the foundational types and tools used across the entire project for search result management and plugin integration.
3
+ The Core Box package provides unified type definitions and utility functions for the TUFF search box system. This package contains the foundational types and tools used across the entire project for search result management and plugin integration.
4
4
 
5
5
  ## Features
6
6
 
@@ -112,14 +112,14 @@ const codeItem = SearchUtils.createDataItem({
112
112
  ```typescript
113
113
  const urlItem = SearchUtils.createSearchItem({
114
114
  name: "GitHub Repository",
115
- desc: "Talex Touch project",
115
+ desc: "TUFF project",
116
116
  pluginName: "web-search",
117
117
  render: {
118
118
  mode: RenderMode.URL,
119
119
  content: "https://github.com/talex-touch/talex-touch", // The actual URL to load
120
120
  preview: {
121
121
  enabled: true,
122
- title: "Talex Touch", // Preview metadata
122
+ title: "Tuff", // Preview metadata
123
123
  description: "Modern desktop application framework", // Preview description
124
124
  image: "https://github.com/talex-touch.png" // Preview image
125
125
  }
@@ -181,11 +181,11 @@ interface IRenderConfig {
181
181
  This package is automatically exported from `@talex-touch/utils`:
182
182
 
183
183
  ```typescript
184
- import {
185
- ISearchItem,
186
- IDataItem,
187
- SearchUtils,
188
- RenderMode
184
+ import {
185
+ ISearchItem,
186
+ IDataItem,
187
+ SearchUtils,
188
+ RenderMode
189
189
  } from '@talex-touch/utils';
190
190
  ```
191
191
 
@@ -0,0 +1,6 @@
1
+ /**
2
+ * TUFF Builder 模块导出
3
+ * 提供 TuffItem 构建工具和实用函数
4
+ */
5
+
6
+ export * from './tuff-builder';