@talex-touch/utils 1.0.42 → 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 (233) 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 +32 -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 +9 -5
  72. package/market/index.ts +1 -1
  73. package/market/types.ts +19 -4
  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 +80 -7
  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 -4
  87. package/plugin/providers/market-client.ts +6 -3
  88. package/plugin/providers/npm-provider.ts +22 -7
  89. package/plugin/providers/tpex-provider.ts +22 -8
  90. package/plugin/sdk/box-items.ts +14 -0
  91. package/plugin/sdk/box-sdk.ts +64 -0
  92. package/plugin/sdk/channel.ts +119 -4
  93. package/plugin/sdk/clipboard.ts +26 -12
  94. package/plugin/sdk/cloud-sync.ts +113 -0
  95. package/plugin/sdk/common.ts +19 -11
  96. package/plugin/sdk/core-box.ts +6 -15
  97. package/plugin/sdk/division-box.ts +160 -65
  98. package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
  99. package/plugin/sdk/feature-sdk.ts +111 -76
  100. package/plugin/sdk/flow.ts +146 -45
  101. package/plugin/sdk/hooks/bridge.ts +13 -6
  102. package/plugin/sdk/hooks/life-cycle.ts +35 -16
  103. package/plugin/sdk/index.ts +14 -3
  104. package/plugin/sdk/intelligence.ts +87 -0
  105. package/plugin/sdk/meta/README.md +179 -0
  106. package/plugin/sdk/meta-sdk.ts +244 -0
  107. package/plugin/sdk/notification.ts +9 -0
  108. package/plugin/sdk/plugin-info.ts +64 -0
  109. package/plugin/sdk/power.ts +155 -0
  110. package/plugin/sdk/recommend.ts +21 -0
  111. package/plugin/sdk/service/index.ts +12 -8
  112. package/plugin/sdk/sqlite.ts +141 -0
  113. package/plugin/sdk/storage.ts +2 -6
  114. package/plugin/sdk/system.ts +2 -9
  115. package/plugin/sdk/temp-files.ts +41 -0
  116. package/plugin/sdk/touch-sdk.ts +18 -0
  117. package/plugin/sdk/types.ts +44 -4
  118. package/plugin/sdk/window/index.ts +12 -9
  119. package/plugin/sdk-version.ts +231 -0
  120. package/preload/renderer.ts +3 -2
  121. package/renderer/hooks/arg-mapper.ts +16 -2
  122. package/renderer/hooks/index.ts +13 -0
  123. package/renderer/hooks/initialize.ts +2 -1
  124. package/renderer/hooks/use-agent-market-sdk.ts +7 -0
  125. package/renderer/hooks/use-agent-market.ts +106 -0
  126. package/renderer/hooks/use-agents-sdk.ts +7 -0
  127. package/renderer/hooks/use-app-sdk.ts +7 -0
  128. package/renderer/hooks/use-channel.ts +33 -4
  129. package/renderer/hooks/use-download-sdk.ts +21 -0
  130. package/renderer/hooks/use-intelligence-sdk.ts +7 -0
  131. package/renderer/hooks/use-intelligence-stats.ts +290 -0
  132. package/renderer/hooks/use-intelligence.ts +55 -214
  133. package/renderer/hooks/use-market-sdk.ts +16 -0
  134. package/renderer/hooks/use-notification-sdk.ts +7 -0
  135. package/renderer/hooks/use-permission-sdk.ts +7 -0
  136. package/renderer/hooks/use-permission.ts +325 -0
  137. package/renderer/hooks/use-platform-sdk.ts +7 -0
  138. package/renderer/hooks/use-plugin-sdk.ts +16 -0
  139. package/renderer/hooks/use-settings-sdk.ts +7 -0
  140. package/renderer/hooks/use-update-sdk.ts +21 -0
  141. package/renderer/index.ts +1 -0
  142. package/renderer/ref.ts +19 -10
  143. package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
  144. package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
  145. package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
  146. package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
  147. package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
  148. package/renderer/shared/components/index.ts +5 -0
  149. package/renderer/shared/components/shims-vue.d.ts +5 -0
  150. package/renderer/shared/index.ts +2 -0
  151. package/renderer/shared/plugin-detail.ts +62 -0
  152. package/renderer/storage/app-settings.ts +3 -1
  153. package/renderer/storage/base-storage.ts +508 -82
  154. package/renderer/storage/intelligence-storage.ts +31 -40
  155. package/renderer/storage/openers.ts +3 -1
  156. package/renderer/storage/storage-subscription.ts +126 -42
  157. package/renderer/touch-sdk/env.ts +10 -10
  158. package/renderer/touch-sdk/index.ts +114 -18
  159. package/renderer/touch-sdk/terminal.ts +24 -13
  160. package/search/feature-matcher.ts +279 -0
  161. package/search/fuzzy-match.ts +64 -34
  162. package/search/index.ts +10 -0
  163. package/search/levenshtein-utils.ts +17 -11
  164. package/transport/errors.ts +310 -0
  165. package/transport/event/builder.ts +378 -0
  166. package/transport/event/index.ts +7 -0
  167. package/transport/event/types.ts +292 -0
  168. package/transport/events/index.ts +2670 -0
  169. package/transport/events/meta-overlay.ts +79 -0
  170. package/transport/events/types/agents.ts +177 -0
  171. package/transport/events/types/app-index.ts +9 -0
  172. package/transport/events/types/app.ts +475 -0
  173. package/transport/events/types/box-item.ts +222 -0
  174. package/transport/events/types/clipboard.ts +80 -0
  175. package/transport/events/types/core-box.ts +534 -0
  176. package/transport/events/types/device-idle.ts +7 -0
  177. package/transport/events/types/division-box.ts +99 -0
  178. package/transport/events/types/download.ts +115 -0
  179. package/transport/events/types/file-index.ts +73 -0
  180. package/transport/events/types/flow.ts +149 -0
  181. package/transport/events/types/index.ts +70 -0
  182. package/transport/events/types/market.ts +39 -0
  183. package/transport/events/types/meta-overlay.ts +184 -0
  184. package/transport/events/types/notification.ts +140 -0
  185. package/transport/events/types/permission.ts +90 -0
  186. package/transport/events/types/platform.ts +8 -0
  187. package/transport/events/types/plugin.ts +620 -0
  188. package/transport/events/types/sentry.ts +20 -0
  189. package/transport/events/types/storage.ts +208 -0
  190. package/transport/events/types/transport.ts +60 -0
  191. package/transport/events/types/tray.ts +16 -0
  192. package/transport/events/types/update.ts +78 -0
  193. package/transport/index.ts +139 -0
  194. package/transport/main.ts +2 -0
  195. package/transport/sdk/constants.ts +29 -0
  196. package/transport/sdk/domains/agents-market.ts +47 -0
  197. package/transport/sdk/domains/agents.ts +62 -0
  198. package/transport/sdk/domains/app.ts +48 -0
  199. package/transport/sdk/domains/disposable.ts +35 -0
  200. package/transport/sdk/domains/download.ts +139 -0
  201. package/transport/sdk/domains/index.ts +13 -0
  202. package/transport/sdk/domains/intelligence.ts +616 -0
  203. package/transport/sdk/domains/market.ts +35 -0
  204. package/transport/sdk/domains/notification.ts +62 -0
  205. package/transport/sdk/domains/permission.ts +85 -0
  206. package/transport/sdk/domains/platform.ts +19 -0
  207. package/transport/sdk/domains/plugin.ts +144 -0
  208. package/transport/sdk/domains/settings.ts +92 -0
  209. package/transport/sdk/domains/update.ts +64 -0
  210. package/transport/sdk/index.ts +60 -0
  211. package/transport/sdk/main-transport.ts +710 -0
  212. package/transport/sdk/main.ts +9 -0
  213. package/transport/sdk/plugin-transport.ts +654 -0
  214. package/transport/sdk/port-policy.ts +38 -0
  215. package/transport/sdk/renderer-transport.ts +1165 -0
  216. package/transport/types.ts +605 -0
  217. package/types/agent.ts +399 -0
  218. package/types/cloud-sync.ts +157 -0
  219. package/types/division-box.ts +31 -31
  220. package/types/download.ts +1 -0
  221. package/types/flow.ts +63 -12
  222. package/types/icon.ts +2 -1
  223. package/types/index.ts +5 -0
  224. package/types/intelligence.ts +166 -173
  225. package/types/modules/base.ts +2 -0
  226. package/types/path-browserify.d.ts +5 -0
  227. package/types/platform.ts +12 -0
  228. package/types/startup-info.ts +32 -0
  229. package/types/touch-app-core.ts +8 -8
  230. package/types/update.ts +94 -1
  231. package/vitest.config.ts +25 -0
  232. package/auth/useClerkConfig.ts +0 -40
  233. package/auth/useClerkProvider.ts +0 -52
