@terreno/api 0.0.18 → 0.2.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 (71) hide show
  1. package/README.md +73 -3
  2. package/dist/api.d.ts +96 -3
  3. package/dist/api.js +159 -11
  4. package/dist/api.test.js +906 -2
  5. package/dist/auth.js +3 -1
  6. package/dist/betterAuth.d.ts +91 -0
  7. package/dist/betterAuth.js +8 -0
  8. package/dist/betterAuth.test.d.ts +1 -0
  9. package/dist/betterAuth.test.js +181 -0
  10. package/dist/betterAuthApp.d.ts +22 -0
  11. package/dist/betterAuthApp.js +38 -0
  12. package/dist/betterAuthApp.test.d.ts +1 -0
  13. package/dist/betterAuthApp.test.js +242 -0
  14. package/dist/betterAuthSetup.d.ts +60 -0
  15. package/dist/betterAuthSetup.js +278 -0
  16. package/dist/betterAuthSetup.test.d.ts +1 -0
  17. package/dist/betterAuthSetup.test.js +684 -0
  18. package/dist/errors.js +14 -11
  19. package/dist/example.js +7 -7
  20. package/dist/expressServer.js +2 -2
  21. package/dist/githubAuth.test.js +3 -3
  22. package/dist/index.d.ts +6 -0
  23. package/dist/index.js +6 -0
  24. package/dist/openApi.test.js +8 -5
  25. package/dist/openApiBuilder.d.ts +69 -1
  26. package/dist/openApiBuilder.js +109 -5
  27. package/dist/openApiValidator.d.ts +296 -0
  28. package/dist/openApiValidator.js +698 -0
  29. package/dist/openApiValidator.test.d.ts +1 -0
  30. package/dist/openApiValidator.test.js +346 -0
  31. package/dist/plugins.test.js +3 -3
  32. package/dist/terrenoApp.d.ts +189 -0
  33. package/dist/terrenoApp.js +352 -0
  34. package/dist/terrenoApp.test.d.ts +1 -0
  35. package/dist/terrenoApp.test.js +264 -0
  36. package/dist/terrenoPlugin.d.ts +38 -0
  37. package/dist/terrenoPlugin.js +2 -0
  38. package/dist/tests.js +34 -24
  39. package/package.json +8 -2
  40. package/src/__snapshots__/openApi.test.ts.snap +399 -0
  41. package/src/__snapshots__/openApiBuilder.test.ts.snap +108 -0
  42. package/src/api.test.ts +743 -2
  43. package/src/api.ts +270 -6
  44. package/src/auth.ts +3 -1
  45. package/src/betterAuth.test.ts +160 -0
  46. package/src/betterAuth.ts +104 -0
  47. package/src/betterAuthApp.test.ts +114 -0
  48. package/src/betterAuthApp.ts +60 -0
  49. package/src/betterAuthSetup.test.ts +485 -0
  50. package/src/betterAuthSetup.ts +251 -0
  51. package/src/errors.ts +14 -11
  52. package/src/example.ts +7 -7
  53. package/src/expressServer.ts +4 -5
  54. package/src/githubAuth.test.ts +3 -3
  55. package/src/index.ts +6 -0
  56. package/src/openApi.test.ts +8 -5
  57. package/src/openApiBuilder.ts +188 -15
  58. package/src/openApiValidator.test.ts +241 -0
  59. package/src/openApiValidator.ts +860 -0
  60. package/src/plugins.test.ts +3 -3
  61. package/src/terrenoApp.test.ts +201 -0
  62. package/src/terrenoApp.ts +347 -0
  63. package/src/terrenoPlugin.ts +39 -0
  64. package/src/tests.ts +34 -24
  65. package/.cursorrules +0 -107
  66. package/.windsurfrules +0 -107
  67. package/AGENTS.md +0 -313
  68. package/dist/response.d.ts +0 -0
  69. package/dist/response.js +0 -1
  70. package/index.ts +0 -1
  71. package/src/response.ts +0 -0
