UNPKG

@lunora/cli

Version:

The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands

303 lines (231 loc) 11.5 kB
--- name: lunora-quickstart description: Creates or adds Lunora to an app. Use for new Lunora projects, `lunora init`, framework/provider wiring, the first `lunora dev` run, env vars, or writing the first schema + query/mutation round-trip. --- # Lunora Quickstart Set up a working Lunora project as fast as possible. ## When to Use - Starting a brand new project with Lunora. - Adding Lunora to an existing Vite, Next.js, Astro, Nuxt, SvelteKit, or TanStack Start app. - Scaffolding a Lunora app for prototyping. ## When Not to Use - The project already has Lunora installed and `lunora/` exists — just build, and run `lunora codegen` after schema/function edits. - You only need to add auth to an existing Lunora app — use the `lunora-setup-auth` skill. ## Workflow 1. Determine the starting point: new project or existing app. 2. New project: scaffold with `lunora init` and pick a template. 3. Existing app: run `lunora init --here` to patch the Vite config and wire Lunora into the current project. 4. Run `lunora codegen` to generate `lunora/_generated/` and typecheck the schema + functions. This is the agent's feedback loop. 5. Start the dev loop. As an agent, run `lunora dev --background` — it starts the server as a managed detached process, blocks until it accepts requests, prints the URL + PID, and returns (under a detected AI agent, plain `lunora dev` does this automatically, with JSON logs). Never leave a bare `lunora dev` running in your own shell — it is long-running and does not exit. 6. Verify a query/mutation round-trip works end to end. ## Path 1: New Project (Recommended) `lunora init` fetches a whole-project template (frontend + worker entry + Vite plugin + `lunora/` already wired together). ```bash lunora init my-app --vite react cd my-app pnpm install ``` ### Pick a stack: `--vite` (SPA) or `-t` (bespoke template) There are **two scaffold paths**, and they take different flags: **`--vite <framework>` — the create-vite overlay.** Fetches the official create-vite base and applies the Lunora layer on top. Use it for a plain SPA: | `--vite` value | Stack | | -------------- | ----------------------------------------------- | | `react` | React SPA (**the default**) | | `vue` | Vue SPA | | `solid` | Solid SPA | | `svelte` | Svelte SPA | | `vanilla` | No framework (overlay-only — not in the picker) | **`-t` / `--template <type>` — a bespoke Lunora template.** Whole-project templates fetched remotely (via `giget`) from `gh:anolilab/lunora/templates/<type>`: | `-t` value | Stack | | ---------------------- | ---------------------------------------------------------- | | `next` | Next.js (App Router, OpenNext on Cloudflare) | | `tanstack-start-react` | TanStack Start (React) — SSR with live-loader routes | | `tanstack-start-solid` | TanStack Start (Solid) | | `react-router` | React Router v7 (framework mode), SSR in the Lunora worker | | `astro` | Astro + a standalone Lunora worker | | `analog` | AnalogJS (Angular) — single worker, Lunora in Nitro | | `nuxt` | Nuxt (Vue) — single worker, Lunora in Nitro | | `sveltekit` | SvelteKit + a standalone Lunora worker | | `expo` | React Native (Expo) — iOS/Android/web + a Lunora worker | | `standalone` | Worker-only Lunora backend, no frontend | > There is **no `--template vite`.** SPAs go through `--vite <framework>`; `-t` > is only for the bespoke templates above. With neither flag, an interactive run shows the framework picker (defaulting to the React overlay) and a **non-interactive run errors out** — so as an agent, always pass `--vite` or `-t` explicitly. If the user stated no preference, use `--vite react`. ### Useful `init` flags ```bash lunora init my-app --vite react --ci github # + a GitHub Actions deploy pipeline (or --ci gitlab) lunora init my-app -t next --add auth,email # scaffold capabilities non-interactively lunora init my-app --vite react --yes # skip the interactive auth/email offer lunora init my-app --vite react --dry-run # walk every step, write nothing ``` `--add` accepts a comma-separated list of `ai | auth | backup | browser | cloudflare-access | crons | email | flags | hyperdrive | payment | presence | queue | storage | workflow`. `--ref <branch|tag|commit>` pins the template source (e.g. `--ref alpha`); `--from <dir>` copies from a local templates root offline (expects `<type>/` subdirs). ### Generate types and push the first run Run this yourself — it is one-shot and exits cleanly: ```bash lunora codegen ``` It writes `lunora/_generated/` and typechecks your schema + functions. Read its output to find out whether the code you just wrote is valid. ### Start the dev loop ```bash lunora dev ``` `lunora dev` runs the Vite dev server with the Cloudflare Worker on the same origin, plus codegen-on-save and the Lunora Studio. It is long-running and does not exit, so: - **Local development (user at the keyboard):** ask the user to run `lunora dev` in a terminal. - **Agents:** run `lunora dev --background`. It detaches the server, waits until it answers HTTP, prints `Dev server running at <url> (pid <n>)`, and exits — no orphaned shell, no PID bookkeeping. When Lunora detects an AI agent (Claude Code, Cursor, Codex, …), plain `lunora dev` flips into this mode automatically with JSON logs; `LUNORA_AGENT_MODE=0` opts out. Manage the running server afterwards: ```bash lunora dev status --json # machine-readable: url, pid, uptime, logFile lunora dev logs --lines 50 # tail the captured output (.lunora/dev.log) lunora dev stop # idempotent — succeeds even if nothing runs ``` A second `lunora dev` never double-starts: it reports the existing instance (`.lunora/dev.json` is the lockfile). Probe readiness or liveness at `GET /_lunora/status` (`{"ok":true}`). Vite serves on `http://localhost:5173` by default; the Worker is served on the same origin via `@cloudflare/vite-plugin`. ## Path 2: Add Lunora to an Existing App Use this when the user already has a Vite-based frontend and wants Lunora as the backend. ```bash lunora init --here ``` This finds the existing `vite.config.*` (or creates a minimal one), patches in the Lunora Vite plugin, and scaffolds a starter `lunora/`. Then run `lunora codegen` and `lunora dev` as above. ### Wire up the client provider Create the `LunoraClient` once at module scope (never inside a component) and wrap the app with the framework provider. React example: ```tsx // src/client/main.tsx import { LunoraClient } from "@lunora/client"; import { LunoraProvider } from "@lunora/react"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { App } from "./App"; // @cloudflare/vite-plugin serves the Worker on the same origin as Vite. const url = (import.meta.env.VITE_LUNORA_URL as string | undefined) ?? globalThis.location.origin; const client = new LunoraClient({ url }); createRoot(document.querySelector("#root")!).render( <StrictMode> <LunoraProvider client={client}> <App /> </LunoraProvider> </StrictMode>, ); ``` Every client adapter has a matching provider: `@lunora/vue`, `@lunora/solid`, `@lunora/svelte`, `@lunora/angular` (`provideLunora` / `injectLunoraClient`), and `@lunora/react-native` (`createLunoraClient`, re-exporting `@lunora/react`). For meta-frameworks, `@lunora/astro` and `@lunora/nuxt` mount Lunora on the server side. `VITE_LUNORA_URL` is optional — it defaults to `location.origin`, which is correct for the single-origin dev setup. ## Writing Your First Function Create a schema and a query/mutation to verify the full loop. `lunora/schema.ts`: ```ts import { defineSchema, defineTable, v } from "@lunora/server"; export default defineSchema({ todos: defineTable({ text: v.string(), done: v.boolean(), createdAt: v.number(), }).index("by_creation", ["createdAt"]), }); ``` `lunora/todos.ts`: ```ts import type { Id } from "@lunora/server"; import { mutation, query, v } from "@lunora/server"; export const list = query.query(async ({ ctx }) => ctx.db.query("todos").withIndex("by_creation").collect()); export const add = mutation .input({ text: v.string() }) .mutation(async ({ ctx, args: { text } }): Promise<Id<"todos">> => ctx.db.insert("todos", { text, done: false, createdAt: Date.now() })); ``` Run `lunora codegen`, then use it in a component. The `api` object and `Doc` / `Id` types come from `lunora/_generated/`: ```tsx import { useMutation, useQuery } from "@lunora/react"; import { api } from "../../lunora/_generated/api"; import type { Doc } from "../../lunora/_generated/dataModel"; function Todos() { const todos = useQuery(api.todos.list, {}) as Doc<"todos">[] | undefined; const { mutate: add, pending } = useMutation(api.todos.add); return ( <div> <button disabled={pending} onClick={() => add({ text: "New todo" })}> Add </button> {todos?.map((t) => ( <div key={t._id}>{t.text}</div> ))} </div> ); } ``` `useQuery` opens a live subscription: the list re-renders the instant any mutation changes the queried rows. ## Development vs Production Use `lunora dev` during development. When ready to ship: ```bash lunora deploy ``` `lunora deploy` runs codegen, the schema-drift gate, and `wrangler deploy`. Do not use it during day-to-day development. Before deploying, run the preflight: ```bash lunora doctor ``` It checks `wrangler.jsonc` (the `SHARD` durable-object binding), D1 placeholder ids, `.dev.vars` secrets, and container exports. ## Next Steps - Add authentication: use the `lunora-setup-auth` skill. - Add a prebuilt capability (mail, presence, storage, rate limit, crons): `lunora registry add <item>` (see `lunora registry list`). For capabilities with a dedicated skill, use it: `lunora-setup-mail`, `lunora-setup-storage`, `lunora-setup-scheduler`. See the `lunora` router's capability entry for the full routing. - Build your own reusable capability: use the `lunora-create-package` skill. - Plan a schema change: use the `lunora-migration-helper` skill. - Scaffold more functions: `vis generate lunora-query --name=listMessages`, `lunora-mutation`, `lunora-action`, `lunora-table`, `lunora-cron` (always use the `--name=value` form). ## Checklist - [ ] Determined starting point: new project or existing app. - [ ] New project: scaffolded with `lunora init --vite <framework>` (SPA) or `lunora init -t <template>` (bespoke) — never `--template vite`. - [ ] Existing app: ran `lunora init --here` and wired `LunoraProvider`. - [ ] Ran `lunora codegen`: `lunora/_generated/` exists and typecheck is clean. - [ ] Dev server is running — user terminal, or `lunora dev --background` (check with `lunora dev status`). - [ ] Verified a query/mutation round-trip re-renders the client live.