domma-cms 1.6.60 → 1.7.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.
Files changed (137) hide show
  1. package/CHANGELOG.md +4344 -0
  2. package/README.md +1 -1
  3. package/admin/dist/domma/domma-tools.css +3 -3
  4. package/admin/dist/domma/domma-tools.min.js +3 -3
  5. package/admin/js/lib/effect-defs.js +1 -1
  6. package/admin/js/lib/effects-builder.js +2 -2
  7. package/admin/js/lib/page-editor-reference.js +5 -5
  8. package/admin/js/lib/shortcode-tags.js +1 -1
  9. package/admin/js/templates/docs/usage-shortcodes.html +39 -0
  10. package/docs/DommaFeatures.md +3005 -0
  11. package/docs/MultiTenant.md +176 -0
  12. package/docs/api-reference.md +1667 -0
  13. package/docs/cache.md +96 -0
  14. package/docs/configuration.md +1811 -0
  15. package/docs/editions-and-licences.md +190 -0
  16. package/docs/getting-started.md +201 -0
  17. package/docs/guides/form-triggers.md +260 -0
  18. package/docs/guides/mongodb-storage-adapter.md +282 -0
  19. package/docs/guides/multi-page-forms.md +121 -0
  20. package/docs/images/scribe-composer.png +0 -0
  21. package/docs/markdown-shortcodes.md +1738 -0
  22. package/docs/pentest-report-2026-04-03.md +181 -0
  23. package/docs/plans/2026-02-28-admin-topbar-login-forms.md +349 -0
  24. package/docs/plans/2026-02-28-domma-ui-polish.md +286 -0
  25. package/docs/plans/2026-02-28-reset-seed-fresh-commands.md +520 -0
  26. package/docs/plans/2026-03-02-cookie-consent-design.md +86 -0
  27. package/docs/plans/2026-03-02-cookie-consent-plugin.md +453 -0
  28. package/docs/plans/2026-03-02-tooltip-sweep-and-back-to-top-plugin-design.md +126 -0
  29. package/docs/plans/2026-03-02-tooltip-sweep-back-to-top.md +427 -0
  30. package/docs/plans/2026-03-05-cookie-consent-btt-core.md +115 -0
  31. package/docs/plans/2026-03-05-custom-css-migrate-to-core-design.md +35 -0
  32. package/docs/plans/2026-03-05-custom-css-migrate-to-core.md +352 -0
  33. package/docs/plans/2026-03-05-scribe-script-shortcode.md +373 -0
  34. package/docs/plans/2026-03-05-user-types-design.md +139 -0
  35. package/docs/plans/2026-03-05-user-types-implementation.md +1035 -0
  36. package/docs/plans/2026-03-09-page-editor-view-cta-inserts-design.md +78 -0
  37. package/docs/plans/2026-03-09-page-editor-view-cta-inserts.md +388 -0
  38. package/docs/plans/2026-03-09-pro-documentation-design.md +80 -0
  39. package/docs/plans/2026-03-09-pro-documentation.md +1017 -0
  40. package/docs/plans/2026-06-10-core-project-design.md +240 -0
  41. package/docs/plugin-development.md +827 -0
  42. package/docs/recruitment-recipe.md +501 -0
  43. package/docs/scaffolding.md +312 -0
  44. package/docs/screenshots/calculator-floating.png +0 -0
  45. package/docs/screenshots/calculator-standard.png +0 -0
  46. package/docs/screenshots/email-settings.png +0 -0
  47. package/docs/screenshots/messages-count.png +0 -0
  48. package/docs/structure/user-types.md +107 -0
  49. package/docs/superpowers/plans/2026-05-25-admin-sidebar-via-menus.md +1112 -0
  50. package/docs/superpowers/plans/2026-06-08-navbar-mobile-drawer-and-menu-enrichment.md +1161 -0
  51. package/docs/superpowers/plans/2026-06-25-domma-docs-project.md +695 -0
  52. package/docs/superpowers/plans/2026-06-26-surveys-HANDOFF.md +68 -0
  53. package/docs/superpowers/plans/2026-06-26-surveys-pro-plugin.md +1902 -0
  54. package/docs/superpowers/plans/2026-08-19-collection-context-sort-export.md +1577 -0
  55. package/docs/superpowers/plans/2026-10-09-content-api.md +1192 -0
  56. package/docs/superpowers/specs/2026-03-12-form-followup-actions-design.md +152 -0
  57. package/docs/superpowers/specs/2026-03-12-page-editor-form-insert-and-per-page-theme-design.md +115 -0
  58. package/docs/superpowers/specs/2026-03-17-nav-hidden-items-design.md +85 -0
  59. package/docs/superpowers/specs/2026-03-19-accordion-insert-design.md +175 -0
  60. package/docs/superpowers/specs/2026-03-25-static-block-shortcode-design.md +153 -0
  61. package/docs/superpowers/specs/2026-03-25-text-shortcode-design.md +168 -0
  62. package/docs/superpowers/specs/2026-04-05-card-builder-design.md +315 -0
  63. package/docs/superpowers/specs/2026-04-06-insert-builders-design.md +278 -0
  64. package/docs/superpowers/specs/2026-04-06-layouts-design.md +226 -0
  65. package/docs/superpowers/specs/2026-04-07-blog-plugin-design.md +300 -0
  66. package/docs/superpowers/specs/2026-04-07-calendar-plugin-design.md +538 -0
  67. package/docs/superpowers/specs/2026-04-17-components-area-design.md +368 -0
  68. package/docs/superpowers/specs/2026-05-02-effects-slideover-overhaul-design.md +193 -0
  69. package/docs/superpowers/specs/2026-05-06-dashboard-rebuild-design.md +257 -0
  70. package/docs/superpowers/specs/2026-05-24-menus-design.md +466 -0
  71. package/docs/superpowers/specs/2026-05-25-admin-sidebar-via-menus-design.md +358 -0
  72. package/docs/superpowers/specs/2026-05-25-projects-design.md +380 -0
  73. package/docs/superpowers/specs/2026-06-08-navbar-mobile-drawer-and-menu-enrichment-design.md +292 -0
  74. package/docs/superpowers/specs/2026-06-08-sidebar-tools-plugins-folders-design.md +59 -0
  75. package/docs/superpowers/specs/2026-06-25-domma-docs-project-design.md +192 -0
  76. package/docs/superpowers/specs/2026-06-26-domma-charts-upstream-proposal.md +44 -0
  77. package/docs/superpowers/specs/2026-06-26-manager-scheduler-upstream-proposal.md +46 -0
  78. package/docs/superpowers/specs/2026-06-26-surveys-pro-plugin-design.md +206 -0
  79. package/docs/superpowers/specs/2026-07-02-shortcode-context-menu-design.md +96 -0
  80. package/docs/superpowers/specs/2026-08-19-collection-context-sort-export-design.md +330 -0
  81. package/docs/superpowers/specs/2026-09-22-dcms-marketplace-design.md +1150 -0
  82. package/docs/superpowers/specs/2026-10-09-content-api-design.md +325 -0
  83. package/docs/superpowers/specs/plans/2026-03-12-form-followup-actions.md +624 -0
  84. package/docs/superpowers/specs/plans/2026-03-12-page-editor-form-insert-and-per-page-theme.md +125 -0
  85. package/docs/superpowers/specs/plans/2026-03-17-nav-hidden-items.md +578 -0
  86. package/docs/superpowers/specs/plans/2026-03-19-accordion-insert.md +635 -0
  87. package/docs/superpowers/specs/plans/2026-03-25-text-shortcode.md +545 -0
  88. package/docs/superpowers/specs/plans/2026-04-05-card-builder.md +1970 -0
  89. package/docs/superpowers/specs/plans/2026-04-06-insert-builders.md +1049 -0
  90. package/docs/superpowers/specs/plans/2026-04-06-layouts.md +1015 -0
  91. package/docs/superpowers/specs/plans/2026-04-07-calendar-plugin.md +1526 -0
  92. package/docs/superpowers/specs/plans/2026-04-08-plugin-monetisation-split.md +242 -0
  93. package/docs/superpowers/specs/plans/2026-04-17-components-area.md +2038 -0
  94. package/docs/superpowers/specs/plans/2026-05-02-effects-slideover-overhaul.md +2335 -0
  95. package/docs/superpowers/specs/plans/2026-05-06-dashboard-rebuild.md +1999 -0
  96. package/docs/superpowers/specs/plans/2026-05-17-cache-system.md +1436 -0
  97. package/docs/superpowers/specs/plans/2026-05-24-menus.md +3096 -0
  98. package/docs/superpowers/specs/plans/2026-05-25-projects.md +2869 -0
  99. package/docs/superpowers/specs/plans/2026-09-22-marketplace-step2-consolidation.md +505 -0
  100. package/docs/superpowers/specs/plans/2026-09-22-marketplace-step3-catalogue-and-key.md +154 -0
  101. package/docs/superpowers/specs/plans/2026-09-22-tools-promotion.md +260 -0
  102. package/docs/theming.md +320 -0
  103. package/package.json +2 -2
  104. package/public/js/celebrations/core/canvas.js +1 -1
  105. package/public/js/celebrations/index.js +1 -1
  106. package/public/js/celebrations/licenses/ABeeZee-Regular-OFL.txt +93 -0
  107. package/public/js/celebrations/licenses/Allura-Regular-OFL.txt +93 -0
  108. package/public/js/celebrations/message-attrs.mjs +1 -0
  109. package/public/js/celebrations/themes/anniversary.js +1 -0
  110. package/public/js/celebrations/themes/autumn.js +1 -0
  111. package/public/js/celebrations/themes/birthday.js +1 -0
  112. package/public/js/celebrations/themes/butterfly-flight.js +1 -0
  113. package/public/js/celebrations/themes/calendar.js +1 -0
  114. package/public/js/celebrations/themes/chinese-new-year-dates.js +1 -0
  115. package/public/js/celebrations/themes/chinese-new-year.js +1 -0
  116. package/public/js/celebrations/themes/christmas.js +1 -1
  117. package/public/js/celebrations/themes/easter.js +1 -0
  118. package/public/js/celebrations/themes/garden-drawing.js +1 -0
  119. package/public/js/celebrations/themes/garden.js +1 -0
  120. package/public/js/celebrations/themes/graduation.js +1 -0
  121. package/public/js/celebrations/themes/greeting.js +1 -0
  122. package/public/js/celebrations/themes/make-it-rain.js +1 -0
  123. package/public/js/celebrations/themes/money-rain.js +1 -0
  124. package/public/js/celebrations/themes/new-year.js +1 -0
  125. package/public/js/celebrations/themes/occasion-drawing.js +1 -0
  126. package/public/js/celebrations/themes/occasions.js +1 -0
  127. package/public/js/celebrations/themes/rain-control.js +1 -0
  128. package/public/js/celebrations/themes/sparkler-font-data.js +1 -0
  129. package/public/js/celebrations/themes/sparkler-font.js +1 -0
  130. package/public/js/celebrations/themes/sparkler-writing.js +1 -0
  131. package/public/js/celebrations/themes/summer-garden.js +1 -0
  132. package/public/js/celebrations/themes/winter.js +1 -0
  133. package/public/js/celebrations/trait-data.mjs +1 -1
  134. package/public/js/effects.js +1 -1
  135. package/scripts/build.js +4 -0
  136. package/server/services/markdown.js +5 -2
  137. package/server/templates/page.html +1 -1
