@believablecreations/bc-app-cms
v0.2.3
Published
Extensible application content management system for Angular
Downloads
780
Readme
@believablecreations/bc-app-cms
BC App CMS is a publishable Angular client and UI for an extensible application content management system.
The package is split into secondary entry points so applications only download a feature when its route or component is used:
@believablecreations/bc-app-cms— configuration, HTTP client, route composition@believablecreations/bc-app-cms/api— CMS endpoint and access metadata maps@believablecreations/bc-app-cms/pages— page models, APIs, forms, state, and page administration@believablecreations/bc-app-cms/media— media models, API, NgRx state, uploads, screens, and routes@believablecreations/bc-app-cms/blog— blog models, API, NgRx state, public/admin screens, and routes@believablecreations/bc-app-cms/contact— contact models, API, state, form, admin screens, and routes@believablecreations/bc-app-cms/config— application, API, security, SMS, and location APIs, NgRx state, and admin routes@believablecreations/bc-app-cms/email— email models, APIs, NgRx state, administration screen, and routes@believablecreations/bc-app-cms/access-control— permissions, roles, users, schemas, auth-cache APIs, NgRx state, and routes@believablecreations/bc-app-cms/system— system import/export, audit, rollback, deferred-operation APIs/state, and routes@believablecreations/bc-app-cms/users— user models, API, NgRx state, profile/admin screens, and routes@believablecreations/bc-app-cms/notifications— notification APIs, realtime NgRx-style state, center, preferences, and routes@believablecreations/bc-app-cms/store— commerce, checkout, orders, products, subscriptions, tax APIs, NgRx state, and routes@believablecreations/bc-app-cms/shipping— shipping models, forms, APIs, NgRx state, and admin route@believablecreations/bc-app-cms/square— Square catalog, media, order, subscription APIs, NgRx state, and admin route@believablecreations/bc-app-cms/auth— auth APIs/forms, NgRx Signal Store, interceptor, adapters, screens, and routes@believablecreations/bc-app-cms/media-config— media configuration, security incidents, and NgRx state@believablecreations/bc-app-cms/routes— one-call routes for package-owned CMS screens@believablecreations/bc-app-cms/shared— reusable controls such as the international phone input@believablecreations/bc-app-cms/shell— reusable application shell, guards, realtime socket, consent, analytics, SEO, UI components, and shared application models
Install
npm install @believablecreations/bc-app-cms @ngrx/signals \
@believablecreations/ngx-focus-point \
@fortawesome/angular-fontawesome @fortawesome/free-brands-svg-iconsAngular, Angular Material/CDK, RxJS, NgRx Signals, Focus Point, and the public Font Awesome brand packages are peer dependencies. This keeps the consuming application in control of framework versions and prevents duplicate Angular or NgRx runtimes. Use Angular and NgRx versions whose peer ranges agree.
Use local library changes
The package includes the bc-app-cms-local command. For the first local setup in
a consuming application, point it at the Believable Creations source workspace:
npx --no-install bc-app-cms-local \
--source "/absolute/path/to/believable-creations-app"The command builds BC App CMS and Focus Point, copies the completed builds to an immutable snapshot, and links both packages into the current application. Once the application is linked, rebuild and refresh the links with:
npx --no-install bc-app-cms-localThe immutable snapshot keeps a running consumer from observing ng-packagr while
it is clearing and repopulating dist. To return to the versions declared in the
consumer's package.json, run:
npx --no-install bc-app-cms-local --unlinkAn application may add these commands as npm scripts, but it does not need its own linking or build helper.
Configure the client
import {provideHttpClient, withInterceptors} from '@angular/common/http';
import {provideBelievableCms} from '@believablecreations/bc-app-cms';
import {cmsAuthInterceptor, provideCmsAuthInterceptor} from '@believablecreations/bc-app-cms/auth';
import {provideCmsShell} from '@believablecreations/bc-app-cms/shell';
export const appConfig: ApplicationConfig = {
providers: [
provideBelievableCms({
apiUrl: 'https://cms.example.com/api',
adminPath: 'admin',
defaultLanguage: 'en',
branding: {
appName: 'Example',
logoUrl: '/assets/example-logo.svg',
iconUrl: '/assets/example-icon.svg',
},
auth: {
theme: 'centered-card',
},
}),
provideCmsShell({
baseUrl: 'https://www.example.com',
socketUrl: 'https://cms.example.com',
requireCookieConsentForGoogleAnalytics: true,
}),
provideCmsAuthInterceptor({
accessToken: () => sessionStorage.getItem('access-token'),
onTokenExpired: () => location.assign('/auth/sign-in'),
}),
provideHttpClient(withInterceptors([cmsAuthInterceptor])),
],
};The auth interceptor only modifies requests under the configured CMS API URL. Realtime and notification side effects are exposed through optional adapters so the package can use the host application's socket, toast, and service-worker choices without importing host code.
User-facing package branding is read from the active AppConfig.appName value.
Navigation labels, accessibility text, document titles, SEO, wallet labels,
configuration defaults and email-template examples all use App when appName is
missing, blank, or whitespace-only. The root entry point exports
resolveCmsAppName() and DEFAULT_CMS_APP_NAME for host-owned UI that needs the
same behavior.
Set branding.logoUrl and branding.iconUrl to host-owned image URLs. Auth
renders the full logo on wider layouts and the compact icon on smaller screens.
The legacy LOGO_CONFIG provider and direct app-logo logo/icon inputs
remain supported and take precedence over the root branding defaults. The host
is responsible for serving both assets.
Choose an authentication layout
Authentication themes are layout presets only. The package supplies the box structure and responsive behavior; the consuming application owns colors, typography, borders, imagery, and other visual treatment in global styles.
provideBelievableCms({
apiUrl: 'https://cms.example.com/api',
auth: {
theme: 'split-panel',
// `alternate` matches the supplied references: aside after sign-in,
// aside before sign-up.
splitPanelPosition: 'alternate',
},
});Available themes:
centered-card— brand above one centered form panel.split-panel— supporting content and the form in two columns. Below768px, the supporting panel collapses and the form's account-switch action remains available.
splitPanelPosition accepts start, end, or alternate. The exported
CMS_AUTH_LAYOUT_THEMES constant and CmsAuthLayoutTheme type can drive a host
configuration picker without duplicating the supported values.
The rendered layout exposes these stable global styling hooks:
bc-cms-auth-layoutand.bc-cms-auth.bc-cms-auth-scene,.bc-cms-auth-shell.bc-cms-auth-brand,.bc-cms-auth-brand-content.bc-cms-brand-logo,.bc-cms-brand-icon, anddata-bc-cms-brand-asset.bc-cms-auth-form,.bc-cms-auth-form-content.bc-cms-auth-aside,.bc-cms-auth-aside-content.bc-cms-auth-aside-brand,.bc-cms-auth-aside-title,.bc-cms-auth-aside-description, and.bc-cms-auth-aside-action.bc-cms-auth-switchdata-bc-cms-auth-theme,data-bc-cms-auth-view, anddata-bc-cms-auth-aside-position
Layout sizing can be tuned with
--bc-cms-auth-scene-padding, --bc-cms-auth-scene-padding-mobile,
--bc-cms-auth-shell-max-width, --bc-cms-auth-shell-max-width-mobile,
--bc-cms-auth-shell-gap, --bc-cms-auth-split-gap,
--bc-cms-auth-split-aside-width, --bc-cms-auth-split-form-width,
--bc-cms-auth-aside-padding, and
--bc-cms-auth-aside-action-min-width.
For example, a host can apply its design system without changing the selected structure:
bc-cms-auth-layout {
--bc-cms-auth-shell-max-width: 68rem;
--bc-cms-auth-aside-padding: 4rem;
}
.bc-cms-auth-scene {
background: var(--app-auth-page-background);
font-family: var(--app-body-font);
}
.bc-cms-auth-form-content,
.bc-cms-auth-aside-content {
background: var(--app-auth-panel-background);
color: var(--app-auth-panel-foreground);
}CmsAuthLayoutComponent is also exported from
@believablecreations/bc-app-cms/auth. A host-owned auth screen can pass its
theme input directly; package-owned screens read the same value from
provideBelievableCms().
Mount all package-owned screens
import {createBelievableCmsRoutes} from '@believablecreations/bc-app-cms/routes';
export const routes: Routes = [
...createBelievableCmsRoutes(),
];The preset includes public auth/blog/contact/store screens, authenticated account
and notification screens, and CMS administration for pages, users, access control,
media, email, configuration, security, system, store, Square, and shipping. It
returns final full-path route rows rather than guarded feature mounts. Public rows
have no auth guard, customer-owned rows carry authGuard, and every administration
row carries both authGuard and the exact API ACL action/resource guard required
by that screen. Each feature can still be disabled with false or given a custom
path, aliases, additional host guards, or a replacement lazy loader.
When the backend reports mustChangePassword, the auth guards redirect the
session to /auth/change-password until the temporary password is replaced.
Mount one extracted feature
export const routes: Routes = [
{
path: 'admin/contact',
loadChildren: () => import('@believablecreations/bc-app-cms/contact')
.then(({CONTACT_ADMIN_ROUTES}) => CONTACT_ADMIN_ROUTES),
},
];Feature route arrays retain their own route-level guards when mounted separately.
The application mounts these package routes directly. CMS feature sources belong only in this package; consuming applications should not keep app-local copies or compatibility barrels.
Extend the right-panel menu
Host applications and separately installed feature libraries can contribute items without copying or modifying the package menu:
import {provideCmsMenuItems} from '@believablecreations/bc-app-cms/shell';
export const appConfig: ApplicationConfig = {
providers: [
provideCmsMenuItems(
{
// `root` is the right panel itself.
targetId: 'root',
audience: 'authenticated',
placement: {after: 'shop'},
items: [{
id: 'appointments',
icon: 'event',
title: 'Appointments',
link: '/appointments',
params: {type: 'none'},
}],
},
{
// Any existing container ID can be targeted.
targetId: 'administrator',
audience: 'authenticated',
placement: {before: 'settings'},
items: ({permissions, userId}) => permissions.canReadAnyPage ? [{
id: 'host-reports',
icon: 'analytics',
title: `Reports for ${userId}`,
link: '/admin/reports',
params: {type: 'none'},
}] : [],
},
),
],
};Each call is a multi-provider, so multiple host features can register their own
contributions independently. targetId can be root, administrator,
store-management, or any other menu container ID. Placement supports start,
end, {before: 'item-id'}, and {after: 'item-id'}. Item factories receive
the current authentication state, user ID, store visibility, and package
permission flags and are reevaluated with the right-panel computed signal.
Visible contributions reveal a hidden target and its ancestors by default. Set
revealTarget: false when the package container's original visibility gate must
remain authoritative.
NgRx state
Feature state is built with @ngrx/signals and @ngrx/signals/entities. Install a
compatible NgRx Signals version alongside Angular; it is a peer dependency so the
host and package always share one NgRx runtime.
Most package stores are root-provided and can be injected directly:
import {CmsAuthStore} from '@believablecreations/bc-app-cms/auth';
import {MediaStore} from '@believablecreations/bc-app-cms/media';
const auth = inject(CmsAuthStore);
const media = inject(MediaStore);Editor/draft stores that should not share state across concurrent screens are exported for route- or component-level providers. Package-owned screens provide those scoped stores themselves.
Content Security Policy and Trusted Types
The root entry point exports immutable defaults and an additive policy builder:
import {
CmsSecurityPolicyBuilder,
DEFAULT_CMS_CSP_DIRECTIVES,
DEFAULT_CMS_TRUSTED_TYPE_POLICIES,
} from '@believablecreations/bc-app-cms';
const policy = new CmsSecurityPolicyBuilder()
.whitelist('connect-src', 'https://cms.example.com', 'wss://cms.example.com')
.blacklist('script-src', "'wasm-unsafe-eval'")
.whitelistTrustedTypePolicies('my-editor-policy')
.blacklistTrustedTypePolicies('angular#unsafe-bypass')
.build(requestNonce);Allowlist/whitelist entries extend the package defaults. Blocklist/blacklist
entries are evaluated last and therefore always win. disableDirectives() can
remove an entire directive. For non-fluent configuration, use
createCmsContentSecurityPolicy({nonce, allowlist, blocklist}).
provideBelievableCms() also installs the package's DOMPurify-backed default
Trusted Types policy in the browser. This supplies both TrustedHTML for Quill
and other reviewed editor integrations and restricted TrustedScriptURL
handling for same-origin, analytics, Square, and package-supported loaders.
Applications using the policy builder therefore do not need an inline default
policy. Add an application-specific dynamic script origin only when required:
provideBelievableCms({
apiUrl: 'https://cms.example.com/api',
trustedTypes: {
allowedScriptOrigins: ['https://scripts.example.com'],
},
});For a host that treats every DOM assignment in the CMS document as trusted,
enable the permissive default policy. The CSP script-src directive still
controls which script resources the browser may execute:
provideBelievableCms({
apiUrl: 'https://cms.example.com/api',
trustedTypes: {trustAll: true},
});Dependency tree
The arrows below mean “imports or composes.” The consuming application sits at the top; the root package supplies configuration, URL construction, HTTP access, realtime adapters, SSR tokens, branding, and security policy helpers used throughout the feature entry points.
flowchart TD
APP["Consuming Angular application"] --> ROOT["@believablecreations/bc-app-cms (root)"]
APP --> ROUTES["/routes"]
APP --> SHELL["/shell"]
APP --> FEATURES["Feature entry points"]
ROUTES --> LEGACY["/legacy-ui"]
ROUTES --> PAGES["/pages"]
ROUTES --> AUTH["/auth"]
ROUTES --> ACL["/access-control"]
SHELL --> ROOT
SHELL --> AUTH
SHELL --> ACL
SHELL --> CONFIG["/config"]
SHELL --> SHARED["/shared"]
SHELL --> MEDIA["/media"]
SHELL --> EMAIL["/email"]
SHELL --> NOTIFY["/notifications"]
SHELL --> STORE["/store"]
SHELL --> USERS["/users"]
SHELL --> SQUARE["/square"]
FEATURES --> ROOT
FEATURES --> AUTH
FEATURES --> ACL
STORE --> USERS
STORE --> MEDIA
STORE --> SHIPPING["/shipping"]
SQUARE --> ACL
USERS --> MEDIA
PAGES --> SHARED
CONTACT["/contact"] --> SHARED
LEGACY --> ROOT
LEGACY --> FEATURESEntry-point dependencies
| Entry point | Direct package dependencies | Main external dependencies | | --- | --- | --- | | root | none | Angular core/common, RxJS | | routes | legacy-ui, pages, auth, access-control, shell (tests/link validation) | Angular Router | | shell | root, shared, auth, access-control, config, media, notifications, store, users, email, square | Material/CDK, service worker, Socket.IO, Font Awesome | | legacy-ui | root plus most feature entry points | Material/CDK, NgRx Signals, Quill, Monaco, Square SDK, Focus Point | | access-control | root, auth | Angular HTTP/Router, NgRx Signals | | auth | root | Angular HTTP/Router/Forms, NgRx Signals, RxJS | | blog | root, auth, access-control | Angular Router/Forms, NgRx Signals | | config | root, legacy-ui routes | Angular HTTP/Router, NgRx Signals | | contact | root, auth, access-control, shared | Angular Forms/Router, NgRx Signals | | email | root, auth, access-control | Angular HTTP/Router, NgRx Signals | | media | root, auth, access-control | Angular HTTP/Router, NgRx Signals, Focus Point | | media-config | root | Angular HTTP, NgRx Signals | | notifications | root, auth | Angular HTTP/Router, RxJS | | pages | root, auth, access-control, shared | Angular Router/Forms, NgRx Signals | | shared | root | Angular Forms, libphonenumber-js | | shipping | root, auth, access-control | Angular HTTP/Router/Forms, NgRx Signals | | square | root, auth, access-control | Angular HTTP/Router, NgRx Signals, Square SDK types | | store | root, auth, access-control, users, media, shipping | Angular Router/Forms, NgRx Signals, Square Web SDK | | system | root, auth, access-control | Angular HTTP/Router, NgRx Signals | | users | root, auth, access-control, media | Angular HTTP/Router, NgRx Signals | | api | compatibility layer; generally independent of newer feature stores | Angular HTTP | | smoke-consumer | imports every public entry point | TypeScript compiler only |
Layering rules
- The root entry point must not import feature entry points.
- Feature HTTP services should depend on the root
CmsApiClient. - Administration routes depend on
authGuardandCanAccessGuard(). - Shared UI may depend on stable feature stores, but domain features should avoid depending on
shell. - Cross-domain models should be referenced from their owning public entry point.
- The route composer may depend on every route family; features must not depend on the route composer.
- Consumers must import public package paths only, never
src/libpaths.
Peer dependencies are installed once by the consuming application. They are not bundled into this package, which prevents duplicate Angular, NgRx, Material, editor, payment, or icon runtimes.
