domma-cms 0.38.2 → 0.39.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/CLAUDE.md CHANGED
@@ -25,32 +25,9 @@ Domma provides built-in solutions. Never use vanilla JS equivalents.
25
25
 
26
26
  ## Architecture
27
27
 
28
- ```
29
- server/ Fastify 5 API + SSR (DO NOT modify — upstream-replaced on update)
30
- server.js Entry point; registers routes, plugins, static serving
31
- config.js Config loader: config singleton, getConfig(), saveConfig()
32
- middleware/auth.js JWT authenticate, requireRole, requireAdmin, canManageUser
33
- routes/api/ REST API (pages, settings, navigation, media, auth, users, plugins, …)
34
- routes/public.js Catch-all SSR for public pages; enforces page.visibility
35
- services/ Core business logic (see Services section below)
36
- templates/page.html Public page HTML shell ({{headInject}}, {{bodyEndInject}} slots)
37
- admin/ Domma SPA admin panel (DO NOT modify — upstream-replaced on update)
38
- public/js/ Public site JS (site.js + component init)
39
- public/css/ Site stylesheet (site.css is yours to edit)
40
- bin/ CLI entry point
41
- scripts/ Setup, reset, seed, fresh, pro, build utilities
42
- content/pages/ Markdown pages (URL → file mapping below)
43
- content/media/ Uploaded media
44
- content/users/ User accounts (JSON)
45
- content/collections/ Collection entries
46
- content/blocks/ Reusable content blocks
47
- content/forms/ Form definitions
48
- content/versions/ Page version history
49
- content/custom.css Optional custom CSS (served at /custom.css)
50
- config/ All configuration JSON (see Config section)
51
- plugins/ CMS plugins (yours to create/modify)
52
- docs/ Reference documentation
53
- ```
28
+ **DO NOT modify `server/` or `admin/`** — both are replaced wholesale by the upstream updater.
29
+ Yours to edit: `public/css/site.css`, `content/custom.css`, `plugins/`, and `config/` (see Config
30
+ Reference for which config files the updater merges vs leaves alone).
54
31
 
55
32
  **URL → file mapping:**
56
33
 
@@ -76,38 +53,10 @@ Page content here. Shortcodes work anywhere in the body.
76
53
 
77
54
  **Page visibility** is enforced server-side in `server/routes/public.js` — unauthenticated users and insufficient role levels receive a 403/redirect.
78
55
 
79
- ### Shortcodes (~28 types — full syntax in `docs/markdown-shortcodes.md`)
80
-
81
- | Shortcode | Purpose |
82
- |-----------------------------------------|----------------------------------------------------|
83
- | `[card]` | Card container (optional title, collapsible, icon) |
84
- | `[grid cols="N"]` / `[col]` | CSS Grid layout (Domma Grid — NOT `.col` class) |
85
- | `[row]` / `[col]` | Row/column layout |
86
- | `[slideover trigger="..." title="..."]` | Slide-over panel |
87
- | `[dconfig]{...}[/dconfig]` | Declarative page config (JSON) |
88
- | `[tabs]` / `[tab title="..."]` | Tab panels |
89
- | `[accordion]` / `[item title="..."]` | Accordion sections |
90
- | `[carousel]` / `[slide]` | Image/content carousel |
91
- | `[hero title="..." tagline="..."]` | Hero section |
92
- | `[table]` | Data table (Markdown table inside) |
93
- | `[badge variant="..."]` | Badge/label |
94
- | `[countdown to="..." /]` | Countdown timer |
95
- | `[timeline]` / `[event]` | Timeline component |
96
- | `[spacer /]` | Vertical spacer |
97
- | `[center]` | Centre-align content |
98
- | `[icon name="..." /]` | Inline icon |
99
- | `[embed url="..." /]` | Video (YouTube/Vimeo/self-hosted) or iframe embed |
100
- | `[cta action="..."]` | Collection action trigger (admin) |
101
- | `[block name="..."]` | Embed a reusable content block |
102
- | `[view slug="..."]` | Embed a saved view |
103
- | `[collection slug="..."]` | Render a collection inline |
104
- | `[text field="..."]` | Output a single text field value |
105
- | `[button label="..." href="..."]` | Styled button / CTA link |
106
- | `[link href="..."]` | Styled anchor |
107
- | `[listgroup]` / `[item]` | List group component |
108
- | `[form slug="..."]` | Embed a form |
109
- | `[banner]` | Full-width banner strip |
110
- | `[celebrate]` | Confetti / effects trigger |
56
+ ### Shortcodes
57
+
58
+ ~28 shortcode types. **Full list and syntax: `docs/markdown-shortcodes.md`** — read it before writing or
59
+ editing shortcode markup; the nesting rules below are the part the doc does not make obvious.
111
60
 
