@thatopen/services 0.0.2 → 0.0.3

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 (63) hide show
  1. package/AGENTS.md +86 -76
  2. package/dist/cli.js +10 -1
  3. package/docs/access-backend-data.md +19 -19
  4. package/docs/app-architecture.md +94 -94
  5. package/docs/app-layout.md +139 -139
  6. package/docs/app-wiring.md +123 -123
  7. package/docs/bim-components/coding-conventions.md +128 -128
  8. package/docs/bim-components/element-collections.md +69 -69
  9. package/docs/bim-components/expose-events.md +84 -84
  10. package/docs/bim-components/library-examples.md +28 -28
  11. package/docs/bim-components/observable-collections.md +83 -83
  12. package/docs/bim-components/overview.md +160 -160
  13. package/docs/bim-components/per-frame-updates.md +20 -20
  14. package/docs/bim-components/save-and-restore-state.md +21 -21
  15. package/docs/bim-components/setup-and-cleanup.md +64 -64
  16. package/docs/bim-components/type-conventions.md +49 -49
  17. package/docs/bim-components/user-driven-object-creation.md +56 -56
  18. package/docs/cli-setup.md +54 -54
  19. package/docs/cloud-components.md +74 -74
  20. package/docs/connect-logic-to-ui.md +113 -113
  21. package/docs/previewing.md +45 -45
  22. package/docs/publishing.md +35 -35
  23. package/docs/scaffolding.md +71 -54
  24. package/docs/ui-components/async-actions.md +18 -18
  25. package/docs/ui-components/confirmation-dialog.md +39 -39
  26. package/docs/ui-components/data-table.md +185 -185
  27. package/docs/ui-components/display-text.md +39 -39
  28. package/docs/ui-components/inline-form.md +44 -44
  29. package/docs/ui-components/library-examples.md +24 -24
  30. package/docs/ui-components/overview.md +194 -194
  31. package/docs/ui-components/rendering-patterns.md +129 -129
  32. package/docs/ui-components/sections-layout.md +123 -123
  33. package/docs/update-grid-elements.md +46 -46
  34. package/docs/using-colors.md +32 -32
  35. package/docs/using-icons.md +37 -37
  36. package/package.json +2 -2
  37. package/src/cli/templates/bim/CONTEXT.md +1 -4
  38. package/src/cli/templates/bim/package.json +0 -1
  39. package/src/cli/templates/bim/src/main.ts +1 -2
  40. package/src/cli/templates/bim-beta/CONTEXT.md +241 -0
  41. package/src/cli/templates/bim-beta/package.json +25 -0
  42. package/src/cli/templates/bim-beta/src/app.ts +16 -0
  43. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/index.ts +91 -0
  44. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/src/index.ts +1 -0
  45. package/src/cli/templates/bim-beta/src/bim-components/CloudRunner/src/types.ts +5 -0
  46. package/src/cli/templates/bim-beta/src/bim-components/index.ts +1 -0
  47. package/src/cli/templates/bim-beta/src/globals.ts +1 -0
  48. package/src/cli/templates/bim-beta/src/main.ts +89 -0
  49. package/src/cli/templates/bim-beta/src/setups/cloud-runner.ts +13 -0
  50. package/src/cli/templates/bim-beta/src/setups/index.ts +3 -0
  51. package/src/cli/templates/bim-beta/src/setups/ui-manager.ts +27 -0
  52. package/src/cli/templates/bim-beta/src/setups/viewports-manager.ts +22 -0
  53. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/index.ts +26 -0
  54. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/src/index.ts +1 -0
  55. package/src/cli/templates/bim-beta/src/ui-components/app-info-section/src/types.ts +15 -0
  56. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/index.ts +37 -0
  57. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/src/index.ts +1 -0
  58. package/src/cli/templates/bim-beta/src/ui-components/cloud-runner-section/src/types.ts +14 -0
  59. package/src/cli/templates/bim-beta/src/ui-components/index.ts +2 -0
  60. package/src/cli/templates/cloud/CONTEXT.md +205 -205
  61. package/src/cli/templates/cloud/src/main.ts +70 -70
  62. package/src/cli/templates/shared/AGENTS.md +30 -23
  63. package/src/cli/templates/shared/CLAUDE.md +1 -1