@@ -0,0 +1,240 @@
1
+ # Core Project - Design Document
2
+
3
+ **Date:** 2026-06-10
4
+ **Status:** Implemented (2026-06-10)
5
+ **Approach:** Core by exclusion - a seeded, protected `core` project that owns every artefact not claimed by another project. No stamping, no data migration.
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ Every site gains a built-in **Core** project representing the core site itself. All
12
+ artefacts that are not attached to another project belong to Core - by **resolution
13
+ fallback**, not by tagging. The Core project record is seeded at boot (create-if-absent),
14
+ cannot be deleted, and its slug/rootUrl are locked.
15
+
16
+ This completes the Projects model: "untagged" stops being a half-state that means both
17
+ "site-wide" and "the main site". Instead, the main site has a real identity - a sidebar
18
+ entry, an overview page with artefact counts, and per-type filtered lists - exactly like
19
+ any scaffolded project.
20
+
21
+ ### Why fallback, not stamping
22
+
23
+ The obvious implementation (write `meta.project: 'core'` onto every existing artefact and
24
+ every initial artefact at site generation) was considered and rejected:
25
+
26
+ 1. **Access-control regression.** `canSeeArtefact()` treats untagged artefacts as visible
27
+ to *everyone*, including scoped users. A recipe-seeded user with
28
+ `projects: ['onboarding']` currently sees the navbar menu, the site pages, and shared
29
+ blocks because they are untagged. Stamping them `core` would hide all of it from every
30
+ scoped user on every site, on upgrade day.
31
+ 2. **Migration burden.** Stamping requires rewriting page frontmatter, menu/form/block/
32
+ collection meta, and a guarded one-shot migration per site - and only fixes the past.
33
+ Any artefact created later without a tag (plugins, API clients, hand-edited files)
34
+ becomes a stray again.
35
+ 3. **Fallback fixes the invariant.** "Not in another project ⇒ in Core" holds forever,
36
+ for past and future artefacts, with zero data rewrites. The retrofit to existing sites
37
+ is simply the CMS update itself.
38
+
39
+ ---
40
+
41
+ ## Data Model
42
+
43
+ ### Seeded record
44
+
45
+ `content/collections/projects/data.json` gains one seeded entry:
46
+
47
+ ```json
48
+ {
49
+ "slug": "core",
50
+ "name": "Core",
51
+ "description": "The core site - everything not assigned to another project.",
52
+ "icon": "home",
53
+ "rootUrl": "/",
54
+ "sortOrder": -1,
55
+ "protected": true
56
+ }
57
+ ```
58
+
59
+ - **Locked fields:** `slug` (already immutable), `rootUrl`, `protected`.
60
+ - **Editable fields:** `name`, `description`, `icon`, `sortOrder`.
61
+ - `sortOrder: -1` pins Core first in the sidebar by default; admins may re-order.
62
+ - `rootUrl: '/'` is **descriptive only** - resolution does not depend on it (see below).
63
+ `validateProject()` currently rejects `rootUrl === '/'`; the rule is relaxed to allow
64
+ it for the `core` slug only.
65
+
66
+ ### Constant + display flag
67
+
68
+ Enforcement follows the `BASE_ROLE_NAMES` precedent in `roles.js`:
69
+
70
+ ```js
71
+ // server/services/projects.js
72
+ export const CORE_PROJECT_SLUG = 'core';
73
+ ```
74
+
75
+ The constant is the enforcement mechanism (guards compare against it); the
76
+ `protected: true` field on the record is a display hint so the admin UI never hardcodes
77
+ the slug. The projects preset schema in `presetCollections.js` gains a
78
+ `protected` (boolean, default `false`) field.
79
+
80
+ ---
81
+
82
+ ## Seeding
83
+
84
+ A new `seedCoreProject()` in `server/services/projects.js`, called from `server.js`
85
+ immediately after `presetCollections.seedAll()`:
86
+
87
+ 1. List projects; if any entry has `slug === 'core'` → **adopt**: ensure
88
+ `protected: true` is set, leave all other fields untouched (a pre-existing user-created
89
+ "core" project keeps its name/icon/rootUrl - resolution does not depend on them).
90
+ 2. Otherwise → create the seeded record above.
91
+
92
+ Idempotent and record-level (checks for the slug, **not** for `data.json` existence -
93
+ `seedAll()` already creates an empty `data.json`, and retrofitted sites have existing
94
+ project entries that must be preserved).
95
+
96
+ This single hook is the entire retrofit: every existing site gets Core on first boot
97
+ after upgrading. No per-site scripts, no `.bak` files. Fleet sites that predate Projects
98
+ entirely (e.g. older deployments) retrofit by upgrading to current - same path.
99
+
100
+ ---
101
+
102
+ ## Resolution Changes (`server/services/projects.js`)
103
+
104
+ ### Pages - `getProjectForPage(urlPath, explicitProject)`
105
+
106
+ | Rule | Before | After |
107
+ |---|---|---|
108
+ | `project: null` in frontmatter | `null` (untagged opt-out) | `'core'` - opt-out is meaningless once Core owns the floor; treated as core and deprecated in docs |
109
+ | `project: '<slug>'` | use it | unchanged |
110
+ | Missing field, prefix match | longest `rootUrl` prefix wins | unchanged - scaffolded projects keep winning (their rootUrls are longer than nothing) |
111
+ | Missing field, no match | `null` | `'core'` |
112
+
113
+ Implementation: the matcher loop is untouched; the final return becomes
114
+ `return best ?? CORE_PROJECT_SLUG`. Core deliberately does **not** participate in prefix
115
+ matching - `startsWith(rootUrl + '/')` with `rootUrl: '/'` would test `startsWith('//')`
116
+ and match nothing anyway. Fallback is both simpler and correct.
117
+
118
+ Safety: if the core record is somehow absent (mid-boot, hand-deleted data file), return
119
+ `null` as before; the next boot re-seeds.
120
+
121
+ ### Non-page artefacts
122
+
123
+ A small helper, used everywhere a project grouping is displayed or enumerated:
124
+
125
+ ```js
126
+ export function resolveArtefactProject(artefact) {
127
+ return artefact?.meta?.project || CORE_PROJECT_SLUG;
128
+ }
129
+ ```
130
+
131
+ `getArtefactsForProject(slug)` switches its eight `meta.project === slug` comparisons to
132
+ `resolveArtefactProject(x) === slug`. The pages branch already works unchanged (it calls
133
+ `getProjectForPage`, which now falls back to core). Net effect:
134
+ `getArtefactsForProject('core')` enumerates everything unclaimed - the "Core site"
135
+ overview - and other projects' results are byte-identical to today.
136
+
137
+ ### Access control - explicitly unchanged
138
+
139
+ `canSeeArtefact()` gains one clause and **no semantic change** for existing data:
140
+
141
+ ```js
142
+ const project = artefact?.meta?.project;
143
+ if (!project || project === CORE_PROJECT_SLUG) return true;
144
+ ```
145
+
146
+ **Core is an organisational bucket, never an access boundary.** Untagged artefacts and
147
+ explicitly-`core`-tagged artefacts are visible to all authenticated users, exactly as
148
+ untagged is today. Scoped users keep their navbar, site pages, and shared blocks.
149
+ "Core-only" user scoping is out of scope for this design; `projects: []` (no restriction)
150
+ remains the way to express it. If Core ever needs to be restrictable, that is a separate
151
+ design with real consequences (every scoped recipe user would lose the public site).
152
+
153
+ `listProjectsForUser()` always includes Core for any authenticated caller (consistency:
154
+ if `canSeeArtefact` says yes to core artefacts, the project list says yes to the
155
+ project). Consequence: the Projects sidebar section - currently hidden when the list is
156
+ empty - now renders on **every** site, with at least Core in it. This is the desired
157
+ headline behaviour.
158
+
159
+ ---
160
+
161
+ ## Protection (delete / update / untag)
162
+
163
+ Guards at both layers, following the roles precedent (`Cannot remove built-in role`):
164
+
165
+ | Operation | Service (`projects.js`) | Route (`routes/api/projects.js`) |
166
+ |---|---|---|
167
+ | `DELETE /projects/core` | `deleteProject()` throws `Cannot delete the core project` | 403 **before** the artefact-count enumeration (don't waste the scan; the existing 409-when-tagged guard does not protect core because untag-all is an escape hatch) |
168
+ | `POST /projects/core/untag-all` | `untagAllForProject()` throws | 403 - meaningless for core: resolution re-claims everything instantly |
169
+ | `PUT /projects/core` | `updateProject()` rejects changes to `rootUrl` and `protected`; accepts `name`, `description`, `icon`, `sortOrder` | 400 with field-specific message |
170
+
171
+ Note the interaction with **other** projects' untag-all: artefacts untagged from any
172
+ project now land in Core (previously "untagged"). Same data state, new label - the word
173
+ "untagged" disappears from UI language in favour of "Core".
174
+
175
+ ---
176
+
177
+ ## Scaffolder & Site Generation
178
+
179
+ **No scaffolder changes.** Recipes with a `project` block keep creating their project and
180
+ stamping `meta.project: '<namespace>'` exactly as today; their longer `rootUrl` prefixes
181
+ keep winning page resolution over the core fallback. Recipe-seeded users
182
+ (`projects: ['<namespace>']`) continue to see core artefacts via the access rule above.
183
+
184
+ **No site-generation changes.** `npx` generation, the Domma CMS Manager, payment-flow
185
+ provisioning, and injection all produce untagged initial artefacts as today - boot
186
+ seeding plus resolution fallback files them under Core automatically. `scripts/setup.js`,
187
+ `scripts/reset.js`, `scripts/fresh.js` are untouched (reset wipes content; the next boot
188
+ re-seeds Core).
189
+
190
+ ---
191
+
192
+ ## Admin UI (`admin/` - upstream-replaced, ships with the CMS release)
193
+
194
+ | View | Change |
195
+ |---|---|
196
+ | `views/projects.js` (list) | Render a "System" badge on `protected: true` rows; hide the delete button |
197
+ | `views/project-settings.js` | When `protected`: disable the Root URL input, hide the danger zone (untag-all + delete) entirely |
198
+ | `views/project-detail.js` | No change - counts come from `GET /projects/:slug/artefacts`, which now resolves correctly for core |
199
+ | Sidebar Projects section | No code change needed; it now always renders because the list is never empty |
200
+ | Quick-create under Core | Omit the `meta.project` stamp (artefacts stay in the natural default state, which *is* Core); an explicit `'core'` tag is treated identically if one is ever written |
201
+
202
+ ---
203
+
204
+ ## Tests
205
+
206
+ - **Seeding:** creates core when absent; idempotent on re-boot; adopts a pre-existing
207
+ `core` slug without clobbering fields; survives alongside existing project entries.
208
+ - **Resolution:** `getProjectForPage('/about', undefined)` → `'core'`;
209
+ `project: null` → `'core'`; longest-prefix project still beats core;
210
+ `resolveArtefactProject({})` → `'core'`.
211
+ - **Enumeration:** `getArtefactsForProject('core')` counts untagged artefacts of all nine
212
+ types; other projects' counts unchanged (regression guard).
213
+ - **Access:** extend `tests/services/projects-scope.test.js` - scoped user sees
214
+ core-resolved and explicitly-core-tagged artefacts; `listProjectsForUser` includes core
215
+ for scoped users.
216
+ - **Protection:** service throws on delete/untag-all of core; routes return 403; update
217
+ rejects `rootUrl`/`protected` changes but accepts `name`/`icon`.
218
+
219
+ ---
220
+
221
+ ## Task Breakdown
222
+
223
+ | # | Task | Files | Size |
224
+ |---|---|---|---|
225
+ | 1 | `CORE_PROJECT_SLUG`, `resolveArtefactProject()`, `seedCoreProject()`, fallback in `getProjectForPage`, resolution scan in `getArtefactsForProject`, guards in `deleteProject`/`updateProject`/`untagAllForProject`, `canSeeArtefact` clause, `listProjectsForUser` inclusion, relax `validateProject` for core's `/` | `server/services/projects.js` | ~50 lines |
226
+ | 2 | `protected` field on projects preset schema | `server/services/presetCollections.js` | ~2 lines |
227
+ | 3 | Call `seedCoreProject()` at boot after `seedAll()` | `server/server.js` | ~3 lines |
228
+ | 4 | 403 guards on DELETE and untag-all; PUT field rejection passthrough | `server/routes/api/projects.js` | ~15 lines |
229
+ | 5 | System badge, hidden delete, locked settings fields | `admin/js/views/projects.js`, `project-settings.js` | ~25 lines |
230
+ | 6 | Tests per section above | `tests/services/`, `tests/routes/` | ~150 lines |
231
+ | 7 | Docs: CLAUDE.md Projects section, `docs/configuration.md`, `docs/scaffolding.md` - document core-by-exclusion, deprecate `project: null` | docs | - |
232
+
233
+ ---
234
+
235
+ ## Out of Scope / Future
236
+
237
+ - **Core as an access boundary** - deliberately excluded; see Access control section.
238
+ - **Per-service `untagAllForProject` plumbing for pages/roles/users** - pre-existing
239
+ deferral, unaffected by this design.
240
+ - **Renaming "untagged" UI strings** beyond the projects views - sweep opportunistically.