@talex-touch/utils 1.0.31 → 1.0.33

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 (116) hide show
  1. package/animation/window-node.ts +15 -12
  2. package/animation/window.ts +19 -15
  3. package/auth/clerk-types.ts +1 -1
  4. package/auth/index.ts +1 -1
  5. package/auth/useAuthState.ts +6 -5
  6. package/auth/useClerkConfig.ts +4 -4
  7. package/auth/useClerkProvider.ts +3 -2
  8. package/channel/index.ts +23 -22
  9. package/common/file-scan-constants.ts +137 -121
  10. package/common/file-scan-utils.ts +48 -27
  11. package/common/index.ts +3 -3
  12. package/common/search/gather.ts +1 -1
  13. package/common/search/index.ts +5 -6
  14. package/common/storage/constants.ts +3 -2
  15. package/common/storage/entity/app-settings.ts +5 -3
  16. package/common/storage/entity/shortcut-settings.ts +10 -10
  17. package/common/storage/shortcut-storage.ts +6 -4
  18. package/common/utils/file.ts +14 -6
  19. package/common/utils/index.ts +62 -52
  20. package/common/utils/polling.ts +88 -84
  21. package/common/utils/task-queue.ts +11 -10
  22. package/common/utils/time.ts +50 -47
  23. package/common/utils/timing.ts +41 -37
  24. package/core-box/builder/index.ts +1 -1
  25. package/core-box/builder/tuff-builder.ts +254 -229
  26. package/core-box/index.ts +4 -6
  27. package/core-box/preview/index.ts +1 -0
  28. package/core-box/preview/types.ts +43 -0
  29. package/core-box/recommendation.ts +77 -0
  30. package/core-box/tuff/index.ts +1 -1
  31. package/core-box/tuff/tuff-dsl.ts +328 -266
  32. package/electron/download-manager.ts +43 -42
  33. package/electron/env-tool.ts +19 -18
  34. package/electron/file-parsers/index.ts +2 -2
  35. package/electron/file-parsers/parsers/text-parser.ts +15 -14
  36. package/electron/file-parsers/registry.ts +9 -7
  37. package/electron/file-parsers/types.ts +4 -4
  38. package/electron/index.ts +2 -2
  39. package/eventbus/index.ts +11 -11
  40. package/index.ts +5 -4
  41. package/intelligence/client.ts +87 -0
  42. package/intelligence/index.ts +1 -0
  43. package/package.json +14 -14
  44. package/permission/index.ts +8 -8
  45. package/plugin/channel.ts +77 -68
  46. package/plugin/index.ts +96 -82
  47. package/plugin/install.ts +8 -8
  48. package/plugin/log/types.ts +5 -5
  49. package/plugin/node/index.ts +1 -1
  50. package/plugin/node/logger-manager.ts +14 -11
  51. package/plugin/node/logger.ts +8 -8
  52. package/plugin/plugin-source.ts +11 -11
  53. package/plugin/preload.ts +1 -1
  54. package/plugin/providers/registry.ts +8 -7
  55. package/plugin/providers/types.ts +6 -6
  56. package/plugin/sdk/README.md +216 -0
  57. package/plugin/sdk/box-sdk.ts +219 -0
  58. package/plugin/sdk/channel.ts +20 -20
  59. package/plugin/sdk/clipboard.ts +8 -6
  60. package/plugin/sdk/common.ts +10 -6
  61. package/plugin/sdk/core-box.ts +2 -3
  62. package/plugin/sdk/division-box.ts +266 -0
  63. package/plugin/sdk/enum/bridge-event.ts +1 -1
  64. package/plugin/sdk/examples/storage-onDidChange-example.js +1 -1
  65. package/plugin/sdk/feature-sdk.ts +235 -0
  66. package/plugin/sdk/features.ts +34 -26
  67. package/plugin/sdk/hooks/bridge.ts +3 -6
  68. package/plugin/sdk/hooks/index.ts +1 -1
  69. package/plugin/sdk/hooks/life-cycle.ts +4 -10
  70. package/plugin/sdk/index.ts +10 -7
  71. package/plugin/sdk/service/index.ts +3 -3
  72. package/plugin/sdk/storage.ts +4 -4
  73. package/plugin/sdk/system.ts +1 -1
  74. package/plugin/sdk/types.ts +165 -146
  75. package/plugin/sdk/window/index.ts +8 -5
  76. package/preload/loading.ts +6 -6
  77. package/preload/renderer.ts +4 -2
  78. package/renderer/hooks/arg-mapper.ts +1 -2
  79. package/renderer/hooks/index.ts +2 -0
  80. package/renderer/hooks/initialize.ts +10 -8
  81. package/renderer/hooks/performance.ts +4 -4
  82. package/renderer/hooks/use-channel.ts +150 -0
  83. package/renderer/hooks/use-intelligence.ts +236 -0
  84. package/renderer/index.ts +6 -2
  85. package/renderer/ref.ts +32 -36
  86. package/renderer/slots.ts +29 -26
  87. package/renderer/storage/app-settings.ts +16 -6
  88. package/renderer/storage/base-storage.ts +222 -114
  89. package/renderer/storage/index.ts +3 -0
  90. package/renderer/storage/intelligence-storage.ts +218 -0
  91. package/renderer/storage/openers.ts +13 -3
  92. package/renderer/touch-sdk/env.ts +41 -41
  93. package/renderer/touch-sdk/index.ts +1 -1
  94. package/renderer/touch-sdk/terminal.ts +5 -5
  95. package/renderer/touch-sdk/utils.ts +4 -3
  96. package/search/levenshtein-utils.ts +11 -11
  97. package/search/types.ts +102 -102
  98. package/service/index.ts +11 -11
  99. package/service/protocol/index.ts +217 -14
  100. package/types/division-box.ts +248 -0
  101. package/types/download.ts +72 -34
  102. package/types/index.ts +3 -1
  103. package/types/intelligence.ts +607 -0
  104. package/types/modules/base.ts +16 -16
  105. package/types/modules/index.ts +1 -1
  106. package/types/modules/module-lifecycle.ts +21 -21
  107. package/types/modules/module-manager.ts +11 -11
  108. package/types/modules/module.ts +16 -16
  109. package/types/storage.ts +0 -1
  110. package/types/touch-app-core.ts +32 -32
  111. package/types/update.ts +91 -21
  112. package/core-box/README.md +0 -218
  113. package/core-box/builder/tuff-builder.example.ts.bak +0 -258
  114. package/core-box/run-tests.sh +0 -7
  115. package/core-box/search.ts +0 -1
  116. package/electron/clipboard-helper.ts +0 -199