@@ -0,0 +1,256 @@
1
+ import type {
2
+ CoreBoxCanvasConfig,
3
+ CoreBoxThemeConfig,
4
+ LayoutAtomConfig,
5
+ LayoutCanvasConfig,
6
+ ThemePresetConfig,
7
+ } from './layout-atom-types'
8
+
9
+ /**
10
+ * Version of the preset export format
11
+ * Increment when breaking changes occur
12
+ */
13
+ export const PRESET_EXPORT_VERSION = 2
14
+
15
+ /**
16
+ * Preset export data structure
17
+ * Supports both local file export and future cloud publishing
18
+ */
19
+ export interface PresetExportData {
20
+ /** Export format version */
21
+ version: number
22
+ /** Export timestamp (ISO 8601) */
23
+ exportedAt: string
24
+ /** Optional preset metadata */
25
+ meta: PresetMeta
26
+ /** Layout atom configuration */
27
+ layout?: LayoutAtomConfig
28
+ /** CoreBox theme configuration */
29
+ coreBox?: CoreBoxThemeConfig
30
+ /** Theme-level configuration */
31
+ theme?: ThemePresetConfig
32
+ /** Main layout canvas configuration */
33
+ mainCanvas?: LayoutCanvasConfig
34
+ /** CoreBox canvas configuration */
35
+ coreBoxCanvas?: CoreBoxCanvasConfig
36
+ }
37
+
38
+ export interface PresetCompat {
39
+ minAppVersion?: string
40
+ maxAppVersion?: string
41
+ }
42
+
43
+ /**
44
+ * Preset metadata for identification and discovery
45
+ */
46
+ export interface PresetMeta {
47
+ /** Unique preset ID (uuid for local, assigned for cloud) */
48
+ id?: string
49
+ /** Human-readable name */
50
+ name: string
51
+ /** Optional description */
52
+ description?: string
53
+ /** Author information */
54
+ author?: PresetAuthor
55
+ /** Tags for categorization */
56
+ tags?: string[]
57
+ /** Preview image URL or data URI */
58
+ preview?: string
59
+ /** Creation timestamp */
60
+ createdAt?: string
61
+ /** Last update timestamp */
62
+ updatedAt?: string
63
+ /** Release channel */
64
+ channel?: 'stable' | 'beta'
65
+ /** Compatibility window */
66
+ compat?: PresetCompat
67
+ /** Preset source */
68
+ source?: 'local' | 'nexus'
69
+ }
70
+
71
+ /**
72
+ * Author information for cloud-published presets
73
+ */
74
+ export interface PresetAuthor {
75
+ /** Author display name */
76
+ name: string
77
+ /** Author ID (for cloud) */
78
+ id?: string
79
+ /** Author avatar URL */
80
+ avatar?: string
81
+ }
82
+
83
+ /**
84
+ * Cloud preset listing item (for marketplace)
85
+ */
86
+ export interface CloudPresetItem {
87
+ /** Unique cloud ID */
88
+ id: string
89
+ /** Preset metadata */
90
+ meta: PresetMeta
91
+ /** Download count */
92
+ downloads: number
93
+ /** Like count */
94
+ likes: number
95
+ /** Whether current user liked */
96
+ liked?: boolean
97
+ /** Publish status */
98
+ status: 'published' | 'pending' | 'rejected'
99
+ /** Publish timestamp */
100
+ publishedAt: string
101
+ }
102
+
103
+ /**
104
+ * Cloud preset API response
105
+ */
106
+ export interface CloudPresetListResponse {
107
+ items: CloudPresetItem[]
108
+ total: number
109
+ page: number
110
+ pageSize: number
111
+ }
112
+
113
+ /**
114
+ * Cloud preset publish request
115
+ */
116
+ export interface CloudPresetPublishRequest {
117
+ preset: PresetExportData
118
+ visibility: 'public' | 'unlisted' | 'private'
119
+ }
120
+
121
+ /**
122
+ * Validation result for preset import
123
+ */
124
+ export interface PresetValidationResult {
125
+ valid: boolean
126
+ errors: string[]
127
+ warnings: string[]
128
+ }
129
+
130
+ /**
131
+ * Validates a preset export data structure
132
+ */
133
+ export function validatePresetData(data: unknown): PresetValidationResult {
134
+ const errors: string[] = []
135
+ const warnings: string[] = []
136
+
137
+ if (!data || typeof data !== 'object') {
138
+ return { valid: false, errors: ['Invalid data format'], warnings: [] }
139
+ }
140
+
141
+ const preset = data as Record<string, unknown>
142
+
143
+ // Check version
144
+ if (typeof preset.version !== 'number') {
145
+ errors.push('Missing or invalid version field')
146
+ } else if (preset.version > PRESET_EXPORT_VERSION) {
147
+ warnings.push(`Preset version (${preset.version}) is newer than supported (${PRESET_EXPORT_VERSION})`)
148
+ }
149
+
150
+ // Check meta
151
+ if (!preset.meta || typeof preset.meta !== 'object') {
152
+ errors.push('Missing or invalid meta field')
153
+ } else {
154
+ const meta = preset.meta as Record<string, unknown>
155
+ if (typeof meta.name !== 'string' || !meta.name.trim()) {
156
+ errors.push('Missing or invalid preset name')
157
+ }
158
+ }
159
+
160
+ // Check that at least one config is present
161
+ if (!preset.layout && !preset.coreBox && !preset.theme && !preset.mainCanvas && !preset.coreBoxCanvas) {
162
+ warnings.push('Preset contains no configurable fields')
163
+ }
164
+
165
+ // Validate layout structure if present
166
+ if (preset.layout) {
167
+ const layout = preset.layout as Record<string, unknown>
168
+ if (!layout.preset || !layout.header || !layout.aside || !layout.view || !layout.nav) {
169
+ errors.push('Invalid layout configuration structure')
170
+ }
171
+ }
172
+
173
+ // Validate coreBox structure if present
174
+ if (preset.coreBox) {
175
+ const coreBox = preset.coreBox as Record<string, unknown>
176
+ if (!coreBox.preset || !coreBox.logo || !coreBox.input || !coreBox.results) {
177
+ errors.push('Invalid CoreBox configuration structure')
178
+ }
179
+ }
180
+
181
+ if (preset.theme) {
182
+ const theme = preset.theme as Record<string, unknown>
183
+ if (!theme.style && !theme.addon && !theme.transition && !theme.palette && !theme.window) {
184
+ warnings.push('Theme preset is empty')
185
+ }
186
+ }
187
+
188
+ if (preset.mainCanvas) {
189
+ const mainCanvas = preset.mainCanvas as Record<string, unknown>
190
+ if (
191
+ typeof mainCanvas.columns !== 'number'
192
+ || typeof mainCanvas.rowHeight !== 'number'
193
+ || !Array.isArray(mainCanvas.items)
194
+ ) {
195
+ errors.push('Invalid mainCanvas configuration structure')
196
+ }
197
+ }
198
+
199
+ if (preset.coreBoxCanvas) {
200
+ const coreBoxCanvas = preset.coreBoxCanvas as Record<string, unknown>
201
+ if (
202
+ typeof coreBoxCanvas.columns !== 'number'
203
+ || typeof coreBoxCanvas.rowHeight !== 'number'
204
+ || !Array.isArray(coreBoxCanvas.items)
205
+ ) {
206
+ errors.push('Invalid coreBoxCanvas configuration structure')
207
+ }
208
+ }
209
+
210
+ return {
211
+ valid: errors.length === 0,
212
+ errors,
213
+ warnings
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Creates a new preset export data structure
219
+ */
220
+ export function createPresetExport(options: {
221
+ name: string
222
+ description?: string
223
+ layout?: LayoutAtomConfig
224
+ coreBox?: CoreBoxThemeConfig
225
+ theme?: ThemePresetConfig
226
+ mainCanvas?: LayoutCanvasConfig
227
+ coreBoxCanvas?: CoreBoxCanvasConfig
228
+ author?: PresetAuthor
229
+ tags?: string[]
230
+ channel?: 'stable' | 'beta'
231
+ source?: 'local' | 'nexus'
232
+ compat?: PresetCompat
233
+ }): PresetExportData {
234
+ const now = new Date().toISOString()
235
+ return {
236
+ version: PRESET_EXPORT_VERSION,
237
+ exportedAt: now,
238
+ meta: {
239
+ id: crypto.randomUUID(),
240
+ name: options.name,
241
+ description: options.description,
242
+ author: options.author,
243
+ tags: options.tags,
244
+ createdAt: now,
245
+ updatedAt: now,
246
+ channel: options.channel ?? 'beta',
247
+ source: options.source ?? 'local',
248
+ compat: options.compat,
249
+ },
250
+ layout: options.layout,
251
+ coreBox: options.coreBox,
252
+ theme: options.theme,
253
+ mainCanvas: options.mainCanvas,
254
+ coreBoxCanvas: options.coreBoxCanvas,
255
+ }
256
+ }
@@ -8,6 +8,7 @@ export interface ShortcutMeta {
8
8
  modificationTime: number
9
9
  author: string
10
10
  description?: string
11
+ enabled?: boolean
11
12
  }
12
13
 
13
14
  export interface Shortcut {
@@ -55,6 +55,17 @@ class ShortcutStorage {
55
55
  this._save()
56
56
  return true
57
57
  }
58
+
59
+ updateShortcutEnabled(id: string, enabled: boolean): boolean {
60
+ const shortcut = this.getShortcutById(id)
61
+ if (!shortcut) {
62
+ return false
63
+ }
64
+ shortcut.meta.enabled = enabled
65
+ shortcut.meta.modificationTime = Date.now()
66
+ this._save()
67
+ return true
68
+ }
58
69
  }
59
70
 
60
71
  export default ShortcutStorage
@@ -0,0 +1,105 @@
1
+ export interface CloneIssue {
2
+ path: string
3
+ reason: string
4
+ type: string
5
+ }
6
+
7
+ const DEFAULT_MAX_DEPTH = 4
8
+ const MAX_INSPECT_ITEMS = 50
9
+
10
+ function getValueType(value: unknown): string {
11
+ return Object.prototype.toString.call(value)
12
+ }
13
+
14
+ export function isCloneError(error: unknown): boolean {
15
+ const message = error instanceof Error ? error.message : String(error)
16
+ return message.includes('could not be cloned')
17
+ || message.includes('DataCloneError')
18
+ || message.includes('Cannot clone')
19
+ }
20
+
21
+ export function summarizeClonePayload(payload: unknown): Record<string, unknown> {
22
+ const summary: Record<string, unknown> = { valueType: typeof payload }
23
+ if (payload === null) {
24
+ summary.value = null
25
+ return summary
26
+ }
27
+ if (typeof payload !== 'object') {
28
+ summary.value = payload
29
+ return summary
30
+ }
31
+ summary.objectType = getValueType(payload)
32
+ if (Array.isArray(payload)) {
33
+ summary.length = payload.length
34
+ return summary
35
+ }
36
+ try {
37
+ const keys = Object.keys(payload as Record<string, unknown>)
38
+ summary.keys = keys.slice(0, 20)
39
+ summary.keysCount = keys.length
40
+ } catch (error) {
41
+ summary.keysError = error instanceof Error ? error.message : String(error)
42
+ }
43
+ return summary
44
+ }
45
+
46
+ export function findCloneIssue(
47
+ payload: unknown,
48
+ options?: { maxDepth?: number }
49
+ ): CloneIssue | null {
50
+ if (typeof structuredClone !== 'function') return null
51
+
52
+ const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH
53
+ const seen = new WeakSet<object>()
54
+
55
+ const inspect = (value: unknown, path: string, depth: number): CloneIssue | null => {
56
+ try {
57
+ structuredClone(value)
58
+ return null
59
+ } catch (error) {
60
+ const reason = error instanceof Error ? error.message : String(error)
61
+ if (depth >= maxDepth) {
62
+ return { path, reason: 'max_depth', type: getValueType(value) }
63
+ }
64
+ if (value && typeof value === 'object') {
65
+ if (seen.has(value)) {
66
+ return { path, reason: 'circular_ref', type: getValueType(value) }
67
+ }
68
+ seen.add(value)
69
+ if (Array.isArray(value)) {
70
+ const limit = Math.min(value.length, MAX_INSPECT_ITEMS)
71
+ for (let i = 0; i < limit; i += 1) {
72
+ const result = inspect(value[i], `${path}[${i}]`, depth + 1)
73
+ if (result) return result
74
+ }
75
+ if (value.length > limit) {
76
+ return { path, reason: 'array_truncated', type: getValueType(value) }
77
+ }
78
+ } else {
79
+ let entries: [string, unknown][]
80
+ try {
81
+ entries = Object.entries(value as Record<string, unknown>)
82
+ } catch (entryError) {
83
+ const entryReason = entryError instanceof Error ? entryError.message : String(entryError)
84
+ return { path, reason: entryReason, type: getValueType(value) }
85
+ }
86
+ const limit = Math.min(entries.length, MAX_INSPECT_ITEMS)
87
+ for (let i = 0; i < limit; i += 1) {
88
+ const entry = entries[i]
89
+ if (!entry)
90
+ continue
91
+ const [key, entryValue] = entry
92
+ const result = inspect(entryValue, `${path}.${key}`, depth + 1)
93
+ if (result) return result
94
+ }
95
+ if (entries.length > limit) {
96
+ return { path, reason: 'keys_truncated', type: getValueType(value) }
97
+ }
98
+ }
99
+ }
100
+ return { path, reason, type: getValueType(value) }
101
+ }
102
+ }
103
+
104
+ return inspect(payload, 'payload', 0)
105
+ }
@@ -1,14 +1,22 @@
1
- // eslint-disable-next-line @typescript-eslint/no-var-requires
1
+ import pathBrowserify from 'path-browserify'
2
+ import { hasWindow } from '../../env'
3
+
2
4
  const path = (() => {
3
- if (typeof window === 'undefined') {
4
- return require('node:path')
5
- }
6
- try {
7
- return require('path-browserify')
5
+ if (hasWindow()) {
6
+ return pathBrowserify
8
7
  }
9
- catch {
10
- return require('node:path')
8
+
9
+ const nodeRequire = typeof require === 'function' ? require : null
10
+ if (nodeRequire) {
11
+ try {
12
+ return nodeRequire('node:path')
13
+ }
14
+ catch {
15
+ return pathBrowserify
16
+ }
11
17
  }
18
+
19
+ return pathBrowserify
12
20
  })()
13
21
 
14
22
  /**
@@ -1,3 +1,5 @@
1
+ import { hasDocument } from '../../env'
2
+
1
3
  /**
2
4
  * Delays execution for a given amount of milliseconds.
3
5
  *
@@ -51,7 +53,7 @@ export function anyStr2Num(str: string): bigint {
51
53
  * ```
52
54
  */
53
55
  export function num2anyStr(num: bigint): string {
54
- const [baseStr, encoded] = num.toString().split('000')
56
+ const [baseStr, encoded = ''] = num.toString().split('000')
55
57
  const base = Number(baseStr)
56
58
 
57
59
  let result = ''
@@ -100,7 +102,7 @@ export function structuredStrictStringify(value: unknown): string {
100
102
  return 'null'
101
103
  if (Array.isArray(val))
102
104
  return 'Array'
103
- if (typeof Document !== 'undefined') {
105
+ if (hasDocument()) {
104
106
  if (val instanceof Node)
105
107
  return 'DOMNode'
106
108
  }
@@ -177,6 +179,8 @@ export function structuredStrictStringify(value: unknown): string {
177
179
  }
178
180
 
179
181
  export * from './file'
182
+ export * from './safe-path'
183
+ export * from './safe-shell'
180
184
 
181
185
  export { type AdaptiveTaskQueueOptions, runAdaptiveTaskQueue } from './task-queue'
182
186
 
@@ -0,0 +1,173 @@
1
+ export interface PayloadPreviewOptions {
2
+ maxOutputChars?: number
3
+ maxStringChars?: number
4
+ maxDepth?: number
5
+ maxKeys?: number
6
+ maxArrayLength?: number
7
+ }
8
+
9
+ const DEFAULT_OPTIONS: Required<PayloadPreviewOptions> = {
10
+ maxOutputChars: 800,
11
+ maxStringChars: 200,
12
+ maxDepth: 4,
13
+ maxKeys: 30,
14
+ maxArrayLength: 20,
15
+ }
16
+
17
+ function truncate(value: string, maxChars: number): string {
18
+ if (maxChars <= 0)
19
+ return ''
20
+ return value.length > maxChars ? `${value.slice(0, maxChars)}…` : value
21
+ }
22
+
23
+ function redactDataUrl(value: string, maxPrefixChars: number): string {
24
+ if (!value.startsWith('data:'))
25
+ return value
26
+ const base64Index = value.indexOf(';base64,')
27
+ if (base64Index === -1)
28
+ return value
29
+
30
+ const prefixEnd = base64Index + ';base64,'.length
31
+ const prefix = value.slice(0, prefixEnd)
32
+ const omittedChars = Math.max(0, value.length - prefixEnd)
33
+ const safePrefix = truncate(prefix, Math.max(10, maxPrefixChars))
34
+ return `${safePrefix}[base64 omitted ${omittedChars} chars]`
35
+ }
36
+
37
+ function isTypedArray(value: unknown): value is ArrayBufferView {
38
+ return (
39
+ typeof ArrayBuffer !== 'undefined'
40
+ && typeof ArrayBuffer.isView === 'function'
41
+ && ArrayBuffer.isView(value)
42
+ )
43
+ }
44
+
45
+ function bufferLength(value: unknown): number | null {
46
+ try {
47
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
48
+ return value.length
49
+ }
50
+ }
51
+ catch {
52
+ // ignore
53
+ }
54
+ return null
55
+ }
56
+
57
+ export function formatPayloadPreview(payload: unknown, options?: PayloadPreviewOptions): string {
58
+ const opts = { ...DEFAULT_OPTIONS, ...(options ?? {}) }
59
+
60
+ if (payload === null || payload === undefined)
61
+ return String(payload)
62
+ if (typeof payload === 'string') {
63
+ const redacted = redactDataUrl(payload, opts.maxStringChars)
64
+ return truncate(redacted, opts.maxOutputChars)
65
+ }
66
+
67
+ const seen = typeof WeakSet !== 'undefined' ? new WeakSet<object>() : null
68
+
69
+ const simplify = (value: unknown, depth: number): unknown => {
70
+ if (value === null || value === undefined)
71
+ return value
72
+ if (depth >= opts.maxDepth)
73
+ return '[max-depth]'
74
+
75
+ const valueType = typeof value
76
+ if (valueType === 'string') {
77
+ const redacted = redactDataUrl(value as string, opts.maxStringChars)
78
+ return truncate(redacted, opts.maxStringChars)
79
+ }
80
+ if (valueType === 'number' || valueType === 'boolean')
81
+ return value
82
+ if (valueType === 'bigint')
83
+ return `${value.toString()}n`
84
+ if (valueType === 'symbol')
85
+ return String(value)
86
+ if (valueType === 'function')
87
+ return '[function]'
88
+
89
+ if (value instanceof Error) {
90
+ return {
91
+ name: value.name,
92
+ message: truncate(value.message ?? '', opts.maxStringChars),
93
+ stack: typeof value.stack === 'string' ? truncate(value.stack, opts.maxOutputChars) : undefined,
94
+ }
95
+ }
96
+
97
+ const bufLen = bufferLength(value)
98
+ if (typeof bufLen === 'number')
99
+ return `[buffer ${bufLen} bytes]`
100
+ if (isTypedArray(value))
101
+ return `[typed-array ${value.byteLength} bytes]`
102
+ if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
103
+ return `[array-buffer ${value.byteLength} bytes]`
104
+ }
105
+
106
+ if (Array.isArray(value)) {
107
+ const items = value.slice(0, opts.maxArrayLength).map(item => simplify(item, depth + 1))
108
+ if (value.length > opts.maxArrayLength) {
109
+ items.push(`[+${value.length - opts.maxArrayLength} more]`)
110
+ }
111
+ return items
112
+ }
113
+
114
+ if (valueType === 'object') {
115
+ const obj = value as Record<string, unknown>
116
+ if (seen) {
117
+ if (seen.has(obj))
118
+ return '[circular]'
119
+ seen.add(obj)
120
+ }
121
+
122
+ if (obj instanceof Map) {
123
+ return {
124
+ _type: 'Map',
125
+ size: obj.size,
126
+ entries: Array.from(obj.entries())
127
+ .slice(0, opts.maxArrayLength)
128
+ .map(([k, v]) => [simplify(k, depth + 1), simplify(v, depth + 1)]),
129
+ }
130
+ }
131
+
132
+ if (obj instanceof Set) {
133
+ return {
134
+ _type: 'Set',
135
+ size: obj.size,
136
+ values: Array.from(obj.values())
137
+ .slice(0, opts.maxArrayLength)
138
+ .map(v => simplify(v, depth + 1)),
139
+ }
140
+ }
141
+
142
+ const keys = Object.keys(obj)
143
+ const out: Record<string, unknown> = {}
144
+ for (const key of keys.slice(0, opts.maxKeys)) {
145
+ try {
146
+ out[key] = simplify(obj[key], depth + 1)
147
+ }
148
+ catch {
149
+ out[key] = '[throws]'
150
+ }
151
+ }
152
+ if (keys.length > opts.maxKeys) {
153
+ out._moreKeys = keys.length - opts.maxKeys
154
+ }
155
+ return out
156
+ }
157
+
158
+ try {
159
+ return truncate(String(value), opts.maxStringChars)
160
+ }
161
+ catch {
162
+ return '[unserializable]'
163
+ }
164
+ }
165
+
166
+ try {
167
+ const text = JSON.stringify(simplify(payload, 0))
168
+ return truncate(text, opts.maxOutputChars)
169
+ }
170
+ catch {
171
+ return '[unserializable]'
172
+ }
173
+ }