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
The user opens Project Settings → Store.
StoreLayoutloads all modules vialistModules()and filters them by search query, tag, or collection.The user expands a
StoreModuleRow, optionally toggles optional extensions, and clicks Install or Update.InstallModuleModalbuilds anApplyTemplateBodyand 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 }A
PUTrequest is sent to/api/projects/[projectId]/extensions.The server applies the template entities inside a MongoDB transaction, then — if
moduleNameandmoduleVersionare present — writes or updates anInstalledModulerecord on the project document.- New install: pushes a new entry to
project.installedModules. - Re-install / update: uses a positional
$setto updateupdatedAt,updatedVersion, andinstalledExtensionsin place.
- New install: pushes a new entry to
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.