112
61
  ### Shortcode Nesting Rules
113
62
 
@@ -140,37 +89,12 @@ Add custom public-site JS to `public/js/site.js` or new files loaded from `publi
140
89
 
141
90
  ## Core Services (`server/services/`)
142
91
 
143
- | File | Purpose |
144
- |-------------------------|-----------------------------------------------------------------|
145
- | `content.js` | File CRUD for pages and media; reads `config.content.*` |
146
- | `markdown.js` | gray-matter + marked + shortcode pipeline |
147
- | `renderer.js` | Server-side HTML assembly; injects globals into page shell |
148
- | `users.js` | File-based user CRUD (`content/users/{id}.json`) |
149
- | `userTypes.js` | Role seed, load, invalidate, `getRoleLevel()`, `getPermissionsFor()` |
150
- | `roles.js` | Role helpers and hierarchy |
151
- | `userProfiles.js` | User profile CRUD |
152
- | `permissionRegistry.js` | Central permission map; route guards read this at request time |
153
- | `rowAccess.js` | Row-level access control for collections |
154
- | `collections.js` | Collection entry CRUD; delegates I/O to storage adapter |
155
- | `apiTokens.js` | Project-scoped API tokens (`api-tokens` preset) for the external `/api/v1` surface; SHA-256 hash stored, plaintext shown once |
156
- | `apiEndpoints.js` | Custom endpoint definitions (`api-endpoints` preset) — registry/matcher/executor for `/api/x/<project><path>` |
157
- | `adapterRegistry.js` | `getAdapter(slug)` — resolves + caches adapter per collection; `invalidate(slug)` on schema change |
158
- | `adapters/FileAdapter.js` | Default adapter — plain JSON files |
159
- | `adapters/MongoAdapter.js` | Optional Pro adapter — native MongoDB driver; `cms_` prefix |
160
- | `connectionManager.js` | `initialise(cfg)`, `getDb(name)`, `shutdown()` — Mongo connections |
161
- | `blocks.js` | Reusable block CRUD (`content/blocks/`) |
162
- | `forms.js` | Form definition CRUD (`content/forms/`) |
163
- | `views.js` | Saved view CRUD |
164
- | `versions.js` | Page version history (`content/versions/`) |
165
- | `actions.js` | Collection action handlers |
166
- | `hooks.js` | Plugin hook registry (`registerShortcode`, `registerSanitizeRules`, etc.) |
167
- | `presetCollections.js` | Preset schema seeding and management |
168
- | `email.js` | SMTP mail sending (uses `config/site.json` SMTP settings) |
169
- | `images.js` | Image processing / thumbnail generation |
170
- | `plugins.js` | Plugin discovery, validation, registration, injection snippets |
171
- | `pluginScaffold.js` | Scaffold new plugins from `plugins/_template` (`POST /api/plugins/scaffold`, pre-enabled, restart to activate) |
172
- | `pluginFiles.js` | In-admin plugin code editor I/O — list/read/write/delete files confined to `plugins/<name>/`, gated by `plugins.develop` |
173
- | `cache/index.js` | Pluggable response cache (Memory/None/Redis drivers) with tag-based invalidation. See `docs/cache.md`. |
92
+ Business logic lives in `server/services/` — `ls` it and read the file you need; names are literal
93
+ (`collections.js`, `users.js`, `markdown.js`, `plugins.js`, …). Non-obvious ones:
94
+
95
+ - `permissionRegistry.js` — central permission map; route guards read the **cache at request time**
96
+ - `hooks.js` — plugin hook registry (`registerShortcode`, `registerSanitizeRules`, `registerMenuLocation`, …)
97
+ - `cache/index.js` — pluggable response cache with tag-based invalidation; see `docs/cache.md`
174
98
 
175
99
  ## Storage Adapters
176
100
 
@@ -198,17 +122,8 @@ Seeded on first run with: `admin`, `manager`, `editor`, `subscriber`.
198
122
 
199
123
  ## Plugin Development
200
124
 
201
- Each plugin needs exactly 3 files:
202
-
203
- ```
204
- plugins/my-plugin/
205
- plugin.json Required manifest (name, displayName, version, description, author, date, icon)
206
- plugin.js Default export: Fastify plugin function
207
- config.js Default export: plain object of config defaults
208
- ```
209
-
210
- See `docs/plugin-development.md` for full plugin API, hooks, and injection points.
211
- Each plugin directory has its own `CLAUDE.md` with routes, storage, and gotchas.
125
+ Plugins live in `plugins/<name>/` — see `plugins/CLAUDE.md` (loaded when working there) for the required
126
+ file structure, and `docs/plugin-development.md` for the full API, hooks, and injection points.
212
127
 
213
128
  ## Config Reference
214
129
 
@@ -290,14 +205,9 @@ The single source of truth is `getProjectForPage(urlPath, frontmatterProject)` i
290
205
 
291
206
  **Filtering on list endpoints.** Every artefact list path runs `list.filter(item => canSeeArtefact(user, item))`. Get/write endpoints return 403 when the artefact's project isn't in the user's scope. The Projects section in the admin sidebar always renders — `listProjectsForUser(user)` always includes core, so every site shows at least the Core project.
292
207
 
293
- ## External API & API tokens
294
-
295
- Collections are externally consumable at `/api/v1/:slug[/:id]` — a stable alias of `/api/collections/:slug/public[...]` (same handlers). Per-verb access via `schema.api.<verb>.access`: `'public'`, a role name (JWT), or `'token'`. Token mode is **strict** — only a valid `dcms_<64 hex>` Bearer token is accepted (never a JWT), and a token never satisfies a role mode. `schema.api.read.fields` optionally allowlists which `data` fields public/external reads return (admin endpoints unaffected).
296
-
297
- Tokens live in the `api-tokens` **preset collection** (always file-based and undeletable — both derived automatically from `PRESETS`). Each token is bound to one project (`data.project`, immutable) and only works on collections whose `resolveArtefactProject(schema)` matches; optional `scopes: [{collection, verbs[]}]` narrow it further. Service: `server/services/apiTokens.js` — SHA-256 hash stored, plaintext returned once by `createToken()`, in-memory validate cache invalidated via the `collection:entry*` hooks. Admin routes: `server/routes/api/api-tokens.js` (projects.js DI pattern). Admin UI: System → API Tokens (`admin/js/views/api-tokens.js`). Scaffolder recipes may declare `apiTokens: [{name, scopes?}]` — generated at apply time under the recipe's project, idempotent on re-apply, plaintext surfaced once in `created.apiTokens`.
298
-
299
- Permission family `api-tokens.{read,create,update,delete}`: custom roles are **not back-filled** (same gotcha as Menus/Projects), but `roles.js seed()` self-heals every BASE role against its seed definition at boot (append-only; root's seed = the full registry, and the base `admin` seed includes `api-tokens` + `api-endpoints` since 0.25.1). A seeded permission removed from a base role is re-added at boot — use a custom role to run with less. `ensureSidebarItem()` in `sidebar-migration.js` appends the System → API Tokens entry on existing installs (no-op when the item's URL already exists anywhere in the persisted menu). Note: `admin/js/views/collection-editor.js` was de-minified (identifiers still mangled) so the API-access tab could gain the Token mode + read-fields input.
300
-
301
- ## Custom API endpoints (API Builder)
208
+ ## External API & API tokens, custom API endpoints
302
209
 
303
- Named endpoints over collection data at `/api/x/<project><path>` — e.g. `/api/x/world-cup/fixtures-day/:date`. Definitions are **data, never code**: entries in the `api-endpoints` preset collection binding a path (with `:param` segments) to a collection query — filter clauses whose values may embed `{{params.x}}` (required → 400 when missing) or `{{query.x}}` (optional → clause dropped), sort/limit, a read field allowlist, `mode: list|single`, and `auth: public|token|<role>`. One catch-all route (`server/routes/api/endpoints-public.js`, `fastify.get('/x/*')`) matches at runtime via the compiled registry in `server/services/apiEndpoints.js` (static segments beat `:param`; registry invalidated via the `collection:entry*` hooks + own mutations). Auth reuses `checkPublicAccess` from `routes/api/collections.js` (now exported) with a synthesized schema, so token project binding/scopes and the field allowlist are the same battle-tested path as `/api/v1`. Public-auth responses are cached with tags `collection:<data collection>` + `collection:api-endpoints` — both invalidated automatically by the service layer on entry/definition writes. Endpoints are the 10th project artefact type (`apis` in `getArtefactsForProject`; skipped by untag-all since the project IS the URL namespace; a project with endpoints refuses deletion). Admin: **Data → API Builder** (`admin/js/views/api-endpoints.js` list, `api-endpoint-editor.js` builder with try-it console — raw `fetch`, tests the SAVED definition only). Validation refuses system-managed collections (would leak `api-tokens` hashes), duplicate path shapes per project (`/a/:x` ≡ `/a/:y` → 409), and unknown roles/ops. Scaffolder block: `apiEndpoints: [{path, collection, filter, ...}]`, idempotent by path shape. Permission family `api-endpoints.*` — in the base super-admin/admin seeds (boot self-heal); custom roles need a manual grant. Since 0.25.1 an endpoint may only expose collections resolving to **its own project or core** (server 400s cross-project bindings; routes 403 collections outside the caller's scope; the builder's picker filters to match).
210
+ The external surface — `/api/v1/:slug` collection access, project-scoped API tokens, and API Builder
211
+ endpoints at `/api/x/<project><path>` — has its own contracts (strict token auth, project binding,
212
+ read-field allowlists, cross-project refusals). Full detail in the **`domma-api-surface` skill**
213
+ (`.claude/skills/domma-api-surface/SKILL.md`) — load it before touching any of that.
@@ -1,9 +1,9 @@
1
1
  /*!
2
- * Domma Tools CSS v0.29.3
2
+ * Domma Tools CSS v0.39.0
3
3
  * Dynamic Object Manipulation & Modeling API
4
4
  * (c) 2026 Darryl Waterhouse & DCBW-IT
5
- * Built: 2026-07-01T10:03:02.866Z
6
- * Commit: 63e2f20
5
+ * Built: 2026-08-09T13:58:02.916Z
6
+ * Commit: 757ca75
7
7
  */
8
8
 
9
9
  /*!
@@ -1260,7 +1260,7 @@
1260
1260
  display: flex;
1261
1261
  flex-direction: column;
1262
1262
  height: 100%;
1263
- background: var(--dm-bg);
1263
+ background: var(--dm-background);
1264
1264
  color: var(--dm-text);
1265
1265
  font-family: var(--dm-font-sans);
1266
1266
  border: 1px solid var(--dm-border);
@@ -1274,7 +1274,7 @@
1274
1274
  justify-content: space-between;
1275
1275
  align-items: center;
1276
1276
  padding: 1rem;
1277
- background: var(--dm-bg-secondary);
1277
+ background: var(--dm-surface-secondary);
1278
1278
  border-bottom: 1px solid var(--dm-border);
1279
1279
  gap: 1rem;
1280
1280
  }
@@ -1301,7 +1301,7 @@
1301
1301
  padding: 0.5rem;
1302
1302
  border: 1px solid var(--dm-border);
1303
1303
  border-radius: var(--dm-radius-sm);
1304
- background: var(--dm-bg);
1304
+ background: var(--dm-background);
1305
1305
  color: var(--dm-text);
1306
1306
  font-size: 0.875rem;
1307
1307
  width: 200px;
@@ -1498,7 +1498,7 @@
1498
1498
 
1499
1499
  /* Field Cards */
1500
1500
  .sb-field-card {
1501
- background: var(--dm-bg-secondary);
1501
+ background: var(--dm-surface-secondary);
1502
1502
  border: 2px solid var(--dm-border);
1503
1503
  border-radius: var(--dm-radius-md);
1504
1504
  padding: 1rem;
@@ -1546,7 +1546,7 @@
1546
1546
  .sb-field-type {
1547
1547
  font-size: 0.75rem;
1548
1548
  color: var(--dm-text-secondary);
1549
- background: var(--dm-bg);
1549
+ background: var(--dm-background);
1550
1550
  padding: 0.25rem 0.5rem;
1551
1551
  border-radius: var(--dm-radius-sm);
1552
1552
  }
@@ -1583,7 +1583,7 @@
1583
1583
  .sb-field-controls button {
1584
1584
  flex: 1;
1585
1585
  padding: 0.25rem 0.5rem;
1586
- background: var(--dm-bg);
1586
+ background: var(--dm-background);
1587
1587
  border: 1px solid var(--dm-border);
1588
1588
  border-radius: var(--dm-radius-sm);
1589
1589
  color: var(--dm-text);
@@ -1930,7 +1930,7 @@
1930
1930
  border-left: 1px solid var(--dm-border);
1931
1931
  overflow-y: auto;
1932
1932
  padding: 1rem;
1933
- background: var(--dm-bg);
1933
+ background: var(--dm-background);
1934
1934
  }
1935
1935
 
1936
1936
  .sb-state-title {
@@ -1946,7 +1946,7 @@
1946
1946
  font-family: var(--dm-font-mono);
1947
1947
  font-size: 0.75rem;
1948
1948
  color: var(--dm-text);
1949
- background: var(--dm-bg-secondary);
1949
+ background: var(--dm-surface-secondary);
1950
1950
  padding: 0.75rem;
1951
1951
  border-radius: var(--dm-radius-sm);
1952
1952
  overflow-x: auto;
@@ -1970,7 +1970,7 @@
1970
1970
  padding: 0.5rem 1rem;
1971
1971
  border: 1px solid var(--dm-border);
1972
1972
  border-radius: var(--dm-radius-sm);
1973
- background: var(--dm-bg);
1973
+ background: var(--dm-background);
1974
1974
  color: var(--dm-text);
1975
1975
  cursor: pointer;
1976
1976
  font-size: 0.875rem;
@@ -2180,7 +2180,7 @@
2180
2180
  }
2181
2181
 
2182
2182
  .sb-export-output {
2183
- background: var(--dm-bg-secondary);
2183
+ background: var(--dm-surface-secondary);
2184
2184
  border: 1px solid var(--dm-border);
2185
2185
  border-radius: var(--dm-radius-md);
2186
2186
  padding: 1rem;
@@ -2203,7 +2203,7 @@
2203
2203
  padding: 1rem;
2204
2204
  border: 1px solid var(--dm-border);
2205
2205
  border-radius: var(--dm-radius-md);
2206
- background: var(--dm-bg-secondary);
2206
+ background: var(--dm-surface-secondary);
2207
2207
  color: var(--dm-text);
2208
2208
  font-family: var(--dm-font-mono);
2209
2209
  font-size: 0.875rem;
@@ -2228,7 +2228,7 @@
2228
2228
  justify-content: space-between;
2229
2229
  align-items: center;
2230
2230
  padding: 1rem;
2231
- background: var(--dm-bg-secondary);
2231
+ background: var(--dm-surface-secondary);
2232
2232
  border: 1px solid var(--dm-border);
2233
2233
  border-radius: var(--dm-radius-md);
2234
2234
  }
@@ -2248,7 +2248,7 @@
2248
2248
  padding: 0.75rem;
2249
2249
  border: 1px solid var(--dm-border);
2250
2250
  border-radius: var(--dm-radius-sm);
2251
- background: var(--dm-bg);
2251
+ background: var(--dm-background);
2252
2252
  color: var(--dm-text);
2253
2253
  font-size: 1rem;
2254
2254
  }