@kardoe/quickback 0.5.14 → 0.5.16

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.
@@ -296,6 +296,7 @@ function getProvidersForPreset(preset, templateId) {
296
296
  authBinding: "AUTH_DB",
297
297
  featuresBinding: "DB",
298
298
  webhooksBinding: "WEBHOOKS_DB",
299
+ realtime: true,
299
300
  },
300
301
  },
301
302
  auth: { name: "better-auth", config: {} },
@@ -315,7 +316,7 @@ function getProvidersForPreset(preset, templateId) {
315
316
  case "cloudflare":
316
317
  return {
317
318
  runtime: { name: "cloudflare", config: {} },
318
- database: { name: "cloudflare-d1", config: { binding: "DB" } },
319
+ database: { name: "cloudflare-d1", config: { binding: "DB", realtime: true } },
319
320
  auth: { name: "better-auth", config: {} },
320
321
  };
321
322
  case "turso":
@@ -333,12 +334,10 @@ function getProvidersForPreset(preset, templateId) {
333
334
  }
334
335
  }
335
336
  function generateTodoFeature(targetDir) {
336
- // Combined mode: schema + config in a single file using defineTable()
337
337
  // Audit fields (createdAt, createdBy, modifiedAt, modifiedBy) and soft delete (deletedAt)
338
338
  // are added automatically by the compiler based on firewall config — do not define them here.
339
339
  const todosContent = `/**
340
- * Todo Feature — Combined Mode
341
- * Schema + security config in one file using defineTable().
340
+ * Todo Feature
342
341
  *
343
342
  * Audit fields (createdAt, createdBy, modifiedAt, modifiedBy) and soft delete
344
343
  * (deletedAt) are added automatically by the compiler — no need to define them.
@@ -388,7 +387,7 @@ export default defineTable(todos, {
388
387
  },
389
388
  });
390
389
  `;
391
- // Actions are still separate in combined mode
390
+ // Actions are defined in a separate file
392
391
  const actionsContent = `/**
393
392
  * Todo Actions
394
393
  * Custom business logic with security guards
@@ -430,8 +429,8 @@ function generateProjectsFeature(targetDir) {
430
429
  // SaaS template uses a richer "projects" feature with views
431
430
  // Audit fields and soft delete (deletedAt) are added automatically by the compiler.
432
431
  const projectsContent = `/**
433
- * Projects Feature — Combined Mode
434
- * Richer example for B2B SaaS with views.
432
+ * Projects Feature
433
+ * B2B SaaS example with views.
435
434
  *
436
435
  * Audit fields (createdAt, createdBy, modifiedAt, modifiedBy) and soft delete
437
436
  * (deletedAt) are added automatically by the compiler — no need to define them.
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/docs/content.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA6ZzC,CAAC;AAEF,eAAO,MAAM,UAAU,UAwGtB,CAAC"}
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/docs/content.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAiazC,CAAC;AAEF,eAAO,MAAM,UAAU,UAyGtB,CAAC"}
@@ -67,7 +67,7 @@ export const DOCS = {
67
67
  },
68
68
  "changelog": {
69
69
  "title": "Changelog",
70
- "content": "# Changelog\n\nRelease notes for the Quickback compiler, CLI, and platform.\n\n---\n\n## v0.5.11 — February 24, 2026\n\n### Email OTP & Auth Fixes\n\n- Fixed email-OTP magic links not working correctly\n- Removed deprecated `/internal/validate` endpoint — use standard Better Auth session validation instead\n- Auth is now required for all API routes when running locally (previously some routes were unprotected in dev)\n\n### Multiple Table Exports Fix\n\n- Fixed a compiler error when a single file exports multiple Drizzle tables alongside a `defineTable()` default export\n- The CLI now properly detects and reports this with a clear error message pointing to the fix\n\n### Headless Drizzle Rename Hints\n\n- Added `compiler.migrations.renames` configuration for CI/CD environments where Drizzle's interactive rename prompts would block compilation\n- Compile errors now include explicit rename key paths and fail fast on malformed rename config keys\n- See [Configuration](/compiler/config) for details\n\n### Security Contract Report Artifacts\n\n- Added generated security contract artifacts to compiler output:\n - `reports/security-contracts.report.json`\n - `reports/security-contracts.report.sig.json`\n- Added config-driven signing controls:\n - `compiler.securityContracts.report.signature.enabled`\n - `compiler.securityContracts.report.signature.required`\n - `compiler.securityContracts.report.signature.key` / `keyEnv` / `keyId`\n- Missing required signing keys now fail loudly with explicit remediation guidance\n- Added strict config validation for report/signature paths and signing options\n\n### Mandatory Unsafe Action Audit Trail\n\n- Added structured unsafe action config (`unsafe: { reason, adminOnly, crossTenant, targetScope }`)\n- Cross-tenant unsafe actions now require Better Auth authentication plus platform admin role (`ctx.userRole === \"admin\"`)\n- Added mandatory audit logging for unsafe cross-tenant actions (success, denial, and error paths)\n- Cloudflare output now includes optional `AUDIT_DB` wiring, `drizzle.audit.config.ts`, and `db:migrate:audit:*` scripts when unsafe actions are present\n- Added compile-time raw SQL guard for actions and handlers (`allowRawSql: true` required per action)\n\n---\n\n## v0.5.10 — February 19, 2026\n\n### Compiler Page Parsing & CLI Output\n\n- Improved compiler page parsing for `definePage()` definitions\n- Better CLI output formatting during compilation\n- Added `apiPath` to schema registry for CMS integration\n\n### Bug Fixes\n\n- Fixed hyphenated action names not being properly quoted in generated code (e.g., `mark-complete` now generates valid JavaScript)\n- API key authentication (`x-api-key` header) is now handled separately from session tokens (`Bearer` header)\n\n---\n\n## v0.5.9 — February 16, 2026\n\n### CMS Pages & CLI Page Support\n\n- Added `definePage()` support for CMS-managed pages\n- Auth middleware improvements for page routes\n- CLI now supports page definitions alongside table and action definitions\n\n---\n\n## v0.5.8 — February 14, 2026\n\n### CMS App\n\n- Introduced the Quickback CMS — a schema-driven admin panel that connects to your generated API\n- CMS namespace added to actions for admin-specific operations\n- Fixed `guard` → `access` naming inconsistency in CMS action definitions\n\n### Schema Registry & Firewall Improvements\n\n- Added schema registry generator — the compiler now outputs a JSON schema registry used by the CMS\n- Firewall error modes: choose between `reveal` (403 with details) and `hide` (opaque 404) for security-sensitive deployments\n\n### Bug Fixes\n\n- Fixed anonymous user email format generation\n- Organization selector improvements in Account UI\n- Config validation now catches more errors at compile time\n- Better CRUD error handling with structured error responses\n- Fixed masking to use representative star counts instead of fixed formatting\n\n---\n\n## v0.5.7 — February 12, 2026\n\n### Scoped Database for Actions\n\nActions now receive a **security-scoped database** instead of a raw Drizzle instance. The compiler generates a proxy wrapper that automatically enforces org isolation, owner filtering, and soft-delete visibility — the same protections that CRUD routes have always had.\n\nThe scoped DB uses duck-typed column detection at runtime:\n\n| Column Detected | SELECT / UPDATE / DELETE | INSERT |\n|---|---|---|\n| `organizationId` | Adds `WHERE organizationId = ?` | Auto-injects `organizationId` from context |\n| `ownerId` | Adds `WHERE ownerId = ?` | Auto-injects `ownerId` from context |\n| `deletedAt` | Adds `WHERE deletedAt IS NULL` | — |\n\nThis means **every action is secure by default** — no manual `WHERE` clauses needed.\n\n```typescript\ndefineActions(todos, {\n complete: {\n type: \"record\",\n execute: async ({ db, ctx, record, input }) => {\n // db is scoped — only sees records in user's org, excludes soft-deleted\n const siblings = await db.select().from(todos);\n // ↑ automatically filtered to ctx.activeOrgId + deletedAt IS NULL\n },\n },\n});\n```\n\n#### Unsafe Mode\n\nActions that intentionally need to bypass security (admin reports, cross-org queries, migrations) can declare `unsafe: true` to receive a raw, unscoped database handle:\n\n```typescript\ndefineActions(analytics, {\n globalReport: {\n unsafe: true,\n execute: async ({ db, rawDb, ctx, input }) => {\n // db → still scoped (safety net)\n // rawDb → bypasses all security filters\n const allOrgs = await rawDb.select().from(organizations);\n },\n },\n});\n```\n\nWithout `unsafe: true`, `rawDb` is `undefined`.\n\n**Related docs:** [Actions](/compiler/definitions/actions), [Actions API](/compiler/using-the-api/actions-api)\n\n---\n\n### Cascading Soft Delete\n\nSoft-deleting a parent record now **automatically cascades** to child and junction tables within the same feature. The compiler detects foreign key references at build time and generates cascade UPDATE statements.\n\n```\nDELETE /api/v1/projects/:id\n```\n\nGenerated behavior:\n```typescript\n// 1. Soft delete the parent\nawait db.update(projects)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projects.id, id));\n\n// 2. Auto-cascade to children (compiler-generated)\nawait db.update(projectMembers)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projectMembers.projectId, id));\n\nawait db.update(projectTasks)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projectTasks.projectId, id));\n```\n\nRules:\n- Only applies to **soft delete** (the default). Hard delete relies on database-level `ON DELETE CASCADE`.\n- Only cascades within the **same feature** — cross-feature references are not affected.\n- Child tables must have `deletedAt` / `deletedBy` columns (auto-added by the compiler's audit fields).\n\n**Related docs:** [Actions API — Cascading Soft Delete](/compiler/using-the-api/actions-api#cascading-soft-delete)\n\n---\n\n### Advanced Query Parameters\n\nNew query parameter capabilities for all list endpoints:\n\n- **Field selection** — `?fields=id,name,status` returns only the columns you need\n- **Multi-sort** — `?sort=status:asc,createdAt:desc` sorts by multiple fields\n- **Total count** — `?count=true` returns total matching records in response headers (`X-Total-Count`)\n- **Full-text search** — `?search=keyword` searches across all text columns\n\n```bash\n# Get only names and statuses, sorted by status then date, with total count\nGET /api/v1/todos?fields=id,name,status&sort=status:asc,createdAt:desc&count=true\n\n# Search across all text fields\nGET /api/v1/todos?search=urgent\n```\n\n**Related docs:** [Query Parameters](/compiler/using-the-api/query-params)\n\n---\n\n### Audit Field Improvements\n\n- `deletedAt` and `deletedBy` fields are now **always injected** by the compiler for tables with soft delete enabled — no need to define them in your schema\n- All audit fields (`createdAt`, `createdBy`, `modifiedAt`, `modifiedBy`, `deletedAt`, `deletedBy`) are auto-managed\n\n---\n\n## v0.5.6 — February 8, 2026\n\n### Database Naming Conventions\n\n- Default table and column naming changed to **snake_case** with `usePlurals: false`\n- Table names derived from generated Better Auth schema for consistency\n- Removed legacy single-database mode — split databases (auth + features) is now the standard\n\n### Auth Variable Shadowing Fix\n\n- Fixed `member` variable in auth middleware that shadowed the Drizzle `member` table import\n- Renamed to `sessionMember` to avoid conflicts in generated routes\n\n---\n\n## v0.5.5 — February 5, 2026\n\n### Better Auth Plugins\n\n- Published `@kardoe/better-auth-upgrade-anonymous` v1.1.0 — post-passkey email collection flow\n- Published `@kardoe/better-auth-combo-auth` — combined email + password + OTP authentication\n- Published `@kardoe/better-auth-aws-ses` — AWS SES email provider for Better Auth\n\n### OpenAPI Spec Generation\n\n- Generated APIs now include a full OpenAPI specification at `/openapi.json`\n- Better Auth endpoints included in the spec\n- Runtime route: `GET /openapi.json`\n\n### Security Hardening\n\n- Global error handler prevents leaking internal error details\n- Security headers middleware (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)\n- `BETTER_AUTH_SECRET` properly passed to generated config\n\n---\n\n## v0.5.4 — January 30, 2026\n\n### Account UI\n\n- Pre-built authentication UI deployed as Cloudflare Workers\n- Features: sessions, organizations, passkeys, passwordless, admin panel, API keys\n- Dual-mode: standalone (degit template) or embedded with Quickback projects\n\n### Webhook System\n\n- Inbound webhook endpoints with signature verification\n- Outbound webhooks via Cloudflare Queues with automatic retries\n- Configurable per-feature webhook events\n\n### Realtime & Vector Search\n\n- Durable Objects + WebSocket realtime subscriptions\n- Vector embeddings via Cloudflare Vectorize\n- KV and R2 storage integrations\n\n---\n\n## v0.5.0 — January 2026\n\n### Initial Release\n\n- **Quickback Compiler** — TypeScript-first backend compiler\n- **Four Security Pillars** — Firewall, Access, Guards, Masking\n- **Combined Mode** — `defineTable()` with schema + security in a single file\n- **Templates** — Cloudflare Workers, Bun standalone, B2B SaaS\n- **Cloud Compiler** — Remote compilation via `compiler.quickback.dev`\n- **CLI** — `quickback create`, `quickback compile`, `quickback init`\n- **Better Auth Integration** — Organizations, roles, sessions\n- **Drizzle ORM** — Schema-first with automatic migrations\n- **Cloudflare D1** — Split database support (auth + features)"
70
+ "content": "# Changelog\n\nRelease notes for the Quickback compiler, CLI, and platform.\n\n---\n\n## v0.5.11 — February 24, 2026\n\n### Email OTP & Auth Fixes\n\n- Fixed email-OTP magic links not working correctly\n- Removed deprecated `/internal/validate` endpoint — use standard Better Auth session validation instead\n- Auth is now required for all API routes when running locally (previously some routes were unprotected in dev)\n\n### Multiple Table Exports Fix\n\n- Fixed a compiler error when a single file exports multiple Drizzle tables alongside a `defineTable()` default export\n- The CLI now properly detects and reports this with a clear error message pointing to the fix\n\n### Headless Drizzle Rename Hints\n\n- Added `compiler.migrations.renames` configuration for CI/CD environments where Drizzle's interactive rename prompts would block compilation\n- Compile errors now include explicit rename key paths and fail fast on malformed rename config keys\n- See [Configuration](/compiler/config) for details\n\n### Security Contract Report Artifacts\n\n- Added generated security contract artifacts to compiler output:\n - `reports/security-contracts.report.json`\n - `reports/security-contracts.report.sig.json`\n- Added config-driven signing controls:\n - `compiler.securityContracts.report.signature.enabled`\n - `compiler.securityContracts.report.signature.required`\n - `compiler.securityContracts.report.signature.key` / `keyEnv` / `keyId`\n- Missing required signing keys now fail loudly with explicit remediation guidance\n- Added strict config validation for report/signature paths and signing options\n\n### Mandatory Unsafe Action Audit Trail\n\n- Added structured unsafe action config (`unsafe: { reason, adminOnly, crossTenant, targetScope }`)\n- Cross-tenant unsafe actions now require Better Auth authentication plus platform admin role (`ctx.userRole === \"admin\"`)\n- Added mandatory audit logging for unsafe cross-tenant actions (success, denial, and error paths)\n- Cloudflare output now includes optional `AUDIT_DB` wiring, `drizzle.audit.config.ts`, and `db:migrate:audit:*` scripts when unsafe actions are present\n- Added compile-time raw SQL guard for actions and handlers (`allowRawSql: true` required per action)\n\n---\n\n## v0.5.10 — February 19, 2026\n\n### Compiler Page Parsing & CLI Output\n\n- Improved compiler page parsing for `definePage()` definitions\n- Better CLI output formatting during compilation\n- Added `apiPath` to schema registry for CMS integration\n\n### Bug Fixes\n\n- Fixed hyphenated action names not being properly quoted in generated code (e.g., `mark-complete` now generates valid JavaScript)\n- API key authentication (`x-api-key` header) is now handled separately from session tokens (`Bearer` header)\n\n---\n\n## v0.5.9 — February 16, 2026\n\n### CMS Pages & CLI Page Support\n\n- Added `definePage()` support for CMS-managed pages\n- Auth middleware improvements for page routes\n- CLI now supports page definitions alongside table and action definitions\n\n---\n\n## v0.5.8 — February 14, 2026\n\n### CMS App\n\n- Introduced the Quickback CMS — a schema-driven admin panel that connects to your generated API\n- CMS namespace added to actions for admin-specific operations\n- Fixed `guard` → `access` naming inconsistency in CMS action definitions\n\n### Schema Registry & Firewall Improvements\n\n- Added schema registry generator — the compiler now outputs a JSON schema registry used by the CMS\n- Firewall error modes: choose between `reveal` (403 with details) and `hide` (opaque 404) for security-sensitive deployments\n\n### Bug Fixes\n\n- Fixed anonymous user email format generation\n- Organization selector improvements in Account UI\n- Config validation now catches more errors at compile time\n- Better CRUD error handling with structured error responses\n- Fixed masking to use representative star counts instead of fixed formatting\n\n---\n\n## v0.5.7 — February 12, 2026\n\n### Scoped Database for Actions\n\nActions now receive a **security-scoped database** instead of a raw Drizzle instance. The compiler generates a proxy wrapper that automatically enforces org isolation, owner filtering, and soft-delete visibility — the same protections that CRUD routes have always had.\n\nThe scoped DB uses duck-typed column detection at runtime:\n\n| Column Detected | SELECT / UPDATE / DELETE | INSERT |\n|---|---|---|\n| `organizationId` | Adds `WHERE organizationId = ?` | Auto-injects `organizationId` from context |\n| `ownerId` | Adds `WHERE ownerId = ?` | Auto-injects `ownerId` from context |\n| `deletedAt` | Adds `WHERE deletedAt IS NULL` | — |\n\nThis means **every action is secure by default** — no manual `WHERE` clauses needed.\n\n```typescript\ndefineActions(todos, {\n complete: {\n type: \"record\",\n execute: async ({ db, ctx, record, input }) => {\n // db is scoped — only sees records in user's org, excludes soft-deleted\n const siblings = await db.select().from(todos);\n // ↑ automatically filtered to ctx.activeOrgId + deletedAt IS NULL\n },\n },\n});\n```\n\n#### Unsafe Mode\n\nActions that intentionally need to bypass security (admin reports, cross-org queries, migrations) can declare `unsafe: true` to receive a raw, unscoped database handle:\n\n```typescript\ndefineActions(analytics, {\n globalReport: {\n unsafe: true,\n execute: async ({ db, rawDb, ctx, input }) => {\n // db → still scoped (safety net)\n // rawDb → bypasses all security filters\n const allOrgs = await rawDb.select().from(organizations);\n },\n },\n});\n```\n\nWithout `unsafe: true`, `rawDb` is `undefined`.\n\n**Related docs:** [Actions](/compiler/definitions/actions), [Actions API](/compiler/using-the-api/actions-api)\n\n---\n\n### Cascading Soft Delete\n\nSoft-deleting a parent record now **automatically cascades** to child and junction tables within the same feature. The compiler detects foreign key references at build time and generates cascade UPDATE statements.\n\n```\nDELETE /api/v1/projects/:id\n```\n\nGenerated behavior:\n```typescript\n// 1. Soft delete the parent\nawait db.update(projects)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projects.id, id));\n\n// 2. Auto-cascade to children (compiler-generated)\nawait db.update(projectMembers)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projectMembers.projectId, id));\n\nawait db.update(projectTasks)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projectTasks.projectId, id));\n```\n\nRules:\n- Only applies to **soft delete** (the default). Hard delete relies on database-level `ON DELETE CASCADE`.\n- Only cascades within the **same feature** — cross-feature references are not affected.\n- Child tables must have `deletedAt` / `deletedBy` columns (auto-added by the compiler's audit fields).\n\n**Related docs:** [Actions API — Cascading Soft Delete](/compiler/using-the-api/actions-api#cascading-soft-delete)\n\n---\n\n### Advanced Query Parameters\n\nNew query parameter capabilities for all list endpoints:\n\n- **Field selection** — `?fields=id,name,status` returns only the columns you need\n- **Multi-sort** — `?sort=status:asc,createdAt:desc` sorts by multiple fields\n- **Total count** — `?count=true` returns total matching records in response headers (`X-Total-Count`)\n- **Full-text search** — `?search=keyword` searches across all text columns\n\n```bash\n# Get only names and statuses, sorted by status then date, with total count\nGET /api/v1/todos?fields=id,name,status&sort=status:asc,createdAt:desc&count=true\n\n# Search across all text fields\nGET /api/v1/todos?search=urgent\n```\n\n**Related docs:** [Query Parameters](/compiler/using-the-api/query-params)\n\n---\n\n### Audit Field Improvements\n\n- `deletedAt` and `deletedBy` fields are now **always injected** by the compiler for tables with soft delete enabled — no need to define them in your schema\n- All audit fields (`createdAt`, `createdBy`, `modifiedAt`, `modifiedBy`, `deletedAt`, `deletedBy`) are auto-managed\n\n---\n\n## v0.5.6 — February 8, 2026\n\n### Database Naming Conventions\n\n- Default table and column naming changed to **snake_case** with `usePlurals: false`\n- Table names derived from generated Better Auth schema for consistency\n- Removed legacy single-database mode — split databases (auth + features) is now the standard\n\n### Auth Variable Shadowing Fix\n\n- Fixed `member` variable in auth middleware that shadowed the Drizzle `member` table import\n- Renamed to `sessionMember` to avoid conflicts in generated routes\n\n---\n\n## v0.5.5 — February 5, 2026\n\n### Better Auth Plugins\n\n- Published `@kardoe/better-auth-upgrade-anonymous` v1.1.0 — post-passkey email collection flow\n- Published `@kardoe/better-auth-combo-auth` — combined email + password + OTP authentication\n- Published `@kardoe/better-auth-aws-ses` — AWS SES email provider for Better Auth\n\n### OpenAPI Spec Generation\n\n- Generated APIs now include a full OpenAPI specification at `/openapi.json`\n- Better Auth endpoints included in the spec\n- Runtime route: `GET /openapi.json`\n\n### Security Hardening\n\n- Global error handler prevents leaking internal error details\n- Security headers middleware (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)\n- `BETTER_AUTH_SECRET` properly passed to generated config\n\n---\n\n## v0.5.4 — January 30, 2026\n\n### Account UI\n\n- Pre-built authentication UI deployed as Cloudflare Workers\n- Features: sessions, organizations, passkeys, passwordless, admin panel, API keys\n- Dual-mode: standalone (degit template) or embedded with Quickback projects\n\n### Webhook System\n\n- Inbound webhook endpoints with signature verification\n- Outbound webhooks via Cloudflare Queues with automatic retries\n- Configurable per-feature webhook events\n\n### Realtime & Vector Search\n\n- Durable Objects + WebSocket realtime subscriptions\n- Vector embeddings via Cloudflare Vectorize\n- KV and R2 storage integrations\n\n---\n\n## v0.5.0 — January 2026\n\n### Initial Release\n\n- **Quickback Compiler** — TypeScript-first backend compiler\n- **Four Security Pillars** — Firewall, Access, Guards, Masking\n- **`defineTable()`** — Schema + security configuration in a single file\n- **Templates** — Cloudflare Workers, Bun standalone, B2B SaaS\n- **Cloud Compiler** — Remote compilation via `compiler.quickback.dev`\n- **CLI** — `quickback create`, `quickback compile`, `quickback init`\n- **Better Auth Integration** — Organizations, roles, sessions\n- **Drizzle ORM** — Schema-first with automatic migrations\n- **Cloudflare D1** — Split database support (auth + features)"
71
71
  },
72
72
  "cms/actions": {
73
73
  "title": "Actions",
@@ -91,7 +91,7 @@ export const DOCS = {
91
91
  },
92
92
  "cms/record-layouts": {
93
93
  "title": "Record Layouts",
94
- "content": "# Record Layouts\n\nThe CMS record detail page groups fields into collapsible sections. By default, fields are auto-grouped by naming heuristics (Identity, Contact Info, Financial, etc.). With **record layouts**, you control the exact grouping.\n\nThere are two layers:\n\n1. **Code-defined layouts** — developers configure named layouts in `defineTable()`\n2. **User-created views** — end-users create and save custom views via the CMS UI\n\n## Code-Defined Layouts\n\nAdd a `layouts` property to your `defineTable()` config:\n\n```typescript\nexport default defineTable(contacts, {\n firewall: { organization: {} },\n crud: { /* ... */ },\n layouts: {\n default: {\n sections: [\n { label: \"Contact Info\", columns: 2, fields: [\"name\", \"email\", \"phone\", \"mobile\"] },\n { label: \"Address\", columns: 2, fields: [\"address1\", \"address2\", \"city\", \"state\", \"zip\"] },\n { label: \"Internal Notes\", collapsed: true, fields: [\"notes\", \"internalNotes\"] }\n ]\n },\n compact: {\n sections: [\n { label: \"Summary\", fields: [\"name\", \"status\", \"email\"] }\n ]\n }\n }\n});\n```\n\nEach layout has an ordered list of sections. Each section specifies:\n\n| Property | Type | Default | Description |\n|----------|------|---------|-------------|\n| `label` | string | required | Section header text |\n| `fields` | string[] | required | Column names to display |\n| `columns` | `1 \\| 2` | `1` | Number of columns for field layout |\n| `collapsed` | boolean | `false` | Whether the section starts collapsed |\n\nFields not assigned to any section are collected into an \"Other Fields\" section at the bottom.\n\n### Layout Switcher\n\nWhen a table has multiple named layouts, a dropdown appears in the record detail header. Selections persist per table using `localStorage`.\n\nIf only one layout is defined, it's used automatically without showing a dropdown.\n\n## User-Created Custom Views\n\nEnd-users can create their own record layouts via the CMS UI. These are stored in the database and can be shared with other organization members.\n\n### Creating a View\n\n1. Open any record detail page\n2. Click the **+ View** button in the header\n3. In the view builder dialog:\n - Name your view\n - Add sections and assign fields from a dropdown\n - Set columns (1 or 2) and collapsed state per section\n - Optionally share with your organization\n4. Click **Create View**\n\nThe new view appears in the layout dropdown alongside code-defined layouts.\n\n### Editing and Deleting Views\n\n- Click the **gear icon** next to the dropdown to edit the current custom view\n- Click the **trash icon** to delete it (with confirmation)\n- Code-defined layouts cannot be edited or deleted from the CMS\n\n### Access Control\n\n| Operation | Who Can Do It |\n|-----------|---------------|\n| Create a view | Any member, admin, or owner |\n| Edit/delete own views | The creator |\n| Edit/delete any view | Admins and owners |\n| View shared views | All organization members |\n\n### Setting Up the Custom View Feature\n\nTo enable user-created views, add a `customView` table to your Quickback project:\n\n```typescript\n// quickback/features/cms/custom-view.ts\n\nexport const customView = sqliteTable(\"custom_view\", {\n id: text(\"id\").primaryKey(),\n organizationId: text(\"organization_id\").notNull(),\n tableName: text(\"table_name\").notNull(),\n name: text(\"name\").notNull(),\n description: text(\"description\"),\n layoutConfig: text(\"layout_config\").notNull(),\n isShared: integer(\"is_shared\", { mode: \"boolean\" }).default(false),\n});\n\nexport default defineTable(customView, {\n displayColumn: \"name\",\n firewall: { organization: {} },\n crud: {\n list: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n get: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n create: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n update: {\n access: {\n or: [\n { roles: [\"owner\", \"admin\"] },\n { roles: [\"member\"], record: { createdBy: { equals: \"$userId\" } } }\n ]\n }\n },\n delete: {\n access: {\n or: [\n { roles: [\"owner\", \"admin\"] },\n { roles: [\"member\"], record: { createdBy: { equals: \"$userId\" } } }\n ]\n },\n mode: \"hard\"\n }\n },\n guards: {\n createable: [\"tableName\", \"name\", \"description\", \"layoutConfig\", \"isShared\"],\n updatable: [\"name\", \"description\", \"layoutConfig\", \"isShared\"]\n }\n});\n```\n\nCompile your project to generate the API endpoints. The CMS will automatically detect the `customView` table and enable the view builder UI.\n\n## Fallback Behavior\n\n| Scenario | Result |\n|----------|--------|\n| No `layouts` config, no custom views | Auto-grouping by naming heuristics |\n| `layouts` config defined | Uses \"default\" layout or first available |\n| Multiple layouts | Dropdown for switching, persisted per table |\n| Custom views created | Appear in dropdown below code-defined layouts |\n\n## Next Steps\n\n- **[Table Views](/cms/table-views)** — Column projections for list views\n- **[Schema Format](/cms/schema-format)** — Full TypeScript type reference\n- **[Database Schema](/compiler/definitions/database-schema)** — defineTable() configuration reference"
94
+ "content": "# Record Layouts\n\nThe CMS record detail page groups fields into collapsible sections. By default, fields are auto-grouped by naming heuristics (Identity, Contact Info, Financial, etc.). With **record layouts**, you control the exact grouping.\n\nThere are two layers:\n\n1. **Code-defined layouts** — developers configure named layouts in `defineTable()`\n2. **User-created views** — end-users create and save custom views via the CMS UI\n\n## Code-Defined Layouts\n\nAdd a `layouts` property to your `defineTable()` config:\n\n```typescript\nexport default defineTable(contacts, {\n firewall: { organization: {} },\n crud: { /* ... */ },\n layouts: {\n default: {\n sections: [\n { label: \"Contact Info\", columns: 2, fields: [\"name\", \"email\", \"phone\", \"mobile\"] },\n { label: \"Address\", columns: 2, fields: [\"address1\", \"address2\", \"city\", \"state\", \"zip\"] },\n { label: \"Internal Notes\", collapsed: true, fields: [\"notes\", \"internalNotes\"] }\n ]\n },\n compact: {\n sections: [\n { label: \"Summary\", fields: [\"name\", \"status\", \"email\"] }\n ]\n }\n }\n});\n```\n\nEach layout has an ordered list of sections. Each section specifies:\n\n| Property | Type | Default | Description |\n|----------|------|---------|-------------|\n| `label` | string | required | Section header text |\n| `fields` | string[] | required | Column names to display |\n| `columns` | `1 \\| 2` | `1` | Number of columns for field layout |\n| `collapsed` | boolean | `false` | Whether the section starts collapsed |\n\nFields not assigned to any section are collected into an \"Other Fields\" section at the bottom.\n\n### Layout Switcher\n\nWhen a table has multiple named layouts, a dropdown appears in the record detail header. Selections persist per table using `localStorage`.\n\nIf only one layout is defined, it's used automatically without showing a dropdown.\n\n## User-Created Custom Views\n\nEnd-users can create their own record layouts via the CMS UI. These are stored in the database and can be shared with other organization members.\n\n### Creating a View\n\n1. Open any record detail page\n2. Click the **+ View** button in the header\n3. In the view builder dialog:\n - Name your view\n - Add sections and assign fields from a dropdown\n - Set columns (1 or 2) and collapsed state per section\n - Optionally share with your organization\n4. Click **Create View**\n\nThe new view appears in the layout dropdown alongside code-defined layouts.\n\n### Editing and Deleting Views\n\n- Click the **gear icon** next to the dropdown to edit the current custom view\n- Click the **trash icon** to delete it (with confirmation)\n- Code-defined layouts cannot be edited or deleted from the CMS\n\n### Access Control\n\n| Operation | Who Can Do It |\n|-----------|---------------|\n| Create a view | Any member, admin, or owner |\n| Edit/delete own views | The creator |\n| Edit/delete any view | Admins and owners |\n| View shared views | All organization members |\n\n### Setting Up the Custom View Feature\n\nTo enable user-created views, add a `customView` table to your Quickback project:\n\n```typescript\n// quickback/features/cms/custom-view.ts\n\nexport const customView = sqliteTable(\"custom_view\", {\n id: text(\"id\").primaryKey(),\n organizationId: text(\"organization_id\").notNull(),\n tableName: text(\"table_name\").notNull(),\n name: text(\"name\").notNull(),\n description: text(\"description\"),\n layoutConfig: text(\"layout_config\").notNull(),\n isShared: integer(\"is_shared\", { mode: \"boolean\" }).default(false),\n});\n\nexport default defineTable(customView, {\n displayColumn: \"name\",\n firewall: { organization: {} },\n crud: {\n list: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n get: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n create: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n update: {\n access: {\n or: [\n { roles: [\"owner\", \"admin\"] },\n { roles: [\"member\"], record: { createdBy: { equals: \"$userId\" } } }\n ]\n }\n },\n delete: {\n access: {\n or: [\n { roles: [\"owner\", \"admin\"] },\n { roles: [\"member\"], record: { createdBy: { equals: \"$userId\" } } }\n ]\n },\n mode: \"hard\"\n }\n },\n guards: {\n createable: [\"tableName\", \"name\", \"description\", \"layoutConfig\", \"isShared\"],\n updatable: [\"name\", \"description\", \"layoutConfig\", \"isShared\"]\n }\n});\n```\n\nCompile your project to generate the API endpoints. The CMS will automatically detect the `customView` table and enable the view builder UI.\n\n## Fallback Behavior\n\n| Scenario | Result |\n|----------|--------|\n| No `layouts` config, no custom views | Auto-grouping by naming heuristics |\n| `layouts` config defined | Uses \"default\" layout or first available |\n| Multiple layouts | Dropdown for switching, persisted per table |\n| Custom views created | Appear in dropdown below code-defined layouts |\n\n## Next Steps\n\n- **[Table Views](/cms/table-views)** — Column projections for list views\n- **[Schema Format](/cms/schema-format)** — Full TypeScript type reference\n- **[Database Schema](/compiler/definitions/schema)** — defineTable() configuration reference"
95
95
  },
96
96
  "cms/schema-format": {
97
97
  "title": "Schema Format Reference",
@@ -179,7 +179,7 @@ export const DOCS = {
179
179
  },
180
180
  "compiler/definitions/schema": {
181
181
  "title": "Database Schema",
182
- "content": "Quickback uses [Drizzle ORM](https://orm.drizzle.team/) to define your database schema. With `defineTable`, you combine your schema definition and security configuration in a single file.\n\n## Defining Tables with defineTable\n\nEach table gets its own file with schema and config together. Use the Drizzle dialect that matches your target database:\n\n| Target Database | Import From | Table Function |\n|-----------------|-------------|----------------|\n| Cloudflare D1, Turso, SQLite | `drizzle-orm/sqlite-core` | `sqliteTable` |\n| Supabase, Neon, PostgreSQL | `drizzle-orm/pg-core` | `pgTable` |\n| PlanetScale, MySQL | `drizzle-orm/mysql-core` | `mysqlTable` |\n\n```typescript\n// quickback/features/jobs/jobs.ts\n// For D1/SQLite targets:\n\nexport const jobs = sqliteTable('jobs', {\n id: text('id').primaryKey(),\n organizationId: text('organization_id').notNull(),\n title: text('title').notNull(),\n department: text('department').notNull(),\n status: text('status').notNull(), // \"draft\" | \"open\" | \"closed\"\n salaryMin: integer('salary_min'),\n salaryMax: integer('salary_max'),\n});\n\nexport default defineTable(jobs, {\n firewall: { organization: {} },\n guards: {\n createable: [\"title\", \"department\", \"status\", \"salaryMin\", \"salaryMax\"],\n updatable: [\"title\", \"department\", \"status\"],\n },\n crud: {\n list: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\", \"interviewer\"] } },\n get: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\", \"interviewer\"] } },\n create: { access: { roles: [\"owner\", \"hiring-manager\"] } },\n update: { access: { roles: [\"owner\", \"hiring-manager\"] } },\n delete: { access: { roles: [\"owner\", \"hiring-manager\"] } },\n },\n});\n\nexport type Job = typeof jobs.$inferSelect;\n```\n\n## Column Types\n\nDrizzle supports all standard SQL column types:\n\n| Type | Drizzle Function | Example |\n|------|------------------|---------|\n| String | `text()`, `varchar()` | `text('name')` |\n| Integer | `integer()`, `bigint()` | `integer('count')` |\n| Boolean | `boolean()` | `boolean('is_active')` |\n| Timestamp | `timestamp()` | `timestamp('created_at')` |\n| JSON | `json()`, `jsonb()` | `jsonb('metadata')` |\n| UUID | `uuid()` | `uuid('id')` |\n| Decimal | `decimal()`, `numeric()` | `decimal('price', { precision: 10, scale: 2 })` |\n\n## Column Modifiers\n\n```typescript\n// Required field\nname: text('name').notNull()\n\n// Default value\nisActive: boolean('is_active').default(true)\n\n// Primary key\nid: text('id').primaryKey()\n\n// Unique constraint\nemail: text('email').unique()\n\n// Default to current timestamp\ncreatedAt: timestamp('created_at').defaultNow()\n```\n\n## File Organization\n\nOrganize your tables by feature. Each feature directory contains table files:\n\n```\nquickback/\n├── quickback.config.ts\n└── features/\n ├── jobs/\n │ ├── jobs.ts # Main table + config\n │ ├── applications.ts # Related table + config\n │ └── actions.ts # Custom actions\n ├── candidates/\n │ ├── candidates.ts # Table + config\n │ └── candidate-notes.ts # Related table\n └── organizations/\n └── organizations.ts\n```\n\n**Key points:**\n- Tables with `export default defineTable(...)` get CRUD routes generated\n- Tables without a default export are internal (no routes, used for joins/relations)\n- Route paths are derived from filenames: `applications.ts` → `/api/v1/applications`\n\n### defineTable vs defineResource\n\nBoth functions are available:\n\n- **`defineTable`** - The standard function for defining tables with CRUD routes\n- **`defineResource`** - Alias for `defineTable`, useful when thinking in terms of REST resources\n\n```typescript\n// These are equivalent:\nexport default defineTable(jobs, { /* config */ });\nexport default defineResource(jobs, { /* config */ });\n```\n\n## 1 Resource = 1 Security Boundary\n\nEach `defineTable()` call defines a complete, self-contained security boundary. The security config you write — firewall, access, guards, and masking — is compiled into a single resource file that wraps all CRUD routes for that table.\n\nThis is a deliberate design choice. Mixing two resources with different security rules in one configuration would create ambiguity about which firewall, access, or masking rules apply to which table. By keeping it 1:1, there's never any question.\n\n| Scenario | What to do |\n|----------|------------|\n| Table needs its own API routes + security | Own file with `defineTable()` |\n| Table is internal/supporting (no direct API) | Extra `.ts` file in the parent feature directory, no `defineTable()` |\n\nA supporting table without `defineTable()` is useful when it's accessed internally — by action handlers, joins, or background jobs — but should never be directly exposed as its own API endpoint.\n\n## Internal Tables (No Routes)\n\nFor junction tables or internal data structures that shouldn't have API routes, simply omit the `defineTable` export:\n\n```typescript\n// quickback/features/jobs/interview-scores.ts\n\n// Internal scoring table - no routes needed\nexport const interviewScores = sqliteTable('interview_scores', {\n applicationId: text('application_id').notNull(),\n interviewerId: text('interviewer_id').notNull(),\n score: integer('score'),\n organizationId: text('organization_id').notNull(), // Always scope junction tables\n});\n\n// No default export = no CRUD routes generated\n```\n\nThese internal tables still participate in the database schema and migrations — they just don't get API routes or security configuration.\n\nThe compiler automatically injects **audit fields** (`createdAt`, `modifiedAt`, `deletedAt`, etc.) and **`organizationId`** into child/junction tables when the parent feature is org-scoped. This ensures cascade soft deletes and scoped queries work correctly without manual column definitions.\n\n## Audit Fields\n\nQuickback automatically adds and manages these audit fields on **all tables** in a feature (including child/junction tables without `defineTable`) - you don't need to define them:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `createdAt` | timestamp | Set when record is created |\n| `createdBy` | text | User ID who created the record |\n| `modifiedAt` | timestamp | Updated on every change |\n| `modifiedBy` | text | User ID who last modified |\n| `deletedAt` | timestamp | Set on soft delete (optional) |\n| `deletedBy` | text | User ID who deleted (optional) |\n\nThe soft delete fields (`deletedAt`, `deletedBy`) are only added if your resource uses soft delete mode.\n\n### Disabling Audit Fields\n\nTo disable automatic audit fields for your project:\n\n```typescript\n// quickback.config.ts\nexport default defineConfig({\n // ...\n compiler: {\n features: {\n auditFields: false, // Disable for entire project\n }\n }\n});\n```\n\n### Protected System Fields\n\nThese fields are always protected and cannot be set by clients, even with `guards: false`:\n\n- `id` (when `generateId` is not `false`)\n- `createdAt`, `createdBy`\n- `modifiedAt`, `modifiedBy`\n- `deletedAt`, `deletedBy`\n\n## Ownership Fields\n\nFor the firewall to work, include the appropriate ownership columns:\n\n```typescript\n// For organization-scoped data (most common)\norganizationId: text('organization_id').notNull()\n\n// For user-owned data (personal data)\nownerId: text('owner_id').notNull()\n\n// For team-scoped data\nteamId: text('team_id').notNull()\n```\n\n## Display Column\n\nWhen a table has foreign key columns (e.g., `departmentId` referencing `departments`), Quickback can automatically resolve the human-readable label in GET and LIST responses. This eliminates the need for frontend lookup calls.\n\n### Auto-Detection\n\nQuickback auto-detects the display column by checking for common column names in this order:\n\n`name` → `title` → `label` → `headline` → `subject` → `code` → `displayName` → `fullName` → `description`\n\nIf your `departments` table has a `name` column, it's automatically used as the display column. No config needed.\n\n### Explicit Override\n\nOverride the auto-detected column with `displayColumn`:\n\n```typescript\nexport default defineTable(interviewStages, {\n displayColumn: 'label', // Use 'label' instead of auto-detected 'name'\n firewall: { organization: {} },\n crud: {\n list: { access: { roles: [\"hiring-manager\", \"recruiter\"] } },\n get: { access: { roles: [\"hiring-manager\", \"recruiter\"] } },\n },\n});\n```\n\n### How Labels Appear in Responses\n\nFor any FK column ending in `Id`, the API adds a `_label` field with the resolved display value:\n\n```json\n{\n \"id\": \"job_abc\",\n \"title\": \"Senior Engineer\",\n \"departmentId\": \"dept_xyz\",\n \"department_label\": \"Engineering\",\n \"locationId\": \"loc_123\",\n \"location_label\": \"San Francisco\"\n}\n```\n\nThe pattern is `{columnWithoutId}_label`. The frontend can find all labels with:\n\n```typescript\nObject.keys(record).filter(k => k.endsWith('_label'))\n```\n\nLabel resolution works within the same feature (tables in the same feature directory). System columns (`organizationId`, `createdBy`, `modifiedBy`) are never resolved.\n\nFor LIST endpoints, labels are batch-resolved efficiently — one query per FK column, not per record.\n\n## References\n\nWhen your FK columns don't match the target table name by convention (e.g., `vendorId` actually points to the `contact` table), declare explicit references so the CMS and schema registry know the correct target:\n\n```typescript\nexport default defineTable(applications, {\n references: {\n candidateId: \"candidate\",\n jobId: \"job\",\n referredById: \"candidate\",\n },\n // ... firewall, guards, crud\n});\n```\n\nEach key is a column name ending in `Id`, and the value is the camelCase table name it references. These mappings flow into the schema registry as `fkTarget` on each column, enabling the CMS to render typeahead/lookup inputs that search the correct table.\n\nConvention-based matching (strip `Id` suffix, look for a matching table) still works for simple cases like `projectId` → `project`. Use `references` only for columns where the convention doesn't match.\n\n## Input Hints\n\nControl how the CMS renders form inputs for specific columns. By default the CMS infers input types from the column's SQL type — `inputHints` lets you override that:\n\n```typescript\nexport default defineTable(jobs, {\n inputHints: {\n status: \"select\",\n department: \"select\",\n salaryMin: \"currency\",\n salaryMax: \"currency\",\n description: \"textarea\",\n },\n // ... firewall, guards, crud\n});\n```\n\n### Available Hint Values\n\n| Hint | Renders As |\n|------|-----------|\n| `select` | Dropdown select (single value) |\n| `multi-select` | Multi-value select |\n| `radio` | Radio button group |\n| `checkbox` | Checkbox toggle |\n| `textarea` | Multi-line text input |\n| `lookup` | FK typeahead search |\n| `hidden` | Hidden from forms |\n| `color` | Color picker |\n| `date` | Date picker |\n| `datetime` | Date + time picker |\n| `time` | Time picker |\n| `currency` | Currency input with formatting |\n\nInput hints are emitted in the schema registry as `inputHints` on the table metadata, and the CMS reads them to render the appropriate form controls.\n\n## Relations (Optional)\n\nDrizzle supports defining relations for type-safe joins:\n\n```typescript\n\nexport const jobsRelations = relations(jobs, ({ one, many }) => ({\n organization: one(organizations, {\n fields: [jobs.organizationId],\n references: [organizations.id],\n }),\n applications: many(applications),\n}));\n```\n\n## Database Configuration\n\nConfigure database options in your Quickback config:\n\n```typescript\n// quickback.config.ts\nexport default defineConfig({\n name: 'my-app',\n providers: {\n database: defineDatabase('cloudflare-d1', {\n generateId: 'prefixed', // 'uuid' | 'cuid' | 'nanoid' | 'prefixed' | 'serial' | false\n namingConvention: 'snake_case', // 'camelCase' | 'snake_case'\n usePlurals: false, // Auth table names: 'users' vs 'user'\n }),\n },\n compiler: {\n features: {\n auditFields: true, // Auto-manage audit timestamps\n }\n }\n});\n```\n\n## Choosing Your Dialect\n\nUse the Drizzle dialect that matches your database provider:\n\n### SQLite (D1, Turso, better-sqlite3)\n\n```typescript\n\nexport const jobs = sqliteTable('jobs', {\n id: text('id').primaryKey(),\n title: text('title').notNull(),\n metadata: text('metadata', { mode: 'json' }), // JSON stored as text\n isOpen: integer('is_open', { mode: 'boolean' }).default(false),\n organizationId: text('organization_id').notNull(),\n});\n```\n\n### PostgreSQL (Supabase, Neon)\n\n```typescript\n\nexport const jobs = pgTable('jobs', {\n id: serial('id').primaryKey(),\n title: text('title').notNull(),\n metadata: jsonb('metadata'), // Native JSONB\n isOpen: boolean('is_open').default(false),\n organizationId: text('organization_id').notNull(),\n});\n```\n\n### Key Differences\n\n| Feature | SQLite | PostgreSQL |\n|---------|--------|------------|\n| Boolean | `integer({ mode: 'boolean' })` | `boolean()` |\n| JSON | `text({ mode: 'json' })` | `jsonb()` or `json()` |\n| Auto-increment | `integer().primaryKey()` | `serial()` |\n| UUID | `text()` | `uuid()` |\n\n## Next Steps\n\n- [Configure the firewall](/compiler/definitions/firewall) for data isolation\n- [Set up access control](/compiler/definitions/access) for CRUD operations\n- [Define guards](/compiler/definitions/guards) for field modification rules\n- [Add custom actions](/compiler/definitions/actions) for business logic"
182
+ "content": "Quickback uses [Drizzle ORM](https://orm.drizzle.team/) to define your database schema. With `defineTable`, you combine your schema definition and security configuration in a single file.\n\n## Defining Tables with defineTable\n\nEach table gets its own file with schema and config together. Use the Drizzle dialect that matches your target database:\n\n| Target Database | Import From | Table Function |\n|-----------------|-------------|----------------|\n| Cloudflare D1, Turso, SQLite | `drizzle-orm/sqlite-core` | `sqliteTable` |\n| Supabase, Neon, PostgreSQL | `drizzle-orm/pg-core` | `pgTable` |\n| PlanetScale, MySQL | `drizzle-orm/mysql-core` | `mysqlTable` |\n\n```typescript\n// quickback/features/jobs/jobs.ts\n// For D1/SQLite targets:\n\nexport const jobs = sqliteTable('jobs', {\n id: text('id').primaryKey(),\n organizationId: text('organization_id').notNull(),\n title: text('title').notNull(),\n department: text('department').notNull(),\n status: text('status').notNull(), // \"draft\" | \"open\" | \"closed\"\n salaryMin: integer('salary_min'),\n salaryMax: integer('salary_max'),\n});\n\nexport default defineTable(jobs, {\n firewall: { organization: {} },\n guards: {\n createable: [\"title\", \"department\", \"status\", \"salaryMin\", \"salaryMax\"],\n updatable: [\"title\", \"department\", \"status\"],\n },\n crud: {\n list: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\", \"interviewer\"] } },\n get: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\", \"interviewer\"] } },\n create: { access: { roles: [\"owner\", \"hiring-manager\"] } },\n update: { access: { roles: [\"owner\", \"hiring-manager\"] } },\n delete: { access: { roles: [\"owner\", \"hiring-manager\"] } },\n },\n});\n\nexport type Job = typeof jobs.$inferSelect;\n```\n\n## Column Types\n\nDrizzle supports all standard SQL column types:\n\n| Type | Drizzle Function | Example |\n|------|------------------|---------|\n| String | `text()`, `varchar()` | `text('name')` |\n| Integer | `integer()`, `bigint()` | `integer('count')` |\n| Boolean | `boolean()` | `boolean('is_active')` |\n| Timestamp | `timestamp()` | `timestamp('created_at')` |\n| JSON | `json()`, `jsonb()` | `jsonb('metadata')` |\n| UUID | `uuid()` | `uuid('id')` |\n| Decimal | `decimal()`, `numeric()` | `decimal('price', { precision: 10, scale: 2 })` |\n\n## Column Modifiers\n\n```typescript\n// Required field\nname: text('name').notNull()\n\n// Default value\nisActive: boolean('is_active').default(true)\n\n// Primary key\nid: text('id').primaryKey()\n\n// Unique constraint\nemail: text('email').unique()\n\n// Default to current timestamp\ncreatedAt: timestamp('created_at').defaultNow()\n```\n\n## File Organization\n\nOrganize your tables by feature. Each feature directory contains table files:\n\n```\nquickback/\n├── quickback.config.ts\n└── features/\n ├── jobs/\n │ ├── jobs.ts # Main table + config\n │ ├── applications.ts # Related table + config\n │ └── actions.ts # Custom actions\n ├── candidates/\n │ ├── candidates.ts # Table + config\n │ └── candidate-notes.ts # Related table\n └── organizations/\n └── organizations.ts\n```\n\n**Key points:**\n- Tables with `export default defineTable(...)` get CRUD routes generated\n- Tables without a default export are internal (no routes, used for joins/relations)\n- Route paths are derived from filenames: `applications.ts` → `/api/v1/applications`\n\n### defineTable vs defineResource\n\n- **`defineTable`** — The standard function for defining tables with CRUD routes\n- **`defineResource`** — Deprecated. Still works but emits a warning during compilation. Use `defineTable` instead.\n\n```typescript\n// Preferred:\nexport default defineTable(jobs, { /* config */ });\n```\n\n## 1 Resource = 1 Security Boundary\n\nEach `defineTable()` call defines a complete, self-contained security boundary. The security config you write — firewall, access, guards, and masking — is compiled into a single resource file that wraps all CRUD routes for that table.\n\nThis is a deliberate design choice. Mixing two resources with different security rules in one configuration would create ambiguity about which firewall, access, or masking rules apply to which table. By keeping it 1:1, there's never any question.\n\n| Scenario | What to do |\n|----------|------------|\n| Table needs its own API routes + security | Own file with `defineTable()` |\n| Table is internal/supporting (no direct API) | Extra `.ts` file in the parent feature directory, no `defineTable()` |\n\nA supporting table without `defineTable()` is useful when it's accessed internally — by action handlers, joins, or background jobs — but should never be directly exposed as its own API endpoint.\n\n## Internal Tables (No Routes)\n\nFor junction tables or internal data structures that shouldn't have API routes, simply omit the `defineTable` export:\n\n```typescript\n// quickback/features/jobs/interview-scores.ts\n\n// Internal scoring table - no routes needed\nexport const interviewScores = sqliteTable('interview_scores', {\n applicationId: text('application_id').notNull(),\n interviewerId: text('interviewer_id').notNull(),\n score: integer('score'),\n organizationId: text('organization_id').notNull(), // Always scope junction tables\n});\n\n// No default export = no CRUD routes generated\n```\n\nThese internal tables still participate in the database schema and migrations — they just don't get API routes or security configuration.\n\nThe compiler automatically injects **audit fields** (`createdAt`, `modifiedAt`, `deletedAt`, etc.) and **`organizationId`** into child/junction tables when the parent feature is org-scoped. This ensures cascade soft deletes and scoped queries work correctly without manual column definitions.\n\n## Audit Fields\n\nQuickback automatically adds and manages these audit fields on **all tables** in a feature (including child/junction tables without `defineTable`) - you don't need to define them:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `createdAt` | timestamp | Set when record is created |\n| `createdBy` | text | User ID who created the record |\n| `modifiedAt` | timestamp | Updated on every change |\n| `modifiedBy` | text | User ID who last modified |\n| `deletedAt` | timestamp | Set on soft delete (optional) |\n| `deletedBy` | text | User ID who deleted (optional) |\n\nThe soft delete fields (`deletedAt`, `deletedBy`) are only added if your resource uses soft delete mode.\n\n### Disabling Audit Fields\n\nTo disable automatic audit fields for your project:\n\n```typescript\n// quickback.config.ts\nexport default defineConfig({\n // ...\n compiler: {\n features: {\n auditFields: false, // Disable for entire project\n }\n }\n});\n```\n\n### Protected System Fields\n\nThese fields are always protected and cannot be set by clients, even with `guards: false`:\n\n- `id` (when `generateId` is not `false`)\n- `createdAt`, `createdBy`\n- `modifiedAt`, `modifiedBy`\n- `deletedAt`, `deletedBy`\n\n## Ownership Fields\n\nFor the firewall to work, include the appropriate ownership columns:\n\n```typescript\n// For organization-scoped data (most common)\norganizationId: text('organization_id').notNull()\n\n// For user-owned data (personal data)\nownerId: text('owner_id').notNull()\n\n// For team-scoped data\nteamId: text('team_id').notNull()\n```\n\n## Display Column\n\nWhen a table has foreign key columns (e.g., `departmentId` referencing `departments`), Quickback can automatically resolve the human-readable label in GET and LIST responses. This eliminates the need for frontend lookup calls.\n\n### Auto-Detection\n\nQuickback auto-detects the display column by checking for common column names in this order:\n\n`name` → `title` → `label` → `headline` → `subject` → `code` → `displayName` → `fullName` → `description`\n\nIf your `departments` table has a `name` column, it's automatically used as the display column. No config needed.\n\n### Explicit Override\n\nOverride the auto-detected column with `displayColumn`:\n\n```typescript\nexport default defineTable(interviewStages, {\n displayColumn: 'label', // Use 'label' instead of auto-detected 'name'\n firewall: { organization: {} },\n crud: {\n list: { access: { roles: [\"hiring-manager\", \"recruiter\"] } },\n get: { access: { roles: [\"hiring-manager\", \"recruiter\"] } },\n },\n});\n```\n\n### How Labels Appear in Responses\n\nFor any FK column ending in `Id`, the API adds a `_label` field with the resolved display value:\n\n```json\n{\n \"id\": \"job_abc\",\n \"title\": \"Senior Engineer\",\n \"departmentId\": \"dept_xyz\",\n \"department_label\": \"Engineering\",\n \"locationId\": \"loc_123\",\n \"location_label\": \"San Francisco\"\n}\n```\n\nThe pattern is `{columnWithoutId}_label`. The frontend can find all labels with:\n\n```typescript\nObject.keys(record).filter(k => k.endsWith('_label'))\n```\n\nLabel resolution works within the same feature (tables in the same feature directory). System columns (`organizationId`, `createdBy`, `modifiedBy`) are never resolved.\n\nFor LIST endpoints, labels are batch-resolved efficiently — one query per FK column, not per record.\n\n## References\n\nWhen your FK columns don't match the target table name by convention (e.g., `vendorId` actually points to the `contact` table), declare explicit references so the CMS and schema registry know the correct target:\n\n```typescript\nexport default defineTable(applications, {\n references: {\n candidateId: \"candidate\",\n jobId: \"job\",\n referredById: \"candidate\",\n },\n // ... firewall, guards, crud\n});\n```\n\nEach key is a column name ending in `Id`, and the value is the camelCase table name it references. These mappings flow into the schema registry as `fkTarget` on each column, enabling the CMS to render typeahead/lookup inputs that search the correct table.\n\nConvention-based matching (strip `Id` suffix, look for a matching table) still works for simple cases like `projectId` → `project`. Use `references` only for columns where the convention doesn't match.\n\n## Input Hints\n\nControl how the CMS renders form inputs for specific columns. By default the CMS infers input types from the column's SQL type — `inputHints` lets you override that:\n\n```typescript\nexport default defineTable(jobs, {\n inputHints: {\n status: \"select\",\n department: \"select\",\n salaryMin: \"currency\",\n salaryMax: \"currency\",\n description: \"textarea\",\n },\n // ... firewall, guards, crud\n});\n```\n\n### Available Hint Values\n\n| Hint | Renders As |\n|------|-----------|\n| `select` | Dropdown select (single value) |\n| `multi-select` | Multi-value select |\n| `radio` | Radio button group |\n| `checkbox` | Checkbox toggle |\n| `textarea` | Multi-line text input |\n| `lookup` | FK typeahead search |\n| `hidden` | Hidden from forms |\n| `color` | Color picker |\n| `date` | Date picker |\n| `datetime` | Date + time picker |\n| `time` | Time picker |\n| `currency` | Currency input with formatting |\n\nInput hints are emitted in the schema registry as `inputHints` on the table metadata, and the CMS reads them to render the appropriate form controls.\n\n## Relations (Optional)\n\nDrizzle supports defining relations for type-safe joins:\n\n```typescript\n\nexport const jobsRelations = relations(jobs, ({ one, many }) => ({\n organization: one(organizations, {\n fields: [jobs.organizationId],\n references: [organizations.id],\n }),\n applications: many(applications),\n}));\n```\n\n## Database Configuration\n\nConfigure database options in your Quickback config:\n\n```typescript\n// quickback.config.ts\nexport default defineConfig({\n name: 'my-app',\n providers: {\n database: defineDatabase('cloudflare-d1', {\n generateId: 'prefixed', // 'uuid' | 'cuid' | 'nanoid' | 'prefixed' | 'serial' | false\n namingConvention: 'snake_case', // 'camelCase' | 'snake_case'\n usePlurals: false, // Auth table names: 'users' vs 'user'\n }),\n },\n compiler: {\n features: {\n auditFields: true, // Auto-manage audit timestamps\n }\n }\n});\n```\n\n## Choosing Your Dialect\n\nUse the Drizzle dialect that matches your database provider:\n\n### SQLite (D1, Turso, better-sqlite3)\n\n```typescript\n\nexport const jobs = sqliteTable('jobs', {\n id: text('id').primaryKey(),\n title: text('title').notNull(),\n metadata: text('metadata', { mode: 'json' }), // JSON stored as text\n isOpen: integer('is_open', { mode: 'boolean' }).default(false),\n organizationId: text('organization_id').notNull(),\n});\n```\n\n### PostgreSQL (Supabase, Neon)\n\n```typescript\n\nexport const jobs = pgTable('jobs', {\n id: serial('id').primaryKey(),\n title: text('title').notNull(),\n metadata: jsonb('metadata'), // Native JSONB\n isOpen: boolean('is_open').default(false),\n organizationId: text('organization_id').notNull(),\n});\n```\n\n### Key Differences\n\n| Feature | SQLite | PostgreSQL |\n|---------|--------|------------|\n| Boolean | `integer({ mode: 'boolean' })` | `boolean()` |\n| JSON | `text({ mode: 'json' })` | `jsonb()` or `json()` |\n| Auto-increment | `integer().primaryKey()` | `serial()` |\n| UUID | `text()` | `uuid()` |\n\n## Next Steps\n\n- [Configure the firewall](/compiler/definitions/firewall) for data isolation\n- [Set up access control](/compiler/definitions/access) for CRUD operations\n- [Define guards](/compiler/definitions/guards) for field modification rules\n- [Add custom actions](/compiler/definitions/actions) for business logic"
183
183
  },
184
184
  "compiler/definitions/validation": {
185
185
  "title": "Validation",
@@ -313,6 +313,10 @@ export const DOCS = {
313
313
  "title": "Auth",
314
314
  "content": "Quickback Stack uses [Better Auth](https://www.better-auth.com/) for authentication, running on Cloudflare Workers with D1 as the session store.\n\n## Overview\n\nBetter Auth provides:\n- Email/password authentication\n- Session management with cookies\n- Multi-tenant organizations with roles\n- Plugin ecosystem for passwordless auth, passkeys, and more\n\n## Configuration\n\nAuth is configured in your `quickback.config.ts`:\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\"),\n auth: defineAuth(\"better-auth\", {\n emailAndPassword: { enabled: true },\n plugins: [\"emailOtp\", \"passkey\", \"magicLink\"],\n }),\n },\n});\n```\n\n## Auth Base Path\n\nAll Better Auth routes are served under:\n\n```\n/auth/v1/*\n```\n\nCommon endpoints:\n- `POST /auth/v1/sign-in/email` — Email/password sign in\n- `POST /auth/v1/sign-up/email` — Create account\n- `GET /auth/v1/get-session` — Get current session\n- `POST /auth/v1/sign-out` — Sign out\n\n## Organization Roles\n\n| Role | Description |\n|------|-------------|\n| `owner` | Full access — can delete the organization and transfer ownership |\n| `admin` | Full access — can manage members and resources, cannot delete the organization |\n| `member` | Standard access — read and limited write, cannot delete or manage members |\n\nThese are Better Auth's built-in organization roles — no configuration needed. The `creatorRole` defaults to `owner`.\n\n> **Tip:** Account UI's role picker uses these exact three roles. Use `[\"owner\", \"admin\", \"member\"]` in your [Access](/compiler/definitions/access) rules so generated projects plug into Better Auth and Account UI seamlessly.\n\nRoles are used throughout the security layers — in [Access](/compiler/definitions/access) rules, [Firewall](/compiler/definitions/firewall) owner checks, and RLS policies.\n\n## Next Steps\n\n- [Plugins](/stack/auth/plugins) — Email OTP, passkeys, magic links, and more\n- [Security](/stack/auth/security) — Cookies, rate limiting, cross-domain auth"
315
315
  },
316
+ "stack/auth/jwt-optimization": {
317
+ "title": "JWT Optimization",
318
+ "content": "Quickback automatically optimizes authenticated API requests using JWT tokens. After the first request (which uses session cookies), subsequent requests skip the database entirely by sending a signed JWT that encodes the full auth context.\n\n## How It Works\n\n### Request Flow\n\n```\nFirst request (no JWT cached):\n Browser → Cookie auth → 2 DB queries → Response + set-auth-token header\n Browser caches JWT in localStorage\n\nSubsequent requests (JWT cached):\n Browser → Bearer JWT → Signature check only → Response (0 DB queries)\n```\n\n### What's in the JWT\n\nThe JWT encodes everything needed for auth middleware:\n\n| Claim | Description |\n|-------|-------------|\n| `sub` | User ID |\n| `orgId` | Active organization ID |\n| `role` | Organization membership role (`owner`, `admin`, `member`) |\n| `userRole` | Global user role (when admin panel is enabled) |\n| `email` | User email |\n| `name` | User display name |\n| `iat` | Issued-at timestamp |\n| `exp` | Expiry (15 minutes from issue) |\n\n### Signing\n\nJWTs are signed with HMAC-SHA256 using your `BETTER_AUTH_SECRET`. No additional configuration is needed — this uses the same secret you already set for Better Auth.\n\n---\n\n## Token Lifecycle\n\n### Automatic Minting\n\nWhen a request arrives without a JWT (or with an expired one), the auth middleware:\n\n1. Authenticates via session cookie (existing Better Auth flow)\n2. Signs a JWT with the full auth context\n3. Returns it in the `set-auth-token` response header\n\nThe Quickback API client (`quickback-client.ts`) automatically captures this header and stores the JWT in `localStorage`.\n\n### Re-minting on Org Switch\n\nWhen a user switches organizations, the JWT must be re-minted because `orgId` and `role` change. The auth UI handles this automatically:\n\n```typescript\n// This happens automatically in the org switcher\nawait authClient.organization.setActive({ organizationSlug: slug });\n\n// Fetch fresh JWT with new org context\nconst res = await fetch('/api/v1/token', {\n method: 'POST',\n credentials: 'include',\n});\nconst { token } = await res.json();\nlocalStorage.setItem('bearer_token', token);\n```\n\n### Invalidation on Role Changes\n\nWhen an admin changes a user's role or removes them from an organization, the server broadcasts an `auth:token-invalidated` event via WebSocket. The client automatically clears the cached JWT, forcing the next request to fall back to session auth and mint a fresh token.\n\n```\nAdmin changes user role\n → databaseHooks fires on member update\n → Broadcasts auth:token-invalidated via realtime\n → Client clears localStorage JWT\n → Next request uses session cookie → gets fresh JWT\n```\n\n### Expiry\n\nJWTs expire after 15 minutes. This is a safety net — in practice, JWTs are re-minted well before expiry through the `set-auth-token` response header on session-fallback requests.\n\nWhen a JWT expires or is invalid:\n1. The middleware silently falls back to session cookie auth\n2. A fresh JWT is minted and returned in the response header\n3. The client stores the new JWT for subsequent requests\n\n---\n\n## Token Endpoint\n\n`POST /api/v1/token` mints a fresh JWT from the current session. This endpoint goes through normal auth middleware, so the user must have a valid session cookie.\n\n**Request:**\n```bash\ncurl -X POST https://your-api.example.com/api/v1/token \\\n -H \"Content-Type: application/json\" \\\n --cookie \"better-auth.session_token=...\"\n```\n\n**Response:**\n```json\n{\n \"token\": \"eyJhbGciOiJIUzI1NiJ9...\"\n}\n```\n\nUse this endpoint after operations that change auth context (like switching organizations) to get a JWT that reflects the new state.\n\n---\n\n## Client Integration\n\n### Quickback API Client\n\nThe `quickback-client.ts` API client handles JWT auth automatically:\n\n- **Sends** the cached JWT as `Authorization: Bearer <token>` on every request\n- **Captures** refreshed JWTs from `set-auth-token` response headers\n- **Clears** the JWT on `401` responses (falls back to session cookie)\n\nNo additional client configuration is needed.\n\n### Custom API Calls\n\nIf you make direct `fetch()` calls to your API (outside the Quickback client), include the JWT:\n\n```typescript\nconst jwt = localStorage.getItem('bearer_token');\n\nconst res = await fetch('https://your-api.example.com/api/v1/things', {\n headers: {\n 'Authorization': `Bearer ${jwt}`,\n 'Content-Type': 'application/json',\n },\n credentials: 'include', // Fallback to session cookie if no JWT\n});\n\n// Capture refreshed token\nconst newToken = res.headers.get('set-auth-token');\nif (newToken) {\n localStorage.setItem('bearer_token', newToken);\n}\n```\n\n### Sign Out\n\nClear the JWT on sign-out to prevent stale tokens:\n\n```typescript\nlocalStorage.removeItem('bearer_token');\nawait authClient.signOut();\n```\n\n---\n\n## Security\n\n### Threat Model\n\n| Threat | Mitigation |\n|--------|------------|\n| Token theft (XSS) | 15-minute expiry limits exposure window. `httpOnly` cookies protect the session. |\n| Token replay | Short expiry + `set-auth-token` rotation on session fallback |\n| Stale permissions | WebSocket `auth:token-invalidated` broadcast on role changes |\n| Token forgery | HMAC-SHA256 signature verified with `BETTER_AUTH_SECRET` |\n| Timing attacks | `crypto.subtle.verify()` provides constant-time comparison |\n\n### Key Points\n\n- JWTs are a **performance optimization**, not a replacement for session auth\n- Session cookies remain the source of truth — JWTs are derived from them\n- If a JWT is lost, stolen, or cleared, the system gracefully falls back to cookie auth\n- No additional secrets or configuration needed — uses your existing `BETTER_AUTH_SECRET`\n- The JWT contains no sensitive data beyond what's already in the auth context\n\n---\n\n## Files Worker\n\nThe files worker (for Cloudflare R2 file uploads) also supports JWT authentication. When a request includes a JWT `Authorization` header, the worker verifies it directly instead of calling back to the auth API — eliminating a network round-trip for file operations.\n\n---\n\n## Related\n\n- [Auth Overview](/docs/stack/auth) — Setup and configuration\n- [Auth Security](/docs/stack/auth/security) — Cookie security, rate limiting, CORS\n- [API Keys](/docs/stack/auth/api-keys) — Programmatic API access"
319
+ },
316
320
  "stack/auth/plugins": {
317
321
  "title": "Auth Plugins",
318
322
  "content": "Quickback ships with a curated set of Better Auth plugins and helper wiring so your auth stack is production-ready by default. Enable these in `quickback.config.ts` under `providers.auth`.\n\n## Available Plugins\n\n### Email OTP (with AWS SES)\n\nQuickback wires the Better Auth Email OTP flow to AWS SES. When `emailOtp` is enabled, the compiler emits the SES plugin and combo-auth email flow (magic link + OTP).\n\n**Enable in config:**\n\n```ts\n\nexport default defineConfig({\n name: \"quickback-api\",\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\", {\n vars: {\n AWS_SES_REGION: \"us-east-2\",\n EMAIL_FROM: \"noreply@yourdomain.com\",\n EMAIL_FROM_NAME: \"Your App | Account Services\",\n APP_NAME: \"Your App\",\n APP_URL: \"https://account.yourdomain.com\",\n BETTER_AUTH_URL: \"https://api.yourdomain.com\",\n },\n }),\n auth: defineAuth(\"better-auth\", {\n emailAndPassword: { enabled: true },\n plugins: [\"emailOtp\"],\n }),\n },\n});\n```\n\n**Endpoints:**\n- `POST /auth/v1/email-otp/send-verification-otp`\n- `POST /auth/v1/email-otp/verify-otp`\n\n**Email readiness check:**\n\nUse this to show UI warnings when SES isn't configured.\n\n- `GET /api/v1/system/email-status`\n- Response: `{ \"emailConfigured\": true|false }`\n\n### Upgrade Anonymous\n\nAdds a first-class endpoint to convert an anonymous user into a full user. This flips `isAnonymous` to `false` and refreshes the session cache immediately.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n emailAndPassword: { enabled: true },\n plugins: [\"anonymous\", \"upgradeAnonymous\"],\n}),\n```\n\n**Endpoint:**\n- `POST /auth/v1/upgrade-anonymous`\n\n**Note:** If you send `Content-Type: application/json`, include a body (an empty `{}` is fine). This avoids request parsing errors in some clients.\n\n### AWS SES Plugin\n\nThis is a Quickback-provided Better Auth plugin used by Email OTP. It handles SES signing and delivery and is auto-included when `emailOtp` is enabled.\n\nRequired vars (use Wrangler secrets for credentials):\n- `AWS_ACCESS_KEY_ID` (secret)\n- `AWS_SECRET_ACCESS_KEY` (secret)\n- `AWS_SES_REGION`\n- `EMAIL_FROM`\n\nOptional vars:\n- `EMAIL_FROM_NAME`\n- `EMAIL_REPLY_TO` - Reply-to address for emails (defaults to `EMAIL_FROM`)\n- `APP_NAME`\n- `APP_URL`\n- `BETTER_AUTH_URL`\n\n### Magic Links\n\nMagic links provide passwordless authentication by sending a unique login link to the user's email. When clicked, the link authenticates the user without requiring a password.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n plugins: [\"magicLink\"],\n}),\n```\n\n**Endpoints:**\n- `POST /auth/v1/magic-link/send` - Send magic link email\n- `GET /auth/v1/magic-link/verify` - Verify magic link token\n\n**Email customization:**\n\nMagic link emails use the same AWS SES configuration as Email OTP. Customize the email template via environment variables:\n\n- `APP_NAME` - Application name in email subject\n- `APP_URL` - Base URL for magic link redirect\n- `EMAIL_FROM` - Sender email address\n- `EMAIL_FROM_NAME` - Sender display name\n\n**Frontend integration:**\n\n```ts\n// Request magic link\nawait fetch('/auth/v1/magic-link/send', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: 'user@yourdomain.com' })\n});\n\n// User clicks link in email, which redirects to your app\n// The token is verified automatically via the callback URL\n```\n\n### Passkeys\n\nPasskeys provide passwordless authentication using WebAuthn/FIDO2. Users authenticate with biometrics (fingerprint, face) or device PIN instead of passwords.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n plugins: [\"passkey\"],\n}),\n```\n\n**Required environment variable:**\n- `ACCOUNT_URL` - Your account/frontend URL (used as the relying party origin for WebAuthn)\n\n**Endpoints:**\n- `POST /auth/v1/passkey/register/options` - Get registration challenge\n- `POST /auth/v1/passkey/register/verify` - Complete registration\n- `POST /auth/v1/passkey/authenticate/options` - Get authentication challenge\n- `POST /auth/v1/passkey/authenticate/verify` - Complete authentication\n- `GET /auth/v1/passkey/list-user-passkeys` - List user's registered passkeys\n- `POST /auth/v1/passkey/delete-passkey` - Delete a registered passkey\n\n**Browser support:**\n\nPasskeys are supported in all modern browsers:\n- Chrome 67+\n- Safari 14+\n- Firefox 60+\n- Edge 79+\n\n**Registration flow:**\n\n```ts\n// 1. Get registration options\nconst optionsRes = await fetch('/auth/v1/passkey/register/options', {\n method: 'POST',\n credentials: 'include'\n});\nconst options = await optionsRes.json();\n\n// 2. Create credential using WebAuthn API\nconst credential = await navigator.credentials.create({\n publicKey: options\n});\n\n// 3. Send credential to server\nawait fetch('/auth/v1/passkey/register/verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(credential)\n});\n```\n\n**Authentication flow:**\n\n```ts\n// 1. Get authentication options\nconst optionsRes = await fetch('/auth/v1/passkey/authenticate/options', {\n method: 'POST'\n});\nconst options = await optionsRes.json();\n\n// 2. Get credential using WebAuthn API\nconst credential = await navigator.credentials.get({\n publicKey: options\n});\n\n// 3. Verify credential\nawait fetch('/auth/v1/passkey/authenticate/verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(credential)\n});\n```\n\n## Where This Runs\n\nAll Better Auth plugin routes are served under your auth base path:\n\n```\n/auth/v1/*\n```"
@@ -359,15 +363,15 @@ export const DOCS = {
359
363
  },
360
364
  "stack/realtime/durable-objects": {
361
365
  "title": "Realtime",
362
- "content": "Quickback can generate realtime notification helpers for broadcasting changes to connected clients. This enables live updates via WebSocket using Cloudflare Durable Objects.\n\n## Enabling Realtime\n\nAdd `realtime` configuration to individual table definitions:\n\n```typescript\n// features/applications/applications.ts\n\nexport const applications = sqliteTable(\"applications\", {\n id: text(\"id\").primaryKey(),\n candidateId: text(\"candidate_id\").notNull(),\n jobId: text(\"job_id\").notNull(),\n stage: text(\"stage\").notNull(),\n organizationId: text(\"organization_id\").notNull(),\n});\n\nexport default defineTable(applications, {\n firewall: { organization: {} },\n realtime: {\n enabled: true, // Enable realtime for this table\n onInsert: true, // Broadcast on INSERT (default: true)\n onUpdate: true, // Broadcast on UPDATE (default: true)\n onDelete: true, // Broadcast on DELETE (default: true)\n requiredRoles: [\"recruiter\", \"hiring-manager\"], // Who receives broadcasts\n fields: [\"id\", \"candidateId\", \"stage\"], // Fields to include (optional)\n },\n // ... guards, crud\n});\n```\n\nWhen any table has realtime enabled, the compiler generates `src/lib/realtime.ts` with helper functions for sending notifications.\n\n## Generated Helper\n\nThe `createRealtime()` factory provides convenient methods for both Postgres Changes (CRUD events) and custom broadcasts:\n\n```typescript\n\nexport const execute: ActionExecutor = async ({ db, ctx, input }) => {\n const realtime = createRealtime(ctx.env);\n\n // After creating a record\n await realtime.insert('applications', newApplication, ctx.activeOrgId!);\n\n // After updating a record\n await realtime.update('applications', newApplication, oldApplication, ctx.activeOrgId!);\n\n // After deleting a record\n await realtime.delete('applications', { id: applicationId }, ctx.activeOrgId!);\n\n return { success: true };\n};\n```\n\n## Event Format\n\nQuickback broadcasts events in a structured JSON format for easy client-side handling.\n\n### Insert Event\n\n```typescript\nawait realtime.insert('applications', {\n id: 'app_123',\n candidateId: 'cnd_456',\n stage: 'applied'\n}, ctx.activeOrgId!);\n```\n\nBroadcasts:\n```json\n{\n \"type\": \"postgres_changes\",\n \"table\": \"applications\",\n \"eventType\": \"INSERT\",\n \"schema\": \"public\",\n \"new\": {\n \"id\": \"app_123\",\n \"candidateId\": \"cnd_456\",\n \"stage\": \"applied\"\n },\n \"old\": null\n}\n```\n\n### Update Event\n\n```typescript\nawait realtime.update('applications',\n { id: 'app_123', stage: 'interview' }, // new\n { id: 'app_123', stage: 'screening' }, // old\n ctx.activeOrgId!\n);\n```\n\nBroadcasts:\n```json\n{\n \"type\": \"postgres_changes\",\n \"table\": \"applications\",\n \"eventType\": \"UPDATE\",\n \"schema\": \"public\",\n \"new\": { \"id\": \"app_123\", \"stage\": \"interview\" },\n \"old\": { \"id\": \"app_123\", \"stage\": \"screening\" }\n}\n```\n\n### Delete Event\n\n```typescript\nawait realtime.delete('applications',\n { id: 'app_123' },\n ctx.activeOrgId!\n);\n```\n\nBroadcasts:\n```json\n{\n \"type\": \"postgres_changes\",\n \"table\": \"applications\",\n \"eventType\": \"DELETE\",\n \"schema\": \"public\",\n \"new\": null,\n \"old\": { \"id\": \"app_123\" }\n}\n```\n\n## Custom Broadcasts\n\nFor arbitrary events that don't map to CRUD operations:\n\n```typescript\nawait realtime.broadcast('screening-complete', {\n applicationId: 'app_123',\n candidateId: 'cnd_456',\n stage: 'interview'\n}, ctx.activeOrgId!);\n```\n\nBroadcasts:\n```json\n{\n \"type\": \"broadcast\",\n \"event\": \"screening-complete\",\n \"payload\": {\n \"applicationId\": \"app_123\",\n \"candidateId\": \"cnd_456\",\n \"stage\": \"interview\"\n }\n}\n```\n\n## User-Specific Broadcasts\n\nTarget a specific user instead of the entire organization:\n\n```typescript\n// Only this user receives the notification\nawait realtime.insert('notifications', newNotification, ctx.activeOrgId!, {\n userId: ctx.userId\n});\n\n// Notify a hiring manager of a new application\nawait realtime.broadcast('application-received', {\n applicationId: 'app_123',\n jobId: 'job_789'\n}, ctx.activeOrgId!, {\n userId: hiringManagerId\n});\n```\n\n## Role-Based Filtering\n\nLimit which roles receive a broadcast using `targetRoles`:\n\n```typescript\n// Only hiring managers and recruiters receive this broadcast\nawait realtime.insert('applications', application, ctx.activeOrgId!, {\n targetRoles: ['hiring-manager', 'recruiter']\n});\n\n// Interviewers only see applications assigned to them\nawait realtime.update('applications', newRecord, oldRecord, ctx.activeOrgId!, {\n targetRoles: ['hiring-manager']\n});\n```\n\nIf `targetRoles` is not specified, all authenticated users in the organization receive the broadcast.\n\n## Per-Role Field Masking\n\nApply different field masking based on the subscriber's role using `maskingConfig`:\n\n```typescript\nawait realtime.insert('candidates', newCandidate, ctx.activeOrgId!, {\n maskingConfig: {\n phone: { type: 'phone', show: { roles: ['owner', 'hiring-manager', 'recruiter'] } },\n email: { type: 'email', show: { roles: ['owner', 'hiring-manager', 'recruiter'] } },\n resumeUrl: { type: 'redact', show: { roles: ['owner', 'hiring-manager', 'recruiter'] } },\n },\n});\n```\n\n**How masking works:**\n- Each subscriber receives a payload masked according to their role\n- Roles in the `show.roles` array see unmasked values\n- All other roles see the masked version\n- Masking is pre-computed per-role (O(roles) not O(subscribers))\n\n**Available mask types:**\n| Type | Example Output |\n|------|----------------|\n| `email` | `j***@y*********.com` |\n| `phone` | `******4567` |\n| `ssn` | `*****6789` |\n| `creditCard` | `**** **** **** 1111` |\n| `name` | `J***` |\n| `redact` | `[REDACTED]` |\n\n### Owner-Based Masking\n\nShow unmasked data to the record owner:\n\n```typescript\nmaskingConfig: {\n phone: {\n type: 'phone',\n show: { roles: ['hiring-manager'], or: 'owner' } // Hiring manager OR owner sees unmasked\n },\n}\n```\n\n## Client-Side Subscription\n\n### WebSocket Authentication\n\nThe Broadcaster supports two authentication methods:\n\n**Session Token (Browser/App):**\n```typescript\nconst ws = new WebSocket('wss://api.yourdomain.com/realtime/v1/websocket');\n\nws.onopen = () => {\n ws.send(JSON.stringify({\n type: 'auth',\n token: sessionToken, // JWT from Better Auth session\n organizationId: activeOrgId,\n }));\n};\n\nws.onmessage = (e) => {\n const msg = JSON.parse(e.data);\n if (msg.type === 'auth_success') {\n console.log('Authenticated:', {\n role: msg.role,\n roles: msg.roles,\n authMethod: msg.authMethod, // 'session'\n });\n }\n};\n```\n\n**API Key (Server/CLI):**\n```typescript\nconst ws = new WebSocket('wss://api.yourdomain.com/realtime/v1/websocket');\n\nws.onopen = () => {\n ws.send(JSON.stringify({\n type: 'auth',\n token: apiKey, // API key for machine-to-machine auth\n organizationId: orgId,\n }));\n};\n\n// authMethod will be 'api_key' on success\n```\n\n### Handling Messages\n\n```typescript\nws.onmessage = (event) => {\n const msg = JSON.parse(event.data);\n\n // Handle CRUD events\n if (msg.type === 'postgres_changes') {\n const { table, eventType, new: newRecord, old: oldRecord } = msg;\n\n if (eventType === 'INSERT') {\n addRecord(table, newRecord);\n } else if (eventType === 'UPDATE') {\n updateRecord(table, newRecord);\n } else if (eventType === 'DELETE') {\n removeRecord(table, oldRecord.id);\n }\n }\n\n // Handle custom broadcasts\n if (msg.type === 'broadcast') {\n const { event, payload } = msg;\n\n if (event === 'screening-complete') {\n refreshApplication(payload.applicationId);\n } else if (event === 'interview-scheduled') {\n showInterviewNotification(payload.applicationId);\n }\n }\n};\n```\n\n## Required Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| `REALTIME_URL` | URL of the broadcast/realtime worker |\n| `ACCESS_TOKEN` | Internal service-to-service auth token |\n\nExample configuration:\n\n```toml\n# wrangler.toml\n[vars]\nREALTIME_URL = \"https://your-realtime-worker.workers.dev\"\nACCESS_TOKEN = \"your-internal-secret-token\"\n```\n\n## Architecture\n\n```\n┌─────────────┐ POST /broadcast ┌──────────────────┐\n│ API Worker │ ───────────────────────► │ Realtime Worker │\n│ (Quickback)│ │ (Durable Object)│\n└─────────────┘ └────────┬─────────┘\n │\n WebSocket│\n │\n ┌────────▼─────────┐\n │ Browser Clients │\n │ (WebSocket) │\n └──────────────────┘\n```\n\n1. **API Worker** - Your Quickback-generated API. Calls `realtime.insert()` etc. after CRUD operations.\n2. **Realtime Worker** - Separate worker with Durable Object for managing WebSocket connections.\n3. **Browser Clients** - Connect via WebSocket, subscribe to channels.\n\n## Best Practices\n\n### Broadcast After Commit\n\nAlways broadcast after the database operation succeeds:\n\n```typescript\n// Good - broadcast after successful insert\nconst [newApp] = await db.insert(applications).values(data).returning();\nawait realtime.insert('applications', newApp, ctx.activeOrgId!);\n\n// Bad - don't broadcast before confirming success\nawait realtime.insert('applications', data, ctx.activeOrgId!);\nawait db.insert(applications).values(data); // Could fail!\n```\n\n### Minimal Payloads\n\nOnly include necessary data in broadcasts:\n\n```typescript\n// Good - minimal payload\nawait realtime.update('applications',\n { id: record.id, stage: 'interview' },\n { id: record.id, stage: 'screening' },\n ctx.activeOrgId!\n);\n\n// Avoid - sending entire record with large content\nawait realtime.update('applications', fullRecord, oldRecord, ctx.activeOrgId!);\n```\n\n### Use Broadcasts for Complex Events\n\nFor events that don't map cleanly to CRUD:\n\n```typescript\n// Hiring pipeline stage completed\nawait realtime.broadcast('pipeline-stage-complete', {\n applicationId: application.id,\n stages: ['applied', 'screening', 'interview'],\n currentStage: 'offer',\n candidateId: application.candidateId\n}, ctx.activeOrgId!);\n```\n\n## Custom Event Namespaces\n\nFor complex applications with many custom events, you can define typed event namespaces using `defineRealtime`. This generates strongly-typed helper methods for your custom events.\n\n### Defining Event Namespaces\n\nCreate a file in `services/realtime/`:\n\n```typescript\n// services/realtime/screening.ts\n\nexport default defineRealtime({\n name: 'screening',\n events: ['started', 'progress', 'completed', 'failed'],\n description: 'Candidate screening pipeline events',\n});\n```\n\n### Generated Helper Methods\n\nAfter compilation, the `createRealtime()` helper includes your custom namespace:\n\n```typescript\n\nexport const execute: ActionExecutor = async ({ ctx, input }) => {\n const realtime = createRealtime(ctx.env);\n\n // Type-safe custom event methods\n await realtime.screening.started({\n applicationId: input.applicationId,\n }, ctx.activeOrgId!);\n\n // Progress updates\n await realtime.screening.progress({\n applicationId: input.applicationId,\n percent: 50,\n step: 'background-check',\n }, ctx.activeOrgId!);\n\n // Completion\n await realtime.screening.completed({\n applicationId: input.applicationId,\n result: 'passed',\n duration: 1234,\n }, ctx.activeOrgId!);\n\n return { success: true };\n};\n```\n\n### Event Format\n\nCustom namespace events use the `broadcast` type with namespaced event names:\n\n```json\n{\n \"type\": \"broadcast\",\n \"event\": \"screening:started\",\n \"payload\": {\n \"applicationId\": \"app_123\"\n }\n}\n```\n\n### Multiple Namespaces\n\nDefine multiple namespaces for different parts of your application:\n\n```typescript\n// services/realtime/notifications.ts\nexport default defineRealtime({\n name: 'notifications',\n events: ['interview-reminder', 'offer-update', 'application-received'],\n description: 'Recruitment notification events',\n});\n\n// services/realtime/presence.ts\nexport default defineRealtime({\n name: 'presence',\n events: ['joined', 'left', 'reviewing', 'idle'],\n description: 'Who is reviewing which candidate',\n});\n```\n\nUsage:\n```typescript\nconst realtime = createRealtime(ctx.env);\n\n// Notification events\nawait realtime.notifications['interview-reminder']({ ... }, ctx.activeOrgId!);\n\n// Presence events — who's reviewing which candidate\nawait realtime.presence.reviewing({ userId: ctx.userId, candidateId: 'cnd_456' }, ctx.activeOrgId!);\n```\n\n### Namespace vs Generic Broadcast\n\n| Use Case | Approach |\n|----------|----------|\n| One-off custom event | `realtime.broadcast('event-name', payload, orgId)` |\n| Repeated event patterns | `defineRealtime` namespace |\n| Type-safe events | `defineRealtime` namespace |\n| Event discovery | `defineRealtime` (appears in generated types) |"
366
+ "content": "Quickback can generate realtime notification helpers for broadcasting changes to connected clients. This enables live updates via WebSocket using Cloudflare Durable Objects.\n\n## Enabling Realtime\n\nAdd `realtime` configuration to individual table definitions:\n\n```typescript\n// features/applications/applications.ts\n\nexport const applications = sqliteTable(\"applications\", {\n id: text(\"id\").primaryKey(),\n candidateId: text(\"candidate_id\").notNull(),\n jobId: text(\"job_id\").notNull(),\n stage: text(\"stage\").notNull(),\n organizationId: text(\"organization_id\").notNull(),\n});\n\nexport default defineTable(applications, {\n firewall: { organization: {} },\n realtime: {\n enabled: true, // Enable realtime for this table\n onInsert: true, // Broadcast on INSERT (default: true)\n onUpdate: true, // Broadcast on UPDATE (default: true)\n onDelete: true, // Broadcast on DELETE (default: true)\n requiredRoles: [\"recruiter\", \"hiring-manager\"], // Who receives broadcasts\n fields: [\"id\", \"candidateId\", \"stage\"], // Fields to include (optional)\n },\n // ... guards, crud\n});\n```\n\nWhen any table has realtime enabled, the compiler generates `src/lib/realtime.ts` with helper functions for sending notifications.\n\n## Generated Helper\n\nThe `createRealtime()` factory provides convenient methods for both Postgres Changes (CRUD events) and custom broadcasts:\n\n```typescript\n\nexport const execute: ActionExecutor = async ({ db, ctx, input }) => {\n const realtime = createRealtime(ctx.env);\n\n // After creating a record\n await realtime.insert('applications', newApplication, ctx.activeOrgId!);\n\n // After updating a record\n await realtime.update('applications', newApplication, oldApplication, ctx.activeOrgId!);\n\n // After deleting a record\n await realtime.delete('applications', { id: applicationId }, ctx.activeOrgId!);\n\n return { success: true };\n};\n```\n\n## Event Format\n\nQuickback broadcasts events in a structured JSON format for easy client-side handling.\n\n### Insert Event\n\n```typescript\nawait realtime.insert('applications', {\n id: 'app_123',\n candidateId: 'cnd_456',\n stage: 'applied'\n}, ctx.activeOrgId!);\n```\n\nBroadcasts:\n```json\n{\n \"type\": \"postgres_changes\",\n \"table\": \"applications\",\n \"eventType\": \"INSERT\",\n \"schema\": \"public\",\n \"new\": {\n \"id\": \"app_123\",\n \"candidateId\": \"cnd_456\",\n \"stage\": \"applied\"\n },\n \"old\": null\n}\n```\n\n### Update Event\n\n```typescript\nawait realtime.update('applications',\n { id: 'app_123', stage: 'interview' }, // new\n { id: 'app_123', stage: 'screening' }, // old\n ctx.activeOrgId!\n);\n```\n\nBroadcasts:\n```json\n{\n \"type\": \"postgres_changes\",\n \"table\": \"applications\",\n \"eventType\": \"UPDATE\",\n \"schema\": \"public\",\n \"new\": { \"id\": \"app_123\", \"stage\": \"interview\" },\n \"old\": { \"id\": \"app_123\", \"stage\": \"screening\" }\n}\n```\n\n### Delete Event\n\n```typescript\nawait realtime.delete('applications',\n { id: 'app_123' },\n ctx.activeOrgId!\n);\n```\n\nBroadcasts:\n```json\n{\n \"type\": \"postgres_changes\",\n \"table\": \"applications\",\n \"eventType\": \"DELETE\",\n \"schema\": \"public\",\n \"new\": null,\n \"old\": { \"id\": \"app_123\" }\n}\n```\n\n## Custom Broadcasts\n\nFor arbitrary events that don't map to CRUD operations:\n\n```typescript\nawait realtime.broadcast('screening-complete', {\n applicationId: 'app_123',\n candidateId: 'cnd_456',\n stage: 'interview'\n}, ctx.activeOrgId!);\n```\n\nBroadcasts:\n```json\n{\n \"type\": \"broadcast\",\n \"event\": \"screening-complete\",\n \"payload\": {\n \"applicationId\": \"app_123\",\n \"candidateId\": \"cnd_456\",\n \"stage\": \"interview\"\n }\n}\n```\n\n## User-Specific Broadcasts\n\nTarget a specific user instead of the entire organization:\n\n```typescript\n// Only this user receives the notification\nawait realtime.insert('notifications', newNotification, ctx.activeOrgId!, {\n userId: ctx.userId\n});\n\n// Notify a hiring manager of a new application\nawait realtime.broadcast('application-received', {\n applicationId: 'app_123',\n jobId: 'job_789'\n}, ctx.activeOrgId!, {\n userId: hiringManagerId\n});\n```\n\n## Role-Based Filtering\n\nLimit which roles receive a broadcast using `targetRoles`:\n\n```typescript\n// Only hiring managers and recruiters receive this broadcast\nawait realtime.insert('applications', application, ctx.activeOrgId!, {\n targetRoles: ['hiring-manager', 'recruiter']\n});\n\n// Interviewers only see applications assigned to them\nawait realtime.update('applications', newRecord, oldRecord, ctx.activeOrgId!, {\n targetRoles: ['hiring-manager']\n});\n```\n\nIf `targetRoles` is not specified, all authenticated users in the organization receive the broadcast.\n\n## Per-Role Field Masking\n\nApply different field masking based on the subscriber's role using `maskingConfig`:\n\n```typescript\nawait realtime.insert('candidates', newCandidate, ctx.activeOrgId!, {\n maskingConfig: {\n phone: { type: 'phone', show: { roles: ['owner', 'hiring-manager', 'recruiter'] } },\n email: { type: 'email', show: { roles: ['owner', 'hiring-manager', 'recruiter'] } },\n resumeUrl: { type: 'redact', show: { roles: ['owner', 'hiring-manager', 'recruiter'] } },\n },\n});\n```\n\n**How masking works:**\n- Each subscriber receives a payload masked according to their role\n- Roles in the `show.roles` array see unmasked values\n- All other roles see the masked version\n- Masking is pre-computed per-role (O(roles) not O(subscribers))\n\n**Available mask types:**\n| Type | Example Output |\n|------|----------------|\n| `email` | `j***@y*********.com` |\n| `phone` | `******4567` |\n| `ssn` | `*****6789` |\n| `creditCard` | `**** **** **** 1111` |\n| `name` | `J***` |\n| `redact` | `[REDACTED]` |\n\n### Owner-Based Masking\n\nShow unmasked data to the record owner:\n\n```typescript\nmaskingConfig: {\n phone: {\n type: 'phone',\n show: { roles: ['hiring-manager'], or: 'owner' } // Hiring manager OR owner sees unmasked\n },\n}\n```\n\n## Client-Side Subscription\n\n### WebSocket Authentication\n\nQuickback uses **ticket-based authentication** for WebSocket connections. The client first obtains a short-lived ticket from the main API, then connects with it as a URL parameter. The Broadcaster verifies the ticket cryptographically at upgrade time — no HTTP round-trip needed.\n\n```typescript\n// 1. Get a ws-ticket from your API (requires session auth)\nconst response = await fetch('/realtime/v1/ws-ticket', {\n method: 'POST',\n headers: { Authorization: `Bearer ${sessionToken}` },\n});\nconst { wsTicket } = await response.json();\n\n// 2. Connect with ticket as URL param\nconst ws = new WebSocket(\n `wss://api.yourdomain.com/realtime/v1/websocket?ws_ticket=${wsTicket}&organizationId=${activeOrgId}`\n);\n\nws.onopen = () => {\n console.log('Connected and authenticated!');\n // No auth message needed — pre-authenticated at upgrade\n};\n```\n\nTickets expire after 60 seconds, so fetch a fresh one before each connection or reconnection.\n\n### Handling Messages\n\n```typescript\nws.onmessage = (event) => {\n const msg = JSON.parse(event.data);\n\n // Handle CRUD events\n if (msg.type === 'postgres_changes') {\n const { table, eventType, new: newRecord, old: oldRecord } = msg;\n\n if (eventType === 'INSERT') {\n addRecord(table, newRecord);\n } else if (eventType === 'UPDATE') {\n updateRecord(table, newRecord);\n } else if (eventType === 'DELETE') {\n removeRecord(table, oldRecord.id);\n }\n }\n\n // Handle custom broadcasts\n if (msg.type === 'broadcast') {\n const { event, payload } = msg;\n\n if (event === 'screening-complete') {\n refreshApplication(payload.applicationId);\n } else if (event === 'interview-scheduled') {\n showInterviewNotification(payload.applicationId);\n }\n }\n};\n```\n\n## Required Environment Variables\n\n| Variable | Where | Description |\n|----------|-------|-------------|\n| `REALTIME_URL` | Main API | URL of the broadcast/realtime worker |\n| `ACCESS_TOKEN` | Both | Shared secret for broadcast API auth and ws-ticket signing |\n\nThe `ACCESS_TOKEN` serves double duty: it authenticates internal broadcast API calls from the main worker, and it signs/verifies WebSocket tickets. Set it as a secret on the broadcast worker:\n\n```bash\ncd cloudflare-workers/broadcast && wrangler secret put ACCESS_TOKEN\n```\n\n## Architecture\n\n```\n 1. POST /ws-ticket\n┌──────────────┐ ◄──────────────────────────────── ┌─────────────────┐\n│ API Worker │ ────────────────────────────────► │ Browser Client │\n│ (Quickback) │ { wsTicket, expiresIn: 60 } │ │\n└──────┬───────┘ └───────┬─────────┘\n │ │\n │ POST /broadcast 2. WS ?ws_ticket=...│\n │ │\n ▼ ▼\n┌──────────────────┐ Verified at upgrade\n│ Realtime Worker │ ◄──────── WebSocket ────── (no HTTP call)\n│ (Durable Object) │\n└──────────────────┘\n```\n\n1. **Client gets a ticket** — `POST /realtime/v1/ws-ticket` on the main API (session-authenticated). Returns a 60-second HMAC-signed ticket.\n2. **Client connects** — Opens WebSocket with `?ws_ticket=<ticket>`. The Durable Object verifies cryptographically at upgrade.\n3. **API broadcasts** — After CRUD ops, the main API calls `POST /broadcast` on the realtime worker, which delivers to matching subscribers.\n\n## Best Practices\n\n### Broadcast After Commit\n\nAlways broadcast after the database operation succeeds:\n\n```typescript\n// Good - broadcast after successful insert\nconst [newApp] = await db.insert(applications).values(data).returning();\nawait realtime.insert('applications', newApp, ctx.activeOrgId!);\n\n// Bad - don't broadcast before confirming success\nawait realtime.insert('applications', data, ctx.activeOrgId!);\nawait db.insert(applications).values(data); // Could fail!\n```\n\n### Minimal Payloads\n\nOnly include necessary data in broadcasts:\n\n```typescript\n// Good - minimal payload\nawait realtime.update('applications',\n { id: record.id, stage: 'interview' },\n { id: record.id, stage: 'screening' },\n ctx.activeOrgId!\n);\n\n// Avoid - sending entire record with large content\nawait realtime.update('applications', fullRecord, oldRecord, ctx.activeOrgId!);\n```\n\n### Use Broadcasts for Complex Events\n\nFor events that don't map cleanly to CRUD:\n\n```typescript\n// Hiring pipeline stage completed\nawait realtime.broadcast('pipeline-stage-complete', {\n applicationId: application.id,\n stages: ['applied', 'screening', 'interview'],\n currentStage: 'offer',\n candidateId: application.candidateId\n}, ctx.activeOrgId!);\n```\n\n## Custom Event Namespaces\n\nFor complex applications with many custom events, you can define typed event namespaces using `defineRealtime`. This generates strongly-typed helper methods for your custom events.\n\n### Defining Event Namespaces\n\nCreate a file in `services/realtime/`:\n\n```typescript\n// services/realtime/screening.ts\n\nexport default defineRealtime({\n name: 'screening',\n events: ['started', 'progress', 'completed', 'failed'],\n description: 'Candidate screening pipeline events',\n});\n```\n\n### Generated Helper Methods\n\nAfter compilation, the `createRealtime()` helper includes your custom namespace:\n\n```typescript\n\nexport const execute: ActionExecutor = async ({ ctx, input }) => {\n const realtime = createRealtime(ctx.env);\n\n // Type-safe custom event methods\n await realtime.screening.started({\n applicationId: input.applicationId,\n }, ctx.activeOrgId!);\n\n // Progress updates\n await realtime.screening.progress({\n applicationId: input.applicationId,\n percent: 50,\n step: 'background-check',\n }, ctx.activeOrgId!);\n\n // Completion\n await realtime.screening.completed({\n applicationId: input.applicationId,\n result: 'passed',\n duration: 1234,\n }, ctx.activeOrgId!);\n\n return { success: true };\n};\n```\n\n### Event Format\n\nCustom namespace events use the `broadcast` type with namespaced event names:\n\n```json\n{\n \"type\": \"broadcast\",\n \"event\": \"screening:started\",\n \"payload\": {\n \"applicationId\": \"app_123\"\n }\n}\n```\n\n### Multiple Namespaces\n\nDefine multiple namespaces for different parts of your application:\n\n```typescript\n// services/realtime/notifications.ts\nexport default defineRealtime({\n name: 'notifications',\n events: ['interview-reminder', 'offer-update', 'application-received'],\n description: 'Recruitment notification events',\n});\n\n// services/realtime/presence.ts\nexport default defineRealtime({\n name: 'presence',\n events: ['joined', 'left', 'reviewing', 'idle'],\n description: 'Who is reviewing which candidate',\n});\n```\n\nUsage:\n```typescript\nconst realtime = createRealtime(ctx.env);\n\n// Notification events\nawait realtime.notifications['interview-reminder']({ ... }, ctx.activeOrgId!);\n\n// Presence events — who's reviewing which candidate\nawait realtime.presence.reviewing({ userId: ctx.userId, candidateId: 'cnd_456' }, ctx.activeOrgId!);\n```\n\n### Namespace vs Generic Broadcast\n\n| Use Case | Approach |\n|----------|----------|\n| One-off custom event | `realtime.broadcast('event-name', payload, orgId)` |\n| Repeated event patterns | `defineRealtime` namespace |\n| Type-safe events | `defineRealtime` namespace |\n| Event discovery | `defineRealtime` (appears in generated types) |"
363
367
  },
364
368
  "stack/realtime": {
365
369
  "title": "Realtime",
366
- "content": "The Quickback Stack uses Cloudflare Durable Objects to broadcast real-time updates over WebSockets. CRUD events and custom broadcasts are delivered to connected clients with the same security layers (firewall isolation, role-based filtering, field masking) applied.\n\n## Architecture\n\n```\n┌─────────────┐ POST /broadcast ┌──────────────────┐\n│ API Worker │ ───────────────────────► │ Realtime Worker │\n│ (Quickback) │ │ (Durable Object) │\n└─────────────┘ └────────┬─────────┘\n │\n WebSocket│\n │\n ┌────────▼─────────┐\n │ Browser Clients │\n │ (WebSocket) │\n └──────────────────┘\n```\n\n1. **API Worker** — Your Quickback-compiled API. Calls `realtime.insert()` etc. after CRUD operations.\n2. **Realtime Worker** — Separate Cloudflare Worker with Durable Object for managing WebSocket connections, one per organization.\n3. **Browser Clients** — Connect via WebSocket, receive filtered and masked broadcasts.\n\n## Key Features\n\n| Feature | Description |\n|---------|-------------|\n| Organization-scoped | Each org gets its own Durable Object instance |\n| Role-based filtering | Only send events to users with matching roles |\n| Per-role masking | Different users see different field values based on their role |\n| User-specific targeting | Send events to a specific user within an org |\n| Custom broadcasts | Arbitrary events beyond CRUD |\n| Custom namespaces | `defineRealtime()` for type-safe event helpers |\n| Two auth methods | Session tokens (browser) and API keys (server/CLI) |\n\n## Enabling Realtime\n\nAdd `realtime` to individual table definitions:\n\n```typescript\nexport default defineTable(applications, {\n firewall: { organization: {} },\n realtime: {\n enabled: true,\n onInsert: true,\n onUpdate: true,\n onDelete: true,\n requiredRoles: [\"recruiter\", \"hiring-manager\"],\n fields: [\"id\", \"candidateId\", \"stage\"],\n },\n});\n```\n\nAnd enable the realtime binding in your database config. The compiler generates the Durable Object worker and helper functions.\n\n## Pages\n\n- **[Durable Objects Setup](/stack/realtime/durable-objects)** — Configuration, wrangler bindings, event formats, masking, and custom namespaces\n- **[Using Realtime](/stack/realtime/using-realtime)** — WebSocket connection, authentication, and client-side handling"
370
+ "content": "The Quickback Stack uses Cloudflare Durable Objects to broadcast real-time updates over WebSockets. CRUD events and custom broadcasts are delivered to connected clients with the same security layers (firewall isolation, role-based filtering, field masking) applied.\n\n## Architecture\n\n```\n┌─────────────┐ POST /broadcast ┌──────────────────┐\n│ API Worker │ ───────────────────────► │ Realtime Worker │\n│ (Quickback) │ │ (Durable Object) │\n└─────────────┘ └────────┬─────────┘\n │\n WebSocket│\n │\n ┌────────▼─────────┐\n │ Browser Clients │\n │ (WebSocket) │\n └──────────────────┘\n```\n\n1. **API Worker** — Your Quickback-compiled API. Calls `realtime.insert()` etc. after CRUD operations.\n2. **Realtime Worker** — Separate Cloudflare Worker with Durable Object for managing WebSocket connections, one per organization.\n3. **Browser Clients** — Connect via WebSocket, receive filtered and masked broadcasts.\n\n## Key Features\n\n| Feature | Description |\n|---------|-------------|\n| Organization-scoped | Each org gets its own Durable Object instance |\n| Role-based filtering | Only send events to users with matching roles |\n| Per-role masking | Different users see different field values based on their role |\n| User-specific targeting | Send events to a specific user within an org |\n| Custom broadcasts | Arbitrary events beyond CRUD |\n| Custom namespaces | `defineRealtime()` for type-safe event helpers |\n| Ticket-based auth | HMAC-signed tickets verified at WebSocket upgrade no HTTP round-trip |\n\n## Enabling Realtime\n\nAdd `realtime` to individual table definitions:\n\n```typescript\nexport default defineTable(applications, {\n firewall: { organization: {} },\n realtime: {\n enabled: true,\n onInsert: true,\n onUpdate: true,\n onDelete: true,\n requiredRoles: [\"recruiter\", \"hiring-manager\"],\n fields: [\"id\", \"candidateId\", \"stage\"],\n },\n});\n```\n\nAnd enable the realtime binding in your database config. The compiler generates the Durable Object worker and helper functions.\n\n## Pages\n\n- **[Durable Objects Setup](/stack/realtime/durable-objects)** — Configuration, wrangler bindings, event formats, masking, and custom namespaces\n- **[Using Realtime](/stack/realtime/using-realtime)** — WebSocket connection, authentication, and client-side handling"
367
371
  },
368
372
  "stack/realtime/using-realtime": {
369
373
  "title": "Using Realtime",
370
- "content": "This page covers connecting to the Quickback realtime system from client applications — authentication, subscribing to events, and handling messages.\n\n## Connecting\n\nOpen a WebSocket connection to the realtime worker:\n\n```typescript\nconst ws = new WebSocket(\"wss://api.yourdomain.com/realtime/v1/websocket\");\n```\n\n## Authentication\n\nAfter connecting, send an auth message to authenticate. Two methods are supported.\n\n### Session Token (Browser/App)\n\n```typescript\nws.onopen = () => {\n ws.send(JSON.stringify({\n type: \"auth\",\n token: sessionToken, // JWT from Better Auth session\n organizationId: activeOrgId,\n }));\n};\n```\n\n### API Key (Server/CLI)\n\n```typescript\nws.onopen = () => {\n ws.send(JSON.stringify({\n type: \"auth\",\n token: apiKey, // API key for machine-to-machine auth\n organizationId: orgId,\n }));\n};\n```\n\n### Auth Response\n\nOn success:\n\n```json\n{\n \"type\": \"auth_success\",\n \"organizationId\": \"org_123\",\n \"userId\": \"user_456\",\n \"role\": \"hiring-manager\",\n \"roles\": [\"hiring-manager\", \"recruiter\"],\n \"authMethod\": \"session\"\n}\n```\n\nOn failure, the connection is closed with an error message.\n\n## Handling Messages\n\n### CRUD Events\n\nCRUD events use the `postgres_changes` type:\n\n```typescript\nws.onmessage = (event) => {\n const msg = JSON.parse(event.data);\n\n if (msg.type === \"postgres_changes\") {\n const { table, eventType, new: newRecord, old: oldRecord } = msg;\n\n switch (eventType) {\n case \"INSERT\":\n addRecord(table, newRecord);\n break;\n case \"UPDATE\":\n updateRecord(table, newRecord);\n break;\n case \"DELETE\":\n removeRecord(table, oldRecord.id);\n break;\n }\n }\n};\n```\n\n**Event payload:**\n\n```json\n{\n \"type\": \"postgres_changes\",\n \"table\": \"applications\",\n \"schema\": \"public\",\n \"eventType\": \"INSERT\",\n \"new\": { \"id\": \"app_123\", \"candidateId\": \"cnd_456\", \"stage\": \"interview\" },\n \"old\": null\n}\n```\n\nFor UPDATE events, both `new` and `old` are populated. For DELETE events, only `old` is populated.\n\n### Custom Broadcasts\n\nCustom events use the `broadcast` type:\n\n```typescript\nif (msg.type === \"broadcast\") {\n const { event, payload } = msg;\n\n if (event === \"screening-complete\") {\n refreshApplication(payload.applicationId);\n } else if (event === \"screening:progress\") {\n updateProgressBar(payload.percent);\n }\n}\n```\n\n**Event payload:**\n\n```json\n{\n \"type\": \"broadcast\",\n \"event\": \"screening-complete\",\n \"payload\": {\n \"applicationId\": \"app_123\",\n \"candidateId\": \"cnd_456\",\n \"stage\": \"interview\"\n }\n}\n```\n\nCustom namespaces (from `defineRealtime()`) use the format `namespace:event` — e.g., `screening:started`, `screening:progress`.\n\n## Security\n\n### Role-Based Filtering\n\nEvents are only delivered to users whose role matches the broadcast's `targetRoles`. If a table's realtime config specifies `requiredRoles: [\"hiring-manager\", \"recruiter\"]`, only users with those roles receive the events.\n\n### Per-Role Masking\n\nField values are masked according to the subscriber's role. For example, with this masking config:\n\n```typescript\nmasking: {\n ssn: { type: \"ssn\", show: { roles: [\"owner\"] } },\n}\n```\n\n- **Owner sees:** `{ ssn: \"123-45-6789\" }`\n- **Recruiter sees:** `{ ssn: \"*****6789\" }`\n\nMasking is pre-computed per-role (O(roles), not O(subscribers)) for efficiency.\n\n### User-Specific Events\n\nEvents can target a specific user within an organization. Only that user receives the broadcast — all other connections in the org are skipped.\n\n### Organization Isolation\n\nEach organization has its own Durable Object instance. Users can only subscribe to events in organizations they belong to, enforced during authentication.\n\n## Reconnection\n\nWebSocket connections can drop due to network issues. Implement reconnection logic in your client:\n\n```typescript\nfunction connect() {\n const ws = new WebSocket(\"wss://api.yourdomain.com/realtime/v1/websocket\");\n\n ws.onopen = () => {\n ws.send(JSON.stringify({\n type: \"auth\",\n token: sessionToken,\n organizationId: activeOrgId,\n }));\n };\n\n ws.onclose = () => {\n // Reconnect after delay\n setTimeout(connect, 2000);\n };\n\n ws.onmessage = (event) => {\n const msg = JSON.parse(event.data);\n handleMessage(msg);\n };\n\n return ws;\n}\n```\n\n## Required Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| `REALTIME_URL` | URL of the broadcast/realtime worker |\n| `ACCESS_TOKEN` | Internal service-to-service auth token |\n\n```toml\n# wrangler.toml\n[vars]\nREALTIME_URL = \"https://my-app-broadcast.workers.dev\"\nACCESS_TOKEN = \"your-internal-secret-token\"\n```\n\n## Cloudflare Only\n\nRealtime requires Cloudflare Durable Objects and is only available with the Cloudflare runtime.\n\n## See Also\n\n- [Durable Objects Setup](/stack/realtime/durable-objects) — Configuration, event formats, masking, and custom namespaces\n- [Masking](/compiler/definitions/masking) — Field masking configuration"
374
+ "content": "This page covers connecting to the Quickback realtime system from client applications — authentication, subscribing to events, and handling messages.\n\n## Connecting\n\nOpen a WebSocket connection to the realtime worker:\n\n```typescript\nconst ws = new WebSocket(\"wss://api.yourdomain.com/realtime/v1/websocket\");\n```\n\n## Authentication\n\nQuickback uses **ticket-based authentication** for WebSocket connections. This is a two-step process:\n\n1. **Get a ticket** — Call the ws-ticket endpoint with your session auth\n2. **Connect with ticket** — Pass the ticket as a URL parameter when opening the WebSocket\n\nThis approach is faster and more secure than in-band auth messages — the connection is authenticated at upgrade time with no HTTP round-trip from the Durable Object.\n\n### Step 1: Get a WebSocket Ticket\n\n```typescript\nconst response = await fetch(\"/realtime/v1/ws-ticket\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${sessionToken}`,\n \"Content-Type\": \"application/json\",\n },\n});\nconst { wsTicket, expiresIn } = await response.json();\n// expiresIn = 60 (seconds)\n```\n\nThe ticket is a short-lived (60-second) HMAC-signed token containing your userId, organizationId, and roles.\n\n### Step 2: Connect with Ticket\n\n```typescript\nconst ws = new WebSocket(\n `wss://api.yourdomain.com/realtime/v1/websocket?ws_ticket=${wsTicket}&organizationId=${activeOrgId}`\n);\n\nws.onopen = () => {\n console.log(\"Connected and authenticated!\");\n // No auth message needed — connection is pre-authenticated\n};\n```\n\nIf the ticket is invalid or expired, the WebSocket upgrade is rejected with a 401 status.\n\n## Handling Messages\n\n### CRUD Events\n\nCRUD events use the `postgres_changes` type:\n\n```typescript\nws.onmessage = (event) => {\n const msg = JSON.parse(event.data);\n\n if (msg.type === \"postgres_changes\") {\n const { table, eventType, new: newRecord, old: oldRecord } = msg;\n\n switch (eventType) {\n case \"INSERT\":\n addRecord(table, newRecord);\n break;\n case \"UPDATE\":\n updateRecord(table, newRecord);\n break;\n case \"DELETE\":\n removeRecord(table, oldRecord.id);\n break;\n }\n }\n};\n```\n\n**Event payload:**\n\n```json\n{\n \"type\": \"postgres_changes\",\n \"table\": \"applications\",\n \"schema\": \"public\",\n \"eventType\": \"INSERT\",\n \"new\": { \"id\": \"app_123\", \"candidateId\": \"cnd_456\", \"stage\": \"interview\" },\n \"old\": null\n}\n```\n\nFor UPDATE events, both `new` and `old` are populated. For DELETE events, only `old` is populated.\n\n### Custom Broadcasts\n\nCustom events use the `broadcast` type:\n\n```typescript\nif (msg.type === \"broadcast\") {\n const { event, payload } = msg;\n\n if (event === \"screening-complete\") {\n refreshApplication(payload.applicationId);\n } else if (event === \"screening:progress\") {\n updateProgressBar(payload.percent);\n }\n}\n```\n\n**Event payload:**\n\n```json\n{\n \"type\": \"broadcast\",\n \"event\": \"screening-complete\",\n \"payload\": {\n \"applicationId\": \"app_123\",\n \"candidateId\": \"cnd_456\",\n \"stage\": \"interview\"\n }\n}\n```\n\nCustom namespaces (from `defineRealtime()`) use the format `namespace:event` — e.g., `screening:started`, `screening:progress`.\n\n## Security\n\n### Role-Based Filtering\n\nEvents are only delivered to users whose role matches the broadcast's `targetRoles`. If a table's realtime config specifies `requiredRoles: [\"hiring-manager\", \"recruiter\"]`, only users with those roles receive the events.\n\n### Per-Role Masking\n\nField values are masked according to the subscriber's role. For example, with this masking config:\n\n```typescript\nmasking: {\n ssn: { type: \"ssn\", show: { roles: [\"owner\"] } },\n}\n```\n\n- **Owner sees:** `{ ssn: \"123-45-6789\" }`\n- **Recruiter sees:** `{ ssn: \"*****6789\" }`\n\nMasking is pre-computed per-role (O(roles), not O(subscribers)) for efficiency.\n\n### User-Specific Events\n\nEvents can target a specific user within an organization. Only that user receives the broadcast — all other connections in the org are skipped.\n\n### Organization Isolation\n\nEach organization has its own Durable Object instance. Users can only subscribe to events in organizations they belong to, enforced during authentication.\n\n## Reconnection\n\nWebSocket connections can drop due to network issues. Implement reconnection logic in your client — note that you need to fetch a fresh ticket on each reconnect since tickets expire after 60 seconds:\n\n```typescript\nasync function connect() {\n // Get a fresh ticket each time\n const res = await fetch(\"/realtime/v1/ws-ticket\", {\n method: \"POST\",\n headers: { Authorization: `Bearer ${sessionToken}` },\n });\n const { wsTicket } = await res.json();\n\n const ws = new WebSocket(\n `wss://api.yourdomain.com/realtime/v1/websocket?ws_ticket=${wsTicket}&organizationId=${activeOrgId}`\n );\n\n ws.onclose = () => {\n // Reconnect after delay\n setTimeout(connect, 2000);\n };\n\n ws.onmessage = (event) => {\n const msg = JSON.parse(event.data);\n handleMessage(msg);\n };\n\n return ws;\n}\n```\n\n## Required Environment Variables\n\n| Variable | Where | Description |\n|----------|-------|-------------|\n| `REALTIME_URL` | Main API | URL of the broadcast/realtime worker |\n| `ACCESS_TOKEN` | Both | Shared secret for broadcast API auth and ws-ticket signing |\n\n```toml\n# Main API wrangler.toml\n[vars]\nREALTIME_URL = \"https://my-app-broadcast.workers.dev\"\n\n# Broadcast worker: set secret via CLI\n# wrangler secret put ACCESS_TOKEN\n```\n\n## Cloudflare Only\n\nRealtime requires Cloudflare Durable Objects and is only available with the Cloudflare runtime.\n\n## See Also\n\n- [Durable Objects Setup](/stack/realtime/durable-objects) — Configuration, event formats, masking, and custom namespaces\n- [Masking](/compiler/definitions/masking) — Field masking configuration"
371
375
  },
372
376
  "stack/storage": {
373
377
  "title": "Storage",
@@ -493,6 +497,7 @@ export const TOPIC_LIST = [
493
497
  "stack/auth/api-keys",
494
498
  "stack/auth/device-auth",
495
499
  "stack/auth",
500
+ "stack/auth/jwt-optimization",
496
501
  "stack/auth/plugins",
497
502
  "stack/auth/security",
498
503
  "stack/auth/using-auth",
@@ -1 +1 @@
1
- {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/docs/content.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/docs/content.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import pc from "picocolors";
12
12
  // Version injected at build time by scripts/inject-version.ts
13
- const CLI_VERSION = "0.5.14";
13
+ const CLI_VERSION = "0.5.16";
14
14
  function getPackageVersion() {
15
15
  return CLI_VERSION;
16
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,GAAG,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5D,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,iBAAiB,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QACpG,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAoBD;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAQ5E;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,aAAa,EACpB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAAC,cAAc,CAAC,CA6CzB;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAQhF;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,WAAW,SAAK,EAChB,UAAU,SAAO,EACjB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,GAAG,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5D,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,iBAAiB,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QACpG,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAoBD;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAQ5E;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,aAAa,EACpB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAAC,cAAc,CAAC,CA8DzB;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAQhF;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,WAAW,SAAK,EAChB,UAAU,SAAO,EACjB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC"}