@dbs-portal/core-module-registry
Version:
Core module registry system for automatic module discovery and registration
1,213 lines (1,212 loc) • 33.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { z } from "zod";
import { E, F, w, e, u, d, f, k, s, a, b, g, j, c, t, v, i, q, m, r, n, o, l, h, p, x } from "./platform-utils-Cix3Fefx.js";
var ModuleCategory = /* @__PURE__ */ ((ModuleCategory2) => {
ModuleCategory2["IDENTITY"] = "identity";
ModuleCategory2["CONTENT"] = "content";
ModuleCategory2["COMMUNICATION"] = "communication";
ModuleCategory2["SYSTEM"] = "system";
ModuleCategory2["BUSINESS"] = "business";
ModuleCategory2["ANALYTICS"] = "analytics";
ModuleCategory2["INTEGRATION"] = "integration";
return ModuleCategory2;
})(ModuleCategory || {});
var ModuleStatus = /* @__PURE__ */ ((ModuleStatus2) => {
ModuleStatus2["ACTIVE"] = "active";
ModuleStatus2["INACTIVE"] = "inactive";
ModuleStatus2["DEVELOPMENT"] = "development";
ModuleStatus2["DEPRECATED"] = "deprecated";
ModuleStatus2["MAINTENANCE"] = "maintenance";
return ModuleStatus2;
})(ModuleStatus || {});
var RouteGuardType = /* @__PURE__ */ ((RouteGuardType2) => {
RouteGuardType2["PERMISSION"] = "permission";
RouteGuardType2["ROLE"] = "role";
RouteGuardType2["CUSTOM"] = "custom";
return RouteGuardType2;
})(RouteGuardType || {});
var NavigationBadgeType = /* @__PURE__ */ ((NavigationBadgeType2) => {
NavigationBadgeType2["COUNT"] = "count";
NavigationBadgeType2["STATUS"] = "status";
NavigationBadgeType2["NEW"] = "new";
return NavigationBadgeType2;
})(NavigationBadgeType || {});
const RouteGuardSchema = z.object({
name: z.string(),
type: z.nativeEnum(RouteGuardType),
condition: z.string(),
redirect: z.string().optional(),
message: z.string().optional()
});
const ModuleRouteSchema = z.lazy(() => z.object({
path: z.string(),
component: z.string(),
exact: z.boolean().optional(),
permissions: z.array(z.string()).optional(),
roles: z.array(z.string()).optional(),
title: z.string().optional(),
description: z.string().optional(),
meta: z.record(z.any()).optional(),
children: z.array(ModuleRouteSchema).optional(),
redirect: z.string().optional(),
guards: z.array(RouteGuardSchema).optional()
}));
const NavigationBadgeSchema = z.object({
text: z.string(),
color: z.string(),
type: z.nativeEnum(NavigationBadgeType)
});
const NavigationItemSchema = z.lazy(() => z.object({
key: z.string(),
label: z.string(),
icon: z.string(),
path: z.string(),
category: z.string(),
order: z.number(),
permissions: z.array(z.string()).optional(),
roles: z.array(z.string()).optional(),
children: z.array(NavigationItemSchema).optional(),
badge: NavigationBadgeSchema.optional(),
tooltip: z.string().optional(),
external: z.boolean().optional(),
target: z.string().optional(),
hidden: z.boolean().optional()
}));
const ModuleMetadataSchema = z.object({
// Core Identification
id: z.string().regex(/^[a-z0-9-]+$/, "Module ID must be lowercase alphanumeric with hyphens"),
name: z.string().min(1, "Module name is required"),
version: z.string().regex(/^\d+\.\d+\.\d+/, "Version must follow semantic versioning"),
description: z.string().optional(),
// Categorization
category: z.nativeEnum(ModuleCategory),
subcategory: z.string().optional(),
tags: z.array(z.string()).default([]),
// Visual Configuration
icon: z.string().min(1, "Icon is required"),
color: z.string().optional(),
banner: z.string().optional(),
// Security and Access
permissions: z.array(z.string()).default([]),
roles: z.array(z.string()).optional(),
public: z.boolean().optional(),
// Routing Configuration
routes: z.array(ModuleRouteSchema).default([]),
basePath: z.string().optional(),
// Navigation Configuration
navigation: z.array(NavigationItemSchema).default([]),
hideFromNavigation: z.boolean().optional(),
// Dependencies
dependencies: z.array(z.string()).default([]),
peerDependencies: z.array(z.string()).optional(),
optionalDependencies: z.array(z.string()).optional(),
// Module Status
status: z.nativeEnum(ModuleStatus).default(
"active"
/* ACTIVE */
),
priority: z.number().default(100),
experimental: z.boolean().optional(),
// Metadata
author: z.string().optional(),
license: z.string().optional(),
repository: z.string().optional(),
homepage: z.string().optional(),
documentation: z.string().optional(),
// Feature Configuration
features: z.record(z.boolean()).optional(),
config: z.record(z.any()).optional(),
// Lifecycle Hooks
onLoad: z.string().optional(),
onUnload: z.string().optional(),
onActivate: z.string().optional(),
onDeactivate: z.string().optional(),
// Performance
lazy: z.boolean().optional(),
preload: z.boolean().optional(),
chunkName: z.string().optional()
});
const validateModuleMetadata = (metadata) => {
try {
ModuleMetadataSchema.parse(metadata);
return { valid: true, errors: [] };
} catch (error) {
if (error instanceof z.ZodError) {
return {
valid: false,
errors: error.errors.map((err) => ({
field: err.path.join("."),
message: err.message,
value: "received" in err ? err.received : void 0
}))
};
}
return {
valid: false,
errors: [{ field: "unknown", message: "Unknown validation error" }]
};
}
};
class ModuleCache {
constructor(defaultTTL = 5 * 60 * 1e3, options = {}) {
__publicField(this, "cache", /* @__PURE__ */ new Map());
__publicField(this, "options");
__publicField(this, "stats");
__publicField(this, "cleanupTimer", null);
this.options = {
defaultTTL,
maxSize: 1e3,
cleanupInterval: 60 * 1e3,
// 1 minute
enableStats: true,
...options
};
this.stats = {
hits: 0,
misses: 0,
sets: 0,
deletes: 0,
evictions: 0,
size: 0,
hitRate: 0
};
this.startCleanupTimer();
}
/**
* Get value from cache
*/
get(key) {
const entry = this.cache.get(key);
if (!entry) {
this.updateStats("miss");
return null;
}
if (Date.now() > entry.expiry) {
this.cache.delete(key);
this.updateStats("miss");
return null;
}
this.updateStats("hit");
return entry.value;
}
/**
* Set value in cache
*/
set(key, value, ttl) {
const actualTTL = ttl ?? this.options.defaultTTL;
const expiry = Date.now() + actualTTL;
if (this.cache.size >= this.options.maxSize && !this.cache.has(key)) {
this.evictOldest();
}
const entry = {
value,
expiry,
timestamp: Date.now()
};
this.cache.set(key, entry);
this.updateStats("set");
}
/**
* Check if key exists in cache (without updating access time)
*/
has(key) {
const entry = this.cache.get(key);
if (!entry) {
return false;
}
if (Date.now() > entry.expiry) {
this.cache.delete(key);
return false;
}
return true;
}
/**
* Delete value from cache
*/
delete(key) {
const deleted = this.cache.delete(key);
if (deleted) {
this.updateStats("delete");
}
return deleted;
}
/**
* Clear all cache entries
*/
clear() {
this.cache.clear();
this.resetStats();
}
/**
* Get cache size
*/
size() {
return this.cache.size;
}
/**
* Get all cache keys
*/
keys() {
return Array.from(this.cache.keys());
}
/**
* Get cache statistics
*/
getStats() {
this.updateHitRate();
return { ...this.stats, size: this.cache.size };
}
/**
* Reset cache statistics
*/
resetStats() {
this.stats = {
hits: 0,
misses: 0,
sets: 0,
deletes: 0,
evictions: 0,
size: this.cache.size,
hitRate: 0
};
}
/**
* Get cache entries that match a pattern
*/
getByPattern(pattern) {
const results = [];
const now = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (now > entry.expiry) {
this.cache.delete(key);
continue;
}
if (pattern.test(key)) {
results.push({ key, value: entry.value });
}
}
return results;
}
/**
* Set multiple values at once
*/
setMany(entries) {
entries.forEach(({ key, value, ttl }) => {
this.set(key, value, ttl);
});
}
/**
* Get multiple values at once
*/
getMany(keys) {
return keys.map((key) => ({
key,
value: this.get(key)
}));
}
/**
* Delete multiple keys at once
*/
deleteMany(keys) {
let deletedCount = 0;
keys.forEach((key) => {
if (this.delete(key)) {
deletedCount++;
}
});
return deletedCount;
}
/**
* Get cache memory usage estimate (in bytes)
*/
getMemoryUsage() {
let totalSize = 0;
for (const [key, entry] of this.cache.entries()) {
totalSize += key.length * 2;
totalSize += JSON.stringify(entry.value).length * 2;
totalSize += 64;
}
return totalSize;
}
/**
* Cleanup expired entries
*/
cleanup() {
const now = Date.now();
let cleanedCount = 0;
for (const [key, entry] of this.cache.entries()) {
if (now > entry.expiry) {
this.cache.delete(key);
cleanedCount++;
}
}
return cleanedCount;
}
/**
* Destroy cache and cleanup resources
*/
destroy() {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
this.clear();
}
/**
* Start automatic cleanup timer
*/
startCleanupTimer() {
if (this.options.cleanupInterval > 0) {
this.cleanupTimer = setInterval(() => {
this.cleanup();
}, this.options.cleanupInterval);
}
}
/**
* Evict oldest entry when cache is full
*/
evictOldest() {
let oldestKey = null;
let oldestTimestamp = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (entry.timestamp < oldestTimestamp) {
oldestTimestamp = entry.timestamp;
oldestKey = key;
}
}
if (oldestKey) {
this.cache.delete(oldestKey);
this.updateStats("eviction");
}
}
/**
* Update cache statistics
*/
updateStats(operation) {
if (!this.options.enableStats) {
return;
}
switch (operation) {
case "hit":
this.stats.hits++;
break;
case "miss":
this.stats.misses++;
break;
case "set":
this.stats.sets++;
break;
case "delete":
this.stats.deletes++;
break;
case "eviction":
this.stats.evictions++;
break;
}
}
/**
* Update hit rate calculation
*/
updateHitRate() {
const total = this.stats.hits + this.stats.misses;
this.stats.hitRate = total > 0 ? this.stats.hits / total * 100 : 0;
}
}
class BrowserEventEmitter {
constructor() {
__publicField(this, "events", /* @__PURE__ */ new Map());
__publicField(this, "onceEvents", /* @__PURE__ */ new Map());
__publicField(this, "maxListeners", 10);
}
/**
* Add a listener for the specified event
*/
on(event, listener) {
if (!this.events.has(event)) {
this.events.set(event, /* @__PURE__ */ new Set());
}
const listeners = this.events.get(event);
listeners.add(listener);
if (listeners.size > this.maxListeners) {
console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${listeners.size} ${event} listeners added.`);
}
return this;
}
/**
* Add a one-time listener for the specified event
*/
once(event, listener) {
if (!this.onceEvents.has(event)) {
this.onceEvents.set(event, /* @__PURE__ */ new Set());
}
this.onceEvents.get(event).add(listener);
return this;
}
/**
* Remove a listener for the specified event
*/
off(event, listener) {
const listeners = this.events.get(event);
if (listeners) {
listeners.delete(listener);
if (listeners.size === 0) {
this.events.delete(event);
}
}
const onceListeners = this.onceEvents.get(event);
if (onceListeners) {
onceListeners.delete(listener);
if (onceListeners.size === 0) {
this.onceEvents.delete(event);
}
}
return this;
}
/**
* Emit an event to all listeners
*/
emit(event, ...args) {
let hasListeners = false;
const listeners = this.events.get(event);
if (listeners && listeners.size > 0) {
hasListeners = true;
const listenersArray = Array.from(listeners);
for (const listener of listenersArray) {
try {
listener.apply(this, args);
} catch (error) {
this.emitError(error);
}
}
}
const onceListeners = this.onceEvents.get(event);
if (onceListeners && onceListeners.size > 0) {
hasListeners = true;
const onceListenersArray = Array.from(onceListeners);
this.onceEvents.delete(event);
for (const listener of onceListenersArray) {
try {
listener.apply(this, args);
} catch (error) {
this.emitError(error);
}
}
}
return hasListeners;
}
/**
* Remove all listeners for a specific event or all events
*/
removeAllListeners(event) {
if (event) {
this.events.delete(event);
this.onceEvents.delete(event);
} else {
this.events.clear();
this.onceEvents.clear();
}
return this;
}
/**
* Get the number of listeners for an event
*/
listenerCount(event) {
const regularCount = this.events.get(event)?.size || 0;
const onceCount = this.onceEvents.get(event)?.size || 0;
return regularCount + onceCount;
}
/**
* Get all listeners for an event
*/
listeners(event) {
const regularListeners = Array.from(this.events.get(event) || []);
const onceListeners = Array.from(this.onceEvents.get(event) || []);
return [...regularListeners, ...onceListeners];
}
/**
* Set the maximum number of listeners before warning
*/
setMaxListeners(n2) {
this.maxListeners = n2;
return this;
}
/**
* Get the maximum number of listeners
*/
getMaxListeners() {
return this.maxListeners;
}
/**
* Get all event names that have listeners
*/
eventNames() {
const regularEvents = Array.from(this.events.keys());
const onceEvents = Array.from(this.onceEvents.keys());
return [.../* @__PURE__ */ new Set([...regularEvents, ...onceEvents])];
}
/**
* Add listener (alias for on)
*/
addListener(event, listener) {
return this.on(event, listener);
}
/**
* Remove listener (alias for off)
*/
removeListener(event, listener) {
return this.off(event, listener);
}
/**
* Prepend listener to the beginning of the listeners array
*/
prependListener(event, listener) {
const listeners = this.events.get(event);
if (listeners) {
const existingListeners = Array.from(listeners);
listeners.clear();
listeners.add(listener);
existingListeners.forEach((l2) => listeners.add(l2));
} else {
this.on(event, listener);
}
return this;
}
/**
* Prepend one-time listener
*/
prependOnceListener(event, listener) {
const onceListeners = this.onceEvents.get(event);
if (onceListeners) {
const existingListeners = Array.from(onceListeners);
onceListeners.clear();
onceListeners.add(listener);
existingListeners.forEach((l2) => onceListeners.add(l2));
} else {
this.once(event, listener);
}
return this;
}
/**
* Emit error event safely
*/
emitError(error) {
if (this.listenerCount("error") > 0) {
try {
this.emit("error", error);
} catch (e2) {
console.error("EventEmitter error in error handler:", e2);
}
} else {
console.error("Unhandled EventEmitter error:", error);
}
}
}
const defaultEventEmitter = new BrowserEventEmitter();
const _ModuleRegistry = class _ModuleRegistry {
constructor(options = {}) {
__publicField(this, "modules", /* @__PURE__ */ new Map());
__publicField(this, "registryListeners", /* @__PURE__ */ new Set());
__publicField(this, "cache");
__publicField(this, "options");
__publicField(this, "initialized", false);
__publicField(this, "initializing", false);
__publicField(this, "eventEmitter");
this.eventEmitter = new BrowserEventEmitter();
this.options = {
enableCaching: true,
cacheTimeout: 5 * 60 * 1e3,
// 5 minutes
validateOnRegister: true,
allowDuplicates: false,
autoInitialize: true,
...options
};
this.cache = new ModuleCache(this.options.cacheTimeout);
this.eventEmitter.on("error", (error) => {
console.error("ModuleRegistry Error:", error);
this.notifyListeners("onRegistryError", error);
});
}
/**
* Event emitter methods - delegate to internal event emitter
*/
on(event, listener) {
this.eventEmitter.on(event, listener);
return this;
}
once(event, listener) {
this.eventEmitter.once(event, listener);
return this;
}
off(event, listener) {
this.eventEmitter.off(event, listener);
return this;
}
emit(event, ...args) {
return this.eventEmitter.emit(event, ...args);
}
removeAllListeners(event) {
this.eventEmitter.removeAllListeners(event);
return this;
}
listenerCount(event) {
return this.eventEmitter.listenerCount(event);
}
listeners(event) {
return this.eventEmitter.listeners(event);
}
/**
* Get singleton instance of ModuleRegistry
*/
static getInstance(options) {
if (!_ModuleRegistry.instance) {
_ModuleRegistry.instance = new _ModuleRegistry(options);
}
return _ModuleRegistry.instance;
}
/**
* Reset singleton instance (mainly for testing)
*/
static resetInstance() {
if (_ModuleRegistry.instance) {
_ModuleRegistry.instance.removeAllListeners();
_ModuleRegistry.instance.modules.clear();
_ModuleRegistry.instance.registryListeners.clear();
_ModuleRegistry.instance.cache.clear();
}
_ModuleRegistry.instance = null;
}
/**
* Initialize the registry with module discovery
*/
async initialize() {
if (this.initialized) {
return;
}
if (this.initializing) {
return new Promise((resolve) => {
this.once("initialized", resolve);
});
}
this.initializing = true;
try {
const { ModuleDiscoveryService } = await import("./ModuleDiscoveryService-Dtr212Ba.js");
const discoveryService = new ModuleDiscoveryService();
const modules = await discoveryService.discoverModules();
for (const module of modules) {
try {
await this.registerModule(module, "discovery");
} catch (error) {
console.warn(`Failed to register module ${module.id}:`, error);
this.emit("error", error, module.id);
}
}
this.initialized = true;
this.initializing = false;
this.emit("initialized", this.getAllModules());
this.notifyListeners("onRegistryInitialized", this.getAllModules());
} catch (error) {
this.initializing = false;
this.emit("error", error);
throw error;
}
}
/**
* Register a module in the registry (with duplicate check)
*/
async registerModule(metadata, source = "runtime") {
try {
if (this.options.validateOnRegister) {
const validation = this.validateModule(metadata);
if (!validation.valid) {
throw new Error(`Module validation failed: ${validation.errors.map((e2) => e2.message).join(", ")}`);
}
}
if (!this.options.allowDuplicates && this.modules.has(metadata.id)) {
const existingModule = this.modules.get(metadata.id);
console.warn(`⚠️ Module ${metadata.id} is already registered`, {
existing: {
source: existingModule?.source,
timestamp: existingModule?.timestamp,
name: existingModule?.metadata.name
},
new: {
source,
name: metadata.name
}
});
throw new Error(`Module ${metadata.id} is already registered`);
}
const registration = {
metadata,
timestamp: Date.now(),
source
};
this.modules.set(metadata.id, registration);
if (this.options.enableCaching) {
this.cache.set(`module:${metadata.id}`, registration);
}
const event = {
type: "register",
moduleId: metadata.id,
module: metadata,
timestamp: Date.now()
};
this.emit("moduleRegistered", event);
this.notifyListeners("onModuleRegistered", metadata);
} catch (error) {
this.emit("error", error, metadata.id);
throw error;
}
}
/**
* Register a module safely (won't throw if already registered)
*/
async registerModuleSafe(metadata, source = "runtime") {
try {
if (this.modules.has(metadata.id)) {
console.log(`ℹ️ Module ${metadata.id} is already registered, skipping`);
return false;
}
await this.registerModule(metadata, source);
return true;
} catch (error) {
console.warn(`Failed to register module ${metadata.id}:`, error);
return false;
}
}
/**
* Unregister a module from the registry
*/
unregisterModule(moduleId) {
try {
const registration = this.modules.get(moduleId);
if (!registration) {
return false;
}
this.modules.delete(moduleId);
if (this.options.enableCaching) {
this.cache.delete(`module:${moduleId}`);
}
const event = {
type: "unregister",
moduleId,
module: registration.metadata,
timestamp: Date.now()
};
this.emit("moduleUnregistered", event);
this.notifyListeners("onModuleUnregistered", registration.metadata);
return true;
} catch (error) {
this.emit("error", error, moduleId);
return false;
}
}
/**
* Update a module's metadata
*/
updateModule(moduleId, updates) {
try {
const registration = this.modules.get(moduleId);
if (!registration) {
return false;
}
const updatedMetadata = { ...registration.metadata, ...updates };
if (this.options.validateOnRegister) {
const validation = this.validateModule(updatedMetadata);
if (!validation.valid) {
throw new Error(`Module validation failed: ${validation.errors.map((e2) => e2.message).join(", ")}`);
}
}
const updatedRegistration = {
...registration,
metadata: updatedMetadata,
timestamp: Date.now()
};
this.modules.set(moduleId, updatedRegistration);
if (this.options.enableCaching) {
this.cache.set(`module:${moduleId}`, updatedRegistration);
}
const event = {
type: "update",
moduleId,
module: updatedMetadata,
timestamp: Date.now()
};
this.emit("moduleUpdated", event);
this.notifyListeners("onModuleUpdated", updatedMetadata);
return true;
} catch (error) {
this.emit("error", error, moduleId);
return false;
}
}
/**
* Get a specific module by ID
*/
getModule(moduleId) {
if (this.options.enableCaching) {
const cached = this.cache.get(`module:${moduleId}`);
if (cached) {
return cached.metadata;
}
}
const registration = this.modules.get(moduleId);
return registration?.metadata;
}
/**
* Get all registered modules
*/
getAllModules() {
return Array.from(this.modules.values()).map((reg) => reg.metadata);
}
/**
* Get modules by category
*/
getModulesByCategory(category) {
return this.getAllModules().filter((module) => module.category === category);
}
/**
* Get modules by status
*/
getModulesByStatus(status) {
return this.getAllModules().filter((module) => module.status === status);
}
/**
* Get active modules only
*/
getActiveModules() {
return this.getModulesByStatus(ModuleStatus.ACTIVE);
}
/**
* Get modules with specific permissions
*/
getModulesWithPermissions(permissions) {
return this.getAllModules().filter(
(module) => permissions.some((permission) => module.permissions.includes(permission))
);
}
/**
* Search modules by name, description, or tags
*/
searchModules(query) {
const lowerQuery = query.toLowerCase();
return this.getAllModules().filter(
(module) => module.name.toLowerCase().includes(lowerQuery) || module.description.toLowerCase().includes(lowerQuery) || module.tags.some((tag) => tag.toLowerCase().includes(lowerQuery))
);
}
/**
* Check if a module is registered
*/
hasModule(moduleId) {
return this.modules.has(moduleId);
}
/**
* Get module count
*/
getModuleCount() {
return this.modules.size;
}
/**
* Validate module metadata
*/
validateModule(metadata) {
return validateModuleMetadata(metadata);
}
/**
* Add registry listener
*/
addListener(listener) {
this.registryListeners.add(listener);
}
/**
* Remove registry listener
*/
removeListener(listener) {
this.registryListeners.delete(listener);
}
/**
* Clear all modules (mainly for testing)
*/
clear() {
this.modules.clear();
this.cache.clear();
this.registryListeners.clear();
this.initialized = false;
this.initializing = false;
}
/**
* Get registry statistics
*/
getStats() {
const modules = this.getAllModules();
const categories = /* @__PURE__ */ new Map();
const statuses = /* @__PURE__ */ new Map();
modules.forEach((module) => {
categories.set(module.category, (categories.get(module.category) || 0) + 1);
statuses.set(module.status, (statuses.get(module.status) || 0) + 1);
});
return {
totalModules: modules.length,
categories: Object.fromEntries(categories),
statuses: Object.fromEntries(statuses),
initialized: this.initialized,
cacheEnabled: this.options.enableCaching
};
}
/**
* Notify all listeners of an event
*/
notifyListeners(method, ...args) {
this.registryListeners.forEach((listener) => {
try {
const fn = listener[method];
if (typeof fn === "function") {
fn.apply(listener, args);
}
} catch (error) {
console.error(`Error in registry listener ${method}:`, error);
}
});
}
};
__publicField(_ModuleRegistry, "instance", null);
let ModuleRegistry = _ModuleRegistry;
const loadModuleAsync = async (modulePath) => {
try {
const module = await import(modulePath);
return module.default || module;
} catch (error) {
console.error(`Failed to load module from ${modulePath}:`, error);
throw error;
}
};
const normalizeModuleId = (id) => {
return id.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
};
const isValidModuleId = (id) => {
return /^[a-z0-9-]+$/.test(id) && id.length > 0 && !id.startsWith("-") && !id.endsWith("-");
};
const resolveModulePath = (baseDir, moduleId) => {
return `${baseDir}/${moduleId}`;
};
const getModulePackageName = (moduleId) => {
return `-portal/module-${moduleId}`;
};
const hasPermission = (userPermissions, requiredPermissions) => {
if (requiredPermissions.length === 0) return true;
return requiredPermissions.some((permission) => userPermissions.includes(permission));
};
const hasRole = (userRoles, requiredRoles) => {
if (!requiredRoles || requiredRoles.length === 0) return true;
return requiredRoles.some((role) => userRoles.includes(role));
};
const filterModulesByPermissions = (modules, userPermissions) => {
return modules.filter((module) => hasPermission(userPermissions, module.permissions));
};
const filterModulesByRoles = (modules, userRoles) => {
return modules.filter((module) => hasRole(userRoles, module.roles || []));
};
const sortModulesByPriority = (modules) => {
return [...modules].sort((a2, b2) => b2.priority - a2.priority);
};
const sortModulesByName = (modules) => {
return [...modules].sort((a2, b2) => a2.name.localeCompare(b2.name));
};
const groupModulesByCategory = (modules) => {
return modules.reduce((groups, module) => {
const category = module.category;
if (!groups[category]) {
groups[category] = [];
}
groups[category].push(module);
return groups;
}, {});
};
const validateModuleId = (id) => {
if (!id) {
return { valid: false, error: "Module ID is required" };
}
if (!isValidModuleId(id)) {
return { valid: false, error: "Module ID must be lowercase alphanumeric with hyphens" };
}
if (id.length > 50) {
return { valid: false, error: "Module ID must be 50 characters or less" };
}
return { valid: true };
};
const resolveDependencies = (modules) => {
const moduleMap = new Map(modules.map((m2) => [m2.id, m2]));
const resolved = [];
const resolving = /* @__PURE__ */ new Set();
const resolved_ids = /* @__PURE__ */ new Set();
const resolve = (moduleId) => {
if (resolved_ids.has(moduleId)) return;
if (resolving.has(moduleId)) {
throw new Error(`Circular dependency detected: ${moduleId}`);
}
const module = moduleMap.get(moduleId);
if (!module) {
throw new Error(`Module not found: ${moduleId}`);
}
resolving.add(moduleId);
for (const depId of module.dependencies) {
resolve(depId);
}
resolving.delete(moduleId);
resolved_ids.add(moduleId);
resolved.push(module);
};
for (const module of modules) {
resolve(module.id);
}
return resolved;
};
class ModuleRegistryError extends Error {
constructor(message, moduleId, cause) {
super(message);
this.moduleId = moduleId;
this.cause = cause;
this.name = "ModuleRegistryError";
}
}
class ModuleValidationError extends ModuleRegistryError {
constructor(message, moduleId, validationErrors) {
super(message, moduleId);
this.validationErrors = validationErrors;
this.name = "ModuleValidationError";
}
}
class ModuleDiscoveryError extends ModuleRegistryError {
constructor(message, source, cause) {
super(message, void 0, cause);
this.source = source;
this.name = "ModuleDiscoveryError";
}
}
const measurePerformance = async (operation, label) => {
const start = performance.now();
const result = await operation();
const duration = performance.now() - start;
console.debug(`${label} took ${duration.toFixed(2)}ms`);
return { result, duration };
};
const debounce = (func, wait) => {
let timeout = null;
return (...args) => {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
func(...args);
}, wait);
};
};
const throttle = (func, limit) => {
let inThrottle = false;
return (...args) => {
if (!inThrottle) {
func(...args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
};
async function createModuleDiscoveryService(options) {
const { ModuleDiscoveryService } = await import(
/* webpackChunkName: "discovery-service" */
"./ModuleDiscoveryService-Dtr212Ba.js"
);
return new ModuleDiscoveryService(options);
}
const VERSION = "1.0.0";
const DEFAULT_REGISTRY_OPTIONS = {
enableCaching: true,
cacheTimeout: 5 * 60 * 1e3,
// 5 minutes
validateOnRegister: true,
allowDuplicates: false,
autoInitialize: true
};
const DEFAULT_DISCOVERY_OPTIONS = {
packageScanPaths: ["packages/modules/*", "packages/core/*"],
configScanPaths: ["packages/modules/*", "src/pages/modules/*"],
packageNamePattern: "@dbs-portal/module-*",
configFilePattern: "module.config.{ts,js}",
enablePackageScanning: true,
enableConfigDetection: true,
enableRuntimeRegistration: true,
validateModules: true,
ignorePatterns: ["node_modules/**", "dist/**", "**/*.test.*", "**/*.spec.*"]
};
export {
BrowserEventEmitter,
DEFAULT_DISCOVERY_OPTIONS,
DEFAULT_REGISTRY_OPTIONS,
E as ENV,
F as FEATURES,
ModuleCache,
ModuleCategory,
ModuleDiscoveryError,
ModuleMetadataSchema,
ModuleRegistry,
ModuleRegistryError,
ModuleRouteSchema,
ModuleStatus,
ModuleValidationError,
NavigationBadgeSchema,
NavigationBadgeType,
NavigationItemSchema,
RouteGuardSchema,
RouteGuardType,
VERSION,
w as assertFeature,
e as clearModuleCache,
u as createLogger,
createModuleDiscoveryService,
d as createPlatformError,
debounce,
defaultEventEmitter,
f as detectEnvironment,
filterModulesByPermissions,
filterModulesByRoles,
k as getCurrentDirectory,
s as getEnvironmentName,
a as getFs,
b as getGlob,
getModulePackageName,
g as getPath,
j as getPathSeparator,
c as getPlatformInfo,
t as getPlatformName,
groupModulesByCategory,
v as hasFeature,
hasPermission,
hasRole,
i as initializePlatformModules,
q as isBrowser,
m as isDevelopment,
r as isNode,
n as isProduction,
o as isTest,
isValidModuleId,
l as loadConditionalModule,
h as loadConditionalModuleSync,
loadModuleAsync,
measurePerformance,
normalizeModuleId,
p as preloadBrowserModules,
resolveDependencies,
resolveModulePath,
sortModulesByName,
sortModulesByPriority,
throttle,
validateModuleId,
validateModuleMetadata,
x as warnMissingFeature
};
//# sourceMappingURL=index.js.map