Litestore · Developer docs
Codebase

Config

The platform/config/ folder holds Litestore's fixed values, such as store copy, policies, timings and AI limits, as typed constants you change in code.

The platform/config/ folder contains the values that shape how the store behaves but that a store owner doesn't change in the admin: the store's identity and copy, order and refund policies, email timings, referral rules, social-proof thresholds, AI limits and pricing defaults.

Each file is plain TypeScript with typed constants and a one-line comment for each. Litestore has no JSON layer and no runtime config service. To change a value, edit the constant and redeploy. Environment variables don't go here, because env.ts declares them, and the few business settings a store owner does change (store details, currency, payments, notification email, AI autonomy) are admin settings.

Config folder

site.ts
copy.ts
storefront.ts
store-policies.ts
email-automations.ts
affiliates.ts
ai.ts
fonts.ts
payments.ts
pricing.ts
external-services.ts

Site and copy

platform/config/site.ts exports the store's identity and combines it into one config object.

platform/config/site.ts
export const config = {
  site: siteConfig,
  metadata: metadataConfig,
  copy: copyConfig,
}
ExportWhat it does
siteConfigIt contains the store name, slug and description, and reads the email and URL from env.NEXT_PUBLIC_SITE_EMAIL and env.NEXT_PUBLIC_SITE_URL. Its affiliateUrl is the domain of the store's short links; addSearchParams() passes links on that domain through untouched, so affiliate links don't get their ref parameters rewritten. Its feeds entry holds the two RSS URLs that pages link to for feed discovery.
metadataConfigThis Next.js Metadata object sets the OpenGraph image at /opengraph.png (1200×630), the summary_large_image Twitter card and the RSS alternates.

platform/config/copy.ts holds the titles and descriptions of the storefront index pages: collections, categories and blog.

Storefront behaviour

platform/config/storefront.ts holds storefront switches and copy that aren't per-store admin settings.

ConstantWhat it controls
SEO_TITLE_TEMPLATEThe page title pattern, "%s · {site}".
REVIEWS_ENABLED, WISHLIST_ENABLEDWhether reviews and the wishlist are on.
HIDE_OUT_OF_STOCK_VARIANTS, SHOW_VARIANT_OPTION_NAMESHow the variant picker behaves.
ANNOUNCEMENT_DISMISS_MODE, ANNOUNCEMENT_COOLDOWN_DAYSHow long a dismissed announcement bar stays hidden.
NEWSLETTER_COPY, FOOTER_PAYMENT_METHODS, SOCIAL_PLATFORMSFooter copy and icons.
NEW_PRODUCT_DAYS (14), SALE_ENDS_SOON_HOURS (48), BESTSELLER_UNITS (50), SELLING_FAST_UNITS_24H (5)The thresholds for the social proof on products, computed by productProof in modules/products/social-proof.ts.

Store policies

platform/config/store-policies.ts holds the timings and limits that shape orders, refunds, shipping and support.

ConstantDefault
CUSTOMER_CANCEL_WINDOW_HOURS2
REFUND_REQUEST_WINDOW_DAYS, REFUND_REQUEST_WINDOW_FROM30, from "delivery"
REFUND_MAX_REQUESTS_PER_WINDOW, REFUND_VELOCITY_WINDOW_DAYS0 (no cap), 30
DEFAULT_HANDLING_DAYS, SHIPPING_WORKING_DAYS, SHIPPING_HOLIDAYS1, Monday to Friday, none
PICKUP_UNCOLLECTED_AFTER_DAYS, SHIPMENT_MISSING_TRACKING_DAYS7, 1
REORDER_LEAD_TIME_DAYS, ORDER_INTENT_RETENTION_DAYS7, 90
MESSAGE_MAX_ATTACHMENTS, CURRENCY_ROUNDING_MODE5, "standard"

Email automations and referrals

platform/config/email-automations.ts sets the cadence of the lifecycle emails: the abandoned-cart delay and follow-up, the optional recovery, win-back and welcome coupon codes, the review request and reminder, and the signup nudge. A delay of zero turns its step off. Workflows explains how they run.

platform/config/affiliates.ts sets the referral and collab rules: whether referrals are on, the reward points and how long a reward is held, the attribution window, collab coupon limits, the commission clearing period, the minimum payout and the default commission rate, and when a collab application is approved automatically.

AI limits

platform/config/ai.ts holds the cost and abuse ceilings every AI call runs under.

ConstantDefaultWhat it does
AI_RATE_LIMIT20 requests per minuteThe per-user ceiling for the admin AI routes. A chat turn is one request; tool steps inside it don't count again.
AI_MONTHLY_BUDGET_USD50The store-wide spend ceiling per calendar month (UTC). Every model call is refused once month-to-date spend reaches it. 0 means unlimited.
AI_MODEL_PRICES, AI_UNKNOWN_MODEL_PRICE—The list prices used to count spend. A model missing from the table is priced as the most expensive one, so the budget can only trip early.