@@ -1,4 +1,4 @@
1
- import { ISearchProvider, TuffQuery, TuffSearchResult } from '@talex-touch/utils'
1
+ import type { ISearchProvider, TuffQuery, TuffSearchResult } from '@talex-touch/utils'
2
2
 
3
3
  export * from './gather'
4
4
 
@@ -30,7 +30,6 @@ export interface TuffUpdate {
30
30
  sourceStats?: TuffSearchResult['sources']
31
31
  }
32
32
 
33
-
34
33
  /**
35
34
  * Search Engine Interface (formerly ISearchEngine)
36
35
  *
@@ -41,13 +40,13 @@ export interface ISearchEngine<C> {
41
40
  * Registers a search provider with the engine.
42
41
  * @param provider - An instance of ISearchProvider.
43
42
  */
44
- registerProvider(provider: ISearchProvider<C>): void
43
+ registerProvider: (provider: ISearchProvider<C>) => void
45
44
 
46
45
  /**
47
46
  * Unregisters a search provider by its unique ID.
48
47
  * @param providerId - The unique ID of the provider to remove.
49
48
  */
50
- unregisterProvider(providerId: string): void
49
+ unregisterProvider: (providerId: string) => void
51
50
 
52
51
  /**
53
52
  * Executes a search across all registered and relevant providers.
@@ -57,11 +56,11 @@ export interface ISearchEngine<C> {
57
56
  * @returns A promise that resolves to a TuffSearchResult object,
58
57
  * containing the ranked items and metadata about the search operation.
59
58
  */
60
- search(query: TuffQuery): Promise<TuffSearchResult>
59
+ search: (query: TuffQuery) => Promise<TuffSearchResult>
61
60
 
62
61
  /**
63
62
  * Performs background maintenance tasks, such as pre-heating caches,
64
63
  * refreshing indexes, etc.
65
64
  */
