create-prenta 1.3.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 (49) hide show
  1. package/CHANGELOG.md +964 -0
  2. package/LICENSE +21 -0
  3. package/assets/prisma/migrations/0001_init/migration.sql +941 -0
  4. package/assets/prisma/migrations/0002_media_usage_field_path/migration.sql +11 -0
  5. package/assets/prisma/migrations/0003_ai_crawler_hits/migration.sql +21 -0
  6. package/assets/prisma/migrations/0004_drop_search_vector/migration.sql +4 -0
  7. package/assets/prisma/migrations/0005_script_tag_delivery_controls/migration.sql +8 -0
  8. package/assets/prisma/migrations/0006_totp_last_step/migration.sql +4 -0
  9. package/assets/prisma/migrations/0007_workflow_fk_drop_dead_enums/migration.sql +27 -0
  10. package/assets/prisma/migrations/0008_form_delivery_attempts/migration.sql +29 -0
  11. package/assets/prisma/migrations/0009_webhook_secret_rotation/migration.sql +6 -0
  12. package/assets/prisma/migrations/0010_media_is_decorative/migration.sql +3 -0
  13. package/assets/prisma/migrations/0011_media_content_hash/migration.sql +4 -0
  14. package/assets/prisma/migrations/0012_rename_to_prenta/migration.sql +89 -0
  15. package/assets/prisma/migrations/migration_lock.toml +3 -0
  16. package/assets/prisma/schema.prisma +989 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +182 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/options.d.ts +160 -0
  22. package/dist/options.d.ts.map +1 -0
  23. package/dist/options.js +206 -0
  24. package/dist/options.js.map +1 -0
  25. package/dist/scaffold.d.ts +45 -0
  26. package/dist/scaffold.d.ts.map +1 -0
  27. package/dist/scaffold.js +2163 -0
  28. package/dist/scaffold.js.map +1 -0
  29. package/dist/templates/admin-page.ts.template.d.ts +24 -0
  30. package/dist/templates/admin-page.ts.template.d.ts.map +1 -0
  31. package/dist/templates/admin-page.ts.template.js +145 -0
  32. package/dist/templates/admin-page.ts.template.js.map +1 -0
  33. package/dist/templates/astro-site.d.ts +36 -0
  34. package/dist/templates/astro-site.d.ts.map +1 -0
  35. package/dist/templates/astro-site.js +918 -0
  36. package/dist/templates/astro-site.js.map +1 -0
  37. package/dist/templates/login-page.ts.template.d.ts +14 -0
  38. package/dist/templates/login-page.ts.template.d.ts.map +1 -0
  39. package/dist/templates/login-page.ts.template.js +143 -0
  40. package/dist/templates/login-page.ts.template.js.map +1 -0
  41. package/dist/templates/marketing-custom.d.ts +56 -0
  42. package/dist/templates/marketing-custom.d.ts.map +1 -0
  43. package/dist/templates/marketing-custom.js +814 -0
  44. package/dist/templates/marketing-custom.js.map +1 -0
  45. package/dist/templates/prenta.config.ts.template.d.ts +3 -0
  46. package/dist/templates/prenta.config.ts.template.d.ts.map +1 -0
  47. package/dist/templates/prenta.config.ts.template.js +253 -0
  48. package/dist/templates/prenta.config.ts.template.js.map +1 -0
  49. package/package.json +58 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,964 @@
