@terreno/api 0.0.17 → 0.1.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.
Files changed (77) hide show
  1. package/.claude/CLAUDE.local.md +204 -0
  2. package/.cursor/rules/00-root.mdc +338 -0
  3. package/.github/copilot-instructions.md +333 -0
  4. package/AGENTS.md +333 -0
  5. package/README.md +76 -7
  6. package/biome.jsonc +1 -1
  7. package/dist/api.d.ts +68 -1
  8. package/dist/api.js +140 -5
  9. package/dist/api.query.test.js +1 -1
  10. package/dist/api.test.js +222 -484
  11. package/dist/auth.js +3 -1
  12. package/dist/errors.js +15 -12
  13. package/dist/example.js +7 -7
  14. package/dist/expressServer.d.ts +8 -2
  15. package/dist/expressServer.js +8 -1
  16. package/dist/githubAuth.d.ts +64 -0
  17. package/dist/githubAuth.js +293 -0
  18. package/dist/githubAuth.test.d.ts +1 -0
  19. package/dist/githubAuth.test.js +351 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.js +3 -0
  22. package/dist/logger.js +1 -1
  23. package/dist/middleware.js +1 -1
  24. package/dist/notifiers/googleChatNotifier.js +1 -1
  25. package/dist/notifiers/googleChatNotifier.test.js +1 -1
  26. package/dist/notifiers/slackNotifier.js +1 -1
  27. package/dist/notifiers/slackNotifier.test.js +1 -1
  28. package/dist/notifiers/zoomNotifier.js +1 -1
  29. package/dist/notifiers/zoomNotifier.test.js +1 -1
  30. package/dist/openApi.test.js +8 -5
  31. package/dist/openApiBuilder.d.ts +69 -1
  32. package/dist/openApiBuilder.js +109 -5
  33. package/dist/openApiValidator.d.ts +296 -0
  34. package/dist/openApiValidator.js +698 -0
  35. package/dist/openApiValidator.test.d.ts +1 -0
  36. package/dist/openApiValidator.test.js +346 -0
  37. package/dist/permissions.js +1 -1
  38. package/dist/plugins.test.js +3 -3
  39. package/dist/terrenoPlugin.d.ts +4 -0
  40. package/dist/terrenoPlugin.js +2 -0
  41. package/dist/tests/bunSetup.js +2 -2
  42. package/dist/tests.js +34 -24
  43. package/package.json +7 -2
  44. package/src/__snapshots__/openApi.test.ts.snap +399 -0
  45. package/src/__snapshots__/openApiBuilder.test.ts.snap +108 -0
  46. package/src/api.query.test.ts +1 -1
  47. package/src/api.test.ts +161 -374
  48. package/src/api.ts +210 -4
  49. package/src/auth.ts +3 -1
  50. package/src/errors.ts +15 -12
  51. package/src/example.ts +7 -7
  52. package/src/expressServer.ts +18 -2
  53. package/src/githubAuth.test.ts +223 -0
  54. package/src/githubAuth.ts +335 -0
  55. package/src/index.ts +3 -0
  56. package/src/logger.ts +1 -1
  57. package/src/middleware.ts +1 -1
  58. package/src/notifiers/googleChatNotifier.test.ts +1 -1
  59. package/src/notifiers/googleChatNotifier.ts +1 -1
  60. package/src/notifiers/slackNotifier.test.ts +1 -1
  61. package/src/notifiers/slackNotifier.ts +1 -1
  62. package/src/notifiers/zoomNotifier.test.ts +1 -1
  63. package/src/notifiers/zoomNotifier.ts +1 -1
  64. package/src/openApi.test.ts +8 -5
  65. package/src/openApiBuilder.ts +188 -15
  66. package/src/openApiValidator.test.ts +241 -0
  67. package/src/openApiValidator.ts +860 -0
  68. package/src/permissions.ts +1 -1
  69. package/src/plugins.test.ts +3 -3
  70. package/src/terrenoPlugin.ts +5 -0
  71. package/src/tests/bunSetup.ts +2 -2
  72. package/src/tests.ts +34 -24
  73. package/CLAUDE.md +0 -107
  74. package/dist/response.d.ts +0 -0
  75. package/dist/response.js +0 -1
  76. package/index.ts +0 -1
  77. package/src/response.ts +0 -0
