Litestore · Developer guide

Config

Learn what each file in config/ controls — site identity, activity logging, AI prompts, themes, color palettes, pricing defaults and the shipping country list.

config/ holds the values you edit without touching code:

  • Site identity and copy
  • Which modules write an audit trail
  • The AI system prompts
  • The four themes
  • The pricing defaults
  • The checkout country list

Every file is plain TypeScript with as const literals. There is no JSON layer and no runtime config service.

Environment variables live in env.ts, not here.

The barrel

config/index.ts composes the storefront-facing files into one config object.

config/index.ts
export const config = {
  site: siteConfig,
  links: linksConfig,
  metadata: metadataConfig,
  search: searchConfig,
  copy: copyConfig,
  heroSlides: heroSlidesConfig,
}

It is deliberately partial. Activity, AI, theme, pricing and payments are imported directly from their own modules.

config/site.ts exports four things.

siteConfig carries the store name, slug, description, and the email and URL read from env.NEXT_PUBLIC_SITE_EMAIL and env.NEXT_PUBLIC_SITE_URL.

It also holds affiliateUrl, the short-link domain that addSearchParams() treats as a passthrough so affiliate links skip ref-param rewriting.

searchConfig.filters is the three-entry facet list:

collection
category
collab

heroSlidesConfig is three static homepage hero slides with primary and secondary CTAs.

metadataConfig is a Next.js Metadata object supplying the OpenGraph image at /opengraph.png (1200×630), the summary_large_image Twitter card and the RSS alternates.

config/links.ts is the outbound-link list: five social handles read by the footer and the JSON-LD sameAs graph, plus feeds, the two RSS URLs used for discovery links.

Its header records that the upstream template's marketing surface — author homepage, cross-promo, press mentions and five affiliate links paying a third party — was deleted because nothing referenced it.

config/copy.ts is the storefront text:

  • Page titles and descriptions for managed pages, collections, categories, suppliers, blog, collabs and tags
  • The five listing section headings (Featured collections, Popular products, …)
  • Six empty-state strings
  • Five CTA labels

It exports CopyConfig as its own type.

Activity logging

config/activity.ts decides which writes reach the Activity table.

Four module lists partition every BaseCRUD model name:

  • CRITICAL_MODULES log all operations — order, refund, user, customer, product, campaign, serviceApiKey, channel
  • CREATE_DELETE_MODULES log create and delete — media, tag, coupon, socialPost, link
  • DELETE_ONLY_MODULES log delete — productVariant, emailTemplate, stockLocation, productRelationship
  • SKIP_LOGGING_MODULES log nothing — activity, address, wishlist, cart, notification

Membership is not optional. The activity-coverage rule in scripts/check-discipline.ts reads this file and fails when a BaseCRUD modelName appears in none of the four lists.

The old default was silent: an unlisted module simply got no audit trail.

CRITICAL_OPERATIONS log regardless of module:

delete
refund
cancel
ban
suspend

ACTIVITY_CLEANUP sets a 90-day retention window and a 1,000-row delete batch.

DURABLE_ACTIVITY_TYPE_PREFIXES names the prefixes that survive that purge:

commission
payout
affiliatePayout
price_changed
setting
campaign
refund
return
payment
loyalty

Order and product churn is deliberately not durable. Order financials are reconstructable from the PaymentTransaction and Order ledgers.

shouldLogActivity(module, operation) is the function that applies all of this, and config/activity.test.ts covers it.

The presentation half

config/activity-registry.ts is the largest file in the directory.

ACTIVITY_MODULES lists every module the feed can filter by. A module logged in config/activity.ts but missing here renders rows that can never be filtered, which lib/__tests__/activity-taxonomy.test.ts pins.

ACTIVITY_REGISTRY maps each activity type to an icon, a color, a label, its module and a sort priority.

The file exports the readers over it:

  • getActivityConfig
  • getModuleActivityTypes
  • getModuleLabel
  • getModuleIcon
  • formatActivityType
  • getModulesWithCounts

AI prompts

config/ai.ts holds five system prompts with their generation settings:

  • adminChat (maxSteps 5, maxTokens 2000, temperature 0.7)
  • productDescription (1000 / 0.3)
  • emailGeneration (800 / 0.7)
  • socialPost (500 / 0.9)
  • analyticsChat (600 / 0.2)

It carries no model names and no API keys — those are resolved elsewhere.

It also carries no tool catalog, and says why. Every registered tool describes itself through the AI SDK, with server/ai/agent-tools.ts as the single authority.

The hand-maintained list that used to live in the admin-chat prompt advertised tools that did not exist and omitted real ones.

Themes and palettes

config/theme.ts is the theme registry and its access API.

THEMES holds four entries — starter (target admin), starter-web, amelia and brutal (target web) — with DEFAULT_ADMIN_THEME_ID set to starter and DEFAULT_STOREFRONT_THEME_ID to amelia.

