@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,469 @@
|
|
|
1
|
+
# PurgeTSS & Code Conventions
|
|
2
|
+
|
|
3
|
+
## Module System: CommonJS (NOT ES6 modules)
|
|
4
|
+
|
|
5
|
+
Titanium uses **CommonJS** for modules. Do NOT use ES6 `import`/`export`:
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
// CORRECT - CommonJS
|
|
9
|
+
const { Navigation } = require('lib/services/navigation')
|
|
10
|
+
exports.Navigation = { /* ... */ }
|
|
11
|
+
|
|
12
|
+
// WRONG - ES6 modules (won't work in Titanium)
|
|
13
|
+
import { Navigation } from 'lib/services/navigation'
|
|
14
|
+
export const Navigation = { /* ... */ }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## ES6+ Features (Non-Module)
|
|
18
|
+
|
|
19
|
+
Use `const`, `let`, destructuring, and template literals. Prefer arrow functions for callbacks and traditional functions for main logic.
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
const loadUserData = (userId) => {
|
|
23
|
+
const user = fetchUser(userId)
|
|
24
|
+
return { ...user, loadedAt: Date.now() }
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## No Semicolons
|
|
29
|
+
Omit semicolons at the end of lines (ASI handles it).
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
const users = fetchUsers()
|
|
33
|
+
const name = user.name
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Styling with PurgeTSS (Utility-First)
|
|
37
|
+
Use utility classes for layout, colors, typography, and spacing. Avoid manual TSS files.
|
|
38
|
+
|
|
39
|
+
```xml
|
|
40
|
+
<!-- GOOD: PurgeTSS Utility Classes -->
|
|
41
|
+
<View class="vertical h-auto w-screen bg-gray-100">
|
|
42
|
+
<Label class="m-4 text-xl font-bold text-blue-600" text="L('welcome')" />
|
|
43
|
+
</View>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Critical PurgeTSS Rules:**
|
|
47
|
+
- NO `flex-row`/`flex-col` → Use `horizontal`/`vertical`
|
|
48
|
+
- NO `w-full` → Use `w-screen`
|
|
49
|
+
- NO `p-*` on Views → Use `m-*` on children
|
|
50
|
+
- NO `justify-*`/`items-center` → Use margins/positioning
|
|
51
|
+
|
|
52
|
+
## Platform & Device Modifiers
|
|
53
|
+
Leverage PurgeTSS modifiers instead of writing conditional logic in controllers.
|
|
54
|
+
|
|
55
|
+
```xml
|
|
56
|
+
<View class="ios:mt-10 tablet:mt-20 mt-5" />
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## applyProperties() Pattern
|
|
60
|
+
Use `applyProperties()` to batch UI updates and minimize bridge crossings for properties not managed by classes.
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
$.nameLabel.applyProperties({
|
|
64
|
+
text: user.name,
|
|
65
|
+
opacity: 1
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## ListView Templates
|
|
70
|
+
Always use templates for performance. Never create views dynamically inside list rows.
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
function renderUsers(users) {
|
|
74
|
+
const data = users.map(user => ({
|
|
75
|
+
name: { text: user.name },
|
|
76
|
+
template: 'userTemplate'
|
|
77
|
+
}))
|
|
78
|
+
$.section.items = data
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Memory Cleanup
|
|
83
|
+
Always remove listeners to avoid memory leaks. Implement a `cleanup` function.
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
function cleanup() {
|
|
87
|
+
$.destroy()
|
|
88
|
+
// Remove Ti.App listeners here
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## i18n and Accessibility (a11y)
|
|
93
|
+
Use `L()` for all text and always include `accessibilityLabel` for interactive elements.
|
|
94
|
+
|
|
95
|
+
```xml
|
|
96
|
+
<Label text="L('welcome_message')" accessibilityLabel="Welcome Header" />
|
|
97
|
+
<ImageView class="fa-solid fa-user" accessibilityLabel="User Profile Picture" />
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Icon Fonts (PurgeTSS Workflow)
|
|
101
|
+
Integrate icons using classes generated by `purgetss build-fonts`. Classes are mapped to both `text` and `title` properties.
|
|
102
|
+
|
|
103
|
+
```xml
|
|
104
|
+
<Label class="fa-solid fa-envelope mr-2 text-gray-400" />
|
|
105
|
+
<Button class="fas fa-play" title="L('play')" />
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Grid System
|
|
109
|
+
Use PurgeTSS percentage-based grid for alignments.
|
|
110
|
+
|
|
111
|
+
```xml
|
|
112
|
+
<View class="horizontal w-screen">
|
|
113
|
+
<View class="w-8/12 bg-red-500" />
|
|
114
|
+
<View class="w-4/12 bg-blue-500" />
|
|
115
|
+
</View>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
For true grid layouts, use the Grid component:
|
|
119
|
+
```xml
|
|
120
|
+
<View class="grid-cols-3 gap-2">
|
|
121
|
+
<View class="bg-red-500" />
|
|
122
|
+
<View class="bg-blue-500" />
|
|
123
|
+
<View class="bg-green-500" />
|
|
124
|
+
</View>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Animation Component
|
|
128
|
+
PurgeTSS includes an Animation component (`purgetss.ui`) for 2D Matrix transformations.
|
|
129
|
+
|
|
130
|
+
```xml
|
|
131
|
+
<Animation id="myAnim" module="purgetss.ui" class="close:opacity-0 duration-500 open:opacity-100" />
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Naming Conventions
|
|
135
|
+
|
|
136
|
+
### Files and Folders
|
|
137
|
+
```
|
|
138
|
+
app/
|
|
139
|
+
├── controllers/
|
|
140
|
+
│ ├── index.js # camelCase for files
|
|
141
|
+
│ ├── user/ # lowercase folders
|
|
142
|
+
│ │ ├── profile.js # feature-based grouping
|
|
143
|
+
│ │ └── settings.js
|
|
144
|
+
├── lib/
|
|
145
|
+
│ ├── api/
|
|
146
|
+
│ │ └── userApi.js # suffixed with type (Api, Service)
|
|
147
|
+
│ ├── services/
|
|
148
|
+
│ │ └── authService.js
|
|
149
|
+
│ └── helpers/
|
|
150
|
+
│ └── dateHelper.js
|
|
151
|
+
├── views/
|
|
152
|
+
│ └── user/
|
|
153
|
+
│ └── profile.xml # match controller path exactly
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Variables and Functions
|
|
157
|
+
```javascript
|
|
158
|
+
// Constants: UPPER_SNAKE_CASE
|
|
159
|
+
const API_BASE_URL = 'https://api.example.com'
|
|
160
|
+
const MAX_RETRY_COUNT = 3
|
|
161
|
+
const DEFAULT_TIMEOUT = 5000
|
|
162
|
+
|
|
163
|
+
// Variables: camelCase
|
|
164
|
+
const userId = $.args.userId
|
|
165
|
+
const isLoading = false
|
|
166
|
+
let currentPage = 1
|
|
167
|
+
|
|
168
|
+
// Functions: camelCase, verb prefix for actions
|
|
169
|
+
function loadUserData() { /* ... */ }
|
|
170
|
+
function handleLoginClick() { /* ... */ }
|
|
171
|
+
function validateEmail(email) { /* ... */ }
|
|
172
|
+
|
|
173
|
+
// Boolean variables: is/has/should prefix
|
|
174
|
+
const isAuthenticated = true
|
|
175
|
+
const hasPermission = false
|
|
176
|
+
const shouldRefresh = true
|
|
177
|
+
|
|
178
|
+
// Event handlers: on + Event
|
|
179
|
+
function onItemClick(e) { /* ... */ }
|
|
180
|
+
function onRefresh() { /* ... */ }
|
|
181
|
+
function onSearchChange(e) { /* ... */ }
|
|
182
|
+
|
|
183
|
+
// Private functions: underscore prefix (optional)
|
|
184
|
+
function _calculateTotal(items) { /* ... */ }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Classes and Services
|
|
188
|
+
```javascript
|
|
189
|
+
// Classes: PascalCase
|
|
190
|
+
class UserService {
|
|
191
|
+
constructor() { /* ... */ }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Singleton exports: camelCase instance
|
|
195
|
+
exports.userService = new UserService()
|
|
196
|
+
|
|
197
|
+
// Factory functions: create prefix
|
|
198
|
+
function createHTTPClient(config) { /* ... */ }
|
|
199
|
+
function createAnimationController() { /* ... */ }
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Events and Constants
|
|
203
|
+
```javascript
|
|
204
|
+
// Event names: namespace:action format
|
|
205
|
+
const Events = {
|
|
206
|
+
USER_LOGGED_IN: 'user:loggedIn',
|
|
207
|
+
USER_LOGGED_OUT: 'user:loggedOut',
|
|
208
|
+
CART_UPDATED: 'cart:updated',
|
|
209
|
+
NETWORK_ONLINE: 'network:online'
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Error codes: UPPER_SNAKE_CASE
|
|
213
|
+
const ErrorCodes = {
|
|
214
|
+
AUTH_ERROR: 'AUTH_ERROR',
|
|
215
|
+
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
216
|
+
VALIDATION_ERROR: 'VALIDATION_ERROR'
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Import/Export Patterns
|
|
221
|
+
|
|
222
|
+
### Named Exports (Preferred)
|
|
223
|
+
```javascript
|
|
224
|
+
// lib/api/userApi.js
|
|
225
|
+
exports.getUser = async function(id) { /* ... */ }
|
|
226
|
+
exports.updateUser = async function(id, data) { /* ... */ }
|
|
227
|
+
exports.deleteUser = async function(id) { /* ... */ }
|
|
228
|
+
|
|
229
|
+
// Usage
|
|
230
|
+
const { getUser, updateUser } = require('lib/api/userApi')
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Default Export (For Classes/Singletons)
|
|
234
|
+
```javascript
|
|
235
|
+
// lib/services/logger.js
|
|
236
|
+
class Logger {
|
|
237
|
+
debug(tag, message) { /* ... */ }
|
|
238
|
+
info(tag, message) { /* ... */ }
|
|
239
|
+
error(tag, message) { /* ... */ }
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = new Logger()
|
|
243
|
+
|
|
244
|
+
// Usage
|
|
245
|
+
const logger = require('lib/services/logger')
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Mixed Exports
|
|
249
|
+
```javascript
|
|
250
|
+
// lib/services/eventBus.js
|
|
251
|
+
const _ = require('alloy/underscore')._
|
|
252
|
+
const Backbone = require('alloy/backbone')
|
|
253
|
+
|
|
254
|
+
const EventBus = _.clone(Backbone.Events)
|
|
255
|
+
|
|
256
|
+
// Named export for event constants
|
|
257
|
+
exports.Events = {
|
|
258
|
+
USER_UPDATED: 'user:updated',
|
|
259
|
+
SYNC_COMPLETE: 'sync:complete'
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Default export for the bus itself
|
|
263
|
+
module.exports = EventBus
|
|
264
|
+
|
|
265
|
+
// Usage
|
|
266
|
+
const EventBus = require('lib/services/eventBus')
|
|
267
|
+
const { Events } = EventBus
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Re-exports (Barrel Files)
|
|
271
|
+
```javascript
|
|
272
|
+
// lib/api/index.js - Barrel file
|
|
273
|
+
exports.userApi = require('./userApi').userApi
|
|
274
|
+
exports.productApi = require('./productApi').productApi
|
|
275
|
+
exports.orderApi = require('./orderApi').orderApi
|
|
276
|
+
|
|
277
|
+
// Usage - cleaner imports
|
|
278
|
+
const { userApi, productApi } = require('lib/api')
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### CommonJS Compatibility
|
|
282
|
+
```javascript
|
|
283
|
+
// When using native modules that use require()
|
|
284
|
+
const SomeNativeModule = require('ti.somemodule')
|
|
285
|
+
|
|
286
|
+
// Wrap in ES6 service
|
|
287
|
+
exports.nativeService = {
|
|
288
|
+
doSomething() {
|
|
289
|
+
return SomeNativeModule.performAction()
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Async/Promise Patterns
|
|
295
|
+
|
|
296
|
+
### Async/Await (Preferred)
|
|
297
|
+
```javascript
|
|
298
|
+
// Service layer - always async
|
|
299
|
+
exports.fetchUserProfile = async function(userId) {
|
|
300
|
+
const user = await userApi.getById(userId)
|
|
301
|
+
const preferences = await preferencesApi.getByUser(userId)
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
...user,
|
|
305
|
+
preferences
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Controller - with error handling
|
|
310
|
+
async function loadData() {
|
|
311
|
+
setLoading(true)
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
const data = await service.fetchData()
|
|
315
|
+
renderData(data)
|
|
316
|
+
} catch (error) {
|
|
317
|
+
handleError(error)
|
|
318
|
+
} finally {
|
|
319
|
+
setLoading(false)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Parallel Requests
|
|
325
|
+
```javascript
|
|
326
|
+
// When requests are independent - run in parallel
|
|
327
|
+
async function loadDashboard() {
|
|
328
|
+
const [user, stats, notifications] = await Promise.all([
|
|
329
|
+
userService.getCurrentUser(),
|
|
330
|
+
statsService.getDashboardStats(),
|
|
331
|
+
notificationService.getUnread()
|
|
332
|
+
])
|
|
333
|
+
|
|
334
|
+
renderDashboard({ user, stats, notifications })
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Sequential with Dependencies
|
|
339
|
+
```javascript
|
|
340
|
+
// When each request depends on the previous
|
|
341
|
+
async function processOrder(cartId) {
|
|
342
|
+
// 1. Get cart (needed for order)
|
|
343
|
+
const cart = await cartService.getById(cartId)
|
|
344
|
+
|
|
345
|
+
// 2. Create order (needs cart data)
|
|
346
|
+
const order = await orderService.create(cart)
|
|
347
|
+
|
|
348
|
+
// 3. Process payment (needs order)
|
|
349
|
+
const payment = await paymentService.process(order.id)
|
|
350
|
+
|
|
351
|
+
return { order, payment }
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Error Handling with Specific Catches
|
|
356
|
+
```javascript
|
|
357
|
+
async function login(email, password) {
|
|
358
|
+
try {
|
|
359
|
+
const result = await authService.login(email, password)
|
|
360
|
+
return result
|
|
361
|
+
|
|
362
|
+
} catch (error) {
|
|
363
|
+
// Handle specific error types differently
|
|
364
|
+
if (error.code === 'AUTH_ERROR') {
|
|
365
|
+
showMessage(L('invalid_credentials'))
|
|
366
|
+
} else if (error.code === 'NETWORK_ERROR') {
|
|
367
|
+
showMessage(L('check_connection'))
|
|
368
|
+
} else {
|
|
369
|
+
showMessage(L('unknown_error'))
|
|
370
|
+
logger.error('Login', 'Unexpected error', { error: error.message })
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
throw error // Re-throw for caller to handle if needed
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Converting Callbacks to Promises
|
|
379
|
+
```javascript
|
|
380
|
+
// Wrap Ti.Network.createHTTPClient in Promise
|
|
381
|
+
function httpGet(url) {
|
|
382
|
+
return new Promise((resolve, reject) => {
|
|
383
|
+
const client = Ti.Network.createHTTPClient({
|
|
384
|
+
onload: function() {
|
|
385
|
+
try {
|
|
386
|
+
resolve(JSON.parse(this.responseText))
|
|
387
|
+
} catch (e) {
|
|
388
|
+
reject(new Error('Invalid JSON response'))
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
onerror: function(e) {
|
|
392
|
+
reject(new Error(e.error || 'Request failed'))
|
|
393
|
+
},
|
|
394
|
+
timeout: 10000
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
client.open('GET', url)
|
|
398
|
+
client.send()
|
|
399
|
+
})
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Wrap Ti.Media.showCamera in Promise
|
|
403
|
+
function takePhoto() {
|
|
404
|
+
return new Promise((resolve, reject) => {
|
|
405
|
+
Ti.Media.showCamera({
|
|
406
|
+
success: (e) => resolve(e.media),
|
|
407
|
+
cancel: () => reject(new Error('User cancelled')),
|
|
408
|
+
error: (e) => reject(new Error(e.error))
|
|
409
|
+
})
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Debouncing Async Operations
|
|
415
|
+
```javascript
|
|
416
|
+
// Useful for search, auto-save, etc.
|
|
417
|
+
let searchTimeout = null
|
|
418
|
+
|
|
419
|
+
function onSearchChange(e) {
|
|
420
|
+
clearTimeout(searchTimeout)
|
|
421
|
+
|
|
422
|
+
searchTimeout = setTimeout(async () => {
|
|
423
|
+
const query = e.value.trim()
|
|
424
|
+
|
|
425
|
+
if (query.length < 2) {
|
|
426
|
+
return renderResults([])
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
const results = await searchService.search(query)
|
|
431
|
+
renderResults(results)
|
|
432
|
+
} catch (error) {
|
|
433
|
+
// Ignore errors for outdated searches
|
|
434
|
+
logger.debug('Search', 'Search failed', { query })
|
|
435
|
+
}
|
|
436
|
+
}, 300)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function cleanup() {
|
|
440
|
+
clearTimeout(searchTimeout)
|
|
441
|
+
$.destroy()
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Retry Pattern
|
|
446
|
+
```javascript
|
|
447
|
+
async function fetchWithRetry(fn, maxRetries = 3) {
|
|
448
|
+
let lastError
|
|
449
|
+
|
|
450
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
451
|
+
try {
|
|
452
|
+
return await fn()
|
|
453
|
+
} catch (error) {
|
|
454
|
+
lastError = error
|
|
455
|
+
|
|
456
|
+
// Exponential backoff
|
|
457
|
+
const delay = Math.pow(2, i) * 1000
|
|
458
|
+
await new Promise(resolve => setTimeout(resolve, delay))
|
|
459
|
+
|
|
460
|
+
logger.warn('Retry', `Attempt ${i + 1} failed`, { error: error.message })
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
throw lastError
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Usage
|
|
468
|
+
const data = await fetchWithRetry(() => api.getData())
|
|
469
|
+
```
|