@fleetbase/ember-core 0.3.9 → 0.3.11
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/addon/exports/host-services.js +1 -0
- package/addon/exports/services.js +1 -0
- package/addon/library/subject-custom-fields.js +1 -1
- package/addon/services/app-cache.js +2 -1
- package/addon/services/crud.js +19 -0
- package/addon/services/current-user.js +27 -25
- package/addon/services/events.js +322 -0
- package/addon/services/legacy-universe.js +0 -1
- package/addon/services/resource-action.js +18 -0
- package/addon/services/universe/extension-manager.js +8 -7
- package/addon/services/universe/hook-service.js +9 -7
- package/addon/services/universe/registry-service.js +11 -16
- package/addon/services/universe.js +1 -1
- package/addon/utils/inject-engine-service.js +1 -1
- package/app/services/events.js +1 -0
- package/package.json +1 -1
- package/BOOT_SEQUENCE_REFACTOR_GUIDE.md +0 -294
- package/UNIVERSE_REFACTOR_MIGRATION_GUIDE.md +0 -318
- package/UNIVERSE_REFACTOR_README.md +0 -220
|
@@ -212,7 +212,7 @@ export default class UniverseService extends Service.extend(Evented) {
|
|
|
212
212
|
* @returns {ApplicationInstance} The application instance
|
|
213
213
|
*/
|
|
214
214
|
getApplicationInstance() {
|
|
215
|
-
return this.applicationInstance
|
|
215
|
+
return this.applicationInstance;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
/**
|
|
@@ -35,7 +35,7 @@ function automaticServiceResolution(service, target, owner) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function _getOwner(target) {
|
|
38
|
-
return
|
|
38
|
+
return getOwner(target);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export default function injectEngineService(target, engineName, serviceName, options = {}) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-core/services/events';
|
package/package.json
CHANGED
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
# Boot Sequence Refactor Guide
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
This guide provides the steps to refactor the application boot sequence to enable true lazy loading and move away from the old `bootEngines` mechanism that loads all extensions at startup.
|
|
6
|
-
|
|
7
|
-
## Understanding the Extension Loading Flow
|
|
8
|
-
|
|
9
|
-
The Fleetbase application has a three-tier extension loading system:
|
|
10
|
-
|
|
11
|
-
1. **pnpm Installation**: All extensions are installed via pnpm, making them available to the application
|
|
12
|
-
2. **System Configuration**: Extensions defined in `fleetbase.config.js` or `EXTENSIONS` environment variable are loaded globally
|
|
13
|
-
3. **User Permissions**: Individual users can install/uninstall extensions, which affects what loads for them specifically
|
|
14
|
-
|
|
15
|
-
Only extensions that are both installed AND enabled (via config or user permissions) will be initialized.
|
|
16
|
-
|
|
17
|
-
## The Goal
|
|
18
|
-
|
|
19
|
-
Stop loading all extension code at boot time. Instead:
|
|
20
|
-
- Load only the `extension.js` files (metadata registration)
|
|
21
|
-
- Keep engine bundles lazy-loaded (loaded on-demand when routes are visited)
|
|
22
|
-
- Preserve the `engines` property required by ember-engines for lazy loading
|
|
23
|
-
|
|
24
|
-
## Key Changes
|
|
25
|
-
|
|
26
|
-
1. **Keep `app.engines` property**: Required by ember-engines for lazy loading
|
|
27
|
-
2. **Create new `initialize-universe` instance initializer**: Loads `extension.js` files and registers metadata
|
|
28
|
-
3. **Remove `bootEngines` calls**: No more manual engine booting at startup
|
|
29
|
-
|
|
30
|
-
## Step-by-Step Guide
|
|
31
|
-
|
|
32
|
-
### Step 1: Update `app.js` to Preserve Engines Property
|
|
33
|
-
|
|
34
|
-
The `engines` property is **required** by ember-engines to enable lazy loading. Keep the existing structure but remove any `bootEngines` calls.
|
|
35
|
-
|
|
36
|
-
**Current `app.js` (fleetbase/console/app/app.js):**
|
|
37
|
-
|
|
38
|
-
```javascript
|
|
39
|
-
import Application from '@ember/application';
|
|
40
|
-
import Resolver from 'ember-resolver';
|
|
41
|
-
import loadInitializers from 'ember-load-initializers';
|
|
42
|
-
import config from '@fleetbase/console/config/environment';
|
|
43
|
-
import loadExtensions from '@fleetbase/ember-core/utils/load-extensions';
|
|
44
|
-
import mapEngines from '@fleetbase/ember-core/utils/map-engines';
|
|
45
|
-
import loadRuntimeConfig from '@fleetbase/console/utils/runtime-config';
|
|
46
|
-
import applyRouterFix from './utils/router-refresh-patch';
|
|
47
|
-
|
|
48
|
-
export default class App extends Application {
|
|
49
|
-
modulePrefix = config.modulePrefix;
|
|
50
|
-
podModulePrefix = config.podModulePrefix;
|
|
51
|
-
Resolver = Resolver;
|
|
52
|
-
extensions = [];
|
|
53
|
-
engines = {}; // ← KEEP THIS! Required by ember-engines
|
|
54
|
-
|
|
55
|
-
async ready() {
|
|
56
|
-
applyRouterFix(this);
|
|
57
|
-
const extensions = await loadExtensions();
|
|
58
|
-
|
|
59
|
-
this.extensions = extensions;
|
|
60
|
-
this.engines = mapEngines(extensions); // ← KEEP THIS! Maps extensions to engines
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
document.addEventListener('DOMContentLoaded', async () => {
|
|
65
|
-
await loadRuntimeConfig();
|
|
66
|
-
loadInitializers(App, config.modulePrefix);
|
|
67
|
-
|
|
68
|
-
let fleetbase = App.create();
|
|
69
|
-
fleetbase.deferReadiness();
|
|
70
|
-
fleetbase.boot();
|
|
71
|
-
});
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
**What to Keep:**
|
|
75
|
-
- ✅ `extensions` property - tracks which extensions are enabled
|
|
76
|
-
- ✅ `engines` property - required by ember-engines for lazy loading
|
|
77
|
-
- ✅ `loadExtensions()` - determines which extensions to load based on config + user permissions
|
|
78
|
-
- ✅ `mapEngines()` - creates the engines object required by ember-engines
|
|
79
|
-
|
|
80
|
-
**What Changes:**
|
|
81
|
-
- ❌ Remove any `bootEngines()` calls (if present in instance initializers)
|
|
82
|
-
- ❌ Remove `initialize-widgets.js` instance initializer (logic moves to `extension.js`)
|
|
83
|
-
|
|
84
|
-
### Step 2: Remove Old Instance Initializers
|
|
85
|
-
|
|
86
|
-
Delete the following instance initializers that perform eager engine loading:
|
|
87
|
-
|
|
88
|
-
**Files to Delete:**
|
|
89
|
-
- `app/instance-initializers/load-extensions.js` (if it calls `bootEngines`)
|
|
90
|
-
- `app/instance-initializers/initialize-widgets.js` (widgets now registered via `extension.js`)
|
|
91
|
-
|
|
92
|
-
### Step 3: Create New `initialize-universe` Initializer
|
|
93
|
-
|
|
94
|
-
Create a new instance initializer at `app/instance-initializers/initialize-universe.js`:
|
|
95
|
-
|
|
96
|
-
```javascript
|
|
97
|
-
import { getOwner } from '@ember/application';
|
|
98
|
-
import { scheduleOnce } from '@ember/runloop';
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Initializes the Universe by loading and executing extension.js files
|
|
102
|
-
* from all enabled extensions. This replaces the old bootEngines mechanism.
|
|
103
|
-
*
|
|
104
|
-
* Key differences from old approach:
|
|
105
|
-
* - Only loads extension.js files (small, metadata only)
|
|
106
|
-
* - Does NOT load engine bundles (those lazy-load when routes are visited)
|
|
107
|
-
* - Respects both system config and user permissions
|
|
108
|
-
*
|
|
109
|
-
* @param {ApplicationInstance} appInstance The application instance
|
|
110
|
-
*/
|
|
111
|
-
export function initialize(appInstance) {
|
|
112
|
-
const universe = appInstance.lookup('service:universe');
|
|
113
|
-
const owner = getOwner(appInstance);
|
|
114
|
-
const app = owner.application;
|
|
115
|
-
|
|
116
|
-
// Set application instance on universe
|
|
117
|
-
universe.applicationInstance = appInstance;
|
|
118
|
-
|
|
119
|
-
// Get the list of enabled extensions from the app
|
|
120
|
-
// This list already respects config + user permissions via loadExtensions()
|
|
121
|
-
const extensions = app.extensions || [];
|
|
122
|
-
|
|
123
|
-
// Load and execute extension.js from each enabled extension
|
|
124
|
-
extensions.forEach(extensionName => {
|
|
125
|
-
try {
|
|
126
|
-
// Dynamically require the extension.js file
|
|
127
|
-
// This is a small file with only metadata, not the full engine bundle
|
|
128
|
-
const setupExtension = require(`${extensionName}/extension`).default;
|
|
129
|
-
|
|
130
|
-
if (typeof setupExtension === 'function') {
|
|
131
|
-
// Execute the extension setup function
|
|
132
|
-
// This registers menus, widgets, hooks, etc. as metadata
|
|
133
|
-
setupExtension(appInstance, universe);
|
|
134
|
-
}
|
|
135
|
-
} catch (error) {
|
|
136
|
-
// Silently fail if extension.js doesn't exist
|
|
137
|
-
// Extensions can migrate gradually to the new pattern
|
|
138
|
-
// console.warn(`Could not load extension.js for ${extensionName}:`, error);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Execute any boot callbacks
|
|
143
|
-
scheduleOnce('afterRender', universe, 'executeBootCallbacks');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export default {
|
|
147
|
-
name: 'initialize-universe',
|
|
148
|
-
initialize
|
|
149
|
-
};
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Step 4: Verify `router.js` Engine Mounting
|
|
153
|
-
|
|
154
|
-
Your `prebuild.js` script already handles mounting engines in `router.js`. Verify that engines are mounted like this:
|
|
155
|
-
|
|
156
|
-
```javascript
|
|
157
|
-
// This is generated by prebuild.js
|
|
158
|
-
this.mount('@fleetbase/fleetops-engine', { as: 'console.fleet-ops' });
|
|
159
|
-
this.mount('@fleetbase/customer-portal-engine', { as: 'console.customer-portal' });
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**Important**: The `this.mount()` calls are what enable ember-engines lazy loading. When a user navigates to a route, ember-engines automatically loads the engine bundle on-demand.
|
|
163
|
-
|
|
164
|
-
### Step 5: Migrate Extensions to `extension.js` Pattern
|
|
165
|
-
|
|
166
|
-
For each extension, create an `addon/extension.js` file that registers metadata without importing components:
|
|
167
|
-
|
|
168
|
-
**Example: FleetOps `addon/extension.js`**
|
|
169
|
-
|
|
170
|
-
```javascript
|
|
171
|
-
import { MenuItem, MenuPanel, Widget, ExtensionComponent } from '@fleetbase/ember-core/contracts';
|
|
172
|
-
|
|
173
|
-
export default function (app, universe) {
|
|
174
|
-
// Register admin menu panel
|
|
175
|
-
universe.registerAdminMenuPanel(
|
|
176
|
-
'Fleet-Ops',
|
|
177
|
-
new MenuPanel({
|
|
178
|
-
title: 'Fleet-Ops',
|
|
179
|
-
icon: 'route',
|
|
180
|
-
items: [
|
|
181
|
-
new MenuItem({
|
|
182
|
-
title: 'Navigator App',
|
|
183
|
-
icon: 'location-arrow',
|
|
184
|
-
component: new ExtensionComponent('@fleetbase/fleetops-engine', 'components/admin/navigator-app')
|
|
185
|
-
}),
|
|
186
|
-
new MenuItem({
|
|
187
|
-
title: 'Avatar Management',
|
|
188
|
-
icon: 'images',
|
|
189
|
-
component: new ExtensionComponent('@fleetbase/fleetops-engine', 'components/admin/avatar-management')
|
|
190
|
-
})
|
|
191
|
-
]
|
|
192
|
-
})
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
// Register widgets
|
|
196
|
-
universe.registerDefaultWidget(
|
|
197
|
-
new Widget({
|
|
198
|
-
widgetId: 'fleet-ops-metrics',
|
|
199
|
-
name: 'Fleet-Ops Metrics',
|
|
200
|
-
description: 'Key metrics from Fleet-Ops',
|
|
201
|
-
icon: 'truck',
|
|
202
|
-
component: new ExtensionComponent('@fleetbase/fleetops-engine', 'components/widget/metrics'),
|
|
203
|
-
grid_options: { w: 12, h: 12, minW: 8, minH: 12 }
|
|
204
|
-
})
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
// Register hooks
|
|
208
|
-
universe.registerHook(
|
|
209
|
-
new Hook({
|
|
210
|
-
name: 'application:before-model',
|
|
211
|
-
handler: (session, router) => {
|
|
212
|
-
// Custom logic here
|
|
213
|
-
},
|
|
214
|
-
priority: 10
|
|
215
|
-
})
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
**Key Points:**
|
|
221
|
-
- ❌ NO `import MyComponent from './components/my-component'` - this would load the engine!
|
|
222
|
-
- ✅ Use `ExtensionComponent` with engine name + path for lazy loading
|
|
223
|
-
- ✅ Use contract classes (`MenuItem`, `Widget`, `Hook`) for type safety
|
|
224
|
-
|
|
225
|
-
See [UNIVERSE_REFACTOR_MIGRATION_GUIDE.md](./UNIVERSE_REFACTOR_MIGRATION_GUIDE.md) for detailed migration examples.
|
|
226
|
-
|
|
227
|
-
## How Lazy Loading Works with This Approach
|
|
228
|
-
|
|
229
|
-
1. **App Boot**: Application boots with `app.engines` property set
|
|
230
|
-
2. **`initialize-universe`**: Loads small `extension.js` files via `require()`
|
|
231
|
-
3. **Metadata Registration**: Extensions register menus, widgets, hooks (no component code loaded)
|
|
232
|
-
4. **User Navigation**: User navigates to `/console/fleet-ops`
|
|
233
|
-
5. **Ember-Engines**: Detects route is in a mounted engine, lazy-loads the engine bundle
|
|
234
|
-
6. **Component Resolution**: `<LazyEngineComponent>` resolves components from loaded engine
|
|
235
|
-
|
|
236
|
-
## Performance Impact
|
|
237
|
-
|
|
238
|
-
| Metric | Before (bootEngines) | After (Lazy Loading) |
|
|
239
|
-
|--------|---------------------|---------------------|
|
|
240
|
-
| Initial Load Time | 10-40 seconds | <1 second |
|
|
241
|
-
| Initial Bundle Size | Core + All Engines | Core + extension.js files |
|
|
242
|
-
| Engine Loading | All at boot | On-demand when route visited |
|
|
243
|
-
| Memory Usage | All engines in memory | Only visited engines in memory |
|
|
244
|
-
|
|
245
|
-
## Ember-Engines Requirements
|
|
246
|
-
|
|
247
|
-
According to [ember-engines documentation](https://github.com/ember-engines/ember-engines):
|
|
248
|
-
|
|
249
|
-
> **Lazy loading** - An engine can allow its parent to boot with only its routing map loaded. The rest of the engine can be loaded only as required (i.e. when a route in an engine is visited). This allows applications to boot faster and limit their memory consumption.
|
|
250
|
-
|
|
251
|
-
**Required for lazy loading:**
|
|
252
|
-
1. ✅ `app.engines` property must be set (maps extension names to engine modules)
|
|
253
|
-
2. ✅ Engines must be mounted in `router.js` via `this.mount()`
|
|
254
|
-
3. ✅ Engine's `index.js` must have `lazyLoading: true` (default)
|
|
255
|
-
|
|
256
|
-
**What breaks lazy loading:**
|
|
257
|
-
1. ❌ Calling `owner.lookup('engine:my-engine')` at boot time
|
|
258
|
-
2. ❌ Importing components from engines in `extension.js`
|
|
259
|
-
3. ❌ Manual `bootEngines()` calls
|
|
260
|
-
|
|
261
|
-
## Troubleshooting
|
|
262
|
-
|
|
263
|
-
### Extension not loading
|
|
264
|
-
- Check that extension is in `app.extensions` array
|
|
265
|
-
- Verify `extension.js` file exists and exports a function
|
|
266
|
-
- Check browser console for errors
|
|
267
|
-
|
|
268
|
-
### Components not rendering
|
|
269
|
-
- Ensure `ExtensionComponent` has correct engine name and path
|
|
270
|
-
- Verify engine is mounted in `router.js`
|
|
271
|
-
- Check that `<LazyEngineComponent>` is used in templates
|
|
272
|
-
|
|
273
|
-
### Engines loading at boot
|
|
274
|
-
- Remove any `owner.lookup('engine:...')` calls from initializers
|
|
275
|
-
- Remove component imports from `extension.js`
|
|
276
|
-
- Verify no `bootEngines()` calls remain
|
|
277
|
-
|
|
278
|
-
## Migration Checklist
|
|
279
|
-
|
|
280
|
-
- [ ] Update `app.js` to keep `engines` property
|
|
281
|
-
- [ ] Remove old instance initializers (`load-extensions.js`, `initialize-widgets.js`)
|
|
282
|
-
- [ ] Create new `initialize-universe.js` instance initializer
|
|
283
|
-
- [ ] Verify `router.js` has `this.mount()` calls for all engines
|
|
284
|
-
- [ ] Create `extension.js` for each extension
|
|
285
|
-
- [ ] Replace component imports with `ExtensionComponent` definitions
|
|
286
|
-
- [ ] Test lazy loading in browser dev tools (Network tab)
|
|
287
|
-
- [ ] Verify initial bundle size reduction
|
|
288
|
-
- [ ] Test all extension functionality still works
|
|
289
|
-
|
|
290
|
-
## References
|
|
291
|
-
|
|
292
|
-
- [Ember Engines Guide](https://guides.emberjs.com/v5.6.0/applications/ember-engines/)
|
|
293
|
-
- [ember-engines GitHub](https://github.com/ember-engines/ember-engines)
|
|
294
|
-
- [Ember Engines RFC](https://github.com/emberjs/rfcs/blob/master/text/0010-engines.md)
|
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
# UniverseService Refactor Migration Guide
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
The UniverseService has been completely refactored to improve performance, maintainability, and developer experience. This guide will help you migrate your extensions to the new architecture.
|
|
6
|
-
|
|
7
|
-
## What Changed?
|
|
8
|
-
|
|
9
|
-
### 1. Service Decomposition
|
|
10
|
-
|
|
11
|
-
The monolithic `UniverseService` has been split into specialized services:
|
|
12
|
-
|
|
13
|
-
- **ExtensionManager**: Manages lazy loading of engines
|
|
14
|
-
- **RegistryService**: Manages all registries using Ember's container
|
|
15
|
-
- **MenuService**: Manages menu items and panels
|
|
16
|
-
- **WidgetService**: Manages dashboard widgets
|
|
17
|
-
- **HookService**: Manages application hooks
|
|
18
|
-
|
|
19
|
-
The original `UniverseService` now acts as a facade, delegating to these services while maintaining backward compatibility.
|
|
20
|
-
|
|
21
|
-
### 2. Contract System
|
|
22
|
-
|
|
23
|
-
New contract classes provide a fluent, type-safe API:
|
|
24
|
-
|
|
25
|
-
- `ExtensionComponent`: Lazy-loadable component definitions
|
|
26
|
-
- `MenuItem`: Menu item definitions
|
|
27
|
-
- `MenuPanel`: Menu panel definitions
|
|
28
|
-
- `Hook`: Hook definitions
|
|
29
|
-
- `Widget`: Widget definitions
|
|
30
|
-
- `Registry`: Registry namespace definitions
|
|
31
|
-
|
|
32
|
-
### 3. Lazy Loading Architecture
|
|
33
|
-
|
|
34
|
-
The old `bootEngines` mechanism has been replaced with on-demand lazy loading:
|
|
35
|
-
|
|
36
|
-
- Engines are no longer loaded at boot time
|
|
37
|
-
- Components are loaded only when needed
|
|
38
|
-
- The `<LazyEngineComponent>` wrapper handles lazy loading automatically
|
|
39
|
-
|
|
40
|
-
## Migration Steps
|
|
41
|
-
|
|
42
|
-
### Step 1: Create `extension.js` File
|
|
43
|
-
|
|
44
|
-
Each engine should create a new `addon/extension.js` file to replace the `setupExtension` method in `engine.js`.
|
|
45
|
-
|
|
46
|
-
**Before (`addon/engine.js`):**
|
|
47
|
-
|
|
48
|
-
```javascript
|
|
49
|
-
import NavigatorAppComponent from './components/admin/navigator-app';
|
|
50
|
-
|
|
51
|
-
export default class FleetOpsEngine extends Engine {
|
|
52
|
-
setupExtension = function (app, engine, universe) {
|
|
53
|
-
universe.registerHeaderMenuItem('Fleet-Ops', 'console.fleet-ops', {
|
|
54
|
-
icon: 'route',
|
|
55
|
-
priority: 0
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
universe.registerAdminMenuPanel('Fleet-Ops Config', [
|
|
59
|
-
{
|
|
60
|
-
title: 'Navigator App',
|
|
61
|
-
component: NavigatorAppComponent
|
|
62
|
-
}
|
|
63
|
-
]);
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**After (`addon/extension.js`):**
|
|
69
|
-
|
|
70
|
-
```javascript
|
|
71
|
-
import { MenuItem, MenuPanel, ExtensionComponent } from '@fleetbase/ember-core/contracts';
|
|
72
|
-
|
|
73
|
-
export default function (app, universe) {
|
|
74
|
-
// Register header menu item
|
|
75
|
-
universe.registerHeaderMenuItem(
|
|
76
|
-
new MenuItem('Fleet-Ops', 'console.fleet-ops')
|
|
77
|
-
.withIcon('route')
|
|
78
|
-
.withPriority(0)
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Register admin panel with lazy component
|
|
82
|
-
universe.registerAdminMenuPanel(
|
|
83
|
-
new MenuPanel('Fleet-Ops Config')
|
|
84
|
-
.addItem(
|
|
85
|
-
new MenuItem('Navigator App')
|
|
86
|
-
.withIcon('location-arrow')
|
|
87
|
-
.withComponent(
|
|
88
|
-
new ExtensionComponent('@fleetbase/fleetops-engine', 'components/admin/navigator-app')
|
|
89
|
-
)
|
|
90
|
-
)
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**After (`addon/engine.js`):**
|
|
96
|
-
|
|
97
|
-
```javascript
|
|
98
|
-
// Remove the setupExtension method entirely
|
|
99
|
-
export default class FleetOpsEngine extends Engine {
|
|
100
|
-
// ... other engine configuration
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Step 2: Use Contract Classes
|
|
105
|
-
|
|
106
|
-
Instead of plain objects, use the new contract classes for better type safety and developer experience.
|
|
107
|
-
|
|
108
|
-
**Before:**
|
|
109
|
-
|
|
110
|
-
```javascript
|
|
111
|
-
universe.registerWidget({
|
|
112
|
-
widgetId: 'fleet-ops-metrics',
|
|
113
|
-
name: 'Fleet-Ops Metrics',
|
|
114
|
-
icon: 'truck',
|
|
115
|
-
component: WidgetComponent,
|
|
116
|
-
grid_options: { w: 12, h: 12 }
|
|
117
|
-
});
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
**After:**
|
|
121
|
-
|
|
122
|
-
```javascript
|
|
123
|
-
import { Widget, ExtensionComponent } from '@fleetbase/ember-core/contracts';
|
|
124
|
-
|
|
125
|
-
universe.registerDashboardWidgets([
|
|
126
|
-
new Widget('fleet-ops-metrics')
|
|
127
|
-
.withName('Fleet-Ops Metrics')
|
|
128
|
-
.withIcon('truck')
|
|
129
|
-
.withComponent(
|
|
130
|
-
new ExtensionComponent('@fleetbase/fleetops-engine', 'components/widget/fleet-ops-key-metrics')
|
|
131
|
-
)
|
|
132
|
-
.withGridOptions({ w: 12, h: 12 })
|
|
133
|
-
]);
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Step 3: Update Component References
|
|
137
|
-
|
|
138
|
-
Replace direct component imports with lazy component definitions.
|
|
139
|
-
|
|
140
|
-
**Before:**
|
|
141
|
-
|
|
142
|
-
```javascript
|
|
143
|
-
import MyComponent from './components/my-component';
|
|
144
|
-
|
|
145
|
-
universe.registerMenuItem('my-registry', 'My Item', {
|
|
146
|
-
component: MyComponent
|
|
147
|
-
});
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
**After:**
|
|
151
|
-
|
|
152
|
-
```javascript
|
|
153
|
-
import { MenuItem, ExtensionComponent } from '@fleetbase/ember-core/contracts';
|
|
154
|
-
|
|
155
|
-
universe.registerMenuItem(
|
|
156
|
-
'my-registry',
|
|
157
|
-
new MenuItem('My Item')
|
|
158
|
-
.withComponent(
|
|
159
|
-
new ExtensionComponent('@fleetbase/my-engine', 'components/my-component')
|
|
160
|
-
)
|
|
161
|
-
);
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Step 4: Update Templates Using Registry Components
|
|
165
|
-
|
|
166
|
-
Templates that render components from registries need to use the `<LazyEngineComponent>` wrapper.
|
|
167
|
-
|
|
168
|
-
**Before:**
|
|
169
|
-
|
|
170
|
-
```handlebars
|
|
171
|
-
{{#each this.menuItems as |item|}}
|
|
172
|
-
{{component item.component model=@model}}
|
|
173
|
-
{{/each}}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**After:**
|
|
177
|
-
|
|
178
|
-
```handlebars
|
|
179
|
-
{{#each this.menuItems as |item|}}
|
|
180
|
-
<LazyEngineComponent @componentDef={{item.component}} @model={{@model}} />
|
|
181
|
-
{{/each}}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### Step 5: Update Hook Registrations
|
|
185
|
-
|
|
186
|
-
Use the new `Hook` contract for better hook management.
|
|
187
|
-
|
|
188
|
-
**Before:**
|
|
189
|
-
|
|
190
|
-
```javascript
|
|
191
|
-
universe.registerHook('application:before-model', (session, router) => {
|
|
192
|
-
if (session.isCustomer) {
|
|
193
|
-
router.transitionTo('customer-portal');
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
**After:**
|
|
199
|
-
|
|
200
|
-
```javascript
|
|
201
|
-
import { Hook } from '@fleetbase/ember-core/contracts';
|
|
202
|
-
|
|
203
|
-
universe.registerHook(
|
|
204
|
-
new Hook('application:before-model', (session, router) => {
|
|
205
|
-
if (session.isCustomer) {
|
|
206
|
-
router.transitionTo('customer-portal');
|
|
207
|
-
}
|
|
208
|
-
})
|
|
209
|
-
.withPriority(10)
|
|
210
|
-
.withId('customer-redirect')
|
|
211
|
-
);
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## Backward Compatibility
|
|
215
|
-
|
|
216
|
-
The refactored `UniverseService` maintains backward compatibility with the old API. You can continue using the old syntax while migrating:
|
|
217
|
-
|
|
218
|
-
```javascript
|
|
219
|
-
// Old syntax still works
|
|
220
|
-
universe.registerHeaderMenuItem('My Item', 'my.route', { icon: 'star' });
|
|
221
|
-
|
|
222
|
-
// New syntax is preferred
|
|
223
|
-
universe.registerHeaderMenuItem(
|
|
224
|
-
new MenuItem('My Item', 'my.route').withIcon('star')
|
|
225
|
-
);
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Benefits of Migration
|
|
229
|
-
|
|
230
|
-
1. **Performance**: Sub-second boot times with lazy loading
|
|
231
|
-
2. **Type Safety**: Contract classes provide validation and IDE support
|
|
232
|
-
3. **Maintainability**: Specialized services are easier to understand and modify
|
|
233
|
-
4. **Developer Experience**: Fluent API with method chaining
|
|
234
|
-
5. **Extensibility**: Easy to add new features without breaking changes
|
|
235
|
-
|
|
236
|
-
## Common Patterns
|
|
237
|
-
|
|
238
|
-
### Menu Item with Click Handler
|
|
239
|
-
|
|
240
|
-
```javascript
|
|
241
|
-
new MenuItem('Track Order')
|
|
242
|
-
.withIcon('barcode')
|
|
243
|
-
.withType('link')
|
|
244
|
-
.withWrapperClass('btn-block py-1 border')
|
|
245
|
-
.withComponent(
|
|
246
|
-
new ExtensionComponent('@fleetbase/fleetops-engine', 'components/order-tracking-lookup')
|
|
247
|
-
)
|
|
248
|
-
.onClick((menuItem) => {
|
|
249
|
-
universe.transitionMenuItem('virtual', menuItem);
|
|
250
|
-
})
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Widget with Refresh Interval
|
|
254
|
-
|
|
255
|
-
```javascript
|
|
256
|
-
new Widget('live-metrics')
|
|
257
|
-
.withName('Live Metrics')
|
|
258
|
-
.withComponent(
|
|
259
|
-
new ExtensionComponent('@fleetbase/my-engine', 'components/widget/live-metrics')
|
|
260
|
-
.withLoadingComponent('skeletons/widget')
|
|
261
|
-
)
|
|
262
|
-
.withRefreshInterval(5000)
|
|
263
|
-
.asDefault()
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Hook with Priority and Once
|
|
267
|
-
|
|
268
|
-
```javascript
|
|
269
|
-
new Hook('order:before-save')
|
|
270
|
-
.withPriority(10)
|
|
271
|
-
.once()
|
|
272
|
-
.execute(async (order) => {
|
|
273
|
-
await validateOrder(order);
|
|
274
|
-
})
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
## Troubleshooting
|
|
278
|
-
|
|
279
|
-
### Component Not Found Error
|
|
280
|
-
|
|
281
|
-
If you see "Component not found in engine" errors:
|
|
282
|
-
|
|
283
|
-
1. Check that the component path is correct
|
|
284
|
-
2. Ensure the engine name matches exactly
|
|
285
|
-
3. Verify the component exists in the engine
|
|
286
|
-
|
|
287
|
-
### Loading Spinner Not Showing
|
|
288
|
-
|
|
289
|
-
If the loading spinner doesn't appear:
|
|
290
|
-
|
|
291
|
-
1. Check that you're using `<LazyEngineComponent>` in templates
|
|
292
|
-
2. Verify the `componentDef` is a lazy definition object
|
|
293
|
-
3. Ensure the loading component exists
|
|
294
|
-
|
|
295
|
-
### Hooks Not Executing
|
|
296
|
-
|
|
297
|
-
If hooks aren't running:
|
|
298
|
-
|
|
299
|
-
1. Check the hook name matches exactly
|
|
300
|
-
2. Verify the hook is registered before it's needed
|
|
301
|
-
3. Use `universe.hookService.getHooks(hookName)` to debug
|
|
302
|
-
|
|
303
|
-
## Support
|
|
304
|
-
|
|
305
|
-
For questions or issues with the migration, please:
|
|
306
|
-
|
|
307
|
-
1. Check the contract class documentation in `addon/contracts/`
|
|
308
|
-
2. Review the service documentation in `addon/services/universe/`
|
|
309
|
-
3. Open an issue on GitHub with details about your migration challenge
|
|
310
|
-
|
|
311
|
-
## Timeline
|
|
312
|
-
|
|
313
|
-
- **Phase 1**: Refactored services are available, old API still works
|
|
314
|
-
- **Phase 2**: Extensions migrate to new `extension.js` pattern
|
|
315
|
-
- **Phase 3**: Deprecation warnings for old patterns
|
|
316
|
-
- **Phase 4**: Old `setupExtension` pattern removed (future release)
|
|
317
|
-
|
|
318
|
-
You can migrate at your own pace. The new architecture is fully backward compatible.
|