66
- maintain(): void
65
+ maintain: () => void
67
66
  }
@@ -1,7 +1,8 @@
1
1
  export enum StorageList {
2
2
  APP_SETTING = 'app-setting.ini',
3
3
  SHORTCUT_SETTING = 'shortcut-setting.ini',
4
- OPENERS = 'openers.json'
4
+ OPENERS = 'openers.json',
5
+ IntelligenceConfig = 'aisdk-config',
5
6
  }
6
7
 
7
8
  /**
@@ -13,5 +14,5 @@ export enum ConfigKeys {
13
14
  * Stores the timestamp of the last successful full application scan using mdfind.
14
15
  * This is used to schedule the next comprehensive scan.
15
16
  */
16
- APP_PROVIDER_LAST_MDFIND_SCAN = 'app_provider_last_mdfind_scan'
17
+ APP_PROVIDER_LAST_MDFIND_SCAN = 'app_provider_last_mdfind_scan',
17
18
  }
@@ -51,9 +51,11 @@ const _appSettingOriginData = {
51
51
  showTray: true,
52
52
  adminPrivileges: false,
53
53
  hideDock: false,
54
+ runAsAdmin: false,
55
+ customDesktop: false,
54
56
  },
55
57
  layout: 'simple',
56
- };
58
+ }
57
59
 
58
60
  export const appSettingOriginData = Object.freeze(_appSettingOriginData)
59
61
 
@@ -63,5 +65,5 @@ export const appSettingOriginData = Object.freeze(_appSettingOriginData)
63
65
  * Combines the default configuration with support for dynamic additional properties.
64
66
  */
65
67
  export type AppSetting = typeof _appSettingOriginData & {
66
- [key: string]: any;
67
- };
68
+ [key: string]: any
69
+ }
@@ -4,19 +4,19 @@ export enum ShortcutType {
4
4
  }
5
5
 
6
6
  export interface ShortcutMeta {
7
- creationTime: number;
8
- modificationTime: number;
9
- author: string;
10
- description?: string;
7
+ creationTime: number
8
+ modificationTime: number
9
+ author: string
10
+ description?: string
11
11
  }
12
12
 
13
13
  export interface Shortcut {
14
- id: string;
15
- accelerator: string;
16
- type: ShortcutType;
17
- meta: ShortcutMeta;
14
+ id: string
15
+ accelerator: string
16
+ type: ShortcutType
17
+ meta: ShortcutMeta
18
18
  }
19
19
 
20
- export type ShortcutSetting = Shortcut[];
20
+ export type ShortcutSetting = Shortcut[]
21
21
 
22
- export const shortcutSettingOriginData: ShortcutSetting = [];
22
+ export const shortcutSettingOriginData: ShortcutSetting = []
@@ -1,12 +1,13 @@
1
+ import type { Shortcut, ShortcutSetting } from './entity/shortcut-settings'
1
2
  import { StorageList } from './constants'
2
- import { shortcutSettingOriginData, ShortcutSetting, Shortcut } from './entity/shortcut-settings'
3
+ import { shortcutSettingOriginData } from './entity/shortcut-settings'
3
4
 
