@devwithbobby/loops 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 (59) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.config/commitlint.config.ts +11 -0
  4. package/.config/lefthook.yml +11 -0
  5. package/.github/workflows/release.yml +52 -0
  6. package/.github/workflows/test-and-lint.yml +39 -0
  7. package/README.md +517 -0
  8. package/biome.json +45 -0
  9. package/bun.lock +1166 -0
  10. package/bunfig.toml +7 -0
  11. package/convex.json +3 -0
  12. package/example/CLAUDE.md +106 -0
  13. package/example/README.md +21 -0
  14. package/example/bun-env.d.ts +17 -0
  15. package/example/convex/_generated/api.d.ts +53 -0
  16. package/example/convex/_generated/api.js +23 -0
  17. package/example/convex/_generated/dataModel.d.ts +60 -0
  18. package/example/convex/_generated/server.d.ts +149 -0
  19. package/example/convex/_generated/server.js +90 -0
  20. package/example/convex/convex.config.ts +7 -0
  21. package/example/convex/example.ts +76 -0
  22. package/example/convex/schema.ts +3 -0
  23. package/example/convex/tsconfig.json +34 -0
  24. package/example/src/App.tsx +185 -0
  25. package/example/src/frontend.tsx +39 -0
  26. package/example/src/index.css +15 -0
  27. package/example/src/index.html +12 -0
  28. package/example/src/index.tsx +19 -0
  29. package/example/tsconfig.json +28 -0
  30. package/package.json +95 -0
  31. package/prds/CHANGELOG.md +38 -0
  32. package/prds/CLAUDE.md +408 -0
  33. package/prds/CONTRIBUTING.md +274 -0
  34. package/prds/ENV_SETUP.md +222 -0
  35. package/prds/MONITORING.md +301 -0
  36. package/prds/RATE_LIMITING.md +412 -0
  37. package/prds/SECURITY.md +246 -0
  38. package/renovate.json +32 -0
  39. package/src/client/index.ts +530 -0
  40. package/src/client/types.ts +64 -0
  41. package/src/component/_generated/api.d.ts +55 -0
  42. package/src/component/_generated/api.js +23 -0
  43. package/src/component/_generated/dataModel.d.ts +60 -0
  44. package/src/component/_generated/server.d.ts +149 -0
  45. package/src/component/_generated/server.js +90 -0
  46. package/src/component/convex.config.ts +27 -0
  47. package/src/component/lib.ts +1125 -0
  48. package/src/component/schema.ts +17 -0
  49. package/src/component/tables/contacts.ts +16 -0
  50. package/src/component/tables/emailOperations.ts +22 -0
  51. package/src/component/validators.ts +39 -0
  52. package/src/utils.ts +6 -0
  53. package/test/client/_generated/_ignore.ts +1 -0
  54. package/test/client/index.test.ts +65 -0
  55. package/test/client/setup.test.ts +54 -0
  56. package/test/component/lib.test.ts +225 -0
  57. package/test/component/setup.test.ts +21 -0
  58. package/tsconfig.build.json +20 -0
  59. package/tsconfig.json +22 -0
