Store Module — Types & Registry API

The Business Platform Store provides a curated catalogue of pre-built modules that can be installed into a project with a single click. This document describes the core types and the registry API that powers the Store.


Core Types

StoreModule

Represents a single installable module in the Store.

Field Type Description
name FeatureName Unique identifier for the module (matches the underlying feature name).
label InlineTranslation Localised display name shown in the Store UI.
description InlineTranslation Short localised description of what the namespace provides.
icon string URL to the module's icon image.
version string Current published version of the module (semver string).
tags string[] Tag names used for filtering (see StoreTag).
requiredExtensions FeatureExtensionName[] Extensions that are always installed with this module.
extensions FeatureExtensionName[] All available extensions (required + optional).
changelog? StoreChangelog[] Optional ordered list of changelog entries, newest first.

Example

const contactModule: StoreModule = {
  name: FeatureName.CONTACT,
  label: { en: "Contacts", de: "Kontakte" },
  description: { en: "Manage your contacts and relationships." },
  icon: "https://img.icons8.com/fluency/96/contacts.png",
  version: "1.2.0",
  tags: ["crm", "function"],
  requiredExtensions: [FeatureExtensionName.CONTACTS_TYPE],
  extensions: [
    FeatureExtensionName.CONTACTS_TYPE,
    FeatureExtensionName.CONTACTS_BOM
  ]
};

StoreCollection

A curated bundle of multiple StoreModules, presented as a ready-made starter pack. When a user selects a collection, all listed modules are pre-selected along with a set of recommended extensions.

Field Type Description
name string Unique identifier for the collection.
label InlineTranslation Localised display name.
description InlineTranslation Localised description.
icon string URL to the collection's icon image.
color string Accent colour (hex) used in the UI.
modules FeatureName[] Ordered list of namespace names included in this collection.
preSelectedExtensions? FeatureExtensionName[] Extensions that are pre-checked when the collection is applied.

Example

const crmCollection: StoreCollection = {
  name: "crm-pack",
  label: { en: "CRM Pack", de: "CRM-Paket" },
  description: { en: "Customer relationship management with contacts and email." },
  icon: "https://img.icons8.com/fluency/96/contacts.png",
  color: "#4F46E5",
  modules: [FeatureName.CONTACT, FeatureName.EMAIL],
  preSelectedExtensions: [
    FeatureExtensionName.CONTACTS_TYPE,
    FeatureExtensionName.CONTACTS_BOM,
    FeatureExtensionName.EMAILS_TYPE,
    FeatureExtensionName.EMAILS_BOM
  ]
};

StoreTag

A classification label used to filter modules in the Store sidebar.

Field Type Description
name string Unique identifier for the tag.
label InlineTranslation Localised display name.
type `"industry" "function"`
icon? string Optional Font Awesome class string (e.g. "far fa-building").

Tags of type "industry" are shown under the Industry heading; tags of type "function" are shown under Function.


StoreChangelog

A single entry in a module's changelog.

Field Type Description
version string The version this entry describes.
description InlineTranslation Short localised summary of changes.

InstalledModule

Recorded on the Project document when a module is successfully installed. Updated in-place when the same module is re-installed or upgraded.

Field Type Description
moduleName FeatureName Name of the installed module.
installedAt string ISO 8601 timestamp of first installation.
installedVersion string Version that was installed.
updatedAt? string ISO 8601 timestamp of most recent update.
updatedVersion? string Version applied in the most recent update.
installedExtensions InstalledExtension[] List of extensions that were installed.

InstalledExtension

Field Type Description
extensionName FeatureExtensionName Name of the installed extension.
installedAt string ISO 8601 timestamp of when this extension was installed.

Store Registry API

The registry is populated at application startup by importing the side-effect module store.init.ts. Once initialised, the following functions are available.

Initialisation

// Import once — typically in your app's entry point or server bootstrap.
import "@smallstack/shared/templates/store.init";

This registers all built-in modules, collections, and tags. After this import the query functions below work correctly.


Module functions

registerModule(module: StoreModule): void

Adds a module to the registry. Throws if a module with the same name is already registered.

getModule(name: FeatureName): StoreModule | undefined

Returns the registered module for the given name, or undefined if not found.