@@ -1,139 +1,139 @@
1
- # Configure App Layout
2
-
3
- ## Grid element constraint
4
-
5
- Every entry in `grid.elements` must be a single `bim-panel-section`. This is a hard constraint of the platform — the grid only knows how to host panel sections, not arbitrary HTML containers.
6
-
7
- **Correct:** one `grid.elements` entry = one `bim-panel-section`
8
- **Wrong:** a UI component that returns a `<div>` wrapping multiple `<bui-panel-section>` elements used as a grid entry
9
-
10
- If you need multiple sections in one area, register each section separately in `grid.elements` and group them using `panel:` or `tabs:` in the grid template — never wrap them in a container component.
11
-
12
- ```ts
13
- // ✗ Wrong — MyDashboard returns <div><bui-panel-section>...</bui-panel-section><bui-panel-section>...</bui-panel-section></div>
14
- grid.elements = {
15
- dashboard: { template: uis.custom.get("myDashboard").template, initialState: { components } },
16
- }
17
-
18
- // ✓ Correct — each section is its own grid element; grouping happens in the template
19
- grid.elements = {
20
- stats: { template: uis.custom.get("statsSection").template, initialState: { components } },
21
- charts: { template: uis.custom.get("chartsSection").template, initialState: { components } },
22
- }
23
- // Then in the layout template:
24
- // "panel:right(stats,charts) viewer" 1fr / 22rem 1fr
25
- ```
26
-
27
- ---
28
-
29
- ## Grid sync rules
30
-
31
- Any change to the grid requires updates in multiple places. Never update one without the others.
32
-
33
- **Adding or removing a layout:**
34
- 1. `app.ts` → first argument of `BUI.Grid<[layouts], ...>`
35
- 2. `main.ts` → `grid.layouts` (add/remove the definition) and `grid.layout` if it was the initial layout
36
-
37
- **Adding or removing an element:**
38
- 1. `app.ts` → second argument of `BUI.Grid<..., [elements]>`
39
- 2. `main.ts` → `grid.elements`, `grid.areaGroups` (if applicable), and any layout templates that reference it
40
-
41
- **Renaming an element** — the name originates in the component, so the change cascades outward:
42
- 1. Rename the folder in `ui-components/` (kebab-case of the new name)
43
- 2. Update all internal identifiers: types (`{ComponentName}State`, `{ComponentName}Component`, `{ComponentName}GridElement`), functions (`{camelCase}Template`, `on{PascalCase}Created`)
44
- 3. Update the `ui-components/index.ts` barrel to point to the renamed folder
45
- 4. `app.ts` → second argument of `BUI.Grid` reflects the new `{ComponentName}GridElement`
46
- 5. `main.ts` → `grid.elements` and all layout templates that use that area name
47
-
48
- ---
49
-
50
- ## Defining layouts
51
-
52
- ```ts
53
- grid.layouts = {
54
- Viewer: {
55
- template: `"viewer" 1fr / 1fr`,
56
- },
57
- Quantities: {
58
- icon: app.icons.QUANTITY,
59
- template: `
60
- "qtos viewer" 1fr
61
- /40rem 1fr
62
- `,
63
- },
64
- }
65
- grid.layout = "Collider"
66
- ```
67
-
68
- ---
69
-
70
- ## Special area tokens
71
-
72
- ```
73
- {groupType}:{areaName}(elementA, elementB)
74
- ```
75
-
76
- There are two group types: `tabs` and `panel`.
77
-
78
- ### `tabs:` — one element at a time
79
-
80
- Shows one element at a time with tab switchers. The user picks which section is visible. Supports nested sub-groups:
81
-
82
- ```
83
- tabs:{areaName}({groupName}[elementA, elementB], elementC)
84
- ```
85
-
86
- ```ts
87
- Collider: {
88
- icon: appIcons.COLLISION,
89
- template: `
90
- "tabs:left(group[collider,qtos],collider) viewer" 1fr
91
- /30rem 1fr
92
- `,
93
- }
94
- ```
95
-
96
- `areaGroups` options for `tabs:`: `switchersCompact` or `switchersFull`.
97
-
98
- ### `panel:` — all elements stacked vertically
99
-
100
- Stacks all elements and keeps them all visible simultaneously, scrollable as a single panel. Use when sections are related and should all be visible at once (e.g. an inspector with multiple info sections):
101
-
102
- ```ts
103
- app: {
104
- template: `
105
- "tabs:left viewport panel:right" 1fr
106
- / 22rem 1fr 20rem
107
- `,
108
- }
109
- ```
110
-
111
- `areaGroups` options for `panel:`: `label` to give the panel a title.
112
-
113
- ### Choosing between `tabs:` and `panel:`
114
-
115
- - Use `tabs:` when sections are **alternatives** — the user focuses on one at a time
116
- - Use `panel:` when sections are **complementary** — the user needs all of them visible together
117
-
118
- ---
119
-
120
- ## `grid.elements` and `grid.areaGroups`
121
-
122
- ```ts
123
- grid.elements = {
124
- viewer: viewport,
125
- qtos: {
126
- template: uis.custom.get("qtosSection").template,
127
- initialState: { components },
128
- label: "Quantities",
129
- },
130
- }
131
-
132
- grid.areaGroups = {
133
- left: { switchersFull: true },
134
- right: { label: "Inspector" },
135
- group: { label: "Configuration", icon: appIcons.APPLY },
136
- }
137
- ```
138
-
139
- > **Grouping sections always happens at the grid level.** To group, combine, or stack panel sections together, the answer is always `tabs:` or `panel:` in the grid template — never nesting one section inside another in the template code.
1
+ # Configure App Layout
2
+
3
+ ## Grid element constraint
4
+
5
+ Every entry in `grid.elements` must be a single `bim-panel-section`. This is a hard constraint of the platform — the grid only knows how to host panel sections, not arbitrary HTML containers.
6
+
7
+ **Correct:** one `grid.elements` entry = one `bim-panel-section`
8
+ **Wrong:** a UI component that returns a `<div>` wrapping multiple `<bui-panel-section>` elements used as a grid entry
9
+
10
+ If you need multiple sections in one area, register each section separately in `grid.elements` and group them using `panel:` or `tabs:` in the grid template — never wrap them in a container component.
11
+
12
+ ```ts
13
+ // ✗ Wrong — MyDashboard returns <div><bui-panel-section>...</bui-panel-section><bui-panel-section>...</bui-panel-section></div>
14
+ grid.elements = {
15
+ dashboard: { template: uis.custom.get("myDashboard").template, initialState: { components } },
16
+ }
17
+
18
+ // ✓ Correct — each section is its own grid element; grouping happens in the template
19
+ grid.elements = {
20
+ stats: { template: uis.custom.get("statsSection").template, initialState: { components } },
21
+ charts: { template: uis.custom.get("chartsSection").template, initialState: { components } },
22
+ }
23
+ // Then in the layout template:
24
+ // "panel:right(stats,charts) viewer" 1fr / 22rem 1fr
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Grid sync rules
30
+
31
+ Any change to the grid requires updates in multiple places. Never update one without the others.
32
+
33
+ **Adding or removing a layout:**
34
+ 1. `app.ts` → first argument of `BUI.Grid<[layouts], ...>`
35
+ 2. `main.ts` → `grid.layouts` (add/remove the definition) and `grid.layout` if it was the initial layout
36
+
37
+ **Adding or removing an element:**
38
+ 1. `app.ts` → second argument of `BUI.Grid<..., [elements]>`
39
+ 2. `main.ts` → `grid.elements`, `grid.areaGroups` (if applicable), and any layout templates that reference it
40
+
41
+ **Renaming an element** — the name originates in the component, so the change cascades outward:
42
+ 1. Rename the folder in `ui-components/` (kebab-case of the new name)
43
+ 2. Update all internal identifiers: types (`{ComponentName}State`, `{ComponentName}Component`, `{ComponentName}GridElement`), functions (`{camelCase}Template`, `on{PascalCase}Created`)
44
+ 3. Update the `ui-components/index.ts` barrel to point to the renamed folder
45
+ 4. `app.ts` → second argument of `BUI.Grid` reflects the new `{ComponentName}GridElement`
46
+ 5. `main.ts` → `grid.elements` and all layout templates that use that area name
47
+
48
+ ---
49
+
50
+ ## Defining layouts
51
+
52
+ ```ts
53
+ grid.layouts = {
54
+ Viewer: {
55
+ template: `"viewer" 1fr / 1fr`,
56
+ },
57
+ Quantities: {
58
+ icon: app.icons.QUANTITY,
59
+ template: `
60
+ "qtos viewer" 1fr
61
+ /40rem 1fr
62
+ `,
63
+ },
64
+ }
65
+ grid.layout = "Collider"
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Special area tokens
71
+
72
+ ```
73
+ {groupType}:{areaName}(elementA, elementB)
74
+ ```
75
+
76
+ There are two group types: `tabs` and `panel`.
77
+
78
+ ### `tabs:` — one element at a time
79
+
80
+ Shows one element at a time with tab switchers. The user picks which section is visible. Supports nested sub-groups:
81
+
82
+ ```
83
+ tabs:{areaName}({groupName}[elementA, elementB], elementC)
84
+ ```
85
+
86
+ ```ts
87
+ Collider: {
88
+ icon: appIcons.COLLISION,
89
+ template: `
90
+ "tabs:left(group[collider,qtos],collider) viewer" 1fr
91
+ /30rem 1fr
92
+ `,
93
+ }
94
+ ```
95
+
96
+ `areaGroups` options for `tabs:`: `switchersCompact` or `switchersFull`.
97
+
98
+ ### `panel:` — all elements stacked vertically
99
+
100
+ Stacks all elements and keeps them all visible simultaneously, scrollable as a single panel. Use when sections are related and should all be visible at once (e.g. an inspector with multiple info sections):
101
+
102
+ ```ts
103
+ app: {
104
+ template: `
105
+ "tabs:left viewport panel:right" 1fr
106
+ / 22rem 1fr 20rem
107
+ `,
108
+ }
109
+ ```
110
+
111
+ `areaGroups` options for `panel:`: `label` to give the panel a title.
112
+
113
+ ### Choosing between `tabs:` and `panel:`
114
+
115
+ - Use `tabs:` when sections are **alternatives** — the user focuses on one at a time
116
+ - Use `panel:` when sections are **complementary** — the user needs all of them visible together
117
+
118
+ ---
119
+
120
+ ## `grid.elements` and `grid.areaGroups`
121
+
122
+ ```ts
123
+ grid.elements = {
124
+ viewer: viewport,
125
+ qtos: {
126
+ template: uis.custom.get("qtosSection").template,
127
+ initialState: { components },
128
+ label: "Quantities",
129
+ },
130
+ }
131
+
132
+ grid.areaGroups = {
133
+ left: { switchersFull: true },
134
+ right: { label: "Inspector" },
135
+ group: { label: "Configuration", icon: appIcons.APPLY },
136
+ }
137
+ ```
138
+
139
+ > **Grouping sections always happens at the grid level.** To group, combine, or stack panel sections together, the answer is always `tabs:` or `panel:` in the grid template — never nesting one section inside another in the template code.
@@ -1,123 +1,123 @@
1
- # App Wiring
2
-
3
- ## `app.ts` — App Identity
4
-
5
- Defines the app's typed shape and the typed accessor for `AppManager`. The `App` type uses `GridElement` types exported from `ui-components/`:
6
-
7
- ```ts
8
- import * as OBC from "@thatopen/components"
9
- import * as BUI from "@thatopen/ui"
10
- import { AppManager } from "@thatopen/services"
11
- import { icons } from "./globals"
12
- import { FilesSectionGridElement, QtosSectionGridElement } from "./ui-components"
13
-
14
- export type App = {
15
- icons: (keyof typeof icons)[]
16
- grid: BUI.Grid<
17
- ["Viewer", "Quantities", "Collider"],
18
- [
19
- "viewer",
20
- FilesSectionGridElement,
21
- QtosSectionGridElement,
22
- ]
23
- >
24
- }
25
-
26
- export const getAppManager = (components: OBC.Components) =>
27
- components.get(AppManager<App>)
28
- ```
29
-
30
- `getAppManager` is the **only** valid way to access `AppManager` in an app. Never use `components.get(AppManager)` directly — it drops the `App` type parameter and loses all type information about the app's icons, grid layout, and grid elements.
31
-
32
- ```ts
33
- // ✗ Avoid — loses the App type
34
- const app = components.get(AppManager)
35
-
36
- // ✓ Correct — fully typed
37
- const app = getAppManager(components)
38
- ```
39
-
40
- ### Grid element types
41
-
42
- - Raw elements → typed as a string literal: `"viewer"`
43
- - Template-based sections → typed as `{ComponentName}GridElement`, defined in the component's `src/types.ts` and exported through `ui-components/index.ts`
44
-
45
- ```ts
46
- // ui-components/files-section/src/types.ts
47
- export type FilesSectionGridElement = {
48
- name: "filesSection"
49
- state: FilesSectionState
50
- }
51
- ```
52
-
53
- ---
54
-
55
- ## `main.ts` — Entry Point
56
-
57
- `main.ts` boots the platform and calls `app.init()`. No business logic belongs here.
58
-
59
- ```ts
60
- async function main() {
61
- const client = EngineServicesClient.fromPlatformContext()
62
-
63
- const { components } = await client.setup(
64
- { OBC, OBF, BUI, CUI, THREE, FRAGS },
65
- { uuid: ViewportsManager.uuid },
66
- { uuid: AppManager.uuid },
67
- { uuid: UIManager.uuid },
68
- ) as { components: OBC.Components }
69
-
70
- await viewportsManager(components)
71
- const viewports = components.get(ViewportsManager)
72
- // @ts-ignore
73
- const { element: viewport } = [...viewports._instances.values()][0]
74
-
75
- const app = getAppManager(components)
76
- await app.init({
77
- client,
78
- icons: appIcons,
79
- componentSetups: { ... },
80
- grid: (grid) => { ... },
81
- })
82
- }
83
-
84
- main().catch(console.error)
85
- ```
86
-
87
- ### `client.setup()` — Platform initialization
88
-
89
- - **First argument** — libraries object (`OBC`, `OBF`, `BUI`, `CUI`, `THREE`, `FRAGS`, etc.)
90
- - **Subsequent arguments** — `{ uuid }` for each platform built-in component needed. Always present: `ViewportsManager`, `AppManager`, `UIManager`
91
-
92
- Custom components (from `bim-components/`) do not need to be listed here — they self-register via `components.add()` in their constructor.
93
-
94
- ---
95
-
96
- ## `componentSetups` in `app.init()`
97
-
98
- ```ts
99
- componentSetups: {
100
- core: [uiManager, fragmentsManager, highlighter, ifcLoader],
101
- lazy: [
102
- { uuid: MyComponent.uuid, fn: myComponentSetup },
103
- ]
104
- }
105
- ```
106
-
107
- **`core`** — runs before the grid mounts, in parallel. `uiManager` always goes here. Other engine component setups (fragments, highlighter, IFC loader) also typically go here.
108
-
109
- **`lazy`** — runs the first time the corresponding component is instantiated. The `uuid` is imported from the `bim-components/` barrel. Use for custom components that can be loaded on demand.
110
-
111
- The `uuid` of each custom component — a static property defined on the class — is imported from the `bim-components/` barrel when referencing it in `lazy`:
112
-
113
- ```ts
114
- import { MyComponent } from "../bim-components"
115
-
116
- componentSetups: {
117
- lazy: [
118
- { uuid: MyComponent.uuid, fn: myComponentSetup },
119
- ]
120
- }
121
- ```
122
-
123
- When it's not obvious whether a custom component should be `core` or `lazy`, **decide based on whether the component is needed at boot or only on demand.**
1
+ # App Wiring
2
+
3
+ ## `app.ts` — App Identity
4
+
5
+ Defines the app's typed shape and the typed accessor for `AppManager`. The `App` type uses `GridElement` types exported from `ui-components/`:
6
+
7
+ ```ts
8
+ import * as OBC from "@thatopen/components"
9
+ import * as BUI from "@thatopen/ui"
10
+ import { AppManager } from "@thatopen/services"
11
+ import { icons } from "./globals"
12
+ import { FilesSectionGridElement, QtosSectionGridElement } from "./ui-components"
13
+
14
+ export type App = {
15
+ icons: (keyof typeof icons)[]
16
+ grid: BUI.Grid<
17
+ ["Viewer", "Quantities", "Collider"],
18
+ [
19
+ "viewer",
20
+ FilesSectionGridElement,
21
+ QtosSectionGridElement,
22
+ ]
23
+ >
24
+ }
25
+
26
+ export const getAppManager = (components: OBC.Components) =>
27
+ components.get(AppManager<App>)
28
+ ```
29
+
30
+ `getAppManager` is the **only** valid way to access `AppManager` in an app. Never use `components.get(AppManager)` directly — it drops the `App` type parameter and loses all type information about the app's icons, grid layout, and grid elements.
31
+
32
+ ```ts
33
+ // ✗ Avoid — loses the App type
34
+ const app = components.get(AppManager)
35
+
36
+ // ✓ Correct — fully typed
37
+ const app = getAppManager(components)
38
+ ```
39
+
40
+ ### Grid element types
41
+
42
+ - Raw elements → typed as a string literal: `"viewer"`
43
+ - Template-based sections → typed as `{ComponentName}GridElement`, defined in the component's `src/types.ts` and exported through `ui-components/index.ts`
44
+
45
+ ```ts
46
+ // ui-components/files-section/src/types.ts
47
+ export type FilesSectionGridElement = {
48
+ name: "filesSection"
49
+ state: FilesSectionState
50
+ }
51
+ ```
52
+
53
+ ---
54
+
55
+ ## `main.ts` — Entry Point
56
+
57
+ `main.ts` boots the platform and calls `app.init()`. No business logic belongs here.
58
+
59
+ ```ts
60
+ async function main() {
61
+ const client = EngineServicesClient.fromPlatformContext()
62
+
63
+ const { components } = await client.setup(
64
+ { OBC, OBF, BUI, CUI, THREE, FRAGS },
65
+ { uuid: ViewportsManager.uuid },
66
+ { uuid: AppManager.uuid },
67
+ { uuid: UIManager.uuid },
68
+ ) as { components: OBC.Components }
69
+
70
+ await viewportsManager(components)
71
+ const viewports = components.get(ViewportsManager)
72
+ // @ts-ignore
73
+ const { element: viewport } = [...viewports._instances.values()][0]
74
+
75
+ const app = getAppManager(components)
76
+ await app.init({
77
+ client,
78
+ icons: appIcons,
79
+ componentSetups: { ... },
80
+ grid: (grid) => { ... },
81
+ })
82
+ }
83
+
84
+ main().catch(console.error)
85
+ ```
86
+
87
+ ### `client.setup()` — Platform initialization
88
+
89
+ - **First argument** — libraries object (`OBC`, `OBF`, `BUI`, `CUI`, `THREE`, `FRAGS`, etc.)
90
+ - **Subsequent arguments** — `{ uuid }` for each platform built-in component needed. Always present: `ViewportsManager`, `AppManager`, `UIManager`
91
+
92
+ Custom components (from `bim-components/`) do not need to be listed here — they self-register via `components.add()` in their constructor.
93
+
94
+ ---
95
+
96
+ ## `componentSetups` in `app.init()`
97
+
98
+ ```ts
99
+ componentSetups: {
100
+ core: [uiManager, fragmentsManager, highlighter, ifcLoader],
101
+ lazy: [
102
+ { uuid: MyComponent.uuid, fn: myComponentSetup },
103
+ ]
104
+ }
105
+ ```
106
+
107
+ **`core`** — runs before the grid mounts, in parallel. `uiManager` always goes here. Other engine component setups (fragments, highlighter, IFC loader) also typically go here.
108
+
109
+ **`lazy`** — runs the first time the corresponding component is instantiated. The `uuid` is imported from the `bim-components/` barrel. Use for custom components that can be loaded on demand.
110
+
111
+ The `uuid` of each custom component — a static property defined on the class — is imported from the `bim-components/` barrel when referencing it in `lazy`:
112
+
113
+ ```ts
114
+ import { MyComponent } from "../bim-components"
115
+
116
+ componentSetups: {
117
+ lazy: [
118
+ { uuid: MyComponent.uuid, fn: myComponentSetup },
119
+ ]
120
+ }
121
+ ```
122
+
123
+ When it's not obvious whether a custom component should be `core` or `lazy`, **decide based on whether the component is needed at boot or only on demand.**