@corva/create-app 0.0.0-210a1a8

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 (204) hide show
  1. package/README.md +220 -0
  2. package/bin/cca.js +5 -0
  3. package/bin/create-corva-app.cjs +25 -0
  4. package/common/node/.env +15 -0
  5. package/common/node/.env.sample +26 -0
  6. package/common/node/gitignore +130 -0
  7. package/common/package.json +3 -0
  8. package/common/python/.env +5 -0
  9. package/common/python/.env.sample +7 -0
  10. package/common/python/Makefile +18 -0
  11. package/common/python/gitignore +161 -0
  12. package/common/python/pyproject.toml +25 -0
  13. package/common/python/uv.lock +381 -0
  14. package/common/ui/config/vite/corva-vite-config.mjs +384 -0
  15. package/common/ui/devApp.js +1 -0
  16. package/common/ui/index.html +17 -0
  17. package/common/ui/isolatedApp.js +1 -0
  18. package/common/ui/public/DevCenterIsolatedAppPage.html +26 -0
  19. package/common/ui/public/favicon.ico +0 -0
  20. package/common/ui/vite.config.mjs +3 -0
  21. package/common/ui/vitest.global-setup.js +4 -0
  22. package/common/ui/vitest.setup.js +26 -0
  23. package/lib/commands/attach.js +28 -0
  24. package/lib/commands/create.js +471 -0
  25. package/lib/commands/release.js +73 -0
  26. package/lib/commands/rerun.js +34 -0
  27. package/lib/commands/zip.js +39 -0
  28. package/lib/constants/cache.js +5 -0
  29. package/lib/constants/cli.js +32 -0
  30. package/lib/constants/manifest.js +263 -0
  31. package/lib/constants/messages.js +15 -0
  32. package/lib/constants/package.js +300 -0
  33. package/lib/flow.js +59 -0
  34. package/lib/flows/attach.js +8 -0
  35. package/lib/flows/lib/api.js +385 -0
  36. package/lib/flows/lib/create-zip-archive.js +83 -0
  37. package/lib/flows/lib/json.js +30 -0
  38. package/lib/flows/lib/manifest.js +81 -0
  39. package/lib/flows/lib/notification.js +142 -0
  40. package/lib/flows/lib/step-error.js +10 -0
  41. package/lib/flows/lib/waitForMs.js +3 -0
  42. package/lib/flows/prepare.js +6 -0
  43. package/lib/flows/release.js +30 -0
  44. package/lib/flows/rerun.js +8 -0
  45. package/lib/flows/steps/attach/add-app-to-stream.js +23 -0
  46. package/lib/flows/steps/attach/get-all-live-assets.js +135 -0
  47. package/lib/flows/steps/attach/index.js +5 -0
  48. package/lib/flows/steps/attach/prepare-data.js +19 -0
  49. package/lib/flows/steps/prepare-load-app-files.js +12 -0
  50. package/lib/flows/steps/release/add-label.js +10 -0
  51. package/lib/flows/steps/release/add-notes.js +10 -0
  52. package/lib/flows/steps/release/get-config.js +41 -0
  53. package/lib/flows/steps/release/prepare-data.js +12 -0
  54. package/lib/flows/steps/release/publish.js +11 -0
  55. package/lib/flows/steps/release/remove-failed-upload.js +21 -0
  56. package/lib/flows/steps/release/resolve-prebuilt-zip.js +35 -0
  57. package/lib/flows/steps/release/upload-zip-to-corva.js +136 -0
  58. package/lib/flows/steps/release/wait-for-build.js +36 -0
  59. package/lib/flows/steps/rerun/create-task.js +77 -0
  60. package/lib/flows/steps/rerun/ensure-that-app-in-stream.js +68 -0
  61. package/lib/flows/steps/rerun/get-app-version.js +111 -0
  62. package/lib/flows/steps/rerun/prepare-data.js +162 -0
  63. package/lib/flows/steps/rerun/prepare-well-and-stream-data.js +188 -0
  64. package/lib/flows/steps/rerun/rerun.js +13 -0
  65. package/lib/flows/steps/zip-cleanup.js +17 -0
  66. package/lib/flows/steps/zip-create-archive.js +15 -0
  67. package/lib/flows/steps/zip-file-list-resolve.js +273 -0
  68. package/lib/flows/steps/zip-prepare.js +20 -0
  69. package/lib/flows/steps/zip.js +6 -0
  70. package/lib/flows/zip-simple.js +8 -0
  71. package/lib/flows/zip.js +7 -0
  72. package/lib/helpers/cli-version.js +163 -0
  73. package/lib/helpers/commands.js +13 -0
  74. package/lib/helpers/logger.js +35 -0
  75. package/lib/helpers/manifest.js +82 -0
  76. package/lib/helpers/resolve-app-runtime.js +143 -0
  77. package/lib/helpers/utils.js +101 -0
  78. package/lib/helpers/versioning.js +94 -0
  79. package/lib/main.js +63 -0
  80. package/lib/options/api-key.js +6 -0
  81. package/lib/options/app-key.js +6 -0
  82. package/lib/options/app-version.js +3 -0
  83. package/lib/options/bump-version.js +19 -0
  84. package/lib/options/cache.js +11 -0
  85. package/lib/options/env.js +3 -0
  86. package/lib/options/original-cwd.js +3 -0
  87. package/lib/options/silent.js +3 -0
  88. package/lib/options/zip-file-name.js +18 -0
  89. package/package.json +1 -0
  90. package/template_extensions/corva/.commitlintrc.json +6 -0
  91. package/template_extensions/corva/.eslintrc +32 -0
  92. package/template_extensions/corva/.github/pull_request_template.md +14 -0
  93. package/template_extensions/corva/.github/workflows/code-checks.yml +15 -0
  94. package/template_extensions/corva/.github/workflows/develop.yml +19 -0
  95. package/template_extensions/corva/.github/workflows/feat-fix-delete.yml +14 -0
  96. package/template_extensions/corva/.github/workflows/feat-fix.yml +23 -0
  97. package/template_extensions/corva/.github/workflows/release-fix-X.X.X.yml +16 -0
  98. package/template_extensions/corva/.github/workflows/validate-pr-title.yml +19 -0
  99. package/template_extensions/corva/.husky/commit-msg +5 -0
  100. package/template_extensions/corva/.husky/pre-commit +4 -0
  101. package/template_extensions/corva/.release-please-manifest.json +3 -0
  102. package/template_extensions/corva/release-please-config.json +10 -0
  103. package/templates/scheduler_data-time/javascript/README.md +19 -0
  104. package/templates/scheduler_data-time/javascript/__tests__/processor.spec.js +15 -0
  105. package/templates/scheduler_data-time/javascript/index.js +15 -0
  106. package/templates/scheduler_data-time/python/README.md +31 -0
  107. package/templates/scheduler_data-time/python/lambda_function.py +7 -0
  108. package/templates/scheduler_data-time/python/test/__init__.py +0 -0
  109. package/templates/scheduler_data-time/python/test/app_test.py +10 -0
  110. package/templates/scheduler_data-time/typescript/README.md +25 -0
  111. package/templates/scheduler_data-time/typescript/__tests__/processor.spec.ts +15 -0
  112. package/templates/scheduler_data-time/typescript/index.ts +8 -0
  113. package/templates/scheduler_depth/javascript/README.md +19 -0
  114. package/templates/scheduler_depth/javascript/__tests__/processor.spec.js +17 -0
  115. package/templates/scheduler_depth/javascript/index.js +15 -0
  116. package/templates/scheduler_depth/python/README.md +31 -0
  117. package/templates/scheduler_depth/python/lambda_function.py +7 -0
  118. package/templates/scheduler_depth/python/test/__init__.py +0 -0
  119. package/templates/scheduler_depth/python/test/app_test.py +10 -0
  120. package/templates/scheduler_depth/typescript/README.md +25 -0
  121. package/templates/scheduler_depth/typescript/__tests__/processor.spec.ts +17 -0
  122. package/templates/scheduler_depth/typescript/index.ts +8 -0
  123. package/templates/scheduler_natural-time/javascript/README.md +19 -0
  124. package/templates/scheduler_natural-time/javascript/__tests__/processor.spec.js +15 -0
  125. package/templates/scheduler_natural-time/javascript/index.js +15 -0
  126. package/templates/scheduler_natural-time/python/README.md +31 -0
  127. package/templates/scheduler_natural-time/python/lambda_function.py +7 -0
  128. package/templates/scheduler_natural-time/python/test/__init__.py +0 -0
  129. package/templates/scheduler_natural-time/python/test/app_test.py +10 -0
  130. package/templates/scheduler_natural-time/typescript/README.md +25 -0
  131. package/templates/scheduler_natural-time/typescript/__tests__/processor.spec.ts +15 -0
  132. package/templates/scheduler_natural-time/typescript/index.ts +8 -0
  133. package/templates/stream_depth/javascript/README.md +19 -0
  134. package/templates/stream_depth/javascript/__tests__/processor.spec.js +20 -0
  135. package/templates/stream_depth/javascript/index.js +14 -0
  136. package/templates/stream_depth/python/README.md +31 -0
  137. package/templates/stream_depth/python/lambda_function.py +7 -0
  138. package/templates/stream_depth/python/test/__init__.py +0 -0
  139. package/templates/stream_depth/python/test/app_test.py +16 -0
  140. package/templates/stream_depth/typescript/README.md +25 -0
  141. package/templates/stream_depth/typescript/__tests__/processor.spec.ts +20 -0
  142. package/templates/stream_depth/typescript/index.ts +8 -0
  143. package/templates/stream_time/javascript/README.md +19 -0
  144. package/templates/stream_time/javascript/__tests__/processor.spec.js +14 -0
  145. package/templates/stream_time/javascript/index.js +14 -0
  146. package/templates/stream_time/python/README.md +31 -0
  147. package/templates/stream_time/python/lambda_function.py +7 -0
  148. package/templates/stream_time/python/test/__init__.py +0 -0
  149. package/templates/stream_time/python/test/app_test.py +16 -0
  150. package/templates/stream_time/typescript/README.md +25 -0
  151. package/templates/stream_time/typescript/__tests__/processor.spec.ts +14 -0
  152. package/templates/stream_time/typescript/index.ts +8 -0
  153. package/templates/task/javascript/README.md +19 -0
  154. package/templates/task/javascript/__tests__/processor.spec.js +16 -0
  155. package/templates/task/javascript/index.js +15 -0
  156. package/templates/task/python/README.md +31 -0
  157. package/templates/task/python/lambda_function.py +7 -0
  158. package/templates/task/python/test/__init__.py +0 -0
  159. package/templates/task/python/test/app_test.py +8 -0
  160. package/templates/task/typescript/README.md +25 -0
  161. package/templates/task/typescript/__tests__/processor.spec.ts +16 -0
  162. package/templates/task/typescript/index.ts +8 -0
  163. package/templates/ui/javascript/.codex/config.toml +3 -0
  164. package/templates/ui/javascript/.cursor/mcp.json +8 -0
  165. package/templates/ui/javascript/.eslintrc +13 -0
  166. package/templates/ui/javascript/.mcp.json +8 -0
  167. package/templates/ui/javascript/.prettierrc +1 -0
  168. package/templates/ui/javascript/AGENTS.md +306 -0
  169. package/templates/ui/javascript/CLAUDE.md +1 -0
  170. package/templates/ui/javascript/README.md +31 -0
  171. package/templates/ui/javascript/gitignore +27 -0
  172. package/templates/ui/javascript/src/App.completion.jsx +52 -0
  173. package/templates/ui/javascript/src/App.drilling.jsx +49 -0
  174. package/templates/ui/javascript/src/App.module.scss +17 -0
  175. package/templates/ui/javascript/src/AppSettings.jsx +28 -0
  176. package/templates/ui/javascript/src/__tests__/App.test.jsx +26 -0
  177. package/templates/ui/javascript/src/__tests__/AppSettings.test.jsx +28 -0
  178. package/templates/ui/javascript/src/__tests__/TestsExample.test.jsx +37 -0
  179. package/templates/ui/javascript/src/assets/logo.svg +7 -0
  180. package/templates/ui/javascript/src/constants.js +3 -0
  181. package/templates/ui/javascript/src/index.js +8 -0
  182. package/templates/ui/typescript/.codex/config.toml +3 -0
  183. package/templates/ui/typescript/.cursor/mcp.json +8 -0
  184. package/templates/ui/typescript/.eslintrc +31 -0
  185. package/templates/ui/typescript/.mcp.json +8 -0
  186. package/templates/ui/typescript/.prettierrc +1 -0
  187. package/templates/ui/typescript/AGENTS.md +347 -0
  188. package/templates/ui/typescript/CLAUDE.md +1 -0
  189. package/templates/ui/typescript/README.md +31 -0
  190. package/templates/ui/typescript/gitignore +27 -0
  191. package/templates/ui/typescript/src/App.completion.tsx +52 -0
  192. package/templates/ui/typescript/src/App.drilling.tsx +49 -0
  193. package/templates/ui/typescript/src/App.module.scss +17 -0
  194. package/templates/ui/typescript/src/AppSettings.tsx +28 -0
  195. package/templates/ui/typescript/src/__mocks__/mockData.ts +22 -0
  196. package/templates/ui/typescript/src/__tests__/App.test.tsx +27 -0
  197. package/templates/ui/typescript/src/__tests__/AppSettings.test.tsx +28 -0
  198. package/templates/ui/typescript/src/__tests__/TestsExample.test.tsx +37 -0
  199. package/templates/ui/typescript/src/assets/logo.svg +7 -0
  200. package/templates/ui/typescript/src/constants.ts +3 -0
  201. package/templates/ui/typescript/src/custom.d.ts +19 -0
  202. package/templates/ui/typescript/src/index.js +8 -0
  203. package/templates/ui/typescript/src/types.ts +3 -0
  204. package/templates/ui/typescript/tsconfig.json +20 -0
