opensaas-migration
Expert knowledge for migrating projects to OpenSaaS Stack. Invoke whenever the user mentions migrating from KeystoneJS, Prisma, or an existing Next.js project; asks about access control patterns or opensaas.config.ts; or is troubleshooting any aspect of an OpenSaaS Stack migration. Don't wait for the user to say "migration" — trigger whenever the conversation touches these areas.
What this skill does
# OpenSaaS Stack Migration
Expert guidance for migrating existing projects to OpenSaaS Stack.
## Migration Process
### 1. Install Required Packages
**IMPORTANT: Always install packages before starting migration**
Detect the user's package manager (check for `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, or `bun.lockb`) and use their preferred package manager.
**Required packages:**
```bash
# Using npm
npm install --save-dev @opensaas/stack-cli
npm install @opensaas/stack-core
# Using pnpm
pnpm add -D @opensaas/stack-cli
pnpm add @opensaas/stack-core
# Using yarn
yarn add -D @opensaas/stack-cli
yarn add @opensaas/stack-core
# Using bun
bun add -D @opensaas/stack-cli
bun add @opensaas/stack-core
```
**Optional packages (based on user needs):**
- `@opensaas/stack-auth` - If the project needs authentication
- `@opensaas/stack-ui` - If the project needs the admin UI
- `@opensaas/stack-tiptap` - If the project needs rich text editing
- `@opensaas/stack-storage` - If the project needs file storage
- `@opensaas/stack-rag` - If the project needs semantic search/RAG
**Database adapters (required for Prisma 7):**
SQLite:
```bash
npm install better-sqlite3 @prisma/adapter-better-sqlite3
```
PostgreSQL:
```bash
npm install pg @prisma/adapter-pg
```
Neon (serverless PostgreSQL):
```bash
npm install @neondatabase/serverless @prisma/adapter-neon ws
```
### 2. Uninstall Old Packages (KeystoneJS Only)
**IMPORTANT: For KeystoneJS projects, uninstall KeystoneJS packages before installing OpenSaaS**
KeystoneJS migrations should preserve the existing file structure and just swap packages. Do NOT create a new project structure.
```bash
# Detect package manager and uninstall KeystoneJS packages
npm uninstall @keystone-6/core @keystone-6/auth @keystone-6/fields-document
# Or with pnpm
pnpm remove @keystone-6/core @keystone-6/auth @keystone-6/fields-document
```
Remove all `@keystone-6/*` packages from `package.json`.
### 3. Schema Analysis
**Prisma Projects:**
- Analyze existing `schema.prisma`
- Identify models, fields, and relationships
- Note any Prisma-specific features used
**KeystoneJS Projects:**
- Review list definitions in `keystone.config.ts` or `keystone.ts`
- Map KeystoneJS fields to OpenSaaS fields
- Identify access control patterns
- **Note the existing file structure** - preserve it during migration
### 4. Access Control Design
**Common Patterns:**
```typescript
// Public read, authenticated write
operation: {
query: () => true,
create: ({ session }) => !!session?.userId,
update: ({ session }) => !!session?.userId,
delete: ({ session }) => !!session?.userId,
}
// Author-only access
operation: {
query: () => true,
update: ({ session, item }) => item.authorId === session?.userId,
delete: ({ session, item }) => item.authorId === session?.userId,
}
// Admin-only
operation: {
query: ({ session }) => session?.role === 'admin',
create: ({ session }) => session?.role === 'admin',
update: ({ session }) => session?.role === 'admin',
delete: ({ session }) => session?.role === 'admin',
}
// Filter-based access
operation: {
query: ({ session }) => ({
where: { authorId: { equals: session?.userId } }
}),
}
```
### 5. Field Mapping
**Prisma to OpenSaaS:**
| Prisma Type | OpenSaaS Field |
| ----------- | ------------------------------ |
| `String` | `text()` |
| `Int` | `integer()` |
| `Boolean` | `checkbox()` |
| `DateTime` | `timestamp()` |
| `Enum` | `select({ options: [...] })` |
| `Relation` | `relationship({ ref: '...' })` |
**KeystoneJS to OpenSaaS:**
| KeystoneJS Field | OpenSaaS Field |
| ---------------- | -------------------------------------------------------------------------- |
| `text` | `text()` |
| `integer` | `integer()` |
| `checkbox` | `checkbox()` |
| `timestamp` | `timestamp()` |
| `select` | `select()` |
| `relationship` | `relationship()` |
| `password` | `password()` |
| `virtual` | `virtual()` — **requires changes** (no GraphQL, use `hooks.resolveOutput`) |
### 6. Database Configuration
**SQLite (Development):**
```typescript
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'
export default config({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./dev.db',
prismaClientConstructor: (PrismaClient) => {
const adapter = new PrismaBetterSqlite3({ url: process.env.DATABASE_URL || 'file:./dev.db' })
return new PrismaClient({ adapter })
},
},
})
```
**PostgreSQL (Production):**
```typescript
import { PrismaPg } from '@prisma/adapter-pg'
import pg from 'pg'
export default config({
db: {
provider: 'postgresql',
url: process.env.DATABASE_URL,
prismaClientConstructor: (PrismaClient) => {
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL })
const adapter = new PrismaPg(pool)
return new PrismaClient({ adapter })
},
},
})
```
## KeystoneJS Migration Strategy
**CRITICAL: KeystoneJS projects should be migrated IN PLACE**
Do NOT create a new project structure. Instead:
### File Structure Preservation
**Keep existing files and update them:**
1. **Rename config file:**
- `keystone.config.ts` → `opensaas.config.ts`
- OR `keystone.ts` → `opensaas.config.ts`
2. **Update imports in ALL files:**
```typescript
// Before (KeystoneJS)
import { config, list } from '@keystone-6/core'
import { text, relationship, timestamp } from '@keystone-6/core/fields'
// After (OpenSaaS)
import { config, list } from '@opensaas/stack-core'
import { text, relationship, timestamp } from '@opensaas/stack-core/fields'
```
3. **Rename KeystoneJS concepts to OpenSaaS:**
- `keystone.config.ts` → `opensaas.config.ts`
- `Keystone` references → `OpenSaaS` or remove entirely
- Keep all other file names and structure as-is
4. **Update schema/list definitions:**
- Keep existing list definitions
- Update field imports from `@keystone-6/core/fields` to `@opensaas/stack-core/fields`
- Adapt access control syntax (KeystoneJS and OpenSaaS are similar)
- Keep existing GraphQL API file structure
5. **Preserve API routes and pages:**
- Keep existing Next.js pages
- Update any KeystoneJS context calls to use OpenSaaS context
- Maintain existing route structure
### Import Mapping
| KeystoneJS Import | OpenSaaS Import |
| ----------------------------- | ----------------------------- |
| `@keystone-6/core` | `@opensaas/stack-core` |
| `@keystone-6/core/fields` | `@opensaas/stack-core/fields` |
| `@keystone-6/auth` | `@opensaas/stack-auth` |
| `@keystone-6/fields-document` | `@opensaas/stack-tiptap` |
### Example: KeystoneJS to OpenSaaS Config
**Before (keystone.config.ts):**
```typescript
import { config, list } from '@keystone-6/core'
import { text, relationship, timestamp } from '@keystone-6/core/fields'
export default config({
db: {
provider: 'postgresql',
url: process.env.DATABASE_URL,
},
lists: {
Post: list({
fields: {
title: text({ validation: { isRequired: true } }),
content: text({ ui: { displayMode: 'textarea' } }),
author: relationship({ ref: 'User.posts' }),
publishedAt: timestamp(),
},
}),
},
})
```
**After (opensaas.config.ts):**
```typescRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.