@talex-touch/utils 1.0.29 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth/clerk-types.ts +56 -0
- package/auth/index.ts +4 -0
- package/auth/useAuth.ts +0 -0
- package/auth/useAuthState.ts +44 -0
- package/auth/useClerkConfig.ts +40 -0
- package/auth/useClerkProvider.ts +51 -0
- package/common/storage/entity/app-settings.ts +1 -1
- package/core-box/README.md +1 -1
- package/core-box/tuff/tuff-dsl.ts +6 -46
- package/index.ts +4 -0
- package/package.json +1 -1
- package/plugin/index.ts +16 -8
- package/plugin/sdk/features.ts +1 -1
- package/plugin/sdk/index.ts +1 -1
- package/plugin/sdk/storage.ts +64 -0
- package/plugin/sdk/types.ts +75 -0
- package/preload/renderer.ts +1 -1
- package/renderer/hooks/index.ts +1 -0
- package/renderer/hooks/initialize.ts +5 -0
- package/renderer/hooks/performance.ts +87 -0
- package/renderer/storage/app-settings.ts +0 -2
- package/renderer/storage/base-storage.ts +0 -1
- package/search/types.ts +6 -5
- package/types/download.ts +162 -0
- package/types/icon.ts +44 -0
- package/types/index.ts +1 -0
- package/types/modules/module-lifecycle.ts +1 -1
- package/types/storage.ts +56 -0
- package/types/update.ts +99 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export interface ClerkUser {
|
|
2
|
+
id: string
|
|
3
|
+
emailAddresses: Array<{
|
|
4
|
+
emailAddress: string
|
|
5
|
+
id: string
|
|
6
|
+
}>
|
|
7
|
+
firstName?: string
|
|
8
|
+
lastName?: string
|
|
9
|
+
username?: string
|
|
10
|
+
imageUrl?: string
|
|
11
|
+
createdAt: string
|
|
12
|
+
updatedAt: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ClerkAuthState {
|
|
16
|
+
isLoaded: boolean
|
|
17
|
+
isSignedIn: boolean
|
|
18
|
+
user: ClerkUser | null
|
|
19
|
+
sessionId: string | null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface LoginOptions {
|
|
23
|
+
onSuccess?: (user: any) => void
|
|
24
|
+
onError?: (error: any) => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface LoginResult {
|
|
28
|
+
success: boolean
|
|
29
|
+
user?: any
|
|
30
|
+
error?: any
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CurrentUser {
|
|
34
|
+
id: string
|
|
35
|
+
name: string
|
|
36
|
+
email: string
|
|
37
|
+
avatar?: string
|
|
38
|
+
provider: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clerk Auth Config
|
|
43
|
+
*/
|
|
44
|
+
export interface ClerkConfig {
|
|
45
|
+
publishableKey: string
|
|
46
|
+
domain?: string
|
|
47
|
+
signInUrl?: string
|
|
48
|
+
signUpUrl?: string
|
|
49
|
+
afterSignInUrl?: string
|
|
50
|
+
afterSignUpUrl?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type ClerkResourceSnapshot = {
|
|
54
|
+
user?: any | null
|
|
55
|
+
session?: { id?: string | null } | null
|
|
56
|
+
}
|
package/auth/index.ts
ADDED
package/auth/useAuth.ts
ADDED
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createGlobalState } from '@vueuse/core'
|
|
2
|
+
import { shallowReactive } from 'vue'
|
|
3
|
+
import { ClerkAuthState, CurrentUser } from './clerk-types'
|
|
4
|
+
|
|
5
|
+
export const useAuthState = createGlobalState(() => {
|
|
6
|
+
const authState = shallowReactive<ClerkAuthState>({
|
|
7
|
+
isLoaded: false,
|
|
8
|
+
isSignedIn: false,
|
|
9
|
+
user: null,
|
|
10
|
+
sessionId: null
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
return { authState }
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export function useCurrentUser() {
|
|
17
|
+
const { authState } = useAuthState()
|
|
18
|
+
|
|
19
|
+
const currentUser = computed((): CurrentUser | null => {
|
|
20
|
+
if (!authState.isSignedIn || !authState.user) {
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { firstName, lastName, username, imageUrl } = authState.user
|
|
25
|
+
let name = ''
|
|
26
|
+
if (firstName || lastName) {
|
|
27
|
+
name = [firstName, lastName].filter(Boolean).join(' ')
|
|
28
|
+
} else {
|
|
29
|
+
name = username || ''
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const email = authState.user.emailAddresses?.[0]?.emailAddress || ''
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
id: authState.user.id,
|
|
36
|
+
name,
|
|
37
|
+
email,
|
|
38
|
+
avatar: imageUrl,
|
|
39
|
+
provider: 'clerk'
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return { currentUser, authState }
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ClerkConfig } from "./clerk-types"
|
|
2
|
+
|
|
3
|
+
const clerkPublishableKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
|
|
4
|
+
const clerkDomain = import.meta.env.VITE_CLERK_DOMAIN
|
|
5
|
+
|
|
6
|
+
if (!clerkPublishableKey?.length) {
|
|
7
|
+
throw new Error('VITE_CLERK_PUBLISHABLE_KEY is not set')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const CLERK_PUBLISHABLE_KEY_KEY = 'clerk-publishable-key'
|
|
11
|
+
|
|
12
|
+
export const defaultClerkConfig: ClerkConfig = {
|
|
13
|
+
publishableKey: clerkPublishableKey,
|
|
14
|
+
domain: clerkDomain,
|
|
15
|
+
signInUrl: '/sign-in',
|
|
16
|
+
signUpUrl: '/sign-up',
|
|
17
|
+
afterSignInUrl: '/home',
|
|
18
|
+
afterSignUpUrl: '/home'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useClerkConfig() {
|
|
22
|
+
const getClerkConfig = (): ClerkConfig => {
|
|
23
|
+
return {
|
|
24
|
+
...defaultClerkConfig,
|
|
25
|
+
publishableKey:
|
|
26
|
+
localStorage.getItem(CLERK_PUBLISHABLE_KEY_KEY) || defaultClerkConfig.publishableKey
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const setClerkConfig = (config: Partial<ClerkConfig>): void => {
|
|
31
|
+
if (config.publishableKey) {
|
|
32
|
+
localStorage.setItem(CLERK_PUBLISHABLE_KEY_KEY, config.publishableKey)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
getClerkConfig,
|
|
38
|
+
setClerkConfig
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Clerk } from '@clerk/clerk-js'
|
|
2
|
+
import { useClerkConfig } from './useClerkConfig'
|
|
3
|
+
|
|
4
|
+
let clerkInstance: Clerk | null = null
|
|
5
|
+
|
|
6
|
+
export function useClerkProvider() {
|
|
7
|
+
const initializeClerk = async (): Promise<Clerk> => {
|
|
8
|
+
if (clerkInstance) {
|
|
9
|
+
return clerkInstance
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { getClerkConfig } = useClerkConfig()
|
|
13
|
+
const config = getClerkConfig()
|
|
14
|
+
|
|
15
|
+
if (!config.publishableKey) {
|
|
16
|
+
throw new Error('Clerk publishable key is required')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
clerkInstance = new Clerk(config.publishableKey)
|
|
21
|
+
await clerkInstance.load()
|
|
22
|
+
|
|
23
|
+
console.log('Clerk initialized successfully')
|
|
24
|
+
return clerkInstance
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Failed to initialize Clerk:', error)
|
|
27
|
+
throw error
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getClerk = (): Clerk | null => {
|
|
32
|
+
return clerkInstance
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const isClerkInitialized = (): boolean => {
|
|
36
|
+
return clerkInstance !== null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const cleanupClerk = (): void => {
|
|
40
|
+
if (clerkInstance) {
|
|
41
|
+
clerkInstance = null
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
initializeClerk,
|
|
47
|
+
getClerk,
|
|
48
|
+
isClerkInitialized,
|
|
49
|
+
cleanupClerk
|
|
50
|
+
}
|
|
51
|
+
}
|
package/core-box/README.md
CHANGED
|
@@ -140,7 +140,7 @@ The core interface for all search results:
|
|
|
140
140
|
interface ISearchItem {
|
|
141
141
|
name: string; // Display name
|
|
142
142
|
desc: string; // Description
|
|
143
|
-
icon:
|
|
143
|
+
icon: ITuffIcon; // Icon configuration
|
|
144
144
|
push: boolean; // Push mode support
|
|
145
145
|
names: string[]; // Searchable names
|
|
146
146
|
keyWords: string[]; // Search keywords
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* @module core-box/tuff-dsl
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { TalexTouch } from "packages/utils/types";
|
|
19
|
+
// import { TalexTouch } from "packages/utils/types";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* 定义高亮范围
|
|
@@ -375,55 +375,15 @@ export interface TuffCustomRender {
|
|
|
375
375
|
scripts?: string[];
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
+
import type { ITuffIcon } from '../../types/icon'
|
|
379
|
+
|
|
378
380
|
/**
|
|
379
|
-
*
|
|
381
|
+
* Icon definition
|
|
380
382
|
*
|
|
381
383
|
* @description
|
|
382
|
-
*
|
|
383
|
-
* 可以是简单字符串或包含详细配置的对象。
|
|
384
|
+
* Unified icon type supporting only ITuffIcon object format
|
|
384
385
|
*/
|
|
385
|
-
export type TuffIcon =
|
|
386
|
-
| string // 简单字符串:emoji、URL、组件名
|
|
387
|
-
| {
|
|
388
|
-
/**
|
|
389
|
-
* 图标类型
|
|
390
|
-
* @description 指定图标的数据格式和来源
|
|
391
|
-
* @required
|
|
392
|
-
*/
|
|
393
|
-
type: 'emoji' | 'url' | 'base64' | 'fluent' | 'component';
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* 图标值
|
|
397
|
-
* @description 根据type不同,可能是emoji字符、URL地址、Base64编码或组件名
|
|
398
|
-
* @required
|
|
399
|
-
*/
|
|
400
|
-
value: string;
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* 备用图标
|
|
404
|
-
* @description 当主图标无法加载时显示的替代图标
|
|
405
|
-
*/
|
|
406
|
-
fallback?: string;
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* 动态加载函数
|
|
410
|
-
* @description 用于异步加载图标资源的函数
|
|
411
|
-
*/
|
|
412
|
-
loader?: () => Promise<string>;
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* 样式配置
|
|
416
|
-
* @description 控制图标的视觉效果
|
|
417
|
-
*/
|
|
418
|
-
style?: {
|
|
419
|
-
/** 图标尺寸 */
|
|
420
|
-
size?: number;
|
|
421
|
-
/** 图标颜色 */
|
|
422
|
-
color?: string;
|
|
423
|
-
/** 动画效果 */
|
|
424
|
-
animation?: 'spin' | 'pulse' | 'bounce';
|
|
425
|
-
};
|
|
426
|
-
};
|
|
386
|
+
export type TuffIcon = ITuffIcon
|
|
427
387
|
|
|
428
388
|
/**
|
|
429
389
|
* 标签定义
|
package/index.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
export * from './auth'
|
|
1
2
|
export * from './base'
|
|
2
3
|
export * from './common'
|
|
3
4
|
export * from './plugin'
|
|
4
5
|
export * from './core-box'
|
|
5
6
|
export * from './channel'
|
|
6
7
|
export * from './types'
|
|
8
|
+
export * from './types/icon'
|
|
7
9
|
export * from './eventbus'
|
|
8
10
|
export * from './preload'
|
|
11
|
+
export * from './types/download'
|
|
12
|
+
export * from './types/update'
|
package/package.json
CHANGED
package/plugin/index.ts
CHANGED
|
@@ -14,6 +14,9 @@ export enum PluginStatus {
|
|
|
14
14
|
LOADING,
|
|
15
15
|
LOADED,
|
|
16
16
|
LOAD_FAILED,
|
|
17
|
+
|
|
18
|
+
DEV_DISCONNECTED, // Dev Server 断连
|
|
19
|
+
DEV_RECONNECTING, // 正在重连
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export interface PluginIssue {
|
|
@@ -26,14 +29,16 @@ export interface PluginIssue {
|
|
|
26
29
|
timestamp?: number
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
export interface
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
init(): Promise<void>
|
|
32
|
+
export interface DevServerHealthCheckResult {
|
|
33
|
+
healthy: boolean
|
|
34
|
+
version?: string
|
|
35
|
+
timestamp: number
|
|
36
|
+
error?: string
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
import type { ITuffIcon } from '../types/icon'
|
|
40
|
+
|
|
41
|
+
|
|
37
42
|
export interface IPlatformInfo {
|
|
38
43
|
enable: boolean
|
|
39
44
|
arch: Arch[]
|
|
@@ -51,7 +56,7 @@ export interface IPluginBaseInfo {
|
|
|
51
56
|
readme: string
|
|
52
57
|
version: string
|
|
53
58
|
desc: string
|
|
54
|
-
icon:
|
|
59
|
+
icon: ITuffIcon
|
|
55
60
|
platforms: IPlatform
|
|
56
61
|
_uniqueChannelKey: string
|
|
57
62
|
}
|
|
@@ -134,7 +139,7 @@ export interface IPluginFeature {
|
|
|
134
139
|
id: string
|
|
135
140
|
name: string
|
|
136
141
|
desc: string
|
|
137
|
-
icon:
|
|
142
|
+
icon: ITuffIcon
|
|
138
143
|
push: boolean
|
|
139
144
|
platform: IPlatform
|
|
140
145
|
commands: IFeatureCommand[]
|
|
@@ -323,11 +328,14 @@ export interface IPluginManager {
|
|
|
323
328
|
pluginPath: string
|
|
324
329
|
watcher: FSWatcher | null
|
|
325
330
|
devWatcher: any // Temporarily any, as DevPluginWatcher is internal to core-app
|
|
331
|
+
healthMonitor: any | null // DevServerHealthMonitor instance, set by PluginModule
|
|
326
332
|
|
|
327
333
|
getPluginList(): Array<object>
|
|
328
334
|
setActivePlugin(pluginName: string): boolean
|
|
329
335
|
hasPlugin(name: string): boolean
|
|
330
336
|
getPluginByName(name: string): ITouchPlugin | undefined
|
|
337
|
+
enablePlugin(pluginName: string): Promise<boolean>
|
|
338
|
+
disablePlugin(pluginName: string): Promise<boolean>
|
|
331
339
|
reloadPlugin(pluginName: string): Promise<void>
|
|
332
340
|
persistEnabledPlugins(): Promise<void>
|
|
333
341
|
listPlugins(): Promise<Array<string>>
|
package/plugin/sdk/features.ts
CHANGED
package/plugin/sdk/index.ts
CHANGED
package/plugin/sdk/storage.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { StorageStats, StorageTreeNode, FileDetails } from '../../types/storage'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Get the storage for the current plugin.
|
|
3
5
|
* It provides simple file-based storage that is persisted across application launches.
|
|
@@ -50,6 +52,68 @@ export function usePluginStorage() {
|
|
|
50
52
|
*/
|
|
51
53
|
listFiles: async (): Promise<string[]> => {
|
|
52
54
|
return channel.send('plugin:storage:list-files', { pluginName })
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Gets storage statistics for the current plugin.
|
|
59
|
+
* @returns A promise that resolves with storage statistics.
|
|
60
|
+
*/
|
|
61
|
+
getStats: async (): Promise<StorageStats> => {
|
|
62
|
+
return channel.send('plugin:storage:get-stats', { pluginName })
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets the directory tree structure of plugin storage.
|
|
67
|
+
* @returns A promise that resolves with the tree structure.
|
|
68
|
+
*/
|
|
69
|
+
getTree: async (): Promise<StorageTreeNode[]> => {
|
|
70
|
+
return channel.send('plugin:storage:get-tree', { pluginName })
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gets detailed information about a specific file.
|
|
75
|
+
* @param fileName The name of the file to get details for.
|
|
76
|
+
* @returns A promise that resolves with file details.
|
|
77
|
+
*/
|
|
78
|
+
getFileDetails: async (fileName: string): Promise<FileDetails | null> => {
|
|
79
|
+
return channel.send('plugin:storage:get-file-details', { pluginName, fileName })
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Clears all storage for the current plugin.
|
|
84
|
+
* @returns A promise that resolves with the operation result.
|
|
85
|
+
*/
|
|
86
|
+
clearAll: async (): Promise<{ success: boolean, error?: string }> => {
|
|
87
|
+
return channel.send('plugin:storage:clear', { pluginName })
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Opens the plugin storage folder in the system file manager.
|
|
92
|
+
* @returns A promise that resolves when the folder is opened.
|
|
93
|
+
*/
|
|
94
|
+
openFolder: async (): Promise<void> => {
|
|
95
|
+
await channel.send('plugin:storage:open-folder', { pluginName })
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Listens for changes to the storage.
|
|
100
|
+
* @param fileName The file name to listen for changes
|
|
101
|
+
* @param callback The function to call when the storage changes for the current plugin.
|
|
102
|
+
* @returns A function to unsubscribe from the listener.
|
|
103
|
+
*/
|
|
104
|
+
onDidChange: (fileName: string, callback: (newConfig: any) => void) => {
|
|
105
|
+
const listener = (data: { name: string, fileName?: string }) => {
|
|
106
|
+
if (data.name === pluginName &&
|
|
107
|
+
(data.fileName === fileName || data.fileName === undefined)) {
|
|
108
|
+
callback(data)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
channel.regChannel('plugin:storage:update', listener)
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
channel.unRegChannel('plugin:storage:update', listener)
|
|
116
|
+
}
|
|
53
117
|
}
|
|
54
118
|
}
|
|
55
119
|
}
|
package/plugin/sdk/types.ts
CHANGED
|
@@ -842,3 +842,78 @@ export interface IPluginInfoManager {
|
|
|
842
842
|
*/
|
|
843
843
|
getPlatforms(): any
|
|
844
844
|
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Plugin state change event types
|
|
848
|
+
*
|
|
849
|
+
* @description
|
|
850
|
+
* Represents different types of plugin state changes for incremental updates
|
|
851
|
+
*/
|
|
852
|
+
export type PluginStateEvent =
|
|
853
|
+
| { type: 'added'; plugin: any }
|
|
854
|
+
| { type: 'removed'; name: string }
|
|
855
|
+
| { type: 'updated'; name: string; changes: Partial<any> }
|
|
856
|
+
| { type: 'status-changed'; name: string; status: number }
|
|
857
|
+
| { type: 'readme-updated'; name: string; readme: string }
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Plugin filter options for list queries
|
|
861
|
+
*/
|
|
862
|
+
export interface PluginFilters {
|
|
863
|
+
/** Filter by plugin status */
|
|
864
|
+
status?: number
|
|
865
|
+
/** Filter by enabled state */
|
|
866
|
+
enabled?: boolean
|
|
867
|
+
/** Filter by development mode */
|
|
868
|
+
dev?: boolean
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Trigger feature request payload
|
|
873
|
+
*/
|
|
874
|
+
export interface TriggerFeatureRequest {
|
|
875
|
+
/** Plugin name */
|
|
876
|
+
plugin: string
|
|
877
|
+
/** Feature ID */
|
|
878
|
+
feature: string
|
|
879
|
+
/** Search query or trigger input */
|
|
880
|
+
query?: string
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Input changed event payload
|
|
885
|
+
*/
|
|
886
|
+
export interface InputChangedRequest {
|
|
887
|
+
/** Plugin name */
|
|
888
|
+
plugin: string
|
|
889
|
+
/** Feature ID */
|
|
890
|
+
feature: string
|
|
891
|
+
/** Current input value */
|
|
892
|
+
query: string
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Plugin install request payload
|
|
897
|
+
*/
|
|
898
|
+
export interface InstallRequest {
|
|
899
|
+
/** Install source (URL, file path, etc) */
|
|
900
|
+
source: string
|
|
901
|
+
/** Hint type for installer */
|
|
902
|
+
hintType?: string
|
|
903
|
+
/** Additional metadata */
|
|
904
|
+
metadata?: Record<string, any>
|
|
905
|
+
/** Client metadata */
|
|
906
|
+
clientMetadata?: Record<string, any>
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Plugin install response
|
|
911
|
+
*/
|
|
912
|
+
export interface InstallResponse {
|
|
913
|
+
/** Whether installation was successful */
|
|
914
|
+
success: boolean
|
|
915
|
+
/** Error message if failed */
|
|
916
|
+
error?: string
|
|
917
|
+
/** Installed plugin name */
|
|
918
|
+
pluginName?: string
|
|
919
|
+
}
|
package/preload/renderer.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { LoadingEvent, LoadingMode, LoadingState, PreloadAPI } from './load
|
|
|
2
2
|
|
|
3
3
|
function getPreloadApi(): PreloadAPI | null {
|
|
4
4
|
if (typeof window === 'undefined') return null
|
|
5
|
-
return window.api ?? null
|
|
5
|
+
return (window as any).api ?? null
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function sendPreloadEvent(event: LoadingEvent): void {
|
package/renderer/hooks/index.ts
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renderer Process Performance Monitoring
|
|
3
|
+
*
|
|
4
|
+
* Collects detailed performance metrics in the renderer process
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Renderer performance metrics
|
|
9
|
+
*/
|
|
10
|
+
export interface RendererPerformanceMetrics {
|
|
11
|
+
/** Performance timing origin */
|
|
12
|
+
timeOrigin: number
|
|
13
|
+
/** Navigation start time */
|
|
14
|
+
navigationStart: number
|
|
15
|
+
/** DOM content loaded event end */
|
|
16
|
+
domContentLoadedEventEnd?: number
|
|
17
|
+
/** Load event end */
|
|
18
|
+
loadEventEnd?: number
|
|
19
|
+
/** DOM interactive time */
|
|
20
|
+
domInteractive?: number
|
|
21
|
+
/** First paint time */
|
|
22
|
+
firstPaint?: number
|
|
23
|
+
/** First contentful paint time */
|
|
24
|
+
firstContentfulPaint?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get current renderer performance metrics
|
|
29
|
+
*/
|
|
30
|
+
export function getRendererPerformanceMetrics(): RendererPerformanceMetrics {
|
|
31
|
+
const navEntry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming
|
|
32
|
+
const paintEntries = performance.getEntriesByType('paint')
|
|
33
|
+
|
|
34
|
+
const firstPaint = paintEntries.find(e => e.name === 'first-paint')
|
|
35
|
+
const firstContentfulPaint = paintEntries.find(e => e.name === 'first-contentful-paint')
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
timeOrigin: performance.timeOrigin,
|
|
39
|
+
navigationStart: performance.timeOrigin,
|
|
40
|
+
domContentLoadedEventEnd: navEntry?.domContentLoadedEventEnd,
|
|
41
|
+
loadEventEnd: navEntry?.loadEventEnd,
|
|
42
|
+
domInteractive: navEntry?.domInteractive,
|
|
43
|
+
firstPaint: firstPaint?.startTime,
|
|
44
|
+
firstContentfulPaint: firstContentfulPaint?.startTime
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Wait for page load and collect metrics
|
|
50
|
+
*/
|
|
51
|
+
export function collectPerformanceMetricsOnLoad(): Promise<RendererPerformanceMetrics> {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
if (document.readyState === 'complete') {
|
|
54
|
+
// Page already loaded
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
resolve(getRendererPerformanceMetrics())
|
|
57
|
+
}, 0)
|
|
58
|
+
} else {
|
|
59
|
+
// Wait for load event
|
|
60
|
+
window.addEventListener('load', () => {
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
resolve(getRendererPerformanceMetrics())
|
|
63
|
+
}, 0)
|
|
64
|
+
}, { once: true })
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get performance summary for display
|
|
71
|
+
*/
|
|
72
|
+
export function getPerformanceSummary(): {
|
|
73
|
+
domReady: number
|
|
74
|
+
pageLoad: number
|
|
75
|
+
firstPaint: number | undefined
|
|
76
|
+
firstContentfulPaint: number | undefined
|
|
77
|
+
} {
|
|
78
|
+
const metrics = getRendererPerformanceMetrics()
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
domReady: metrics.domContentLoadedEventEnd || 0,
|
|
82
|
+
pageLoad: metrics.loadEventEnd || 0,
|
|
83
|
+
firstPaint: metrics.firstPaint,
|
|
84
|
+
firstContentfulPaint: metrics.firstContentfulPaint
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
@@ -327,7 +327,6 @@ export class TouchStorage<T extends object> {
|
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
const result = channel.sendSync('storage:get', this.#qualifiedName)
|
|
330
|
-
console.log("result", result)
|
|
331
330
|
const parsed = result ? (result as Partial<T>) : {};
|
|
332
331
|
this.assignData(parsed, true);
|
|
333
332
|
|
package/search/types.ts
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* @version 1.0.0
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import type {
|
|
19
|
+
import type { IFeatureCommand } from '../plugin';
|
|
20
|
+
import type { ITuffIcon } from '../types/icon';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Search Result Item Interface
|
|
@@ -59,9 +60,9 @@ export interface ISearchItem {
|
|
|
59
60
|
* Defines how the item's icon should be displayed in the UI.
|
|
60
61
|
* Supports various icon types including files, remix icons, and data URLs.
|
|
61
62
|
*
|
|
62
|
-
* @see {@link
|
|
63
|
+
* @see {@link ITuffIcon} for icon configuration options
|
|
63
64
|
*/
|
|
64
|
-
icon:
|
|
65
|
+
icon: ITuffIcon;
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
68
|
* Whether this item supports push mode functionality
|
|
@@ -592,7 +593,7 @@ export function createDataItem(options: {
|
|
|
592
593
|
icon: {
|
|
593
594
|
type: iconType,
|
|
594
595
|
value: iconValue,
|
|
595
|
-
init: async () => {} // Required by
|
|
596
|
+
init: async () => {} // Required by ITuffIcon interface
|
|
596
597
|
},
|
|
597
598
|
push: false, // Data items don't support push mode
|
|
598
599
|
names: [name], // Include name in searchable names
|
|
@@ -675,7 +676,7 @@ export function createSearchItem(options: {
|
|
|
675
676
|
/** Description or subtitle text */
|
|
676
677
|
desc: string;
|
|
677
678
|
/** Icon configuration object */
|
|
678
|
-
icon:
|
|
679
|
+
icon: ITuffIcon;
|
|
679
680
|
/** Name of the plugin creating this item */
|
|
680
681
|
pluginName: string;
|
|
681
682
|
/** Plugin type (defaults to "feature") */
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// 下载优先级枚举
|
|
2
|
+
export enum DownloadPriority {
|
|
3
|
+
CRITICAL = 100, // 用户手动触发
|
|
4
|
+
HIGH = 80, // 插件安装
|
|
5
|
+
NORMAL = 50, // 应用更新
|
|
6
|
+
LOW = 20, // 资源文件
|
|
7
|
+
BACKGROUND = 10 // 后台预加载
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// 下载模块枚举
|
|
11
|
+
export enum DownloadModule {
|
|
12
|
+
APP_UPDATE = 'app_update',
|
|
13
|
+
PLUGIN_INSTALL = 'plugin_install',
|
|
14
|
+
RESOURCE_DOWNLOAD = 'resource_download',
|
|
15
|
+
USER_MANUAL = 'user_manual'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 下载状态枚举
|
|
19
|
+
export enum DownloadStatus {
|
|
20
|
+
PENDING = 'pending', // 等待中
|
|
21
|
+
DOWNLOADING = 'downloading', // 下载中
|
|
22
|
+
PAUSED = 'paused', // 已暂停
|
|
23
|
+
COMPLETED = 'completed', // 已完成
|
|
24
|
+
FAILED = 'failed', // 失败
|
|
25
|
+
CANCELLED = 'cancelled' // 已取消
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 切片状态枚举
|
|
29
|
+
export enum ChunkStatus {
|
|
30
|
+
PENDING = 'pending',
|
|
31
|
+
DOWNLOADING = 'downloading',
|
|
32
|
+
COMPLETED = 'completed',
|
|
33
|
+
FAILED = 'failed'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 下载任务请求接口
|
|
37
|
+
export interface DownloadRequest {
|
|
38
|
+
id?: string
|
|
39
|
+
url: string
|
|
40
|
+
destination: string
|
|
41
|
+
filename?: string
|
|
42
|
+
priority: DownloadPriority
|
|
43
|
+
module: DownloadModule
|
|
44
|
+
metadata?: Record<string, any>
|
|
45
|
+
checksum?: string
|
|
46
|
+
headers?: Record<string, string>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 下载任务实体
|
|
50
|
+
export interface DownloadTask {
|
|
51
|
+
id: string
|
|
52
|
+
url: string
|
|
53
|
+
destination: string
|
|
54
|
+
filename: string
|
|
55
|
+
priority: DownloadPriority
|
|
56
|
+
module: DownloadModule
|
|
57
|
+
status: DownloadStatus
|
|
58
|
+
progress: DownloadProgress
|
|
59
|
+
chunks: ChunkInfo[]
|
|
60
|
+
metadata: Record<string, any>
|
|
61
|
+
createdAt: Date
|
|
62
|
+
updatedAt: Date
|
|
63
|
+
error?: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 下载进度接口
|
|
67
|
+
export interface DownloadProgress {
|
|
68
|
+
totalSize?: number
|
|
69
|
+
downloadedSize: number
|
|
70
|
+
speed: number // bytes/s
|
|
71
|
+
remainingTime?: number // seconds
|
|
72
|
+
percentage: number // 0-100
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 切片信息接口
|
|
76
|
+
export interface ChunkInfo {
|
|
77
|
+
index: number
|
|
78
|
+
start: number
|
|
79
|
+
end: number
|
|
80
|
+
size: number
|
|
81
|
+
downloaded: number
|
|
82
|
+
status: ChunkStatus
|
|
83
|
+
filePath: string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 下载配置接口
|
|
87
|
+
export interface DownloadConfig {
|
|
88
|
+
concurrency: {
|
|
89
|
+
maxConcurrent: number // 最大并发数 (1-10)
|
|
90
|
+
autoAdjust: boolean // 自动调整
|
|
91
|
+
networkAware: boolean // 网络感知
|
|
92
|
+
priorityBased: boolean // 基于优先级
|
|
93
|
+
}
|
|
94
|
+
chunk: {
|
|
95
|
+
size: number // 切片大小
|
|
96
|
+
resume: boolean // 断点续传
|
|
97
|
+
autoRetry: boolean // 自动重试
|
|
98
|
+
maxRetries: number // 最大重试次数
|
|
99
|
+
}
|
|
100
|
+
storage: {
|
|
101
|
+
tempDir: string // 临时目录
|
|
102
|
+
historyRetention: number // 历史保留天数
|
|
103
|
+
autoCleanup: boolean // 自动清理
|
|
104
|
+
}
|
|
105
|
+
network: {
|
|
106
|
+
timeout: number // 超时时间
|
|
107
|
+
retryDelay: number // 重试延迟
|
|
108
|
+
maxRetries: number // 最大重试次数
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 网络状态接口
|
|
113
|
+
export interface NetworkStatus {
|
|
114
|
+
speed: number // bytes/s
|
|
115
|
+
latency: number // ms
|
|
116
|
+
stability: number // 0-1
|
|
117
|
+
recommendedConcurrency: number
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 队列状态接口
|
|
121
|
+
export interface QueueStatus {
|
|
122
|
+
totalTasks: number
|
|
123
|
+
pendingTasks: number
|
|
124
|
+
activeTasks: number
|
|
125
|
+
completedTasks: number
|
|
126
|
+
failedTasks: number
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 下载统计接口
|
|
130
|
+
export interface DownloadStats {
|
|
131
|
+
totalTasks: number
|
|
132
|
+
completedTasks: number
|
|
133
|
+
failedTasks: number
|
|
134
|
+
totalDownloaded: number
|
|
135
|
+
averageSpeed: number
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 默认下载配置
|
|
139
|
+
export const defaultDownloadConfig: DownloadConfig = {
|
|
140
|
+
concurrency: {
|
|
141
|
+
maxConcurrent: 3,
|
|
142
|
+
autoAdjust: true,
|
|
143
|
+
networkAware: true,
|
|
144
|
+
priorityBased: true
|
|
145
|
+
},
|
|
146
|
+
chunk: {
|
|
147
|
+
size: 1024 * 1024, // 1MB
|
|
148
|
+
resume: true,
|
|
149
|
+
autoRetry: true,
|
|
150
|
+
maxRetries: 3
|
|
151
|
+
},
|
|
152
|
+
storage: {
|
|
153
|
+
tempDir: '', // 将在运行时设置
|
|
154
|
+
historyRetention: 30,
|
|
155
|
+
autoCleanup: true
|
|
156
|
+
},
|
|
157
|
+
network: {
|
|
158
|
+
timeout: 30000,
|
|
159
|
+
retryDelay: 5000,
|
|
160
|
+
maxRetries: 3
|
|
161
|
+
}
|
|
162
|
+
}
|
package/types/icon.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon type definitions
|
|
3
|
+
*
|
|
4
|
+
* @description
|
|
5
|
+
* Defines common icon interfaces and types, supporting emoji, url, and file types
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Icon type enumeration
|
|
10
|
+
* @description
|
|
11
|
+
* - emoji: Emoji characters (e.g., "🚀")
|
|
12
|
+
* - url: Remote URL (http/https) or Data URL (data:image/...)
|
|
13
|
+
* - file: Local file path (relative to plugin root directory)
|
|
14
|
+
*/
|
|
15
|
+
export type TuffIconType = 'emoji' | 'url' | 'file'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Icon status enumeration
|
|
19
|
+
* @description
|
|
20
|
+
* - normal: Normal state
|
|
21
|
+
* - loading: Loading state
|
|
22
|
+
* - error: Error state
|
|
23
|
+
*/
|
|
24
|
+
export type TuffIconStatus = 'normal' | 'loading' | 'error'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Common icon interface
|
|
28
|
+
*
|
|
29
|
+
* @description
|
|
30
|
+
* Unified icon data structure supporting three icon types and status management
|
|
31
|
+
*/
|
|
32
|
+
export interface ITuffIcon {
|
|
33
|
+
/** Icon type */
|
|
34
|
+
type: TuffIconType
|
|
35
|
+
|
|
36
|
+
/** Icon value */
|
|
37
|
+
value: string
|
|
38
|
+
|
|
39
|
+
/** Icon status (optional) */
|
|
40
|
+
status?: TuffIconStatus
|
|
41
|
+
|
|
42
|
+
/** Error message (when status is error) */
|
|
43
|
+
error?: string
|
|
44
|
+
}
|
package/types/index.ts
CHANGED
package/types/storage.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin storage statistics
|
|
3
|
+
*/
|
|
4
|
+
export interface StorageStats {
|
|
5
|
+
/** Total size in bytes */
|
|
6
|
+
totalSize: number
|
|
7
|
+
/** Number of files (excluding directories) */
|
|
8
|
+
fileCount: number
|
|
9
|
+
/** Number of directories */
|
|
10
|
+
dirCount: number
|
|
11
|
+
/** Maximum size limit in bytes (10MB) */
|
|
12
|
+
maxSize: number
|
|
13
|
+
/** Usage percentage (0-100) */
|
|
14
|
+
usagePercent: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Storage tree node representing a file or directory
|
|
19
|
+
*/
|
|
20
|
+
export interface StorageTreeNode {
|
|
21
|
+
/** File or directory name */
|
|
22
|
+
name: string
|
|
23
|
+
/** Relative path from storage root */
|
|
24
|
+
path: string
|
|
25
|
+
/** Node type */
|
|
26
|
+
type: 'file' | 'directory'
|
|
27
|
+
/** Size in bytes (for directories, this is the total size of all contained files) */
|
|
28
|
+
size: number
|
|
29
|
+
/** Last modified timestamp */
|
|
30
|
+
modified: number
|
|
31
|
+
/** Child nodes (only for directories) */
|
|
32
|
+
children?: StorageTreeNode[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Detailed information about a specific file
|
|
37
|
+
*/
|
|
38
|
+
export interface FileDetails {
|
|
39
|
+
/** File name */
|
|
40
|
+
name: string
|
|
41
|
+
/** Relative path from storage root */
|
|
42
|
+
path: string
|
|
43
|
+
/** File size in bytes */
|
|
44
|
+
size: number
|
|
45
|
+
/** Creation timestamp */
|
|
46
|
+
created: number
|
|
47
|
+
/** Last modified timestamp */
|
|
48
|
+
modified: number
|
|
49
|
+
/** File type (extension or detected type) */
|
|
50
|
+
type: string
|
|
51
|
+
/** File content (if available and size permits) */
|
|
52
|
+
content?: any
|
|
53
|
+
/** Whether content was truncated due to size */
|
|
54
|
+
truncated?: boolean
|
|
55
|
+
}
|
|
56
|
+
|
package/types/update.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// 更新源类型枚举
|
|
2
|
+
export enum UpdateProviderType {
|
|
3
|
+
GITHUB = 'github',
|
|
4
|
+
OFFICIAL = 'official',
|
|
5
|
+
CUSTOM = 'custom'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// 更新源配置接口
|
|
9
|
+
export interface UpdateSourceConfig {
|
|
10
|
+
type: UpdateProviderType
|
|
11
|
+
name: string
|
|
12
|
+
url?: string
|
|
13
|
+
enabled: boolean
|
|
14
|
+
priority: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 下载资源接口
|
|
18
|
+
export interface DownloadAsset {
|
|
19
|
+
name: string
|
|
20
|
+
url: string
|
|
21
|
+
size: number
|
|
22
|
+
platform: 'win32' | 'darwin' | 'linux'
|
|
23
|
+
arch: 'x64' | 'arm64'
|
|
24
|
+
checksum?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// GitHub Release接口(兼容GitHub API格式)
|
|
28
|
+
export interface GitHubRelease {
|
|
29
|
+
tag_name: string
|
|
30
|
+
name: string
|
|
31
|
+
published_at: string
|
|
32
|
+
body: string
|
|
33
|
+
assets: DownloadAsset[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 更新检查结果接口
|
|
37
|
+
export interface UpdateCheckResult {
|
|
38
|
+
hasUpdate: boolean
|
|
39
|
+
release?: GitHubRelease
|
|
40
|
+
error?: string
|
|
41
|
+
source: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 自定义更新源配置
|
|
45
|
+
export interface CustomUpdateConfig {
|
|
46
|
+
name: string
|
|
47
|
+
url: string
|
|
48
|
+
apiFormat: 'github' | 'custom'
|
|
49
|
+
headers?: Record<string, string>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 应用预览渠道枚举
|
|
53
|
+
export enum AppPreviewChannel {
|
|
54
|
+
MASTER = 'master',
|
|
55
|
+
SNAPSHOT = 'snapshot'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 更新设置配置
|
|
59
|
+
export interface UpdateSettings {
|
|
60
|
+
enabled: boolean
|
|
61
|
+
frequency: 'startup' | 'daily' | 'weekly' | 'manual'
|
|
62
|
+
source: UpdateSourceConfig
|
|
63
|
+
crossChannel: boolean
|
|
64
|
+
ignoredVersions: string[]
|
|
65
|
+
customSources: CustomUpdateConfig[]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 默认更新设置
|
|
69
|
+
export const defaultUpdateSettings: UpdateSettings = {
|
|
70
|
+
enabled: true,
|
|
71
|
+
frequency: 'startup',
|
|
72
|
+
source: {
|
|
73
|
+
type: UpdateProviderType.GITHUB,
|
|
74
|
+
name: 'GitHub Releases',
|
|
75
|
+
url: 'https://api.github.com/repos/talex-touch/tuff/releases',
|
|
76
|
+
enabled: true,
|
|
77
|
+
priority: 1
|
|
78
|
+
},
|
|
79
|
+
crossChannel: false,
|
|
80
|
+
ignoredVersions: [],
|
|
81
|
+
customSources: []
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 更新错误类型
|
|
85
|
+
export enum UpdateErrorType {
|
|
86
|
+
NETWORK_ERROR = 'network_error',
|
|
87
|
+
TIMEOUT_ERROR = 'timeout_error',
|
|
88
|
+
API_ERROR = 'api_error',
|
|
89
|
+
PARSE_ERROR = 'parse_error',
|
|
90
|
+
VERSION_ERROR = 'version_error',
|
|
91
|
+
UNKNOWN_ERROR = 'unknown_error'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 更新错误接口
|
|
95
|
+
export interface UpdateError extends Error {
|
|
96
|
+
type: UpdateErrorType
|
|
97
|
+
code?: string
|
|
98
|
+
statusCode?: number
|
|
99
|
+
}
|