@@ -0,0 +1,31 @@
1
+ {
2
+ "root": true,
3
+ "globals": {
4
+ "vi": "readonly"
5
+ },
6
+ "parser": "@typescript-eslint/parser",
7
+ "parserOptions": {
8
+ "ecmaVersion": 2020,
9
+ "sourceType": "module",
10
+ "ecmaFeature": {
11
+ "jsx": true
12
+ }
13
+ },
14
+ "extends": ["@corva/eslint-config-browser"],
15
+ "overrides": [
16
+ {
17
+ "files": ["*.ts", "*.tsx"],
18
+ "extends": ["@corva/eslint-config-browser", "plugin:@typescript-eslint/recommended"],
19
+ "rules": {
20
+ "@typescript-eslint/no-explicit-any": "off",
21
+ "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
22
+ /* Turned off until adopted by @corva/eslint-config-browser */
23
+ "react/prop-types": 0,
24
+ "react/default-props-match-prop-types": 0,
25
+ "react/no-unused-prop-types": 0,
26
+ "react/require-default-props": 0
27
+ /* Turned off until adopted by @corva/eslint-config-browser */
28
+ }
29
+ }
30
+ ]
31
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "corva-ui": {
4
+ "command": "npx",
5
+ "args": ["-p", "@corva/ui", "corva-ui-mcp"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ "@corva/eslint-config-browser/prettier"
@@ -0,0 +1,347 @@
1
+ # AGENTS.md
2
+
3
+ Corva.AI platform UI app — a React dashboard widget scaffolded by `create-corva-app`.
4
+ Deployed to the Corva platform via `yarn release`.
5
+
6
+ ## Critical Rules
7
+
8
+ These constraints are enforced by the platform. Violating them breaks the build or runtime.
9
+
10
+ 1. **Root export** — `src/index.js` MUST default-export `{ component: App, settings: AppSettings }`. The component may also be a `ParentApp` wrapper (e.g., `{ component: ParentApp, settings: AppSettings }`) that wraps `App` with Context.Providers and prop initialization. This is the platform loader contract.
11
+ 2. **UI components** — Use only `@corva/ui` components. Import from `@corva/ui/componentsV2`; fall back to `@corva/ui/components` only when a V2 version doesn't exist yet. `@material-ui/core` v4 is available as the underlying library.
12
+ 3. **HTTP clients** — Use `corvaDataAPI` / `corvaAPI` from `@corva/ui/clients` for all network requests.
13
+ 4. **Do not rename or delete** `App.tsx`, `AppSettings.tsx`, or `index.js` — they are platform entry points.
14
+
15
+ ## Project Structure
16
+
17
+ ```
18
+ src/
19
+ App.tsx — Main app component (default export required)
20
+ AppSettings.tsx — Settings panel component (default export required)
21
+ index.js — Root export: { component, settings }
22
+ constants.ts — Default settings values
23
+ types.ts — Custom app settings interface
24
+ custom.d.ts — Module declarations for CSS/SVG imports
25
+ App.module.scss — Component styles (SCSS modules)
26
+ __tests__/ — Vitest test files
27
+ __mocks__/mockData.ts — Mock data for tests
28
+ assets/ — Static assets (SVGs, images)
29
+ config/vite/ — Shared Corva Vite config
30
+ vite.config.mjs — Vite entry config
31
+ vitest.setup.js — Test environment setup
32
+ index.html — Dev server host page
33
+ tsconfig.json — TypeScript configuration
34
+ manifest.json — Corva platform app metadata (generated at scaffold time)
35
+ ```
36
+
37
+ ## Key Patterns
38
+
39
+ ### Component Structure
40
+
41
+ - Use `useAppCommons()` from `@corva/ui/effects` to access platform data: `appKey`, `appSettings`, `well`, `rig`, `fracFleet`, `wells`, `currentUser`, `onSettingChange`, `onSettingsChange`, etc.
42
+ - Define custom app settings shape in `src/types.ts` (`CustomAppSettings`).
43
+ - Platform types (Well, Rig, FracFleet, User, etc.) are typed in `@corva/ui` — do NOT duplicate them.
44
+
45
+ ```typescript
46
+ // App component pattern — use useAppCommons() instead of props
47
+ const App = () => {
48
+ const { appKey, well, rig, appSettings } = useAppCommons();
49
+ const { isExampleCheckboxChecked } = appSettings || {};
50
+ // ...
51
+ };
52
+
53
+ // AppSettings component pattern — same hook
54
+ const AppSettings = () => {
55
+ const { appSettings, onSettingChange } = useAppCommons();
56
+ // ...
57
+ };
58
+ ```
59
+
60
+ ### Styling
61
+
62
+ The preferred way to write styles is SCSS (`.scss` files).
63
+
64
+ Every `.scss` file must import the shared utilities:
65
+
66
+ ```scss
67
+ @import '@corva/ui/styles/common';
68
+ ```
69
+
70
+ This provides all functions, variables, and mixins below.
71
+
72
+ For custom shared variables or mixins specific to the project, create a `src/styles/` directory (e.g. `_variables.scss`, `_common.scss`) and import them alongside the `@corva/ui` import.
73
+
74
+ **`spacing($top, $right?, $bottom?, $left?)`** — 8px base unit:
75
+
76
+ ```scss
77
+ padding: spacing(2); // 16px
78
+ margin: spacing(1, 0); // 8px 0
79
+ gap: spacing(0.5); // 4px
80
+ padding: spacing(2, 1, 2, 1); // 16px 8px 16px 8px
81
+ ```
82
+
83
+ **`colorAlpha($color, $opacity)`** — transparency:
84
+
85
+ ```scss
86
+ background: colorAlpha($palette_t1, 0.08); // white at 8% opacity
87
+ border: 1px solid colorAlpha($palette_t1, 0.12);
88
+ ```
89
+
90
+ **`transition($properties...)`** — standard `cubic-bezier(0.4, 0, 0.2, 1) 0.15s`:
91
+
92
+ ```scss
93
+ transition: transition(opacity);
94
+ transition: transition(color, background-color);
95
+ ```
96
+
97
+ **Full example:**
98
+
99
+ ```scss
100
+ @import '@corva/ui/styles/common';
101
+
102
+ .container {
103
+ padding: spacing(2);
104
+ background: $palette_b5;
105
+ color: $palette_t1;
106
+ transition: transition(opacity, background-color);
107
+
108
+ &:hover {
109
+ background: colorAlpha($palette_t1, 0.08);
110
+ }
111
+ }
112
+
113
+ .label {
114
+ color: $palette_t7;
115
+ font-size: 12px;
116
+ }
117
+ ```
118
+
119
+ **Import in component:**
120
+
121
+ ```typescript
122
+ import styles from './App.module.scss';
123
+ // Use: <div className={styles.container}>
124
+ ```
125
+
126
+ **Colors & Theming:**
127
+
128
+ NEVER hardcode color values (hex, rgb, hsl). Always use `@corva/ui` theme colors:
129
+
130
+ - In SCSS: use SCSS variables from `@corva/ui/styles/common` like `$palette_t1`, `$palette_b5`, `$palette_t7`
131
+ - Use `colorAlpha($color, $opacity)` for transparency instead of raw `rgba()`
132
+ - Run MCP tool `get_theme_docs` (section: "variables") to see all available theme variables
133
+ - Run MCP tool `get_theme_docs` (section: "palette") to see available palette colors with hex values
134
+
135
+ **Rules:**
136
+
137
+ - No inline `style={{...}}` unless absolutely necessary for dynamic values
138
+ - Use `classnames` for conditional/composed classes, never manual string joins
139
+ - No global selectors (tag selectors like `div`, `span`) — use `:global()` only when absolutely necessary
140
+ - No `!important`
141
+ - Use a descriptive camelCase class as the root selector (e.g. `.toolbar`, `.chartPanel`) instead of generic `.root`
142
+
143
+ ### State Management (Zustand)
144
+
145
+ The Corva platform can render multiple instances of the same app simultaneously. If a Zustand store is created at module level (singleton), all instances share the same state. To avoid this, always create store instances inside a React Context provider.
146
+
147
+ **Store factory + context** — create a factory function and a context to hold the store instance:
148
+
149
+ ```typescript
150
+ // store/createAppStore.ts
151
+ import { createContext } from 'react';
152
+ import { create } from 'zustand';
153
+
154
+ import { AppStore } from '~/types';
155
+
156
+ export const createAppStore = (initProps?: Partial<SavedSettings>) => {
157
+ return create<AppStore>()((set, get) => ({
158
+ // Compose sub-stores
159
+ ...createSettingsStore(initProps?.settings)(set, get),
160
+ ...createDataStore()(set, get),
161
+ }));
162
+ };
163
+
164
+ export const StoreContext = createContext<ReturnType<typeof createAppStore> | null>(null);
165
+ ```
166
+
167
+ **Provider** — create the store once per app mount and pass it via context:
168
+
169
+ ```typescript
170
+ // StoreProvider.tsx
171
+ import { FC, PropsWithChildren, useState } from 'react';
172
+
173
+ import { StoreContext, createAppStore } from '~/store/createAppStore';
174
+
175
+ export const StoreProvider: FC<PropsWithChildren<{ savedSettings: SavedSettings }>> = ({
176
+ children,
177
+ savedSettings,
178
+ }) => {
179
+ const [appStore] = useState(() => createAppStore(savedSettings));
180
+
181
+ return <StoreContext.Provider value={appStore}>{children}</StoreContext.Provider>;
182
+ };
183
+ ```
184
+
185
+ **Consumer hooks** — read from context, never from a global store:
186
+
187
+ ```typescript
188
+ // hooks/useAppStore.ts
189
+ import { useContext } from 'react';
190
+ import { useStore } from 'zustand';
191
+ import { useStoreWithEqualityFn } from 'zustand/traditional';
192
+
193
+ import { StoreContext } from '~/store/createAppStore';
194
+ import { AppStore } from '~/types';
195
+
196
+ /** Read a single top-level key from the store. */
197
+ export const useAppStore = <T extends keyof AppStore>(key: T): AppStore[T] => {
198
+ const store = useContext(StoreContext);
199
+ if (!store) throw new Error('useAppStore must be used within StoreProvider');
200
+ return useStore(store, state => state[key]);
201
+ };
202
+
203
+ /** Subscribe to a derived value with optional custom equality. */
204
+ export const useAppStoreSelector = <R>(
205
+ selector: (state: AppStore) => R,
206
+ equalityFn?: (a: R, b: R) => boolean
207
+ ): R => {
208
+ const store = useContext(StoreContext);
209
+ if (!store) throw new Error('useAppStoreSelector must be used within StoreProvider');
210
+ return useStoreWithEqualityFn(store, selector, equalityFn);
211
+ };
212
+ ```
213
+
214
+ **Usage in components:**
215
+
216
+ ```typescript
217
+ const isLegendVisible = useAppStore('isLegendVisible');
218
+ const selectedChannels = useAppStoreSelector(state => state.selectedChannels);
219
+ ```
220
+
221
+ **Wire it up in `ParentApp`** (see full example with `QueryClientProvider` in the Data Fetching section below).
222
+
223
+ General rules:
224
+
225
+ - Name stores `use[Name]Store` (e.g., `useWellDataStore`)
226
+ - Use selectors: `const value = useAppStore('value')`
227
+ - Keep business logic in store actions
228
+
229
+ ### Data Fetching (React Query v4)
230
+
231
+ Same as Zustand: the platform runs multiple app instances, so each must have its own `QueryClient`. Never create a `QueryClient` at module level — use a hook with `useState` to create it once per mount.
232
+
233
+ **`useQueryClient` hook** — creates an isolated `QueryClient` per app instance:
234
+
235
+ ```typescript
236
+ // hooks/useQueryClient.ts
237
+ import { useState } from 'react';
238
+ import { QueryCache, QueryClient } from '@tanstack/react-query';
239
+ import { showErrorNotification } from '@corva/ui/utils';
240
+
241
+ export const useQueryClient = () => {
242
+ const [queryClient] = useState(
243
+ new QueryClient({
244
+ queryCache: new QueryCache({
245
+ onError: (error: unknown) => {
246
+ if (typeof error === 'object' && error !== null && 'message' in error) {
247
+ showErrorNotification(`Something went wrong: ${String(error.message)}`);
248
+ }
249
+ },
250
+ }),
251
+ defaultOptions: {
252
+ queries: {
253
+ refetchOnWindowFocus: false,
254
+ refetchOnReconnect: false,
255
+ },
256
+ },
257
+ })
258
+ );
259
+
260
+ return queryClient;
261
+ };
262
+ ```
263
+
264
+ **Wire it up in `ParentApp`** — wrap the app tree with `QueryClientProvider`:
265
+
266
+ ```typescript
267
+ import { QueryClientProvider } from '@tanstack/react-query';
268
+
269
+ const ParentApp = () => {
270
+ const { appSettings, onSettingsChange } = useAppCommons();
271
+ const queryClient = useQueryClient();
272
+
273
+ return (
274
+ <QueryClientProvider client={queryClient}>
275
+ <StoreProvider savedSettings={appSettings?.savedSettings}>
276
+ <App />
277
+ </StoreProvider>
278
+ </QueryClientProvider>
279
+ );
280
+ };
281
+
282
+ export default { component: ParentApp, settings: AppSettings };
283
+ ```
284
+
285
+ General rules:
286
+
287
+ - Encapsulate queries in custom hooks (e.g., `useWellData`)
288
+ - Use typed query keys (array format)
289
+ - Always handle `isLoading` and `isError` states
290
+
291
+ ## Naming Conventions
292
+
293
+ - Boolean variables: prefix with `is`, `has`, or `should`
294
+ - Non-empty array checks: `!!array.length` (not `array.length > 0`)
295
+ - Zustand stores: `use[StoreName]Store`
296
+ - TypeScript props: use `interface` (not `type`)
297
+
298
+ ## Testing
299
+
300
+ - **Framework:** Vitest + React Testing Library
301
+ - **Wrapper:** Use `AppTestWrapper` from `@corva/ui/testing` to wrap components — it provides `useAppCommons()` context
302
+ - **Pattern:** Pass data via `AppTestWrapper` props, render components with no props
303
+ - **Mocking:** Mock network requests (`vi.mock`/`vi.fn` or msw), never call real APIs
304
+ - **Timezone:** UTC is enforced (`process.env.TZ = 'UTC'`)
305
+ - **Mocked globals:** `ResizeObserver`, `MutationObserver` (in `vitest.setup.js`)
306
+
307
+ ```tsx
308
+ // Test pattern — data flows through AppTestWrapper context
309
+ render(
310
+ <AppTestWrapper appSettings={{ isExampleCheckboxChecked: true }} well={mockWell}>
311
+ <App />
312
+ </AppTestWrapper>
313
+ );
314
+ ```
315
+
316
+ ## Commands
317
+
318
+ ```
319
+ yarn start Dev server at http://app.local.corva.ai:8080/
320
+ yarn build Production bundle
321
+ yarn test Run tests
322
+ yarn lint ESLint check
323
+ yarn typecheck Type check (the build does not typecheck)
324
+ yarn zip Create deployment ZIP
325
+ yarn release Release to Corva platform
326
+ ```
327
+
328
+ ## @corva/ui Documentation
329
+
330
+ Local documentation for `@corva/ui` is bundled at `node_modules/@corva/ui/docs/`.
331
+ Before implementing any UI component, data fetch, or API call, read the relevant doc file:
332
+
333
+ - **V2 Components:** `node_modules/@corva/ui/docs/01-components-v2/` — Props, examples, usage
334
+ - **V1 Components:** `node_modules/@corva/ui/docs/02-components-v1/` — Legacy components reference
335
+ - **Hooks:** `node_modules/@corva/ui/docs/03-hooks/` — Parameters, return types, examples
336
+ - **API Clients:** `node_modules/@corva/ui/docs/04-clients/` — corvaAPI, corvaDataAPI endpoints
337
+ - **Theme:** `node_modules/@corva/ui/docs/05-theme/` — Color palette, CSS variables
338
+ - **Utilities:** `node_modules/@corva/ui/docs/06-utilities/` — Helper functions
339
+ - **Constants:** `node_modules/@corva/ui/docs/07-constants/` — Platform constants
340
+
341
+ Start with `node_modules/@corva/ui/docs/README.md` for the full index.
342
+ Do NOT guess `@corva/ui` APIs — read the local docs first.
343
+
344
+ ## MCP Server (Interactive Fallback)
345
+
346
+ The `corva-ui` MCP server is pre-configured (`.mcp.json`, `.cursor/mcp.json`, `.codex/config.toml`).
347
+ Use it for interactive queries when the local docs are insufficient or you need to search across components.
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,31 @@
1
+ # Getting Started with Create Corva App
2
+
3
+ ## Available Scripts
4
+
5
+ In the project directory, you can run:
6
+
7
+ ### `yarn start`
8
+
9
+ Runs the app in the development mode.\
10
+ Open [http://app.local.corva.ai:8080](http://app.local.corva.ai:8080/) to view it in the browser.
11
+
12
+ The page will reload if you make edits.\
13
+ You will also see any lint errors in the console.
14
+
15
+ ### `yarn build`
16
+
17
+ Bundles the app into static files for production.
18
+
19
+ ### `yarn zip`
20
+
21
+ Bundles the app into ZIP file in app root directory
22
+
23
+ ### `yarn release`
24
+
25
+ Releases the app into Corva
26
+
27
+ ## Documentation
28
+
29
+ - [Dev Center documentation](https://dc-docs.corva.ai/) – information about development process
30
+ - [Component Library](https://dc-docs.corva.ai/docs/Frontend/Data%20visualization/Components%20library)
31
+ - [Datasets documentation](https://dc-docs.corva.ai/docs/Datasets/Link%20App%20to%20Dataset)
@@ -0,0 +1,27 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+
8
+ # testing
9
+ /coverage
10
+
11
+ # production
12
+ /build
13
+ /dist
14
+
15
+ # misc
16
+ .env
17
+ .DS_Store
18
+ .env.local
19
+ .env.development.local
20
+ .env.test.local
21
+ .env.production.local
22
+ .idea
23
+
24
+ npm-debug.log*
25
+ yarn-debug.log*
26
+ yarn-error.log*
27
+ .eslintcache
@@ -0,0 +1,52 @@
1
+ import { AppContainer, AppHeader } from '@corva/ui/componentsV2';
2
+ import { useAppCommons } from '@corva/ui/effects';
3
+
4
+ import { DEFAULT_SETTINGS } from './constants';
5
+ import logo from './assets/logo.svg';
6
+
7
+ import styles from './App.module.scss';
8
+
9
+ const App = () => {
10
+ const { appKey, fracFleet, well, wells, appSettings } = useAppCommons();
11
+ const { isExampleCheckboxChecked = DEFAULT_SETTINGS.isExampleCheckboxChecked } =
12
+ appSettings || {};
13
+ // NOTE: On general type dashboard app receives wells array
14
+ // on asset type dashboard app receives well object
15
+ const wellsList = wells || [well];
16
+
17
+ return (
18
+ <AppContainer header={<AppHeader />} testId={appKey}>
19
+ <div className={styles.container}>
20
+ <img src={logo} alt="logo" className={styles.logo} />
21
+ <p>
22
+ Edit <code>src/App.js</code> and save to reload.
23
+ <br />
24
+ <br />
25
+ </p>
26
+ <p>
27
+ Frac Fleet: <span data-testid="fracFleet">{fracFleet?.name || 'No Frac Fleet'}</span>
28
+ <br />
29
+ Wells: <span data-testid="wellsList">{wellsList.map(well => well?.name).join(', ')}</span>
30
+ </p>
31
+ <a
32
+ className="App-link"
33
+ href="https://reactjs.org"
34
+ target="_blank"
35
+ rel="noopener noreferrer"
36
+ >
37
+ Learn React
38
+ </a>
39
+ </div>
40
+ <div>
41
+ Settings &quot;Example&quot; checkbox is{' '}
42
+ <span data-testid="exampleCheckboxState">
43
+ {isExampleCheckboxChecked ? 'checked' : 'unchecked'}
44
+ </span>
45
+ </div>
46
+ </AppContainer>
47
+ );
48
+ };
49
+
50
+ // Important: Do not change root component default export (App.js). Use it as container
51
+ // for your App. It's required to make build and zip scripts work as expected;
52
+ export default App;
@@ -0,0 +1,49 @@
1
+ import { AppContainer, AppHeader } from '@corva/ui/componentsV2';
2
+ import { useAppCommons } from '@corva/ui/effects';
3
+
4
+ import { DEFAULT_SETTINGS } from './constants';
5
+ import logo from './assets/logo.svg';
6
+
7
+ import styles from './App.module.scss';
8
+
9
+ const App = () => {
10
+ const { appKey, rig, well, appSettings } = useAppCommons();
11
+ const { isExampleCheckboxChecked = DEFAULT_SETTINGS.isExampleCheckboxChecked } =
12
+ appSettings || {};
13
+
14
+ return (
15
+ <AppContainer header={<AppHeader />} testId={appKey}>
16
+ <div className={styles.container}>
17
+ <img src={logo} alt="logo" className={styles.logo} />
18
+ <p>
19
+ Edit <code>src/App.js</code> and save to reload.
20
+ <br />
21
+ <br />
22
+ </p>
23
+ <p>
24
+ Rig: <span data-testid="rig">{rig?.name}</span>
25
+ <br />
26
+ Well: <span data-testid="well">{well?.name}</span>
27
+ </p>
28
+ <a
29
+ className="App-link"
30
+ href="https://reactjs.org"
31
+ target="_blank"
32
+ rel="noopener noreferrer"
33
+ >
34
+ Learn React
35
+ </a>
36
+ </div>
37
+ <div>
38
+ Settings &quot;Example&quot; checkbox is{' '}
39
+ <span data-testid="exampleCheckboxState">
40
+ {isExampleCheckboxChecked ? 'checked' : 'unchecked'}
41
+ </span>
42
+ </div>
43
+ </AppContainer>
44
+ );
45
+ };
46
+
47
+ // Important: Do not change root component default export (App.js). Use it as container
48
+ // for your App. It's required to make build and zip scripts work as expected;
49
+ export default App;
@@ -0,0 +1,17 @@
1
+ .container {
2
+ text-align: center;
3
+ }
4
+
5
+ @keyframes App-logo-spin {
6
+ from {
7
+ transform: rotate(0deg);
8
+ }
9
+ to {
10
+ transform: rotate(-360deg);
11
+ }
12
+ }
13
+
14
+ .logo {
15
+ animation: App-logo-spin infinite 20s linear;
16
+ height: 50px;
17
+ }
@@ -0,0 +1,28 @@
1
+ import { Checkbox, FormControlLabel } from '@material-ui/core';
2
+ import { useAppCommons } from '@corva/ui/effects';
3
+
4
+ import { DEFAULT_SETTINGS } from './constants';
5
+
6
+ const AppSettings = () => {
7
+ const { appSettings, onSettingChange } = useAppCommons();
8
+ const settings = { ...DEFAULT_SETTINGS, ...appSettings };
9
+
10
+ return (
11
+ <div>
12
+ <FormControlLabel
13
+ label="Example checkbox"
14
+ control={
15
+ <Checkbox
16
+ data-testid="exampleCheckbox"
17
+ checked={settings.isExampleCheckboxChecked}
18
+ onChange={e => onSettingChange('isExampleCheckboxChecked', e.target.checked)}
19
+ />
20
+ }
21
+ />
22
+ </div>
23
+ );
24
+ };
25
+
26
+ // Important: Do not change root component default export (AppSettings.js). Use it as container
27
+ // for your App Settings. It's required to make build and zip scripts work as expected;
28
+ export default AppSettings;
@@ -0,0 +1,22 @@
1
+ export const mockAppSettings = {
2
+ isExampleCheckboxChecked: true,
3
+ };
4
+
5
+ export const mockWell = {
6
+ name: 'Test Well',
7
+ asset_id: 12345,
8
+ last_active_at: '2023-01-01',
9
+ id: '1',
10
+ };
11
+
12
+ export const mockRig = {
13
+ name: 'Test Rig',
14
+ id: '1',
15
+ asset_id: 9999,
16
+ };
17
+
18
+ export const mockFracFleet = {
19
+ name: 'Test Frac Fleet',
20
+ id: '1',
21
+ current_pad_id: 101,
22
+ };
@@ -0,0 +1,27 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { AppTestWrapper } from '@corva/ui/testing';
3
+
4
+ import App from '../App';
5
+ import { mockAppSettings } from '../__mocks__/mockData';
6
+
7
+ describe('<App />', () => {
8
+ it('should show correct layout', () => {
9
+ render(
10
+ <AppTestWrapper appSettings={mockAppSettings}>
11
+ <App />
12
+ </AppTestWrapper>
13
+ );
14
+
15
+ screen.getByText(/checked/i);
16
+ });
17
+
18
+ it('should show correct layout when settings are not provided', () => {
19
+ render(
20
+ <AppTestWrapper appSettings={{}}>
21
+ <App />
22
+ </AppTestWrapper>
23
+ );
24
+
25
+ screen.getByText(/unchecked/i);
26
+ });
27
+ });
@@ -0,0 +1,28 @@
1
+ import { render, screen, act } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { AppTestWrapper } from '@corva/ui/testing';
4
+
5
+ import AppSettings from '../AppSettings';
6
+
7
+ describe('<AppSettings />', () => {
8
+ it('should call onChange with a changed setting on settings change', async () => {
9
+ const handleSettingChange = vi.fn();
10
+
11
+ render(
12
+ <AppTestWrapper
13
+ appSettings={{ isExampleCheckboxChecked: true }}
14
+ onSettingChange={handleSettingChange}
15
+ >
16
+ <AppSettings />
17
+ </AppTestWrapper>
18
+ );
19
+
20
+ const exampleCheckbox = screen.getByRole('checkbox', { name: /example/i });
21
+
22
+ await act(async () => {
23
+ await userEvent.click(exampleCheckbox);
24
+ });
25
+
26
+ expect(handleSettingChange).toBeCalledWith('isExampleCheckboxChecked', false);
27
+ });
28
+ });