@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.
Files changed (235) hide show
  1. package/.eslintcache +1 -0
  2. package/__tests__/cloud-sync-sdk.test.ts +442 -0
  3. package/__tests__/icons/icons.test.ts +84 -0
  4. package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
  5. package/__tests__/power-sdk.test.ts +143 -0
  6. package/__tests__/preset-export-types.test.ts +108 -0
  7. package/__tests__/search/fuzzy-match.test.ts +137 -0
  8. package/__tests__/transport/port-policy.test.ts +44 -0
  9. package/__tests__/transport-domain-sdks.test.ts +152 -0
  10. package/__tests__/types/update.test.ts +67 -0
  11. package/account/account-sdk.ts +915 -0
  12. package/account/index.ts +2 -0
  13. package/account/types.ts +321 -0
  14. package/analytics/client.ts +136 -0
  15. package/analytics/index.ts +2 -0
  16. package/analytics/types.ts +156 -0
  17. package/animation/auto-resize.ts +322 -0
  18. package/animation/window-node.ts +26 -19
  19. package/auth/clerk-types.ts +12 -30
  20. package/auth/index.ts +0 -2
  21. package/auth/useAuthState.ts +6 -14
  22. package/base/index.ts +2 -0
  23. package/base/log-level.ts +105 -0
  24. package/channel/index.ts +170 -69
  25. package/cloud-sync/cloud-sync-sdk.ts +450 -0
  26. package/cloud-sync/index.ts +1 -0
  27. package/common/file-scan-utils.ts +17 -9
  28. package/common/index.ts +4 -0
  29. package/common/logger/index.ts +46 -0
  30. package/common/logger/logger-manager.ts +303 -0
  31. package/common/logger/module-logger.ts +270 -0
  32. package/common/logger/transport-logger.ts +234 -0
  33. package/common/logger/types.ts +93 -0
  34. package/common/search/gather.ts +48 -6
  35. package/common/search/index.ts +8 -0
  36. package/common/storage/constants.ts +13 -0
  37. package/common/storage/entity/app-settings.ts +245 -0
  38. package/common/storage/entity/index.ts +3 -0
  39. package/common/storage/entity/layout-atom-types.ts +147 -0
  40. package/common/storage/entity/openers.ts +1 -0
  41. package/common/storage/entity/preset-cloud-api.ts +132 -0
  42. package/common/storage/entity/preset-export-types.ts +256 -0
  43. package/common/storage/entity/shortcut-settings.ts +1 -0
  44. package/common/storage/shortcut-storage.ts +11 -0
  45. package/common/utils/clone-diagnostics.ts +105 -0
  46. package/common/utils/file.ts +16 -8
  47. package/common/utils/index.ts +6 -2
  48. package/common/utils/payload-preview.ts +173 -0
  49. package/common/utils/polling.ts +167 -13
  50. package/common/utils/safe-path.ts +103 -0
  51. package/common/utils/safe-shell.ts +115 -0
  52. package/common/utils/task-queue.ts +4 -1
  53. package/core-box/builder/tuff-builder.ts +0 -1
  54. package/core-box/index.ts +1 -1
  55. package/core-box/recommendation.ts +38 -1
  56. package/core-box/tuff/tuff-dsl.ts +97 -0
  57. package/electron/download-manager.ts +10 -7
  58. package/electron/env-tool.ts +42 -40
  59. package/electron/index.ts +0 -1
  60. package/env/index.ts +156 -0
  61. package/eslint.config.js +55 -0
  62. package/i18n/index.ts +62 -0
  63. package/i18n/locales/en.json +226 -0
  64. package/i18n/locales/zh.json +226 -0
  65. package/i18n/message-keys.ts +236 -0
  66. package/i18n/resolver.ts +181 -0
  67. package/icons/index.ts +257 -0
  68. package/icons/svg.ts +69 -0
  69. package/index.ts +9 -1
  70. package/intelligence/client.ts +72 -42
  71. package/market/constants.ts +21 -3
  72. package/market/index.ts +1 -1
  73. package/market/types.ts +20 -5
  74. package/package.json +15 -5
  75. package/permission/index.ts +143 -46
  76. package/permission/legacy.ts +26 -0
  77. package/permission/registry.ts +304 -0
  78. package/permission/types.ts +164 -0
  79. package/plugin/channel.ts +68 -39
  80. package/plugin/index.ts +82 -8
  81. package/plugin/install.ts +3 -0
  82. package/plugin/log/types.ts +22 -5
  83. package/plugin/node/logger-manager.ts +11 -3
  84. package/plugin/node/logger.ts +24 -17
  85. package/plugin/preload.ts +25 -2
  86. package/plugin/providers/index.ts +4 -0
  87. package/plugin/providers/market-client.ts +218 -0
  88. package/plugin/providers/npm-provider.ts +228 -0
  89. package/plugin/providers/tpex-provider.ts +297 -0
  90. package/plugin/providers/tpex-types.ts +34 -0
  91. package/plugin/sdk/box-items.ts +14 -0
  92. package/plugin/sdk/box-sdk.ts +64 -0
  93. package/plugin/sdk/channel.ts +119 -4
  94. package/plugin/sdk/clipboard.ts +26 -12
  95. package/plugin/sdk/cloud-sync.ts +113 -0
  96. package/plugin/sdk/common.ts +19 -11
  97. package/plugin/sdk/core-box.ts +6 -15
  98. package/plugin/sdk/division-box.ts +160 -65
  99. package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
  100. package/plugin/sdk/feature-sdk.ts +111 -76
  101. package/plugin/sdk/flow.ts +146 -45
  102. package/plugin/sdk/hooks/bridge.ts +113 -49
  103. package/plugin/sdk/hooks/life-cycle.ts +35 -16
  104. package/plugin/sdk/index.ts +14 -3
  105. package/plugin/sdk/intelligence.ts +87 -0
  106. package/plugin/sdk/meta/README.md +179 -0
  107. package/plugin/sdk/meta-sdk.ts +244 -0
  108. package/plugin/sdk/notification.ts +9 -0
  109. package/plugin/sdk/performance.ts +1 -16
  110. package/plugin/sdk/plugin-info.ts +64 -0
  111. package/plugin/sdk/power.ts +155 -0
  112. package/plugin/sdk/recommend.ts +21 -0
  113. package/plugin/sdk/service/index.ts +12 -8
  114. package/plugin/sdk/sqlite.ts +141 -0
  115. package/plugin/sdk/storage.ts +2 -6
  116. package/plugin/sdk/system.ts +2 -9
  117. package/plugin/sdk/temp-files.ts +41 -0
  118. package/plugin/sdk/touch-sdk.ts +18 -0
  119. package/plugin/sdk/types.ts +44 -4
  120. package/plugin/sdk/window/index.ts +12 -9
  121. package/plugin/sdk-version.ts +231 -0
  122. package/preload/renderer.ts +3 -2
  123. package/renderer/hooks/arg-mapper.ts +34 -6
  124. package/renderer/hooks/index.ts +13 -0
  125. package/renderer/hooks/initialize.ts +2 -1
  126. package/renderer/hooks/use-agent-market-sdk.ts +7 -0
  127. package/renderer/hooks/use-agent-market.ts +106 -0
  128. package/renderer/hooks/use-agents-sdk.ts +7 -0
  129. package/renderer/hooks/use-app-sdk.ts +7 -0
  130. package/renderer/hooks/use-channel.ts +33 -4
  131. package/renderer/hooks/use-download-sdk.ts +21 -0
  132. package/renderer/hooks/use-intelligence-sdk.ts +7 -0
  133. package/renderer/hooks/use-intelligence-stats.ts +290 -0
  134. package/renderer/hooks/use-intelligence.ts +202 -104
  135. package/renderer/hooks/use-market-sdk.ts +16 -0
  136. package/renderer/hooks/use-notification-sdk.ts +7 -0
  137. package/renderer/hooks/use-permission-sdk.ts +7 -0
  138. package/renderer/hooks/use-permission.ts +325 -0
  139. package/renderer/hooks/use-platform-sdk.ts +7 -0
  140. package/renderer/hooks/use-plugin-sdk.ts +16 -0
  141. package/renderer/hooks/use-settings-sdk.ts +7 -0
  142. package/renderer/hooks/use-update-sdk.ts +21 -0
  143. package/renderer/index.ts +1 -0
  144. package/renderer/ref.ts +19 -10
  145. package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
  146. package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
  147. package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
  148. package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
  149. package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
  150. package/renderer/shared/components/index.ts +5 -0
  151. package/renderer/shared/components/shims-vue.d.ts +5 -0
  152. package/renderer/shared/index.ts +2 -0
  153. package/renderer/shared/plugin-detail.ts +62 -0
  154. package/renderer/storage/app-settings.ts +3 -1
  155. package/renderer/storage/base-storage.ts +508 -82
  156. package/renderer/storage/intelligence-storage.ts +37 -46
  157. package/renderer/storage/openers.ts +3 -1
  158. package/renderer/storage/storage-subscription.ts +126 -42
  159. package/renderer/touch-sdk/env.ts +10 -10
  160. package/renderer/touch-sdk/index.ts +114 -18
  161. package/renderer/touch-sdk/terminal.ts +24 -13
  162. package/search/feature-matcher.ts +279 -0
  163. package/search/fuzzy-match.ts +64 -34
  164. package/search/index.ts +10 -0
  165. package/search/levenshtein-utils.ts +17 -11
  166. package/transport/errors.ts +310 -0
  167. package/transport/event/builder.ts +378 -0
  168. package/transport/event/index.ts +7 -0
  169. package/transport/event/types.ts +292 -0
  170. package/transport/events/index.ts +2670 -0
  171. package/transport/events/meta-overlay.ts +79 -0
  172. package/transport/events/types/agents.ts +177 -0
  173. package/transport/events/types/app-index.ts +9 -0
  174. package/transport/events/types/app.ts +475 -0
  175. package/transport/events/types/box-item.ts +222 -0
  176. package/transport/events/types/clipboard.ts +80 -0
  177. package/transport/events/types/core-box.ts +534 -0
  178. package/transport/events/types/device-idle.ts +7 -0
  179. package/transport/events/types/division-box.ts +99 -0
  180. package/transport/events/types/download.ts +115 -0
  181. package/transport/events/types/file-index.ts +73 -0
  182. package/transport/events/types/flow.ts +149 -0
  183. package/transport/events/types/index.ts +70 -0
  184. package/transport/events/types/market.ts +39 -0
  185. package/transport/events/types/meta-overlay.ts +184 -0
  186. package/transport/events/types/notification.ts +140 -0
  187. package/transport/events/types/permission.ts +90 -0
  188. package/transport/events/types/platform.ts +8 -0
  189. package/transport/events/types/plugin.ts +620 -0
  190. package/transport/events/types/sentry.ts +20 -0
  191. package/transport/events/types/storage.ts +208 -0
  192. package/transport/events/types/transport.ts +60 -0
  193. package/transport/events/types/tray.ts +16 -0
  194. package/transport/events/types/update.ts +78 -0
  195. package/transport/index.ts +139 -0
  196. package/transport/main.ts +2 -0
  197. package/transport/sdk/constants.ts +29 -0
  198. package/transport/sdk/domains/agents-market.ts +47 -0
  199. package/transport/sdk/domains/agents.ts +62 -0
  200. package/transport/sdk/domains/app.ts +48 -0
  201. package/transport/sdk/domains/disposable.ts +35 -0
  202. package/transport/sdk/domains/download.ts +139 -0
  203. package/transport/sdk/domains/index.ts +13 -0
  204. package/transport/sdk/domains/intelligence.ts +616 -0
  205. package/transport/sdk/domains/market.ts +35 -0
  206. package/transport/sdk/domains/notification.ts +62 -0
  207. package/transport/sdk/domains/permission.ts +85 -0
  208. package/transport/sdk/domains/platform.ts +19 -0
  209. package/transport/sdk/domains/plugin.ts +144 -0
  210. package/transport/sdk/domains/settings.ts +92 -0
  211. package/transport/sdk/domains/update.ts +64 -0
  212. package/transport/sdk/index.ts +60 -0
  213. package/transport/sdk/main-transport.ts +710 -0
  214. package/transport/sdk/main.ts +9 -0
  215. package/transport/sdk/plugin-transport.ts +654 -0
  216. package/transport/sdk/port-policy.ts +38 -0
  217. package/transport/sdk/renderer-transport.ts +1165 -0
  218. package/transport/types.ts +605 -0
  219. package/types/agent.ts +399 -0
  220. package/types/cloud-sync.ts +157 -0
  221. package/types/division-box.ts +47 -27
  222. package/types/download.ts +1 -0
  223. package/types/flow.ts +63 -12
  224. package/types/icon.ts +2 -1
  225. package/types/index.ts +5 -0
  226. package/types/intelligence.ts +1492 -81
  227. package/types/modules/base.ts +2 -0
  228. package/types/path-browserify.d.ts +5 -0
  229. package/types/platform.ts +12 -0
  230. package/types/startup-info.ts +32 -0
  231. package/types/touch-app-core.ts +8 -8
  232. package/types/update.ts +94 -1
  233. package/vitest.config.ts +25 -0
  234. package/auth/useClerkConfig.ts +0 -40
  235. 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 (typeof window === 'undefined') {
25
- return require('node:path')
26
+ if (hasWindow()) {
27
+ return pathBrowserify
26
28
  }
27
- try {
28
- return require('path-browserify')
29
- }
30
- catch {
31
- return require('node:path')
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[0]
110
- const lastChar = fileName[fileName.length - 1]
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'