koguma 0.4.0
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.
- package/LICENSE +21 -0
- package/README.md +297 -0
- package/cli/index.ts +1150 -0
- package/package.json +60 -0
- package/src/admin/_bundle.ts +3 -0
- package/src/admin/dashboard.ts +27 -0
- package/src/api/router.ts +357 -0
- package/src/auth/index.ts +138 -0
- package/src/client/index.ts +61 -0
- package/src/config/define.ts +157 -0
- package/src/config/field.ts +182 -0
- package/src/config/index.ts +27 -0
- package/src/config/meta.ts +189 -0
- package/src/config/types.ts +35 -0
- package/src/db/migrate.ts +146 -0
- package/src/db/queries.ts +293 -0
- package/src/db/schema.ts +115 -0
- package/src/media/index.ts +89 -0
- package/src/react/index.ts +70 -0
- package/src/worker.ts +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matthew Eric Langford
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="logo.svg" alt="Koguma" width="200" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Koguma</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><strong>A little CMS with big heart</strong> — schema-driven content management that runs entirely on Cloudflare's free tier.</p>
|
|
8
|
+
|
|
9
|
+
Koguma gives you a headless CMS with a beautiful admin dashboard, all powered by **Cloudflare Workers + D1 + R2**. Define your content types in code, and Koguma handles the rest — database schema, API routes, admin UI, and media storage.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
### 1. Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add koguma
|
|
19
|
+
# or
|
|
20
|
+
npm install koguma
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2. Define your content
|
|
24
|
+
|
|
25
|
+
Create a `site.config.ts` in your project root:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { defineConfig, contentType, group, field } from "koguma";
|
|
29
|
+
|
|
30
|
+
export default defineConfig({
|
|
31
|
+
siteName: "My Site",
|
|
32
|
+
contentTypes: [
|
|
33
|
+
contentType("blogPost", "Blog Post", {
|
|
34
|
+
displayField: "title",
|
|
35
|
+
groups: [
|
|
36
|
+
group({ id: "content", name: "Content", fields: {
|
|
37
|
+
title: field.shortText("Title"),
|
|
38
|
+
slug: field.shortText("Slug"),
|
|
39
|
+
body: field.richText("Body"),
|
|
40
|
+
heroImage: field.image("Hero Image"),
|
|
41
|
+
}),
|
|
42
|
+
group({ id: "meta", name: "Metadata", fields: {
|
|
43
|
+
author: field.shortText("Author"),
|
|
44
|
+
publishedAt: field.shortText("Published Date"),
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
}),
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. Create your worker
|
|
53
|
+
|
|
54
|
+
Create a `worker.ts`:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { createWorker } from 'koguma/worker';
|
|
58
|
+
import config from './site.config';
|
|
59
|
+
|
|
60
|
+
export default createWorker(config);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 4. Configure Cloudflare
|
|
64
|
+
|
|
65
|
+
Add to your `wrangler.toml`:
|
|
66
|
+
|
|
67
|
+
```toml
|
|
68
|
+
name = "my-site"
|
|
69
|
+
main = "worker.ts"
|
|
70
|
+
compatibility_date = "2025-09-27"
|
|
71
|
+
compatibility_flags = ["nodejs_compat"]
|
|
72
|
+
|
|
73
|
+
[assets]
|
|
74
|
+
directory = "./dist"
|
|
75
|
+
binding = "ASSETS"
|
|
76
|
+
|
|
77
|
+
[[d1_databases]]
|
|
78
|
+
binding = "DB"
|
|
79
|
+
database_name = "my-site-db"
|
|
80
|
+
database_id = "" # filled by `koguma init`
|
|
81
|
+
|
|
82
|
+
[[r2_buckets]]
|
|
83
|
+
binding = "MEDIA"
|
|
84
|
+
bucket_name = "my-site-media"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 5. Deploy
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
koguma init # Create D1 + R2 on Cloudflare
|
|
91
|
+
koguma secret # Set your admin password
|
|
92
|
+
koguma seed --remote # Seed the production database
|
|
93
|
+
koguma deploy # Build + deploy everything
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Your admin dashboard is at `/admin`. Your API is at `/api/content/:type`.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Content Schema
|
|
101
|
+
|
|
102
|
+
### Field Types
|
|
103
|
+
|
|
104
|
+
| Field | Usage | Stored As |
|
|
105
|
+
| --------------------------------- | ---------------------- | ---------------------- |
|
|
106
|
+
| `field.shortText(label)` | Titles, slugs, URLs | `TEXT` |
|
|
107
|
+
| `field.longText(label)` | Descriptions, bios | `TEXT` |
|
|
108
|
+
| `field.richText(label)` | Rich formatted content | `JSON` (document tree) |
|
|
109
|
+
| `field.number(label)` | Counts, order | `REAL` |
|
|
110
|
+
| `field.boolean(label)` | Toggles | `INTEGER` (0/1) |
|
|
111
|
+
| `field.image(label)` | Image from R2 media | `TEXT` (asset ID) |
|
|
112
|
+
| `field.reference(label, typeId)` | Link to another entry | `TEXT` (entry ID) |
|
|
113
|
+
| `field.references(label, typeId)` | Array of entry links | `JSON` (ID array) |
|
|
114
|
+
|
|
115
|
+
### Content Type Options
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
contentType("page", "Page", {
|
|
119
|
+
displayField: "title", // Field shown in admin list view
|
|
120
|
+
singleton: true, // Only one entry allowed (e.g. site settings)
|
|
121
|
+
groups: [ ... ],
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Groups
|
|
126
|
+
|
|
127
|
+
Groups organize fields into collapsible sections in the admin UI:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
group({ id: "details", name: "Details", helpText: "Main content fields", collapsed: false, fields: {
|
|
131
|
+
title: field.shortText("Title"),
|
|
132
|
+
body: field.richText("Body"),
|
|
133
|
+
}, {
|
|
134
|
+
helpText: "Main content fields",
|
|
135
|
+
collapsed: false, // Start expanded (default)
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## CLI Reference
|
|
142
|
+
|
|
143
|
+
All commands auto-detect your project root by looking for `wrangler.toml`.
|
|
144
|
+
|
|
145
|
+
| Command | Description |
|
|
146
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
147
|
+
| `koguma init` | Create D1 database and R2 bucket on Cloudflare, patch `wrangler.toml` with the database ID |
|
|
148
|
+
| `koguma secret` | Set `KOGUMA_SECRET` (admin password) as a Cloudflare secret |
|
|
149
|
+
| `koguma build` | Build the admin dashboard (Vite) and generate the `_bundle.ts` |
|
|
150
|
+
| `koguma seed` | Run `db/seed.sql` against local D1 |
|
|
151
|
+
| `koguma seed --remote` | Run `db/seed.sql` against production D1 |
|
|
152
|
+
| `koguma migrate-media --remote URL` | Download images from source, upload to R2, update D1 URLs |
|
|
153
|
+
| `koguma deploy` | Build admin + frontend, then `wrangler deploy` |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## API Routes
|
|
158
|
+
|
|
159
|
+
### Public (no auth)
|
|
160
|
+
|
|
161
|
+
| Method | Route | Description |
|
|
162
|
+
| ------ | ------------------------ | --------------------------------------- |
|
|
163
|
+
| `GET` | `/api/content/:type` | List all entries of a content type |
|
|
164
|
+
| `GET` | `/api/content/:type/:id` | Get a single entry (with resolved refs) |
|
|
165
|
+
| `GET` | `/api/media/:key` | Serve a media file from R2 |
|
|
166
|
+
|
|
167
|
+
### Admin (auth required)
|
|
168
|
+
|
|
169
|
+
| Method | Route | Description |
|
|
170
|
+
| -------- | ---------------------- | ----------------------------------- |
|
|
171
|
+
| `POST` | `/api/auth/login` | Login with `{ password }` |
|
|
172
|
+
| `POST` | `/api/auth/logout` | Clear session |
|
|
173
|
+
| `GET` | `/api/auth/me` | Check authentication |
|
|
174
|
+
| `GET` | `/api/admin/schema` | Content type schemas (for admin UI) |
|
|
175
|
+
| `GET` | `/api/admin/:type` | List entries |
|
|
176
|
+
| `GET` | `/api/admin/:type/:id` | Get entry |
|
|
177
|
+
| `POST` | `/api/admin/:type` | Create entry |
|
|
178
|
+
| `PUT` | `/api/admin/:type/:id` | Update entry |
|
|
179
|
+
| `DELETE` | `/api/admin/:type/:id` | Delete entry |
|
|
180
|
+
| `GET` | `/api/admin/media` | List media assets |
|
|
181
|
+
| `POST` | `/api/admin/media` | Upload media (multipart form) |
|
|
182
|
+
| `DELETE` | `/api/admin/media/:id` | Delete media |
|
|
183
|
+
|
|
184
|
+
### Response Format
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
// GET /api/content/blogPost
|
|
188
|
+
{
|
|
189
|
+
"entries": [
|
|
190
|
+
{
|
|
191
|
+
"id": "abc-123",
|
|
192
|
+
"title": "Hello World",
|
|
193
|
+
"heroImage": {
|
|
194
|
+
"id": "img-456",
|
|
195
|
+
"url": "/api/media/img-456.jpg",
|
|
196
|
+
"title": "Hero",
|
|
197
|
+
"content_type": "image/jpeg"
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
References and images are automatically resolved to nested objects in the public API (up to 2 levels deep).
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Package Exports
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import { defineConfig, contentType, group, field, Infer } from 'koguma';
|
|
212
|
+
import { createWorker } from 'koguma/worker';
|
|
213
|
+
import { createClient } from 'koguma/client';
|
|
214
|
+
import { useKoguma } from 'koguma/react';
|
|
215
|
+
import type { RichTextDocument, RichTextNode } from 'koguma/types';
|
|
216
|
+
import { generateSchema } from 'koguma/db';
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Local Development
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# Create .dev.vars with your local admin password
|
|
225
|
+
echo "KOGUMA_SECRET=your-password" > .dev.vars
|
|
226
|
+
|
|
227
|
+
# Start wrangler dev
|
|
228
|
+
npx wrangler dev
|
|
229
|
+
|
|
230
|
+
# Admin dashboard is at http://localhost:8787/admin
|
|
231
|
+
# API is at http://localhost:8787/api/content/:type
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Architecture
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
Your Project
|
|
240
|
+
├── site.config.ts ← Content type definitions
|
|
241
|
+
├── worker.ts ← Entry point (imports koguma/worker)
|
|
242
|
+
├── wrangler.toml ← Cloudflare config (D1 + R2 bindings)
|
|
243
|
+
├── .dev.vars ← Local secrets (KOGUMA_SECRET)
|
|
244
|
+
└── db/
|
|
245
|
+
├── seed.sql ← Database schema + seed data
|
|
246
|
+
└── seed.json ← Raw content data
|
|
247
|
+
|
|
248
|
+
Koguma (this package)
|
|
249
|
+
├── src/
|
|
250
|
+
│ ├── config/ ← Schema definitions (defineConfig, field types)
|
|
251
|
+
│ ├── api/ ← Hono router (CRUD + media + auth)
|
|
252
|
+
│ ├── db/ ← D1 queries and schema generation
|
|
253
|
+
│ ├── auth/ ← HMAC-signed cookie sessions
|
|
254
|
+
│ ├── media/ ← R2 upload/serve/delete
|
|
255
|
+
│ ├── admin/ ← Dashboard HTML shell + JS/CSS bundle
|
|
256
|
+
│ ├── client/ ← Fetch client for consuming the API
|
|
257
|
+
│ └── react/ ← React hooks (useKoguma)
|
|
258
|
+
├── admin/ ← Vite + React admin dashboard source
|
|
259
|
+
└── cli/ ← CLI commands (init, build, seed, deploy)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Roadmap
|
|
265
|
+
|
|
266
|
+
### v0.2 — Admin Power-ups
|
|
267
|
+
|
|
268
|
+
- [ ] **Rich text editor** — replace JSON textarea with Tiptap WYSIWYG (bold, italic, headings, links, lists, inline images)
|
|
269
|
+
- [ ] **Media picker** — browse/upload media from within entry editor, image preview thumbnails
|
|
270
|
+
- [ ] **Entry list search & sort** — filter by display field, sortable columns, pagination
|
|
271
|
+
- [ ] **Unsaved changes warning** — dirty form detection, Cmd+S to save
|
|
272
|
+
- [ ] **Breadcrumb navigation** — show current path in the editor toolbar
|
|
273
|
+
|
|
274
|
+
### v0.3 — Content Workflow
|
|
275
|
+
|
|
276
|
+
- [ ] **Draft / publish** — entries have `status: draft | published`, public API filters by default
|
|
277
|
+
- [ ] **Content versioning** — save revision history, view diffs, one-click rollback
|
|
278
|
+
- [ ] **Field validation** — required fields, min/max length, regex, custom validators
|
|
279
|
+
|
|
280
|
+
### v0.4 — Auth & Multi-site
|
|
281
|
+
|
|
282
|
+
- [ ] **Multi-user auth** — user accounts, roles (admin/editor/viewer), audit log
|
|
283
|
+
- [ ] **Multi-site support** — single install, per-site config and content isolation
|
|
284
|
+
|
|
285
|
+
### v0.5 — DX & Media
|
|
286
|
+
|
|
287
|
+
- [ ] **Image optimization** — auto-resize, WebP/AVIF conversion, responsive srcset
|
|
288
|
+
- [ ] **Webhooks** — fire on content changes, configurable URLs, retry logic
|
|
289
|
+
- [ ] **CLI: export/import** — dump and restore content as JSON
|
|
290
|
+
- [ ] **CLI: schema diff & migrate** — detect config ↔ DB drift, apply changes safely
|
|
291
|
+
- [ ] **Typed SDK** — auto-generate TypeScript types from `site.config.ts`
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## License
|
|
296
|
+
|
|
297
|
+
MIT
|