diff --git a/packages/backend/convex/README.md b/packages/backend/convex/README.md index 3c93873..42fd8c9 100644 --- a/packages/backend/convex/README.md +++ b/packages/backend/convex/README.md @@ -1,88 +1,16 @@ -# Welcome to your Convex functions directory! +# Convex Backend -Write your Convex functions here. -See https://docs.convex.dev/functions for more. +This directory contains the Convex control plane for the app and daemon runtime. Generated files under `_generated/` are maintained by Convex and should not be edited by hand. -A query function that takes two arguments looks like: +## Daemon Control Plane -```ts -// convex/myFunctions.ts -import { query } from "./_generated/server"; -import { v } from "convex/values"; +- `schema.ts` defines daemon definitions, high-churn daemon presence, leased daemon commands, append-only command events, and the sample `todos` table. +- `daemons.ts` manages stable daemon definitions with `upsert`, `get`, and `list`. +- `daemonRuntime.ts` records daemon session lifecycle: `connect`, `heartbeat`, `disconnect`, `recordEvent`, and bounded event listing. +- `daemonCommands.ts` owns the command queue: `enqueue`, `available`, `claim`, `start`, `succeed`, `fail`, `cancel`, and bounded command listing. -export const myQueryFunction = query({ - // Validators for arguments. - args: { - first: v.number(), - second: v.string(), - }, +Commands are claimed by daemon session. Preserve the `claimedBySessionId` ownership checks, `leaseExpiresAt` expiry semantics, terminal statuses, and bounded query results when changing this flow. - // Function implementation. - handler: async (ctx, args) => { - // Read the database as many times as you need here. - // See https://docs.convex.dev/database/reading-data. - const documents = await ctx.db.query("tablename").collect(); +## Usage - // Arguments passed from the client are properties of the args object. - console.log(args.first, args.second); - - // Write arbitrary JavaScript here: filter, aggregate, build derived data, - // remove non-public properties, or create new objects. - return documents; - }, -}); -``` - -Using this query function in a React component looks like: - -```ts -const data = useQuery(api.myFunctions.myQueryFunction, { - first: 10, - second: "hello", -}); -``` - -A mutation function looks like: - -```ts -// convex/myFunctions.ts -import { mutation } from "./_generated/server"; -import { v } from "convex/values"; - -export const myMutationFunction = mutation({ - // Validators for arguments. - args: { - first: v.string(), - second: v.string(), - }, - - // Function implementation. - handler: async (ctx, args) => { - // Insert or modify documents in the database here. - // Mutations can also read from the database like queries. - // See https://docs.convex.dev/database/writing-data. - const message = { body: args.first, author: args.second }; - const id = await ctx.db.insert("messages", message); - - // Optionally, return a value from your mutation. - return await ctx.db.get("messages", id); - }, -}); -``` - -Using this mutation function in a React component looks like: - -```ts -const mutation = useMutation(api.myFunctions.myMutationFunction); -function handleButtonPress() { - // fire and forget, the most common way to use mutations - mutation({ first: "Hello!", second: "me" }); - // OR - // use the result once the mutation has completed - mutation({ first: "Hello!", second: "me" }).then((result) => console.log(result)); -} -``` - -Use the Convex CLI to push your functions to a deployment. See everything -the Convex CLI can do by running `npx convex -h` in your project root -directory. To learn more, launch the docs with `npx convex docs`. +Convex functions are exposed by file path through the generated `api` object, for example `api.daemonCommands.enqueue` or `api.daemonRuntime.connect`. Run `bun run dev:server` from the repository root to start the development deployment, and `bun run dev:setup` when configuring Convex for the first time. diff --git a/packages/backend/convex/_generated/api.d.ts b/packages/backend/convex/_generated/api.d.ts index fc4eb3e..ad3e221 100644 --- a/packages/backend/convex/_generated/api.d.ts +++ b/packages/backend/convex/_generated/api.d.ts @@ -12,6 +12,7 @@ import type * as auth from "../auth.js"; import type * as daemonCommands from "../daemonCommands.js"; import type * as daemonRuntime from "../daemonRuntime.js"; import type * as daemons from "../daemons.js"; +import type * as fluePersistence from "../fluePersistence.js"; import type * as healthCheck from "../healthCheck.js"; import type * as http from "../http.js"; import type * as privateData from "../privateData.js"; @@ -28,6 +29,7 @@ declare const fullApi: ApiFromModules<{ daemonCommands: typeof daemonCommands; daemonRuntime: typeof daemonRuntime; daemons: typeof daemons; + fluePersistence: typeof fluePersistence; healthCheck: typeof healthCheck; http: typeof http; privateData: typeof privateData; diff --git a/packages/backend/convex/_generated/server.d.ts b/packages/backend/convex/_generated/server.d.ts index bec05e6..c5e955d 100644 --- a/packages/backend/convex/_generated/server.d.ts +++ b/packages/backend/convex/_generated/server.d.ts @@ -21,6 +21,15 @@ import { } from "convex/server"; import type { DataModel } from "./dataModel.js"; +/** + * Typesafe environment variables declared in `convex.config.ts`. + */ +type Env = { + readonly FLUE_DB_TOKEN: string; + readonly NATIVE_APP_URL: string | undefined; + readonly SITE_URL: string; +}; + /** * Define a query in this Convex app's public API. * @@ -95,6 +104,11 @@ export declare const internalAction: ActionBuilder; */ export declare const httpAction: HttpActionBuilder; +/** + * Typesafe environment variables declared in `convex.config.ts`. + */ +export declare const env: Env; + /** * A set of services for use within Convex query functions. * diff --git a/packages/backend/convex/_generated/server.js b/packages/backend/convex/_generated/server.js index bf3d25a..e379347 100644 --- a/packages/backend/convex/_generated/server.js +++ b/packages/backend/convex/_generated/server.js @@ -91,3 +91,8 @@ export const internalAction = internalActionGeneric; * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. */ export const httpAction = httpActionGeneric; + +/** + * Typesafe environment variables declared in `convex.config.ts`. + */ +export const env = process.env; diff --git a/packages/backend/convex/auth.ts b/packages/backend/convex/auth.ts index a8145a5..3fc3c2f 100644 --- a/packages/backend/convex/auth.ts +++ b/packages/backend/convex/auth.ts @@ -1,5 +1,7 @@ import { expo } from "@better-auth/expo"; -import { createClient, type GenericCtx } from "@convex-dev/better-auth"; +import { env } from "@code/env/convex"; +import { createClient } from "@convex-dev/better-auth"; +import type { GenericCtx } from "@convex-dev/better-auth"; import { convex, crossDomain } from "@convex-dev/better-auth/plugins"; import { betterAuth } from "better-auth/minimal"; @@ -8,15 +10,14 @@ import type { DataModel } from "./_generated/dataModel"; import { query } from "./_generated/server"; import authConfig from "./auth.config"; -const siteUrl = process.env.SITE_URL!; -const nativeAppUrl = process.env.NATIVE_APP_URL || "code://"; +const siteUrl = env.SITE_URL; +const nativeAppUrl = env.NATIVE_APP_URL ?? "code://"; export const authComponent = createClient(components.betterAuth); -function createAuth(ctx: GenericCtx) { - return betterAuth({ - baseURL: process.env.CONVEX_SITE_URL, - trustedOrigins: [siteUrl, nativeAppUrl, "exp://"], +const createAuth = (ctx: GenericCtx) => + betterAuth({ + baseURL: env.CONVEX_SITE_URL, database: authComponent.adapter(ctx), emailAndPassword: { enabled: true, @@ -30,14 +31,12 @@ function createAuth(ctx: GenericCtx) { jwksRotateOnTokenGenerationError: true, }), ], + trustedOrigins: [siteUrl, nativeAppUrl, "exp://"], }); -} export { createAuth }; export const getCurrentUser = query({ args: {}, - handler: async (ctx) => { - return await authComponent.safeGetAuthUser(ctx); - }, + handler: (ctx) => authComponent.safeGetAuthUser(ctx), }); diff --git a/packages/backend/convex/convex.config.ts b/packages/backend/convex/convex.config.ts index cd83cc5..c9af7fc 100644 --- a/packages/backend/convex/convex.config.ts +++ b/packages/backend/convex/convex.config.ts @@ -1,7 +1,14 @@ import betterAuth from "@convex-dev/better-auth/convex.config"; import { defineApp } from "convex/server"; +import { v } from "convex/values"; -const app = defineApp(); +const app = defineApp({ + env: { + NATIVE_APP_URL: v.optional(v.string()), + SITE_URL: v.string(), + FLUE_DB_TOKEN: v.string(), + }, +}); app.use(betterAuth); export default app; diff --git a/packages/backend/package.json b/packages/backend/package.json index a519d77..88cf701 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -5,15 +5,15 @@ "license": "ISC", "author": "", "scripts": { - "dev": "convex dev", - "dev:setup": "convex dev --configure --until-success" + "dev": "convex dev --env-file ../../.env", + "dev:setup": "convex dev --env-file ../../.env --configure --until-success" }, "dependencies": { "@better-auth/expo": "catalog:", + "@code/env": "workspace:*", "@convex-dev/better-auth": "catalog:", "better-auth": "catalog:", "convex": "catalog:", - "dotenv": "catalog:", "zod": "catalog:" }, "devDependencies": {