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 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