package/src/tests.ts CHANGED
@@ -54,10 +54,10 @@ export interface Food {
54
54
  }
55
55
 
56
56
  const userSchema = new Schema<User>({
57
- admin: {default: false, type: Boolean},
58
- age: Number,
59
- name: String,
60
- username: String,
57
+ admin: {default: false, description: "Whether the user has admin privileges", type: Boolean},
58
+ age: {description: "The user's age", type: Number},
59
+ name: {description: "The user's display name", type: String},
60
+ username: {description: "The user's username", type: String},
61
61
  });
62
62
 
63
63
  userSchema.plugin(passportLocalMongoose as any, {
@@ -80,55 +80,65 @@ userSchema.methods.postCreate = async function (body: any) {
80
80
  export const UserModel = model<User>("User", userSchema);
81
81
 
82
82
  const superUserSchema = new Schema<SuperUser>({
83
- superTitle: {required: true, type: String},
83
+ superTitle: {description: "The super user's title", required: true, type: String},
84
84
  });
85
85
  export const SuperUserModel = UserModel.discriminator("SuperUser", superUserSchema);
86
86
 
87
87
  const staffUserSchema = new Schema<StaffUser>({
88
- department: {required: true, type: String},
88
+ department: {
89
+ description: "The department the staff member belongs to",
90
+ required: true,
91
+ type: String,
92
+ },
89
93
  });
90
94
  export const StaffUserModel = UserModel.discriminator("Staff", staffUserSchema);
91
95
 
92
96
  const foodCategorySchema = new Schema<FoodCategory>(
93
97
  {
94
- name: String,
95
- show: Boolean,
98
+ name: {description: "The name of the food category", type: String},
99
+ show: {description: "Whether this category is visible", type: Boolean},
96
100
  },
97
101
  {timestamps: {createdAt: "created", updatedAt: "updated"}}
98
102
  );
99
103
 
100
104
  const likesSchema = new Schema<any>({
101
- likes: Boolean,
102
- userId: {ref: "User", type: "ObjectId"},
105
+ likes: {description: "Whether the user liked the item", type: Boolean},
106
+ userId: {description: "The user who liked the item", ref: "User", type: "ObjectId"},
103
107
  });
104
108
 
105
109
  const foodSchema = new Schema<Food>(
106
110
  {
107
- calories: Number,
108
- categories: [foodCategorySchema],
109
- created: Date,
111
+ calories: {description: "Number of calories in the food", type: Number},
112
+ categories: {description: "Categories this food belongs to", type: [foodCategorySchema]},
113
+ created: {description: "When this food was created", type: Date},
110
114
  eatenBy: [
111
115
  {
116
+ description: "Users who have eaten this food",
112
117
  ref: "User",
113
118
  required: true,
114
119
  type: Schema.Types.ObjectId,
115
120
  },
116
121
  ],
117
- expiration: DateOnly,
118
- hidden: {default: false, type: Boolean},
122
+ expiration: {description: "Expiration date of the food", type: DateOnly as any},
123
+ hidden: {
124
+ default: false,
125
+ description: "Whether this food is hidden from listings",
126
+ type: Boolean,
127
+ },
119
128
  lastEatenWith: {
129
+ description: "Map of user names to dates they last ate this food with",
120
130
  of: Date,
121
131
  type: Map,
122
132
  },
123
- likesIds: {required: true, type: [likesSchema]},
124
- name: String,
125
- ownerId: {ref: "User", type: "ObjectId"},
133
+ likesIds: {description: "User likes for this food", required: true, type: [likesSchema]},
134
+ name: {description: "The name of the food", type: String},
135
+ ownerId: {description: "The user who owns this food entry", ref: "User", type: "ObjectId"},
126
136
  source: {
127
- dateAdded: String,
128
- href: String,
129
- name: String,
137
+ dateAdded: {description: "When the source was added", type: String},
138
+ href: {description: "URL of the source", type: String},
139
+ name: {description: "Name of the source", type: String},
130
140
  },
131
- tags: [String],
141
+ tags: {description: "Tags associated with this food", type: [String]},
132
142
  },
133
143
  {strict: "throw", toJSON: {virtuals: true}, toObject: {virtuals: true}}
134
144
  );
@@ -145,8 +155,8 @@ interface RequiredField {
145
155
  }
146
156
 
147
157
  const requiredSchema = new Schema<RequiredField>({
148
- about: String,
149
- name: {required: true, type: String},
158
+ about: {description: "Information about the item", type: String},
159
+ name: {description: "The name of the item", required: true, type: String},
150
160
  });
151
161
  export const RequiredModel = model<RequiredField>("Required", requiredSchema);
152
162
 
package/.cursorrules DELETED
@@ -1,107 +0,0 @@
1
- # @terreno/api
2
-
3
- REST API framework built on Express/Mongoose, styled after Django REST Framework.
4
-
5
- ## Commands
6
-
7
- ```bash
8
- bun run compile # Compile TypeScript
9
- bun run dev # Watch mode
10
- bun run test # Run tests
11
- bun run lint # Lint code
12
- bun run lint:fix # Fix lint issues
13
- ```
14
-
15
- ## Architecture
16
-
17
- ### modelRouter
18
-
19
- Automatically creates RESTful CRUD APIs for Mongoose models with built-in permissions, population, filtering, and lifecycle hooks.
20
-
21
- ```typescript
22
- import {modelRouter, modelRouterOptions, Permissions} from "@terreno/api";
23
-
24
- const router = modelRouter(YourModel, {
25
- permissions: {
26
- list: [Permissions.IsAuthenticated],
27
- create: [Permissions.IsAuthenticated],
28
- read: [Permissions.IsOwner],
29
- update: [Permissions.IsOwner],
30
- delete: [], // Disabled
31
- },
32
- sort: "-created",
33
- queryFields: ["_id", "type", "name"],
34
- });
35
- ```
36
-
37
- ### Custom Routes
38
-
39
- For non-CRUD endpoints, use the OpenAPI builder:
40
-
41
- ```typescript
42
- import {asyncHandler, authenticateMiddleware, createOpenApiBuilder} from "@terreno/api";
43
-
44
- router.get("/yourRoute/:id", [
45
- authenticateMiddleware(),
46
- createOpenApiBuilder(options)
47
- .withTags(["yourTag"])
48
- .withSummary("Brief summary")
49
- .withPathParameter("id", {type: "string"})
50
- .withResponse(200, {data: {type: "object"}})
51
- .build(),
52
- ], asyncHandler(async (req, res) => {
53
- return res.json({data: result});
54
- }));
55
- ```
56
-
57
- ## Conventions
58
-
59
- ### Error Handling
60
- - Throw `APIError` with appropriate status codes: `throw new APIError({status: 400, title: "Message"})`
61
- - Services should throw user-friendly errors
62
-
63
- ### Mongoose
64
- - Do not use `Model.findOne` - use `Model.findExactlyOne` or `Model.findOneOrThrow`
65
- - Define statics/methods by direct assignment: `schema.methods = {bar() {}}`
66
- - All model types live in `src/modelInterfaces.ts`
67
-
68
- ### User Type Casting
69
- - In API routes: `req.user` is `UserDocument | undefined`
70
- - In @terreno/api callbacks: cast with `const user = u as unknown as UserDocument`
71
- - Never use `as any as UserDocument`
72
-
73
- ### Logging
74
- - Use `logger.info/warn/error/debug` for permanent logs (not `console.log`)
75
-
76
- ### Testing
77
- - Use bun test with expect for testing
78
- - Use existing manual mocks from `src/__mocks__/`
79
- - Never mock @terreno/api or models
80
-
81
- ## Model Type Generation
82
-
83
- When creating/modifying Mongoose models, update `src/modelInterfaces.ts`:
84
-
85
- ```typescript
86
- export type YourModelMethods = {
87
- customMethod: (this: YourModelDocument, param: string) => Promise<void>;
88
- };
89
-
90
- export type YourModelStatics = DefaultStatics<YourModelDocument> & {
91
- customStatic: (this: YourModelModel, param: string) => Promise<YourModelDocument>;
92
- };
93
-
94
- export type YourModelModel = DefaultModel<YourModelDocument> & YourModelStatics;
95
- export type YourModelSchema = mongoose.Schema<YourModelDocument, YourModelModel, YourModelMethods>;
96
- export type YourModelDocument = DefaultDoc & YourModelMethods & {
97
- fieldName: string;
98
- };
99
- ```
100
-
101
- ## SDK Generation
102
-
103
- After modifying routes, regenerate the SDK:
104
-
105
- ```bash
106
- bun run sdk
107
- ```
package/.windsurfrules DELETED
@@ -1,107 +0,0 @@
1
- # @terreno/api
2
-
3
- REST API framework built on Express/Mongoose, styled after Django REST Framework.
4
-
5
- ## Commands
6
-
7
- ```bash
8
- bun run compile # Compile TypeScript
9
- bun run dev # Watch mode
10
- bun run test # Run tests
11
- bun run lint # Lint code
12
- bun run lint:fix # Fix lint issues
13
- ```
14
-
15
- ## Architecture
16
-
17
- ### modelRouter
18
-
19
- Automatically creates RESTful CRUD APIs for Mongoose models with built-in permissions, population, filtering, and lifecycle hooks.
20
-
21
- ```typescript
22
- import {modelRouter, modelRouterOptions, Permissions} from "@terreno/api";
23
-
24
- const router = modelRouter(YourModel, {
25
- permissions: {
26
- list: [Permissions.IsAuthenticated],
27
- create: [Permissions.IsAuthenticated],
28
- read: [Permissions.IsOwner],
29
- update: [Permissions.IsOwner],
30
- delete: [], // Disabled
31
- },
32
- sort: "-created",
33
- queryFields: ["_id", "type", "name"],
34
- });
35
- ```
36
-
37
- ### Custom Routes
38
-
39
- For non-CRUD endpoints, use the OpenAPI builder:
40
-
41
- ```typescript
42
- import {asyncHandler, authenticateMiddleware, createOpenApiBuilder} from "@terreno/api";
43
-
44
- router.get("/yourRoute/:id", [
45
- authenticateMiddleware(),
46
- createOpenApiBuilder(options)
47
- .withTags(["yourTag"])
48
- .withSummary("Brief summary")
49
- .withPathParameter("id", {type: "string"})
50
- .withResponse(200, {data: {type: "object"}})
51
- .build(),
52
- ], asyncHandler(async (req, res) => {
53
- return res.json({data: result});
54
- }));
55
- ```
56
-
57
- ## Conventions
58
-
59
- ### Error Handling
60
- - Throw `APIError` with appropriate status codes: `throw new APIError({status: 400, title: "Message"})`
61
- - Services should throw user-friendly errors
62
-
63
- ### Mongoose
64
- - Do not use `Model.findOne` - use `Model.findExactlyOne` or `Model.findOneOrThrow`
65
- - Define statics/methods by direct assignment: `schema.methods = {bar() {}}`
66
- - All model types live in `src/modelInterfaces.ts`
67
-
68
- ### User Type Casting
69
- - In API routes: `req.user` is `UserDocument | undefined`
70
- - In @terreno/api callbacks: cast with `const user = u as unknown as UserDocument`
71
- - Never use `as any as UserDocument`
72
-
73
- ### Logging
74
- - Use `logger.info/warn/error/debug` for permanent logs (not `console.log`)
75
-
76
- ### Testing
77
- - Use bun test with expect for testing
78
- - Use existing manual mocks from `src/__mocks__/`
79
- - Never mock @terreno/api or models
80
-
81
- ## Model Type Generation
82
-
83
- When creating/modifying Mongoose models, update `src/modelInterfaces.ts`:
84
-
85
- ```typescript
86
- export type YourModelMethods = {
87
- customMethod: (this: YourModelDocument, param: string) => Promise<void>;
88
- };
89
-
90
- export type YourModelStatics = DefaultStatics<YourModelDocument> & {
91
- customStatic: (this: YourModelModel, param: string) => Promise<YourModelDocument>;
92
- };
93
-
94
- export type YourModelModel = DefaultModel<YourModelDocument> & YourModelStatics;
95
- export type YourModelSchema = mongoose.Schema<YourModelDocument, YourModelModel, YourModelMethods>;
96
- export type YourModelDocument = DefaultDoc & YourModelMethods & {
97
- fieldName: string;
98
- };
99
- ```
100
-
101
- ## SDK Generation
102
-
103
- After modifying routes, regenerate the SDK:
104
-
105
- ```bash
106
- bun run sdk
107
- ```
package/AGENTS.md DELETED
@@ -1,313 +0,0 @@
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. Use `yarn` commands, not `npm`.
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 `frontend-example/` and `backend-example/` 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 frontend-example && 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
- #### modelRouter Usage
149
-
150
- ```typescript
151
- import {modelRouter, modelRouterOptions, Permissions} from "@terreno/api";
152
-
153
- const router = modelRouter(YourModel, {
154
- permissions: {
155
- list: [Permissions.IsAuthenticated],
156
- create: [Permissions.IsAuthenticated],
157
- read: [Permissions.IsOwner],
158
- update: [Permissions.IsOwner],
159
- delete: [], // Disabled
160
- },
161
- sort: "-created",
162
- queryFields: ["_id", "type", "name"],
163
- });
164
- ```
165
-
166
- #### Custom Routes
167
-
168
- For non-CRUD endpoints, use the OpenAPI builder:
169
-
170
- ```typescript
171
- import {asyncHandler, authenticateMiddleware, createOpenApiBuilder} from "@terreno/api";
172
-
173
- router.get("/yourRoute/:id", [
174
- authenticateMiddleware(),
175
- createOpenApiBuilder(options)
176
- .withTags(["yourTag"])
177
- .withSummary("Brief summary")
178
- .withPathParameter("id", {type: "string"})
179
- .withResponse(200, {data: {type: "object"}})
180
- .build(),
181
- ], asyncHandler(async (req, res) => {
182
- return res.json({data: result});
183
- }));
184
- ```
185
-
186
- #### API Conventions
187
-
188
- - Throw `APIError` with appropriate status codes: `throw new APIError({status: 400, title: "Message"})`
189
- - Do not use `Model.findOne` - use `Model.findExactlyOne` or `Model.findOneOrThrow`
190
- - Define statics/methods by direct assignment: `schema.methods = {bar() {}}`
191
- - All model types live in `src/modelInterfaces.ts`
192
- - In routes: `req.user` is `UserDocument | undefined`
193
- - In @terreno/api callbacks: cast with `const user = u as unknown as UserDocument`
194
-
195
- ### @terreno/ui
196
-
197
- React Native component library with 88+ components:
198
-
199
- - **Layout**: Box, Page, SplitPage, Card
200
- - **Forms**: TextField, SelectField, DateTimeField, CheckBox
201
- - **Display**: Text, Heading, Badge, DataTable
202
- - **Actions**: Button, IconButton, Link
203
- - **Feedback**: Spinner, Modal, Toast
204
- - **Theming**: TerrenoProvider, useTheme
205
-
206
- Key imports:
207
- ```typescript
208
- import {
209
- Box,
210
- Button,
211
- Card,
212
- Page,
213
- Text,
214
- TextField,
215
- TerrenoProvider,
216
- } from "@terreno/ui";
217
- ```
218
-
219
- #### UI Component Examples
220
-
221
- Layout with Box:
222
- ```typescript
223
- <Box direction="row" padding={4} gap={2} alignItems="center">
224
- <Text>Content</Text>
225
- <Button text="Action" />
226
- </Box>
227
- ```
228
-
229
- Buttons:
230
- ```typescript
231
- <Button
232
- text="Submit"
233
- variant="primary" // 'primary' | 'secondary' | 'outline' | 'ghost'
234
- onClick={handleSubmit}
235
- loading={isLoading}
236
- iconName="check"
237
- />
238
- ```
239
-
240
- Forms:
241
- ```typescript
242
- <TextField
243
- label="Email"
244
- value={email}
245
- onChangeText={setEmail}
246
- error={emailError}
247
- helperText="Enter a valid email"
248
- />
249
- ```
250
-
251
- Modals:
252
- ```typescript
253
- <Modal
254
- title="Confirm Action"
255
- visible={isVisible}
256
- primaryButtonText="Confirm"
257
- secondaryButtonText="Cancel"
258
- onDismiss={() => setIsVisible(false)}
259
- onPrimaryAction={handleConfirm}
260
- >
261
- <Text>Are you sure?</Text>
262
- </Modal>
263
- ```
264
-
265
- #### UI Common Pitfalls
266
-
267
- - Don't use inline styles when theme values are available
268
- - Don't use raw `View`/`Text` when `Box`/@terreno/ui `Text` are available
269
- - Don't forget loading and error states
270
- - Don't use `style` prop when equivalent props exist (`padding`, `margin`)
271
- - Never modify `openApiSdk.ts` manually
272
-
273
- ### @terreno/rtk
274
-
275
- Redux Toolkit Query integration:
276
-
277
- - **generateAuthSlice**: Creates auth reducer and middleware with JWT handling
278
- - **emptyApi**: Base RTK Query API for code generation
279
- - **Platform utilities**: Secure token storage (expo-secure-store for native, AsyncStorage for web)
280
-
281
- Key imports:
282
- ```typescript
283
- import {generateAuthSlice} from "@terreno/rtk";
284
- ```
285
-
286
- Always use generated SDK hooks - never use `axios` or `request` directly:
287
-
288
- ```typescript
289
- // Correct
290
- import {useGetYourRouteQuery} from "@/store/openApiSdk";
291
- const {data, isLoading, error} = useGetYourRouteQuery({id: "value"});
292
-
293
- // Wrong - don't use axios directly
294
- // const result = await axios.get("/api/yourRoute/value");
295
- ```
296
-
297
- ## React Best Practices (Frontend Packages)
298
-
299
- - Use functional components with `React.FC` type
300
- - Import hooks directly: `import {useEffect, useMemo} from 'react'`
301
- - Always provide return types for functions
302
- - Add explanatory comment above each `useEffect`
303
- - Wrap callbacks in `useCallback`
304
- - Prefer const arrow functions
305
- - Use inline styles over `StyleSheet.create`
306
- - Use Luxon for date operations
307
- - Place static content and interfaces at beginning of file
308
- - Minimize `use client`, `useEffect`, and `setState`
309
- - Always support React-Native Web
310
-
311
- ## Dependency Management
312
-
313
- Uses [Bun Catalogs](https://bun.sh/docs/install/catalogs) - shared versions defined in root `package.json` under `catalog`. Reference with `catalog:` in workspace packages.
File without changes
package/dist/response.js DELETED
@@ -1 +0,0 @@
1
- "use strict";
package/index.ts DELETED
@@ -1 +0,0 @@
1
- console.log("Hello via Bun!");
package/src/response.ts DELETED
File without changes