@talex-touch/utils 1.0.40 → 1.0.44
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/.eslintcache +1 -0
- package/__tests__/cloud-sync-sdk.test.ts +442 -0
- package/__tests__/icons/icons.test.ts +84 -0
- package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
- package/__tests__/power-sdk.test.ts +143 -0
- package/__tests__/preset-export-types.test.ts +108 -0
- package/__tests__/search/fuzzy-match.test.ts +137 -0
- package/__tests__/transport/port-policy.test.ts +44 -0
- package/__tests__/transport-domain-sdks.test.ts +152 -0
- package/__tests__/types/update.test.ts +67 -0
- package/account/account-sdk.ts +915 -0
- package/account/index.ts +2 -0
- package/account/types.ts +321 -0
- package/analytics/client.ts +136 -0
- package/analytics/index.ts +2 -0
- package/analytics/types.ts +156 -0
- package/animation/auto-resize.ts +322 -0
- package/animation/window-node.ts +26 -19
- package/auth/clerk-types.ts +12 -30
- package/auth/index.ts +0 -2
- package/auth/useAuthState.ts +6 -14
- package/base/index.ts +2 -0
- package/base/log-level.ts +105 -0
- package/channel/index.ts +170 -69
- package/cloud-sync/cloud-sync-sdk.ts +450 -0
- package/cloud-sync/index.ts +1 -0
- package/common/file-scan-utils.ts +17 -9
- package/common/index.ts +4 -0
- package/common/logger/index.ts +46 -0
- package/common/logger/logger-manager.ts +303 -0
- package/common/logger/module-logger.ts +270 -0
- package/common/logger/transport-logger.ts +234 -0
- package/common/logger/types.ts +93 -0
- package/common/search/gather.ts +48 -6
- package/common/search/index.ts +8 -0
- package/common/storage/constants.ts +13 -0
- package/common/storage/entity/app-settings.ts +245 -0
- package/common/storage/entity/index.ts +3 -0
- package/common/storage/entity/layout-atom-types.ts +147 -0
- package/common/storage/entity/openers.ts +1 -0
- package/common/storage/entity/preset-cloud-api.ts +132 -0
- package/common/storage/entity/preset-export-types.ts +256 -0
- package/common/storage/entity/shortcut-settings.ts +1 -0
- package/common/storage/shortcut-storage.ts +11 -0
- package/common/utils/clone-diagnostics.ts +105 -0
- package/common/utils/file.ts +16 -8
- package/common/utils/index.ts +6 -2
- package/common/utils/payload-preview.ts +173 -0
- package/common/utils/polling.ts +167 -13
- package/common/utils/safe-path.ts +103 -0
- package/common/utils/safe-shell.ts +115 -0
- package/common/utils/task-queue.ts +4 -1
- package/core-box/builder/tuff-builder.ts +0 -1
- package/core-box/index.ts +1 -1
- package/core-box/recommendation.ts +38 -1
- package/core-box/tuff/tuff-dsl.ts +97 -0
- package/electron/download-manager.ts +10 -7
- package/electron/env-tool.ts +42 -40
- package/electron/index.ts +0 -1
- package/env/index.ts +156 -0
- package/eslint.config.js +55 -0
- package/i18n/index.ts +62 -0
- package/i18n/locales/en.json +226 -0
- package/i18n/locales/zh.json +226 -0
- package/i18n/message-keys.ts +236 -0
- package/i18n/resolver.ts +181 -0
- package/icons/index.ts +257 -0
- package/icons/svg.ts +69 -0
- package/index.ts +9 -1
- package/intelligence/client.ts +72 -42
- package/market/constants.ts +21 -3
- package/market/index.ts +1 -1
- package/market/types.ts +20 -5
- package/package.json +15 -5
- package/permission/index.ts +143 -46
- package/permission/legacy.ts +26 -0
- package/permission/registry.ts +304 -0
- package/permission/types.ts +164 -0
- package/plugin/channel.ts +68 -39
- package/plugin/index.ts +82 -8
- package/plugin/install.ts +3 -0
- package/plugin/log/types.ts +22 -5
- package/plugin/node/logger-manager.ts +11 -3
- package/plugin/node/logger.ts +24 -17
- package/plugin/preload.ts +25 -2
- package/plugin/providers/index.ts +4 -0
- package/plugin/providers/market-client.ts +218 -0
- package/plugin/providers/npm-provider.ts +228 -0
- package/plugin/providers/tpex-provider.ts +297 -0
- package/plugin/providers/tpex-types.ts +34 -0
- package/plugin/sdk/box-items.ts +14 -0
- package/plugin/sdk/box-sdk.ts +64 -0
- package/plugin/sdk/channel.ts +119 -4
- package/plugin/sdk/clipboard.ts +26 -12
- package/plugin/sdk/cloud-sync.ts +113 -0
- package/plugin/sdk/common.ts +19 -11
- package/plugin/sdk/core-box.ts +6 -15
- package/plugin/sdk/division-box.ts +160 -65
- package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
- package/plugin/sdk/feature-sdk.ts +111 -76
- package/plugin/sdk/flow.ts +146 -45
- package/plugin/sdk/hooks/bridge.ts +113 -49
- package/plugin/sdk/hooks/life-cycle.ts +35 -16
- package/plugin/sdk/index.ts +14 -3
- package/plugin/sdk/intelligence.ts +87 -0
- package/plugin/sdk/meta/README.md +179 -0
- package/plugin/sdk/meta-sdk.ts +244 -0
- package/plugin/sdk/notification.ts +9 -0
- package/plugin/sdk/performance.ts +1 -16
- package/plugin/sdk/plugin-info.ts +64 -0
- package/plugin/sdk/power.ts +155 -0
- package/plugin/sdk/recommend.ts +21 -0
- package/plugin/sdk/service/index.ts +12 -8
- package/plugin/sdk/sqlite.ts +141 -0
- package/plugin/sdk/storage.ts +2 -6
- package/plugin/sdk/system.ts +2 -9
- package/plugin/sdk/temp-files.ts +41 -0
- package/plugin/sdk/touch-sdk.ts +18 -0
- package/plugin/sdk/types.ts +44 -4
- package/plugin/sdk/window/index.ts +12 -9
- package/plugin/sdk-version.ts +231 -0
- package/preload/renderer.ts +3 -2
- package/renderer/hooks/arg-mapper.ts +34 -6
- package/renderer/hooks/index.ts +13 -0
- package/renderer/hooks/initialize.ts +2 -1
- package/renderer/hooks/use-agent-market-sdk.ts +7 -0
- package/renderer/hooks/use-agent-market.ts +106 -0
- package/renderer/hooks/use-agents-sdk.ts +7 -0
- package/renderer/hooks/use-app-sdk.ts +7 -0
- package/renderer/hooks/use-channel.ts +33 -4
- package/renderer/hooks/use-download-sdk.ts +21 -0
- package/renderer/hooks/use-intelligence-sdk.ts +7 -0
- package/renderer/hooks/use-intelligence-stats.ts +290 -0
- package/renderer/hooks/use-intelligence.ts +202 -104
- package/renderer/hooks/use-market-sdk.ts +16 -0
- package/renderer/hooks/use-notification-sdk.ts +7 -0
- package/renderer/hooks/use-permission-sdk.ts +7 -0
- package/renderer/hooks/use-permission.ts +325 -0
- package/renderer/hooks/use-platform-sdk.ts +7 -0
- package/renderer/hooks/use-plugin-sdk.ts +16 -0
- package/renderer/hooks/use-settings-sdk.ts +7 -0
- package/renderer/hooks/use-update-sdk.ts +21 -0
- package/renderer/index.ts +1 -0
- package/renderer/ref.ts +19 -10
- package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
- package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
- package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
- package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
- package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
- package/renderer/shared/components/index.ts +5 -0
- package/renderer/shared/components/shims-vue.d.ts +5 -0
- package/renderer/shared/index.ts +2 -0
- package/renderer/shared/plugin-detail.ts +62 -0
- package/renderer/storage/app-settings.ts +3 -1
- package/renderer/storage/base-storage.ts +508 -82
- package/renderer/storage/intelligence-storage.ts +37 -46
- package/renderer/storage/openers.ts +3 -1
- package/renderer/storage/storage-subscription.ts +126 -42
- package/renderer/touch-sdk/env.ts +10 -10
- package/renderer/touch-sdk/index.ts +114 -18
- package/renderer/touch-sdk/terminal.ts +24 -13
- package/search/feature-matcher.ts +279 -0
- package/search/fuzzy-match.ts +64 -34
- package/search/index.ts +10 -0
- package/search/levenshtein-utils.ts +17 -11
- package/transport/errors.ts +310 -0
- package/transport/event/builder.ts +378 -0
- package/transport/event/index.ts +7 -0
- package/transport/event/types.ts +292 -0
- package/transport/events/index.ts +2670 -0
- package/transport/events/meta-overlay.ts +79 -0
- package/transport/events/types/agents.ts +177 -0
- package/transport/events/types/app-index.ts +9 -0
- package/transport/events/types/app.ts +475 -0
- package/transport/events/types/box-item.ts +222 -0
- package/transport/events/types/clipboard.ts +80 -0
- package/transport/events/types/core-box.ts +534 -0
- package/transport/events/types/device-idle.ts +7 -0
- package/transport/events/types/division-box.ts +99 -0
- package/transport/events/types/download.ts +115 -0
- package/transport/events/types/file-index.ts +73 -0
- package/transport/events/types/flow.ts +149 -0
- package/transport/events/types/index.ts +70 -0
- package/transport/events/types/market.ts +39 -0
- package/transport/events/types/meta-overlay.ts +184 -0
- package/transport/events/types/notification.ts +140 -0
- package/transport/events/types/permission.ts +90 -0
- package/transport/events/types/platform.ts +8 -0
- package/transport/events/types/plugin.ts +620 -0
- package/transport/events/types/sentry.ts +20 -0
- package/transport/events/types/storage.ts +208 -0
- package/transport/events/types/transport.ts +60 -0
- package/transport/events/types/tray.ts +16 -0
- package/transport/events/types/update.ts +78 -0
- package/transport/index.ts +139 -0
- package/transport/main.ts +2 -0
- package/transport/sdk/constants.ts +29 -0
- package/transport/sdk/domains/agents-market.ts +47 -0
- package/transport/sdk/domains/agents.ts +62 -0
- package/transport/sdk/domains/app.ts +48 -0
- package/transport/sdk/domains/disposable.ts +35 -0
- package/transport/sdk/domains/download.ts +139 -0
- package/transport/sdk/domains/index.ts +13 -0
- package/transport/sdk/domains/intelligence.ts +616 -0
- package/transport/sdk/domains/market.ts +35 -0
- package/transport/sdk/domains/notification.ts +62 -0
- package/transport/sdk/domains/permission.ts +85 -0
- package/transport/sdk/domains/platform.ts +19 -0
- package/transport/sdk/domains/plugin.ts +144 -0
- package/transport/sdk/domains/settings.ts +92 -0
- package/transport/sdk/domains/update.ts +64 -0
- package/transport/sdk/index.ts +60 -0
- package/transport/sdk/main-transport.ts +710 -0
- package/transport/sdk/main.ts +9 -0
- package/transport/sdk/plugin-transport.ts +654 -0
- package/transport/sdk/port-policy.ts +38 -0
- package/transport/sdk/renderer-transport.ts +1165 -0
- package/transport/types.ts +605 -0
- package/types/agent.ts +399 -0
- package/types/cloud-sync.ts +157 -0
- package/types/division-box.ts +47 -27
- package/types/download.ts +1 -0
- package/types/flow.ts +63 -12
- package/types/icon.ts +2 -1
- package/types/index.ts +5 -0
- package/types/intelligence.ts +1492 -81
- package/types/modules/base.ts +2 -0
- package/types/path-browserify.d.ts +5 -0
- package/types/platform.ts +12 -0
- package/types/startup-info.ts +32 -0
- package/types/touch-app-core.ts +8 -8
- package/types/update.ts +94 -1
- package/vitest.config.ts +25 -0
- package/auth/useClerkConfig.ts +0 -40
- package/auth/useClerkProvider.ts +0 -52
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { getTelemetryApiBase, normalizeBaseUrl } from '../env'
|
|
2
|
+
import type {
|
|
3
|
+
ConflictItem,
|
|
4
|
+
DeviceAttestPayload,
|
|
5
|
+
DeviceAttestResponse,
|
|
6
|
+
HandshakeResponse,
|
|
7
|
+
KeyRegisterResponse,
|
|
8
|
+
KeyringMeta,
|
|
9
|
+
KeyringSecret,
|
|
10
|
+
KeyRotateResponse,
|
|
11
|
+
KeysIssueDeviceResponse,
|
|
12
|
+
KeysListResponse,
|
|
13
|
+
KeysRecoverDeviceResponse,
|
|
14
|
+
PullResponse,
|
|
15
|
+
PushResponse,
|
|
16
|
+
QuotaInfo,
|
|
17
|
+
QuotaValidateResponse,
|
|
18
|
+
SyncErrorCode,
|
|
19
|
+
SyncItemInput,
|
|
20
|
+
UploadResponse,
|
|
21
|
+
} from '../types/cloud-sync'
|
|
22
|
+
|
|
23
|
+
const DEFAULT_SYNC_TOKEN_TTL_MS = 1000 * 60 * 60 * 24 * 7
|
|
24
|
+
|
|
25
|
+
export interface CloudSyncSDKOptions {
|
|
26
|
+
baseUrl?: string
|
|
27
|
+
serviceBaseUrl?: string
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated Use baseUrl or serviceBaseUrl instead.
|
|
30
|
+
*/
|
|
31
|
+
apiBaseUrl?: string
|
|
32
|
+
getAuthToken: () => string | Promise<string>
|
|
33
|
+
getDeviceId: () => string | Promise<string>
|
|
34
|
+
fetch?: typeof fetch
|
|
35
|
+
now?: () => number
|
|
36
|
+
syncTokenCache?: { token?: string, expiresAt?: string }
|
|
37
|
+
onSyncTokenUpdate?: (token: string, expiresAt: string) => void
|
|
38
|
+
onHandshake?: (response: HandshakeResponse) => void
|
|
39
|
+
onStepUpRequired?: () => string | null | Promise<string | null>
|
|
40
|
+
formDataFactory?: () => FormData
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class CloudSyncError extends Error {
|
|
44
|
+
status: number
|
|
45
|
+
errorCode?: SyncErrorCode | string
|
|
46
|
+
data?: unknown
|
|
47
|
+
|
|
48
|
+
constructor(message: string, status: number, errorCode?: SyncErrorCode | string, data?: unknown) {
|
|
49
|
+
super(message)
|
|
50
|
+
this.name = 'CloudSyncError'
|
|
51
|
+
this.status = status
|
|
52
|
+
this.errorCode = errorCode
|
|
53
|
+
this.data = data
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class CloudSyncSDK {
|
|
58
|
+
private baseUrl: string
|
|
59
|
+
private getAuthToken: () => Promise<string>
|
|
60
|
+
private getDeviceId: () => Promise<string>
|
|
61
|
+
private fetchFn: typeof fetch | null
|
|
62
|
+
private now: () => number
|
|
63
|
+
private syncTokenCache: { token?: string, expiresAt?: string }
|
|
64
|
+
private onSyncTokenUpdate?: (token: string, expiresAt: string) => void
|
|
65
|
+
private onHandshake?: (response: HandshakeResponse) => void
|
|
66
|
+
private onStepUpRequired?: () => string | null | Promise<string | null>
|
|
67
|
+
private formDataFactory?: () => FormData
|
|
68
|
+
private handshakePromise: Promise<HandshakeResponse> | null = null
|
|
69
|
+
|
|
70
|
+
constructor(options: CloudSyncSDKOptions) {
|
|
71
|
+
const baseUrl = options.baseUrl ?? options.serviceBaseUrl ?? options.apiBaseUrl ?? getTelemetryApiBase()
|
|
72
|
+
this.baseUrl = normalizeBaseUrl(baseUrl)
|
|
73
|
+
this.getAuthToken = async () => options.getAuthToken()
|
|
74
|
+
this.getDeviceId = async () => options.getDeviceId()
|
|
75
|
+
this.fetchFn = options.fetch ?? (globalThis as any).fetch ?? null
|
|
76
|
+
this.now = options.now ?? (() => Date.now())
|
|
77
|
+
this.syncTokenCache = options.syncTokenCache ?? {}
|
|
78
|
+
this.onSyncTokenUpdate = options.onSyncTokenUpdate
|
|
79
|
+
this.onHandshake = options.onHandshake
|
|
80
|
+
this.onStepUpRequired = options.onStepUpRequired
|
|
81
|
+
this.formDataFactory = options.formDataFactory
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async handshake(): Promise<HandshakeResponse> {
|
|
85
|
+
if (this.handshakePromise)
|
|
86
|
+
return this.handshakePromise
|
|
87
|
+
|
|
88
|
+
this.handshakePromise = (async () => {
|
|
89
|
+
const headers = await this.buildHeaders()
|
|
90
|
+
const response = await this.request<HandshakeResponse>('/api/v1/sync/handshake', {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers,
|
|
93
|
+
})
|
|
94
|
+
this.updateSyncToken(response.sync_token, response.sync_token_expires_at)
|
|
95
|
+
try {
|
|
96
|
+
this.onHandshake?.(response)
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
return response
|
|
100
|
+
})()
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
return await this.handshakePromise
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
this.handshakePromise = null
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async push(items: SyncItemInput[]): Promise<PushResponse> {
|
|
111
|
+
return this.requestWithSyncToken<PushResponse>('/api/v1/sync/push', {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
json: { items },
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async pull(params?: { cursor?: number, limit?: number }): Promise<PullResponse> {
|
|
118
|
+
const cursor = params?.cursor ?? 0
|
|
119
|
+
const limit = params?.limit ?? 200
|
|
120
|
+
const query = new URLSearchParams()
|
|
121
|
+
query.set('cursor', String(cursor))
|
|
122
|
+
query.set('limit', String(limit))
|
|
123
|
+
return this.requestWithSyncToken<PullResponse>(`/api/v1/sync/pull?${query.toString()}`, {
|
|
124
|
+
method: 'GET',
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async uploadBlob(file: Blob | File, opts?: { filename?: string, contentType?: string }): Promise<UploadResponse> {
|
|
129
|
+
const formData = this.createFormData()
|
|
130
|
+
const name = opts?.filename ?? (file as File).name ?? 'blob'
|
|
131
|
+
formData.append('file', file, name)
|
|
132
|
+
return this.requestWithSyncToken<UploadResponse>('/api/v1/sync/blobs/upload', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
body: formData,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getQuotas(): Promise<QuotaInfo> {
|
|
139
|
+
const headers = await this.buildHeaders()
|
|
140
|
+
return this.request<QuotaInfo>('/api/v1/quotas', { method: 'GET', headers })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async validateQuotas(payload: { storage_bytes_delta: number, objects_delta: number }): Promise<QuotaValidateResponse> {
|
|
144
|
+
const headers = await this.buildHeaders()
|
|
145
|
+
return this.request<QuotaValidateResponse>('/api/v1/quotas/validate', {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers,
|
|
148
|
+
json: payload,
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async registerKey(payload: { key_type: string, encrypted_key: string, recovery_code_hash?: string | null }): Promise<KeyRegisterResponse> {
|
|
153
|
+
const headers = await this.buildHeaders()
|
|
154
|
+
return this.request<KeyRegisterResponse>('/api/v1/keys/register', {
|
|
155
|
+
method: 'POST',
|
|
156
|
+
headers,
|
|
157
|
+
json: payload,
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async rotateKey(payload: { key_type: string, encrypted_key: string }): Promise<KeyRotateResponse> {
|
|
162
|
+
const headers = await this.buildHeaders()
|
|
163
|
+
return this.request<KeyRotateResponse>('/api/v1/keys/rotate', {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers,
|
|
166
|
+
json: payload,
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async listKeyrings(): Promise<KeyringMeta[]> {
|
|
171
|
+
const headers = await this.buildHeaders()
|
|
172
|
+
const response = await this.request<KeysListResponse>('/api/v1/keys/list', { method: 'GET', headers })
|
|
173
|
+
return response.keyrings
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async issueDeviceKey(payload: {
|
|
177
|
+
target_device_id: string
|
|
178
|
+
key_type: string
|
|
179
|
+
encrypted_key: string
|
|
180
|
+
recovery_code_hash?: string | null
|
|
181
|
+
}, options?: { stepUpToken?: string | null }): Promise<KeysIssueDeviceResponse> {
|
|
182
|
+
const request = async (stepUpToken?: string | null) => {
|
|
183
|
+
const headers = await this.buildHeaders(
|
|
184
|
+
stepUpToken ? { 'x-login-token': stepUpToken } : undefined,
|
|
185
|
+
)
|
|
186
|
+
return this.request<KeysIssueDeviceResponse>('/api/v1/keys/issue-device', {
|
|
187
|
+
method: 'POST',
|
|
188
|
+
headers,
|
|
189
|
+
json: payload,
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
return await request(options?.stepUpToken)
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
if (options?.stepUpToken || !this.isStepUpRequired(error)) {
|
|
198
|
+
throw error
|
|
199
|
+
}
|
|
200
|
+
const fallbackToken = await this.tryRequestStepUpToken()
|
|
201
|
+
if (!fallbackToken) {
|
|
202
|
+
throw error
|
|
203
|
+
}
|
|
204
|
+
return await request(fallbackToken)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async recoverDevice(payload: { recovery_code: string }, options?: { stepUpToken?: string | null }): Promise<KeyringSecret[]> {
|
|
209
|
+
const request = async (stepUpToken?: string | null) => {
|
|
210
|
+
const headers = await this.buildHeaders(
|
|
211
|
+
stepUpToken ? { 'x-login-token': stepUpToken } : undefined,
|
|
212
|
+
)
|
|
213
|
+
const response = await this.request<KeysRecoverDeviceResponse>('/api/v1/keys/recover-device', {
|
|
214
|
+
method: 'POST',
|
|
215
|
+
headers,
|
|
216
|
+
json: payload,
|
|
217
|
+
})
|
|
218
|
+
return response.keyrings
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
return await request(options?.stepUpToken)
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (options?.stepUpToken || !this.isStepUpRequired(error)) {
|
|
226
|
+
throw error
|
|
227
|
+
}
|
|
228
|
+
const fallbackToken = await this.tryRequestStepUpToken()
|
|
229
|
+
if (!fallbackToken) {
|
|
230
|
+
throw error
|
|
231
|
+
}
|
|
232
|
+
return await request(fallbackToken)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async attestDevice(payload: DeviceAttestPayload): Promise<DeviceAttestResponse> {
|
|
237
|
+
const headers = await this.buildHeaders()
|
|
238
|
+
return this.request<DeviceAttestResponse>('/api/v1/devices/attest', {
|
|
239
|
+
method: 'POST',
|
|
240
|
+
headers,
|
|
241
|
+
json: payload,
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async downloadBlob(blobId: string): Promise<{
|
|
246
|
+
data: ArrayBuffer
|
|
247
|
+
contentType: string
|
|
248
|
+
sha256: string | null
|
|
249
|
+
sizeBytes: number | null
|
|
250
|
+
}> {
|
|
251
|
+
const path = `/api/v1/sync/blobs/${encodeURIComponent(blobId)}/download`
|
|
252
|
+
const hadToken = Boolean(this.syncTokenCache.token)
|
|
253
|
+
const syncToken = await this.ensureSession()
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const headers = await this.buildHeaders()
|
|
257
|
+
const result = await this.requestBinary(path, { method: 'GET', headers, syncToken })
|
|
258
|
+
const sizeBytes = Number(result.headers.get('content-length'))
|
|
259
|
+
return {
|
|
260
|
+
data: result.data,
|
|
261
|
+
contentType: result.headers.get('content-type') ?? 'application/octet-stream',
|
|
262
|
+
sha256: result.headers.get('x-content-sha256'),
|
|
263
|
+
sizeBytes: Number.isFinite(sizeBytes) ? sizeBytes : null,
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
if (
|
|
268
|
+
hadToken
|
|
269
|
+
&& error instanceof CloudSyncError
|
|
270
|
+
&& (error.errorCode === 'SYNC_INVALID_TOKEN' || error.errorCode === 'SYNC_TOKEN_EXPIRED')
|
|
271
|
+
) {
|
|
272
|
+
const refreshed = await this.handshake()
|
|
273
|
+
const headers = await this.buildHeaders()
|
|
274
|
+
const result = await this.requestBinary(path, { method: 'GET', headers, syncToken: refreshed.sync_token })
|
|
275
|
+
const sizeBytes = Number(result.headers.get('content-length'))
|
|
276
|
+
return {
|
|
277
|
+
data: result.data,
|
|
278
|
+
contentType: result.headers.get('content-type') ?? 'application/octet-stream',
|
|
279
|
+
sha256: result.headers.get('x-content-sha256'),
|
|
280
|
+
sizeBytes: Number.isFinite(sizeBytes) ? sizeBytes : null,
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
throw error
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private async requestWithSyncToken<T>(path: string, init: RequestInit & { json?: any }): Promise<T> {
|
|
288
|
+
const hadToken = Boolean(this.syncTokenCache.token)
|
|
289
|
+
const syncToken = await this.ensureSession()
|
|
290
|
+
try {
|
|
291
|
+
const headers = this.mergeHeaders(await this.buildHeaders(), init.headers)
|
|
292
|
+
return await this.request<T>(path, { ...init, headers, syncToken })
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
if (
|
|
296
|
+
hadToken
|
|
297
|
+
&& error instanceof CloudSyncError
|
|
298
|
+
&& (error.errorCode === 'SYNC_INVALID_TOKEN' || error.errorCode === 'SYNC_TOKEN_EXPIRED')
|
|
299
|
+
) {
|
|
300
|
+
const refreshed = await this.handshake()
|
|
301
|
+
const headers = this.mergeHeaders(await this.buildHeaders(), init.headers)
|
|
302
|
+
return this.request<T>(path, { ...init, headers, syncToken: refreshed.sync_token })
|
|
303
|
+
}
|
|
304
|
+
throw error
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private async tryRequestStepUpToken(): Promise<string | null> {
|
|
309
|
+
if (!this.onStepUpRequired) {
|
|
310
|
+
return null
|
|
311
|
+
}
|
|
312
|
+
const token = await this.onStepUpRequired()
|
|
313
|
+
if (typeof token !== 'string') {
|
|
314
|
+
return null
|
|
315
|
+
}
|
|
316
|
+
const trimmed = token.trim()
|
|
317
|
+
return trimmed || null
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private isStepUpRequired(error: unknown): boolean {
|
|
321
|
+
if (!(error instanceof CloudSyncError)) {
|
|
322
|
+
return false
|
|
323
|
+
}
|
|
324
|
+
if (error.errorCode !== 'DEVICE_NOT_AUTHORIZED') {
|
|
325
|
+
return false
|
|
326
|
+
}
|
|
327
|
+
const payloadMessage =
|
|
328
|
+
error.data && typeof error.data === 'object' && 'message' in error.data
|
|
329
|
+
? typeof (error.data as { message?: unknown }).message === 'string'
|
|
330
|
+
? (error.data as { message: string }).message
|
|
331
|
+
: ''
|
|
332
|
+
: ''
|
|
333
|
+
const detail = `${typeof error.message === 'string' ? error.message : ''} ${payloadMessage}`
|
|
334
|
+
return /mf2a|step[-\s]?up|required/i.test(detail)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private async ensureSession(): Promise<string> {
|
|
338
|
+
const cachedToken = this.syncTokenCache.token
|
|
339
|
+
if (cachedToken && !this.isTokenExpired(this.syncTokenCache.expiresAt))
|
|
340
|
+
return cachedToken
|
|
341
|
+
const response = await this.handshake()
|
|
342
|
+
return response.sync_token
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private updateSyncToken(token: string, expiresAtInput?: string) {
|
|
346
|
+
const expiresAt = expiresAtInput ?? new Date(this.now() + DEFAULT_SYNC_TOKEN_TTL_MS).toISOString()
|
|
347
|
+
this.syncTokenCache.token = token
|
|
348
|
+
this.syncTokenCache.expiresAt = expiresAt
|
|
349
|
+
this.onSyncTokenUpdate?.(token, expiresAt)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private isTokenExpired(expiresAt?: string) {
|
|
353
|
+
if (!expiresAt)
|
|
354
|
+
return false
|
|
355
|
+
const timestamp = Date.parse(expiresAt)
|
|
356
|
+
if (!Number.isFinite(timestamp))
|
|
357
|
+
return false
|
|
358
|
+
return timestamp <= this.now()
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private createFormData(): FormData {
|
|
362
|
+
if (this.formDataFactory)
|
|
363
|
+
return this.formDataFactory()
|
|
364
|
+
if (typeof FormData !== 'undefined')
|
|
365
|
+
return new FormData()
|
|
366
|
+
throw new Error('[CloudSyncSDK] FormData is not available. Provide formDataFactory in Node/Electron main.')
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
private async buildHeaders(extra?: Record<string, string>) {
|
|
370
|
+
const token = await this.getAuthToken()
|
|
371
|
+
const deviceId = await this.getDeviceId()
|
|
372
|
+
const authHeader = token.startsWith('Bearer ') ? token : `Bearer ${token}`
|
|
373
|
+
return {
|
|
374
|
+
authorization: authHeader,
|
|
375
|
+
'x-device-id': deviceId,
|
|
376
|
+
...(extra ?? {}),
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private mergeHeaders(base: Record<string, string>, extra?: HeadersInit) {
|
|
381
|
+
const headers = new Headers(base)
|
|
382
|
+
if (extra) {
|
|
383
|
+
const extraHeaders = new Headers(extra)
|
|
384
|
+
extraHeaders.forEach((value, key) => {
|
|
385
|
+
headers.set(key, value)
|
|
386
|
+
})
|
|
387
|
+
}
|
|
388
|
+
return headers
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private async requestBinary(
|
|
392
|
+
path: string,
|
|
393
|
+
init: RequestInit & { syncToken?: string },
|
|
394
|
+
): Promise<{ data: ArrayBuffer, headers: Headers }> {
|
|
395
|
+
const fetchFn = this.fetchFn ?? (globalThis as any).fetch
|
|
396
|
+
if (!fetchFn)
|
|
397
|
+
throw new Error('[CloudSyncSDK] Fetch API not available. Provide fetch in options.')
|
|
398
|
+
|
|
399
|
+
const headers = new Headers(init.headers ?? {})
|
|
400
|
+
if (init.syncToken)
|
|
401
|
+
headers.set('x-sync-token', init.syncToken)
|
|
402
|
+
|
|
403
|
+
const url = `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`
|
|
404
|
+
const response = await fetchFn(url, { ...init, headers })
|
|
405
|
+
|
|
406
|
+
if (!response.ok) {
|
|
407
|
+
const contentType = response.headers.get('content-type') ?? ''
|
|
408
|
+
const payload = contentType.includes('application/json')
|
|
409
|
+
? await response.json().catch(() => null)
|
|
410
|
+
: await response.text().catch(() => null)
|
|
411
|
+
|
|
412
|
+
const errorCode = payload?.errorCode as SyncErrorCode | undefined
|
|
413
|
+
const message = errorCode ?? response.statusText ?? 'Cloud sync request failed'
|
|
414
|
+
throw new CloudSyncError(message, response.status, errorCode, payload)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return { data: await response.arrayBuffer(), headers: response.headers }
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private async request<T>(path: string, init: RequestInit & { json?: any, syncToken?: string }): Promise<T> {
|
|
421
|
+
const fetchFn = this.fetchFn ?? (globalThis as any).fetch
|
|
422
|
+
if (!fetchFn)
|
|
423
|
+
throw new Error('[CloudSyncSDK] Fetch API not available. Provide fetch in options.')
|
|
424
|
+
|
|
425
|
+
const headers = new Headers(init.headers ?? {})
|
|
426
|
+
if (init.syncToken)
|
|
427
|
+
headers.set('x-sync-token', init.syncToken)
|
|
428
|
+
if (init.json !== undefined) {
|
|
429
|
+
headers.set('content-type', 'application/json')
|
|
430
|
+
init.body = JSON.stringify(init.json)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const url = `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`
|
|
434
|
+
const response = await fetchFn(url, { ...init, headers })
|
|
435
|
+
const contentType = response.headers.get('content-type') ?? ''
|
|
436
|
+
const payload = contentType.includes('application/json')
|
|
437
|
+
? await response.json().catch(() => null)
|
|
438
|
+
: await response.text().catch(() => null)
|
|
439
|
+
|
|
440
|
+
if (!response.ok) {
|
|
441
|
+
const errorCode = payload?.errorCode as SyncErrorCode | undefined
|
|
442
|
+
const message = errorCode ?? response.statusText ?? 'Cloud sync request failed'
|
|
443
|
+
throw new CloudSyncError(message, response.status, errorCode, payload)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return payload as T
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export type { ConflictItem }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './cloud-sync-sdk'
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* @version 1.0.0
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import pathBrowserify from 'path-browserify'
|
|
10
11
|
import type { FileScanOptions } from './file-scan-constants'
|
|
12
|
+
import { hasWindow } from '../env'
|
|
11
13
|
import {
|
|
12
14
|
BASE_BLACKLISTED_DIRS,
|
|
13
15
|
BLACKLISTED_EXTENSIONS,
|
|
@@ -21,15 +23,21 @@ import {
|
|
|
21
23
|
} from './file-scan-constants'
|
|
22
24
|
|
|
23
25
|
const path = (() => {
|
|
24
|
-
if (
|
|
25
|
-
return
|
|
26
|
+
if (hasWindow()) {
|
|
27
|
+
return pathBrowserify
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
|
|
30
|
+
const nodeRequire = typeof require === 'function' ? require : null
|
|
31
|
+
if (nodeRequire) {
|
|
32
|
+
try {
|
|
33
|
+
return nodeRequire('node:path')
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return pathBrowserify
|
|
37
|
+
}
|
|
32
38
|
}
|
|
39
|
+
|
|
40
|
+
return pathBrowserify
|
|
33
41
|
})()
|
|
34
42
|
|
|
35
43
|
// 重新导出类型
|
|
@@ -106,8 +114,8 @@ export function isIndexableFile(
|
|
|
106
114
|
|
|
107
115
|
// 检查文件名前缀和后缀
|
|
108
116
|
if (fileName) {
|
|
109
|
-
const firstChar = fileName
|
|
110
|
-
const lastChar = fileName
|
|
117
|
+
const firstChar = fileName.charAt(0)
|
|
118
|
+
const lastChar = fileName.charAt(fileName.length - 1)
|
|
111
119
|
|
|
112
120
|
if (BLACKLISTED_FILE_PREFIXES.has(firstChar))
|
|
113
121
|
return false
|
package/common/index.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export * from '../electron/file-parsers'
|
|
2
2
|
export * from './file-scan-constants'
|
|
3
3
|
export * from './file-scan-utils'
|
|
4
|
+
export { getLogger, LoggerManager, loggerManager } from './logger/logger-manager'
|
|
5
|
+
export { ModuleLogger } from './logger/module-logger'
|
|
6
|
+
export { createTransportLogger, transportLoggers, TuffTransportLogger } from './logger/transport-logger'
|
|
7
|
+
export type { LogEntry, LoggerInfo, LoggingConfig, ModuleConfig, ModuleLoggerOptions } from './logger/types'
|
|
4
8
|
export * from './search'
|
|
5
9
|
export * from './storage/index'
|
|
6
10
|
export * from './utils'
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Logger System
|
|
3
|
+
*
|
|
4
|
+
* Unified logging system for Talex Touch modules.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { getLogger, LogLevel } from '@talex-touch/utils/common/logger'
|
|
9
|
+
*
|
|
10
|
+
* const logger = getLogger('my-module', { color: 'blue' })
|
|
11
|
+
*
|
|
12
|
+
* logger.debug('Debug message', { data: 123 })
|
|
13
|
+
* logger.info('Info message')
|
|
14
|
+
* logger.warn('Warning message')
|
|
15
|
+
* logger.error('Error message', error)
|
|
16
|
+
*
|
|
17
|
+
* // Timing
|
|
18
|
+
* logger.time('operation')
|
|
19
|
+
* await someOperation()
|
|
20
|
+
* logger.timeEnd('operation')
|
|
21
|
+
*
|
|
22
|
+
* // Grouping
|
|
23
|
+
* logger.group('Session')
|
|
24
|
+
* logger.info('Session ID:', sessionId)
|
|
25
|
+
* logger.groupEnd()
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export { getLogger, LoggerManager, loggerManager } from './logger-manager'
|
|
30
|
+
export { ModuleLogger } from './module-logger'
|
|
31
|
+
export {
|
|
32
|
+
createTransportLogger,
|
|
33
|
+
transportLoggers,
|
|
34
|
+
TuffTransportLogger,
|
|
35
|
+
} from './transport-logger'
|
|
36
|
+
export {
|
|
37
|
+
type LogEntry,
|
|
38
|
+
type LoggerInfo,
|
|
39
|
+
type LoggingConfig,
|
|
40
|
+
LogLevel,
|
|
41
|
+
type LogLevelString,
|
|
42
|
+
logLevelToString,
|
|
43
|
+
type ModuleConfig,
|
|
44
|
+
type ModuleLoggerOptions,
|
|
45
|
+
stringToLogLevel,
|
|
46
|
+
} from './types'
|