Files
marketplaces/docs/BOOTSTRAP.md
sdarbinyan 76831b8485 docs: consolidate scattered docs into canonical set
Replace ~35 organically-grown docs (docs/platform/*, docs/backend-platform/*,
one-off sprint reports, Search.md, Diagnostics.md, Content-Management.md,
Backend-Handoff-Sprint16.md, docs/superpowers/*, docs/Project-Editor.md,
untracked docs/total.md) with the six canonical docs declared in
.claude/CLAUDE.md: PROJECT.md, ARCHITECTURE.md, BACKEND.md, FRONTEND.md,
BOOTSTRAP.md, EDITOR.md, plus a new PROJECT-STRUCTURE.md.

- BACKEND.md is a punch list per domain (auth, bootstrap draft/publish,
  static pages, categories, products, orders, dashboard metrics, activity,
  translations, search, product engagement) plus a Known reliability issues
  section on the prod 502/504 root cause.
- ARCHITECTURE.md links to (does not duplicate) the enforced
  docs/architecture/foundation/** ADRs and standards docs.
- docs/ADMIN.md and docs/architecture/foundation/** and docs/context/** are
  left untouched per instructions.
- Updated the one dangling docs/Project-Editor.md reference in
  admin-auth.service.ts to point at docs/BACKEND.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 12:28:41 +04:00

9.8 KiB

BOOTSTRAP

The BootstrapConfig model (src/app/shared/models/config/bootstrap-config.model.ts) is the single JSON contract that drives the entire storefront for a tenant. ConfigService loads it once at startup via GET /bootstrap (tenant resolved server-side by request host); PlatformRuntimeService applies it and can re-apply an edited in-memory copy for preview.

Top-level shape

interface BootstrapConfig {
  schemaVersion: string;
  generatedAt: string;
  tenant: TenantConfig;                 // required
  branding: BrandingConfig;              // required
  theme: ThemeConfig;                    // required
  company: CompanyConfig;                // required
  featureFlags: FeatureFlagsConfig;      // required
  features?: MarketplaceFeaturesConfig;  // optional, centralized feature toggles
  apiEndpoints: ApiEndpointsConfig;      // required
  localization: LocalizationConfig;      // required
  seo: SeoConfig;                        // required
  permissions: PermissionsConfig;        // required
  header?: HeaderConfig;
  catalog?: CatalogConfig;
  layout?: PlatformLayoutConfig;
  navigation: NavigationConfig;          // required
  footer?: FooterConfig;
  productPage?: ProductPageConfig;
  userExperience?: UserExperienceConfig;
  pages: PageConfig[];                   // required
  staticPages?: StaticPagesConfig;
  widgetRegistry?: WidgetRegistryConfig;
}

Field-by-field

Key Meaning
schemaVersion Contract version. Breaking changes require a bump; frontend must stay compatible within a minor line.
generatedAt Payload generation timestamp.
tenant id, slug, host, name, defaultLocale, supportedLocales, defaultCurrency, websiteBaseUrl, etc. Tenant is resolved by domain only — never re-derived on the frontend.
branding logoUrl, logoCompactUrl, faviconUrl, brandName.
theme themeId, mode (light|dark|system), palette (12 semantic colors), typography, spacing, borderRadiusScale, shadows, iconSet. See docs/ARCHITECTURE.md theme engine section.
company Legal/contact info used in the footer: companyName, address, contacts.phone/email.
featureFlags Simple boolean toggles: wishlist, compare, reviews, comments, recommendations, etc.
features Newer centralized feature surface (MarketplaceFeaturesConfig) — wishlist/compare/reviews/comments/questions/recommendations/recentlyViewed/searchHistory/recentlySearched/ratings/share/brands/manufacturers/availability/discounts/badges. Resolvers fall back to featureFlags/productPage/userExperience/catalog for older bootstraps.
apiEndpoints Public endpoint map the frontend's API layer reads (base URLs, paths, timeouts). Never contains secrets.
localization defaultLocale, supportedLocales, optional currencyByLocale.
seo default.title/default.description plus per-page SEO overrides.
permissions Roles/permissions for admin surfaces (currently minimal; see docs/BACKEND.md).
header Boolean toggles: showLogo, showSearch, showCategories, showLanguages, showCart, showProfile, showWishlist, showCompare, showRegion.
catalog UI/feature config only (no product data) — see Catalog Config below.
layout PlatformLayoutConfig: `{ type: 'default'
navigation header[] / footer[] link arrays: id, label (translatable), route, order, visible.
footer Payment icons, social links, copyright (per-locale), static-page references.
productPage Feature config only for the product detail page — rating/reviews/questions/tabs/relatedProducts/actions enablement, pagination size, mode. No review/question data lives here.
userExperience Feature config only for wishlist/compare/recentlyViewed/share/savedSearches — flags and limits, never user-specific lists.
pages Array of PageConfig: id, key, route.path, sections: SectionConfig[].
staticPages CMS-style informational/legal pages — see staticPages below.
widgetRegistry Pointer/metadata for the widget manifest (see docs/ARCHITECTURE.md widget engine).

Section config (shared/models/config/section.model.ts)

type SectionLayoutStrategy = 'stack' | 'grid' | 'hero' | 'carousel' | 'split';

interface SectionConfig {
  id: string; type: string; order: number;
  layout?: { strategy?: SectionLayoutStrategy; columns?: number; gap?: string; align?: 'start'|'center'|'end'|'stretch' };
  visibility?: { desktop?: boolean; tablet?: boolean; mobile?: boolean };
  widgets: WidgetConfig[];
  featureFlag?: string;
  visible?: boolean;
}

Widget config (shared/models/config/widget.model.ts)

Each widget has id, type, version, optional title/subtitle/order/padding/visibility/animation/style/permissions/actions/featureFlag/visible, and props: Record<string, unknown> (widget-specific). Typed editors exist in the Project Editor for hero, categories, product-collection; everything else edits props as raw JSON (see docs/EDITOR.md).

Catalog Config (shared/models/config/catalog-config.model.ts)

type CatalogLayoutModeConfig = 'grid'|'large-grid'|'compact-grid'|'grid-2'|'grid-3'|'grid-4'|'compact'|'list';
type CatalogNavigationModeConfig = 'default'|'left-category-navigation'|'mega-category-layout'|'top-category-carousel';
type CatalogLoadingStrategy = 'pagination'|'loadMore'|'infiniteScroll';

Plus defaultSort/availableSorts (relevance|latest|price_asc|price_desc|rating|popular|discount), enabledFilters: string[], and show*/*Enabled booleans (breadcrumbs, category banner, subcategory chips, ratings, discounts, availability, suggestions, search history). This is feature-configuration only — no product/filter data lives in bootstrap.

staticPages

Each entry: id, slug, title, showInHeader, showInFooter, showInSitemap, icon, order, visibility, requiresAuthentication, footerGroup, translations[locale] = { title, html, seo }. Rendered dynamically (no hardcoded page list); HTML is sanitized on render. Known inconsistency: the model requires slug, but the mock bootstrap only populates route for some pages — the frontend's duplicate-slug validator falls back to route when slug is empty (see docs/BACKEND.md).

Representative example (trimmed)

{
  "schemaVersion": "2.1.0",
  "generatedAt": "2026-07-05T10:30:00Z",
  "tenant": {
    "id": "tenant-dexar-ru", "slug": "dexar-ru", "host": "dexarmarket.ru",
    "name": "Dexar Market", "defaultLocale": "ru", "supportedLocales": ["ru", "en", "hy"],
    "defaultCurrency": "RUB", "websiteBaseUrl": "https://dexarmarket.ru"
  },
  "branding": { "logoUrl": "/assets/brand/logo.svg", "faviconUrl": "/assets/brand/favicon.ico", "brandName": "Dexar Market" },
  "theme": {
    "themeId": "dexar-light", "mode": "light",
    "palette": { "primary": "#2F6E5D", "secondary": "#8FA9A2", "backgroundPrimary": "#FFFFFF", "textPrimary": "#1F322D" },
    "typography": { "primaryFontFamily": "DM Sans, sans-serif", "baseFontSize": 16 }
  },
  "layout": { "type": "default" },
  "catalog": { "layout": "grid-4", "navigationMode": "default", "defaultSort": "relevance", "showRatings": true },
  "navigation": {
    "header": [ { "id": "nav-home", "label": "Home", "route": "/", "order": 1, "visible": true } ],
    "footer": [ { "id": "nav-privacy", "label": "Privacy", "route": "/privacy-policy", "order": 1, "visible": true } ]
  },
  "pages": [
    {
      "id": "page-home", "key": "home", "route": { "path": "/", "exact": true },
      "sections": [
        {
          "id": "home-hero", "type": "hero", "order": 1,
          "layout": { "strategy": "hero" },
          "widgets": [ { "id": "w-hero", "type": "hero", "version": "1.0.0", "props": { "title": "Welcome", "layout": "full-bleed" } } ]
        },
        {
          "id": "home-categories", "type": "categories", "order": 2,
          "layout": { "strategy": "grid", "columns": 4 },
          "widgets": [ { "id": "w-categories", "type": "categories", "version": "1.0.0", "props": { "columns": 4 } } ]
        }
      ]
    }
  ],
  "staticPages": [
    { "id": "static-about", "slug": "about", "title": { "en": "About Us" }, "showInFooter": true, "showInHeader": false, "translations": { "en": { "html": "<h1>About Us</h1>" } } }
  ]
}

How ConfigService / PlatformRuntimeService consume it

  1. ConfigService.loadBootstrap() fetches (or, in mock mode, reads local JSON under src/assets/mock/bootstrap/) and parses BootstrapConfig.
  2. PlatformRuntimeService applies theme tokens as CSS variables, sets branding, and exposes the parsed pages/navigation/footer/static-pages to the rest of the app.
  3. Section Engine / Widget Host render pages from bootstrap.pages on route match (see docs/ARCHITECTURE.md).
  4. PlatformRuntimeService.reloadFromBootstrap(next) re-applies an entire new BootstrapConfig in-memory — this is what the Project Editor's Publish action (and Preview) use, without a full browser reload.

How the Project Editor edits it, and draft/publish/preview

The Project Editor (docs/EDITOR.md) edits an in-memory copy of the exact same BootstrapConfig — there is no parallel editor model or DTO translation layer. Today:

  • Load: GET /bootstrap (same endpoint the storefront uses).
  • Save: in-memory snapshot only, persisted to localStorage as a draft (projectEditor.draftBootstrap.v1, scoped by tenant.id) so it survives reloads on the same browser.
  • Publish: runs ProjectValidator, then calls PlatformRuntimeService.reloadFromBootstrap() for live in-memory preview and flips a local status flag — no backend call happens. This is the largest gap covered in docs/BACKEND.md.
  • Preview: same in-memory re-apply mechanism, without marking the state published.