@@ -0,0 +1,204 @@
1
+ # Terreno
2
+
3
+ A monorepo containing shared packages for building full-stack applications with React Native and Express/Mongoose.
4
+
5
+ ## Packages
6
+
7
+ - **api/** - REST API framework built on Express/Mongoose (`@terreno/api`)
8
+ - **ui/** - React Native UI component library (`@terreno/ui`)
9
+ - **rtk/** - Redux Toolkit Query utilities for API backends (`@terreno/rtk`)
10
+ - **demo/** - Demo app for showcasing and testing UI components
11
+ - **example-frontend/** - Example Expo app demonstrating full stack usage
12
+ - **example-backend/** - Example Express backend using @terreno/api
13
+
14
+ ## Development
15
+
16
+ Uses [Bun](https://bun.sh/) as the package manager.
17
+
18
+ ```bash
19
+ bun install # Install dependencies
20
+ bun run compile # Compile all packages
21
+ bun run lint # Lint all packages
22
+ bun run lint:fix # Fix lint issues
23
+ bun run test # Run tests in api and ui
24
+ ```
25
+
26
+ ### Package-specific commands
27
+
28
+ ```bash
29
+ bun run api:test # Test API package
30
+ bun run ui:test # Test UI package
31
+ bun run demo:start # Start demo app
32
+ bun run frontend:web # Start frontend example
33
+ bun run backend:dev # Start backend example
34
+ ```
35
+
36
+ ## How the Packages Work Together
37
+
38
+ The three core packages form a complete full-stack framework:
39
+
40
+ ```
41
+ BACKEND
42
+ @terreno/api
43
+ - Mongoose models with modelRouter -> CRUD endpoints
44
+ - Built-in auth (JWT + Passport)
45
+ - Automatic OpenAPI spec generation
46
+ |
47
+ /openapi.json
48
+ |
49
+ RTK Query SDK Codegen
50
+ |
51
+ FRONTEND
52
+ @terreno/rtk
53
+ - Generated hooks from OpenAPI spec
54
+ - Auth slice with JWT token management
55
+ - Automatic token refresh
56
+ +
57
+ @terreno/ui
58
+ - React Native components (Box, Button, TextField, etc.)
59
+ - TerrenoProvider for theming
60
+ ```
61
+
62
+ ### Integration Flow
63
+
64
+ 1. **Backend (api)**: Define Mongoose models, use `modelRouter` to create CRUD endpoints with permissions
65
+ 2. **OpenAPI Generation**: `setupServer` automatically generates `/openapi.json`
66
+ 3. **SDK Codegen**: Frontend runs `bun run sdk` to generate RTK Query hooks from OpenAPI spec
67
+ 4. **Frontend (rtk + ui)**: Use generated hooks with UI components for type-safe API calls
68
+
69
+ ## Example Apps (Keep These Updated!)
70
+
71
+ The `example-frontend/` and `example-backend/` directories serve as both documentation and integration tests. When adding features to api, ui, or rtk:
72
+
73
+ 1. **Add examples** demonstrating new features
74
+ 2. **Update SDK** after backend changes: `cd example-frontend && bun run sdk`
75
+ 3. **Verify integration** by running both examples together
76
+
77
+ ### Running the Full Stack
78
+
79
+ ```bash
80
+ # Terminal 1: Start backend
81
+ bun run backend:dev
82
+
83
+ # Terminal 2: Start frontend
84
+ bun run frontend:web
85
+ ```
86
+
87
+ ## Code Style
88
+
89
+ ### TypeScript/JavaScript
90
+ - Use ES module syntax and TypeScript for all code
91
+ - Prefer interfaces over types; avoid enums, use maps
92
+ - Prefer const arrow functions over `function` keyword
93
+ - Use descriptive variable names with auxiliary verbs (e.g., `isLoading`)
94
+ - Use camelCase directories (e.g., `components/authWizard`)
95
+ - Favor named exports
96
+ - Use the RORO pattern (Receive an Object, Return an Object)
97
+
98
+ ### Dates and Time
99
+ - Always use Luxon instead of Date or dayjs
100
+
101
+ ### Error Handling
102
+ - Check error conditions at start of functions and return early
103
+ - Limit nested if statements
104
+ - Use multiline syntax with curly braces for all conditionals
105
+
106
+ ### Testing
107
+ - Use bun test with expect for testing
108
+
109
+ ### Logging
110
+ - Frontend: Use `console.info`, `console.debug`, `console.warn`, or `console.error` for permanent logs
111
+ - Backend: Use `logger.info/warn/error/debug` for permanent logs
112
+ - Use `console.log` only for debugging (to be removed)
113
+
114
+ ### Development Practices
115
+ - Don't apologize for errors: fix them
116
+ - Prioritize modularity, DRY, performance, and security
117
+ - Focus on readability over performance
118
+ - Write complete, functional code without TODOs when possible
119
+ - Comments should describe purpose, not effect
120
+
121
+ ## Package Reference
122
+
123
+ ### @terreno/api
124
+
125
+ REST API framework providing:
126
+
127
+ - **modelRouter**: Auto-generates CRUD endpoints for Mongoose models
128
+ - **Permissions**: `IsAuthenticated`, `IsOwner`, `IsAdmin`, `IsAuthenticatedOrReadOnly`
129
+ - **Query Filters**: `OwnerQueryFilter` for filtering list queries by owner
130
+ - **setupServer**: Express server setup with auth, OpenAPI, and middleware
131
+ - **APIError**: Standardized error handling
132
+ - **logger**: Winston-based logging
133
+
134
+ Key imports:
135
+ ```typescript
136
+ import {
137
+ modelRouter,
138
+ setupServer,
139
+ Permissions,
140
+ OwnerQueryFilter,
141
+ APIError,
142
+ logger,
143
+ asyncHandler,
144
+ authenticateMiddleware,
145
+ } from "@terreno/api";
146
+ ```
147
+
148
+ ### @terreno/ui
149
+
150
+ React Native component library with 88+ components:
151
+
152
+ - **Layout**: Box, Page, SplitPage, Card
153
+ - **Forms**: TextField, SelectField, DateTimeField, CheckBox
154
+ - **Display**: Text, Heading, Badge, DataTable
155
+ - **Actions**: Button, IconButton, Link
156
+ - **Feedback**: Spinner, Modal, Toast
157
+ - **Theming**: TerrenoProvider, useTheme
158
+
159
+ Key imports:
160
+ ```typescript
161
+ import {
162
+ Box,
163
+ Button,
164
+ Card,
165
+ Page,
166
+ Text,
167
+ TextField,
168
+ TerrenoProvider,
169
+ } from "@terreno/ui";
170
+ ```
171
+
172
+ ### @terreno/rtk
173
+
174
+ Redux Toolkit Query integration:
175
+
176
+ - **generateAuthSlice**: Creates auth reducer and middleware with JWT handling
177
+ - **emptyApi**: Base RTK Query API for code generation
178
+ - **Platform utilities**: Secure token storage (expo-secure-store for native, AsyncStorage for web)
179
+
180
+ Key imports:
181
+ ```typescript
182
+ import {generateAuthSlice} from "@terreno/rtk";
183
+ ```
184
+
185
+ ## CI/CD Workflows
186
+
187
+ ### Required Secret Validation
188
+
189
+ GitHub Actions workflows that use secrets or environment variables must validate all required variables are set before using them. Add a validation step early in the job that fails fast with a clear error message listing any missing variables.
190
+
191
+ ```yaml
192
+ - name: Validate required secrets
193
+ run: |
194
+ missing=()
195
+ if [ -z "$VAR_NAME" ]; then missing+=("VAR_NAME"); fi
196
+ if [ ${#missing[@]} -ne 0 ]; then
197
+ echo "::error::Missing required secrets: ${missing[*]}"
198
+ exit 1
199
+ fi
200
+ ```
201
+
202
+ ## Dependency Management
203
+
204
+ Uses [Bun Catalogs](https://bun.sh/docs/install/catalogs) - shared versions defined in root `package.json` under `catalog`. Reference with `catalog:` in workspace packages.
@@ -0,0 +1,338 @@
1
+ ---
2
+ description: Terreno monorepo root guidelines
3
+ globs: **/*
4
+ ---
5
+
6
+ # Terreno
7
+
8
+ A monorepo containing shared packages for building full-stack applications with React Native and Express/Mongoose.
9
+
10
+ ## Packages
11
+
12
+ - **api/** - REST API framework built on Express/Mongoose (`@terreno/api`)
13
+ - **ui/** - React Native UI component library (`@terreno/ui`)
14
+ - **rtk/** - Redux Toolkit Query utilities for API backends (`@terreno/rtk`)
15
+ - **mcp-server/** - MCP server for AI assistant integration (`@terreno/mcp-server`)
16
+ - **demo/** - Demo app for showcasing and testing UI components
17
+ - **example-frontend/** - Example Expo app demonstrating full stack usage
18
+ - **example-backend/** - Example Express backend using @terreno/api
19
+
20
+ ## Development
21
+
22
+ Uses [Bun](https://bun.sh/) as the package manager.
23
+
24
+ ```bash
25
+ bun install # Install dependencies
26
+ bun run compile # Compile all packages
27
+ bun run lint # Lint all packages
28
+ bun run lint:fix # Fix lint issues
29
+ bun run test # Run tests in api and ui
30
+ ```
31
+
32
+ ### Package-specific commands
33
+
34
+ ```bash
35
+ bun run api:test # Test API package
36
+ bun run ui:test # Test UI package
37
+ bun run demo:start # Start demo app
38
+ bun run frontend:web # Start frontend example
39
+ bun run backend:dev # Start backend example
40
+ bun run mcp:build # Build MCP server
41
+ bun run mcp:start # Start MCP server
42
+ ```
43
+
44
+ ## How the Packages Work Together
45
+
46
+ The three core packages form a complete full-stack framework:
47
+
48
+ ```
49
+ BACKEND
50
+ @terreno/api
51
+ - Mongoose models with modelRouter -> CRUD endpoints
52
+ - Built-in auth (JWT + Passport)
53
+ - Automatic OpenAPI spec generation
54
+ |
55
+ /openapi.json
56
+ |
57
+ RTK Query SDK Codegen
58
+ |
59
+ FRONTEND
60
+ @terreno/rtk
61
+ - Generated hooks from OpenAPI spec
62
+ - Auth slice with JWT token management
63
+ - Automatic token refresh
64
+ +
65
+ @terreno/ui
66
+ - React Native components (Box, Button, TextField, etc.)
67
+ - TerrenoProvider for theming
68
+ ```
69
+
70
+ ### Integration Flow
71
+
72
+ 1. **Backend (api)**: Define Mongoose models, use `modelRouter` to create CRUD endpoints with permissions
73
+ 2. **OpenAPI Generation**: `setupServer` automatically generates `/openapi.json`
74
+ 3. **SDK Codegen**: Frontend runs `bun run sdk` to generate RTK Query hooks from OpenAPI spec
75
+ 4. **Frontend (rtk + ui)**: Use generated hooks with UI components for type-safe API calls
76
+
77
+ ## Example Apps (Keep These Updated!)
78
+
79
+ The `example-frontend/` and `example-backend/` directories serve as both documentation and integration tests. When adding features to api, ui, or rtk:
80
+
81
+ 1. **Add examples** demonstrating new features
82
+ 2. **Update SDK** after backend changes: `cd example-frontend && bun run sdk`
83
+ 3. **Verify integration** by running both examples together
84
+
85
+ ### Running the Full Stack
86
+
87
+ ```bash
88
+ # Terminal 1: Start backend
89
+ bun run backend:dev
90
+
91
+ # Terminal 2: Start frontend
92
+ bun run frontend:web
93
+ ```
94
+
95
+ ## Code Style
96
+
97
+ ### TypeScript/JavaScript
98
+ - Use ES module syntax and TypeScript for all code
99
+ - Prefer interfaces over types; avoid enums, use maps
100
+ - Prefer const arrow functions over `function` keyword
101
+ - Use descriptive variable names with auxiliary verbs (e.g., `isLoading`)
102
+ - Use camelCase directories (e.g., `components/authWizard`)
103
+ - Favor named exports
104
+ - Use the RORO pattern (Receive an Object, Return an Object)
105
+
106
+ ### Dates and Time
107
+ - Always use Luxon instead of Date or dayjs
108
+
109
+ ### Error Handling
110
+ - Check error conditions at start of functions and return early
111
+ - Limit nested if statements
112
+ - Use multiline syntax with curly braces for all conditionals
113
+
114
+ ### Testing
115
+ - Use bun test with expect for testing
116
+
117
+ ### Logging
118
+ - Frontend: Use `console.info`, `console.debug`, `console.warn`, or `console.error` for permanent logs
119
+ - Backend: Use `logger.info/warn/error/debug` for permanent logs
120
+ - Use `console.log` only for debugging (to be removed)
121
+
122
+ ### Development Practices
123
+ - Don't apologize for errors: fix them
124
+ - Prioritize modularity, DRY, performance, and security
125
+ - Focus on readability over performance
126
+ - Write complete, functional code without TODOs when possible
127
+ - Comments should describe purpose, not effect
128
+
129
+ ## Package Reference
130
+
131
+ ### @terreno/api
132
+
133
+ REST API framework providing:
134
+
135
+ - **modelRouter**: Auto-generates CRUD endpoints for Mongoose models
136
+ - **Permissions**: `IsAuthenticated`, `IsOwner`, `IsAdmin`, `IsAuthenticatedOrReadOnly`
137
+ - **Query Filters**: `OwnerQueryFilter` for filtering list queries by owner
138
+ - **setupServer**: Express server setup with auth, OpenAPI, and middleware
139
+ - **APIError**: Standardized error handling
140
+ - **logger**: Winston-based logging
141
+
142
+ Key imports:
143
+ ```typescript
144
+ import {
145
+ modelRouter,
146
+ setupServer,
147
+ Permissions,
148
+ OwnerQueryFilter,
149
+ APIError,
150
+ logger,
151
+ asyncHandler,
152
+ authenticateMiddleware,
153
+ } from "@terreno/api";
154
+ ```
155
+
156
+ #### modelRouter Usage
157
+
158
+ ```typescript
159
+ import {modelRouter, modelRouterOptions, Permissions} from "@terreno/api";
160
+
161
+ const router = modelRouter(YourModel, {
162
+ permissions: {
163
+ list: [Permissions.IsAuthenticated],
164
+ create: [Permissions.IsAuthenticated],
165
+ read: [Permissions.IsOwner],
166
+ update: [Permissions.IsOwner],
167
+ delete: [], // Disabled
168
+ },
169
+ sort: "-created",
170
+ queryFields: ["_id", "type", "name"],
171
+ });
172
+ ```
173
+
174
+ #### Custom Routes
175
+
176
+ For non-CRUD endpoints, use the OpenAPI builder:
177
+
178
+ ```typescript
179
+ import {asyncHandler, authenticateMiddleware, createOpenApiBuilder} from "@terreno/api";
180
+
181
+ router.get("/yourRoute/:id", [
182
+ authenticateMiddleware(),
183
+ createOpenApiBuilder(options)
184
+ .withTags(["yourTag"])
185
+ .withSummary("Brief summary")
186
+ .withPathParameter("id", {type: "string"})
187
+ .withResponse(200, {data: {type: "object"}})
188
+ .build(),
189
+ ], asyncHandler(async (req, res) => {
190
+ return res.json({data: result});
191
+ }));
192
+ ```
193
+
194
+ #### API Conventions
195
+
196
+ - Throw `APIError` with appropriate status codes: `throw new APIError({status: 400, title: "Message"})`
197
+ - Do not use `Model.findOne` - use `Model.findExactlyOne` or `Model.findOneOrThrow`
198
+ - Define statics/methods by direct assignment: `schema.methods = {bar() {}}`
199
+ - All model types live in `src/modelInterfaces.ts`
200
+ - In routes: `req.user` is `UserDocument | undefined`
201
+ - In @terreno/api callbacks: cast with `const user = u as unknown as UserDocument`
202
+
203
+ ### @terreno/ui
204
+
205
+ React Native component library with 88+ components:
206
+
207
+ - **Layout**: Box, Page, SplitPage, Card
208
+ - **Forms**: TextField, SelectField, DateTimeField, CheckBox
209
+ - **Display**: Text, Heading, Badge, DataTable
210
+ - **Actions**: Button, IconButton, Link
211
+ - **Feedback**: Spinner, Modal, Toast
212
+ - **Theming**: TerrenoProvider, useTheme
213
+
214
+ Key imports:
215
+ ```typescript
216
+ import {
217
+ Box,
218
+ Button,
219
+ Card,
220
+ Page,
221
+ Text,
222
+ TextField,
223
+ TerrenoProvider,
224
+ } from "@terreno/ui";
225
+ ```
226
+
227
+ #### UI Component Examples
228
+
229
+ Layout with Box:
230
+ ```typescript
231
+ <Box direction="row" padding={4} gap={2} alignItems="center">
232
+ <Text>Content</Text>
233
+ <Button text="Action" />
234
+ </Box>
235
+ ```
236
+
237
+ Buttons:
238
+ ```typescript
239
+ <Button
240
+ text="Submit"
241
+ variant="primary" // 'primary' | 'secondary' | 'outline' | 'ghost'
242
+ onClick={handleSubmit}
243
+ loading={isLoading}
244
+ iconName="check"
245
+ />
246
+ ```
247
+
248
+ Forms:
249
+ ```typescript
250
+ <TextField
251
+ label="Email"
252
+ value={email}
253
+ onChangeText={setEmail}
254
+ error={emailError}
255
+ helperText="Enter a valid email"
256
+ />
257
+ ```
258
+
259
+ Modals:
260
+ ```typescript
261
+ <Modal
262
+ title="Confirm Action"
263
+ visible={isVisible}
264
+ primaryButtonText="Confirm"
265
+ secondaryButtonText="Cancel"
266
+ onDismiss={() => setIsVisible(false)}
267
+ onPrimaryAction={handleConfirm}
268
+ >
269
+ <Text>Are you sure?</Text>
270
+ </Modal>
271
+ ```
272
+
273
+ #### UI Common Pitfalls
274
+
275
+ - Don't use inline styles when theme values are available
276
+ - Don't use raw `View`/`Text` when `Box`/@terreno/ui `Text` are available
277
+ - Don't forget loading and error states
278
+ - Don't use `style` prop when equivalent props exist (`padding`, `margin`)
279
+ - Never modify `openApiSdk.ts` manually
280
+
281
+ ### @terreno/rtk
282
+
283
+ Redux Toolkit Query integration:
284
+
285
+ - **generateAuthSlice**: Creates auth reducer and middleware with JWT handling
286
+ - **emptyApi**: Base RTK Query API for code generation
287
+ - **Platform utilities**: Secure token storage (expo-secure-store for native, AsyncStorage for web)
288
+
289
+ Key imports:
290
+ ```typescript
291
+ import {generateAuthSlice} from "@terreno/rtk";
292
+ ```
293
+
294
+ Always use generated SDK hooks - never use `axios` or `request` directly:
295
+
296
+ ```typescript
297
+ // Correct
298
+ import {useGetYourRouteQuery} from "@/store/openApiSdk";
299
+ const {data, isLoading, error} = useGetYourRouteQuery({id: "value"});
300
+
301
+ // Wrong - don't use axios directly
302
+ // const result = await axios.get("/api/yourRoute/value");
303
+ ```
304
+
305
+ ## React Best Practices (Frontend Packages)
306
+
307
+ - Use functional components with `React.FC` type
308
+ - Import hooks directly: `import {useEffect, useMemo} from 'react'`
309
+ - Always provide return types for functions
310
+ - Add explanatory comment above each `useEffect`
311
+ - Wrap callbacks in `useCallback`
312
+ - Prefer const arrow functions
313
+ - Use inline styles over `StyleSheet.create`
314
+ - Use Luxon for date operations
315
+ - Place static content and interfaces at beginning of file
316
+ - Minimize `use client`, `useEffect`, and `setState`
317
+ - Always support React-Native Web
318
+
319
+ ## CI/CD Workflows
320
+
321
+ ### Required Secret Validation
322
+
323
+ GitHub Actions workflows that use secrets or environment variables must validate all required variables are set before using them. Add a validation step early in the job that fails fast with a clear error message listing any missing variables.
324
+
325
+ ```yaml
326
+ - name: Validate required secrets
327
+ run: |
328
+ missing=()
329
+ if [ -z "$VAR_NAME" ]; then missing+=("VAR_NAME"); fi
330
+ if [ ${#missing[@]} -ne 0 ]; then
331
+ echo "::error::Missing required secrets: ${missing[*]}"
332
+ exit 1
333
+ fi
334
+ ```
335
+
336
+ ## Dependency Management
337
+
338
+ Uses [Bun Catalogs](https://bun.sh/docs/install/catalogs) - shared versions defined in root `package.json` under `catalog`. Reference with `catalog:` in workspace packages.