# Generic CMS Module

A drop-in Stage 2 CMS for any FreshVibe Way v8 site. One module.
Schema-driven. Local-first. Single admin. **189/189 tests pass.**

## TL;DR

```bash
cp -r /path/to/cms ./cms          # install
```

Add a route redirect for `/admin*` → `/admin.html 200`. Open `/admin`,
run setup, start editing. No build step. No npm.

## What it ships

- **CRUD for any content type** — define a schema, get a working admin
- **Bulk import** — JSON, JSONL, CSV, or AI-prompted
- **Activity log** — every write is audited (5000-entry cap)
- **Backups** — export, import, auto-rotation (4 slots)
- **Auth** — PBKDF2 (310k iters), lockout, recovery, dev-mode
- **Storage** — pluggable adapter (localStorage today, IndexedDB +
  Remote in v2.0)

## Quick start

```js
import {
  initCmsAuth, registerSchemas,
  getStorageAdapter, setStorageAdapter,
} from './cms/index.js';

registerSchemas({
  book: {
    id: 'book',
    fields: {
      id: { type: 'string', required: true, pattern: '^[a-z0-9_]+$' },
      title: { type: 'string', required: true },
    },
  },
});

await initCmsAuth();
```

See `USAGE.md` for the full guide.

## File layout (skim)

| File | Purpose |
|---|---|
| `index.js` | Public API barrel |
| `schema.js` | `Schema` class |
| `validator.js` | Pure validation function |
| `store.js` | Write gate (`cmsUpsert`, `cmsDelete`) |
| `activity.js` | Append-only audit log |
| `backups.js` | Export / import / rotation |
| `search.js` | Global search |
| `auth/` | PBKDF2 auth, recovery, dev-mode |
| `schemas/` | Content type definitions |
| `editors/` | List / edit / delete UI |
| `upload/` | Bulk import (JSON / JSONL / CSV / AI) |
| `views/` | Activity, backups, help, search |
| `router.js` | Hash router |
| `storage/` | Adapter interface + localStorage impl |
| `docs/` | Operator docs (FAQ, A11Y, privacy, troubleshooting) |
| `module.json` | Self-describing manifest |

## Tests

```bash
cd /workspace/projects/cms
node --test cms/tests/test-*.js cms/tests/acceptance/site-smoke.test.js
```

**189/189 tests pass.** Track breakdown:

- Track 0–3 (133): foundation, store, auth, UI
- Track 4 (22): integration + adoption example + acceptance
- Track 5 (34): hardening — security regression + perf smoke + backup size

## Docs (skim)

| Doc | Read when |
|---|---|
| `USAGE.md` | Adding a content type, using editors |
| `SECURITY.md` | Going live, threat model + checklist |
| `MIGRATION.md` | Moving from Sanity / Contentful / etc. |
| `SCHEMAS.md` | Browsing built-in + example schemas |
| `docs/FAQ.md` | Common operator questions |
| `docs/TROUBLESHOOTING.md` | Something is broken — start here |
| `docs/A11Y.md` | Accessibility posture + manual checklist |
| `docs/PRIVACY-AND-I18N.md` | GDPR / data residency / i18n limits |
| `cms-doctrine.md` | Module rules (file size, storage boundary) |

## Adoption example

A 9-field "Book" content type ships as `schemas/book.js`. Use it as a
starting point for your own content types.

```js
import { bookSchema } from './cms/schemas/book.js';
console.log(bookSchema.toAiPrompt());
```

## Version + status

Internal. **Generic CMS Module v0.5.0-track5 (hardened).**

| Track | Status |
|---|---|
| Track 0: foundation | shipped |
| Track 1: store + activity + backups + search | shipped |
| Track 2: auth | shipped |
| Track 3: UI | shipped |
| Track 4: integration (acceptance + adoption) | shipped |
| Track 5: hardening (security + perf + a11y + docs) | shipped |

## License

Internal.
