@maccesar/titools 2.0.0
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/AGENTS-TEMPLATE.md +173 -0
- package/README.md +867 -0
- package/agents/ti-researcher.md +108 -0
- package/bin/titools.js +53 -0
- package/lib/commands/agents.js +126 -0
- package/lib/commands/install.js +188 -0
- package/lib/commands/uninstall.js +215 -0
- package/lib/commands/update.js +159 -0
- package/lib/config.js +119 -0
- package/lib/downloader.js +153 -0
- package/lib/installer.js +253 -0
- package/lib/platform.js +108 -0
- package/lib/symlink.js +142 -0
- package/lib/utils.js +270 -0
- package/package.json +67 -0
- package/skills/alloy-expert/SKILL.md +247 -0
- package/skills/alloy-expert/assets/ControllerAutoCleanup.js +182 -0
- package/skills/alloy-expert/references/alloy-structure.md +381 -0
- package/skills/alloy-expert/references/anti-patterns.md +133 -0
- package/skills/alloy-expert/references/code-conventions.md +469 -0
- package/skills/alloy-expert/references/contracts.md +280 -0
- package/skills/alloy-expert/references/controller-patterns.md +520 -0
- package/skills/alloy-expert/references/error-handling.md +484 -0
- package/skills/alloy-expert/references/examples.md +735 -0
- package/skills/alloy-expert/references/migration-patterns.md +298 -0
- package/skills/alloy-expert/references/patterns.md +448 -0
- package/skills/alloy-expert/references/performance-patterns.md +855 -0
- package/skills/alloy-expert/references/security-patterns.md +847 -0
- package/skills/alloy-expert/references/state-management.md +779 -0
- package/skills/alloy-expert/references/testing.md +872 -0
- package/skills/alloy-guides/SKILL.md +214 -0
- package/skills/alloy-guides/references/CLI_TASKS.md +243 -0
- package/skills/alloy-guides/references/CONCEPTS.md +191 -0
- package/skills/alloy-guides/references/CONTROLLERS.md +298 -0
- package/skills/alloy-guides/references/MODELS.md +1028 -0
- package/skills/alloy-guides/references/PURGETSS.md +56 -0
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +242 -0
- package/skills/alloy-guides/references/VIEWS_STYLES.md +388 -0
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +109 -0
- package/skills/alloy-guides/references/VIEWS_XML.md +558 -0
- package/skills/alloy-guides/references/WIDGETS.md +176 -0
- package/skills/alloy-howtos/SKILL.md +203 -0
- package/skills/alloy-howtos/references/best_practices.md +138 -0
- package/skills/alloy-howtos/references/cli_reference.md +253 -0
- package/skills/alloy-howtos/references/config_files.md +87 -0
- package/skills/alloy-howtos/references/custom_tags.md +147 -0
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +101 -0
- package/skills/alloy-howtos/references/samples.md +167 -0
- package/skills/purgetss/SKILL.md +442 -0
- package/skills/purgetss/assets/purgetss.config.cjs +17 -0
- package/skills/purgetss/references/EXAMPLES.md +247 -0
- package/skills/purgetss/references/animation-system.md +1294 -0
- package/skills/purgetss/references/apply-directive.md +375 -0
- package/skills/purgetss/references/arbitrary-values.md +612 -0
- package/skills/purgetss/references/class-index.md +1350 -0
- package/skills/purgetss/references/cli-commands.md +948 -0
- package/skills/purgetss/references/configurable-properties.md +654 -0
- package/skills/purgetss/references/custom-rules.md +161 -0
- package/skills/purgetss/references/customization-deep-dive.md +722 -0
- package/skills/purgetss/references/dynamic-component-creation.md +489 -0
- package/skills/purgetss/references/grid-layout.md +455 -0
- package/skills/purgetss/references/icon-fonts.md +609 -0
- package/skills/purgetss/references/installation-setup.md +366 -0
- package/skills/purgetss/references/opacity-modifier.md +291 -0
- package/skills/purgetss/references/platform-modifiers.md +479 -0
- package/skills/purgetss/references/smart-mappings.md +42 -0
- package/skills/purgetss/references/titanium-resets.md +359 -0
- package/skills/purgetss/references/ui-ux-design.md +1526 -0
- package/skills/ti-guides/SKILL.md +94 -0
- package/skills/ti-guides/references/advanced-data-and-images.md +19 -0
- package/skills/ti-guides/references/alloy-cli-advanced.md +84 -0
- package/skills/ti-guides/references/alloy-data-mastery.md +29 -0
- package/skills/ti-guides/references/alloy-widgets-and-themes.md +19 -0
- package/skills/ti-guides/references/android-manifest.md +97 -0
- package/skills/ti-guides/references/app-distribution.md +258 -0
- package/skills/ti-guides/references/application-frameworks.md +377 -0
- package/skills/ti-guides/references/cli-reference.md +402 -0
- package/skills/ti-guides/references/coding-best-practices.md +102 -0
- package/skills/ti-guides/references/commonjs-advanced.md +134 -0
- package/skills/ti-guides/references/hello-world.md +100 -0
- package/skills/ti-guides/references/hyperloop-native-access.md +62 -0
- package/skills/ti-guides/references/javascript-primer.md +411 -0
- package/skills/ti-guides/references/reserved-words.md +36 -0
- package/skills/ti-guides/references/resources.md +183 -0
- package/skills/ti-guides/references/style-and-conventions.md +48 -0
- package/skills/ti-guides/references/tiapp-config.md +609 -0
- package/skills/ti-howtos/SKILL.md +174 -0
- package/skills/ti-howtos/references/android-platform-deep-dives.md +658 -0
- package/skills/ti-howtos/references/automation-fastlane-appium.md +95 -0
- package/skills/ti-howtos/references/buffer-codec-streams.md +140 -0
- package/skills/ti-howtos/references/cross-platform-development.md +348 -0
- package/skills/ti-howtos/references/debugging-profiling.md +543 -0
- package/skills/ti-howtos/references/extending-titanium.md +723 -0
- package/skills/ti-howtos/references/google-maps-v2.md +169 -0
- package/skills/ti-howtos/references/ios-map-kit.md +143 -0
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +783 -0
- package/skills/ti-howtos/references/local-data-sources.md +301 -0
- package/skills/ti-howtos/references/location-and-maps.md +252 -0
- package/skills/ti-howtos/references/media-apis.md +210 -0
- package/skills/ti-howtos/references/notification-services.md +599 -0
- package/skills/ti-howtos/references/remote-data-sources.md +349 -0
- package/skills/ti-howtos/references/tutorials.md +502 -0
- package/skills/ti-howtos/references/using-modules.md +237 -0
- package/skills/ti-howtos/references/web-content-integration.md +307 -0
- package/skills/ti-howtos/references/webpack-build-pipeline.md +78 -0
- package/skills/ti-ui/SKILL.md +179 -0
- package/skills/ti-ui/references/accessibility-deep-dive.md +242 -0
- package/skills/ti-ui/references/animation-and-matrices.md +599 -0
- package/skills/ti-ui/references/application-structures.md +655 -0
- package/skills/ti-ui/references/custom-fonts-styling.md +579 -0
- package/skills/ti-ui/references/event-handling.md +393 -0
- package/skills/ti-ui/references/gestures.md +473 -0
- package/skills/ti-ui/references/icons-and-splash-screens.md +409 -0
- package/skills/ti-ui/references/layouts-and-positioning.md +462 -0
- package/skills/ti-ui/references/listviews-and-performance.md +619 -0
- package/skills/ti-ui/references/orientation.md +362 -0
- package/skills/ti-ui/references/platform-ui-android.md +635 -0
- package/skills/ti-ui/references/platform-ui-ios.md +469 -0
- package/skills/ti-ui/references/scrolling-views.md +252 -0
- package/skills/ti-ui/references/tableviews.md +568 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
# Error Handling & Logging Guide
|
|
2
|
+
|
|
3
|
+
## AppError Classes
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
// lib/core/appError.js
|
|
7
|
+
module.exports = class AppError extends Error {
|
|
8
|
+
constructor(message, code, statusCode = 500) {
|
|
9
|
+
super(message)
|
|
10
|
+
this.code = code
|
|
11
|
+
this.statusCode = statusCode
|
|
12
|
+
this.name = 'AppError'
|
|
13
|
+
Error.captureStackTrace?.(this, this.constructor)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Specific error types
|
|
18
|
+
module.exports = class NetworkError extends AppError {
|
|
19
|
+
constructor(message = 'Network request failed') {
|
|
20
|
+
super(message, 'NETWORK_ERROR', 0)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = class AuthError extends AppError {
|
|
25
|
+
constructor(message = 'Authentication failed') {
|
|
26
|
+
super(message, 'AUTH_ERROR', 401)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = class ValidationError extends AppError {
|
|
31
|
+
constructor(message = 'Validation failed') {
|
|
32
|
+
super(message, 'VALIDATION_ERROR', 400)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = class NotFoundError extends AppError {
|
|
37
|
+
constructor(message = 'Resource not found') {
|
|
38
|
+
super(message, 'NOT_FOUND', 404)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Error codes for reference
|
|
43
|
+
exports.ErrorCodes = {
|
|
44
|
+
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
45
|
+
AUTH_ERROR: 'AUTH_ERROR',
|
|
46
|
+
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
47
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
48
|
+
SERVER_ERROR: 'SERVER_ERROR'
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Logger Service
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
// lib/services/logger.js
|
|
56
|
+
const LogLevel = {
|
|
57
|
+
DEBUG: 0,
|
|
58
|
+
INFO: 1,
|
|
59
|
+
WARN: 2,
|
|
60
|
+
ERROR: 3
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class Logger {
|
|
64
|
+
constructor() {
|
|
65
|
+
this.level = this._getLogLevel()
|
|
66
|
+
this.sessionId = this._generateSessionId()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_getLogLevel() {
|
|
70
|
+
const deployType = Ti.App.deployType
|
|
71
|
+
|
|
72
|
+
if (deployType === 'development') {
|
|
73
|
+
return LogLevel.DEBUG
|
|
74
|
+
} else if (deployType === 'test') {
|
|
75
|
+
return LogLevel.WARN
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return LogLevel.INFO
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_generateSessionId() {
|
|
82
|
+
return Date.now().toString(36) + Math.random().toString(36).substr(2)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_shouldLog(level) {
|
|
86
|
+
return level >= this.level
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_log(level, tag, message, data = {}) {
|
|
90
|
+
if (!this._shouldLog(level)) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const logData = {
|
|
95
|
+
sessionId: this.sessionId,
|
|
96
|
+
timestamp: new Date().toISOString(),
|
|
97
|
+
level: this._levelName(level),
|
|
98
|
+
tag,
|
|
99
|
+
message,
|
|
100
|
+
...data
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const logString = JSON.stringify(logData)
|
|
104
|
+
|
|
105
|
+
switch (level) {
|
|
106
|
+
case LogLevel.DEBUG:
|
|
107
|
+
Ti.API.debug(`[${tag}] ${message}`, logString)
|
|
108
|
+
break
|
|
109
|
+
case LogLevel.INFO:
|
|
110
|
+
Ti.API.info(`[${tag}] ${message}`, logString)
|
|
111
|
+
break
|
|
112
|
+
case LogLevel.WARN:
|
|
113
|
+
Ti.API.warn(`[${tag}] ${message}`, logString)
|
|
114
|
+
break
|
|
115
|
+
case LogLevel.ERROR:
|
|
116
|
+
Ti.API.error(`[${tag}] ${message}`, logString)
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
_levelName(level) {
|
|
122
|
+
return Object.keys(LogLevel).find(key => LogLevel[key] === level)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
debug(tag, message, data) {
|
|
126
|
+
this._log(LogLevel.DEBUG, tag, message, data)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
info(tag, message, data) {
|
|
130
|
+
this._log(LogLevel.INFO, tag, message, data)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
warn(tag, message, data) {
|
|
134
|
+
this._log(LogLevel.WARN, tag, message, data)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
error(tag, message, data) {
|
|
138
|
+
this._log(LogLevel.ERROR, tag, message, data)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = new Logger()
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Error Handler Service
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
// lib/services/errorHandler.js
|
|
149
|
+
const logger = require('lib/services/logger')
|
|
150
|
+
const { AppError } = require('lib/core/appError')
|
|
151
|
+
|
|
152
|
+
exports.handleError = function handleError(error, context = {}) {
|
|
153
|
+
// Log the error
|
|
154
|
+
logger.error('ErrorHandler', 'Error occurred', {
|
|
155
|
+
message: error.message,
|
|
156
|
+
code: error.code,
|
|
157
|
+
status: error.statusCode,
|
|
158
|
+
stack: error.stack,
|
|
159
|
+
...context
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// Report to crash service (if configured)
|
|
163
|
+
_reportToCrashService(error, context)
|
|
164
|
+
|
|
165
|
+
// Return user-friendly message
|
|
166
|
+
return _getUserMessage(error)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function _reportToCrashService(error, context) {
|
|
170
|
+
// Only in production
|
|
171
|
+
if (Ti.App.deployType !== 'production') {
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Example: Sentry integration
|
|
176
|
+
if (typeof Sentry !== 'undefined') {
|
|
177
|
+
Sentry.captureException(error, {
|
|
178
|
+
extra: context
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function _getUserMessage(error) {
|
|
184
|
+
const messages = {
|
|
185
|
+
NETWORK_ERROR: L('error_network'),
|
|
186
|
+
AUTH_ERROR: L('error_auth'),
|
|
187
|
+
VALIDATION_ERROR: L('error_validation'),
|
|
188
|
+
NOT_FOUND: L('error_not_found'),
|
|
189
|
+
SERVER_ERROR: L('error_server')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (error instanceof AppError) {
|
|
193
|
+
return messages[error.code] || L('error_unknown')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return L('error_unknown')
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Using the Logger
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
// lib/services/userService.js
|
|
204
|
+
const logger = require('lib/services/logger')
|
|
205
|
+
const { NetworkError, NotFoundError } = require('lib/core/appError')
|
|
206
|
+
|
|
207
|
+
exports.getUserProfile = async function getUserProfile(userId) {
|
|
208
|
+
logger.info('UserService', 'Fetching user profile', { userId })
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const response = await api.get(`/users/${userId}`)
|
|
212
|
+
|
|
213
|
+
logger.debug('UserService', 'User profile fetched', {
|
|
214
|
+
userId,
|
|
215
|
+
hasData: !!response.data
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
return response.data
|
|
219
|
+
|
|
220
|
+
} catch (error) {
|
|
221
|
+
logger.error('UserService', 'Failed to fetch user profile', {
|
|
222
|
+
userId,
|
|
223
|
+
error: error.message,
|
|
224
|
+
status: error.statusCode
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
if (error.status === 404) {
|
|
228
|
+
throw new NotFoundError('User not found')
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
throw new NetworkError('Failed to load profile')
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## API Client Error Handling
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
// lib/api/client.js
|
|
240
|
+
const logger = require('lib/services/logger')
|
|
241
|
+
const { NetworkError, AuthError, NotFoundError, ValidationError } = require('lib/core/appError')
|
|
242
|
+
|
|
243
|
+
exports.get = async function get(endpoint, params = {}) {
|
|
244
|
+
return new Promise((resolve, reject) => {
|
|
245
|
+
const client = Ti.Network.createHTTPClient({
|
|
246
|
+
timeout: 10000,
|
|
247
|
+
|
|
248
|
+
onload: () => {
|
|
249
|
+
try {
|
|
250
|
+
const data = JSON.parse(client.responseText)
|
|
251
|
+
|
|
252
|
+
logger.debug('API', 'Request successful', {
|
|
253
|
+
endpoint,
|
|
254
|
+
status: client.status
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
resolve(data)
|
|
258
|
+
|
|
259
|
+
} catch (e) {
|
|
260
|
+
logger.error('API', 'Invalid JSON response', {
|
|
261
|
+
endpoint,
|
|
262
|
+
responseText: client.responseText
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
reject(new ValidationError('Invalid response format'))
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
onerror: (e) => {
|
|
270
|
+
logger.error('API', 'Request failed', {
|
|
271
|
+
endpoint,
|
|
272
|
+
status: client.status,
|
|
273
|
+
error: e.error
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
// Map HTTP status to error types
|
|
277
|
+
let error
|
|
278
|
+
|
|
279
|
+
switch (client.status) {
|
|
280
|
+
case 401:
|
|
281
|
+
error = new AuthError()
|
|
282
|
+
break
|
|
283
|
+
case 404:
|
|
284
|
+
error = new NotFoundError()
|
|
285
|
+
break
|
|
286
|
+
case 0:
|
|
287
|
+
error = new NetworkError('Network unavailable')
|
|
288
|
+
break
|
|
289
|
+
default:
|
|
290
|
+
error = new NetworkError(`Request failed: ${client.status}`)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
reject(error)
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
logger.debug('API', 'Sending request', { endpoint, params })
|
|
298
|
+
|
|
299
|
+
client.open('GET', endpoint)
|
|
300
|
+
client.send(params)
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Controller Error Handling Pattern
|
|
306
|
+
|
|
307
|
+
```javascript
|
|
308
|
+
// controllers/user/detail.js
|
|
309
|
+
const logger = require('lib/services/logger')
|
|
310
|
+
const { handleError } = require('lib/services/errorHandler')
|
|
311
|
+
const { getUserProfile } = require('lib/services/userService')
|
|
312
|
+
|
|
313
|
+
function init() {
|
|
314
|
+
logger.debug('UserDetail', 'Controller initialized', { userId: $.args.userId })
|
|
315
|
+
|
|
316
|
+
loadUserData()
|
|
317
|
+
|
|
318
|
+
// Handle retry button
|
|
319
|
+
$.retryButton.addEventListener('click', loadUserData)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function loadUserData() {
|
|
323
|
+
// Show loading state
|
|
324
|
+
setLoading(true)
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
const user = await getUserProfile($.args.userId)
|
|
328
|
+
|
|
329
|
+
// Update UI
|
|
330
|
+
updateUI(user)
|
|
331
|
+
|
|
332
|
+
logger.info('UserDetail', 'User data loaded successfully', {
|
|
333
|
+
userId: user.id
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
} catch (error) {
|
|
337
|
+
// Handle error
|
|
338
|
+
const userMessage = handleError(error, {
|
|
339
|
+
controller: 'user/detail',
|
|
340
|
+
userId: $.args.userId
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
showError(userMessage)
|
|
344
|
+
|
|
345
|
+
} finally {
|
|
346
|
+
setLoading(false)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function setLoading(isLoading) {
|
|
351
|
+
$.activityIndicator.visible = isLoading
|
|
352
|
+
|
|
353
|
+
if (isLoading) {
|
|
354
|
+
$.activityIndicator.show()
|
|
355
|
+
} else {
|
|
356
|
+
$.activityIndicator.hide()
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function showError(message) {
|
|
361
|
+
$.errorLabel.text = message
|
|
362
|
+
$.errorLabel.visible = true
|
|
363
|
+
|
|
364
|
+
// Auto-hide after 3 seconds
|
|
365
|
+
setTimeout(() => {
|
|
366
|
+
$.errorLabel.visible = false
|
|
367
|
+
}, 3000)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function cleanup() {
|
|
371
|
+
logger.debug('UserDetail', 'Controller cleanup')
|
|
372
|
+
$.retryButton.removeEventListener('click', loadUserData)
|
|
373
|
+
$.destroy()
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
$.cleanup = cleanup
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Global Error Handler
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
// alloy.js - Setup global error catching
|
|
383
|
+
const { handleError } = require('lib/services/errorHandler')
|
|
384
|
+
|
|
385
|
+
// Catch unhandled errors in production
|
|
386
|
+
if (Ti.App.deployType === 'production') {
|
|
387
|
+
Ti.App.addEventListener('uncaughtException', (event) => {
|
|
388
|
+
handleError(event.exception, {
|
|
389
|
+
type: 'uncaughtException',
|
|
390
|
+
url: event.url
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
// Show friendly error screen
|
|
394
|
+
Alloy.createController('error/crash').getView().open()
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Crash Reporting Integration
|
|
400
|
+
|
|
401
|
+
```javascript
|
|
402
|
+
// lib/services/crashReporting.js
|
|
403
|
+
exports.initSentry = function initSentry(dsn) {
|
|
404
|
+
if (typeof Sentry === 'undefined') return
|
|
405
|
+
|
|
406
|
+
Sentry.init({
|
|
407
|
+
dsn: dsn,
|
|
408
|
+
environment: Ti.App.deployType,
|
|
409
|
+
release: Ti.App.version
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
// Set user context when available
|
|
413
|
+
const userId = Ti.App.Properties.getString('userId')
|
|
414
|
+
if (userId) {
|
|
415
|
+
Sentry.setUser({ id: userId })
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
exports.reportToCrashService = function reportToCrashService(error, context = {}) {
|
|
420
|
+
if (typeof Sentry !== 'undefined') {
|
|
421
|
+
Sentry.captureException(error, {
|
|
422
|
+
extra: context
|
|
423
|
+
})
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Validation Helper
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
// lib/helpers/validator.js
|
|
432
|
+
const { ValidationError } = require('lib/core/appError')
|
|
433
|
+
const logger = require('lib/services/logger')
|
|
434
|
+
|
|
435
|
+
exports.validateEmail = function validateEmail(email) {
|
|
436
|
+
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
437
|
+
|
|
438
|
+
if (!regex.test(email)) {
|
|
439
|
+
logger.warn('Validator', 'Invalid email', { email })
|
|
440
|
+
|
|
441
|
+
throw new ValidationError(L('error_invalid_email'))
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return email.trim().toLowerCase()
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
exports.validatePassword = function validatePassword(password) {
|
|
448
|
+
if (!password || password.length < 8) {
|
|
449
|
+
logger.warn('Validator', 'Password too short')
|
|
450
|
+
|
|
451
|
+
throw new ValidationError(L('error_password_short'))
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return password
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
exports.validateRequired = function validateRequired(value, fieldName) {
|
|
458
|
+
if (!value || (typeof value === 'string' && !value.trim())) {
|
|
459
|
+
logger.warn('Validator', 'Required field missing', { fieldName })
|
|
460
|
+
|
|
461
|
+
throw new ValidationError(L('error_required', { field: fieldName }))
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return value
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Logging Best Practices
|
|
469
|
+
|
|
470
|
+
| Practice | Example |
|
|
471
|
+
| -------------------------- | ------------------------------------------------------ |
|
|
472
|
+
| Use appropriate log levels | `DEBUG` for diagnostics, `ERROR` for failures |
|
|
473
|
+
| Include context data | `logger.info('Service', 'Action', { userId, action })` |
|
|
474
|
+
| Don't log sensitive data | Never log passwords, tokens, credit cards |
|
|
475
|
+
| Use tags for filtering | `logger.error('AuthService', ...)` |
|
|
476
|
+
| Log at boundaries | Entry/exit of functions, API calls, user actions |
|
|
477
|
+
|
|
478
|
+
| DO | DON'T |
|
|
479
|
+
| ---------------------------------------------------- | ---------------------------------------- |
|
|
480
|
+
| Log at appropriate levels (DEBUG, INFO, WARN, ERROR) | Log everything at INFO or ERROR |
|
|
481
|
+
| Include structured data as second parameter | Build message strings with concatenation |
|
|
482
|
+
| Use DEBUG for development diagnostics | Use Ti.API.log directly |
|
|
483
|
+
| Log errors with stack traces in production | Log sensitive data (passwords, tokens) |
|
|
484
|
+
| Use context objects for correlation | Log without identifying the source |
|