@actuate-media/cms-core 0.35.0 → 0.36.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 (44) hide show
  1. package/dist/__tests__/api/appearance-routes.test.d.ts +2 -0
  2. package/dist/__tests__/api/appearance-routes.test.d.ts.map +1 -0
  3. package/dist/__tests__/api/appearance-routes.test.js +134 -0
  4. package/dist/__tests__/api/appearance-routes.test.js.map +1 -0
  5. package/dist/__tests__/api/globals-routes.test.d.ts +2 -0
  6. package/dist/__tests__/api/globals-routes.test.d.ts.map +1 -0
  7. package/dist/__tests__/api/globals-routes.test.js +100 -0
  8. package/dist/__tests__/api/globals-routes.test.js.map +1 -0
  9. package/dist/__tests__/appearance/appearance.test.d.ts +2 -0
  10. package/dist/__tests__/appearance/appearance.test.d.ts.map +1 -0
  11. package/dist/__tests__/appearance/appearance.test.js +273 -0
  12. package/dist/__tests__/appearance/appearance.test.js.map +1 -0
  13. package/dist/api/handlers.d.ts.map +1 -1
  14. package/dist/api/handlers.js +68 -4
  15. package/dist/api/handlers.js.map +1 -1
  16. package/dist/appearance/color.d.ts +24 -0
  17. package/dist/appearance/color.d.ts.map +1 -0
  18. package/dist/appearance/color.js +90 -0
  19. package/dist/appearance/color.js.map +1 -0
  20. package/dist/appearance/index.d.ts +13 -0
  21. package/dist/appearance/index.d.ts.map +1 -0
  22. package/dist/appearance/index.js +5 -0
  23. package/dist/appearance/index.js.map +1 -0
  24. package/dist/appearance/logo-shape.d.ts +26 -0
  25. package/dist/appearance/logo-shape.d.ts.map +1 -0
  26. package/dist/appearance/logo-shape.js +62 -0
  27. package/dist/appearance/logo-shape.js.map +1 -0
  28. package/dist/appearance/resolve.d.ts +46 -0
  29. package/dist/appearance/resolve.d.ts.map +1 -0
  30. package/dist/appearance/resolve.js +208 -0
  31. package/dist/appearance/resolve.js.map +1 -0
  32. package/dist/appearance/theme-tokens.d.ts +50 -0
  33. package/dist/appearance/theme-tokens.d.ts.map +1 -0
  34. package/dist/appearance/theme-tokens.js +198 -0
  35. package/dist/appearance/theme-tokens.js.map +1 -0
  36. package/dist/appearance/types.d.ts +110 -0
  37. package/dist/appearance/types.d.ts.map +1 -0
  38. package/dist/appearance/types.js +16 -0
  39. package/dist/appearance/types.js.map +1 -0
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +2 -0
  43. package/dist/index.js.map +1 -1
  44. package/package.json +6 -1
@@ -8897,6 +8897,58 @@ export function registerCMSRoutes(router) {
8897
8897
  return internalError(err);
8898
8898
  }
8899
8899
  });
8900
+ // Public brand: resolved logos + favicon for the public frontend. No auth —
8901
+ // brand assets are public site data. Only the PUBLIC brand view + favicon are
8902
+ // exposed here (never the admin-only brand) so a white-labeled admin doesn't
8903
+ // leak through the public API.
8904
+ router.get('/public/brand', async (request) => {
8905
+ try {
8906
+ const { resolveBrand } = await import('../appearance/index.js');
8907
+ const resolved = await resolveBrand(db(), null);
8908
+ return cachedRead(request, {
8909
+ cache: getActuateConfig()?.cache,
8910
+ tags: [ACTUATE_TAG_ROOT, actuateTag('global', 'settings')],
8911
+ wrap: jsonWithCache,
8912
+ build: async () => ({
8913
+ data: { public: resolved.public, favicon: resolved.favicon },
8914
+ }),
8915
+ });
8916
+ }
8917
+ catch (err) {
8918
+ return internalError(err, 'public/brand GET');
8919
+ }
8920
+ });
8921
+ // Authenticated appearance read: stored appearance + brand settings plus the
8922
+ // fully resolved admin/public/favicon views (with asset URLs) so the admin
8923
+ // Appearance tab can render live previews. View-only auth — writes go through
8924
+ // PUT /globals/settings (ADMIN_ROLES), so editors can view but not save.
8925
+ router.get('/appearance', async (request) => {
8926
+ try {
8927
+ const auth = await requireAuth(request);
8928
+ if (auth.error)
8929
+ return auth.error;
8930
+ const { getAppearanceSettings, getBrandSettings, resolveBrand } = await import('../appearance/index.js');
8931
+ const [appearance, brand, resolved] = await Promise.all([
8932
+ getAppearanceSettings(db()),
8933
+ getBrandSettings(db()),
8934
+ resolveBrand(db(), null),
8935
+ ]);
8936
+ return json({
8937
+ data: {
8938
+ appearance,
8939
+ brand,
8940
+ resolved: { admin: resolved.admin, public: resolved.public, favicon: resolved.favicon },
8941
+ // Direct per-asset URLs so the editor shows each field's actual
8942
+ // asset (not the resolved fallback chain).
8943
+ assets: resolved.assets,
8944
+ siteTitle: resolved.siteTitle,
8945
+ },
8946
+ });
8947
+ }
8948
+ catch (err) {
8949
+ return internalError(err, 'appearance GET');
8950
+ }
8951
+ });
8900
8952
  router.get('/globals/:slug', async (request, params) => {
8901
8953
  try {
8902
8954
  const auth = await requireAuth(request);
@@ -8912,10 +8964,13 @@ export function registerCMSRoutes(router) {
8912
8964
  build: async () => {
8913
8965
  const ctx = buildActionContext(auth.session, db());
8914
8966
  const global = await getGlobal(params.slug, ctx);
8915
- if (!global) {
8916
- return errorResponse('Global not found', 404);
8917
- }
8918
- return { data: global };
8967
+ // Globals are upsert-on-write: a slug with no row yet (fresh install,
8968
+ // never-saved settings) is a valid EMPTY state, not an error. Return
8969
+ // {} so the admin editor (e.g. the Settings page) loads cleanly before
8970
+ // the first save, and the next PUT creates the document. The public
8971
+ // `/public/globals/:slug` route keeps its config-gated 404 for
8972
+ // genuinely undeclared globals.
8973
+ return { data: global ?? {} };
8919
8974
  },
8920
8975
  });
8921
8976
  }
@@ -8943,8 +8998,17 @@ export function registerCMSRoutes(router) {
8943
8998
  // Audit settings changes (title/tagline/URL/locale/timezone/etc.) with a
8944
8999
  // per-key before/after diff so the audit trail records what changed and
8945
9000
  // who changed it. Scalar values only — we never log nested secret blobs.
9001
+ // Nested config blobs (`appearance`, `brand`) are logged as a changed
9002
+ // key (not their contents) so appearance/branding edits are auditable
9003
+ // without dumping asset IDs or theme objects into the log.
8946
9004
  try {
8947
9005
  const changes = diffScalarKeys(previous ?? {}, body);
9006
+ const prev = (previous ?? {});
9007
+ for (const key of ['appearance', 'brand']) {
9008
+ if (key in body && JSON.stringify(prev[key]) !== JSON.stringify(body[key])) {
9009
+ changes.push({ key, from: '[changed]', to: '[changed]' });
9010
+ }
9011
+ }
8948
9012
  if (changes.length > 0) {
8949
9013
  void logEvent({
8950
9014
  event: 'settings_changed',