create-momentum-app 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/index.cjs +222 -0
  4. package/package.json +69 -0
  5. package/templates/analog/eslint.config.mjs +9 -0
  6. package/templates/analog/index.html +25 -0
  7. package/templates/analog/package.json.tmpl +47 -0
  8. package/templates/analog/postcss.config.cjs +6 -0
  9. package/templates/analog/src/app/app.config.server.ts +7 -0
  10. package/templates/analog/src/app/app.config.ts +27 -0
  11. package/templates/analog/src/app/app.ts +9 -0
  12. package/templates/analog/src/collections/posts.ts +16 -0
  13. package/templates/analog/src/main.server.ts +6 -0
  14. package/templates/analog/src/main.ts +5 -0
  15. package/templates/analog/src/momentum.config.ts.tmpl +50 -0
  16. package/templates/analog/src/server/middleware/00-init.ts +13 -0
  17. package/templates/analog/src/server/routes/api/[...momentum].ts +73 -0
  18. package/templates/analog/src/server/utils/momentum-init.ts.tmpl +87 -0
  19. package/templates/analog/src/styles.css +140 -0
  20. package/templates/analog/tailwind.config.ts +12 -0
  21. package/templates/analog/tsconfig.app.json +9 -0
  22. package/templates/analog/tsconfig.json +26 -0
  23. package/templates/analog/vite.config.ts.tmpl +19 -0
  24. package/templates/angular/angular.json.tmpl +71 -0
  25. package/templates/angular/eslint.config.mjs +31 -0
  26. package/templates/angular/package.json.tmpl +47 -0
  27. package/templates/angular/postcss.config.js +6 -0
  28. package/templates/angular/src/app/app.config.server.ts +7 -0
  29. package/templates/angular/src/app/app.config.ts +14 -0
  30. package/templates/angular/src/app/app.routes.ts +3 -0
  31. package/templates/angular/src/app/app.ts +9 -0
  32. package/templates/angular/src/collections/posts.ts +16 -0
  33. package/templates/angular/src/index.html.tmpl +13 -0
  34. package/templates/angular/src/main.server.ts +7 -0
  35. package/templates/angular/src/main.ts +5 -0
  36. package/templates/angular/src/momentum.config.ts.tmpl +59 -0
  37. package/templates/angular/src/server.ts.tmpl +74 -0
  38. package/templates/angular/src/styles.css +140 -0
  39. package/templates/angular/tailwind.config.js +11 -0
  40. package/templates/angular/tsconfig.app.json +9 -0
  41. package/templates/angular/tsconfig.json +26 -0
  42. package/templates/shared/.claude/CLAUDE.md +155 -0
  43. package/templates/shared/.claude/skills/collection/SKILL.md +117 -0
  44. package/templates/shared/.claude/skills/momentum-api/SKILL.md +158 -0
  45. package/templates/shared/.env.example.tmpl +11 -0
  46. package/templates/shared/README.md.tmpl +27 -0