assertThemeTarget throws when an admin theme id is used for the storefront or the reverse, and getTheme(channelThemeId) falls back through the stored active theme to the default.

The rest of the surface:

  • getThemeTarget
  • isStorefrontThemeId
  • isAdminThemeId
  • getDefaultTheme
  • getThemeNames
  • getThemeByName

The theme definitions sit in config/themes/, one file each plus _utils.ts. That file exports:

  • createWebTheme, which fills in target: "web"
  • joinThemeCssBlocks
  • createStorefrontBaseCss(themeId) — the shared [data-theme="…"] block that gives dialogs, popovers, badges, inputs, empty states and the cookie banner their per-theme radii, borders and backdrop blur

A theme file overrides --web-* tokens on top of DEFAULT_WEB_THEME_TOKENS. Amelia is borderless, setting "--web-card-border-width": "0px".

scripts/check-theme-boundaries.ts keeps theme ids and theme-specific selectors inside these two locations. Global CSS, admin code and shared components must not name a theme.

Color palettes

config/color-palettes.ts is separate from themes. A palette is a set of color tokens a single storefront block renders with, overriding the same --color-* variables the theme already sets.

  • PaletteColors names the 17 overridable tokens
  • PALETTE_COLOR_FIELDS gives them ordered labels for the admin editor
  • paletteCssVars() turns a palette into an inline style object
  • resolvePaletteById looks one up

STARTER_COLOR_PALETTES ships one palette, starter-ember-noir ("Ember Noir").

The module is pure — no database access and no theme resolution — so client components can import it without pulling server code into the bundle.

The first palette a user sees is built from the active theme by ~/server/admin/settings/color-palettes and is read-only. Editable copies are stored in the custom_color_palettes setting.

Fonts

config/fonts.ts loads one typeface, once.

fontSans is Figtree Variable, served from public/fonts/Figtree-Variable.woff2 with display: "swap" and exposed as --font-sans.

The file's header explains the deletion: a second localFont() call for --font-display bought a duplicate @font-face and a duplicate preload for no visual difference, because the variable font's weight axis already covers 700.

WOFF2 over TTF is 27.8 KB compressed against 62.7 KB.

The file also exports loadGoogleFont(font, weight), which fetches a Google Fonts CSS2 stylesheet and returns the font buffer. It exists for Satori-rendered OG images, not for the app shell.

Payments and pricing

config/payments.ts exports one list: SHIPPING_ALLOWED_COUNTRIES, 24 ISO 3166-1 alpha-2 codes typed as Stripe's AllowedCountry union, used for shipping address collection during checkout.

Poland and Brazil carry comments naming the local methods (Przelewy24/BLIK, Pix) they enable.

config/pricing.ts holds the margin and discount defaults:

  • ASSUMED_COST_RATIO (0.5): the cost ratio applied when a product has no costPrice.
  • DEFAULT_PAYMENT_FEE_PERCENT (2.9) and DEFAULT_PAYMENT_FEE_FIXED (0.3): the assumed processing fee.
  • DEFAULT_PLATFORM_FEE_PERCENT (0) and DEFAULT_MIN_MARGIN_PERCENT (5).
  • BEHAVIORAL_DISCOUNTS: four keyed configs — first_purchase (10% capped at 20, priority 100, the only one enabled), cart_abandoner, returning_customer and high_intent.

Only one behavioral discount applies per cart. The highest priority wins.

External services

config/external-services.ts is a lookup table of 55 third-party services, each with an id, display name, website, favicon URL and one of eleven categories:

payment
social
analytics
ads
email
storage
auth
shipping
ecommerce
ai
other

Favicons all resolve through Google's favicon service so the admin renders one consistent size.

config/external-services.ts
export function getService(id: string): ExternalService | undefined {
  return EXTERNAL_SERVICES[id]
}

export function getServicesByCategory(category: ServiceCategory): ExternalService[] {
  return Object.values(EXTERNAL_SERVICES).filter(s => s.category === category)
}

The same file owns review-import sources. REVIEW_IMPORT_PLATFORMS is:

amazon
aliexpress
google
trustpilot

getReviewImportSources() returns those platforms alongside the file formats, each with an available flag and a configureUrl pointing at the integration that has to be set up first.

Where a new value goes

Storefront wording goes in config/copy.ts, store identity in config/site.ts, and outbound URLs in config/links.ts.

A new theme is a file in config/themes/ plus its entry in THEMES. A block palette is an entry in config/color-palettes.ts.

A new module that writes to the database goes in one of the four lists in config/activity.ts — the activity-coverage rule fails until it does — and in ACTIVITY_MODULES if its rows should be filterable.

A new third-party service goes in config/external-services.ts.

What does not go here: anything environment-specific, which belongs in env.ts, and anything an operator changes from the admin, which belongs in settings — the editable color palettes live in the custom_color_palettes setting rather than in config/color-palettes.ts.

The dividing question is:

Does changing this value require a deploy? If it does not, it is not config.

On this page