The file contains no model names, API keys or prompts. The admin assistant's system prompt and its settings (maxSteps 8, maxTokens 4000, temperature 0.3) are in modules/assistant/prompt.ts. It contains no list of tools, and a comment explains why: each registered tool describes itself through the AI SDK, and modules/assistant/agent-tools.ts is the only place that defines them. A hand-written tool list used to drift from the real tools.

Activity logging

Which writes Litestore records in the Activity table is decided in modules/operations/activity/config.ts. Every BaseCRUD model name belongs to one of four module lists.

ListWhat Litestore logsExamples
CRITICAL_MODULESEvery operationorder, refund, user, customer, setting, product, review, campaign, serviceApiKey, page, feed, channel
CREATE_DELETE_MODULESCreates and deletesmedia, tag, coupon, link
DELETE_ONLY_MODULESDeletes onlyproductVariant, externalLink, stockLocation, productRelationship
SKIP_LOGGING_MODULESNothingactivity, address, wishlist, cart, notification

A module that is missing from all four is skipped without any warning and has no audit trail, so add a new module's model to a list when you create it.

Litestore always logs the operations in CRITICAL_OPERATIONS, whichever module they happen in:

delete
refund
cancel
ban
suspend

ACTIVITY_CLEANUP keeps activity for 90 days and deletes old rows in batches of 1,000. Litestore never purges activity types that start with one of the DURABLE_ACTIVITY_TYPE_PREFIXES:

commission
payout
affiliatePayout
price_changed
setting
campaign
refund
return
payment
loyalty

Litestore deliberately doesn't keep routine order and product changes forever. An order's financial history can always be rebuilt from the PaymentTransaction and Order tables.

shouldLogActivity(module, operation) applies all of these rules.

Activity display

modules/operations/activity/registry.ts holds ACTIVITY_MODULES, the modules the activity feed filters by, and ACTIVITY_REGISTRY, which gives each activity type an icon, a color, a label, its module and a sort priority. You read it with getActivityConfig.

Design

The storefront has one design and no theme registry. Its colours, corners and spacing are CSS variables in app/styles.css, so you change the look there rather than in platform/config/. See Storefront pages.

Fonts

platform/config/fonts.ts loads a single typeface, once. fontSans is Figtree Variable, served from public/fonts/Figtree-Variable.woff2 with display: "swap", and the app reads it as --font-sans. The WOFF2 file is 27.8 KB compressed, compared with 62.7 KB for the TTF.

A comment at the top of the file explains why a second font was removed. A second localFont() call for --font-display added a duplicate @font-face and a duplicate preload, but looked no different, because the variable font's weight axis already reaches 700.

The file also exports loadGoogleFont(font, weight). It fetches a Google Fonts CSS2 stylesheet and returns the font file. Litestore uses it to render OG images with Satori, not for the app itself.

Payments and pricing

platform/config/payments.ts exports one list, SHIPPING_ALLOWED_COUNTRIES. It contains 24 ISO 3166-1 alpha-2 country codes, typed as Stripe's AllowedCountry union. Checkout uses it to determine which countries a shopper can enter as a shipping address.

platform/config/pricing.ts sets the default values for fees, margins and discounts.

ConstantWhat it doesDefault
DEFAULT_PAYMENT_FEE_PERCENTThis is the assumed percentage payment processing fee.2.9
DEFAULT_PAYMENT_FEE_FIXEDThis is the assumed fixed payment processing fee.0.3
DEFAULT_PLATFORM_FEE_PERCENTThis is the default platform fee.0
DEFAULT_MIN_MARGIN_PERCENTThis is the default minimum margin.5
BEHAVIORAL_DISCOUNTSIt configures four discounts: first_purchase, cart_abandoner, returning_customer and high_intent. Only first_purchase is enabled, and it gives 10% off, capped at 20, with priority 100.—

A cart only ever gets one behavioral discount. If several apply, Litestore picks the one with the highest priority.

External services

platform/config/external-services.ts is a lookup table of about 50 third-party services. Each entry has an id, a display name, a website, a favicon URL and one of eleven categories:

payment
social
analytics
ads
email
storage
auth
shipping
ecommerce
ai
other

Every favicon loads through Google's favicon service, so the admin shows them all at the same size.

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

getServiceFavicon(idOrUrl, size) returns the favicon for a known service or for any URL.

Adding config values

What you're addingWhere it goes
Storefront index page wordingplatform/config/copy.ts
Store identityplatform/config/site.ts
A storefront switch or thresholdplatform/config/storefront.ts
An order, refund or shipping timingplatform/config/store-policies.ts
Storefront colours, corners or spacingThe @theme block in app/styles.css
A module that writes to the databaseOne of the four lists in modules/operations/activity/config.ts, and ACTIVITY_MODULES if its rows should be filterable
A third-party serviceplatform/config/external-services.ts

Two kinds of values don't belong here. Anything that differs between environments goes in env.ts. Anything a store owner would change in Shopify's admin, such as store details, currency or payments, is an admin setting.

A useful test is who changes the value. If a store owner needs to change it without a deploy, it belongs in settings. If it's tuning that a developer decides once, it's a constant here.

On this page