listModules(): StoreModule[]

Returns all registered modules in registration order.

searchModules(query: string): StoreModule[]

Case-insensitive full-text search across module name, label values, and description values. Returns matching modules.

const results = searchModules("contact");
// → [contactModule, ...]

isModuleInstalled(project: Project, moduleName: FeatureName): boolean

Returns true if the given module has a recorded InstalledModule entry on the project.

getInstalledVersion(project: Project, moduleName: FeatureName): string | undefined

Returns the installedVersion (or updatedVersion if an update was applied) for the given module, or undefined if not installed.

hasModuleUpdate(project: Project, moduleName: FeatureName): boolean

Returns true when the module's current registry version is greater than the version recorded on the project.

getModulesWithUpdates(project: Project): StoreModule[]

Returns all registered modules for which hasModuleUpdate is true. Used to populate the Updates Available banner.

const updatable = getModulesWithUpdates(project);
if (updatable.length > 0) {
  // show StoreUpdatesBanner
}

Collection functions

registerCollection(collection: StoreCollection): void

Adds a collection to the registry.

getCollection(name: string): StoreCollection | undefined

Returns the collection for the given name, or undefined if not found.

listCollections(): StoreCollection[]

Returns all registered collections in registration order.


Tag functions

registerTag(tag: StoreTag): void

Adds a tag to the registry.

getTag(name: string): StoreTag | undefined

Returns the tag for the given name, or undefined if not found.

getTagsByType(type: "industry" | "function"): StoreTag[]

Returns all tags of the given type. Used to populate the Store sidebar sections.

const industryTags = getTagsByType("industry");
const functionTags = getTagsByType("function");

Extension dependencies

FeatureExtension supports an optional requires field that declares other extensions that must be present before this one can be installed.

interface FeatureExtension {
  name: string;
  label: InlineTranslation;
  /** Other extensions that must be installed before this one. */
  requires?: FeatureExtensionName[];
  check: (options: FeatureExtensionCheckOptions) => FeatureExtensionCheckResult;
  execute: (options: FeatureExtensionExecuteOptions) => void;
}

Use resolveExtensionDependencies to expand a user's selection into the full set of required extensions:


import { getFeatureExtension, resolveExtensionDependencies } from "@smallstack/shared";

const resolved = resolveExtensionDependencies(
  [...selectedExtensions],
  getFeatureExtension
);
// resolved.extensions — full set including auto-added dependencies
// resolved.autoAdded  — extensions added automatically due to `requires`

Module installation flow

  1. The user opens Project Settings → Store.

  2. StoreLayout loads all modules via listModules() and filters them by search query, tag, or collection.

  3. The user expands a StoreModuleRow, optionally toggles optional extensions, and clicks Install or Update.

  4. InstallModuleModal builds an ApplyTemplateBody and enriches it with module-tracking fields:

    interface ApplyTemplateBodyWithModule extends ApplyTemplateBody {
      moduleName?: string;       // FeatureName of the module being installed
      moduleVersion?: string;    // Version string from StoreModule.version
      installedExtensions?: string[]; // FeatureExtensionName[] of selected extensions
    }
    
  5. A PUT request is sent to /api/projects/[projectId]/extensions.

  6. The server applies the template entities inside a MongoDB transaction, then — if moduleName and moduleVersion are present — writes or updates an InstalledModule record on the project document.

    • New install: pushes a new entry to project.installedModules.
    • Re-install / update: uses a positional $set to update updatedAt, updatedVersion, and installedExtensions in place.

Built-in collections

The following collections ship with the platform out of the box.

Name Key Included modules
Real Estate Pack real-estate-pack Contacts, Todos, Real Estate
CRM Pack crm-pack Contacts, Email
Blog Pack blog-pack Blog
Handyman Pack handyman-pack Handyman
SaaS Website Pack website-saas-pack Website SaaS
Landing Page Pack website-landing-page-pack Website Landing Page

Migration from Templates

The previous Templates section (route /settings/templates) has been replaced by the Store. Any existing bookmark or direct link to the templates route is automatically redirected to /settings/store via a client-side goto with replaceState: true. No data migration is required — installed template state is transparently superseded by the new installedModules tracking.