@@ -0,0 +1,140 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ /* Base colors */
8
+ --mcms-background: 0 0% 100%;
9
+ --mcms-foreground: 222 47% 11%;
10
+
11
+ /* Card */
12
+ --mcms-card: 0 0% 100%;
13
+ --mcms-card-foreground: 222 47% 11%;
14
+
15
+ /* Primary - Blue */
16
+ --mcms-primary: 221 83% 53%;
17
+ --mcms-primary-foreground: 210 40% 98%;
18
+
19
+ /* Secondary - Gray */
20
+ --mcms-secondary: 210 40% 96%;
21
+ --mcms-secondary-foreground: 222 47% 11%;
22
+
23
+ /* Muted - Light gray for subtle backgrounds */
24
+ --mcms-muted: 210 40% 96%;
25
+ --mcms-muted-foreground: 215 16% 43%;
26
+
27
+ /* Accent - Hover states */
28
+ --mcms-accent: 210 40% 96%;
29
+ --mcms-accent-foreground: 222 47% 11%;
30
+
31
+ /* Destructive - Red for dangerous actions */
32
+ --mcms-destructive: 0 84% 60%;
33
+ --mcms-destructive-foreground: 210 40% 98%;
34
+
35
+ /* Success - Green for positive feedback */
36
+ --mcms-success: 142 76% 36%;
37
+ --mcms-success-foreground: 0 0% 100%;
38
+
39
+ /* Warning - Amber for caution */
40
+ --mcms-warning: 38 92% 50%;
41
+ --mcms-warning-foreground: 48 96% 5%;
42
+
43
+ /* Info - Blue for informational messages */
44
+ --mcms-info: 199 89% 48%;
45
+ --mcms-info-foreground: 0 0% 100%;
46
+
47
+ /* Overlay - For modal backdrops */
48
+ --mcms-overlay: 0 0% 0%;
49
+
50
+ /* Border, input, and ring */
51
+ --mcms-border: 214 32% 91%;
52
+ --mcms-input: 214 32% 91%;
53
+ --mcms-ring: 221 83% 53%;
54
+
55
+ /* Border radius */
56
+ --mcms-radius: 0.5rem;
57
+
58
+ /* Sidebar colors - Dark sidebar in light mode */
59
+ --mcms-sidebar: 222 47% 11%;
60
+ --mcms-sidebar-foreground: 210 40% 98%;
61
+ --mcms-sidebar-primary: 221 83% 53%;
62
+ --mcms-sidebar-primary-foreground: 210 40% 98%;
63
+ --mcms-sidebar-accent: 217 33% 17%;
64
+ --mcms-sidebar-accent-foreground: 210 40% 98%;
65
+ --mcms-sidebar-border: 217 33% 20%;
66
+ --mcms-sidebar-ring: 221 83% 53%;
67
+ }
68
+
69
+ .dark {
70
+ /* Base colors - Dark background */
71
+ --mcms-background: 222 47% 11%;
72
+ --mcms-foreground: 210 40% 98%;
73
+
74
+ /* Card - slightly lighter than background */
75
+ --mcms-card: 217 33% 15%;
76
+ --mcms-card-foreground: 210 40% 98%;
77
+
78
+ /* Primary - Blue */
79
+ --mcms-primary: 217 91% 60%;
80
+ --mcms-primary-foreground: 222 47% 11%;
81
+
82
+ /* Secondary - distinct from card for badges */
83
+ --mcms-secondary: 215 28% 25%;
84
+ --mcms-secondary-foreground: 210 40% 98%;
85
+
86
+ /* Muted - subtle background */
87
+ --mcms-muted: 217 33% 20%;
88
+ --mcms-muted-foreground: 215 20% 65%;
89
+
90
+ /* Accent - hover states */
91
+ --mcms-accent: 217 33% 22%;
92
+ --mcms-accent-foreground: 210 40% 98%;
93
+
94
+ /* Destructive */
95
+ --mcms-destructive: 0 62% 50%;
96
+ --mcms-destructive-foreground: 210 40% 98%;
97
+
98
+ /* Success */
99
+ --mcms-success: 142 71% 45%;
100
+ --mcms-success-foreground: 0 0% 100%;
101
+
102
+ /* Warning */
103
+ --mcms-warning: 38 92% 50%;
104
+ --mcms-warning-foreground: 48 96% 5%;
105
+
106
+ /* Info */
107
+ --mcms-info: 199 89% 48%;
108
+ --mcms-info-foreground: 0 0% 100%;
109
+
110
+ /* Overlay */
111
+ --mcms-overlay: 0 0% 0%;
112
+
113
+ /* Border, input, and ring - slightly visible */
114
+ --mcms-border: 217 33% 22%;
115
+ --mcms-input: 217 33% 22%;
116
+ --mcms-ring: 224 76% 48%;
117
+
118
+ /* Sidebar colors - Slightly lighter than main in dark mode */
119
+ --mcms-sidebar: 224 71% 4%;
120
+ --mcms-sidebar-foreground: 213 31% 91%;
121
+ --mcms-sidebar-primary: 217 91% 60%;
122
+ --mcms-sidebar-primary-foreground: 210 40% 98%;
123
+ --mcms-sidebar-accent: 217 33% 17%;
124
+ --mcms-sidebar-accent-foreground: 213 31% 91%;
125
+ --mcms-sidebar-border: 217 33% 12%;
126
+ --mcms-sidebar-ring: 217 91% 60%;
127
+ }
128
+ }
129
+
130
+ /* Apply base styles */
131
+ @layer base {
132
+ * {
133
+ border-color: hsl(var(--mcms-border));
134
+ }
135
+
136
+ body {
137
+ background-color: hsl(var(--mcms-background));
138
+ color: hsl(var(--mcms-foreground));
139
+ }
140
+ }
@@ -0,0 +1,11 @@
1
+ const adminPreset = require('@momentumcms/admin/tailwind.preset');
2
+
3
+ /** @type {import('tailwindcss').Config} */
4
+ module.exports = {
5
+ presets: [adminPreset],
6
+ content: [
7
+ './src/**/*.{html,ts}',
8
+ './node_modules/@momentumcms/admin/**/*.{html,ts,mjs}',
9
+ './node_modules/@momentumcms/ui/**/*.{html,ts,mjs}',
10
+ ],
11
+ };
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "types": ["node"]
6
+ },
7
+ "files": ["src/main.ts", "src/main.server.ts"],
8
+ "include": ["src/**/*.d.ts", "src/**/*.ts"]
9
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compileOnSave": false,
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "strict": true,
6
+ "noImplicitOverride": true,
7
+ "noPropertyAccessFromIndexSignature": true,
8
+ "noImplicitReturns": true,
9
+ "noFallthroughCasesInSwitch": true,
10
+ "sourceMap": true,
11
+ "declaration": false,
12
+ "experimentalDecorators": true,
13
+ "moduleResolution": "bundler",
14
+ "importHelpers": true,
15
+ "target": "ES2022",
16
+ "module": "ES2022",
17
+ "lib": ["ES2022", "dom"],
18
+ "skipLibCheck": true
19
+ },
20
+ "angularCompilerOptions": {
21
+ "enableI18nLegacyMessageIdFormat": false,
22
+ "strictInjectionParameters": true,
23
+ "strictInputAccessModifiers": true,
24
+ "strictTemplates": true
25
+ }
26
+ }
@@ -0,0 +1,155 @@
1
+ # Momentum CMS App
2
+
3
+ Angular-based headless CMS application built with [Momentum CMS](https://github.com/momentum-cms/momentum-cms).
4
+
5
+ ## Tech Stack
6
+
7
+ - Angular 21 (SSR) with Express or Analog.js (Nitro)
8
+ - Drizzle ORM (PostgreSQL or SQLite)
9
+ - Better Auth for authentication
10
+ - Tailwind CSS for styling
11
+
12
+ ## Commands
13
+
14
+ ```bash
15
+ npm run dev # Start dev server
16
+ npm run build # Production build
17
+ npm start # Start production server
18
+ npx drizzle-kit generate # Create SQL migrations from schema changes
19
+ npx drizzle-kit push # Direct push (dev only)
20
+ npx drizzle-kit migrate # Apply migrations (production)
21
+ ```
22
+
23
+ ## Project Structure
24
+
25
+ ```
26
+ src/
27
+ collections/ # Collection definitions (data models)
28
+ momentum.config.ts # Central configuration (db, auth, collections, plugins)
29
+ server.ts # Server entry point (Express) or server/ (Analog)
30
+ app/ # Angular app components and routes
31
+ styles.css # Tailwind + Momentum theme variables
32
+ ```
33
+
34
+ ## Defining Collections
35
+
36
+ Collections are the core data model. Each collection generates a database table, REST API, and admin UI.
37
+
38
+ ```typescript
39
+ import { defineCollection, text, richText, select, relationship } from '@momentumcms/core';
40
+
41
+ export const Posts = defineCollection({
42
+ slug: 'posts',
43
+ admin: { useAsTitle: 'title', group: 'Content' },
44
+ access: {
45
+ read: () => true,
46
+ create: ({ req }) => !!req.user,
47
+ update: ({ req }) => req.user?.role === 'admin',
48
+ delete: ({ req }) => req.user?.role === 'admin',
49
+ },
50
+ fields: [
51
+ text('title', { required: true }),
52
+ richText('content'),
53
+ select('status', {
54
+ options: [
55
+ { label: 'Draft', value: 'draft' },
56
+ { label: 'Published', value: 'published' },
57
+ ],
58
+ defaultValue: 'draft',
59
+ }),
60
+ ],
61
+ });
62
+ ```
63
+
64
+ ### Adding a New Collection
65
+
66
+ 1. Create `src/collections/<name>.ts` with `defineCollection()`
67
+ 2. Add to `collections` array in `momentum.config.ts`
68
+ 3. Run `npx drizzle-kit generate && npx drizzle-kit push`
69
+ 4. Restart dev server
70
+
71
+ ### Field Types
72
+
73
+ | Field | Import | Description |
74
+ | -------------- | ----------------------------------------------- | ----------------------- |
75
+ | `text` | `text(name, opts)` | Short text string |
76
+ | `textarea` | `textarea(name, opts)` | Multi-line text |
77
+ | `richText` | `richText(name, opts)` | Rich text editor (HTML) |
78
+ | `number` | `number(name, opts)` | Numeric value |
79
+ | `date` | `date(name, opts)` | Date/datetime |
80
+ | `checkbox` | `checkbox(name, opts)` | Boolean |
81
+ | `select` | `select(name, { options })` | Dropdown select |
82
+ | `radio` | `radio(name, { options })` | Radio buttons |
83
+ | `email` | `email(name, opts)` | Email with validation |
84
+ | `password` | `password(name, opts)` | Password field |
85
+ | `upload` | `upload(name, { relationTo })` | File upload |
86
+ | `relationship` | `relationship(name, { collection: () => Ref })` | FK reference |
87
+ | `array` | `array(name, { fields })` | Repeating sub-fields |
88
+ | `group` | `group(name, { fields })` | Nested field group |
89
+ | `blocks` | `blocks(name, { blocks })` | Content blocks |
90
+ | `json` | `json(name, opts)` | Raw JSON |
91
+ | `point` | `point(name, opts)` | Geolocation |
92
+ | `slug` | `slug(name, { from })` | Auto-generated slug |
93
+
94
+ ### Access Control Helpers
95
+
96
+ ```typescript
97
+ import { allowAll, isAuthenticated, hasRole, hasAnyRole, isOwner, and, or, not } from '@momentumcms/core';
98
+
99
+ access: {
100
+ read: allowAll(),
101
+ create: isAuthenticated(),
102
+ update: or(hasRole('admin'), isOwner()),
103
+ delete: hasRole('admin'),
104
+ }
105
+ ```
106
+
107
+ ## Using the API in Components
108
+
109
+ ```typescript
110
+ import { injectMomentumAPI } from '@momentumcms/admin';
111
+
112
+ @Component({...})
113
+ export class MyComponent {
114
+ private readonly api = injectMomentumAPI();
115
+
116
+ // Observable
117
+ readonly posts$ = this.api.collection<Post>('posts').find$({ limit: 10 });
118
+
119
+ // Promise
120
+ async load(): Promise<void> {
121
+ const result = await this.api.collection<Post>('posts').find({ limit: 10 });
122
+ }
123
+
124
+ // CRUD
125
+ async create(): Promise<void> {
126
+ await this.api.collection<Post>('posts').create({ title: 'New' });
127
+ }
128
+ }
129
+ ```
130
+
131
+ ## Code Style
132
+
133
+ - **Signals for state**: `signal()`, `computed()`, `effect()`
134
+ - **Signal inputs/outputs**: `input()`, `input.required()`, `output()`
135
+ - **inject() function**, not constructor injection
136
+ - **OnPush change detection** for all components
137
+ - **Control flow**: `@if`, `@for`, `@switch`
138
+ - Don't add `standalone: true` (default in Angular 21)
139
+ - Use kebab-case filenames, PascalCase classes
140
+
141
+ ## REST API
142
+
143
+ All collections get auto-generated endpoints:
144
+
145
+ | Method | Path | Description |
146
+ | ------ | ---------------------- | -------------------------------------- |
147
+ | GET | `/api/:collection` | List (query: limit, page, sort, where) |
148
+ | GET | `/api/:collection/:id` | Get by ID |
149
+ | POST | `/api/:collection` | Create |
150
+ | PATCH | `/api/:collection/:id` | Update |
151
+ | DELETE | `/api/:collection/:id` | Delete |
152
+
153
+ ## Documentation
154
+
155
+ Full docs: https://github.com/momentum-cms/momentum-cms/tree/main/docs
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: collection
3
+ description: Generate a new Momentum CMS collection with fields, access control, and hooks
4
+ argument-hint: <collection-name>
5
+ ---
6
+
7
+ # Generate Momentum CMS Collection
8
+
9
+ Create a new collection file following project conventions.
10
+
11
+ ## Arguments
12
+
13
+ - `$ARGUMENTS` - Collection name (e.g., "posts", "products", "pages")
14
+
15
+ ## Steps
16
+
17
+ 1. Create the collection file at `src/collections/<name>.ts`
18
+
19
+ 2. Use this template:
20
+
21
+ ```typescript
22
+ import {
23
+ defineCollection,
24
+ text,
25
+ richText,
26
+ number,
27
+ date,
28
+ checkbox,
29
+ select,
30
+ relationship,
31
+ } from '@momentumcms/core';
32
+
33
+ export const <PascalName> = defineCollection({
34
+ slug: '<kebab-name>',
35
+
36
+ admin: {
37
+ useAsTitle: 'title', // or 'name' - the field to display as title
38
+ defaultColumns: ['title', 'createdAt'],
39
+ group: 'Content', // Admin sidebar group
40
+ },
41
+
42
+ access: {
43
+ read: () => true,
44
+ create: ({ req }) => !!req.user,
45
+ update: ({ req }) => req.user?.role === 'admin',
46
+ delete: ({ req }) => req.user?.role === 'admin',
47
+ },
48
+
49
+ hooks: {
50
+ beforeChange: [],
51
+ afterChange: [],
52
+ },
53
+
54
+ fields: [
55
+ text('title', { required: true }),
56
+ // Add more fields as needed
57
+ ],
58
+ });
59
+ ```
60
+
61
+ 3. Add to momentum config. In `src/momentum.config.ts`, import and add to `collections` array:
62
+
63
+ ```typescript
64
+ import { <PascalName> } from './collections/<name>';
65
+
66
+ // In config:
67
+ collections: [Posts, <PascalName>],
68
+ ```
69
+
70
+ 4. Remind user to run schema generation:
71
+
72
+ ```bash
73
+ npx drizzle-kit generate
74
+ npx drizzle-kit push
75
+ ```
76
+
77
+ ## Field Types Available
78
+
79
+ - `text(name, options)` - Short text (minLength, maxLength)
80
+ - `textarea(name, options)` - Multi-line text (rows, minLength, maxLength)
81
+ - `richText(name, options)` - Rich text editor
82
+ - `number(name, options)` - Numeric value (min, max, step)
83
+ - `date(name, options)` - Date/datetime
84
+ - `checkbox(name, options)` - Boolean
85
+ - `select(name, { options: [...] })` - Dropdown (hasMany for multi-select)
86
+ - `radio(name, { options: [...] })` - Radio buttons
87
+ - `email(name, options)` - Email with validation
88
+ - `upload(name, { relationTo: 'media' })` - File upload
89
+ - `relationship(name, { collection: () => Ref })` - Reference to another collection
90
+ - `array(name, { fields: [...] })` - Array of objects (minRows, maxRows)
91
+ - `group(name, { fields: [...] })` - Nested object
92
+ - `blocks(name, { blocks: [...] })` - Content blocks
93
+ - `json(name, options)` - Raw JSON
94
+ - `point(name, options)` - Geolocation (lat/lng)
95
+ - `slug(name, { from: 'title' })` - Auto-generated slug
96
+
97
+ ## Layout Fields (visual only, no data storage)
98
+
99
+ - `tabs(name, { tabs: [{ label, fields }] })` - Tabbed sections
100
+ - `collapsible(name, { fields, defaultOpen })` - Expandable section
101
+ - `row(name, { fields })` - Horizontal row
102
+
103
+ ## Access Control Helpers
104
+
105
+ ```typescript
106
+ import {
107
+ allowAll,
108
+ denyAll,
109
+ isAuthenticated,
110
+ hasRole,
111
+ hasAnyRole,
112
+ isOwner,
113
+ and,
114
+ or,
115
+ not,
116
+ } from '@momentumcms/core';
117
+ ```
@@ -0,0 +1,158 @@
1
+ ---
2
+ name: momentum-api
3
+ description: Work with Momentum API for data operations in Angular components
4
+ argument-hint: <operation> [collection]
5
+ ---
6
+
7
+ # Momentum API Usage
8
+
9
+ Guide for using `injectMomentumAPI()` in Angular components.
10
+
11
+ ## Arguments
12
+
13
+ - `$ARGUMENTS` - Operation type: "query", "crud", "typed", or collection name
14
+
15
+ ## Quick Reference
16
+
17
+ ### Inject the API
18
+
19
+ ```typescript
20
+ import { injectMomentumAPI } from '@momentumcms/admin';
21
+
22
+ @Component({...})
23
+ export class MyComponent {
24
+ private readonly api = injectMomentumAPI();
25
+ }
26
+ ```
27
+
28
+ ### Query Data (Observables)
29
+
30
+ ```typescript
31
+ // In constructor or with toSignal()
32
+ this.api
33
+ .collection<Post>('posts')
34
+ .find$({ limit: 10 })
35
+ .subscribe((result) => {
36
+ this.posts.set(result.docs);
37
+ });
38
+ ```
39
+
40
+ ### Query Data (Promises)
41
+
42
+ ```typescript
43
+ async loadData(): Promise<void> {
44
+ const result = await this.api.collection<Post>('posts').find({ limit: 10 });
45
+ this.posts.set(result.docs);
46
+ }
47
+ ```
48
+
49
+ ### CRUD Operations
50
+
51
+ ```typescript
52
+ // Create
53
+ const post = await this.api.collection<Post>('posts').create({ title: 'New Post' });
54
+
55
+ // Read
56
+ const post = await this.api.collection<Post>('posts').findById('123');
57
+
58
+ // Update
59
+ const updated = await this.api.collection<Post>('posts').update('123', { title: 'Updated' });
60
+
61
+ // Delete
62
+ const result = await this.api.collection<Post>('posts').delete('123');
63
+ ```
64
+
65
+ ## Find Options
66
+
67
+ ```typescript
68
+ interface FindOptions {
69
+ where?: Record<string, unknown>; // Filter conditions
70
+ sort?: string; // Sort field (prefix with - for desc)
71
+ limit?: number; // Max results (default: 10)
72
+ page?: number; // Page number (default: 1)
73
+ }
74
+ ```
75
+
76
+ ## Full Component Example
77
+
78
+ ```typescript
79
+ import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
80
+ import { injectMomentumAPI } from '@momentumcms/admin';
81
+
82
+ @Component({
83
+ selector: 'app-posts',
84
+ template: `
85
+ @if (loading()) {
86
+ <p>Loading...</p>
87
+ } @else {
88
+ @for (post of posts(); track post.id) {
89
+ <article>
90
+ <h2>{{ post.title }}</h2>
91
+ <button (click)="deletePost(post.id)">Delete</button>
92
+ </article>
93
+ }
94
+ }
95
+ `,
96
+ changeDetection: ChangeDetectionStrategy.OnPush,
97
+ })
98
+ export class PostsComponent {
99
+ private readonly api = injectMomentumAPI();
100
+
101
+ readonly posts = signal<any[]>([]);
102
+ readonly loading = signal(true);
103
+
104
+ constructor() {
105
+ this.loadPosts();
106
+ }
107
+
108
+ async loadPosts(): Promise<void> {
109
+ this.loading.set(true);
110
+ try {
111
+ const result = await this.api.collection('posts').find({
112
+ limit: 20,
113
+ sort: '-createdAt',
114
+ });
115
+ this.posts.set(result.docs);
116
+ } finally {
117
+ this.loading.set(false);
118
+ }
119
+ }
120
+
121
+ async deletePost(id: string): Promise<void> {
122
+ await this.api.collection('posts').delete(id);
123
+ this.posts.update((posts) => posts.filter((p) => p.id !== id));
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## Platform Behavior
129
+
130
+ - **SSR**: Direct database access (no HTTP overhead)
131
+ - **Browser**: HTTP calls to `/api/*`
132
+ - **Same interface** - code works identically on both platforms
133
+
134
+ ## TransferState (SSR Hydration)
135
+
136
+ TransferState is **enabled by default** for read operations. Data fetched during SSR is cached and reused on browser hydration.
137
+
138
+ ```typescript
139
+ // Default: TransferState enabled (no duplicate fetch on hydration)
140
+ const posts = await this.api.collection<Post>('posts').find({ limit: 10 });
141
+
142
+ // Opt-out: always fetch fresh data
143
+ const posts = await this.api.collection<Post>('posts').find({ limit: 10, transfer: false });
144
+ ```
145
+
146
+ Requires `provideClientHydration()` in app config.
147
+
148
+ ## Error Handling
149
+
150
+ ```typescript
151
+ try {
152
+ await this.api.collection('posts').create({ title: '' });
153
+ } catch (error) {
154
+ if (error instanceof ValidationError) {
155
+ console.error('Validation failed:', error.errors);
156
+ }
157
+ }
158
+ ```
@@ -0,0 +1,11 @@
1
+ # Database
2
+ {{envDbVar}}
3
+
4
+ # Auth
5
+ BETTER_AUTH_URL=http://localhost:{{defaultPort}}
6
+
7
+ # Server
8
+ PORT={{defaultPort}}
9
+
10
+ # CORS (comma-separated origins for production)
11
+ # CORS_ORIGIN=https://your-domain.com
@@ -0,0 +1,27 @@
1
+ # {{projectName}}
2
+
3
+ A [Momentum CMS](https://github.com/momentum-cms/momentum-cms) application.
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ # Copy environment variables
9
+ cp .env.example .env
10
+
11
+ # Start the dev server
12
+ npm run dev
13
+ ```
14
+
15
+ Open [http://localhost:{{defaultPort}}/admin](http://localhost:{{defaultPort}}/admin) to access the admin dashboard.
16
+
17
+ ## Scripts
18
+
19
+ | Command | Description |
20
+ |---------|-------------|
21
+ | `npm run dev` | Start development server |
22
+ | `npm run build` | Production build |
23
+ | `npm start` | Start production server |
24
+
25
+ ## Learn More
26
+
27
+ - [Momentum CMS Documentation](https://github.com/momentum-cms/momentum-cms)