4
5
  class ShortcutStorage {
5
6
  private _config: ShortcutSetting = []
6
7
 
7
8
  constructor(private readonly storage: {
8
- getConfig: (name: string) => any,
9
- saveConfig: (name: string, content?: string) => void,
9
+ getConfig: (name: string) => any
10
+ saveConfig: (name: string, content?: string) => void
10
11
  }) {
11
12
  this.init()
12
13
  }
@@ -16,7 +17,8 @@ class ShortcutStorage {
16
17
  if (!config || !Array.isArray(config) || config.length === 0) {
17
18
  this._config = [...shortcutSettingOriginData]
18
19
  this._save()
19
- } else {
20
+ }
21
+ else {
20
22
  this._config = config
21
23
  }
22
24
  }
@@ -1,7 +1,15 @@
1
1
  // eslint-disable-next-line @typescript-eslint/no-var-requires
2
- const path = typeof window === 'undefined'
3
- ? require('path')
4
- : require('path-browserify')
2
+ const path = (() => {
3
+ if (typeof window === 'undefined') {
4
+ return require('node:path')
5
+ }
6
+ try {
7
+ return require('path-browserify')
8
+ }
9
+ catch {
10
+ return require('node:path')
11
+ }
12
+ })()
5
13
 
6
14
  /**
7
15
  * Enum for various file types.
@@ -21,7 +29,7 @@ export enum FileType {
21
29
  Spreadsheet = 'Spreadsheet',
22
30
  Presentation = 'Presentation',
23
31
  Ebook = 'Ebook',
24
- Other = 'Other'
32
+ Other = 'Other',
25
33
  }
26
34
 
27
35
  const extensionMap: Map<FileType, Set<string>> = new Map([
@@ -37,8 +45,8 @@ const extensionMap: Map<FileType, Set<string>> = new Map([
37
45
  [FileType.Font, new Set(['.ttf', '.otf', '.woff', '.woff2'])],
38
46
  [FileType.Spreadsheet, new Set(['.xls', '.xlsx', '.csv', '.numbers'])],
39
47
  [FileType.Presentation, new Set(['.ppt', '.pptx', '.key'])],
40
- [FileType.Ebook, new Set(['.epub', '.mobi', '.azw'])]
41
- ]);
48
+ [FileType.Ebook, new Set(['.epub', '.mobi', '.azw'])],
49
+ ])
42
50
 
43
51
  /**
44
52
  * Get the file type from a file path.
@@ -10,7 +10,7 @@
10
10
  * ```
11
11
  */
12
12
  export async function sleep(time: number): Promise<number> {
13
- return new Promise(resolve => setTimeout(() => resolve(time), time))
13
+ return new Promise(resolve => setTimeout(() => resolve(time), time))
14
14
  }
15
15
 
16
16
  /**
@@ -28,14 +28,14 @@ export async function sleep(time: number): Promise<number> {
28
28
  * ```
29
29
  */
30
30
  export function anyStr2Num(str: string): bigint {
31
- const codes = Array.from(str).map(ch => ch.charCodeAt(0))
32
- const minCode = Math.min(...codes)
31
+ const codes = Array.from(str).map(ch => ch.charCodeAt(0))
32
+ const minCode = Math.min(...codes)
33
33
 
34
- const encoded = codes
35
- .map(code => (code - minCode).toString().padStart(2, '0'))
36
- .join('')
34
+ const encoded = codes
35
+ .map(code => (code - minCode).toString().padStart(2, '0'))
36
+ .join('')
37
37
 
38
- return BigInt(`${minCode}000${encoded}`)
38
+ return BigInt(`${minCode}000${encoded}`)
39
39
  }
40
40
 
41
41
  /**
@@ -51,16 +51,16 @@ export function anyStr2Num(str: string): bigint {
51
51
  * ```
52
52
  */
53
53
  export function num2anyStr(num: bigint): string {
54
- const [baseStr, encoded] = num.toString().split('000')
55
- const base = Number(baseStr)
54
+ const [baseStr, encoded] = num.toString().split('000')
55
+ const base = Number(baseStr)
56
56
 
57
- let result = ''
58
- for (let i = 0; i < encoded.length; i += 2) {
59
- const offset = Number(encoded.slice(i, i + 2))
60
- result += String.fromCharCode(base + offset)
61
- }
57
+ let result = ''
58
+ for (let i = 0; i < encoded.length; i += 2) {
59
+ const offset = Number(encoded.slice(i, i + 2))
60
+ result += String.fromCharCode(base + offset)
61
+ }
62
62
 
63
- return result
63
+ return result
64
64
  }
65
65
 
66
66
  const LOCALHOST_KEYS = ['localhost', '127.0.0.1']
@@ -78,7 +78,7 @@ const LOCALHOST_KEYS = ['localhost', '127.0.0.1']
78
78
  * ```
79
79
  */
80
80
  export function isLocalhostUrl(urlStr: string): boolean {
81
- return LOCALHOST_KEYS.includes(new URL(urlStr).hostname)
81
+ return LOCALHOST_KEYS.includes(new URL(urlStr).hostname)
82
82
  }
83
83
 
84
84
  /**
@@ -90,85 +90,95 @@ export function isLocalhostUrl(urlStr: string): boolean {
90
90
  * @throws Error if an unsupported value is found (reports path and type).
91
91
  */
92
92
  export function structuredStrictStringify(value: unknown): string {
93
- const seen = new WeakMap<object, string>();
93
+ const seen = new WeakMap<object, string>()
94
94
  const badTypes = [
95
- 'symbol'
96
- ];
95
+ 'symbol',
96
+ ]
97
97
  // Support Map/Set, but not Error, DOM, Proxy, WeakMap, WeakSet
98
98
  function getType(val: any): string {
99
- if (val === null) return 'null';
100
- if (Array.isArray(val)) return 'Array';
99
+ if (val === null)
100
+ return 'null'
101
+ if (Array.isArray(val))
102
+ return 'Array'
101
103
  if (typeof Document !== 'undefined') {
102
- if (val instanceof Node) return 'DOMNode';
104
+ if (val instanceof Node)
105
+ return 'DOMNode'
103
106
  }
104
107
  // if (val instanceof Error) return 'Error';
105
- if (val instanceof WeakMap) return 'WeakMap';
106
- if (val instanceof WeakSet) return 'WeakSet';
107
- if (typeof val === 'object' && val !== null && val.constructor?.name === 'Proxy') return 'Proxy';
108
- if (typeof val === 'bigint') return 'BigInt';
109
- return typeof val;
108
+ if (val instanceof WeakMap)
109
+ return 'WeakMap'
110
+ if (val instanceof WeakSet)
111
+ return 'WeakSet'
112
+ if (typeof val === 'object' && val !== null && val.constructor?.name === 'Proxy')
113
+ return 'Proxy'
114
+ if (typeof val === 'bigint')
115
+ return 'BigInt'
116
+ return typeof val
110
117
  }
111
118
 
112
119
  function serialize(val: any, path: string): any {
113
- const type = getType(val);
120
+ const type = getType(val)
114
121
  // Block disallowed/unsafe types and edge cases for structured-clone
115
122
  if (badTypes.includes(typeof val) || type === 'DOMNode' || type === 'Proxy' || type === 'WeakMap' || type === 'WeakSet' || type === 'BigInt') {
116
- throw new Error(`Cannot serialize property at path "${path}": type "${type}"`);
123
+ throw new Error(`Cannot serialize property at path "${path}": type "${type}"`)
117
124
  }
118
125
  // JSON doesn't support undefined, skip it for values in objects, preserve in arrays as null
119
- if (typeof val === 'undefined') return null;
126
+ if (typeof val === 'undefined')
127
+ return null
120
128
  // Simple value
121
129
  if (
122
- val === null ||
123
- typeof val === 'number' ||
124
- typeof val === 'boolean' ||
125
- typeof val === 'string'
126
- ) return val;
130
+ val === null
131
+ || typeof val === 'number'
132
+ || typeof val === 'boolean'
133
+ || typeof val === 'string'
134
+ ) {
135
+ return val
136
+ }
127
137
  // Cycle check
128
138
  if (typeof val === 'object') {
129
139
  if (seen.has(val)) {
130
- return `[Circular ~${seen.get(val)}]`; // You could just throw if you dislike this fallback!
140
+ return `[Circular ~${seen.get(val)}]` // You could just throw if you dislike this fallback!
131
141
  }
132
- seen.set(val, path);
142
+ seen.set(val, path)
133
143
  if (val instanceof Error) {
134
144
  return {
135
145
  name: val.name,
136
146
  message: val.message,
137
147
  stack: val.stack,
138
- };
148
+ }
139
149
  }
140
150
  if (Array.isArray(val)) {
141
- return val.map((item, idx) => serialize(item, `${path}[${idx}]`));
151
+ return val.map((item, idx) => serialize(item, `${path}[${idx}]`))
142
152
  }
143
153
  if (val instanceof Date) {
144
- return val.toISOString();
154
+ return val.toISOString()
145
155
  }
146
156
  if (val instanceof Map) {
147
- const obj: Record<string, any> = {};
157
+ const obj: Record<string, any> = {}
148
158
  for (const [k, v] of val.entries()) {
149
- obj[typeof k === 'string' ? k : JSON.stringify(k)] = serialize(v, `${path}[Map(${typeof k === 'string' ? k : JSON.stringify(k)})]`);
159
+ obj[typeof k === 'string' ? k : JSON.stringify(k)] = serialize(v, `${path}[Map(${typeof k === 'string' ? k : JSON.stringify(k)})]`)
150
160
  }
151
- return obj;
161
+ return obj
152
162
  }
153
163
  if (val instanceof Set) {
154
- return Array.from(val).map((item, idx) => serialize(item, `${path}[SetEntry${idx}]`));
164
+ return Array.from(val).map((item, idx) => serialize(item, `${path}[SetEntry${idx}]`))
155
165
  }
156
166
  // General object
157
- const res: any = {};
167
+ const res: any = {}
158
168
  for (const key of Object.keys(val)) {
159
- res[key] = serialize(val[key], `${path}.${key}`);
169
+ res[key] = serialize(val[key], `${path}.${key}`)
160
170
  }
161
- return res;
171
+ return res
162
172
  }
163
- throw new Error(`Cannot serialize property at path "${path}": unknown type`);
173
+ throw new Error(`Cannot serialize property at path "${path}": unknown type`)
164
174
  }
165
175
 
166
- return JSON.stringify(serialize(value, 'root'));
176
+ return JSON.stringify(serialize(value, 'root'))
167
177
  }
168
178
 
169
- export * from './timing'
179
+ export * from './file'
170
180
 
171
- export { runAdaptiveTaskQueue, type AdaptiveTaskQueueOptions } from './task-queue'
181
+ export { type AdaptiveTaskQueueOptions, runAdaptiveTaskQueue } from './task-queue'
172
182
 
173
183
  export * from './time'
174
- export * from './file'
184
+ export * from './timing'
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  /**
2
3
  * @module polling
3
4
  * A high-precision, efficient, singleton polling service for scheduling periodic tasks.
@@ -6,20 +7,20 @@
6
7
  */
7
8
 
8
9
  interface PollingTask {
9
- id: string;
10
- callback: () => void | Promise<void>;
11
- intervalMs: number;
12
- nextRunMs: number;
10
+ id: string
11
+ callback: () => void | Promise<void>
12
+ intervalMs: number
13
+ nextRunMs: number
13
14
  }
14
15
 
15
- type TimeUnit = 'seconds' | 'minutes' | 'hours';
16
+ type TimeUnit = 'seconds' | 'minutes' | 'hours'
16
17
 
17
18
  export class PollingService {
18
- private static instance: PollingService;
19
- private tasks = new Map<string, PollingTask>();
20
- private timerId: NodeJS.Timeout | null = null;
21
- private isRunning = false;
22
- private quitListenerCleanup?: () => void;
19
+ private static instance: PollingService
20
+ private tasks = new Map<string, PollingTask>()
21
+ private timerId: NodeJS.Timeout | null = null
22
+ private isRunning = false
23
+ private quitListenerCleanup?: () => void
23
24
 
24
25
  private constructor() {
25
26
  // Private constructor to enforce singleton pattern
@@ -27,21 +28,21 @@ export class PollingService {
27
28
 
28
29
  public static getInstance(): PollingService {
29
30
  if (!PollingService.instance) {
30
- PollingService.instance = new PollingService();
31
+ PollingService.instance = new PollingService()
31
32
  }
32
- return PollingService.instance;
33
+ return PollingService.instance
33
34
  }
34
35
 
35
36
  private convertToMs(interval: number, unit: TimeUnit): number {
36
37
  switch (unit) {
37
38
  case 'seconds':
38
- return interval * 1000;
39
+ return interval * 1000
39
40
  case 'minutes':
40
- return interval * 60 * 1000;
41
+ return interval * 60 * 1000
41
42
  case 'hours':
42
- return interval * 60 * 60 * 1000;
43
+ return interval * 60 * 60 * 1000
43
44
  default:
44
- throw new Error(`Invalid time unit: ${unit}`);
45
+ throw new Error(`Invalid time unit: ${unit}`)
45
46
  }
46
47
  }
47
48
 
@@ -54,31 +55,31 @@ export class PollingService {
54
55
  public register(
55
56
  id: string,
56
57
  callback: () => void | Promise<void>,
57
- options: { interval: number; unit: TimeUnit; runImmediately?: boolean }
58
+ options: { interval: number, unit: TimeUnit, runImmediately?: boolean },
58
59
  ): void {
59
60
  if (this.tasks.has(id)) {
60
- console.warn(`[PollingService] Task with ID '${id}' is already registered. Overwriting.`);
61
+ console.warn(`[PollingService] Task with ID '${id}' is already registered. Overwriting.`)
61
62
  }
62
63
 
63
- const intervalMs = this.convertToMs(options.interval, options.unit);
64
+ const intervalMs = this.convertToMs(options.interval, options.unit)
64
65
  if (intervalMs <= 0) {
65
- console.error(`[PollingService] Task '${id}' has an invalid interval of ${intervalMs}ms. Registration aborted.`);
66
- return;
66
+ console.error(`[PollingService] Task '${id}' has an invalid interval of ${intervalMs}ms. Registration aborted.`)
67
+ return
67
68
  }
68
69
 
69
- const nextRunMs = options.runImmediately ? Date.now() : Date.now() + intervalMs;
70
+ const nextRunMs = options.runImmediately ? Date.now() : Date.now() + intervalMs
70
71
 
71
72
  this.tasks.set(id, {
72
73
  id,
73
74
  callback,
74
75
  intervalMs,
75
76
  nextRunMs,
76
- });
77
+ })
77
78
 
78
- console.log(`[PollingService] Task '${id}' registered to run every ${options.interval} ${options.unit}.`);
79
+ console.debug(`[PollingService] Task '${id}' registered to run every ${options.interval} ${options.unit}.`)
79
80
 
80
81
  if (this.isRunning) {
81
- this._reschedule();
82
+ this._reschedule()
82
83
  }
83
84
  }
84
85
 
@@ -88,12 +89,13 @@ export class PollingService {
88
89
  */
89
90
  public unregister(id: string): void {
90
91
  if (this.tasks.delete(id)) {
91
- console.log(`[PollingService] Task '${id}' unregistered.`);
92
+ console.log(`[PollingService] Task '${id}' unregistered.`)
92
93
  if (this.isRunning) {
93
- this._reschedule();
94
+ this._reschedule()
94
95
  }
95
- } else {
96
- console.warn(`[PollingService] Attempted to unregister a non-existent task with ID '${id}'.`);
96
+ }
97
+ else {
98
+ console.warn(`[PollingService] Attempted to unregister a non-existent task with ID '${id}'.`)
97
99
  }
98
100
  }
99
101
 
@@ -103,7 +105,7 @@ export class PollingService {
103
105
  * @returns - True if the task is registered, false otherwise.
104
106
  */
105
107
  public isRegistered(id: string): boolean {
106
- return this.tasks.has(id);
108
+ return this.tasks.has(id)
107
109
  }
108
110
 
109
111
  /**
@@ -112,13 +114,13 @@ export class PollingService {
112
114
  */
113
115
  public start(): void {
114
116
  if (this.isRunning) {
115
- console.warn('[PollingService] Already running, skipping start.');
116
- return;
117
+ console.warn('[PollingService] Already running, skipping start.')
118
+ return
117
119
  }
118
- this.isRunning = true;
119
- console.log('[PollingService] Polling service started');
120
- this._setupQuitListener();
121
- this._reschedule();
120
+ this.isRunning = true
121
+ console.log('[PollingService] Polling service started')
122
+ this._setupQuitListener()
123
+ this._reschedule()
122
124
  }
123
125
 
124
126
  /**
@@ -130,25 +132,26 @@ export class PollingService {
130
132
  try {
131
133
  // Use dynamic require to avoid hard dependency on Electron
132
134
  // Similar to the approach used in packages/utils/plugin/channel.ts
133
- const electron = (globalThis as any)?.electron ??
134
- (typeof require !== 'undefined' ? require('electron') : null);
135
-
135
+ const electron = (globalThis as any)?.electron
136
+ ?? (typeof require !== 'undefined' ? require('electron') : null)
137
+
136
138
  if (electron?.app) {
137
- const app = electron.app;
138
-
139
+ const app = electron.app
140
+
139
141
  // Listen to before-quit event
140
142
  const quitHandler = () => {
141
- this.stop('app quit');
142
- };
143
-
144
- app.on('before-quit', quitHandler);
145
-
143
+ this.stop('app quit')
144
+ }
145
+
146
+ app.on('before-quit', quitHandler)
147
+
146
148
  // Store cleanup function
147
149
  this.quitListenerCleanup = () => {
148
- app.removeListener('before-quit', quitHandler);
149
- };
150
+ app.removeListener('before-quit', quitHandler)
151
+ }
150
152
  }
151
- } catch (error) {
153
+ }
154
+ catch (error) {
152
155
  // Not in Electron environment or Electron not available
153
156
  // This is fine, just skip the quit listener setup
154
157
  }
@@ -160,73 +163,74 @@ export class PollingService {
160
163
  */
161
164
  public stop(reason?: string): void {
162
165
  if (!this.isRunning) {
163
- return;
166
+ return
164
167
  }
165
- this.isRunning = false;
168
+ this.isRunning = false
166
169
  if (this.timerId) {
167
- clearTimeout(this.timerId);
168
- this.timerId = null;
170
+ clearTimeout(this.timerId)
171
+ this.timerId = null
169
172
  }
170
-
173
+
171
174
  // Clean up quit listener
172
175
  if (this.quitListenerCleanup) {
173
- this.quitListenerCleanup();
174
- this.quitListenerCleanup = undefined;
176
+ this.quitListenerCleanup()
177
+ this.quitListenerCleanup = undefined
175
178
  }
176
-
179
+
177
180
  if (reason) {
178
- console.log(`[PollingService] Stopping polling service: ${reason}`);
179
- } else {
180
- console.log('[PollingService] Polling service stopped');
181
+ console.log(`[PollingService] Stopping polling service: ${reason}`)
182
+ }
183
+ else {
184
+ console.log('[PollingService] Polling service stopped')
181
185
  }
182
186
  }
183
187
 
184
188
  private _reschedule(): void {
185
189
  if (this.timerId) {
186
- clearTimeout(this.timerId);
187
- this.timerId = null;
190
+ clearTimeout(this.timerId)
191
+ this.timerId = null
188
192
  }
189
193
 
190
194
  if (!this.isRunning || this.tasks.size === 0) {
191
- return;
195
+ return
192
196
  }
193
197
 
194
- const now = Date.now();
198
+ const now = Date.now()
195
199
  const nextTask = Array.from(this.tasks.values()).reduce((prev, curr) =>
196
- prev.nextRunMs < curr.nextRunMs ? prev : curr
197
- );
200
+ prev.nextRunMs < curr.nextRunMs ? prev : curr,
201
+ )
198
202
 
199
- const delay = Math.max(0, nextTask.nextRunMs - now);
200
- this.timerId = setTimeout(() => this._tick(), delay);
203
+ const delay = Math.max(0, nextTask.nextRunMs - now)
204
+ this.timerId = setTimeout(() => this._tick(), delay)
201
205
  }
202
206
 
203
207
  private async _tick(): Promise<void> {
204
- const now = Date.now();
205
- const tasksToRun: PollingTask[] = [];
208
+ const now = Date.now()
209
+ const tasksToRun: PollingTask[] = []
206
210
 
207
211
  for (const task of this.tasks.values()) {
208
212
  if (task.nextRunMs <= now) {
209
- tasksToRun.push(task);
213
+ tasksToRun.push(task)
210
214
  }
211
215
  }
212
216
 
213
217
  if (tasksToRun.length > 0) {
214
- // console.debug(`[PollingService] Executing ${tasksToRun.length} tasks.`);
215
- for (const task of tasksToRun) {
216
- try {
217
- await task.callback();
218
- } catch (error) {
219
- console.error(`[PollingService] Error executing task '${task.id}':`, error);
220
- }
221
- // Update next run time based on its last scheduled run time, not 'now'.
222
- // This prevents drift if a task takes a long time to execute.
223
- task.nextRunMs += task.intervalMs;
218
+ // console.debug(`[PollingService] Executing ${tasksToRun.length} tasks.`);
219
+ for (const task of tasksToRun) {
220
+ try {
221
+ await task.callback()
224
222
  }
223
+ catch (error) {
224
+ console.error(`[PollingService] Error executing task '${task.id}':`, error)
225
+ }
226
+ // Update next run time based on its last scheduled run time, not 'now'.
227
+ // This prevents drift if a task takes a long time to execute.
228
+ task.nextRunMs += task.intervalMs
229
+ }
225
230
  }
226
231
 
227
-
228
- this._reschedule();
232
+ this._reschedule()
229
233
  }
230
234
  }
231
235
 
232
- export const pollingService = PollingService.getInstance();
236
+ export const pollingService = PollingService.getInstance()