Commit a0ca227
feat: harden plugin SDK + split host modules for production
Three-tranche plugin SDK hardening pass, plus a parallel refactor that
splits the monolithic plugin host modules into focused submodules. Net
diff: +11.6k / -7k LOC across 207 files; ~2.8k LOC net reduction in
plugin examples thanks to the new shared primitives and APIs replacing
bespoke workaround code.
WHY: the original "build 5 plugins to find SDK gaps" strategy worked.
Each plugin (Forms / SEO / Newsletter / Analytics / Search) hit a gap
that landed in this PR — every fix here was triggered by a real plugin
breaking against a real SDK shortcoming.
TRANCHE 1 — SDK completeness
- api.cms.pages.list() / republish(id) / republishAll() — plugins can
enumerate + re-render pages from inside the QuickJS sandbox. Two new
permissions (cms.pages.read, cms.pages.publish) synced across the
4-place convention (types union + capabilities matrix + builder
aliases + permissions docs).
- publish.html filter context extended from string to (html, { pluginId,
siteId, pageId, slug }). Eliminates the Map<siteId, pageId> bridges
SEO Suite + Search had to ship to associate HTML with its page.
- ID pattern split: MANIFEST_SLUG_PATTERN (strict kebab, URL-bound) for
resource + admin-page IDs; RESOURCE_FIELD_ID_PATTERN (camelCase
allowed, JSON-key-bound) for field IDs. Two categories, two patterns.
- Republish primitives in server/publish/republish.ts reuse the
existing renderPublishedSnapshot pipeline — no duplicate render code.
TRANCHE 2 — Plugin author DX
- Tabs primitive (compound API, generic on value type, ARIA tablist,
arrow-key nav) in src/ui/components/Tabs/. Six plugins refactored
to drop ~150 LOC of bespoke tab shells.
- jsonField() helper in server/db/jsonExtract.ts — dialect-aware
JSON extraction with branded JsonFieldExpr type, allowlisted in
db-postgres-isms.test.ts plus a NEW bidirectional egress gate
(json-extract-egress.test.ts) preventing the patterns from spreading.
- cms.storage.collection().list({ filter, orderBy, limit, offset })
with { records, totalCount } envelope. Full operator suite
(eq/ne/gt/gte/lt/lte/in/like). TypeBox schemas in storageSchemas.ts
as source of truth. Six plugins refactored: server-side filters
replace JS-side filter loops.
BOOT RESILIENCE
- InstalledPluginResult discriminated union applied to BOTH read AND
write repository paths. Boot loop now isolates per-phase failures
(manifest-validation / module-pack-load / server-entrypoint-load /
activate) — one broken plugin can no longer crash the entire CMS.
- Admin UI surfaces error-state plugins with Reinstall + Remove
affordances; Settings / Schedules / Enable hidden when manifest
is unparseable. 409 Conflict for PATCH/restart on broken plugins.
- frontendInjections filters broken plugins so they can't pollute
published pages with stale injection plans.
CSP RELAXATION FIX
- relaxCspForPlan() previously only relaxed script-src for INLINE
scripts; external scripts only relaxed worker-src. Tracker plugins
with a single external <script src=…> tag had their tag injected
but blocked by script-src 'none'. Fixed: external scripts now also
relax script-src to 'self' (same-origin, where plugin bundles live).
Gated by 4 new tests covering all four CSP relaxation tiers.
PARALLEL REFACTOR — host module split
- server/plugins/quickjsHost.ts → server/plugins/quickjs/{...}
- server/plugins/pluginWorkerHost.ts → server/plugins/host/{...}
- server/plugins/workerProtocol.ts → server/plugins/protocol/{...}
- server/publish/dataRenderer.ts → folded into publishedHtmlPipeline.ts
- server/handlers/cms/tracker.ts → replaced by the declarative
frontend.assets[] surface
- BlockPicker → BlockLibrary (dock-style picker with persistent height)
- Various editor-store slice splits
USER PREFERENCES (already shipped in prior commit, refined here)
- Server-backed dashboard layout persistence per user via
user_preferences (user_id, key, value_json) table. Hook is
optimistic + debounced + flushes on unmount.
- Repository functions renamed to *Row suffix for consistency with
other repos (readMediaAssetRow pattern).
NEW HOST-UI PRIMITIVES (cumulative across all tranches)
- Widget, WidgetList, RangeTabs, Tabs / TabList / Tab / TabPanel,
Sparkline, Bars, StackedBar, StatValue, Delta — all exposed via
@pagebuilder/host-ui, gated by plugin-host-ui-runtime-parity.test.
ARCHITECTURE INVARIANTS ADDED
- plugin-cms-pages-surface.test.ts
- publish-html-filter-context.test.ts
- no-plugin-tab-shells.test.ts
- storage-list-envelope.test.ts
- json-extract-egress.test.ts
- plugin-boot-resilience.test.ts
- frontendInjections.test.ts (4 CSP relaxation tiers)
- dispatcher-html-pipeline.test.ts
- centralized-site-mutation-history.test.ts
- visual-components-mutation-contract.test.ts
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>1 parent 38d083b commit a0ca227
207 files changed
Lines changed: 11588 additions & 7041 deletions
File tree
- docs/plugins
- examples/plugins
- analytics
- frontend
- server
- forms-builder
- admin
- server
- newsletter
- admin
- sections
- server
- search
- admin
- sections
- server
- seo-suite
- admin
- server
- showcase
- frontend
- server
- three-kit
- frontend
- uptime-monitor/server
- public/runtime
- server
- db
- handlers/cms
- plugins
- plugins
- host
- handlers
- protocol
- schemas
- quickjs
- bootstrap
- publish
- runtime
- repositories
- data
- src
- __tests__
- architecture
- editor-store
- persistence
- plugins
- publisher
- server
- toolbar
- admin
- layouts/AdminCanvasLayout
- modals/Settings
- pages
- dashboard/components
- plugins
- components
- PermissionReviewSection
- PluginCard
- site
- store
- slices
- site
- framework
- toolbar
- plugin-host-ui
- state
- core
- persistence
- plugin-sdk
- builders
- cli
- plugins
- ui/components/Tabs
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
64 | | - | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
65 | 69 | | |
66 | 70 | | |
67 | 71 | | |
| |||
73 | 77 | | |
74 | 78 | | |
75 | 79 | | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
76 | 94 | | |
77 | 95 | | |
78 | 96 | | |
| |||
81 | 99 | | |
82 | 100 | | |
83 | 101 | | |
84 | | - | |
| 102 | + | |
85 | 103 | | |
86 | 104 | | |
87 | 105 | | |
| |||
133 | 151 | | |
134 | 152 | | |
135 | 153 | | |
136 | | - | |
137 | | - | |
| 154 | + | |
| 155 | + | |
138 | 156 | | |
139 | 157 | | |
140 | 158 | | |
| |||
183 | 201 | | |
184 | 202 | | |
185 | 203 | | |
186 | | - | |
| 204 | + | |
187 | 205 | | |
188 | | - | |
| 206 | + | |
189 | 207 | | |
190 | 208 | | |
191 | | - | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
192 | 224 | | |
193 | 225 | | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
194 | 240 | | |
195 | 241 | | |
196 | 242 | | |
| |||
240 | 286 | | |
241 | 287 | | |
242 | 288 | | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
243 | 294 | | |
244 | 295 | | |
245 | 296 | | |
| |||
263 | 314 | | |
264 | 315 | | |
265 | 316 | | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
266 | 347 | | |
267 | 348 | | |
268 | 349 | | |
| |||
720 | 801 | | |
721 | 802 | | |
722 | 803 | | |
723 | | - | |
| 804 | + | |
724 | 805 | | |
725 | | - | |
| 806 | + | |
726 | 807 | | |
727 | | - | |
| 808 | + | |
728 | 809 | | |
729 | 810 | | |
730 | | - | |
731 | | - | |
732 | | - | |
733 | | - | |
734 | | - | |
735 | | - | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
736 | 838 | | |
737 | 839 | | |
738 | | - | |
| 840 | + | |
739 | 841 | | |
740 | | - | |
741 | | - | |
742 | | - | |
743 | | - | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
744 | 856 | | |
745 | 857 | | |
746 | 858 | | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
747 | 874 | | |
748 | 875 | | |
749 | 876 | | |
| |||
771 | 898 | | |
772 | 899 | | |
773 | 900 | | |
774 | | - | |
775 | 901 | | |
776 | 902 | | |
777 | 903 | | |
778 | 904 | | |
779 | | - | |
780 | | - | |
781 | | - | |
782 | | - | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
| 914 | + | |
| 915 | + | |
| 916 | + | |
783 | 917 | | |
784 | 918 | | |
785 | 919 | | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
786 | 962 | | |
787 | 963 | | |
788 | 964 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
38 | | - | |
| 37 | + | |
39 | 38 | | |
| 39 | + | |
| 40 | + | |
40 | 41 | | |
41 | 42 | | |
42 | 43 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
186 | 195 | | |
187 | 196 | | |
188 | 197 | | |
| |||
Binary file not shown.
0 commit comments