1
+ # create-actuate-cms
2
+
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5eea995: Product rename: ActuateCMS is now **Prenta**. Every package moves from the
8
+ `@actuate-media/*` scope to `@prenta/*` with simplified names (`@prenta/core`,
9
+ `@prenta/admin`, `create-prenta`), and version numbers continue from where the
10
+ old scope left off.
11
+
12
+ This is a clean break — no backwards-compat aliases:
13
+ - **npm packages**: `@actuate-media/cms-core` → `@prenta/core`,
14
+ `@actuate-media/cms-admin` → `@prenta/admin`, all other packages keep their
15
+ suffix under `@prenta/*`. `create-actuate-cms` → `create-prenta`
16
+ (`npm create prenta`), `create-actuate-astro` → `create-prenta-astro`.
17
+ CLI bins: `actuate`/`actuate-cms` → `prenta`/`prenta-cms`; `actuate-mcp` →
18
+ `prenta-mcp`.
19
+ - **Database**: migration `0012_rename_to_prenta` renames all 36 `actuate_*`
20
+ tables to `prenta_*` in place (plus prefixed constraints, indexes, and
21
+ sequences). Run `prisma migrate deploy` when upgrading.
22
+ - **Runtime identifiers**: `getActuateConfig` → `getPrentaConfig`,
23
+ `handleActuateAPI` → `handlePrentaAPI`, `ActuateClient` → `PrentaClient`,
24
+ `ActuateDB` → `PrentaDB`, config file `actuate.config.ts` →
25
+ `prenta.config.ts`, CSS scopes `.actuate-admin`/`.actuate-canvas` →
26
+ `.prenta-admin`/`.prenta-canvas`, preview protocol `actuate-preview:*` →
27
+ `prenta-preview:*`, webhook headers `X-Actuate-*` → `X-Prenta-*`, session
28
+ cookie `actuate_session` → `prenta_session`, env vars `ACTUATE_*` →
29
+ `PRENTA_*`, purge route `/api/actuate/purge` → `/api/prenta/purge`.
30
+ - **Sessions and webhooks**: all sessions reset on upgrade (cookie + JWT issuer
31
+ changed); webhook consumers must re-verify signatures against the new
32
+ `X-Prenta-*` headers.
33
+
34
+ The old `@actuate-media/*` packages are deprecated on npm and will receive no
35
+ further releases.
36
+
37
+ ## 1.2.4
38
+
39
+ ### Patch Changes
40
+
41
+ - 6ff80a6: Add `@actuate-media/sections-react/testing` (`assertSectionRenderParity`, canvas render options) and wire an Astro-split starter `test:parity` gate so canvas and public orchestrators can't drift into a WYSIWYG lie.
42
+
43
+ ## 1.2.3
44
+
45
+ ### Patch Changes
46
+
47
+ - 73854fc: Add `siteIdentityJsonLd*` / `jsonLdHasOrganization` so any page can emit resolvable WebSite + Organization JSON-LD (fixes dangling `#organization` @ids). Add `assertServingOriginConfigured` ops guard (Launch Gate E) and wire it into the Astro starter middleware + CMS API route boot; BaseLayout falls back to site-identity JSON-LD when page meta lacks Organization.
48
+
49
+ ## 1.2.2
50
+
51
+ ### Patch Changes
52
+
53
+ - 576f076: Fix `blogPostCollection` archivePath footgun: deriving with a new slug/urlPrefix rebases `seo.archivePath` onto the urlPrefix unless set explicitly; `defineConfig` warns on duplicate archive paths. Astro starter/example: hybrid prerender defaults (`404.astro` prerendered; CMS catch-all / home / blog index explicit `prerender = false`).
54
+
55
+ ## 1.2.1
56
+
57
+ ### Patch Changes
58
+
59
+ - dfffe08: Add `publicPageCacheControl()` on `@actuate-media/cms-core/astro/cache` (ADR 0011 cache-static defaults: 1h s-maxage / 24h SWR; preview → `private, no-store`) and wire the Astro starter + example site to use it instead of hardcoded Cache-Control strings. Purge remains path/URL-based per ADR 0011.
60
+
61
+ ## 1.2.0
62
+
63
+ ### Minor Changes
64
+
65
+ - cf97a40: Launch-critical Astro SEO + freshness: `documentPublicPath` / `rewritePublicOrigin`, first-class `AstroPageMeta.robots`, admin/MCP path builders (#739), Yoast child sitemap + XSL proxies with origin rewrite, production purge 503 without CDN credentials, globals purge webhooks, and Launch Gate docs.
66
+
67
+ ## 1.1.0
68
+
69
+ ### Minor Changes
70
+
71
+ - 7230d21: Scaffolded Astro public sites (`astro-split` workspaces and `--framework astro-site`) now pin Astro 7 (`astro@^7.1`, `@astrojs/vercel@^11`, `@astrojs/react@^6`). This picks up the fixes for three Astro XSS advisories (GHSA-f48w-9m4c-m7f5, GHSA-7pw4-f3q4-r2p2, GHSA-4g3v-8h47-v7g6). No template API changes: `output: 'server'`, the Vercel adapter, middleware, live content collections, and the purge webhook route all work unchanged on Astro 7. Node.js requirement stays 22.12+.
72
+
73
+ ## 1.0.0
74
+
75
+ ### Major Changes
76
+
77
+ - 90f9801: Astro-first scaffolding: `astro-split` is now the default framework and emits a pnpm workspace instead of a nested `site/` folder.
78
+
79
+ **create-actuate-cms (breaking)**
80
+ - Default framework flipped from `next` to `astro-split`. `npm create actuate-cms` now scaffolds a pnpm workspace — Next CMS host in `apps/cms`, Astro public site in `apps/site` — instead of a Next monolith. Pass `--framework next` for the classic monolith (unchanged output).
81
+ - The `astro-split` layout changed from a root Next app with a nested `site/` Astro app to a proper workspace (`pnpm-workspace.yaml`, delegating root scripts, `packageManager: pnpm`). Re-running the scaffolder over an old layout is not supported.
82
+ - `astro-split` + `marketing-custom` (the default template) now compose: the section kit lands in `packages/sections` and all three layers are wired from it — `apps/cms/actuate.config.ts` imports the schema, the admin page imports `sectionRenderers` (via `transpilePackages`), and the Astro site registers the same renderer map. `verify:sections` runs from the root, both apps, and CI.
83
+ - Same-apex routing is now the default: `apps/site/vercel.json` ships rewrites for `/api/cms/*` and the admin/login paths to the CMS host (`CMS_HOST_ORIGIN` placeholder) instead of an opt-in `vercel.preview-proxy.example.json`.
84
+ - The Astro site template ships cache-static delivery: `Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400` on published pages, a `/api/actuate/purge` webhook endpoint built on `createPublishPurgeReceiver` (Cloudflare / Vercel adapters selected by env), and an optional Upstash layer-2 cache that the purge endpoint drains.
85
+ - `ASTRO.md` documents the workspace flow, production routing, and the purge webhook setup (Settings → Webhooks).
86
+
87
+ **create-actuate-astro**
88
+ - `--split` now scaffolds the pnpm workspace (`apps/cms` + `apps/site` + `packages/sections` with the designed-sections standard template). Site-only mode (the default) is unchanged.
89
+
90
+ ## 0.33.2
91
+
92
+ ### Patch Changes
93
+
94
+ - 56c1309: Fix page tag saves silently no-oping when `tags` / `primaryTag` were undeclared (passthrough + scaffold `page-tags` collection), and replace the status-token tag palette with distinct `--tag-*` colors so pink is no longer warning orange.
95
+
96
+ ## 0.33.1
97
+
98
+ ### Patch Changes
99
+
100
+ - f4766d4: Bump `sharp` to `^0.35.0` so consumers inherit the patched libvips line (GHSA-f88m-g3jw-g9cj) instead of resolving `0.34.5` from the previous pin.
101
+
102
+ ## 0.33.0
103
+
104
+ ### Minor Changes
105
+
106
+ - c72bd83: Make **Standard (designed sections)** (`marketing-custom`) the default scaffold template so new projects ship schema + shared admin/public section renderers + seed/verify by default. Minimal built-ins-only kits remain available via `--template default`.
107
+
108
+ ## 0.32.2
109
+
110
+ ### Patch Changes
111
+
112
+ - f676bcc: Resolve View live / View Site from the effective public site URL (`GET /site/public-url`, DB SEO overrides first), warn when that URL matches the CMS admin host, unlock Settings → General site URL overrides for split deploys, and default Astro-split `NEXT_PUBLIC_SITE_URL` to the Astro apex.
113
+
114
+ ## 0.32.1
115
+
116
+ ### Patch Changes
117
+
118
+ - 3d1a79f: Timestamp-bound HMAC webhook signing with skew/replay verification (`verifyActuateWebhook`), delivery-id headers on form webhooks, publish secret reveal + 24h rotation grace, and consumer verification docs (#679).
119
+
120
+ ## 0.32.0
121
+
122
+ ### Minor Changes
123
+
124
+ - 58f3ca1: Split-deploy hardening: denormalize media IDs on document write, regenerate JSON-LD on published updates, include Organization parent for Corporation/OnlineBusiness subtypes, and ship Astro security-headers middleware matching Next baselines.
125
+
126
+ ## 0.31.0
127
+
128
+ ### Minor Changes
129
+
130
+ - cc6ad93: Split-deploy SEO redirects: ship `applyPublicRedirect` / `createAstroRedirectMiddleware`, wire Astro starters and Next scaffold to the shared engine, clarify Never hit vs Stale, and return 409 on duplicate source updates.
131
+
132
+ ## 0.30.1
133
+
134
+ ### Patch Changes
135
+
136
+ - b6945e3: Astro starter: the scaffolded `BaseLayout.astro` now renders the full composed head from `/resolve` — Open Graph tags, `twitter:card`, a robots meta tag (including the forced `noindex` in preview mode), and hreflang alternate links — via a single `meta` prop (`AstroPageMeta` from `toPageMeta()`), and injects JSON-LD with `<Fragment set:html>` instead of an invalid `<div>` inside `<head>`. Static pages can still pass plain `title` / `description` / `canonical` props. `examples/astro-site` re-synced from the template.
137
+
138
+ ## 0.30.0
139
+
140
+ ### Minor Changes
141
+
142
+ - c6b006c: Astro discoverability and best practices: `create-actuate-astro` + `--framework astro-site`, Live Content Collections loader (`@actuate-media/cms-core/astro/content`), islands/preview docs, and fleet first-cutover runbook.
143
+
144
+ ## 0.29.1
145
+
146
+ ### Patch Changes
147
+
148
+ - fe3e76c: Astro public-site starter and `--framework astro-split` `site/` template now pin Astro 6 (`astro@^6.4`, `@astrojs/vercel@^10`, `@astrojs/react@^5`) and require Node.js 22.12+.
149
+
150
+ ## 0.29.0
151
+
152
+ ### Minor Changes
153
+
154
+ - fb89234: Add `--framework next|astro-split` (default `next`). `astro-split` scaffolds a Next CMS host (admin + API, no public catch-all) plus an Astro public site under `site/` that uses `createAstroSiteHelpers`, sections-react, and SEO proxies. Also ships `examples/astro-site` as the reference starter.
155
+
156
+ ## 0.28.14
157
+
158
+ ### Patch Changes
159
+
160
+ - fdfae4c: Include `graphql` + `graphql-yoga` in generated projects.
161
+
162
+ cms-core made these optional peer dependencies (Tier 2), so scaffolded apps now
163
+ declare them directly to keep the `/api/cms/graphql` endpoint working out of the
164
+ box — the same pattern already used for `sharp` and `@vercel/blob`. Remove both
165
+ from `package.json` if you only use the REST API.
166
+
167
+ ## 0.28.13
168
+
169
+ ### Patch Changes
170
+
171
+ - da73f45: Drop the broken `lint` script from scaffolded projects.
172
+
173
+ The scaffolder emitted `"lint": "next lint"` while pinning Next ^16 — but Next 16
174
+ removed the `next lint` subcommand, and the generated project ships no ESLint
175
+ config or `eslint-config-next` dependency, so `npm run lint` errored on a fresh
176
+ install. Removed the script (consumers can add ESLint themselves).
177
+
178
+ (Companion docs fix in the same PR: the "Add to Existing Project" guide now
179
+ installs `@vercel/blob` and `sharp` for Vercel media uploads — `@vercel/blob` is
180
+ a dynamically-imported optional peer whose absence 500s the first upload.)
181
+
182
+ ## 0.28.12
183
+
184
+ ### Patch Changes
185
+
186
+ - 95ea95a: Ship an `actuate-add-section` agent skill in the `marketing-custom` scaffold.
187
+
188
+ New projects get `.cursor/skills/actuate-add-section/SKILL.md` — a prescriptive
189
+ contract that turns a designed section into an editable Actuate section (schema
190
+ in `types.ts` + renderer in `register.tsx`, plus optional block + seed), kept in
191
+ sync. When you ask an agent (Cursor, Claude, …) to "make this section editable"
192
+ in the kit, it follows the contract instead of re-deriving it — so a design
193
+ becomes an editable CMS section without hand-wiring. The skill documents the
194
+ exact field types, the `section.content` renderer pattern, a design→schema
195
+ mapping, and a verify checklist. SECTIONS.md and the Design-First Playbook doc
196
+ reference it.
197
+
198
+ ## 0.28.11
199
+
200
+ ### Patch Changes
201
+
202
+ - cb94bf0: Simplify custom-section authoring in the `marketing-custom` template.
203
+
204
+ A custom section used to be four hand-synced artifacts: a block component, a
205
+ schema entry, a public `register.tsx` renderer, and a separate
206
+ `admin-renderers.tsx` canvas renderer. The public renderer also had a latent bug
207
+ (it destructured a non-existent `content` prop instead of `section.content`).
208
+
209
+ Now a section is just its schema (`types.ts`) plus ONE renderer map
210
+ (`register.tsx`) — a single `sectionRenderers` object, typed as
211
+ `AdminSectionRendererMap`, that drives BOTH the public site and the admin editor
212
+ canvas (the two renderer signatures are structural mirrors, so one component
213
+ serves both; the admin map type is assignable to the public one for
214
+ registration). The separate `admin-renderers.tsx` file and its wiring are gone;
215
+ `verify:sections` and `SECTIONS.md` are updated to match.
216
+
217
+ Also adds a **Design-First Playbook** doc: a repeatable "design → editable CMS
218
+ pages" recipe (Design Inventory → schema + renderer → seed → verify).
219
+
220
+ ## 0.28.10
221
+
222
+ ### Patch Changes
223
+
224
+ - 4421c32: Fix media upload 500 ("Cannot find package 'sharp'") on Vercel.
225
+
226
+ cms-core optimizes uploaded images with `sharp` and marks it a Next
227
+ `serverExternalPackage` (a native module can't be bundled). Because it's also
228
+ dynamically imported (`import(/* webpackIgnore */ 'sharp')`), Next never traces
229
+ it — so unless the app depends on `sharp` directly, Vercel omits it from the
230
+ function bundle and every image upload 500s.
231
+
232
+ Two fixes:
233
+ - `create-actuate-cms` now adds `sharp` to the generated `package.json`
234
+ dependencies (like it already does for `@vercel/blob`), so scaffolded sites
235
+ bundle it.
236
+ - The `/media/upload` handler now degrades gracefully: if optimization throws
237
+ (e.g. sharp is missing), it stores the original image and logs a warning
238
+ instead of returning a 500. Uploads succeed; add `sharp` to re-enable
239
+ webp/resize/blur-placeholder.
240
+
241
+ Existing sites hit by this: `npm install sharp` and redeploy.
242
+
243
+ ## 0.28.9
244
+
245
+ ### Patch Changes
246
+
247
+ - f283357: Implement the AWS platform adapter (`aws()`) at parity with `vercel()`.
248
+
249
+ `@actuate-media/platform-aws` previously threw "not yet implemented" — which
250
+ also meant scaffolded `--platform aws` sites had **no working media storage**
251
+ (`storage: { provider: 's3' }` auto-wires to `aws().storage`). All six adapters
252
+ are now implemented:
253
+ - **Storage** — S3 (`@aws-sdk/client-s3`) with presigned URLs
254
+ (`@aws-sdk/s3-request-presigner`) and CloudFront-aware public URLs. Reads
255
+ `AWS_S3_BUCKET` / `AWS_REGION` (plus the scaffolder's `S3_*` fallback names);
256
+ credentials come from the task/instance IAM role by default.
257
+ - **Database** — RDS/Aurora Postgres via a real `pg` connection pool.
258
+ - **Cache** — ElastiCache (Redis/Valkey) via `ioredis`, with an in-memory
259
+ fallback; tag invalidation delegates to `next/cache`.
260
+ - **Email** — SES (`@aws-sdk/client-ses`), honoring reply-to/from.
261
+ - **Scheduler** — EventBridge Scheduler guidance + `generateEventBridgeConfig()`
262
+ / `toEventBridgeCron()` helpers (standard 5-field → EventBridge 6-field).
263
+ - **Rate limit** — Redis sliding-window log (ZADD/ZREMRANGEBYSCORE/ZCARD) with
264
+ an in-memory fallback.
265
+
266
+ Every AWS SDK is an optional peer dependency, dynamically imported on first use,
267
+ so installing the package pulls in nothing until an adapter runs.
268
+
269
+ The `create-actuate-cms` scaffolder now emits AWS-standard env var names
270
+ (`AWS_S3_BUCKET`, `AWS_REGION`, optional `AWS_S3_PUBLIC_URL`) for `--platform aws`
271
+ and enriches the generated AWS deployment guide (S3 IAM, EventBridge cron).
272
+
273
+ ## 0.28.8
274
+
275
+ ### Patch Changes
276
+
277
+ - cc18833: SEO audit remediation — 47 verified fixes across the SEO system.
278
+
279
+ Highlights: redirect middleware no longer excludes legacy `.html/.php/.aspx`
280
+ URLs (migrations); robots.txt now `Allow`s the default OG image (`/api/cms/og.png`)
281
+ so social shares render; RSS always emits the mandatory channel `<description>`;
282
+ structured-data auto-detection no longer emits invalid `Review/Event/JobPosting`
283
+ (required fields enforced or the node is suppressed) and no longer mis-types
284
+ collections by substring (`newsletters`→NewsArticle etc.); the sitemap endpoint
285
+ no longer selects full document JSON for up to 45k rows; IndexNow submits on
286
+ every publish instead of being swallowed by the sitemap debounce; the exported
287
+ `renderJsonLdScript()` now escapes `<`; the editor no longer shows two
288
+ conflicting scores and the Content-tab analysis now understands section-built
289
+ pages; plus pagination `rel`/canonical, `og:locale` normalization + alternates,
290
+ canonical absolutization, VideoObject/Product/WebSite-SearchAction builders,
291
+ redirect loop/case handling, plugin-seo sitemap caps + real `lastmod`, and
292
+ plugin link-health routed through `safeFetch`. Adds several new exports on
293
+ `@actuate-media/cms-core` (e.g. `buildVideoObjectSchema`, `buildProductSchema`).
294
+
295
+ ## 0.28.7
296
+
297
+ ### Patch Changes
298
+
299
+ - d6665dd: Fix the consumer build produced by `npm create actuate-cms`. Scaffolded projects
300
+ now ship an `.npmrc` with `node-linker=hoisted` so Next.js/Turbopack can resolve
301
+ the published packages' transitive dependencies (Radix/TipTap internals) during
302
+ `next build` — previously a default `pnpm install` failed with "Module not found"
303
+ for packages like `@radix-ui/react-use-controllable-state`. Also fixes the
304
+ generated `actuate.config.ts` to use the media plugin's `allowedTypes` option
305
+ (was the non-existent `allowedMimeTypes`, which failed type-check on build).
306
+ - 041a228: Scaffolded projects no longer migrate the production database from preview
307
+ deploys. The build command now runs `prisma migrate deploy` through
308
+ `scripts/deploy-migrate.mjs`, which applies migrations only on Vercel
309
+ **production** builds (`VERCEL_ENV=production`) — or when `ALLOW_MIGRATE_DEPLOY=true`
310
+ is set explicitly against an isolated preview database. Previously every build
311
+ (including preview deploys from unmerged PRs) ran a bare `prisma migrate deploy`
312
+ against the configured database.
313
+
314
+ ## 0.28.6
315
+
316
+ ### Patch Changes
317
+
318
+ - 4afb589: Fix accent color not applying when AdminRoot is wrapped in an outer `.actuate-admin` by targeting the marked admin shell root for theme overrides, and remove the duplicate wrapper from the scaffold admin page template.
319
+ - 4afb589: Add legitimacy-gated 404 redirect recovery: wire public 404 logging, filter probe URLs before suggesting redirects, connect broken internal links to the SEO audit approve flow, and seed suggestions during audits.
320
+
321
+ ## 0.28.5
322
+
323
+ ### Patch Changes
324
+
325
+ - 163ae1d: Design parity phase 3 (wave 1): split-pane editors, Script Tag delivery controls, Media SEO Audit, Pages tree view, and Dashboard onboarding.
326
+
327
+ **cms-core**
328
+ - `ScriptTag` gains `loadAsync`, `disableInPreview` (default on), and `consentCategory` fields (migration `0005_script_tag_delivery_controls`), accepted and validated on the script-tag API routes.
329
+ - `resolveScriptTagsForPath` accepts `{ preview }` and skips preview-disabled tags; the public `GET /script-tags/resolve` endpoint supports `?preview=1`. `loadAsync` injects `async` into external scripts, and consent-gated tags are wrapped in a bootstrap that waits for the consent plugin's "accept all" before injecting. The resolved response shape is unchanged.
330
+ - `ScriptTagGroup` now preserves `async`/`defer` attributes on external scripts.
331
+ - `GET /media` items include server-derived `seoScore`, `seoGrade`, and `seoIssues` (from `calculateMediaSeoScore`), and `PUT /media/:id` accepts `caption`, `description`, and `focusKeyword`.
332
+
333
+ **cms-admin**
334
+ - New `SplitPaneLayout` / `InspectorPane` primitives implementing the design's inline editor-pane pattern; Script Tags is converted to a split-pane view with placement tiles, priority stepper, scope tiles, consent category grid, and Load async / Disable in preview toggles.
335
+ - Media gains a Library / SEO Audit tab bar with summary cards, filter pills, an audit table with score bars, and an Edit flow into the library detail panel (which now edits caption, description, and focus keyword).
336
+ - Pages gains a List / Tree view toggle: hierarchy from `parentPageId` with path-prefix fallback, expand/collapse rows, template pills, and an "Add sub-page" action that preselects the parent (also editable via a new Parent page picker in Page settings).
337
+ - The Dashboard shows a getting-started onboarding block (derived 6-step checklist + quick links) in place of the activity grid while the site has no content or media.
338
+
339
+ **create-actuate-cms**
340
+ - Scaffolded middleware flags `?preview=` requests and the site layout resolves script tags with `preview: true` (uncached) so tags marked "disable in preview" never fire during CMS preview; Prisma assets mirror the new migration.
341
+
342
+ ## 0.28.4
343
+
344
+ ### Patch Changes
345
+
346
+ - 685958f: Correct 0.x upgrade guidance: document that npm update only applies patches within the pinned minor; use `actuate upgrade` for minor bumps. Admin Updates panel and update server now show the full upgrade command chain.
347
+
348
+ Clarify scaffold README populate/script-tag and upgrade steps.
349
+
350
+ ## 0.28.3
351
+
352
+ ### Patch Changes
353
+
354
+ - 1a21341: Clarify scaffold README: `actuate populate` does not seed Script Tags; use admin or a custom insert-if-missing script.
355
+
356
+ ## 0.28.2
357
+
358
+ ### Patch Changes
359
+
360
+ - 6d254f3: Fix Script Tags delivery and admin toggles: green/red enabled switches with optimistic saves, in-process resolve + executable script rendering in scaffold layouts, pathname header for scoped tags, and cache invalidation on tag writes.
361
+
362
+ ## 0.28.1
363
+
364
+ ### Patch Changes
365
+
366
+ - 1a3cf91: Agent-ready public forms (WebMCP): declarative tool metadata on public form schemas, zero-JS `renderPublicFormHtml` / `ActuateFormHtml`, urlencoded submit parsing, HTML confirmation responses, scaffold form prefetch, `actuate doctor` check, and docs.
367
+
368
+ ## 0.28.0
369
+
370
+ ### Minor Changes
371
+
372
+ - a6360cc: Forms launch kit (Batch 7): Integrations tab with Zapier Catch Hook helper and webhook CRUD in the form schema drawer; marketing seed contact form + /contact page; ActuateForm success focus; actuate doctor forms/CRM checks; forms-and-integrations docs.
373
+
374
+ ## 0.27.0
375
+
376
+ ### Minor Changes
377
+
378
+ - a59a501: Batch 6 site performance and SEO: path-scoped ISR cache tags in createNextSiteHelpers, scaffold CMS API cache wiring with onInvalidate, JSON-LD in the catch-all, font preload template, and doctor checks for API cache and structured data HTML.
379
+ - e1ad198: Platform site-launch defaults: export `createNextSiteHelpers()` from `@actuate-media/cms-core/next/site` for ISR-friendly cached `/resolve`, scaffold new projects with production plugins and cached catch-all pages, and extend `actuate doctor` with site-launch checks (plugins, SEO routes, cached resolve, SSR sections).
380
+
381
+ ## 0.26.10
382
+
383
+ ### Patch Changes
384
+
385
+ - 0a170a7: Add `@actuate-media/plugin-consent` — cookie banner with `am_cookie_consent` / `am-consent` wiring and GA4, Google Ads, and Meta Pixel loaders gated on Accept all. Scaffold with `--plugins consent` to emit layout + config wiring.
386
+
387
+ ## 0.26.9
388
+
389
+ ### Patch Changes
390
+
391
+ - 330da89: Scaffold SEO defaults in `actuate.config.ts`, document the prototype-to-production launch checklist, extend the deployment guide with SEO verification steps, and add `path: '/'` plus site-settings globals to the marketing seed.
392
+
393
+ ## 0.26.8
394
+
395
+ ### Patch Changes
396
+
397
+ - 3983016: Yoast-style public sitemap URLs (`/post-sitemap.xml`, `/post-sitemap2.xml`, …), human-readable XSLT browser views, and middleware rewrites so collection sitemaps resolve at the site root.
398
+
399
+ ## 0.26.7
400
+
401
+ ### Patch Changes
402
+
403
+ - 4899af9: Fix consumer production builds on Next.js 16 by not injecting a no-op webpack hook in `withActuateCMS`. Add `CRON_SECRET` to scaffold `.env.example`. Remove obsolete `earlyAccess` from scaffolded `prisma.config.ts`. Load project `.env` files in `actuate doctor` and `actuate deploy:check`.
404
+
405
+ ## 0.26.6
406
+
407
+ ### Patch Changes
408
+
409
+ - 2b61b66: Core Web Vitals: give the `hero` section an optional image rendered as the page LCP, and serve AVIF in scaffolded sites.
410
+ - **Hero image (LCP):** the canonical `hero` section now has an optional `imageUrl`/`imageAlt` field. When set, the renderer draws it below the copy as the page's single priority (eager, `fetchpriority=high`) image with a full-width `sizes` hint and intrinsic dimensions — the explicit, dimensioned LCP element the best-practice spec calls for (rather than leaving the LCP to chance). Alt text is required when an image is set (existing accessibility rule). Dimensions are backfilled by the `/resolve` enrichment (hero is now enriched like `feature`).
411
+ - **AVIF delivery:** `create-actuate-cms` now sets `images.formats: ['image/avif', 'image/webp']` so scaffolded sites serve AVIF (≈50% smaller than JPEG) first, then WebP — a direct LCP win via `next/image` format negotiation.
412
+
413
+ ## 0.26.5
414
+
415
+ ### Patch Changes
416
+
417
+ - 7b7f0be: Core Web Vitals: get intrinsic image dimensions onto every image so `next/image` engages and CLS is eliminated.
418
+
419
+ Without `width`/`height`, consumers silently fall back to an unsized `<img>` (no srcset, layout shift). This closes that gap two ways:
420
+ - **Read-time (`/resolve`) enrichment** now covers the **post featured image** and **`hero` section images** (previously only `feature`/`gallery`). The resolver looks up dimensions + alt from `actuate_media` and returns the featured image as `post.featuredImage = { url, width, height, alt }` (new `ResolveFeaturedImage` type). Exposes a `featuredImageUrl()` helper. Purely additive — the stored document is never mutated.
421
+ - **Author-time persistence**: selecting an image in the section editor's media picker now writes the intrinsic `width`/`height` alongside the URL (`imageUrl` → `imageWidth`/`imageHeight`; gallery `url` → `width`/`height`), so dimensions travel with the document even without `/resolve`. Exposes `companionDimensionKeys()`.
422
+
423
+ `create-actuate-cms` + `apps/dev`: the generated post page now feeds the resolved featured-image dimensions into the post header for a CLS-free, dimensioned LCP image.
424
+
425
+ ## 0.26.4
426
+
427
+ ### Patch Changes
428
+
429
+ - 8a64a6e: Core Web Vitals: enforce the single-priority-image (LCP) policy and ship responsive `sizes` by default.
430
+
431
+ The section renderer now guarantees the key LCP/CLS image rules automatically, so an editor (or AI author) can't ship a slow page:
432
+ - **One priority image per page.** A new `LcpController` (exported `createLcpController`) grants the eager, `fetchpriority="high"` slot to the first image on the page; every other image lazy-loads. This replaces `PostHeader`'s hard-coded `priority` (which could collide with a section image). Pass one shared controller via `options.lcp` to both `PostHeader` and `Sections` to share the budget.
433
+ - **Responsive `sizes` everywhere.** Built-in renderers emit contextual `sizes` (full-width header, half-width feature, column-aware gallery via the exported `gallerySizes`) so mobile stops over-downloading the desktop image. `defaultRenderImage` now forwards `sizes` and sets `fetchPriority`.
434
+ - **No raw `<img>`.** The author-bio avatar now renders through `renderImage` (dimensioned, lazy) like every other image.
435
+ - `PostContext` gains `featuredImageWidth`/`featuredImageHeight` so the post header featured image can be dimensioned (CLS).
436
+
437
+ `create-actuate-cms`: the generated catch-all creates a per-request shared `LcpController` for the post header + body and forwards `sizes`/`fetchPriority` in its `next/image` fallback.
438
+
439
+ Note: `ResolvedRenderOptions` now includes a required `lcp` field — only affects code that constructs that internal type directly; consumers passing `RenderOptions` are unaffected (`lcp` is optional there).
440
+
441
+ ## 0.26.3
442
+
443
+ ### Patch Changes
444
+
445
+ - 108a043: Make the uploaded favicon actually drive the browser tab.
446
+
447
+ Previously the admin tab favicon only read the static `admin.branding.favicon` config value, and the `create-actuate-cms` layouts had no favicon wiring at all — so a favicon uploaded in Settings → Appearance never reached the browser tab.
448
+ - **cms-admin**: the admin now applies the Settings → Appearance favicon to its own browser tab at runtime — fetched on mount and re-applied live when the brand is saved (no reload). The authenticated `public/brand` read bypasses shared caches, so the live update is always fresh. The static `admin.branding.favicon` remains a fast default that the uploaded favicon overrides.
449
+ - **create-actuate-cms**: the generated `(site)` and `(admin)` layouts now resolve the favicon from Brand Settings via `generateMetadata` (mirroring the dev app), so uploads update the public site's `<head>` icons out of the box. Wrapped in try/catch so DB-less builds still emit valid metadata.
450
+
451
+ ## 0.26.2
452
+
453
+ ### Patch Changes
454
+
455
+ - 6f140bc: Auto-wire the `platform.storage` shorthand to a real adapter server-side, and keep `actuate.config.ts` client-safe.
456
+
457
+ `actuate.config.ts` is imported by the admin client bundle, so putting a platform adapter (e.g. `platform: vercel()`) in it pulls `node:*`/`@vercel/blob` into the client build and breaks it with `Module not found: node:fs`. The config must stay adapter-free.
458
+ - **cms-core**: When the API handler boots and sees a remote `platform.storage` shorthand (`vercel-blob` → `@actuate-media/platform-vercel`, `s3` → `@actuate-media/platform-aws`) with no adapter registered, it now dynamically imports the matching platform package and registers its storage adapter server-side. The import specifier is computed so bundlers never try to statically resolve an optional peer; a missing package degrades to a clear guidance warning instead of a silent upload failure. An explicitly registered adapter always wins. `setStorageAdapter` / `getStorageAdapter` are now also re-exported from `@actuate-media/cms-core/api` for server-side registration of custom adapters.
459
+ - **cms-core**: Clearer upload/diagnostics errors — when a Vercel Blob store is linked (`BLOB_STORE_ID`) but `BLOB_READ_WRITE_TOKEN` is missing, messages now explain that Vercel does not provide the token via OIDC and it must be added per-environment.
460
+ - **create-actuate-cms**: The generated `actuate.config.ts` uses the client-safe `storage: { provider: 'vercel-blob' | 's3' }` shorthand again (reverting the adapter-in-config change that broke the client build); uploads work out of the box via server-side auto-wiring.
461
+
462
+ ## 0.26.1
463
+
464
+ ### Patch Changes
465
+
466
+ - 1c81772: Make storage configuration honest. `platform.storage: { provider: 'vercel-blob' }` is a descriptive shorthand that never registered an adapter, so uploads silently failed with a misleading `BLOB_READ_WRITE_TOKEN` error. Now:
467
+ - cms-core logs a clear boot-time warning when a remote `provider` shorthand is set but no adapter is registered, and `/media/upload` (used by media + appearance uploads) returns an error that names the real gap — a missing adapter, not a missing token.
468
+ - `create-actuate-cms` generates `platform: vercel()` (a real adapter) instead of the inert shorthand, and the docs gain a dedicated Storage section.
469
+
470
+ ## 0.26.0
471
+
472
+ ### Minor Changes
473
+
474
+ - 9f5e5dc: Add the AI crawler activity panel (llms.txt measurement layer):
475
+ - **`cms-core`** — new `AiCrawlerHit` model (`actuate_ai_crawler_hits`) + migration, aggregated per `(bot, surface)` so storage stays bounded. `detectAiBot()` classifies known AI user-agents (training vs retrieval) from the existing taxonomy; `llms.txt`, `llms-full.txt`, and `.md` requests from a known crawler are recorded best-effort. New `GET /ai-crawlers` (admin) returns aggregated activity, and an opt-in honeypot (`seo.llms.honeypot`) appends a trap link to `llms.txt` served by `GET /ai-crawlers/trap`.
476
+ - **`cms-admin`** — new "AI crawler activity" panel under SEO → Technical showing per-bot hits, type, surface, and last-seen, plus totals. Frames the numbers honestly (visibility, not a traffic channel).
477
+ - **`create-actuate-cms`** — scaffolded Prisma assets include the new migration so fresh projects get crawler tracking out of the box.
478
+
479
+ Recording is always best-effort and gracefully no-ops on databases that predate the model.
480
+
481
+ ## 0.25.0
482
+
483
+ ### Minor Changes
484
+
485
+ - 0bcc567: Add per-page Markdown for the AI/agent layer:
486
+ - **`GET /md?path=…`** (`@actuate-media/cms-core`) — returns a clean Markdown rendering of a single published page via `documentToMarkdown()`. Served with `X-Robots-Tag: noindex`; noindexed and `llmsInclude: NEVER` documents return `404` (the selection filter is the privacy boundary).
487
+ - **Clean `/{path}.md` URLs** — `create-actuate-cms` middleware rewrites `/{path}.md` → `/api/cms/md`, so any page is fetchable as Markdown by appending `.md` (e.g. `/about.md`). The HTML page stays canonical and `.md` URLs are never in the sitemap.
488
+ - **Shared path resolver** — extracted `matchPathToCollection()` so `/md` and `/resolve` agree on which document a public URL maps to (no logic drift).
489
+
490
+ ## 0.24.0
491
+
492
+ ### Minor Changes
493
+
494
+ - a889054: Add the advanced `llms.txt` layer — clean Markdown content export for AI agents:
495
+ - **`documentToMarkdown()` serializer** (`@actuate-media/cms-core`) — renders a published document to clean Markdown. Rich-text/HTML slots are converted with Turndown; structured section content (and the page-builder block tree) is walked generically using an allow-list of prose/heading keys, so non-content data (URLs, ids, settings) never leaks and new section types render without per-type code.
496
+ - **`/llms-full.txt`** — opt-in route (`seo.llms.fullEnabled`) serving the same curated, noindex, sitemap-excluded selection as `/llms.txt` with each page's Markdown inlined and delimited by its canonical URL. Bounded by `seo.llms.maxFullBytes` (default 2 MB) with clean truncation.
497
+ - **Shared selection** — `/llms.txt` and `/llms-full.txt` now share one selector (`selectLlmsDocs` / `toLlmsPage`) so curation and the noindex privacy boundary can't drift.
498
+ - **Scaffold** — `create-actuate-cms` proxies `/llms-full.txt` at the domain root (always generated; enabling is config-only).
499
+
500
+ ## 0.23.0
501
+
502
+ ### Minor Changes
503
+
504
+ - 4bf16c4: Harden the `/llms.txt` AI discovery file:
505
+ - **Root proxy** — `create-actuate-cms` now scaffolds `app/llms.txt/route.ts`, exposing the file at the spec-required domain root (`/llms.txt`) in addition to `/api/cms/llms.txt`.
506
+ - **noindex** — the route now sends `X-Robots-Tag: noindex` (and the scaffolded proxy preserves it) so the machine-facing index isn't itself indexed.
507
+ - **Sanitization** — titles, descriptions, and the site summary are collapsed to a single line and stripped of markdown link-breaking and control/zero-width characters, then length-capped. Reduces corruption and instruction-smuggling risk from editor fields.
508
+ - **Curation controls** — new `seo.llms` config: `disabled`, `eligibleCollections` (restrict AUTO selection to high-value types), `maxPerSection` (default 25), and `maxEntries` (default 50) so large archives can't flood the file.
509
+
510
+ ## 0.22.0
511
+
512
+ ### Minor Changes
513
+
514
+ - 3f7fbf3: Add RSS 2.0 (`/feed.xml`) and JSON Feed 1.1 (`/feed.json`) content feeds.
515
+
516
+ Both serve the newest published items across post-type collections (falling back
517
+ to pages when there are no post collections), newest first, capped at 50, with
518
+ canonical URLs and per-document `noindex` exclusion. Item descriptions prefer the
519
+ GEO key takeaway, then the meta description, then the excerpt. Pure builders are
520
+ exported as `buildRssFeed` / `buildJsonFeed`. New `create-actuate-cms` projects
521
+ scaffold root `/feed.xml` + `/feed.json` proxy routes.
522
+
523
+ ### Patch Changes
524
+
525
+ - df5d0de: SEO sitemap hardening: canonical URLs, noindex exclusion, accurate lastmod, hreflang, and IndexNow.
526
+
527
+ **cms-core**
528
+ - Sitemaps and the canonical `<link>` / `og:url` now resolve through a single shared `resolveDocumentUrl()` helper, so they can never disagree. It honours an explicit `data.path` (e.g. `/` for the homepage, `/about/story`), maps `home`/`index` to the site root, and otherwise uses `<urlPrefix>/<slug>`. Fixes homepages being listed as `/home` and any page whose `data.path` differed from `/{slug}`.
529
+ - Noindexed documents (`noIndex`/`noindex`/`robotsPolicy: noindex*`, collection/site defaults, non-production guard) are now excluded from the sitemap so it never conflicts with a page's own robots meta tag.
530
+ - The "Include posts in sitemap" crawl setting is now honoured by sitemap generation (both the index and the per-collection sitemap, which 404s when posts are excluded).
531
+ - The "Index entire site" master switch is now enforced: when off, `robots.txt` emits a blanket `Disallow: /` for all crawlers, the sitemap (index + per-collection) serves empty, and publish-time search-engine/IndexNow pings are suppressed. This governs the public site only — CMS admin/API protection (auth + the admin's own `noindex` meta tag + the always-present `/admin` and `/api/` disallows) is unaffected.
532
+ - Sitemap-index `<lastmod>` now reflects each collection's newest published change instead of "now", so crawlers can trust it.
533
+ - `robots.txt` and the SEO settings now advertise the canonical public `/sitemap.xml` instead of the internal `/api/cms/sitemap.xml`.
534
+ - hreflang `<xhtml:link rel="alternate">` annotations (incl. `x-default`) are emitted for translated documents when i18n is configured with more than one locale. No overhead for single-locale sites.
535
+ - IndexNow support: when `INDEXNOW_KEY` is set, changed URLs are submitted to IndexNow (Bing, Yandex, …) on publish/update/delete, and the key is served at `/api/cms/indexnow-key.txt`. New exports: `resolveDocumentUrl`, `buildIndexNowSubmission`, `INDEXNOW_ENDPOINT`.
536
+
537
+ **create-actuate-cms**
538
+ - Scaffolded projects ship root `app/sitemap.xml/route.ts` and `app/robots.txt/route.ts` proxy routes so the canonical `/sitemap.xml` and `/robots.txt` URLs resolve out of the box.
539
+
540
+ ## 0.21.0
541
+
542
+ ### Minor Changes
543
+
544
+ - a6a17e9: Split login: serve the auth screens on their own non-obvious path while the admin app stays at `/admin`.
545
+ - **cms-core**: new `admin.loginPath` config. `createCMSMiddleware` applies the same guards (IP allowlist, CMS_SECRET check, setup bypass) to the login mount and redirects expired sessions there. Emailed password-reset and invite links point at the login mount when configured.
546
+ - **cms-admin**: new `LoginRoot` component mounts login (+ MFA), the first-run setup wizard, and forgot/reset-password on a separate route (e.g. `/securelogin`, with setup at `/securelogin/setup`), then hands off to the admin app after sign-in. `<AdminRoot loginPath>` makes the admin mount render a 404 lookalike for signed-out visitors instead of a login form — no redirect, so the login location is never leaked.
547
+ - **create-actuate-cms**: new `--login-path` flag and prompt (default `/securelogin`). New projects scaffold the split-login layout by default: login/setup at the login path, admin app at the admin path, config + proxy wired accordingly. Pass `--login-path` equal to `--admin-path` for the classic inline login.
548
+
549
+ ## 0.20.0
550
+
551
+ ### Minor Changes
552
+
553
+ - 3e2daa4: Inline branded preview — see the draft through your real site without leaving the editor.
554
+ - cms-admin: the page and post editors gain a "Site preview" toggle in the top bar that swaps the structure canvas for an iframe of the public site rendering the draft (same signed `?preview=<token>` contract as "View on site"). Unsaved edits are persisted first, the frame reloads after every save, and the existing viewport controls size the frame.
555
+ - cms-core: `createSiteClient().resolveDocument(path, { previewToken })` forwards a preview token to `/resolve` so consumer routes can render drafts at their real URL.
556
+ - create-actuate-cms: the scaffolded catch-all route now forwards `?preview=` to the resolver and shows a draft banner, so new projects support "View on site" and inline preview out of the box.
557
+
558
+ ## 0.19.0
559
+
560
+ ### Minor Changes
561
+
562
+ - 0f9c510: Editor & scaffold improvements from Upchat feedback.
563
+
564
+ **cms-admin** — Repeater rows now derive a scannable header from their values
565
+ ("Pro · $79/mo", "99.99% · Uptime") instead of only the positional label
566
+ ("Plan 2"), so long repeaters read like a list instead of a spreadsheet. Blank
567
+ rows still fall back to "Plan N".
568
+
569
+ **create-actuate-cms** — The `marketing-custom` template now ships the full
570
+ custom-section experience out of the box:
571
+ - `src/sections/admin-renderers.tsx` is scaffolded and pre-wired into
572
+ `<AdminRoot sectionRenderers={...}>`, so the editor canvas renders real
573
+ components for custom section types instead of structural placeholders.
574
+ - A pre-filled `actuate.seed.json` ("Marketing homepage" with hero, stats,
575
+ pricing, and CTA sections) loads via `npm run populate`, so the first visit
576
+ to the Page editor shows a fully-authored page instead of an empty canvas.
577
+ - `SECTIONS.md` and the project README document both workflows.
578
+
579
+ ## 0.18.0
580
+
581
+ ### Minor Changes
582
+
583
+ - 0c3a6eb: Posts UX improvements from external-consumer feedback:
584
+ - New `admin.excludeFromPostsAggregate: true` collection flag keeps a post-shaped collection out of the admin's aggregate Posts area (cross-collection list, type tabs, New Post picker) while leaving its own admin list and API routes fully accessible. Enforced server-side in `GET /posts` and mirrored in the admin client.
585
+ - `blogPostCollection()` now satisfies the admin Post Editor's full field contract out of the box: it gains `sections` (json), `category`, and `publishDate` (replacing `publishedDate` — update seeds/queries that referenced the old key), and `body` is no longer hard-required so section-driven posts can publish.
586
+ - `create-actuate-cms` scaffolds now define `posts: blogPostCollection()` instead of an inline collection, so new projects always start with an editor-compatible posts schema.
587
+
588
+ ## 0.17.1
589
+
590
+ ### Patch Changes
591
+
592
+ - 854152d: Developer experience improvements from external-consumer feedback:
593
+ - Every published package now ships its `CHANGELOG.md` in the npm tarball, and packages that were missing a `README.md` (cli, cms-admin, cms-core, import, platform-vercel, all plugins, sections-react) now have one — so release notes and usage docs are visible on npmjs.com and in `node_modules`.
594
+ - New client-safe form validation entry point: `@actuate-media/cms-core/forms/client` exports `validateFormFieldClient()`, `validateFormValuesClient()`, visibility/conditional helpers, and the exact `FORM_EMAIL_RE` / `FORM_PHONE_RE` regexes the server enforces — zero Node dependencies, so public-site forms can mirror server rules without reimplementing them. The server validator now imports the same primitives, so client and server can never drift.
595
+
596
+ ## 0.17.0
597
+
598
+ ### Minor Changes
599
+
600
+ - 72a4fe9: Prisma Postgres is now the default (and recommended) database for scaffolded projects. `create-actuate-cms` adds a `prisma-postgres` database choice with matching `.env.example` connection strings and deployment-guide copy; Neon and Supabase remain available as explicit choices. Internal docs, deployment rules, and code comments no longer describe this repository's own deployment as Neon — it runs on Prisma Postgres. Benchmark policy messages now suggest a dedicated Prisma Postgres database for isolated runs.
601
+
602
+ ## 0.16.0
603
+
604
+ ### Minor Changes
605
+
606
+ - 8e73cf5: Add a `--template marketing-custom` starter (and interactive prompt) that scaffolds custom section types end to end: a `src/sections/types.ts` schema wired into `actuate.config.ts` (`sections.types`), public renderers in `src/sections/register.tsx`, a typed `src/blocks/` component source tree, a `verify:sections` static check, `blocks:extract`/`blocks:diff` scripts for the component-blocks manifest, and a `SECTIONS.md` guide.
607
+
608
+ The default `pages` collection now ships with an explicit `path` field via the `pagePathField()` preset.
609
+
610
+ ## 0.15.0
611
+
612
+ ### Minor Changes
613
+
614
+ - 74662c0: Next.js 16 scaffold fixes and polish.
615
+ - Generate `proxy.ts` (exporting a `proxy` function) instead of `middleware.ts`, matching the Next.js 16 convention.
616
+ - Fix an over-escaped template literal in the generated `app/(site)/layout.tsx` `fetch` call that produced invalid syntax, and add `metadataBase` to the site metadata.
617
+ - Guard the public catch-all route with `isAdminPath()` so requests under the admin base are never resolved as CMS content.
618
+ - Add `@actuate-media/component-blocks` to the default dependencies.
619
+ - New integration tests assert the generated `proxy.ts`, template literal, `metadataBase`, admin guard, and dependency, and TypeScript-parse the key generated files.
620
+
621
+ ## 0.14.0
622
+
623
+ ### Minor Changes
624
+
625
+ - 13906f6: Scaffold a Renovate preset for the code plane (ADR 0003 §2). New projects ship a `renovate.json` that groups every `@actuate-media/*` upgrade into a single scheduled PR, **auto-merges non-major bumps once CI passes** (leaning on the migration gate added in the previous release), and labels major bumps `breaking-change` for manual review. Uses current `matchPackageNames` glob syntax. `DEPLOYMENT.md` documents enabling Renovate and the branch-protection rule that makes CI-gated auto-merge effective. "Patch the fleet" becomes merging each repo's grouped, independently CI-gated PR — no bespoke orchestrator.
626
+
627
+ ## 0.13.0
628
+
629
+ ### Minor Changes
630
+
631
+ - e2d8d71: Scaffold a provider-agnostic CI migration gate (ADR 0003). New projects ship a `.github/workflows/ci.yml` that, on every pull request, stands up a throwaway Postgres service container and runs `prisma migrate deploy` against it — so a broken or out-of-order migration fails in review instead of breaking the production Vercel build. It needs no secrets and works regardless of where production runs (Prisma Postgres, Neon, Supabase, or self-hosted); `migrate deploy` reads `DIRECT_DATABASE_URL` per the generated `prisma.config.ts`. `DEPLOYMENT.md` documents the gate.
632
+
633
+ ## 0.12.0
634
+
635
+ ### Minor Changes
636
+
637
+ - 127f91a: Generate a VS Code MCP config (`.vscode/mcp.json`) alongside the existing
638
+ Cursor/Claude `.cursor/mcp.json`, using VS Code's `servers` + `type: "stdio"`
639
+ schema. Both files ship a placeholder API key and are git-ignored, so a freshly
640
+ scaffolded project wires up an AI agent in either editor after a single
641
+ key-paste. The post-scaffold instructions now cover both editors.
642
+
643
+ ## 0.11.0
644
+
645
+ ### Minor Changes
646
+
647
+ - 31224ae: WS-E (E4 follow-up): seed demo navigation menus and render a footer nav so
648
+ navigation works end-to-end on a fresh scaffold.
649
+ - **cli:** `actuate seed --demo` now seeds two `navigations` documents — a
650
+ `main` menu (location `header`, linking Home/About/Contact) and a `footer`
651
+ menu (location `footer`, linking Privacy/Terms) — so the shipped `<SiteNav>`
652
+ renders real links out of the box instead of an empty header. Items point at
653
+ the demo pages by public URL.
654
+ - **create-actuate-cms:** the generated site layout now also fetches the
655
+ `footer` menu and renders it in a semantic `<footer>` (with a copyright line),
656
+ alongside the existing header nav. The body is a flex column so the footer
657
+ sits at the bottom. Both navs still degrade gracefully when no menu exists.
658
+
659
+ ## 0.10.0
660
+
661
+ ### Minor Changes
662
+
663
+ - 204da7a: WS-E (E3): media resolution + responsive images in the public render path.
664
+ - **sections-react:** new pluggable `renderImage` `RenderOptions` hook (with
665
+ exported `RenderImage`/`RenderImageProps` types and a `defaultRenderImage`).
666
+ The default emits a lazy, `decoding="async"` `<img>` and forwards intrinsic
667
+ `width`/`height` when known; a Next.js consumer can pass a `next/image`-backed
668
+ renderer for responsive `srcset` + optimization. The `feature`, `gallery`, and
669
+ post-header images now route through it (`<PostHeader>` gains an optional
670
+ `options` prop). The package stays React-only — no `next` dependency.
671
+ - **cms-core:** the `/resolve` endpoint now enriches section images with
672
+ intrinsic `width`/`height` and library `altText`, matched from `actuate_media`
673
+ by stored URL (`storageKey`/`publicUrl`). Additive and best-effort — the
674
+ stored document is never mutated and a lookup failure never breaks resolve.
675
+ `feature` sections gain `imageWidth`/`imageHeight` (+ backfilled `imageAlt`);
676
+ `gallery` rows gain `width`/`height` (+ backfilled `alt`).
677
+ - **create-actuate-cms:** the generated catch-all page renders CMS images via a
678
+ `next/image`-backed `renderImage` (falling back to a fluid `<img>` when the
679
+ CMS doesn't know dimensions), and `next.config` now allow-lists the storage
680
+ host under `images.remotePatterns` so optimization works out of the box.
681
+
682
+ - e2e0bd7: WS-E (E4): public navigation endpoint + navigation rendering in the scaffold.
683
+ - **cms-core:** new unauthenticated `GET /api/cms/public/navigations/:slug` that
684
+ returns a clean `{ name, slug, location, items }` tree. Like globals it is site
685
+ chrome — not status-gated — and safe when the navigation plugin isn't installed
686
+ (no match → 404). A shared, pure `coercePublicNavItems` helper sanitizes the
687
+ open `data.items` JSON: drops unlabeled nodes, strips internal `id`s, caps
688
+ recursion depth, and only passes through `_blank`/`_self` targets. Both
689
+ `createSiteClient` and `createServerSiteClient` gain `getNavigation(slug)`
690
+ returning `PublicNavigation | null`, and `PublicNavigation` / `PublicNavItem`
691
+ are now exported types.
692
+ - **sections-react:** new presentational `<SiteNav>` — an accessible `<nav>`
693
+ landmark whose submenus use native `<details>`/`<summary>` disclosure
694
+ (keyboard-operable, no client JS). New-tab links get
695
+ `rel="noopener noreferrer"`; an empty menu renders nothing. Exports `SiteNav`
696
+ plus `SiteNavProps`/`SiteNavItem`.
697
+ - **create-actuate-cms:** `lib/cms.ts` re-exports `getNavigation`, and the
698
+ generated site layout fetches the `main` menu and renders a header with the
699
+ site name + `<SiteNav>`, degrading gracefully to just the name when no menu
700
+ exists yet.
701
+
702
+ - a413616: Scaffold a ready-to-use MCP config and connect-an-agent onboarding (WS-C C5).
703
+
704
+ New projects now ship a `.cursor/mcp.json` wiring the Actuate MCP server (`npx -y @actuate-media/mcp-server`) with a placeholder `ACTUATE_API_KEY`. The file is added to `.gitignore` so a real key pasted in is never committed, and the post-setup output explains the "create API key → paste into `.cursor/mcp.json` → restart Cursor/Claude" flow. The MCP guide is also published into the docs-site nav (`apps/docs/content/mcp.md`).
705
+
706
+ - 6927e4b: Non-interactive scaffolding + env wiring fixes (WS-D D1/D4).
707
+ - **Non-interactive mode (D1):** `create-actuate-cms` now accepts `--yes`,
708
+ `--admin-path`, `--plugins` (comma list, `all`, or `none`), `--platform`,
709
+ `--database`, and `--auth` flags. With `--yes` (or no TTY, e.g. CI/agents) it
710
+ scaffolds headlessly from flags + defaults instead of hanging on a prompt.
711
+ Provided flags also skip their prompt in interactive mode. Invalid values fail
712
+ fast with an actionable error listing the allowed choices.
713
+ - **OAuth env names (D4):** the generated `.env.example` now emits
714
+ `OAUTH_GOOGLE_CLIENT_ID` / `OAUTH_GOOGLE_CLIENT_SECRET` (and the GitHub/
715
+ Microsoft equivalents) to match what `cms-core` actually reads at runtime. The
716
+ bare `GOOGLE_CLIENT_ID` names (which the server ignored) and the unused
717
+ `MICROSOFT_TENANT_ID` are removed.
718
+ - **Vercel Blob dependency (D4):** Vercel projects now get `@vercel/blob` as a
719
+ direct dependency. The generated config wires `storage.provider: 'vercel-blob'`,
720
+ but `@vercel/blob` was only an optional peer of `platform-vercel` — so the
721
+ first media upload previously failed with a missing-module error.
722
+ - **Commerce webhook path (D4):** the scaffolded Stripe `webhookEndpoint` now
723
+ points at `/api/cms/stripe/webhook` (the mounted CMS API) instead of the
724
+ non-existent `/api/actuate/stripe/webhook`.
725
+
726
+ ## 0.9.0
727
+
728
+ ### Minor Changes
729
+
730
+ - 9ba332c: Converge the scaffolder onto the canonical page-content contract (ADR 0001, WS-A4).
731
+ - **create-actuate-cms**: New projects now use the canonical `sections` (JSON `PageSection[]`) field for both `pages` and `posts`, and the generated public catch-all renders `data.sections` with `<Sections>` from `@actuate-media/sections-react` — passing cms-core's allow-list `sanitizeHtml` so author HTML (e.g. the article-body section) is sanitized before the DOM, and rendering a `<PostHeader>` for post-type documents. The deprecated Model C path is retired: the `content[]`/`BLOCK_MAP` `BlockRenderer`, `HeroBlock`, `CTABlock`, `TextBlock`, `ImageTextBlock`, `SiteHeader`, and `SiteFooter` generators (and the `blocks`-gated `layout`/headers/footers config) are removed. Generated apps now depend on `@actuate-media/sections-react` and emit the matching Tailwind `@source` directive.
732
+ - **@actuate-media/cms-core**: `ResolveResult` (the typed `createSiteClient().resolveDocument()` return) now exposes the optional `meta`, `metadata`, `jsonLd`, `jsonLdHtml`, `template`, `post`, and `preview` fields the `/resolve` endpoint already returns, plus the new `ResolvePostAuthor`, `ResolveRelatedPost`, `ResolvePostContext`, and `ResolvePreview` types — so consumers can render the full post experience (header, author, related posts, SEO metadata) without re-declaring the shape.
733
+
734
+ ## 0.8.2
735
+
736
+ ### Patch Changes
737
+
738
+ - ae1af8f: Authoring correctness fixes for the AI site-building workflow (roadmap WS-B/E quick wins).
739
+ - **cms-core**: `createDocument` now honors a requested `status` (`DRAFT` or
740
+ `PUBLISHED`) instead of always forcing `DRAFT`. Publishing on create is gated
741
+ to EDITOR/ADMIN, stamps `publishedAt`, and emits the `document.published`
742
+ webhook + sitemap notify. This fixes `POST /page-builder/create?publish=true`
743
+ (and `status: 'PUBLISHED'`), which previously created a draft silently.
744
+ Statuses other than `DRAFT`/`PUBLISHED` on create are rejected with guidance
745
+ to schedule/archive via update.
746
+ - **client**: `listMedia()` now returns the canonical
747
+ `{ docs, total, page, pageSize, totalPages }` (`MediaList`) envelope. The
748
+ server returns the asset array under `items`/`data` (never `docs`), so the
749
+ previous typing made `listMedia().docs` always `undefined`. Adds exported
750
+ `MediaAsset` and `MediaList` types and an optional `init` (signal/cache) arg.
751
+ - **mcp-server**: the `list_media` tool now normalizes the same `items`→`docs`
752
+ shape so its output matches `list_documents`.
753
+ - **create-actuate-cms**: generated `TextBlock`/`ImageTextBlock` now sanitize
754
+ author-supplied HTML with `sanitizeHtml` before `dangerouslySetInnerHTML`
755
+ (was an XSS vector on hostile content).
756
+
757
+ ## 0.8.1
758
+
759
+ ### Patch Changes
760
+
761
+ - 951963a: Build the Settings > Users access-control system: real invites, roles, seats, and access review.
762
+
763
+ **@actuate-media/cms-core**
764
+ - Schema: add `OWNER` and `VIEWER` to the `UserRole` enum, a new `InviteStatus` enum, a new `Invite` model (`actuate_invites`), and additive user-lifecycle columns (`suspendedAt`, `lockedAt`, `forcePasswordReset`, `mfaRequired`, `failedLoginCount`, `lastActiveAt`, `invitedById`, `acceptedInviteAt`). Two hand-authored migrations (`0011_user_roles_owner_viewer`, `0012_invites_and_user_lifecycle`) — enum values are added in their own migration so Postgres can use them as defaults afterward. All user columns are nullable/defaulted so existing rows migrate cleanly.
765
+ - Add a `roles` module (also exported as the `@actuate-media/cms-core/roles` subpath): display mapping (`OWNER/ADMIN/EDITOR/VIEWER` plus legacy `AUTHOR→Editor`, `CLIENT→Viewer`), assignable roles, privilege ranking, and per-role permission derivation.
766
+ - Add an `Invite` lifecycle service (`createInvite`, `listInvites`, `resendInvite`, `cancelInvite`, `updateInviteRole`, `extendInvite`, `expireStaleInvites`, `markInviteAcceptedForEmail`). Invite tokens are hashed at rest, single-use, and expire (7/14/30 days). Completing the password-reset flow for an invited email now marks the invite `ACCEPTED` atomically.
767
+ - Add a `team` module (`listTeamMembers`, `getUserStats`, `computeAccessReview`) that derives seat usage and access-review recommendations (inactive 90d+, external domain, admin/owner without MFA, stale invite) from real user/security signals.
768
+ - New admin endpoints: enriched `GET /users` (search/role/status filters), `GET /users/stats`, `PATCH /users/:id/role`, hardened `DELETE /users/:id` (revoke = suspend, last-Owner protection), `GET /invites` + `POST /invites/:id/resend|cancel` + `PATCH /invites/:id`, and `GET /access-review` + `POST /access-review/:id/dismiss`. Role assignment/removal of Owner is Owner-only, self-escalation is blocked, role downgrades revoke sessions, and seat limits are enforced. All sensitive actions are audit-logged.
769
+
770
+ **@actuate-media/cms-admin**
771
+ - Rebuild the Settings > Users tab to match the design: dynamic summary cards (members, pending invites, admins, seats used), an actionable access-review banner, a combined team table (active members + pending invites) with role-filter pills and search, per-row role dropdowns and revoke/resend/cancel actions, and an invite modal with role radio cards and advanced options (custom message, expiry). Includes loading, empty, filtered-empty, and error states; destructive and Owner-assignment actions require confirmation.
772
+
773
+ **create-actuate-cms**
774
+ - Sync the scaffold's Prisma schema and migrations with the new Users-management schema so newly created projects start with the `Invite` model, new roles, and user-lifecycle columns.
775
+
776
+ ## 0.8.0
777
+
778
+ ### Minor Changes
779
+
780
+ - f043e18: Fix day-one auth dead-ends and wire up transactional email + user invites.
781
+
782
+ **@actuate-media/cms-core**
783
+ - Add a transactional email sender registry (`setEmailSender`, `getRegisteredEmailSender`, `resolveEmailSender`). Password-reset and form-notification email now resolve a sender via this registry, so apps that configure email purely through `emailPlugin()` actually send (previously only `config.platform.email` was consulted, so reset/notification mail silently never sent).
784
+ - Password reset links are now built from the configured admin path instead of a hardcoded `/admin`, so resets work on apps mounted elsewhere.
785
+ - Add an admin-only `POST /users/invite` endpoint plus `createUserInvite` / `normalizeInviteRole` / `VALID_INVITE_ROLES`. Invites provision the user with an unusable password and a single-use, 7-day token, then email a "set your password" link (reusing the existing `/reset-password` page). When no email sender is configured the response returns the link so an admin can share it manually. Rate-limited per admin.
786
+
787
+ **@actuate-media/cms-admin**
788
+ - `AdminRoot` gains an `onLogout` prop. The account-menu "Logout" item now revokes the server session (`POST /auth/logout`), clears local session state, and returns to the login screen — previously it navigated to a non-existent `/logout` route (404) and left the session valid.
789
+ - The "Invite User" dialog is now a real flow backed by `POST /users/invite` (loading state, error handling, valid roles, optional name) instead of a fake success toast.
790
+
791
+ **create-actuate-cms**
792
+ - The scaffolded admin page now wires `onVerifyTotp` (so MFA users can complete the second-factor step) and `onLogout`, matching the dev app.
793
+
794
+ **@actuate-media/plugin-email**
795
+ - `emailPlugin()` now registers its resolved provider with cms-core's email sender registry, so configuring email via the plugin is sufficient for password-reset, invite, and form-notification email.
796
+
797
+ - 456e5f2: Fix client onboarding so a freshly scaffolded project actually has a working database.
798
+
799
+ **create-actuate-cms**
800
+ - Scaffolded projects now ship the canonical Prisma schema (all `actuate_*` models with `@@map`) and the full migration history, synced from `@actuate-media/cms-core` via `scripts/sync-prisma-assets.ts` and guarded by a drift test. Previously the scaffolder hand-maintained a divergent schema (14 of 33 models, no `@@map`) and shipped no migrations, so `prisma migrate deploy` in the build created no tables and core features (full-text search, i18n, API metrics, API keys, webhooks, notifications, etc.) were broken.
801
+ - The generated `prisma.config.ts` now warns when migrations would run against the pooled `DATABASE_URL` because `DIRECT_DATABASE_URL` is unset.
802
+ - The generated admin page now shows an actionable "run migrations" / backend-unreachable message instead of silently dropping to the login screen when the database has no tables or `/setup/status` fails.
803
+ - Next-steps + deployment docs now use `prisma migrate deploy` (applies the bundled migrations).
804
+
805
+ **@actuate-media/cms-core**
806
+ - `checkSetupRequired` now reports `migrationsRequired: true` (new `SetupStatus` field) when the database is reachable but the tables don't exist yet, and `POST /setup/create-admin` returns an actionable 503 in that case. New exported helper `isSchemaMissingError`.
807
+ - The admin middleware now returns an actionable HTML page (with remediation steps) when `CMS_SECRET` is missing, instead of a bare `500`.
808
+
809
+ ## 0.7.0
810
+
811
+ ### Minor Changes
812
+
813
+ - 5fa84d3: SEO Operations Center — a full detect → prioritize → explain → route → fix → audit → generate workflow.
814
+
815
+ **cms-core**
816
+ - Additive `0009_seo` migration (mirrored to `apps/dev`): `SEOAuditRun`, `SEOIssue` (+ severity/status/category enums), `Redirect` extensions (`hits`, `lastHitAt`, `enabled`, `priority`, `preserveQuery`, `source`), `Redirect404Hit`, and `RedirectSuggestion`. All columns are nullable or defaulted, so it is safe to apply to an existing database.
817
+ - Pure, explainable scoring model (`seo/score.ts`): `calculatePageSeoScore` / `calculateSiteSeoScore` with documented weights and breakdowns.
818
+ - Persistent audit engine (`seo/audit-engine.ts`) with cross-run issue lifecycle (open / resolved / ignored / recurring) and a redirect engine (chain/loop detection, safe flatten, hit tracking, 404 logging, gated AI 404 recovery).
819
+ - New endpoints: `POST /seo/audit`, `GET /seo/audits[/:id]`, `GET /seo/issues[/:id]`, issue `resolve`/`ignore`/`reopen`/`fix`, `GET /seo/overview`, redirect CRUD + import/export + chains/loops/flatten/suggest, technical settings (`/seo/sitemap/*`, `/seo/crawl-settings`, `/seo/robots`), and approval-gated `/ai/seo/*` (generate-field, explain-issue, generate-schema, summarize) that degrade gracefully with 501 when `plugin-ai` is unconfigured.
820
+ - `/resolve` now returns Next.js-ready `metadata` plus validated JSON-LD; public `robots.txt` honors the admin-edited body.
821
+
822
+ **cms-admin**
823
+ - Rebuilt `SEO.tsx` into a five-tab Operations Center (Overview / Content / Technical / Redirects / Audit) with `?tab=` deep-link routing, dynamic badge counts, a typed `seo-service.ts` client, reusable token-only primitives and a focus-trapped drawer, editor/fix/copilot drawers, and full loading / empty / error / permission states.
824
+
825
+ **mcp-server**
826
+ - New SEO tools: `seo_overview`, `run_seo_audit`, `list_seo_audit_runs`, `list_seo_issues`, `get_seo_issue`, `set_seo_issue_status`, `list_redirect_rules`, `seo_redirect_overview`, `seo_redirect_chains`.
827
+
828
+ **create-actuate-cms**
829
+ - Scaffolded projects now ship a redirect-resolving `middleware.ts` backed by the database redirect table.
830
+
831
+ ## 0.6.4
832
+
833
+ ### Patch Changes
834
+
835
+ - c8f1022: Update scaffolded `.env.example` and deploy README to reflect the email + rate-limit changes:
836
+ - Email section now documents `EMAIL_PROVIDER` (resend/ses/smtpcom/smtp), `EMAIL_FROM`, and the generic SMTP vars (`SMTP_HOST`/`SMTP_PORT`/`SMTP_SECURE`/`SMTP_USER`/`SMTP_PASS`, requires `nodemailer`).
837
+ - Rate-limit guidance now notes that the Vercel Marketplace Upstash integration provisions `KV_REST_API_URL` / `KV_REST_API_TOKEN` (read automatically), correcting the previous claim that it provisions `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN`.
838
+
839
+ ## 0.6.3
840
+
841
+ ### Patch Changes
842
+
843
+ - 560d1de: Dashboard redesign + real-data backends.
844
+
845
+ `@actuate-media/cms-admin`:
846
+ - Sidebar now uses explicit Content and Settings group headers with nested
847
+ Pages children (Saved Sections, Templates). The collapse control moved
848
+ to the sidebar footer.
849
+ - Header hoists primary page actions (e.g. "+ New Post", "View Site")
850
+ into the top utility bar so the page hero is no longer the source of
851
+ truth for those affordances. The notification bell is wired to the
852
+ real `NotificationBell` component.
853
+ - Dashboard greeting picks the user's first name, and the "Delivery API"
854
+ - "Content Health" cards now read live data from new endpoints.
855
+
856
+ `@actuate-media/cms-core`:
857
+ - New `ApiRequestMetric` table (minute-bucketed counter + log-scale
858
+ latency histogram) and `recordApiRequest` instrumentation wired into
859
+ `handler-factory.ts`. Writes are fire-and-forget — request paths are
860
+ never blocked on the metric DB.
861
+ - New `ContentIssue` table + nightly `processContentHealthScan` cron
862
+ (`/api/cms/cron/content-health`) that runs a pure-logic scanner for
863
+ missing meta descriptions, missing alt text, outdated content
864
+ (>90 days), and broken internal links via HEAD probes.
865
+ - New endpoints `GET /api/cms/stats/api-delivery` and
866
+ `GET /api/cms/stats/content-health` return the dashboard's
867
+ aggregates with stable, never-undefined response shapes.
868
+ - Migration `0007_dashboard_metrics` adds both tables with the indexes
869
+ the new queries need. Safe to apply to populated DBs (no backfill).
870
+
871
+ `create-actuate-cms`:
872
+ - Scaffolded `vercel.json` now includes the nightly content-health
873
+ cron entry, and the deployment guide documents it.
874
+
875
+ ## 0.6.2
876
+
877
+ ### Patch Changes
878
+
879
+ - e2fabee: Add AI-friendly deployment diagnostics, align generated schemas with first-run admin features, and harden setup guidance for design-first deployments.
880
+
881
+ ## 0.6.1
882
+
883
+ ### Patch Changes
884
+
885
+ - b224105: Fix post-release developer experience regressions in public site globals, scaffolded Prisma wiring, and seed/populate database initialization.
886
+
887
+ ## 0.6.0
888
+
889
+ ### Minor Changes
890
+
891
+ - 266b657: Add first-class public site helpers, CLI init/populate workflows, and generated frontend boilerplate for globals, document resolution, and media normalization.
892
+
893
+ ## 0.5.1
894
+
895
+ ### Patch Changes
896
+
897
+ - b5de8dd: Fix ESM module resolution and dependency duplication issues reported during consumer integration.
898
+ - Add "default" export condition to all platform and plugin packages so they resolve correctly from require() contexts (fixes ERR_PACKAGE_PATH_NOT_EXPORTED in next.config.ts and serverless functions).
899
+ - Move @actuate-media/cms-core from direct dependencies to peerDependencies across all packages to prevent duplicate installations when consumer version ranges don't exactly match the pinned version.
900
+ - Add "prepack" lifecycle script to create-actuate-cms to guarantee dist/ is built before npm publishes the package (fixes empty dist/ in v0.4.0).
901
+
902
+ ## 0.5.0
903
+
904
+ ### Minor Changes
905
+
906
+ - 54c7a3b: Add Script Tags management system for injecting tracking codes, analytics, and custom scripts into public site pages. Admins create tags with HTML/JS code, choose placement (head, body open, body close), set scope (entire site, parent pages + descendants, or exact URLs), and assign priority order. Includes new ScriptTag database model, CRUD + public resolve API endpoints, admin list and editor views, and updated scaffolder site layout with automatic tag rendering.
907
+
908
+ ## 0.4.0
909
+
910
+ ### Minor Changes
911
+
912
+ - 140b05c: Resolve package versions from npm registry at scaffold time instead of hardcoding ^0.1.0, which under pre-1.0 semver rules prevented consumers from receiving updates beyond 0.1.x. Falls back gracefully if npm is unreachable. Also fixes CLI --version to read from package.json instead of a stale hardcoded string.
913
+
914
+ ## 0.3.0
915
+
916
+ ### Minor Changes
917
+
918
+ - 80aa939: Add scaffold site rendering layer with catch-all route, block components, layout components, and critical config wiring fixes.
919
+ - Fix API route template to pass config to handleActuateAPI (enables /resolve and layout regions)
920
+ - Fix admin page template to pass real config to AdminRoot (enables layout sidebar and blocks field)
921
+ - Fix /resolve endpoint to handle root path (/) by looking up "home" or "index" slug
922
+ - Add blocks-first page fields, header/footer collections, and layout config to scaffold (conditional on blocks plugin)
923
+ - Generate catch-all page route with dynamic SEO metadata and first-boot welcome page
924
+ - Generate BlockRenderer, HeroBlock, CTABlock, TextBlock, ImageTextBlock components
925
+ - Generate SiteHeader and SiteFooter layout components with graceful fallbacks
926
+
927
+ ## 0.2.0
928
+
929
+ ### Minor Changes
930
+
931
+ - 4e3a5eb: RC2-RC4 and hardening release.
932
+
933
+ **cms-core:**
934
+ - Replace isomorphic-dompurify with sanitize-html (eliminates Vercel ESM crash)
935
+ - Remove cms-core from serverExternalPackages, move to transpilePackages
936
+ - Add GET /api/cms/health endpoint with model probing and diagnostics
937
+ - Harden db() Proxy with try/catch on property access and rawDb()
938
+ - Harden safeCount/safeFindMany against Proxy throws
939
+ - Add startup validation for missing CMS_SECRET (503 instead of 500)
940
+ - Bypass auth on /stats when secret is missing (returns zero-data)
941
+ - Loosen NextConfigLike types (experimental/images/webpack as unknown)
942
+ - Implement login anomaly detection (new IP, brute force, unusual hours)
943
+ - Implement webhook DNS resolution with private range checking
944
+ - Implement TOTP reauth verification
945
+ - Add Zod validation for secret in defineConfig (min 32 chars)
946
+ - Read CMS_CORE_VERSION from package.json at runtime instead of hardcoding '0.1.0'
947
+
948
+ **cms-admin:**
949
+ - Ship pre-compiled CSS (dist/actuate-admin.css) with all 48 responsive classes
950
+ - New export: @actuate-media/cms-admin/styles/precompiled.css
951
+ - Add CSS containment (`contain: layout style`) on .actuate-admin
952
+ - Fix ShortcutHelp modal using React state instead of DOM manipulation
953
+ - Add fixed-position layout isolation wrapper to AdminRoot
954
+ - Add useApiData retry limits (maxRetries=3) with exponential backoff
955
+ - Dashboard shows health-aware "Database Setup Required" banner
956
+
957
+ **cli:**
958
+ - Add `actuate db:init` command (injects CMS models into Prisma schema)
959
+ - Add `actuate db:status` command (checks model presence and DB connectivity)
960
+
961
+ **create-actuate-cms:**
962
+ - Embed full CMS schema (9 models + enum) in generated schema.prisma
963
+ - Add @actuate-media/cli to scaffolded devDependencies
964
+ - Use pre-compiled CSS import in scaffolded admin layout