@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.
- 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 +32 -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 +9 -5
- package/market/index.ts +1 -1
- package/market/types.ts +19 -4
- 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 +80 -7
- 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 -4
- package/plugin/providers/market-client.ts +6 -3
- package/plugin/providers/npm-provider.ts +22 -7
- package/plugin/providers/tpex-provider.ts +22 -8
- 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 +13 -6
- 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/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 +16 -2
- 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 +55 -214
- 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 +31 -40
- 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 +31 -31
- 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 +166 -173
- 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,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Error types for TuffTransport
|
|
3
|
+
* @module @talex-touch/utils/transport/errors
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Error Codes
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Error codes for TuffTransport operations.
|
|
12
|
+
*/
|
|
13
|
+
export enum TuffTransportErrorCode {
|
|
14
|
+
/**
|
|
15
|
+
* Event is not a valid TuffEvent instance.
|
|
16
|
+
*/
|
|
17
|
+
INVALID_EVENT = 'INVALID_EVENT',
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* No handler registered for the event.
|
|
21
|
+
*/
|
|
22
|
+
UNKNOWN_EVENT = 'UNKNOWN_EVENT',
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Request timed out waiting for response.
|
|
26
|
+
*/
|
|
27
|
+
TIMEOUT = 'TIMEOUT',
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Stream was cancelled by the client.
|
|
31
|
+
*/
|
|
32
|
+
STREAM_CANCELLED = 'STREAM_CANCELLED',
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Batch request failed.
|
|
36
|
+
*/
|
|
37
|
+
BATCH_FAILED = 'BATCH_FAILED',
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Failed to serialize request payload.
|
|
41
|
+
*/
|
|
42
|
+
SERIALIZE_FAILED = 'SERIALIZE_FAILED',
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Failed to deserialize response payload.
|
|
46
|
+
*/
|
|
47
|
+
DESERIALIZE_FAILED = 'DESERIALIZE_FAILED',
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Target window/WebContents not found or destroyed.
|
|
51
|
+
*/
|
|
52
|
+
TARGET_NOT_FOUND = 'TARGET_NOT_FOUND',
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Plugin security key is invalid or expired.
|
|
56
|
+
*/
|
|
57
|
+
INVALID_PLUGIN_KEY = 'INVALID_PLUGIN_KEY',
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Plugin attempted unauthorized operation.
|
|
61
|
+
*/
|
|
62
|
+
PLUGIN_UNAUTHORIZED = 'PLUGIN_UNAUTHORIZED',
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* IPC channel error (e.g., EPIPE).
|
|
66
|
+
*/
|
|
67
|
+
IPC_ERROR = 'IPC_ERROR',
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Transport instance has been destroyed.
|
|
71
|
+
*/
|
|
72
|
+
TRANSPORT_DESTROYED = 'TRANSPORT_DESTROYED',
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generic internal error.
|
|
76
|
+
*/
|
|
77
|
+
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// Error Class
|
|
82
|
+
// ============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Custom error class for TuffTransport operations.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* Provides structured error information including error codes,
|
|
89
|
+
* event context, and optional cause for error chaining.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* try {
|
|
94
|
+
* await transport.send(event, payload)
|
|
95
|
+
* } catch (err) {
|
|
96
|
+
* if (err instanceof TuffTransportError) {
|
|
97
|
+
* switch (err.code) {
|
|
98
|
+
* case TuffTransportErrorCode.TIMEOUT:
|
|
99
|
+
* // Handle timeout
|
|
100
|
+
* break
|
|
101
|
+
* case TuffTransportErrorCode.INVALID_PLUGIN_KEY:
|
|
102
|
+
* // Handle auth error
|
|
103
|
+
* break
|
|
104
|
+
* }
|
|
105
|
+
* }
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export class TuffTransportError extends Error {
|
|
110
|
+
/**
|
|
111
|
+
* Error code for programmatic handling.
|
|
112
|
+
*/
|
|
113
|
+
readonly code: TuffTransportErrorCode
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Event name associated with this error (if applicable).
|
|
117
|
+
*/
|
|
118
|
+
readonly eventName?: string
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Plugin name associated with this error (if applicable).
|
|
122
|
+
*/
|
|
123
|
+
readonly pluginName?: string
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Original error that caused this error (if applicable).
|
|
127
|
+
*/
|
|
128
|
+
override readonly cause?: Error
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Timestamp when this error occurred.
|
|
132
|
+
*/
|
|
133
|
+
readonly timestamp: number
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Creates a new TuffTransportError.
|
|
137
|
+
*
|
|
138
|
+
* @param code - Error code
|
|
139
|
+
* @param message - Human-readable error message
|
|
140
|
+
* @param options - Additional error context
|
|
141
|
+
*/
|
|
142
|
+
constructor(
|
|
143
|
+
code: TuffTransportErrorCode,
|
|
144
|
+
message: string,
|
|
145
|
+
options?: {
|
|
146
|
+
eventName?: string
|
|
147
|
+
pluginName?: string
|
|
148
|
+
cause?: Error
|
|
149
|
+
},
|
|
150
|
+
) {
|
|
151
|
+
super(message)
|
|
152
|
+
this.name = 'TuffTransportError'
|
|
153
|
+
this.code = code
|
|
154
|
+
this.eventName = options?.eventName
|
|
155
|
+
this.pluginName = options?.pluginName
|
|
156
|
+
this.cause = options?.cause
|
|
157
|
+
this.timestamp = Date.now()
|
|
158
|
+
|
|
159
|
+
// Maintain proper stack trace in V8 environments
|
|
160
|
+
if (Error.captureStackTrace) {
|
|
161
|
+
Error.captureStackTrace(this, TuffTransportError)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Creates a serializable representation of this error.
|
|
167
|
+
*
|
|
168
|
+
* @returns Plain object representation
|
|
169
|
+
*/
|
|
170
|
+
toJSON(): Record<string, unknown> {
|
|
171
|
+
return {
|
|
172
|
+
name: this.name,
|
|
173
|
+
code: this.code,
|
|
174
|
+
message: this.message,
|
|
175
|
+
eventName: this.eventName,
|
|
176
|
+
pluginName: this.pluginName,
|
|
177
|
+
timestamp: this.timestamp,
|
|
178
|
+
stack: this.stack,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Creates a TuffTransportError from a plain object.
|
|
184
|
+
*
|
|
185
|
+
* @param obj - Plain object (e.g., from IPC)
|
|
186
|
+
* @returns TuffTransportError instance
|
|
187
|
+
*/
|
|
188
|
+
static fromJSON(obj: Record<string, unknown>): TuffTransportError {
|
|
189
|
+
const error = new TuffTransportError(
|
|
190
|
+
(obj.code as TuffTransportErrorCode) || TuffTransportErrorCode.INTERNAL_ERROR,
|
|
191
|
+
(obj.message as string) || 'Unknown error',
|
|
192
|
+
{
|
|
193
|
+
eventName: obj.eventName as string | undefined,
|
|
194
|
+
pluginName: obj.pluginName as string | undefined,
|
|
195
|
+
},
|
|
196
|
+
)
|
|
197
|
+
if (obj.stack && typeof obj.stack === 'string') {
|
|
198
|
+
error.stack = obj.stack
|
|
199
|
+
}
|
|
200
|
+
return error
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// Error Factory Functions
|
|
206
|
+
// ============================================================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Creates an INVALID_EVENT error.
|
|
210
|
+
*
|
|
211
|
+
* @param context - Context where the error occurred
|
|
212
|
+
* @returns TuffTransportError
|
|
213
|
+
*/
|
|
214
|
+
export function createInvalidEventError(context: string): TuffTransportError {
|
|
215
|
+
return new TuffTransportError(
|
|
216
|
+
TuffTransportErrorCode.INVALID_EVENT,
|
|
217
|
+
`[${context}] Invalid event. Expected TuffEvent created via defineEvent(), `
|
|
218
|
+
+ `received a string or invalid object. String event names are not allowed.`,
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Creates a TIMEOUT error.
|
|
224
|
+
*
|
|
225
|
+
* @param eventName - Event that timed out
|
|
226
|
+
* @param timeoutMs - Timeout duration in milliseconds
|
|
227
|
+
* @returns TuffTransportError
|
|
228
|
+
*/
|
|
229
|
+
export function createTimeoutError(eventName: string, timeoutMs: number): TuffTransportError {
|
|
230
|
+
return new TuffTransportError(
|
|
231
|
+
TuffTransportErrorCode.TIMEOUT,
|
|
232
|
+
`Request "${eventName}" timed out after ${timeoutMs}ms`,
|
|
233
|
+
{ eventName },
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Creates an UNKNOWN_EVENT error.
|
|
239
|
+
*
|
|
240
|
+
* @param eventName - Event name that was not found
|
|
241
|
+
* @returns TuffTransportError
|
|
242
|
+
*/
|
|
243
|
+
export function createUnknownEventError(eventName: string): TuffTransportError {
|
|
244
|
+
return new TuffTransportError(
|
|
245
|
+
TuffTransportErrorCode.UNKNOWN_EVENT,
|
|
246
|
+
`No handler registered for event "${eventName}"`,
|
|
247
|
+
{ eventName },
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Creates an INVALID_PLUGIN_KEY error.
|
|
253
|
+
*
|
|
254
|
+
* @param pluginName - Plugin name (if known)
|
|
255
|
+
* @returns TuffTransportError
|
|
256
|
+
*/
|
|
257
|
+
export function createInvalidPluginKeyError(pluginName?: string): TuffTransportError {
|
|
258
|
+
return new TuffTransportError(
|
|
259
|
+
TuffTransportErrorCode.INVALID_PLUGIN_KEY,
|
|
260
|
+
pluginName
|
|
261
|
+
? `Invalid or expired security key for plugin "${pluginName}"`
|
|
262
|
+
: 'Invalid or expired plugin security key',
|
|
263
|
+
{ pluginName },
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Creates a TARGET_NOT_FOUND error.
|
|
269
|
+
*
|
|
270
|
+
* @param target - Description of the target
|
|
271
|
+
* @param eventName - Event name (if applicable)
|
|
272
|
+
* @returns TuffTransportError
|
|
273
|
+
*/
|
|
274
|
+
export function createTargetNotFoundError(target: string, eventName?: string): TuffTransportError {
|
|
275
|
+
return new TuffTransportError(
|
|
276
|
+
TuffTransportErrorCode.TARGET_NOT_FOUND,
|
|
277
|
+
`Target "${target}" not found or has been destroyed`,
|
|
278
|
+
{ eventName },
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Creates a SERIALIZE_FAILED error.
|
|
284
|
+
*
|
|
285
|
+
* @param eventName - Event name
|
|
286
|
+
* @param cause - Original error
|
|
287
|
+
* @returns TuffTransportError
|
|
288
|
+
*/
|
|
289
|
+
export function createSerializeError(eventName: string, cause: Error): TuffTransportError {
|
|
290
|
+
return new TuffTransportError(
|
|
291
|
+
TuffTransportErrorCode.SERIALIZE_FAILED,
|
|
292
|
+
`Failed to serialize payload for "${eventName}": ${cause.message}`,
|
|
293
|
+
{ eventName, cause },
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Creates a STREAM_CANCELLED error.
|
|
299
|
+
*
|
|
300
|
+
* @param eventName - Event name
|
|
301
|
+
* @param streamId - Stream identifier
|
|
302
|
+
* @returns TuffTransportError
|
|
303
|
+
*/
|
|
304
|
+
export function createStreamCancelledError(eventName: string, streamId: string): TuffTransportError {
|
|
305
|
+
return new TuffTransportError(
|
|
306
|
+
TuffTransportErrorCode.STREAM_CANCELLED,
|
|
307
|
+
`Stream "${streamId}" for event "${eventName}" was cancelled`,
|
|
308
|
+
{ eventName },
|
|
309
|
+
)
|
|
310
|
+
}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TuffEvent Builder - Type-safe event definition system
|
|
3
|
+
* @module @talex-touch/utils/transport/event/builder
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { EventOptions, TuffEvent } from './types'
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Builder Classes
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Namespace builder - Entry point for event definition.
|
|
14
|
+
*
|
|
15
|
+
* @typeParam TNamespace - The namespace string type
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const builder = TuffEventBuilder.namespace('core-box')
|
|
20
|
+
* // or use the shorthand:
|
|
21
|
+
* const builder = defineEvent('core-box')
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export class TuffEventBuilder<TNamespace extends string> {
|
|
25
|
+
private readonly _namespace: TNamespace
|
|
26
|
+
|
|
27
|
+
private constructor(namespace: TNamespace) {
|
|
28
|
+
this._namespace = namespace
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new event builder with the specified namespace.
|
|
33
|
+
*
|
|
34
|
+
* @param ns - The namespace string (e.g., 'core-box', 'storage')
|
|
35
|
+
* @returns A new TuffEventBuilder instance
|
|
36
|
+
* @throws {Error} If namespace is empty or not a string
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const builder = TuffEventBuilder.namespace('my-plugin')
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
static namespace<T extends string>(ns: T): TuffEventBuilder<T> {
|
|
44
|
+
if (!ns || typeof ns !== 'string') {
|
|
45
|
+
throw new Error('[TuffEvent] Namespace must be a non-empty string')
|
|
46
|
+
}
|
|
47
|
+
return new TuffEventBuilder(ns)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Defines the module for this event.
|
|
52
|
+
*
|
|
53
|
+
* @param module - The module string (e.g., 'search', 'ui')
|
|
54
|
+
* @returns A TuffModuleBuilder instance
|
|
55
|
+
* @throws {Error} If module is empty or not a string
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* defineEvent('core-box').module('search')
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
module<TModule extends string>(module: TModule): TuffModuleBuilder<TNamespace, TModule> {
|
|
63
|
+
if (!module || typeof module !== 'string') {
|
|
64
|
+
throw new Error('[TuffEvent] Module must be a non-empty string')
|
|
65
|
+
}
|
|
66
|
+
return new TuffModuleBuilder(this._namespace, module)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Module builder - Second stage of event definition.
|
|
72
|
+
*
|
|
73
|
+
* @typeParam TNamespace - The namespace string type
|
|
74
|
+
* @typeParam TModule - The module string type
|
|
75
|
+
*/
|
|
76
|
+
export class TuffModuleBuilder<
|
|
77
|
+
TNamespace extends string,
|
|
78
|
+
TModule extends string,
|
|
79
|
+
> {
|
|
80
|
+
/**
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
constructor(
|
|
84
|
+
private readonly _namespace: TNamespace,
|
|
85
|
+
private readonly _module: TModule,
|
|
86
|
+
) {}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Defines the action for this event.
|
|
90
|
+
*
|
|
91
|
+
* @param action - The action string (e.g., 'query', 'hide')
|
|
92
|
+
* @returns A TuffActionBuilder instance
|
|
93
|
+
* @throws {Error} If action is empty or not a string
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* defineEvent('core-box').module('search').event('query')
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
event<TAction extends string>(action: TAction): TuffActionBuilder<TNamespace, TModule, TAction> {
|
|
101
|
+
if (!action || typeof action !== 'string') {
|
|
102
|
+
throw new Error('[TuffEvent] Action must be a non-empty string')
|
|
103
|
+
}
|
|
104
|
+
return new TuffActionBuilder(this._namespace, this._module, action)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Action builder - Final stage of event definition.
|
|
110
|
+
*
|
|
111
|
+
* @typeParam TNamespace - The namespace string type
|
|
112
|
+
* @typeParam TModule - The module string type
|
|
113
|
+
* @typeParam TAction - The action string type
|
|
114
|
+
*/
|
|
115
|
+
export class TuffActionBuilder<
|
|
116
|
+
TNamespace extends string,
|
|
117
|
+
TModule extends string,
|
|
118
|
+
TAction extends string,
|
|
119
|
+
> {
|
|
120
|
+
/**
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
constructor(
|
|
124
|
+
private readonly _namespace: TNamespace,
|
|
125
|
+
private readonly _module: TModule,
|
|
126
|
+
private readonly _action: TAction,
|
|
127
|
+
) {}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Finalizes the event definition with request/response types.
|
|
131
|
+
*
|
|
132
|
+
* @typeParam TRequest - Type of the request payload (default: void)
|
|
133
|
+
* @typeParam TResponse - Type of the response payload (default: void)
|
|
134
|
+
* @param options - Optional batch/stream configuration
|
|
135
|
+
* @returns An immutable TuffEvent instance
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* // Simple event with no payload
|
|
140
|
+
* const hideEvent = defineEvent('core-box')
|
|
141
|
+
* .module('ui')
|
|
142
|
+
* .event('hide')
|
|
143
|
+
* .define()
|
|
144
|
+
*
|
|
145
|
+
* // Event with typed request/response
|
|
146
|
+
* const queryEvent = defineEvent('core-box')
|
|
147
|
+
* .module('search')
|
|
148
|
+
* .event('query')
|
|
149
|
+
* .define<{ text: string }, SearchResult[]>()
|
|
150
|
+
*
|
|
151
|
+
* // Event with batch configuration
|
|
152
|
+
* const getEvent = defineEvent('storage')
|
|
153
|
+
* .module('app')
|
|
154
|
+
* .event('get')
|
|
155
|
+
* .define<{ key: string }, unknown>({
|
|
156
|
+
* batch: { enabled: true, windowMs: 50 }
|
|
157
|
+
* })
|
|
158
|
+
*
|
|
159
|
+
* // Stream event
|
|
160
|
+
* const streamEvent = defineEvent('core-box')
|
|
161
|
+
* .module('search')
|
|
162
|
+
* .event('stream')
|
|
163
|
+
* .define<{ text: string }, AsyncIterable<SearchResult>>({
|
|
164
|
+
* stream: { enabled: true }
|
|
165
|
+
* })
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
define<TRequest = void, TResponse = void>(
|
|
169
|
+
options?: EventOptions,
|
|
170
|
+
): TuffEvent<TRequest, TResponse, TNamespace, TModule, TAction> {
|
|
171
|
+
const namespace = this._namespace
|
|
172
|
+
const module = this._module
|
|
173
|
+
const action = this._action
|
|
174
|
+
const eventName = `${namespace}:${module}:${action}`
|
|
175
|
+
|
|
176
|
+
// Create immutable event object
|
|
177
|
+
const event: TuffEvent<TRequest, TResponse, TNamespace, TModule, TAction> = Object.freeze({
|
|
178
|
+
__brand: 'TuffEvent' as const,
|
|
179
|
+
namespace,
|
|
180
|
+
module,
|
|
181
|
+
action,
|
|
182
|
+
_batch: options?.batch,
|
|
183
|
+
_stream: options?.stream,
|
|
184
|
+
_request: undefined as unknown as TRequest,
|
|
185
|
+
_response: undefined as unknown as TResponse,
|
|
186
|
+
|
|
187
|
+
toString(): string {
|
|
188
|
+
return eventName
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
toEventName(): string {
|
|
192
|
+
return eventName
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
return event
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Shorthand Functions
|
|
202
|
+
// ============================================================================
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Creates a new event definition builder.
|
|
206
|
+
*
|
|
207
|
+
* @remarks
|
|
208
|
+
* This is the primary entry point for defining TuffEvents.
|
|
209
|
+
* All events MUST be created using this builder to ensure type safety.
|
|
210
|
+
*
|
|
211
|
+
* @param namespace - The event namespace (e.g., 'core-box', 'storage', 'plugin')
|
|
212
|
+
* @returns A TuffEventBuilder instance
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* // Define a simple event
|
|
217
|
+
* const hideEvent = defineEvent('core-box')
|
|
218
|
+
* .module('ui')
|
|
219
|
+
* .event('hide')
|
|
220
|
+
* .define()
|
|
221
|
+
*
|
|
222
|
+
* // Define an event with typed payloads
|
|
223
|
+
* const queryEvent = defineEvent('core-box')
|
|
224
|
+
* .module('search')
|
|
225
|
+
* .event('query')
|
|
226
|
+
* .define<QueryRequest, QueryResponse>()
|
|
227
|
+
*
|
|
228
|
+
* // Define a batch-enabled event
|
|
229
|
+
* const getEvent = defineEvent('storage')
|
|
230
|
+
* .module('app')
|
|
231
|
+
* .event('get')
|
|
232
|
+
* .define<{ key: string }, unknown>({
|
|
233
|
+
* batch: { enabled: true, windowMs: 50, maxSize: 20 }
|
|
234
|
+
* })
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export const defineEvent = TuffEventBuilder.namespace
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Defines a TuffEvent using a raw event name string.
|
|
241
|
+
*
|
|
242
|
+
* @remarks
|
|
243
|
+
* This is intended for incremental migrations from legacy IPC event names that
|
|
244
|
+
* don't follow the `namespace:module:action` convention yet.
|
|
245
|
+
*
|
|
246
|
+
* Prefer `defineEvent(namespace).module(module).event(action)` for new code.
|
|
247
|
+
*/
|
|
248
|
+
export function defineRawEvent<TRequest = void, TResponse = void>(
|
|
249
|
+
eventName: string,
|
|
250
|
+
options?: EventOptions,
|
|
251
|
+
): TuffEvent<TRequest, TResponse, string, string, string> {
|
|
252
|
+
if (!eventName || typeof eventName !== 'string') {
|
|
253
|
+
throw new Error('[TuffEvent] Raw event name must be a non-empty string')
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const parts = eventName.split(':')
|
|
257
|
+
const namespace = parts[0] || 'raw'
|
|
258
|
+
const module = parts.length >= 2 ? (parts[1] || 'raw') : 'raw'
|
|
259
|
+
const action = parts.length >= 3 ? (parts.slice(2).join(':') || 'raw') : 'raw'
|
|
260
|
+
|
|
261
|
+
const event: TuffEvent<TRequest, TResponse, string, string, string> = Object.freeze({
|
|
262
|
+
__brand: 'TuffEvent' as const,
|
|
263
|
+
namespace,
|
|
264
|
+
module,
|
|
265
|
+
action,
|
|
266
|
+
_batch: options?.batch,
|
|
267
|
+
_stream: options?.stream,
|
|
268
|
+
_request: undefined as unknown as TRequest,
|
|
269
|
+
_response: undefined as unknown as TResponse,
|
|
270
|
+
|
|
271
|
+
toString(): string {
|
|
272
|
+
return eventName
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
toEventName(): string {
|
|
276
|
+
return eventName
|
|
277
|
+
},
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
return event
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ============================================================================
|
|
284
|
+
// Type Guards and Assertions
|
|
285
|
+
// ============================================================================
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Runtime type guard to check if a value is a valid TuffEvent.
|
|
289
|
+
*
|
|
290
|
+
* @param value - The value to check
|
|
291
|
+
* @returns `true` if the value is a TuffEvent, `false` otherwise
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```typescript
|
|
295
|
+
* if (isTuffEvent(maybeEvent)) {
|
|
296
|
+
* // TypeScript now knows maybeEvent is a TuffEvent
|
|
297
|
+
* console.log(maybeEvent.toString())
|
|
298
|
+
* }
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
export function isTuffEvent(value: unknown): value is TuffEvent {
|
|
302
|
+
return (
|
|
303
|
+
typeof value === 'object'
|
|
304
|
+
&& value !== null
|
|
305
|
+
&& (value as Record<string, unknown>).__brand === 'TuffEvent'
|
|
306
|
+
&& typeof (value as Record<string, unknown>).toString === 'function'
|
|
307
|
+
&& typeof (value as Record<string, unknown>).namespace === 'string'
|
|
308
|
+
&& typeof (value as Record<string, unknown>).module === 'string'
|
|
309
|
+
&& typeof (value as Record<string, unknown>).action === 'string'
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Asserts that a value is a valid TuffEvent, throwing if not.
|
|
315
|
+
*
|
|
316
|
+
* @param value - The value to assert
|
|
317
|
+
* @param context - Optional context string for error messages
|
|
318
|
+
* @throws {TypeError} If the value is not a TuffEvent
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* function sendEvent(event: unknown, payload: unknown) {
|
|
323
|
+
* assertTuffEvent(event, 'sendEvent')
|
|
324
|
+
* // TypeScript now knows event is a TuffEvent
|
|
325
|
+
* console.log(`Sending: ${event.toString()}`)
|
|
326
|
+
* }
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
export function assertTuffEvent(
|
|
330
|
+
value: unknown,
|
|
331
|
+
context?: string,
|
|
332
|
+
): asserts value is TuffEvent {
|
|
333
|
+
if (!isTuffEvent(value)) {
|
|
334
|
+
const prefix = context ? `[${context}] ` : ''
|
|
335
|
+
throw new TypeError(
|
|
336
|
+
`${prefix}Invalid event. Expected TuffEvent created via defineEvent(), `
|
|
337
|
+
+ `got ${value === null ? 'null' : typeof value}. `
|
|
338
|
+
+ `String event names are not allowed - use the event builder.`,
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Extracts the event name string from a TuffEvent.
|
|
345
|
+
*
|
|
346
|
+
* @param event - The TuffEvent to extract from
|
|
347
|
+
* @returns The event name string
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* ```typescript
|
|
351
|
+
* const name = getEventName(CoreBoxEvents.ui.hide)
|
|
352
|
+
* // name = 'core-box:ui:hide'
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
export function getEventName(event: TuffEvent): string {
|
|
356
|
+
assertTuffEvent(event, 'getEventName')
|
|
357
|
+
return event.toString()
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Checks if an event has batch configuration enabled.
|
|
362
|
+
*
|
|
363
|
+
* @param event - The TuffEvent to check
|
|
364
|
+
* @returns `true` if batch is enabled
|
|
365
|
+
*/
|
|
366
|
+
export function isBatchEnabled(event: TuffEvent): boolean {
|
|
367
|
+
return event._batch?.enabled === true
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Checks if an event has stream configuration enabled.
|
|
372
|
+
*
|
|
373
|
+
* @param event - The TuffEvent to check
|
|
374
|
+
* @returns `true` if stream is enabled
|
|
375
|
+
*/
|
|
376
|
+
export function isStreamEnabled(event: TuffEvent): boolean {
|
|
377
|
+
return event._stream?.enabled === true
|
|
378
|
+
}
|