The services, contracts, decorators and utilities behind the Fleetbase console — and the foundation every Fleetbase extension is built on.
Documentation · Quickstart · Ember UI · Contributing
@fleetbase/ember-core is the Ember addon that the Fleetbase console and every Fleetbase extension
share. It provides:
- The Universe — the extensibility layer an extension uses to plug into the console: menus, registries, dashboard widgets, lifecycle hooks, engine loading and resource views.
- Contracts — small, chainable classes that describe what an extension contributes
(
MenuItem,Widget,Hook,TableColumn,ResourceActionand more). - Application services — authentication, the API client, the current user, CRUD workflows, filters, notifications, sockets, theming and more.
- Decorators and utilities — store- and API-backed properties, engine service injection, and the helpers the console relies on throughout.
Components live in the companion addon, @fleetbase/ember-ui.
The documentation lives at fleetbase.io/docs/extension-development.
| Topic | Guide |
|---|---|
| Building your first extension | Quickstart · Extension anatomy |
| How an extension is loaded | Architecture · Extension registration |
| The Universe and its sub-services | Universe overview · Menus · Registries · Widgets · Hooks · Extension manager |
| Extending another engine's tables and details views | Resource views |
| API reference | Contracts · Ember services · Decorators |
pnpm add @fleetbase/ember-coreOr, with Ember CLI:
ember install @fleetbase/ember-coreRequires Node 18 or newer. The addon is built and tested against Ember 5.4.
An extension contributes to the console from the setupExtension hook in its addon/extension.js.
The universe it receives is the entry point to everything else:
// addon/extension.js
import { ResourceAction, TableColumn } from '@fleetbase/ember-core/contracts';
export default {
setupExtension(app, universe) {
// Add an item to the console header
universe.getService('menu').registerHeaderMenuItem('Acme', 'console.acme', {
icon: 'rocket',
priority: 5,
});
// Add a column and a row action to Fleet-Ops' drivers table
const views = universe.getService('resource-view');
views.register(
'fleet-ops:driver:table:columns',
new TableColumn({ id: 'safety-score', label: 'Safety Score', valuePath: 'meta.safety_score' }).after('status')
);
views.register(
'fleet-ops:driver:table:row-actions',
new ResourceAction({ id: 'acme-sync', label: 'Sync to Acme', icon: 'sync', permission: 'acme sync driver' })
.withHandler((driver, ctx) => ctx.owner.lookup('service:acme').sync(driver))
);
},
};Every service is injected the usual way:
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
export default class OrdersController extends Controller {
@service fetch;
@service currentUser;
@service notifications;
@action async dispatch(order) {
await this.fetch.post(`orders/${order.id}/dispatch`);
this.notifications.success(`Dispatched by ${this.currentUser.name}`);
}
}Universe — the extensibility layer
universe is a facade over six sub-services, each reached with universe.getService(name):
| Name | Responsible for |
|---|---|
menu |
Header items, admin and settings menus, account dropdowns and custom menu registries |
registry |
Named registries, and registering components, services and helpers across engines |
widget |
Dashboard widgets and dashboards |
hook |
Application lifecycle and custom hooks |
resource-view |
Columns, actions and menu items on other engines' tables and details views |
extension-manager |
Loading engines and sharing services between them |
Contracts — imported from @fleetbase/ember-core/contracts
| Contract | Describes |
|---|---|
MenuItem, MenuPanel |
Menu entries and the admin panels that group them |
ExtensionComponent |
A component that lives in an engine and is loaded on demand |
Widget |
A dashboard widget |
Hook |
A lifecycle or custom hook handler |
Registry |
A named registry |
TableColumn |
A column added to a resource table |
ResourceAction |
A row, bulk or details-menu action |
ActionButton |
A toolbar or details-header button |
Services
| Service | Responsible for |
|---|---|
session |
Authentication and session lifecycle |
current-user |
The signed-in user, their organization, permissions and preferences |
fetch |
The Fleetbase API client: requests, uploads, downloads and caching |
crud |
Delete, bulk-action, export and import workflows |
resource-action |
The base for model-specific action services |
filters |
Query-param filters for resource tables |
table-context |
Selection state for the active table |
abilities |
Permission checks |
notifications |
Toast notifications |
socket |
Real-time channels |
chat |
Chat channels and messages |
events |
Application event tracking |
theme |
Light and dark themes and route body classes |
language |
Locale selection |
loader |
Loading overlays |
app-cache |
Persistent client-side cache |
url-search-params |
Reading and writing the URL's query string |
custom-fields-registry |
Custom fields attached to any model |
Decorators
| Decorator | Does |
|---|---|
@engineService |
Injects a service from another engine |
@fromStore |
Backs a property with a store query |
@fetchFrom |
Backs a property with an API request |
pnpm install --frozen-lockfile
pnpm start # serve the dummy app
pnpm test # lint + the full suite
pnpm run test:ember # the suite on its own
pnpm run lint # eslint, ember-template-lint, stylelint
pnpm run build # production buildRun a subset while working on one area:
pnpm exec ember test --filter="Unit | Service | fetch"The suite runs in headless Chrome through Testem. Coverage is gated at 100% of the addon/
source — statements, branches, functions and lines, checked per file — and enforced in CI, with
results reported to Codecov.
pnpm run coverage # run the suite with coverage
pnpm run coverage:check # enforce the gateSee the Contributing guide. Questions and ideas are welcome in GitHub Discussions and on Discord.
Licensed under the GNU Affero General Public License v3.0 or later.