@elevasis/sdk 1.9.0 → 1.11.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/dist/cli.cjs +228 -190
- package/dist/index.d.ts +127 -154
- package/dist/index.js +15 -139
- package/dist/test-utils/index.d.ts +8270 -0
- package/dist/test-utils/index.js +20070 -0
- package/dist/types/worker/index.d.ts +20 -1
- package/dist/worker/index.js +7 -4
- package/package.json +8 -2
- package/reference/_navigation.md +15 -1
- package/reference/_reference-manifest.json +56 -0
- package/reference/claude-config/sync-notes/2026-04-24-test-utils-and-template-tests.md +73 -0
- package/reference/claude-config/sync-notes/2026-04-24-ui-consolidation-and-sdk-cli-train.md +1 -1
- package/reference/deployment/provided-features.mdx +40 -267
- package/reference/examples/organization-model.ts +96 -564
- package/reference/packages/core/src/organization-model/README.md +101 -97
- package/reference/packages/core/src/test-utils/README.md +5 -10
- package/reference/packages/ui/src/test-utils/README.md +5 -0
- package/reference/resources/types.mdx +72 -163
- package/reference/scaffold/core/organization-graph.mdx +89 -272
- package/reference/scaffold/core/organization-model.mdx +149 -320
- package/reference/scaffold/operations/workflow-recipes.md +94 -1
- package/reference/scaffold/recipes/add-a-feature.md +102 -158
- package/reference/scaffold/recipes/add-a-resource.md +85 -158
- package/reference/scaffold/recipes/customize-organization-model.md +141 -400
- package/reference/scaffold/recipes/gate-by-feature-or-admin.md +114 -158
- package/reference/scaffold/reference/contracts.md +62 -148
- package/reference/scaffold/reference/feature-registry.md +8 -8
- package/reference/scaffold/reference/glossary.md +71 -105
- package/reference/scaffold/ui/feature-flags-and-gating.md +27 -232
- package/reference/scaffold/ui/feature-shell.mdx +50 -219
- package/reference/scaffold/ui/recipes.md +62 -397
|
@@ -1,241 +1,72 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Feature Shell & Provider Runtime
|
|
3
|
-
description:
|
|
3
|
+
description: Current feature shell contract for flat Organization Model features, manifests, route matching, sidebars, and breadcrumbs.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Overview
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
The feature shell derives app navigation from `OrganizationModel.features`. Feature manifests provide implementation hooks such as icons and sidebars; they no longer own structural navigation.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Source Of Truth
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
12
|
+
- `packages/core/src/organization-model/defaults.ts` -- default flat feature list
|
|
13
|
+
- `packages/core/src/organization-model/helpers.ts` -- `childrenOf`, `ancestorsOf`, `parentOf`, `topLevel`, `findByPath`
|
|
14
|
+
- `packages/ui/src/features/registry/types.ts` -- `FeatureModule`
|
|
15
|
+
- `packages/ui/src/provider/ElevasisFeaturesProvider.tsx` -- runtime resolver
|
|
16
|
+
- `packages/ui/src/components/navigation/useBreadcrumbs.ts` -- breadcrumb derivation
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
## FeatureModule
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
```ts
|
|
21
|
+
export interface FeatureModule {
|
|
22
|
+
key: string
|
|
23
|
+
featureId: string
|
|
24
|
+
capabilityIds?: string[]
|
|
25
|
+
icon?: FeatureIconComponent
|
|
26
|
+
sidebar?: FeatureSidebarComponent
|
|
27
|
+
organizationGraph?: { featureId: string }
|
|
28
|
+
}
|
|
29
|
+
```
|
|
19
30
|
|
|
20
|
-
|
|
21
|
-
- `packages/ui/src/features/registry/manifests.ts` -- published `FEATURE_MANIFESTS` convenience map
|
|
22
|
-
- `packages/ui/src/provider/ElevasisFeaturesProvider.tsx` -- runtime composition
|
|
23
|
-
- `packages/ui/src/provider/FeatureShell.tsx` -- route-to-sidebar dispatch
|
|
24
|
-
- `packages/ui/src/provider/resolvers/{RouteResolver,NavResolver}.ts` -- pure path/nav helpers
|
|
25
|
-
- `packages/ui/src/provider/validateManifests.ts` -- startup-time manifest validation
|
|
26
|
-
- `packages/ui/src/provider/published.ts` -- headless published barrel
|
|
27
|
-
- `apps/command-center/src/routes/__root.tsx` -- reference composition
|
|
31
|
+
`featureId` must match an Organization Model feature ID. Structural fields such as route lists, nav labels, and nested links belong on `OrganizationModel.features`.
|
|
28
32
|
|
|
29
|
-
##
|
|
33
|
+
## Shell Model
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
`useElevasisFeatures()` exposes `shellModel`:
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
```ts
|
|
38
|
+
{
|
|
39
|
+
features,
|
|
40
|
+
findByPath,
|
|
41
|
+
findById,
|
|
42
|
+
childrenOf,
|
|
43
|
+
ancestorsOf,
|
|
44
|
+
parentOf,
|
|
45
|
+
topLevel,
|
|
46
|
+
uiPositionFor,
|
|
47
|
+
requiresAdminFor,
|
|
48
|
+
devOnlyFor
|
|
49
|
+
}
|
|
50
|
+
```
|
|
40
51
|
|
|
41
|
-
`
|
|
52
|
+
Sidebar rendering walks `topLevel()` and `childrenOf(id)`. Breadcrumbs resolve `findByPath(location.pathname)` and then map `ancestorsOf(id)`.
|
|
42
53
|
|
|
43
|
-
|
|
54
|
+
## Access
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
Feature access is keyed by Organization Model feature ID. `requiresAdmin` and `devOnly` inherit from ancestor feature nodes and are applied when deriving visible sidebar entries.
|
|
46
57
|
|
|
47
|
-
|
|
58
|
+
Routes still need guards:
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
```tsx
|
|
61
|
+
<ProtectedRoute>
|
|
62
|
+
<FeatureGuard featureKey="sales.crm">
|
|
63
|
+
<Outlet />
|
|
64
|
+
</FeatureGuard>
|
|
65
|
+
</ProtectedRoute>
|
|
66
|
+
```
|
|
51
67
|
|
|
52
|
-
|
|
68
|
+
Use `AdminGuard` for pages that require platform-admin privileges.
|
|
53
69
|
|
|
54
|
-
##
|
|
70
|
+
## External Shells
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
### ElevasisCoreProvider
|
|
59
|
-
|
|
60
|
-
`ElevasisCoreProvider` is the headless root provider for Elevasis-powered applications. It is pure auth + API with no style side-effects -- no CSS variables, no `data-elevasis-scheme`, no font loading. When `apiUrl` is provided it composes the full provider stack:
|
|
61
|
-
|
|
62
|
-
`QueryClientProvider` -> `AuthProvider` -> `ApiClientProvider` -> `ProfileProvider` -> `OrganizationProvider` -> `ElevasisServiceProvider` -> `NotificationProvider` -> `InitializationProvider`
|
|
63
|
-
|
|
64
|
-
Consumers that need Mantine theming use `ElevasisUIProvider` (internal) or `ElevasisProvider` instead. `ElevasisCoreProvider` is exported from `packages/ui/src/provider/published.ts` for headless SDK consumers.
|
|
65
|
-
|
|
66
|
-
### ElevasisServiceProvider
|
|
67
|
-
|
|
68
|
-
`ElevasisServiceProvider` (from `ElevasisServiceContext.tsx`) is the standalone service context. It accepts three props directly: `apiRequest`, `organizationId`, and `isReady`. `ElevasisCoreProvider` composes this internally after org resolution; advanced consumers can mount it standalone for testing or embedding.
|
|
69
|
-
|
|
70
|
-
`useElevasisServices()` reads this context and throws if used outside a provider. Consumed throughout the shared component library wherever API requests are needed.
|
|
71
|
-
|
|
72
|
-
### AppearanceProvider
|
|
73
|
-
|
|
74
|
-
`AppearanceProvider` (from `AppearanceContext.tsx`) supplies an `AppearanceConfig` to the tree: `{ background?: ReactNode, loader?: ReactNode }`. The `background` field controls layers rendered behind app content; `loader` controls loading-state elements. Defaults are set in the provider rather than at context creation to avoid importing heavy visual components at module scope.
|
|
75
|
-
|
|
76
|
-
`useAppearance()` reads this context and throws if used outside a provider.
|
|
77
|
-
|
|
78
|
-
### NotificationProvider
|
|
79
|
-
|
|
80
|
-
`NotificationProvider` (from `NotificationContext.tsx`) accepts a pluggable `NotificationAdapter` for routing notifications to any library. The adapter interface is `{ success, error, info, warning, apiError }`. `ElevasisUIProvider` wires the Mantine adapter automatically.
|
|
81
|
-
|
|
82
|
-
When no provider is present in the tree, `useNotificationAdapter()` falls back to a console-based adapter (not a no-op), so template consumers work without Mantine. The hook never throws.
|
|
83
|
-
|
|
84
|
-
### Memoization Strategy
|
|
85
|
-
|
|
86
|
-
All computed values inside `ElevasisFeaturesProvider` are wrapped in `useMemo()` with precise dependency tracking. Resolved features, nav items, shell model, organization graph, and the context value object are each memoized separately so downstream consumers only re-render when their specific inputs change.
|
|
87
|
-
|
|
88
|
-
## Provider Responsibilities
|
|
89
|
-
|
|
90
|
-
`ElevasisFeaturesProvider` owns:
|
|
91
|
-
|
|
92
|
-
- registration of shared feature manifests
|
|
93
|
-
- feature-flag-aware nav contribution
|
|
94
|
-
- route-to-sidebar subshell dispatch (via `shellRuntime.resolveRoute`)
|
|
95
|
-
- provider-scoped shared runtime context
|
|
96
|
-
- resolved shell-model composition and shell-native route matching
|
|
97
|
-
- organization-model-aware nav label and path resolution
|
|
98
|
-
- resolved feature output (no legacy `shellModule` wrapper)
|
|
99
|
-
|
|
100
|
-
It does **not** own:
|
|
101
|
-
|
|
102
|
-
- TanStack file-based route registration
|
|
103
|
-
- app branding, topbar behavior, admin entries, assistant/context glue
|
|
104
|
-
|
|
105
|
-
Consumers keep thin local route wrappers and app-local nav where needed.
|
|
106
|
-
|
|
107
|
-
## Runtime Flow
|
|
108
|
-
|
|
109
|
-
1. The app defines a manifest list (often by spreading `FEATURE_MANIFESTS` and overriding entries).
|
|
110
|
-
2. The app optionally resolves an organization model and passes it to the provider.
|
|
111
|
-
3. The provider combines `useFeatureAccess()` with the org model's `features[]` array to compute enabled features.
|
|
112
|
-
4. Nav labels and nav paths resolve from the matching `feature.label` in `organizationModel.features` and from `organizationModel.navigation.surfaces` when present.
|
|
113
|
-
5. The provider exposes `shellModel`, `shellRuntime`, resolved feature access, `organizationGraph`, and shared runtime inputs via `useElevasisFeatures()`.
|
|
114
|
-
6. `FeatureShell` calls `shellRuntime.resolveRoute(currentPath)`:
|
|
115
|
-
- `matched` -- render the feature's sidebar subshell
|
|
116
|
-
- `hidden` -- render `FeatureUnavailableState`
|
|
117
|
-
- `unmatched` -- render plain children (consumer owns the route)
|
|
118
|
-
|
|
119
|
-
A route resolves as `hidden` when the matched nav link meets either of these conditions: its `featureKey` fails the `isFeatureEnabled()` check, or its path matches an entry in `disabledSubsectionPaths`. Both checks are applied recursively through nested nav links.
|
|
120
|
-
|
|
121
|
-
Consumer nav derivation runs locally from `shellModel.navItems`; the provider no longer exposes `visibleNavItems`.
|
|
122
|
-
|
|
123
|
-
## Provider-Scoped Runtime Context
|
|
124
|
-
|
|
125
|
-
`useElevasisFeatures()` exposes:
|
|
126
|
-
|
|
127
|
-
- `shellModel` -- `{ navItems: ResolvedShellNavItem[] }` -- the full resolved nav list
|
|
128
|
-
- `shellRuntime` -- `{ resolveRoute: (path) => ResolvedShellRouteMatch }` -- route dispatch
|
|
129
|
-
- `resolvedFeatures` -- all `ResolvedFeatureModule[]` regardless of enabled state
|
|
130
|
-
- `enabledResolvedFeatures` -- filtered to `access.enabled === true`
|
|
131
|
-
- `timeRange`
|
|
132
|
-
- `operationsApiUrl`, `operationsSSEManager`
|
|
133
|
-
- `deliveryApiUrl`, `deliverySSEManager`
|
|
134
|
-
- `organizationModel`
|
|
135
|
-
- `organizationGraph` (resolved from `operations.organization-graph` surface; see [Organization Graph](../core/organization-graph.mdx))
|
|
136
|
-
- `disabledSubsectionPaths` -- used by consumers like `_template` to hide specific subsections (e.g., `/settings/appearance`)
|
|
137
|
-
- `isFeatureEnabled(key: string): boolean` -- checks both membership flag and organization-model feature state
|
|
138
|
-
- `getResolvedFeature(key: string): ResolvedFeatureModule | undefined` -- looks up a resolved feature by its `key` field (module key, not access key)
|
|
139
|
-
|
|
140
|
-
## Nav Resolution
|
|
141
|
-
|
|
142
|
-
### ResolvedShellNavItem
|
|
143
|
-
|
|
144
|
-
`ResolvedShellNavItem` extends `FeatureNavEntry` with two additional fields:
|
|
145
|
-
|
|
146
|
-
- `placement: 'primary' | 'bottom'` -- where the item appears in the shell nav; feature manifests always produce `'primary'`; `appShellOverrides.bottomNavItems` produce `'bottom'`
|
|
147
|
-
- `source: 'app' | 'feature'` -- whether the item came from a manifest (`'feature'`) or from `appShellOverrides` (`'app'`)
|
|
148
|
-
- `featureId?: string` -- the feature ID associated with this nav item for gating checks
|
|
149
|
-
|
|
150
|
-
`shellModel.navItems` contains the merged and filtered list of all `ResolvedShellNavItem`s from both manifest features and `appShellOverrides`.
|
|
151
|
-
|
|
152
|
-
### filterNavLinks
|
|
153
|
-
|
|
154
|
-
`filterNavLinks()` in `ElevasisFeaturesProvider.tsx` recursively removes gated and disabled-subsection links from a `FeatureNavLink[]` array. A link is removed if its `featureKey` fails `isFeatureEnabled()` or if its path matches any entry in `disabledSubsectionPaths`. The function recurses into nested `links` arrays before deciding whether a parent with now-empty children should be retained or dropped.
|
|
155
|
-
|
|
156
|
-
## Access Resolution
|
|
157
|
-
|
|
158
|
-
Shell feature-module keys map directly to feature IDs in the organization model. There is no alias layer:
|
|
159
|
-
|
|
160
|
-
- `createFeatureAccessHook` is the factory that produces `useFeatureAccess`. (Old name `createUseFeatureAccess` is re-exported as `@deprecated`.)
|
|
161
|
-
- `FeatureModule.featureId` must match a `feature.id` value in the org model's `features[]` array. The provider throws at startup if the ID is not found.
|
|
162
|
-
- `MembershipFeatureConfig.features` is `Record<string, boolean>` -- a dynamic map keyed by feature ID. Any feature can be overridden per member without schema changes.
|
|
163
|
-
- The provider fallback for missing feature access identity is retired -- explicit `featureId` is required.
|
|
164
|
-
|
|
165
|
-
## Published Surface
|
|
166
|
-
|
|
167
|
-
`packages/ui/src/provider/published.ts` is intentionally headless: it exports the provider, types, and hooks, but no Mantine-dependent visual provider pieces. External consumers can adopt the feature/provider contract without pulling the full internal visual surface.
|
|
168
|
-
|
|
169
|
-
All nine features are published as individual subpath exports from `@elevasis/ui`:
|
|
170
|
-
|
|
171
|
-
- `@elevasis/ui/features/auth` -- `ProtectedRoute`, `AdminGuard`, `FeatureGuard`, `useUserProfile` (utility feature; no manifest)
|
|
172
|
-
- `@elevasis/ui/features/dashboard` -- `Dashboard`, `ResourceOverview`, `RecentExecutionsByResource`, `UnresolvedErrorsTeaser` (utility feature; no manifest)
|
|
173
|
-
- `@elevasis/ui/features/crm`
|
|
174
|
-
- `@elevasis/ui/features/lead-gen`
|
|
175
|
-
- `@elevasis/ui/features/projects`
|
|
176
|
-
- `@elevasis/ui/features/monitoring`
|
|
177
|
-
- `@elevasis/ui/features/operations`
|
|
178
|
-
- `@elevasis/ui/features/seo`
|
|
179
|
-
- `@elevasis/ui/features/settings`
|
|
180
|
-
|
|
181
|
-
The `auth` and `dashboard` features are not in `FEATURE_MANIFESTS` and are not registered with `ElevasisFeaturesProvider`. They are standalone published feature modules consumed directly by host apps.
|
|
182
|
-
|
|
183
|
-
## Composition Patterns
|
|
184
|
-
|
|
185
|
-
### Command-center composition
|
|
186
|
-
|
|
187
|
-
`apps/command-center/src/routes/__root.tsx`:
|
|
188
|
-
|
|
189
|
-
1. imports the mounted manifest list
|
|
190
|
-
2. resolves `COMMAND_CENTER_ORGANIZATION_MODEL`
|
|
191
|
-
3. passes manifests, organization model, time range, operations API URL, and SSE manager to `ElevasisFeaturesProvider`
|
|
192
|
-
4. keeps app-local nav entries for dashboard, admin, archive (passed into `appShellOverrides`)
|
|
193
|
-
5. derives filtered nav locally from `shellModel.navItems`
|
|
194
|
-
6. lets `FeatureShell` dispatch subshell sidebars for matched routes
|
|
195
|
-
|
|
196
|
-
### appShellOverrides
|
|
197
|
-
|
|
198
|
-
`appShellOverrides` is an `AppShellOverrides` prop on `ElevasisFeaturesProvider` that lets the host app inject nav items outside the manifest registry:
|
|
199
|
-
|
|
200
|
-
- `primaryNavItems?: FeatureNavEntry[]` -- prepended before feature nav items in the primary nav position; organization-model label and path resolution is applied to these entries
|
|
201
|
-
- `bottomNavItems?: FeatureNavEntry[]` -- rendered at the bottom of the shell nav; also subject to organization-model resolution and `filterNavLinks` filtering
|
|
202
|
-
|
|
203
|
-
Both arrays go through the same `isFeatureEnabled()` and `disabledSubsectionPaths` filtering as manifest-derived items. Items whose `featureKey` is disabled, or that have only disabled nested links and no direct `link`, are dropped entirely.
|
|
204
|
-
|
|
205
|
-
Nine feature modules are published (see **Published Surface** above). Six are currently mounted in command-center: `leadGenManifest`, `crmManifest`, `projectsManifest`, `operationsManifest`, `monitoringManifest`, `settingsManifest`. The remaining three (`seoManifest`, `auth`, `dashboard`) are published but not mounted through `ElevasisFeaturesProvider` in command-center -- `auth` and `dashboard` are utility features consumed directly, while `seoManifest` is available for composition but not wired into the current command-center shell.
|
|
206
|
-
|
|
207
|
-
### External-consumer composition
|
|
208
|
-
|
|
209
|
-
External shells consume the published `@elevasis/ui` provider surface. `_template/ui` imports individual manifests, composes a local `FEATURE_MANIFESTS: FeatureModule[]` array, and passes `canonicalOrganizationModel` (from `@foundation/config/organization-model`) into the provider. Host-local nav (home/dashboard) remains app-owned -- the provider covers shared shell features, not total shell ownership.
|
|
210
|
-
|
|
211
|
-
See [Composition & Extensibility](./composition-extensibility.mdx) for the sidebar/page override patterns consumers use to customize shared features without forking.
|
|
212
|
-
|
|
213
|
-
## Feature-Specific Sidebar Behavior
|
|
214
|
-
|
|
215
|
-
### CRM Sidebar
|
|
216
|
-
|
|
217
|
-
The CRM sidebar (`packages/ui/src/features/crm/sidebar/`) exports `CrmSidebar`, `CrmSidebarTop`, and `CrmSidebarMiddle`. `SavedViewsPanel` lives in `packages/ui/src/features/crm/workbench/SavedViewsPanel.tsx` and is exported from the CRM workbench barrel; it provides the saved contact views panel. It is currently commented out of the default `CrmSidebarMiddle` layout while the CRM sidebar section model is being reworked, but can be re-included explicitly when composing a custom sidebar.
|
|
218
|
-
|
|
219
|
-
### Operations Sidebar
|
|
220
|
-
|
|
221
|
-
`OperationsSidebarTop` in `packages/ui/src/features/operations/sidebar/OperationsSidebarTop.tsx` is context-aware by route:
|
|
222
|
-
|
|
223
|
-
- Returns `null` for sessions (`/operations/sessions`) and command-view (`/operations/command-view`) routes -- these sections own their own top-area UI
|
|
224
|
-
- Shows a "Resource Overview" navigation button for the resources section (`/operations/resources`)
|
|
225
|
-
- Returns `null` for all other paths (e.g., the operations index)
|
|
226
|
-
|
|
227
|
-
This pattern avoids hardcoding sidebar top content in the parent and lets each operations sub-section declare its own top panel entry point.
|
|
228
|
-
|
|
229
|
-
## Testing
|
|
230
|
-
|
|
231
|
-
- `createTestFeaturesProvider` fixture at `packages/ui/src/provider/createTestFeaturesProvider.tsx` (internal; exported from `provider/index.ts`) gives tests a pre-wired provider with configurable organization model and feature access.
|
|
232
|
-
- Resolver modules have focused unit tests (`RouteResolver.test.ts`, `NavResolver.test.ts`).
|
|
233
|
-
- `validateManifests.test.ts` covers manifest validation against the organization model.
|
|
234
|
-
- `ElevasisFeaturesProvider.test.tsx`, `FeatureShell.test.tsx`, and `feature-contract.test.ts` cover runtime composition and the published surface.
|
|
235
|
-
|
|
236
|
-
## Key Conceptual Distinctions
|
|
237
|
-
|
|
238
|
-
- **Platform capability vs. shell feature** -- capabilities are the product map; shell features are manifest-backed UI surfaces.
|
|
239
|
-
- **Shell feature key vs. org-model feature id** -- both are the same value (e.g., `crm`). `FeatureModule.featureId` directly matches `feature.id` in the org model; there is no alias layer.
|
|
240
|
-
- **Feature vs. capability** -- a feature object in the org model carries `entityIds`, `surfaceIds`, `resourceIds`, and `capabilityIds`; capabilities are one kind of semantic reference a feature can hold.
|
|
241
|
-
- **Provider-owned vs. consumer-owned** -- provider owns shared manifests, nav contribution, sidebar dispatch, shared runtime context; consumers own route files, branding, topbar behavior, admin entries.
|
|
72
|
+
External templates pass `canonicalOrganizationModel` into `ElevasisFeaturesProvider` and derive sidebar links locally from `shellModel`. There is no separate nav config file to keep in sync.
|