feat(03-01): add Module SDK package and Prisma module registry schema

- Create @tessera/module-sdk with TesseraModule, ModuleRoute, ModuleManifest, ModuleCategory types
- Add Module and TenantModuleActivation Prisma models with tenant-scoped unique constraint
- Apply migration add-module-registry to PostgreSQL
- Framework-agnostic ComponentType for lazy-loaded module UIs
This commit is contained in:
2026-06-19 12:33:55 +02:00
parent 6e2f6e7c3e
commit 8c24c1e267
7 changed files with 183 additions and 0 deletions
@@ -0,0 +1,38 @@
-- CreateTable
CREATE TABLE "Module" (
"id" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"name" TEXT NOT NULL,
"version" TEXT NOT NULL,
"category" TEXT NOT NULL,
"description" JSONB NOT NULL,
"icon" TEXT,
"isSystem" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Module_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "TenantModuleActivation" (
"id" TEXT NOT NULL,
"tenantId" TEXT NOT NULL,
"moduleId" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"activatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "TenantModuleActivation_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Module_slug_key" ON "Module"("slug");
-- CreateIndex
CREATE INDEX "TenantModuleActivation_tenantId_idx" ON "TenantModuleActivation"("tenantId");
-- CreateIndex
CREATE UNIQUE INDEX "TenantModuleActivation_tenantId_moduleId_key" ON "TenantModuleActivation"("tenantId", "moduleId");
-- AddForeignKey
ALTER TABLE "TenantModuleActivation" ADD CONSTRAINT "TenantModuleActivation_moduleId_fkey" FOREIGN KEY ("moduleId") REFERENCES "Module"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+26
View File
@@ -84,3 +84,29 @@ model LdapFieldMapping {
@@unique([ldapConfigId, ldapField])
}
model Module {
id String @id @default(uuid())
slug String @unique
name String
version String
category String
description Json
icon String?
isSystem Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
activations TenantModuleActivation[]
}
model TenantModuleActivation {
id String @id @default(uuid())
tenantId String
moduleId String
isActive Boolean @default(true)
activatedAt DateTime @default(now())
module Module @relation(fields: [moduleId], references: [id], onDelete: Cascade)
@@unique([tenantId, moduleId])
@@index([tenantId])
}
+13
View File
@@ -0,0 +1,13 @@
{
"name": "@tessera/module-sdk",
"version": "0.1.0",
"private": true,
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"type-check": "tsc --noEmit"
},
"devDependencies": {
"typescript": "^5.5.0"
}
}
+14
View File
@@ -0,0 +1,14 @@
/**
* @tessera/module-sdk
*
* Public API for the Tessera module system.
* All module implementations should import types from this package.
*/
export type {
ComponentType,
ModuleCategory,
HttpMethod,
ModuleRoute,
TesseraModule,
ModuleManifest,
} from './types';
+78
View File
@@ -0,0 +1,78 @@
/**
* @tessera/module-sdk
*
* Defines the contract for Tessera modules.
* All modules must implement the TesseraModule interface.
*/
/**
* Generic component type for lazy-loaded module UIs.
* Framework-agnostic — maps to React.ComponentType when consumed by the frontend.
*/
// biome-ignore lint: generic component type for framework independence
export type ComponentType = (props: any) => any;
/**
* Category classification for modules.
* Modules are grouped by category in the marketplace and sidebar.
*/
export type ModuleCategory =
| 'infrastructure'
| 'monitoring'
| 'development'
| 'security'
| 'communication'
| 'workflow'
| 'analytics'
| 'integration'
| string;
/**
* HTTP method for module-defined API routes.
*/
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
/**
* Defines a single API route that a module exposes.
*/
export interface ModuleRoute {
/** HTTP method */
method: HttpMethod;
/** Route path relative to the module's base path (e.g., '/check') */
path: string;
/** Handler identifier (maps to a controller method) */
handler: string;
}
/**
* Full module definition including frontend components.
* Used by the frontend to render module UIs.
*/
export interface TesseraModule {
/** Unique module identifier (UUID) */
id: string;
/** Human-readable module name */
name: string;
/** Semantic version string */
version: string;
/** URL-safe unique slug (e.g., 'domaincheck') */
slug: string;
/** Module category for grouping */
category: ModuleCategory;
/** i18n descriptions keyed by locale (e.g., { de: '...', en: '...' }) */
description: Record<string, string>;
/** Optional icon identifier (e.g., 'Globe', 'Shield') */
icon?: string;
/** Optional API routes the module exposes */
routes?: ModuleRoute[];
/** Lazy-loaded main component for the module page */
component: () => Promise<{ default: ComponentType }>;
/** Optional lazy-loaded card component for the dashboard */
cardComponent?: () => Promise<{ default: ComponentType }>;
}
/**
* Backend-only module metadata (no frontend component references).
* Used for module registration and API-side operations.
*/
export type ModuleManifest = Omit<TesseraModule, 'component' | 'cardComponent'>;
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"]
}
+6
View File
@@ -161,6 +161,12 @@ importers:
specifier: ^5.5.0
version: 5.9.3
packages/module-sdk:
devDependencies:
typescript:
specifier: ^5.5.0
version: 5.9.3
packages/shared:
devDependencies:
typescript: