@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.
Files changed (120) hide show
  1. package/AGENTS-TEMPLATE.md +173 -0
  2. package/README.md +867 -0
  3. package/agents/ti-researcher.md +108 -0
  4. package/bin/titools.js +53 -0
  5. package/lib/commands/agents.js +126 -0
  6. package/lib/commands/install.js +188 -0
  7. package/lib/commands/uninstall.js +215 -0
  8. package/lib/commands/update.js +159 -0
  9. package/lib/config.js +119 -0
  10. package/lib/downloader.js +153 -0
  11. package/lib/installer.js +253 -0
  12. package/lib/platform.js +108 -0
  13. package/lib/symlink.js +142 -0
  14. package/lib/utils.js +270 -0
  15. package/package.json +67 -0
  16. package/skills/alloy-expert/SKILL.md +247 -0
  17. package/skills/alloy-expert/assets/ControllerAutoCleanup.js +182 -0
  18. package/skills/alloy-expert/references/alloy-structure.md +381 -0
  19. package/skills/alloy-expert/references/anti-patterns.md +133 -0
  20. package/skills/alloy-expert/references/code-conventions.md +469 -0
  21. package/skills/alloy-expert/references/contracts.md +280 -0
  22. package/skills/alloy-expert/references/controller-patterns.md +520 -0
  23. package/skills/alloy-expert/references/error-handling.md +484 -0
  24. package/skills/alloy-expert/references/examples.md +735 -0
  25. package/skills/alloy-expert/references/migration-patterns.md +298 -0
  26. package/skills/alloy-expert/references/patterns.md +448 -0
  27. package/skills/alloy-expert/references/performance-patterns.md +855 -0
  28. package/skills/alloy-expert/references/security-patterns.md +847 -0
  29. package/skills/alloy-expert/references/state-management.md +779 -0
  30. package/skills/alloy-expert/references/testing.md +872 -0
  31. package/skills/alloy-guides/SKILL.md +214 -0
  32. package/skills/alloy-guides/references/CLI_TASKS.md +243 -0
  33. package/skills/alloy-guides/references/CONCEPTS.md +191 -0
  34. package/skills/alloy-guides/references/CONTROLLERS.md +298 -0
  35. package/skills/alloy-guides/references/MODELS.md +1028 -0
  36. package/skills/alloy-guides/references/PURGETSS.md +56 -0
  37. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +242 -0
  38. package/skills/alloy-guides/references/VIEWS_STYLES.md +388 -0
  39. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +109 -0
  40. package/skills/alloy-guides/references/VIEWS_XML.md +558 -0
  41. package/skills/alloy-guides/references/WIDGETS.md +176 -0
  42. package/skills/alloy-howtos/SKILL.md +203 -0
  43. package/skills/alloy-howtos/references/best_practices.md +138 -0
  44. package/skills/alloy-howtos/references/cli_reference.md +253 -0
  45. package/skills/alloy-howtos/references/config_files.md +87 -0
  46. package/skills/alloy-howtos/references/custom_tags.md +147 -0
  47. package/skills/alloy-howtos/references/debugging_troubleshooting.md +101 -0
  48. package/skills/alloy-howtos/references/samples.md +167 -0
  49. package/skills/purgetss/SKILL.md +442 -0
  50. package/skills/purgetss/assets/purgetss.config.cjs +17 -0
  51. package/skills/purgetss/references/EXAMPLES.md +247 -0
  52. package/skills/purgetss/references/animation-system.md +1294 -0
  53. package/skills/purgetss/references/apply-directive.md +375 -0
  54. package/skills/purgetss/references/arbitrary-values.md +612 -0
  55. package/skills/purgetss/references/class-index.md +1350 -0
  56. package/skills/purgetss/references/cli-commands.md +948 -0
  57. package/skills/purgetss/references/configurable-properties.md +654 -0
  58. package/skills/purgetss/references/custom-rules.md +161 -0
  59. package/skills/purgetss/references/customization-deep-dive.md +722 -0
  60. package/skills/purgetss/references/dynamic-component-creation.md +489 -0
  61. package/skills/purgetss/references/grid-layout.md +455 -0
  62. package/skills/purgetss/references/icon-fonts.md +609 -0
  63. package/skills/purgetss/references/installation-setup.md +366 -0
  64. package/skills/purgetss/references/opacity-modifier.md +291 -0
  65. package/skills/purgetss/references/platform-modifiers.md +479 -0
  66. package/skills/purgetss/references/smart-mappings.md +42 -0
  67. package/skills/purgetss/references/titanium-resets.md +359 -0
  68. package/skills/purgetss/references/ui-ux-design.md +1526 -0
  69. package/skills/ti-guides/SKILL.md +94 -0
  70. package/skills/ti-guides/references/advanced-data-and-images.md +19 -0
  71. package/skills/ti-guides/references/alloy-cli-advanced.md +84 -0
  72. package/skills/ti-guides/references/alloy-data-mastery.md +29 -0
  73. package/skills/ti-guides/references/alloy-widgets-and-themes.md +19 -0
  74. package/skills/ti-guides/references/android-manifest.md +97 -0
  75. package/skills/ti-guides/references/app-distribution.md +258 -0
  76. package/skills/ti-guides/references/application-frameworks.md +377 -0
  77. package/skills/ti-guides/references/cli-reference.md +402 -0
  78. package/skills/ti-guides/references/coding-best-practices.md +102 -0
  79. package/skills/ti-guides/references/commonjs-advanced.md +134 -0
  80. package/skills/ti-guides/references/hello-world.md +100 -0
  81. package/skills/ti-guides/references/hyperloop-native-access.md +62 -0
  82. package/skills/ti-guides/references/javascript-primer.md +411 -0
  83. package/skills/ti-guides/references/reserved-words.md +36 -0
  84. package/skills/ti-guides/references/resources.md +183 -0
  85. package/skills/ti-guides/references/style-and-conventions.md +48 -0
  86. package/skills/ti-guides/references/tiapp-config.md +609 -0
  87. package/skills/ti-howtos/SKILL.md +174 -0
  88. package/skills/ti-howtos/references/android-platform-deep-dives.md +658 -0
  89. package/skills/ti-howtos/references/automation-fastlane-appium.md +95 -0
  90. package/skills/ti-howtos/references/buffer-codec-streams.md +140 -0
  91. package/skills/ti-howtos/references/cross-platform-development.md +348 -0
  92. package/skills/ti-howtos/references/debugging-profiling.md +543 -0
  93. package/skills/ti-howtos/references/extending-titanium.md +723 -0
  94. package/skills/ti-howtos/references/google-maps-v2.md +169 -0
  95. package/skills/ti-howtos/references/ios-map-kit.md +143 -0
  96. package/skills/ti-howtos/references/ios-platform-deep-dives.md +783 -0
  97. package/skills/ti-howtos/references/local-data-sources.md +301 -0
  98. package/skills/ti-howtos/references/location-and-maps.md +252 -0
  99. package/skills/ti-howtos/references/media-apis.md +210 -0
  100. package/skills/ti-howtos/references/notification-services.md +599 -0
  101. package/skills/ti-howtos/references/remote-data-sources.md +349 -0
  102. package/skills/ti-howtos/references/tutorials.md +502 -0
  103. package/skills/ti-howtos/references/using-modules.md +237 -0
  104. package/skills/ti-howtos/references/web-content-integration.md +307 -0
  105. package/skills/ti-howtos/references/webpack-build-pipeline.md +78 -0
  106. package/skills/ti-ui/SKILL.md +179 -0
  107. package/skills/ti-ui/references/accessibility-deep-dive.md +242 -0
  108. package/skills/ti-ui/references/animation-and-matrices.md +599 -0
  109. package/skills/ti-ui/references/application-structures.md +655 -0
  110. package/skills/ti-ui/references/custom-fonts-styling.md +579 -0
  111. package/skills/ti-ui/references/event-handling.md +393 -0
  112. package/skills/ti-ui/references/gestures.md +473 -0
  113. package/skills/ti-ui/references/icons-and-splash-screens.md +409 -0
  114. package/skills/ti-ui/references/layouts-and-positioning.md +462 -0
  115. package/skills/ti-ui/references/listviews-and-performance.md +619 -0
  116. package/skills/ti-ui/references/orientation.md +362 -0
  117. package/skills/ti-ui/references/platform-ui-android.md +635 -0
  118. package/skills/ti-ui/references/platform-ui-ios.md +469 -0
  119. package/skills/ti-ui/references/scrolling-views.md +252 -0
  120. package/skills/ti-ui/references/tableviews.md +568 -0
@@ -0,0 +1,247 @@
1
+ ---
2
+ name: alloy-expert
3
+ description: "Titanium Alloy architecture and implementation expert. Use when designing, reviewing, analyzing, or examining Alloy project structure, creating controllers/views/services, choosing models vs collections, implementing communication patterns, handling memory cleanup, testing, auditing code, or migrating legacy apps. Detects Alloy vs Classic projects automatically."
4
+ argument-hint: "[architecture-topic]"
5
+ allowed-tools: Read, Grep, Glob, Edit, Write, Bash(git *), Bash(node *)
6
+ ---
7
+
8
+ # Titanium Alloy Expert
9
+
10
+ Complete architectural and implementation guidance for building scalable, maintainable Titanium Alloy applications with PurgeTSS styling.
11
+
12
+ ## Project Detection
13
+
14
+ :::info AUTO-DETECTS ALLOY VS CLASSIC PROJECTS
15
+ This skill automatically detects project type when invoked and provides appropriate guidance.
16
+
17
+ **Detection occurs automatically** - no manual command needed.
18
+
19
+ **Alloy project indicators:**
20
+ - `app/` folder (MVC structure)
21
+ - `app/views/`, `app/controllers/` folders
22
+ - `alloy.jmk` or `config.json` files
23
+
24
+ **Classic project indicators:**
25
+ - `Resources/` folder with `app.js` at root
26
+ - No `app/` folder structure
27
+
28
+ **Behavior based on detection:**
29
+ - **Alloy detected** → Provides Alloy-specific architecture patterns, MVC folder structure, Backbone.js patterns
30
+ - **Classic detected** → Indicates incompatibility, does not suggest Alloy-specific patterns, recommends migration or Classic resources
31
+ - **Unknown** → Asks user to clarify project type
32
+ :::
33
+
34
+ ## Table of Contents
35
+
36
+ - [Titanium Alloy Expert](#titanium-alloy-expert)
37
+ - [Project Detection](#project-detection)
38
+ - [Table of Contents](#table-of-contents)
39
+ - [Workflow](#workflow)
40
+ - [Quick Start Example](#quick-start-example)
41
+ - [Code Standards (Low Freedom)](#code-standards-low-freedom)
42
+ - [PurgeTSS Rules (Low Freedom)](#purgetss-rules-low-freedom)
43
+ - [Quick Decision Matrix](#quick-decision-matrix)
44
+ - [Reference Guides (Progressive Disclosure)](#reference-guides-progressive-disclosure)
45
+ - [Architecture](#architecture)
46
+ - [Implementation](#implementation)
47
+ - [Quality \& Reliability](#quality--reliability)
48
+ - [Performance \& Security](#performance--security)
49
+ - [Migration](#migration)
50
+ - [Specialized Titanium Skills](#specialized-titanium-skills)
51
+ - [Guiding Principles](#guiding-principles)
52
+ - [Response Format](#response-format)
53
+
54
+ ---
55
+
56
+ ## Workflow
57
+
58
+ 1. **Architecture**: Define structure (`lib/api`, `lib/services`, `lib/helpers`)
59
+ 2. **Data Strategy**: Choose Models (SQLite) or Collections (API)
60
+ 3. **Contracts**: Define I/O specs for cross-layer communication
61
+ 4. **Implementation**: Write XML views + ES6+ controllers
62
+ 5. **Quality**: Testing, error handling, logging, performance
63
+ 6. **Cleanup**: Implement `cleanup()` pattern for memory management
64
+
65
+ ## Quick Start Example
66
+
67
+ Minimal example following all conventions:
68
+
69
+ **View (views/user/card.xml)**
70
+ ```xml
71
+ <Alloy>
72
+ <View class="m-2 rounded-xl bg-white shadow-md">
73
+ <View class="horizontal m-3 w-screen">
74
+ <Label class="fa-solid fa-user text-2xl text-blue-500" />
75
+ <Label id="name" class="ml-3 text-lg font-bold" />
76
+ </View>
77
+ <Button class="mx-3 mb-3 h-10 w-screen rounded-md bg-blue-600 text-white"
78
+ title="L('view_profile')"
79
+ onClick="onViewProfile"
80
+ />
81
+ </View>
82
+ </Alloy>
83
+ ```
84
+
85
+ **Controller (controllers/user/card.js)**
86
+ ```javascript
87
+ const { Navigation } = require('lib/services/navigation')
88
+
89
+ function init() {
90
+ const user = $.args.user
91
+ $.name.text = user.name
92
+ }
93
+
94
+ function onViewProfile() {
95
+ Navigation.open('user/profile', { userId: $.args.user.id })
96
+ }
97
+
98
+ function cleanup() {
99
+ $.destroy()
100
+ }
101
+
102
+ $.cleanup = cleanup
103
+ ```
104
+
105
+ **Service (lib/services/navigation.js)**
106
+ ```javascript
107
+ exports.Navigation = {
108
+ open(route, params = {}) {
109
+ const controller = Alloy.createController(route, params)
110
+ const view = controller.getView()
111
+
112
+ view.addEventListener('close', function() {
113
+ if (controller.cleanup) controller.cleanup()
114
+ })
115
+
116
+ view.open()
117
+ return controller
118
+ }
119
+ }
120
+ ```
121
+
122
+ ## Code Standards (Low Freedom)
123
+
124
+ - **NO SEMICOLONS**: Let ASI handle it
125
+ - **MODERN SYNTAX**: `const/let`, destructuring, template literals
126
+ - **applyProperties()**: Batch UI updates to minimize bridge crossings
127
+ - **MEMORY CLEANUP**: Every controller with global listeners MUST have `$.cleanup = cleanup`
128
+ - **ERROR HANDLING**: Use AppError classes, log with context, never swallow errors
129
+ - **TESTABLE**: Inject dependencies, avoid hard coupling
130
+
131
+ ## PurgeTSS Rules (Low Freedom)
132
+
133
+ :::danger CRITICAL: Platform-Specific Properties Require Modifiers
134
+ Using `Ti.UI.iOS.*` or `Ti.UI.Android.*` properties WITHOUT platform modifiers causes cross-platform compilation failures.
135
+
136
+ **Example of the damage:**
137
+ ```tss
138
+ // ❌ WRONG - Adds Ti.UI.iOS to Android project
139
+ "#mainWindow": {
140
+ statusBarStyle: Ti.UI.iOS.StatusBar.LIGHT_CONTENT // FAILS on Android!
141
+ }
142
+ ```
143
+
144
+ **CORRECT - Always use platform modifiers:**
145
+ ```tss
146
+ // ✅ CORRECT - Only adds to iOS
147
+ "#mainWindow[platform=ios]": {
148
+ statusBarStyle: Ti.UI.iOS.StatusBar.LIGHT_CONTENT
149
+ }
150
+ ```
151
+
152
+ **Properties that ALWAYS require platform modifiers:**
153
+ - iOS: `statusBarStyle`, `modalStyle`, `modalTransitionStyle`, any `Ti.UI.iOS.*`
154
+ - Android: `actionBar` configuration, any `Ti.UI.Android.*` constant
155
+
156
+ **Available modifiers:** `[platform=ios]`, `[platform=android]`, `[formFactor=handheld]`, `[formFactor=tablet]`, `[if=Alloy.Globals.customVar]`
157
+
158
+ **For more platform-specific patterns, see** [Platform Modifiers (purgetss)](skills/purgetss/references/platform-modifiers.md) or [Platform UI guides (ti-ui)](skills/ti-ui/references/platform-ui-ios.md).
159
+ :::
160
+
161
+ | WRONG | CORRECT | Why |
162
+ | ------------------------------ | ------------------- | ----------------------------- |
163
+ | `flex-row` | `horizontal` | Flexbox not supported |
164
+ | `flex-col` | `vertical` | Flexbox not supported |
165
+ | `p-4` on View | `m-4` on children | No padding on containers |
166
+ | `justify-*` | margins/positioning | Flexbox not supported |
167
+ | `items-center` (for centering) | layout + sizing | Different meaning in Titanium |
168
+ | `rounded-full` (for circle) | `rounded-full-12` | Need size suffix (12×4=48px) |
169
+ | `border-[1px]` | `border-(1)` | Parentheses, not brackets |
170
+
171
+ **Note on `w-full` vs `w-screen`:**
172
+ - `w-full` → `width: '100%'` (100% of parent container) - exists and valid
173
+ - `w-screen` → `width: Ti.UI.FILL` (fills all available space) - use for full-width elements
174
+
175
+ ## Quick Decision Matrix
176
+
177
+ | Question | Answer |
178
+ | ---------------------------------- | -------------------------------------------------------------- |
179
+ | How to create a new Alloy project? | **`ti create -t app --alloy`** (not `--classic` + `alloy new`) |
180
+ | Where does API call go? | `lib/api/` |
181
+ | Where does business logic go? | `lib/services/` |
182
+ | Where do I store auth tokens? | Keychain (iOS) / KeyStore (Android) via service |
183
+ | Models or Collections? | Collections for API data, Models for SQLite persistence |
184
+ | Ti.App.fireEvent or EventBus? | **Always EventBus** (Backbone.Events) |
185
+ | Direct navigation or service? | **Always Navigation service** (auto cleanup) |
186
+ | Manual TSS or PurgeTSS? | **Always PurgeTSS utility classes** |
187
+ | Controller 100+ lines? | Extract logic to services |
188
+
189
+ ## Reference Guides (Progressive Disclosure)
190
+
191
+ ### Architecture
192
+ - **[Structure & Organization](references/alloy-structure.md)**: Models vs Collections, folder maps, styling strategies, automatic cleanup
193
+ - **[ControllerAutoCleanup.js](assets/ControllerAutoCleanup.js)**: Drop-in utility for automatic controller cleanup (prevents memory leaks)
194
+ - **[Architectural Patterns](references/patterns.md)**: Repository, Service Layer, Event Bus, Factory, Singleton
195
+ - **[Contracts & Communication](references/contracts.md)**: Layer interaction examples and JSDoc specs
196
+ - **[Anti-Patterns](references/anti-patterns.md)**: Fat controllers, flexbox classes, memory leaks, direct API calls
197
+
198
+ ### Implementation
199
+ - **[Code Conventions](references/code-conventions.md)**: ES6 features, PurgeTSS usage, accessibility
200
+ - **[Controller Patterns](references/controller-patterns.md)**: Templates, animation, dynamic styling
201
+ - **[Examples](references/examples.md)**: API clients, SQL models, full screen examples
202
+
203
+ ### Quality & Reliability
204
+ - **[Testing](references/testing.md)**: Unit testing, mocking patterns, controller testing, test helpers
205
+ - **[Error Handling & Logging](references/error-handling.md)**: AppError classes, Logger service, validation
206
+
207
+ ### Performance & Security
208
+ - **[Performance Patterns](references/performance-patterns.md)**: ListView, bridge optimization, memory management, lazy loading
209
+ - **[Security Patterns](references/security-patterns.md)**: Token storage, certificate pinning, encryption, OWASP
210
+ - **[State Management](references/state-management.md)**: Centralized store, reactive patterns, synchronization
211
+
212
+ ### Migration
213
+ - **[Migration Patterns](references/migration-patterns.md)**: Step-by-step guide for modernizing legacy apps
214
+
215
+ ## Specialized Titanium Skills
216
+
217
+ For specific feature implementations, defer to these specialized skills:
218
+
219
+ | Task | Use This Skill |
220
+ | ----------------------------------------------------- | -------------- |
221
+ | PurgeTSS setup, advanced styling, animations | `purgetss` |
222
+ | Location, Maps, Push Notifications, Media APIs | `ti-howtos` |
223
+ | UI layouts, ListViews, gestures, platform-specific UI | `ti-ui` |
224
+ | Alloy CLI, configuration files, debugging | `alloy-howtos` |
225
+ | Alloy MVC complete reference | `alloy-guides` |
226
+ | Hyperloop, native modules, app distribution | `ti-guides` |
227
+
228
+ ## Guiding Principles
229
+
230
+ 1. **Thin Controllers**: Max 100 lines. Delegate to services.
231
+ 2. **Single Source of Truth**: One state store, not scattered Properties.
232
+ 3. **Always Cleanup**: Every listener added = listener removed in `cleanup()`.
233
+ 4. **Never Block UI**: All API/DB calls are async with loading states.
234
+ 5. **Fail Gracefully**: Centralized error handling with user-friendly messages.
235
+
236
+ ## Response Format
237
+
238
+ **For Architecture Questions:**
239
+ 1. Decision: What should be done
240
+ 2. Justification: Technical rationale
241
+ 3. Structure: File and folder placement
242
+ 4. Contract: Clear I/O specification
243
+
244
+ **For Implementation Tasks:**
245
+ 1. Code First: Show implementation immediately
246
+ 2. Minimal Comments: Explain only the "Why" for complex logic
247
+ 3. No Explanations: Deliver exactly what was asked concisely
@@ -0,0 +1,182 @@
1
+ /**
2
+ * ControllerAutoCleanup - Automatic Controller Cleanup for Alloy
3
+ *
4
+ * Monkey-patches Alloy.createController to automatically cleanup
5
+ * controllers when their views are closed, preventing memory leaks.
6
+ *
7
+ * INSTALLATION:
8
+ * 1. Copy this file to your app: app/lib/ControllerAutoCleanup.js
9
+ * 2. Add as first line in alloy.js: require('ControllerAutoCleanup')
10
+ *
11
+ * No other code changes needed - cleanup happens automatically.
12
+ *
13
+ * @see https://github.com/jasonkneen/AlloyXL original inspiration
14
+ */
15
+
16
+ 'use strict'
17
+
18
+ // Logger (optional - remove if not using)
19
+ let log = null
20
+ try {
21
+ log = require('Logger')?.create('CONTROLLER_CLEANUP')
22
+ } catch (e) {
23
+ // Fallback simple logger
24
+ log = {
25
+ debug: (msg, data) => Ti.API.debug(`[ControllerCleanup] ${msg}`, data),
26
+ info: (msg, data) => Ti.API.info(`[ControllerCleanup] ${msg}`, data),
27
+ warn: (msg, data) => Ti.API.warn(`[ControllerCleanup] ${msg}`, data),
28
+ error: (msg, data) => Ti.API.error(`[ControllerCleanup] ${msg}`, data)
29
+ }
30
+ }
31
+
32
+ // Store original Alloy.createController
33
+ const originalCreateController = Alloy.createController
34
+
35
+ /**
36
+ * Recursively cleanup a controller and all its child views
37
+ * @param {Object} controller - Alloy controller to cleanup
38
+ */
39
+ function cleanupController(controller) {
40
+ if (!controller) return
41
+
42
+ // Cleanup child views first (recursive)
43
+ if (controller.__views) {
44
+ Object.values(controller.__views).forEach((childView) => {
45
+ if (childView && typeof childView === 'object') {
46
+ cleanupController(childView)
47
+ }
48
+ })
49
+ }
50
+
51
+ // Only cleanup actual Alloy controllers (not plain views)
52
+ if (controller.__iamalloy) {
53
+ // Remove all Backbone event listeners
54
+ if (typeof controller.off === 'function') {
55
+ controller.off()
56
+ }
57
+
58
+ // Call Alloy's destroy method
59
+ if (typeof controller.destroy === 'function') {
60
+ controller.destroy()
61
+ }
62
+
63
+ log?.debug('Controller cleaned up', { id: controller.id })
64
+ }
65
+
66
+ // Clear reference
67
+ controller = null
68
+ }
69
+
70
+ /**
71
+ * Enhanced Alloy.createController with automatic cleanup
72
+ * @param {string} name - Controller name/path
73
+ * @param {Object} args - Arguments to pass to controller
74
+ * @returns {Object} Alloy controller
75
+ */
76
+ function createController(name, args = {}) {
77
+ try {
78
+ const controller = originalCreateController(name, args)
79
+
80
+ // Only process controllers with views
81
+ if (!controller.__views || Object.keys(controller.__views).length === 0) {
82
+ return controller
83
+ }
84
+
85
+ const view = controller.getView()
86
+ const controllerName = name.split('/').pop() || name
87
+ const viewId = view?.id
88
+
89
+ // Track controllers globally (optional, for debugging)
90
+ Alloy.Controllers = Alloy.Controllers || {}
91
+
92
+ // Warn about controller name conflicts
93
+ if (Alloy.Controllers[controllerName] && !viewId) {
94
+ log?.warn('Controller name conflict - consider using unique IDs', {
95
+ controller: controllerName,
96
+ path: controller.__controllerPath
97
+ })
98
+ }
99
+
100
+ // Use view ID as key if available, otherwise controller name
101
+ const registryKey = viewId || controllerName
102
+ Alloy.Controllers[registryKey] = controller
103
+
104
+ /**
105
+ * Add `once()` helper - listen to event one time only
106
+ * @param {string} eventName - Event name
107
+ * @param {Function} callback - Callback function
108
+ * @returns {Object} controller for chaining
109
+ */
110
+ controller.once = function (eventName, callback) {
111
+ const wrapper = (...args) => {
112
+ controller.off(eventName, wrapper)
113
+ callback(...args)
114
+ }
115
+ controller.on(eventName, wrapper)
116
+ return controller
117
+ }
118
+
119
+ // Only add auto-cleanup to views with addEventListener
120
+ if (view && typeof view.addEventListener === 'function') {
121
+ // Window-like views with open() method
122
+ if (typeof view.open === 'function') {
123
+ view.addEventListener('open', function onOpen(e) {
124
+ view.removeEventListener('open', onOpen)
125
+ controller.trigger('open', e)
126
+ log?.debug('Controller opened', { name: controllerName })
127
+ })
128
+
129
+ view.addEventListener('close', function onClose() {
130
+ view.removeEventListener('close', onClose)
131
+ view = null
132
+
133
+ cleanupController(controller)
134
+ delete Alloy.Controllers[registryKey]
135
+
136
+ log?.debug('Controller cleaned up', { name: controllerName })
137
+ })
138
+
139
+ view.addEventListener('postlayout', function onPostLayout(e) {
140
+ view.removeEventListener('postlayout', onPostLayout)
141
+ controller.trigger('postlayout', e)
142
+ log?.debug('Controller layout finished', { name: controllerName })
143
+ })
144
+ } else {
145
+ // View-like views without open() (Widget, etc.)
146
+ view.addEventListener('postlayout', function onPostLayout(e) {
147
+ view.removeEventListener('postlayout', onPostLayout)
148
+ controller.trigger('postlayout', e)
149
+ log?.debug('View layout finished', { name: controllerName })
150
+ })
151
+ }
152
+ }
153
+
154
+ return controller
155
+
156
+ } catch (error) {
157
+ log?.error('Error creating controller', {
158
+ name,
159
+ error: error.message,
160
+ stack: error.stack
161
+ })
162
+ throw error
163
+ }
164
+ }
165
+
166
+ // Replace Alloy.createController with enhanced version
167
+ Alloy.createController = createController
168
+
169
+ log?.info('ControllerAutoCleanup loaded - automatic cleanup enabled')
170
+
171
+ // Export createController for direct use if needed
172
+ exports.createController = createController
173
+
174
+ /**
175
+ * USAGE:
176
+ *
177
+ * // alloy.js - Load as first line
178
+ * require('ControllerAutoCleanup')
179
+ *
180
+ * // All controllers now auto-cleanup on close
181
+ * Alloy.createController('profile').getView().open()
182
+ */