@@ -0,0 +1,8 @@
1
+ # Changesets
2
+
3
+ Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4
+ with multi-package repos, or single-package repos to help you version and publish your code. You can
5
+ find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6
+
7
+ We have a quick list of common questions to get you started engaging with this project in
8
+ [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
3
+ "changelog": [
4
+ "@changesets/changelog-github",
5
+ { "repo": "samhoque/convex-component-template" }
6
+ ],
7
+ "commit": false,
8
+ "fixed": [],
9
+ "linked": [],
10
+ "access": "public",
11
+ "baseBranch": "main",
12
+ "updateInternalDependencies": "patch",
13
+ "ignore": []
14
+ }
@@ -0,0 +1,11 @@
1
+ import type { UserConfig } from "@commitlint/types";
2
+
3
+ const config: UserConfig = {
4
+ extends: ["gitmoji"],
5
+ rules: {
6
+ "header-max-length": [2, "always", 100],
7
+ "subject-case": [0], // Allow any case in subject
8
+ },
9
+ };
10
+
11
+ export default config;
@@ -0,0 +1,11 @@
1
+ pre-commit:
2
+ commands:
3
+ check:
4
+ glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}"
5
+ run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files}
6
+ stage_fixed: true
7
+
8
+ commit-msg:
9
+ commands:
10
+ commitlint:
11
+ run: bunx commitlint --config .config/commitlint.config.ts --edit {1}
@@ -0,0 +1,52 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ # Prevent multiple releases from running simultaneously
9
+ concurrency: ${{ github.workflow }}-${{ github.ref }}
10
+
11
+ permissions:
12
+ contents: write # For creating releases and pushing commits
13
+ pull-requests: write # For creating version PRs
14
+
15
+ jobs:
16
+ release:
17
+ name: Release
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 15
20
+
21
+ steps:
22
+ - name: Checkout repository
23
+ uses: actions/checkout@v4
24
+
25
+ - name: Setup Bun
26
+ uses: oven-sh/setup-bun@v2
27
+ with:
28
+ bun-version: latest
29
+
30
+ - name: Install dependencies
31
+ run: bun install --frozen-lockfile
32
+
33
+ - name: Build package
34
+ run: bun run build
35
+
36
+ - name: Create Release Pull Request or Publish to npm
37
+ id: changesets
38
+ uses: changesets/action@v1
39
+ with:
40
+ version: bun run ci:version
41
+ publish: bun run ci:publish
42
+ commit: "chore: version packages"
43
+ title: "chore: version packages"
44
+ env:
45
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
47
+
48
+ - name: Log published packages
49
+ if: steps.changesets.outputs.published == 'true'
50
+ run: |
51
+ echo "Published packages:"
52
+ echo '${{ steps.changesets.outputs.publishedPackages }}' | jq -r '.[] | " - \(.name)@\(.version)"'
@@ -0,0 +1,39 @@
1
+ name: Test and lint
2
+ concurrency:
3
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
4
+ cancel-in-progress: true
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+ branches: ["**"]
11
+
12
+ jobs:
13
+ check:
14
+ name: Test and lint
15
+ runs-on: ubuntu-latest
16
+ timeout-minutes: 30
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Setup Bun
22
+ uses: oven-sh/setup-bun@v2
23
+ with:
24
+ bun-version: latest
25
+
26
+ - name: Install dependencies
27
+ run: bun install
28
+
29
+ - name: Build
30
+ run: bun run build
31
+
32
+ - name: Publish package preview
33
+ run: bunx pkg-pr-new publish
34
+
35
+ - name: Test
36
+ run: bun run test
37
+
38
+ - name: Lint
39
+ run: bun run lint
package/README.md ADDED
@@ -0,0 +1,517 @@
1
+ # 🔨 Convex Component Template
2
+
3
+ [![pkg.pr.new](https://pkg.pr.new/badge/robertalv/loops-component)](https://pkg.pr.new/~/robertalv/loops-component)
4
+
5
+ A modern template for building reusable [Convex components](https://www.convex.dev/components) with Bun, TypeScript, and comprehensive testing.
6
+
7
+ > **Note:** Replace `robertalv/loops-component` in the badge above with your GitHub username/organization and repository name once you set up your repository.
8
+
9
+ ## Getting Started
10
+
11
+ ### 1. Rename Your Component
12
+
13
+ After cloning this template, run the rename script to customize it for your component:
14
+
15
+ ```bash
16
+ bun rename.ts
17
+ ```
18
+
19
+ The script will:
20
+ - Prompt you for your component name (e.g., "document search", "rate limiter")
21
+ - Ask for your NPM package name (default: `@samhoque/your-component-name`)
22
+ - Ask for your GitHub repository (default: `samhoque/your-component-name`)
23
+ - Automatically generate all case variants (PascalCase, camelCase, kebab-case, etc.)
24
+ - Replace all template placeholders across the entire codebase
25
+ - Update `package.json` with your package name
26
+ - Optionally delete itself when done
27
+
28
+ **What gets renamed:**
29
+ - Package name in `package.json`
30
+ - All imports and references throughout the codebase
31
+ - Component class names, function names, and identifiers
32
+ - Documentation examples in README and comments
33
+
34
+ ### 2. Set Up Environment Variables
35
+
36
+ **⚠️ IMPORTANT: Set your Loops API key before using the component.**
37
+
38
+ ```bash
39
+ # Set the API key in your Convex environment variables
40
+ npx convex env set LOOPS_API_KEY "your-loops-api-key-here"
41
+ ```
42
+
43
+ **Or via Convex Dashboard:**
44
+ 1. Go to Settings → Environment Variables
45
+ 2. Add `LOOPS_API_KEY` with your Loops.so API key
46
+
47
+ 📖 **See [ENV_SETUP.md](./ENV_SETUP.md) for detailed setup instructions and security best practices.**
48
+
49
+ ### 3. Install Dependencies & Start Development
50
+
51
+ ```bash
52
+ bun install
53
+ cd example && bun install && cd ..
54
+ bun run dev:backend
55
+ ```
56
+
57
+ Then in another terminal:
58
+ ```bash
59
+ cd example
60
+ bun run dev
61
+ ```
62
+
63
+ ## What are Convex Components?
64
+
65
+ Components are isolated, reusable units of Convex functionality with their own:
66
+ - Functions (queries, mutations, actions)
67
+ - Tables and schemas
68
+ - File storage
69
+ - Scheduled functions
70
+
71
+ Components plug into Convex apps (or parent components) through a public interface, enabling modular architecture with proper isolation and security.
72
+
73
+ ## Project Structure
74
+
75
+ ```
76
+ src/
77
+ component/ # The Convex component source code
78
+ convex.config.ts # Component configuration (exported via package.json)
79
+ schema.ts # Convex schema definition
80
+ lib.ts # Component functions (queries, mutations)
81
+ _generated/ # Auto-generated Convex types (gitignored)
82
+
83
+ client/ # Optional: Client library that runs in the app
84
+ index.ts # Helper class for easier component interaction
85
+
86
+ react/ # Optional: React components for UI
87
+ index.tsx # React hooks and components
88
+
89
+ test/
90
+ component/ # Component tests (separate from source)
91
+ setup.test.ts # Test setup with module auto-discovery
92
+ *.test.ts # Unit tests for the component
93
+
94
+ example/
95
+ convex/ # Example app that uses the component
96
+ convex.config.ts # Example app configuration
97
+ schema.ts # Example app schema
98
+ _generated/ # Auto-generated types (gitignored)
99
+ src/ # Example app frontend
100
+ ```
101
+
102
+ ## Key Commands
103
+
104
+ ### Development
105
+ ```bash
106
+ bun run dev:backend # Start Convex dev with live component sources
107
+ bun run build # Build the component for distribution
108
+ ```
109
+
110
+ ### Testing
111
+ ```bash
112
+ bun test # Run all tests
113
+ bun test --watch # Watch mode
114
+ bun test --coverage # Generate coverage reports
115
+ bun test -t "pattern" # Filter tests by name
116
+ CLAUDECODE=1 bun test # AI-friendly quiet output
117
+ ```
118
+
119
+ ### Linting and Formatting
120
+ ```bash
121
+ bun run lint # Lint with Biome
122
+ bun run lint:fix # Auto-fix linting issues
123
+ bun run format # Format code with Biome
124
+ bun run check # Run both lint and format checks
125
+ bun run check:fix # Auto-fix all issues
126
+ ```
127
+
128
+ ### Git Hooks (Lefthook)
129
+
130
+ This project uses [Lefthook](https://github.com/evilmartians/lefthook) for Git hooks. Hooks are automatically installed when you run `bun install`.
131
+
132
+ **Pre-commit hook:**
133
+ - Runs Biome check on staged files
134
+ - Auto-fixes issues and stages the changes
135
+ - Prevents commits with linting/formatting errors
136
+
137
+ To skip hooks (not recommended):
138
+ ```bash
139
+ git commit --no-verify
140
+ ```
141
+
142
+ ### CI/CD
143
+
144
+ The project includes a GitHub Actions workflow that runs on every push and pull request:
145
+
146
+ **Workflow: Test and lint** (`.github/workflows/test-and-lint.yml`)
147
+ - Installs dependencies with Bun
148
+ - Builds the project
149
+ - Publishes preview packages with `pkg.pr.new`
150
+ - Runs all tests
151
+ - Runs linting checks
152
+
153
+ The workflow ensures code quality and prevents broken builds from being merged.
154
+
155
+ #### pkg.pr.new Setup
156
+
157
+ This project uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous package previews. Each commit and PR automatically generates a preview release that can be installed without publishing to npm.
158
+
159
+ **One-time setup required:**
160
+ 1. Install the [pkg.pr.new GitHub App](https://github.com/apps/pkg-pr-new) on your repository
161
+ 2. Once installed, the workflow will automatically publish preview packages on every commit/PR
162
+
163
+ **Using preview packages:**
164
+ ```bash
165
+ # Install from a specific commit (Bun)
166
+ bun add https://pkg.pr.new/robertalv/loops-component/robertalv/loops-component@COMMIT_SHA
167
+
168
+ # Or with npm
169
+ npm i https://pkg.pr.new/robertalv/loops-component/robertalv/loops-component@COMMIT_SHA
170
+ ```
171
+
172
+ Preview URLs will be posted as comments on your pull requests automatically.
173
+
174
+ ## Component Architecture
175
+
176
+ ### 1. Component Definition
177
+
178
+ The component is defined in `src/component/convex.config.ts`:
179
+
180
+ ```typescript
181
+ import { defineComponent } from "convex/server";
182
+ import { api } from "./_generated/api";
183
+
184
+ const component = defineComponent("loopsComponent"); // Change "loopsComponent" to your component name
185
+ component.export(api, { greet: api.lib.greet });
186
+ export default component;
187
+ ```
188
+
189
+ ### 2. Package Exports
190
+
191
+ The `package.json` exports the component using the `@convex-dev/component-source` condition:
192
+
193
+ ```json
194
+ {
195
+ "exports": {
196
+ "./convex.config": {
197
+ "@convex-dev/component-source": "./src/component/convex.config.ts"
198
+ }
199
+ }
200
+ }
201
+ ```
202
+
203
+ This enables live reloading during development.
204
+
205
+ ### 3. Component Usage
206
+
207
+ Apps import and mount the component in their `convex.config.ts`:
208
+
209
+ ```typescript
210
+ import { defineApp } from "convex/server";
211
+ import component from "@your-package/convex.config";
212
+
213
+ const app = defineApp();
214
+ app.use(component);
215
+ export default app;
216
+ ```
217
+
218
+ ### 4. Client Library (Optional)
219
+
220
+ The `src/client/` directory contains helper code that runs in the app (not the component):
221
+
222
+ ```typescript
223
+ export class Counter {
224
+ constructor(
225
+ private component: UseApi<typeof api>,
226
+ private options?: { initialValue?: number }
227
+ ) {}
228
+
229
+ async count(ctx: RunQueryCtx) {
230
+ return await ctx.runQuery(this.component.public.count, {});
231
+ }
232
+ }
233
+ ```
234
+
235
+ This pattern is useful for:
236
+ - Hiding implementation details
237
+ - Managing implicit dependencies (auth, env vars)
238
+ - Providing a cleaner API surface
239
+
240
+ ## Calling Component Functions
241
+
242
+ ### Subtransactions
243
+
244
+ Components use **subtransactions** for cross-component function calls:
245
+
246
+ ```typescript
247
+ // From the app or parent component
248
+ const count = await ctx.runQuery(components.counter.public.count, args);
249
+ const newCount = await ctx.runMutation(components.counter.public.increment, args);
250
+ ```
251
+
252
+ **Key semantics:**
253
+ 1. Sub-queries track reads for reactivity across components
254
+ 2. Sub-mutations contribute to the parent's ACID transaction
255
+ 3. Sub-mutations are isolated from each other (even with `Promise.all`)
256
+ 4. Parent errors roll back sub-mutations
257
+ 5. Sub-mutation errors can be caught without affecting parent
258
+
259
+ ### Exposing Component Functions Publicly
260
+
261
+ Components cannot be called directly from clients. The app must wrap them:
262
+
263
+ ```typescript
264
+ // in the app's convex/counter.ts
265
+ export const count = query({
266
+ handler: async (ctx) => {
267
+ return await ctx.runQuery(components.counter.public.count, {});
268
+ },
269
+ });
270
+ ```
271
+
272
+ This allows the app to add auth, rate limiting, etc.
273
+
274
+ ## Working with Isolation
275
+
276
+ ### Function Access Hierarchy
277
+
278
+ ```mermaid
279
+ flowchart TD
280
+ A[Public Internet / React] --> B[App]
281
+ B --> C[Component1]
282
+ B --> D[Component2]
283
+ C --> E[Component3]
284
+ ```
285
+
286
+ - Clients can only call app functions (not component functions)
287
+ - Apps can call their own functions and component public functions
288
+ - Components can only call their own functions and child component public functions
289
+
290
+ ### Function Handles
291
+
292
+ To allow components to call back into the app, use function handles:
293
+
294
+ ```typescript
295
+ // In the app
296
+ const handle = await createFunctionHandle(api.myMutation);
297
+
298
+ // Pass handle to component
299
+ await ctx.runMutation(components.worker.public.process, { handler: handle });
300
+
301
+ // In the component
302
+ export const process = mutation({
303
+ args: { handler: v.string() },
304
+ handler: async (ctx, args) => {
305
+ // Component can now call the app's function
306
+ const functionHandle: FunctionHandle<"mutation"> = args.handler;
307
+ await ctx.runMutation(functionHandle, {});
308
+ },
309
+ });
310
+ ```
311
+
312
+ **Use cases:**
313
+ - Migrations component iterating over app tables
314
+ - Webhook handlers calling app logic
315
+ - Background job processors
316
+
317
+ ### Table Access
318
+
319
+ Components have **isolated tables**:
320
+ - Components can only read/write their own tables
321
+ - Use `v.id("tableName")` for component tables
322
+ - Use `v.string()` for IDs from other components/app
323
+ - Use function handles to grant table access across boundaries
324
+
325
+ ### Environment Variables and Auth
326
+
327
+ Components **cannot access** `process.env` or `ctx.auth` directly. Pass them through:
328
+
329
+ ```typescript
330
+ // src/client/index.ts (runs in app context)
331
+ class MyComponent {
332
+ constructor(
333
+ private component: UseApi<typeof api>,
334
+ private options?: { apiKey?: string }
335
+ ) {
336
+ this.apiKey = options?.apiKey ?? process.env.MY_API_KEY;
337
+ }
338
+
339
+ async doSomething(ctx: QueryCtx) {
340
+ return await ctx.runQuery(this.component.public.process, {
341
+ apiKey: this.apiKey,
342
+ auth: await ctx.auth.getUserIdentity(),
343
+ });
344
+ }
345
+ }
346
+ ```
347
+
348
+ ### HTTP Actions
349
+
350
+ Components cannot define HTTP routes directly. Instead, they export handlers that the app mounts:
351
+
352
+ ```typescript
353
+ // src/client/index.ts
354
+ export const httpHandler = httpAction(async (ctx, request) => {
355
+ // Handle HTTP request
356
+ });
357
+
358
+ // In app's convex/http.ts
359
+ import { httpRouter } from "convex/server";
360
+ import { httpHandler } from "@your-component/client";
361
+
362
+ const http = httpRouter();
363
+ http.route({ path: "/webhook", method: "POST", handler: httpHandler });
364
+ export default http;
365
+ ```
366
+
367
+ ### Pagination
368
+
369
+ The built-in `.paginate()` doesn't work in components. Use [`convex-helpers` paginator](https://github.com/get-convex/convex-helpers) instead:
370
+
371
+ ```typescript
372
+ import { paginationOptsValidator } from "convex-helpers/server/pagination";
373
+ import { makePagination } from "convex-helpers/server/pagination";
374
+
375
+ export const listItems = query({
376
+ args: { paginationOpts: paginationOptsValidator },
377
+ handler: async (ctx, args) => {
378
+ return await makePagination(ctx.db.query("items"), args.paginationOpts);
379
+ },
380
+ });
381
+ ```
382
+
383
+ ## Testing Pattern
384
+
385
+ This template uses `convex-test` with Bun's test runner. Tests are **separate from component source** to prevent bundling issues.
386
+
387
+ ### Test Setup (`test/component/setup.test.ts`)
388
+
389
+ Auto-discovers component files and creates a test helper:
390
+
391
+ ```typescript
392
+ import { convexTest as baseConvexTest } from "convex-test";
393
+ import { Glob } from "bun";
394
+
395
+ const glob = new Glob("**/*.ts");
396
+ const modules: Record<string, string> = {};
397
+
398
+ for await (const file of glob.scan("./src/component")) {
399
+ if (!file.startsWith("_generated/")) {
400
+ modules[file.replace(/\.ts$/, ".js")] = await Bun.file(
401
+ `./src/component/${file}`
402
+ ).text();
403
+ }
404
+ }
405
+
406
+ export const convexTest = () => baseConvexTest(schema, modules);
407
+ ```
408
+
409
+ ### Writing Tests
410
+
411
+ ```typescript
412
+ import { test, expect } from "bun:test";
413
+ import { api } from "../../src/component/_generated/api";
414
+ import { convexTest } from "./setup.test";
415
+
416
+ test("greet returns greeting", async () => {
417
+ const t = convexTest();
418
+ const result = await t.query(api.lib.greet, { name: "Alice" });
419
+ expect(result).toBe("Hello, Alice!");
420
+ });
421
+
422
+ test("with authentication", async () => {
423
+ const t = convexTest();
424
+ const asUser = t.withIdentity({ subject: "user123" });
425
+ const result = await asUser.query(api.lib.getCurrentUser, {});
426
+ expect(result.subject).toBe("user123");
427
+ });
428
+ ```
429
+
430
+ ## Distribution
431
+
432
+ ### Local Development
433
+
434
+ To use the component in another app during development:
435
+
436
+ ```bash
437
+ bun run build
438
+ bun pack # or npm pack
439
+ ```
440
+
441
+ Then in the other app:
442
+ ```bash
443
+ bun install ../path/to/component/your-component-0.1.0.tgz
444
+ ```
445
+
446
+ ### Publishing
447
+
448
+ This package is currently marked as `private` in package.json. To publish to npm:
449
+
450
+ 1. Remove `"private": true` from package.json
451
+ 2. Run: `bun publish` (or `npm publish` if bun publish is not yet available)
452
+
453
+ ## Dashboard and Deployment
454
+
455
+ ### Component Visibility
456
+
457
+ In the Convex dashboard, you can select each component to see:
458
+ - Data tables
459
+ - Functions
460
+ - File storage
461
+ - Logs
462
+ - Scheduled functions
463
+
464
+ ### Deployment Semantics
465
+
466
+ 1. **Function calls**: Top-level query/mutation counts as single function call (sub-calls are free)
467
+ 2. **Database bandwidth**: Component functions count bandwidth separately
468
+ 3. **Logging**: Component logs appear in dashboard and log streams
469
+ 4. **Exports**: Snapshot exports include all component data
470
+ 5. **Streaming exports**: Only include top-level app data (not components)
471
+
472
+ ## Code Style
473
+
474
+ - **Package manager**: Bun
475
+ - **Linter/Formatter**: Biome
476
+ - **Indentation**: Tabs
477
+ - **Quotes**: Double quotes
478
+ - **TypeScript**: Strict mode with extra checks
479
+
480
+ ## Examples
481
+
482
+ ### First-Party Components
483
+
484
+ All first-party components are open source:
485
+
486
+ - [loops-component](https://github.com/get-convex/loops-component) - Attaching components to app tables with triggers
487
+ - [twilio](https://github.com/get-convex/twilio) - HTTP actions and webhooks
488
+ - [aggregate](https://github.com/get-convex/aggregate) - Testing patterns
489
+ - [migrations](https://github.com/get-convex/migrations) - Function handles and table access
490
+
491
+ ## Important Notes
492
+
493
+ - **Generated files**: Never edit `_generated/` directories
494
+ - **Test location**: Always place tests in `test/component/` (not `src/component/`)
495
+ - **Component name**: Change `"loopsComponent"` in convex.config.ts to your component name
496
+ - **Live reloading**: Enabled via `--live-component-sources` flag
497
+ - **Peer dependencies**: Component uses the app's Convex installation
498
+
499
+ ## Documentation
500
+
501
+ - **[ENV_SETUP.md](./ENV_SETUP.md)** - Environment variable setup and security best practices
502
+ - **[SECURITY.md](./SECURITY.md)** - Security considerations and guidelines
503
+ - **[MONITORING.md](./MONITORING.md)** - Email monitoring and spam detection
504
+ - **[RATE_LIMITING.md](./RATE_LIMITING.md)** - Rate limiting implementation guide
505
+
506
+ ## Resources
507
+
508
+ - [Convex Components Documentation](https://www.convex.dev/components)
509
+ - [Component Authoring Guide](https://docs.convex.dev/components/authoring)
510
+ - [Convex Environment Variables](https://docs.convex.dev/production/environment-variables)
511
+ - [convex-test](https://github.com/get-convex/convex-test)
512
+ - [convex-helpers](https://github.com/get-convex/convex-helpers)
513
+ - [Bun Documentation](https://bun.sh/docs)
514
+
515
+ ## License
516
+
517
+ MIT
package/biome.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
3
+ "vcs": {
4
+ "enabled": false,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": false
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": false,
10
+ "includes": ["**", "!**/_generated", "!dist"]
11
+ },
12
+ "formatter": {
13
+ "enabled": true,
14
+ "indentStyle": "tab"
15
+ },
16
+ "linter": {
17
+ "enabled": true,
18
+ "rules": {
19
+ "recommended": true
20
+ }
21
+ },
22
+ "javascript": {
23
+ "formatter": {
24
+ "quoteStyle": "double"
25
+ }
26
+ },
27
+ "assist": {
28
+ "enabled": true,
29
+ "actions": {
30
+ "source": {
31
+ "organizeImports": "on"
32
+ }
33
+ }
34
+ },
35
+ "json": {
36
+ "parser": {
37
+ "allowComments": true
38
+ }
39
+ },
40
+ "css": {
41
+ "parser": {
42
+ "tailwindDirectives": true
43
+ }
44
+ }
45
+ }