retrex-extensibles-core 2.0.8 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/hooks/useNav.cjs +6 -1
- package/dist/core/hooks/useNav.cjs.map +1 -1
- package/dist/core/hooks/useNav.js +6 -1
- package/dist/core/hooks/useNav.js.map +1 -1
- package/dist/core/hooks/useUtils.cjs.map +1 -1
- package/dist/core/hooks/useUtils.js.map +1 -1
- package/dist/index.cjs +6 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -210,7 +210,12 @@ var useServices = {
|
|
|
210
210
|
};
|
|
211
211
|
|
|
212
212
|
// src/core/hooks/useNav.ts
|
|
213
|
-
var
|
|
213
|
+
var GLOBAL_KEY = "__retrex_navitems__";
|
|
214
|
+
var globalStore = globalThis;
|
|
215
|
+
var navItems = globalStore[GLOBAL_KEY] ?? [];
|
|
216
|
+
if (!globalStore[GLOBAL_KEY]) {
|
|
217
|
+
globalStore[GLOBAL_KEY] = navItems;
|
|
218
|
+
}
|
|
214
219
|
function resolveCategory(item, parent) {
|
|
215
220
|
if (item.category) return item.category;
|
|
216
221
|
if (parent?.category) return parent.category;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/hooks/useNav.ts","../../../src/core/hooks/useModules.ts","../../../src/core/hooks/useSettings.ts","../../../src/core/hooks/useExtensibles.ts","../../../src/core/hooks/useServices.ts"],"sourcesContent":["import { useModules } from './useModules';\nimport { useServices } from './useServices';\n\nexport type NavItemType = 'link' | 'parent';\n\nexport interface NavItem {\n id: string;\n label: string;\n type: NavItemType;\n path?: string;\n icon?: string;\n children?: NavItem[];\n order?: number;\n category?: string;\n module?: string;\n service?: string;\n}\n\nconst navItems: NavItem[] = [];\n\nfunction resolveCategory(item: NavItem, parent?: NavItem): string {\n if (item.category) return item.category;\n if (parent?.category) return parent.category;\n if (item.module || parent?.module) return 'Modules';\n if (item.service || parent?.service) return 'Services';\n return 'Core';\n}\n\nfunction normalizeItem(item: NavItem, parent?: NavItem): NavItem {\n const normalized: NavItem = {\n ...item,\n category: resolveCategory(item, parent),\n module: item.module ?? parent?.module,\n service: item.service ?? parent?.service,\n };\n\n if (item.children && item.children.length > 0) {\n normalized.children = item.children.map((child) => normalizeItem(child, normalized));\n }\n\n return normalized;\n}\n\nfunction isItemActive(item: NavItem): boolean {\n if (item.module) {\n try {\n return useModules.isEnabled(item.module);\n } catch {\n return false;\n }\n }\n if (item.service) {\n try {\n return useServices.isServiceEnabled(item.service);\n } catch {\n return false;\n }\n }\n return true;\n}\n\nfunction filterActive(items: NavItem[]): NavItem[] {\n return items\n .filter((item) => isItemActive(item))\n .map((item) => {\n if (item.type === 'parent' && item.children) {\n return {\n ...item,\n children: filterActive(item.children),\n };\n }\n return item;\n })\n .filter((item) => (item.type === 'parent' ? (item.children?.length ?? 0) > 0 : true));\n}\n\nexport const navitem = {\n create(item: NavItem): void {\n navItems.push(normalizeItem(item));\n },\n};\n\nexport const useNav = {\n register(item: NavItem): void {\n navItems.push(normalizeItem(item));\n },\n getAll(): NavItem[] {\n return navItems;\n },\n getActive(): NavItem[] {\n return filterActive(navItems);\n },\n clear(): void {\n navItems.length = 0;\n },\n};\n","// src/hooks/useModules.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport { ModuleMeta } from '../types'; // Adjust the import path as necessary\n\nexport const useModules = {\n allEnabled(): ModuleMeta[] {\n const modules = useModules.load();\n if (!modules || modules.length === 0) return [];\n // Filter out modules that are not enabled\n \n // Return only the enabled modules\n // This assumes that each module has an 'enabled' property\n // Adjust the property name if necessary\n if (!Array.isArray(modules)) return [];\n if (modules.length === 0) return [];\n if (!modules.every((mod) => mod && typeof mod.enabled === 'boolean')) {\n throw new Error('Invalid module format: each module must have an \"enabled\" boolean property');\n }\n if (modules.some((mod) => !mod.lowerName)) {\n throw new Error('Invalid module format: each module must have a \"lowerName\" property');\n }\n\n // Filter and return only enabled modules\n return modules.filter((mod) => mod.enabled);\n },\n load(): ModuleMeta[] {\n const modulesDir = useSettings.getPaths().modules;\n if (!fs.existsSync(modulesDir)) return [];\n\n const moduleFolders = fs.readdirSync(modulesDir);\n const modules: ModuleMeta[] = [];\n\n moduleFolders.forEach((folder) => {\n const modulePath = path.join(modulesDir, folder);\n const moduleJsonPath = path.join(modulePath, 'module.json');\n if (!fs.existsSync(moduleJsonPath)) return;\n\n try {\n const rawData = fs.readFileSync(moduleJsonPath, 'utf-8');\n const moduleData = JSON.parse(rawData) as ModuleMeta;\n modules.push(moduleData);\n } catch {\n // invalid json or read error, ignore\n }\n });\n\n return modules;\n },\n\n isEnabled(moduleName: string): boolean {\n const modules = useModules.load();\n const mod = modules.find((m) => m.lowerName === moduleName.toLowerCase());\n return mod ? mod.enabled : false;\n },\n\n toggle(moduleName: string): boolean {\n const modulesDir = useSettings.getPaths().modules;\n const modules = useModules.load();\n const modIndex = modules.findIndex((m) => m.lowerName === moduleName.toLowerCase());\n if (modIndex === -1) return false;\n\n const moduleMeta = modules[modIndex];\n moduleMeta.enabled = !moduleMeta.enabled;\n\n const moduleJsonPath = path.join(modulesDir, moduleMeta.lowerName, 'module.json');\n try {\n fs.writeFileSync(moduleJsonPath, JSON.stringify(moduleMeta, null, 2), 'utf-8');\n return true;\n } catch {\n return false;\n }\n },\n\n getModules(): ModuleMeta[] {\n return useModules.load();\n },\n};\n","// src/hooks/useSettings.ts\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport { loadJSON, saveJSON } from './useExtensibles';\nimport type { ExtensiblePaths } from '../types'; // Adjust the import path as necessary\n\nconst defaultPaths: ExtensiblePaths = {\n modules: './app/modules',\n templates: './app/resources/themes',\n services: './app/services',\n events: './app/events',\n langs: './public/langs',\n providers: './app/providers',\n};\n\nlet extensiblePaths: ExtensiblePaths = { ...defaultPaths };\nconst isServer = typeof window === 'undefined';\n\n\nexport const useSettings = {\n getPaths(): ExtensiblePaths {\n if (isServer) {\n const json = loadJSON('./app/extensiblePaths.json');\n if (json) extensiblePaths = json;\n }\n\n if (isServer) {\n for (const key of Object.keys(defaultPaths) as Array<keyof ExtensiblePaths>) {\n const candidate = extensiblePaths[key];\n if (!candidate || !fs.existsSync(candidate)) {\n extensiblePaths[key] = defaultPaths[key];\n }\n }\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n langs: './langs',\n };\n // extensiblePaths = loadJSON(\"./app/extensiblePaths.json\");\n return extensiblePaths;\n },\n\n setPaths(paths: Partial<ExtensiblePaths>): void {\n if (isServer) {\n saveJSON('./app/extensiblePaths.json', {\n ...extensiblePaths,\n ...paths,\n });\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n ...paths,\n };\n },\n \n getKeyValue(key: string): string {\n return 'Coming Soon'\n },\n setKeyValue(key: string, value: string): void {\n return;\n },\n};\n","// src/hooks/useExtensibles.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport type { TemplateType, ExtensibleMeta, ExtensiblePaths } from '../types';\n\nlet paths: ExtensiblePaths;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport function registerExtensibles(extensiblePaths: ExtensiblePaths) {\n paths = extensiblePaths;\n}\n\nexport function loadJSON(filePath: string): any {\n if (!fs.existsSync(filePath)) return null;\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function saveJSON(filePath: string, data: any) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// --- MODULES ---\n\nexport function getModules(): ExtensibleMeta[] {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().modules)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().modules, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().modules, dir, 'module.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isModuleEnabled(name: string) {\n const modules = getModules();\n return modules.find((m) => m.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleModule(name: string, enabled: boolean) {\n const modules = getModules();\n const mod = modules.find((m) => m.lowerName === name.toLowerCase());\n if (!mod) throw new Error(`Module ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().modules, mod.lowerName, 'module.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onModuleEnabled(mod.name);\n else onModuleDisabled(mod.name);\n}\n\n// --- SERVICES ---\n\nexport function getServices(): ExtensibleMeta[] {\n if (!useSettings.getPaths().services) throw new Error('Services path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().services)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().services, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().services, dir, 'service.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isServiceEnabled(name: string) {\n const services = getServices();\n return services.find((s) => s.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleService(name: string, enabled: boolean) {\n const services = getServices();\n const svc = services.find((s) => s.lowerName === name.toLowerCase());\n if (!svc) throw new Error(`Service ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().services, svc.lowerName, 'service.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onServiceEnabled(svc.name);\n else onServiceDisabled(svc.name);\n}\n\n// --- TEMPLATES ---\n\n// Helper to get base path for a template type\nfunction getTemplateBasePath(type: TemplateType): string {\n if (!useSettings.getPaths().templates) throw new Error('Templates path not registered.');\n switch (type) {\n case 'admin-theme':\n case 'client-theme':\n return path.join(useSettings.getPaths().templates, 'themes');\n case 'portal':\n return path.join(useSettings.getPaths().templates, 'portals');\n case 'email':\n return path.join(useSettings.getPaths().templates, 'emails');\n default:\n throw new Error(`Unknown template type: ${type}`);\n }\n}\n\nexport function getTemplates(type: TemplateType): ExtensibleMeta[] {\n const basePath = getTemplateBasePath(type);\n if (!fs.existsSync(basePath)) return [];\n\n const dirs = fs\n .readdirSync(basePath)\n .filter((d: any) => fs.statSync(path.join(basePath, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(basePath, dir, 'template.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isTemplateActive(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n return templates.find((t) => t.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\n// Toggle templates: only 1 active per type allowed\nexport function toggleTemplate(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n\n templates.forEach((tpl) => {\n const metaPath = path.join(getTemplateBasePath(type), tpl.lowerName, 'template.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = tpl.lowerName === name.toLowerCase();\n saveJSON(metaPath, meta);\n });\n\n onTemplateSelect(type, name);\n}\n\n// --- EVENTS (replace with your event system integration) ---\n\nexport function onModuleEnabled(name: string) {\n console.log(`Module enabled: ${name}`);\n // emit event or custom logic here\n}\n\nexport function onModuleDisabled(name: string) {\n console.log(`Module disabled: ${name}`);\n}\n\nexport function onServiceEnabled(name: string) {\n console.log(`Service enabled: ${name}`);\n}\n\nexport function onServiceDisabled(name: string) {\n console.log(`Service disabled: ${name}`);\n}\n\nexport function onTemplateSelect(type: TemplateType, name: string) {\n console.log(`Template selected: type=${type}, name=${name}`);\n}\n\n// --- REGISTER HOOKS FOR MODULES ---\n\nexport async function registerEvents() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const eventsFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'events.server.js');\n if (fs.existsSync(eventsFile)) {\n const modEvents = await import(eventsFile);\n if (modEvents?.registerEvents) await modEvents.registerEvents();\n }\n }\n}\n\nexport async function registerMiddleware() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const middlewareFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'middleware.server.js');\n if (fs.existsSync(middlewareFile)) {\n const modMiddleware = await import(middlewareFile);\n if (modMiddleware?.registerMiddleware) await modMiddleware.registerMiddleware();\n }\n }\n}\n\n/**\n * Registers routes given a path to routes folder (for pages and API routes)\n * @param routesFolderPath string - path to module/service routes folder\n */\nexport async function registerRoutes(routesFolderPath: string) {\n if (!fs.existsSync(routesFolderPath)) return;\n\n const routeFiles = fs.readdirSync(routesFolderPath).filter((f: any) => /\\.(js|ts|jsx|tsx)$/.test(f));\n for (const file of routeFiles) {\n const routeModule = await import(path.join(routesFolderPath, file));\n if (routeModule?.registerRoute) {\n await routeModule.registerRoute();\n }\n }\n}\n\nexport async function loadProviders() {\n const paths = useSettings.getPaths();\n\n const all = [\n { type: 'module', list: getModules(), basePath: paths.modules },\n { type: 'service', list: getServices(), basePath: paths.services },\n ];\n\n for (const { type, list, basePath } of all) {\n for (const item of list) {\n if (!item.enabled) continue;\n\n const metaPath = path.join(path.resolve(basePath), item.lowerName, `${type}.json`);\n const meta = loadJSON(metaPath);\n if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n for (const providerRelPath of meta.providers) {\n try {\n let providerAbsPath = path.join(path.resolve(basePath), item.lowerName, providerRelPath);\n\n // Try .js fallback if file is .ts or .tsx\n const ext = path.extname(providerAbsPath);\n if (ext === '.ts' || ext === '.tsx') {\n const jsPath = providerAbsPath.replace(/\\.(ts|tsx)$/, '.js');\n try {\n await fs.accessSync(jsPath); // Check if compiled JS file exists\n providerAbsPath = jsPath;\n } catch {\n throw new Error(`Compiled JS version not found for provider: ${providerRelPath}`);\n }\n }\n\n const providerUrl = toFileUrl(providerAbsPath);\n const providerModule = await import(providerUrl);\n\n if (typeof providerModule.default === 'function') {\n await providerModule.default();\n console.log(`[${type}] ✅ Provider loaded: ${providerRelPath}`);\n } else {\n console.warn(`[${type}] ⚠️ Provider ${providerRelPath} has no default export.`);\n }\n } catch (err) {\n console.error(`[${type}] ❌ Failed to load provider ${providerRelPath}:`, err);\n }\n }\n }\n }\n}\n\n\n// export async function loadProviders() {\n// paths = {\n// modules: useSettings.getPaths().modules,\n// templates: useSettings.getPaths().templates,\n// services: useSettings.getPaths().services,\n// events: useSettings.getPaths().events,\n// }\n// const all = [\n// { type: 'module', list: getModules(), basePath: useSettings.getPaths().modules },\n// { type: 'service', list: getServices(), basePath: useSettings.getPaths().services },\n// ];\n\n// for (const { type, list, basePath } of all) {\n// for (const item of list) {\n// if (!item.enabled) continue;\n\n// const metaPath = path.join(basePath, item.lowerName, `${type}.json`);\n// const meta = loadJSON(metaPath);\n// if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n// for (const providerRelPath of meta.providers) {\n// try {\n// const providerPath = path.join(basePath, item.lowerName, providerRelPath);\n// const providerModule = await import(providerPath);\n// if (typeof providerModule.default === 'function') {\n// await providerModule.default(); // Call provider\n// console.log(`[${type}] Provider loaded: ${providerRelPath}`);\n// } else {\n// console.warn(`[${type}] Provider ${providerRelPath} has no default export.`);\n// }\n// } catch (err) {\n// console.error(`[${type}] Failed to load provider ${providerRelPath}:`, err);\n// }\n// }\n// }\n// }\n// }\n","// src/hooks/useServices.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\n\nexport interface ServiceMeta {\n name: string;\n lowerName: string;\n version?: string;\n author?: string;\n icon?: string;\n description?: string;\n enabled: boolean;\n providers?: string[];\n serviceType?: string;\n}\n\nexport const useServices = {\n loadServices(): ServiceMeta[] {\n const servicesDir = useSettings.getPaths().services;\n if (!fs.existsSync(servicesDir)) return [];\n\n const serviceFolders = fs.readdirSync(servicesDir);\n const services: ServiceMeta[] = [];\n\n serviceFolders.forEach((folder) => {\n const servicePath = path.join(servicesDir, folder);\n const serviceJsonPath = path.join(servicePath, 'service.json');\n if (!fs.existsSync(serviceJsonPath)) return;\n\n try {\n const rawData = fs.readFileSync(serviceJsonPath, 'utf-8');\n const serviceData = JSON.parse(rawData) as ServiceMeta;\n services.push(serviceData);\n } catch {\n // ignore invalid json or errors\n }\n });\n\n return services;\n },\n\n isServiceEnabled(serviceName: string): boolean {\n const services = useServices.loadServices();\n const service = services.find((s) => s.lowerName === serviceName.toLowerCase());\n return service ? service.enabled : false;\n },\n\n toggleService(serviceName: string, enabled: boolean): boolean {\n const servicesDir = useSettings.getPaths().services;\n const services = useServices.loadServices();\n const index = services.findIndex((s) => s.lowerName === serviceName.toLowerCase());\n if (index === -1) return false;\n\n const serviceMeta = services[index];\n serviceMeta.enabled = enabled;\n\n const serviceJsonPath = path.join(servicesDir, serviceMeta.lowerName, 'service.json');\n try {\n fs.writeFileSync(serviceJsonPath, JSON.stringify(serviceMeta, null, 2), 'utf-8');\n return true;\n } catch {\n return false;\n }\n },\n\n getServices(): ServiceMeta[] {\n return useServices.loadServices();\n },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,IAAAA,kBAAe;AACf,IAAAC,oBAAiB;;;ACAjB,IAAAC,kBAAe;;;ACDf,qBAAe;AACf,uBAAiB;AAiBV,SAAS,SAAS,UAAuB;AAC9C,MAAI,CAAC,eAAAC,QAAG,WAAW,QAAQ,EAAG,QAAO;AACrC,SAAO,KAAK,MAAM,eAAAA,QAAG,aAAa,UAAU,OAAO,CAAC;AACtD;AAEO,SAAS,SAAS,UAAkB,MAAW;AACpD,iBAAAA,QAAG,cAAc,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AACnE;;;ADnBA,IAAM,eAAgC;AAAA,EACpC,SAAS;AAAA,EACT,WAAW;AAAA,EACX,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAI,kBAAmC,EAAE,GAAG,aAAa;AACzD,IAAM,WAAW,OAAO,WAAW;AAG5B,IAAM,cAAc;AAAA,EACzB,WAA4B;AAC1B,QAAI,UAAU;AACZ,YAAM,OAAO,SAAS,4BAA4B;AAClD,UAAI,KAAM,mBAAkB;AAAA,IAC9B;AAEA,QAAI,UAAU;AACZ,iBAAW,OAAO,OAAO,KAAK,YAAY,GAAmC;AAC3E,cAAM,YAAY,gBAAgB,GAAG;AACrC,YAAI,CAAC,aAAa,CAAC,gBAAAC,QAAG,WAAW,SAAS,GAAG;AAC3C,0BAAgB,GAAG,IAAI,aAAa,GAAG;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAEA,sBAAkB;AAAA,MAChB,GAAG;AAAA,MACH,OAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,OAAuC;AAC9C,QAAI,UAAU;AACZ,eAAS,8BAA8B;AAAA,QACrC,GAAG;AAAA,QACH,GAAG;AAAA,MACL,CAAC;AAAA,IACH;AAEA,sBAAkB;AAAA,MAChB,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EACF;AAAA,EAEA,YAAY,KAAqB;AAC/B,WAAO;AAAA,EACT;AAAA,EACA,YAAY,KAAa,OAAqB;AAC5C;AAAA,EACF;AACF;;;AD1DO,IAAM,aAAa;AAAA,EACxB,aAA2B;AACzB,UAAM,UAAU,WAAW,KAAK;AAChC,QAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO,CAAC;AAM9C,QAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO,CAAC;AACrC,QAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClC,QAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,OAAO,OAAO,IAAI,YAAY,SAAS,GAAG;AACpE,YAAM,IAAI,MAAM,4EAA4E;AAAA,IAC9F;AACA,QAAI,QAAQ,KAAK,CAAC,QAAQ,CAAC,IAAI,SAAS,GAAG;AACzC,YAAM,IAAI,MAAM,qEAAqE;AAAA,IACvF;AAGA,WAAO,QAAQ,OAAO,CAAC,QAAQ,IAAI,OAAO;AAAA,EAC5C;AAAA,EACA,OAAqB;AACnB,UAAM,aAAa,YAAY,SAAS,EAAE;AAC1C,QAAI,CAAC,gBAAAC,QAAG,WAAW,UAAU,EAAG,QAAO,CAAC;AAExC,UAAM,gBAAgB,gBAAAA,QAAG,YAAY,UAAU;AAC/C,UAAM,UAAwB,CAAC;AAE/B,kBAAc,QAAQ,CAAC,WAAW;AAChC,YAAM,aAAa,kBAAAC,QAAK,KAAK,YAAY,MAAM;AAC/C,YAAM,iBAAiB,kBAAAA,QAAK,KAAK,YAAY,aAAa;AAC1D,UAAI,CAAC,gBAAAD,QAAG,WAAW,cAAc,EAAG;AAEpC,UAAI;AACF,cAAM,UAAU,gBAAAA,QAAG,aAAa,gBAAgB,OAAO;AACvD,cAAM,aAAa,KAAK,MAAM,OAAO;AACrC,gBAAQ,KAAK,UAAU;AAAA,MACzB,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,YAA6B;AACrC,UAAM,UAAU,WAAW,KAAK;AAChC,UAAM,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,cAAc,WAAW,YAAY,CAAC;AACxE,WAAO,MAAM,IAAI,UAAU;AAAA,EAC7B;AAAA,EAEA,OAAO,YAA6B;AAClC,UAAM,aAAa,YAAY,SAAS,EAAE;AAC1C,UAAM,UAAU,WAAW,KAAK;AAChC,UAAM,WAAW,QAAQ,UAAU,CAAC,MAAM,EAAE,cAAc,WAAW,YAAY,CAAC;AAClF,QAAI,aAAa,GAAI,QAAO;AAE5B,UAAM,aAAa,QAAQ,QAAQ;AACnC,eAAW,UAAU,CAAC,WAAW;AAEjC,UAAM,iBAAiB,kBAAAC,QAAK,KAAK,YAAY,WAAW,WAAW,aAAa;AAChF,QAAI;AACF,sBAAAD,QAAG,cAAc,gBAAgB,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,OAAO;AAC7E,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,aAA2B;AACzB,WAAO,WAAW,KAAK;AAAA,EACzB;AACF;;;AG7EA,IAAAE,kBAAe;AACf,IAAAC,oBAAiB;AAeV,IAAM,cAAc;AAAA,EACzB,eAA8B;AAC5B,UAAM,cAAc,YAAY,SAAS,EAAE;AAC3C,QAAI,CAAC,gBAAAC,QAAG,WAAW,WAAW,EAAG,QAAO,CAAC;AAEzC,UAAM,iBAAiB,gBAAAA,QAAG,YAAY,WAAW;AACjD,UAAM,WAA0B,CAAC;AAEjC,mBAAe,QAAQ,CAAC,WAAW;AACjC,YAAM,cAAc,kBAAAC,QAAK,KAAK,aAAa,MAAM;AACjD,YAAM,kBAAkB,kBAAAA,QAAK,KAAK,aAAa,cAAc;AAC7D,UAAI,CAAC,gBAAAD,QAAG,WAAW,eAAe,EAAG;AAErC,UAAI;AACF,cAAM,UAAU,gBAAAA,QAAG,aAAa,iBAAiB,OAAO;AACxD,cAAM,cAAc,KAAK,MAAM,OAAO;AACtC,iBAAS,KAAK,WAAW;AAAA,MAC3B,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,iBAAiB,aAA8B;AAC7C,UAAM,WAAW,YAAY,aAAa;AAC1C,UAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,cAAc,YAAY,YAAY,CAAC;AAC9E,WAAO,UAAU,QAAQ,UAAU;AAAA,EACrC;AAAA,EAEA,cAAc,aAAqB,SAA2B;AAC5D,UAAM,cAAc,YAAY,SAAS,EAAE;AAC3C,UAAM,WAAW,YAAY,aAAa;AAC1C,UAAM,QAAQ,SAAS,UAAU,CAAC,MAAM,EAAE,cAAc,YAAY,YAAY,CAAC;AACjF,QAAI,UAAU,GAAI,QAAO;AAEzB,UAAM,cAAc,SAAS,KAAK;AAClC,gBAAY,UAAU;AAEtB,UAAM,kBAAkB,kBAAAC,QAAK,KAAK,aAAa,YAAY,WAAW,cAAc;AACpF,QAAI;AACF,sBAAAD,QAAG,cAAc,iBAAiB,KAAK,UAAU,aAAa,MAAM,CAAC,GAAG,OAAO;AAC/E,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,cAA6B;AAC3B,WAAO,YAAY,aAAa;AAAA,EAClC;AACF;;;AJpDA,IAAM,WAAsB,CAAC;AAE7B,SAAS,gBAAgB,MAAe,QAA0B;AAChE,MAAI,KAAK,SAAU,QAAO,KAAK;AAC/B,MAAI,QAAQ,SAAU,QAAO,OAAO;AACpC,MAAI,KAAK,UAAU,QAAQ,OAAQ,QAAO;AAC1C,MAAI,KAAK,WAAW,QAAQ,QAAS,QAAO;AAC5C,SAAO;AACT;AAEA,SAAS,cAAc,MAAe,QAA2B;AAC/D,QAAM,aAAsB;AAAA,IAC1B,GAAG;AAAA,IACH,UAAU,gBAAgB,MAAM,MAAM;AAAA,IACtC,QAAQ,KAAK,UAAU,QAAQ;AAAA,IAC/B,SAAS,KAAK,WAAW,QAAQ;AAAA,EACnC;AAEA,MAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;AAC7C,eAAW,WAAW,KAAK,SAAS,IAAI,CAAC,UAAU,cAAc,OAAO,UAAU,CAAC;AAAA,EACrF;AAEA,SAAO;AACT;AAEA,SAAS,aAAa,MAAwB;AAC5C,MAAI,KAAK,QAAQ;AACf,QAAI;AACF,aAAO,WAAW,UAAU,KAAK,MAAM;AAAA,IACzC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,KAAK,SAAS;AAChB,QAAI;AACF,aAAO,YAAY,iBAAiB,KAAK,OAAO;AAAA,IAClD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAa,OAA6B;AACjD,SAAO,MACJ,OAAO,CAAC,SAAS,aAAa,IAAI,CAAC,EACnC,IAAI,CAAC,SAAS;AACb,QAAI,KAAK,SAAS,YAAY,KAAK,UAAU;AAC3C,aAAO;AAAA,QACL,GAAG;AAAA,QACH,UAAU,aAAa,KAAK,QAAQ;AAAA,MACtC;AAAA,IACF;AACA,WAAO;AAAA,EACT,CAAC,EACA,OAAO,CAAC,SAAU,KAAK,SAAS,YAAY,KAAK,UAAU,UAAU,KAAK,IAAI,IAAK;AACxF;AAEO,IAAM,UAAU;AAAA,EACrB,OAAO,MAAqB;AAC1B,aAAS,KAAK,cAAc,IAAI,CAAC;AAAA,EACnC;AACF;AAEO,IAAM,SAAS;AAAA,EACpB,SAAS,MAAqB;AAC5B,aAAS,KAAK,cAAc,IAAI,CAAC;AAAA,EACnC;AAAA,EACA,SAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EACA,YAAuB;AACrB,WAAO,aAAa,QAAQ;AAAA,EAC9B;AAAA,EACA,QAAc;AACZ,aAAS,SAAS;AAAA,EACpB;AACF;","names":["import_node_fs","import_node_path","import_node_fs","fs","fs","fs","path","import_node_fs","import_node_path","fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/hooks/useNav.ts","../../../src/core/hooks/useModules.ts","../../../src/core/hooks/useSettings.ts","../../../src/core/hooks/useExtensibles.ts","../../../src/core/hooks/useServices.ts"],"sourcesContent":["import { useModules } from './useModules';\nimport { useServices } from './useServices';\n\nexport type NavItemType = 'link' | 'parent';\n\nexport interface NavItem {\n id: string;\n label: string;\n type: NavItemType;\n path?: string;\n icon?: string;\n children?: NavItem[];\n order?: number;\n category?: string;\n module?: string;\n service?: string;\n}\n\nconst GLOBAL_KEY = '__retrex_navitems__';\nconst globalStore = globalThis as unknown as Record<string, NavItem[] | undefined>;\nconst navItems: NavItem[] = globalStore[GLOBAL_KEY] ?? [];\nif (!globalStore[GLOBAL_KEY]) {\n globalStore[GLOBAL_KEY] = navItems;\n}\n\nfunction resolveCategory(item: NavItem, parent?: NavItem): string {\n if (item.category) return item.category;\n if (parent?.category) return parent.category;\n if (item.module || parent?.module) return 'Modules';\n if (item.service || parent?.service) return 'Services';\n return 'Core';\n}\n\nfunction normalizeItem(item: NavItem, parent?: NavItem): NavItem {\n const normalized: NavItem = {\n ...item,\n category: resolveCategory(item, parent),\n module: item.module ?? parent?.module,\n service: item.service ?? parent?.service,\n };\n\n if (item.children && item.children.length > 0) {\n normalized.children = item.children.map((child) => normalizeItem(child, normalized));\n }\n\n return normalized;\n}\n\nfunction isItemActive(item: NavItem): boolean {\n if (item.module) {\n try {\n return useModules.isEnabled(item.module);\n } catch {\n return false;\n }\n }\n if (item.service) {\n try {\n return useServices.isServiceEnabled(item.service);\n } catch {\n return false;\n }\n }\n return true;\n}\n\nfunction filterActive(items: NavItem[]): NavItem[] {\n return items\n .filter((item) => isItemActive(item))\n .map((item) => {\n if (item.type === 'parent' && item.children) {\n return {\n ...item,\n children: filterActive(item.children),\n };\n }\n return item;\n })\n .filter((item) => (item.type === 'parent' ? (item.children?.length ?? 0) > 0 : true));\n}\n\nexport const navitem = {\n create(item: NavItem): void {\n navItems.push(normalizeItem(item));\n },\n};\n\nexport const useNav = {\n register(item: NavItem): void {\n navItems.push(normalizeItem(item));\n },\n getAll(): NavItem[] {\n return navItems;\n },\n getActive(): NavItem[] {\n return filterActive(navItems);\n },\n clear(): void {\n navItems.length = 0;\n },\n};\n","// src/hooks/useModules.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport { ModuleMeta } from '../types'; // Adjust the import path as necessary\n\nexport const useModules = {\n allEnabled(): ModuleMeta[] {\n const modules = useModules.load();\n if (!modules || modules.length === 0) return [];\n // Filter out modules that are not enabled\n \n // Return only the enabled modules\n // This assumes that each module has an 'enabled' property\n // Adjust the property name if necessary\n if (!Array.isArray(modules)) return [];\n if (modules.length === 0) return [];\n if (!modules.every((mod) => mod && typeof mod.enabled === 'boolean')) {\n throw new Error('Invalid module format: each module must have an \"enabled\" boolean property');\n }\n if (modules.some((mod) => !mod.lowerName)) {\n throw new Error('Invalid module format: each module must have a \"lowerName\" property');\n }\n\n // Filter and return only enabled modules\n return modules.filter((mod) => mod.enabled);\n },\n load(): ModuleMeta[] {\n const modulesDir = useSettings.getPaths().modules;\n if (!fs.existsSync(modulesDir)) return [];\n\n const moduleFolders = fs.readdirSync(modulesDir);\n const modules: ModuleMeta[] = [];\n\n moduleFolders.forEach((folder) => {\n const modulePath = path.join(modulesDir, folder);\n const moduleJsonPath = path.join(modulePath, 'module.json');\n if (!fs.existsSync(moduleJsonPath)) return;\n\n try {\n const rawData = fs.readFileSync(moduleJsonPath, 'utf-8');\n const moduleData = JSON.parse(rawData) as ModuleMeta;\n modules.push(moduleData);\n } catch {\n // invalid json or read error, ignore\n }\n });\n\n return modules;\n },\n\n isEnabled(moduleName: string): boolean {\n const modules = useModules.load();\n const mod = modules.find((m) => m.lowerName === moduleName.toLowerCase());\n return mod ? mod.enabled : false;\n },\n\n toggle(moduleName: string): boolean {\n const modulesDir = useSettings.getPaths().modules;\n const modules = useModules.load();\n const modIndex = modules.findIndex((m) => m.lowerName === moduleName.toLowerCase());\n if (modIndex === -1) return false;\n\n const moduleMeta = modules[modIndex];\n moduleMeta.enabled = !moduleMeta.enabled;\n\n const moduleJsonPath = path.join(modulesDir, moduleMeta.lowerName, 'module.json');\n try {\n fs.writeFileSync(moduleJsonPath, JSON.stringify(moduleMeta, null, 2), 'utf-8');\n return true;\n } catch {\n return false;\n }\n },\n\n getModules(): ModuleMeta[] {\n return useModules.load();\n },\n};\n","// src/hooks/useSettings.ts\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport { loadJSON, saveJSON } from './useExtensibles';\nimport type { ExtensiblePaths } from '../types'; // Adjust the import path as necessary\n\nconst defaultPaths: ExtensiblePaths = {\n modules: './app/modules',\n templates: './app/resources/themes',\n services: './app/services',\n events: './app/events',\n langs: './public/langs',\n providers: './app/providers',\n};\n\nlet extensiblePaths: ExtensiblePaths = { ...defaultPaths };\nconst isServer = typeof window === 'undefined';\n\n\nexport const useSettings = {\n getPaths(): ExtensiblePaths {\n if (isServer) {\n const json = loadJSON('./app/extensiblePaths.json');\n if (json) extensiblePaths = json;\n }\n\n if (isServer) {\n for (const key of Object.keys(defaultPaths) as Array<keyof ExtensiblePaths>) {\n const candidate = extensiblePaths[key];\n if (!candidate || !fs.existsSync(candidate)) {\n extensiblePaths[key] = defaultPaths[key];\n }\n }\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n langs: './langs',\n };\n // extensiblePaths = loadJSON(\"./app/extensiblePaths.json\");\n return extensiblePaths;\n },\n\n setPaths(paths: Partial<ExtensiblePaths>): void {\n if (isServer) {\n saveJSON('./app/extensiblePaths.json', {\n ...extensiblePaths,\n ...paths,\n });\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n ...paths,\n };\n },\n \n getKeyValue(key: string): string {\n return 'Coming Soon'\n },\n setKeyValue(key: string, value: string): void {\n return;\n },\n};\n","// src/hooks/useExtensibles.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport type { TemplateType, ExtensibleMeta, ExtensiblePaths } from '../types';\n\nlet paths: ExtensiblePaths;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport function registerExtensibles(extensiblePaths: ExtensiblePaths) {\n paths = extensiblePaths;\n}\n\nexport function loadJSON(filePath: string): any {\n if (!fs.existsSync(filePath)) return null;\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function saveJSON(filePath: string, data: any) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// --- MODULES ---\n\nexport function getModules(): ExtensibleMeta[] {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().modules)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().modules, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().modules, dir, 'module.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isModuleEnabled(name: string) {\n const modules = getModules();\n return modules.find((m) => m.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleModule(name: string, enabled: boolean) {\n const modules = getModules();\n const mod = modules.find((m) => m.lowerName === name.toLowerCase());\n if (!mod) throw new Error(`Module ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().modules, mod.lowerName, 'module.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onModuleEnabled(mod.name);\n else onModuleDisabled(mod.name);\n}\n\n// --- SERVICES ---\n\nexport function getServices(): ExtensibleMeta[] {\n if (!useSettings.getPaths().services) throw new Error('Services path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().services)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().services, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().services, dir, 'service.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isServiceEnabled(name: string) {\n const services = getServices();\n return services.find((s) => s.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleService(name: string, enabled: boolean) {\n const services = getServices();\n const svc = services.find((s) => s.lowerName === name.toLowerCase());\n if (!svc) throw new Error(`Service ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().services, svc.lowerName, 'service.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onServiceEnabled(svc.name);\n else onServiceDisabled(svc.name);\n}\n\n// --- TEMPLATES ---\n\n// Helper to get base path for a template type\nfunction getTemplateBasePath(type: TemplateType): string {\n if (!useSettings.getPaths().templates) throw new Error('Templates path not registered.');\n switch (type) {\n case 'admin-theme':\n case 'client-theme':\n return path.join(useSettings.getPaths().templates, 'themes');\n case 'portal':\n return path.join(useSettings.getPaths().templates, 'portals');\n case 'email':\n return path.join(useSettings.getPaths().templates, 'emails');\n default:\n throw new Error(`Unknown template type: ${type}`);\n }\n}\n\nexport function getTemplates(type: TemplateType): ExtensibleMeta[] {\n const basePath = getTemplateBasePath(type);\n if (!fs.existsSync(basePath)) return [];\n\n const dirs = fs\n .readdirSync(basePath)\n .filter((d: any) => fs.statSync(path.join(basePath, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(basePath, dir, 'template.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isTemplateActive(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n return templates.find((t) => t.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\n// Toggle templates: only 1 active per type allowed\nexport function toggleTemplate(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n\n templates.forEach((tpl) => {\n const metaPath = path.join(getTemplateBasePath(type), tpl.lowerName, 'template.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = tpl.lowerName === name.toLowerCase();\n saveJSON(metaPath, meta);\n });\n\n onTemplateSelect(type, name);\n}\n\n// --- EVENTS (replace with your event system integration) ---\n\nexport function onModuleEnabled(name: string) {\n console.log(`Module enabled: ${name}`);\n // emit event or custom logic here\n}\n\nexport function onModuleDisabled(name: string) {\n console.log(`Module disabled: ${name}`);\n}\n\nexport function onServiceEnabled(name: string) {\n console.log(`Service enabled: ${name}`);\n}\n\nexport function onServiceDisabled(name: string) {\n console.log(`Service disabled: ${name}`);\n}\n\nexport function onTemplateSelect(type: TemplateType, name: string) {\n console.log(`Template selected: type=${type}, name=${name}`);\n}\n\n// --- REGISTER HOOKS FOR MODULES ---\n\nexport async function registerEvents() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const eventsFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'events.server.js');\n if (fs.existsSync(eventsFile)) {\n const modEvents = await import(eventsFile);\n if (modEvents?.registerEvents) await modEvents.registerEvents();\n }\n }\n}\n\nexport async function registerMiddleware() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const middlewareFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'middleware.server.js');\n if (fs.existsSync(middlewareFile)) {\n const modMiddleware = await import(middlewareFile);\n if (modMiddleware?.registerMiddleware) await modMiddleware.registerMiddleware();\n }\n }\n}\n\n/**\n * Registers routes given a path to routes folder (for pages and API routes)\n * @param routesFolderPath string - path to module/service routes folder\n */\nexport async function registerRoutes(routesFolderPath: string) {\n if (!fs.existsSync(routesFolderPath)) return;\n\n const routeFiles = fs.readdirSync(routesFolderPath).filter((f: any) => /\\.(js|ts|jsx|tsx)$/.test(f));\n for (const file of routeFiles) {\n const routeModule = await import(path.join(routesFolderPath, file));\n if (routeModule?.registerRoute) {\n await routeModule.registerRoute();\n }\n }\n}\n\nexport async function loadProviders() {\n const paths = useSettings.getPaths();\n\n const all = [\n { type: 'module', list: getModules(), basePath: paths.modules },\n { type: 'service', list: getServices(), basePath: paths.services },\n ];\n\n for (const { type, list, basePath } of all) {\n for (const item of list) {\n if (!item.enabled) continue;\n\n const metaPath = path.join(path.resolve(basePath), item.lowerName, `${type}.json`);\n const meta = loadJSON(metaPath);\n if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n for (const providerRelPath of meta.providers) {\n try {\n let providerAbsPath = path.join(path.resolve(basePath), item.lowerName, providerRelPath);\n\n // Try .js fallback if file is .ts or .tsx\n const ext = path.extname(providerAbsPath);\n if (ext === '.ts' || ext === '.tsx') {\n const jsPath = providerAbsPath.replace(/\\.(ts|tsx)$/, '.js');\n try {\n await fs.accessSync(jsPath); // Check if compiled JS file exists\n providerAbsPath = jsPath;\n } catch {\n throw new Error(`Compiled JS version not found for provider: ${providerRelPath}`);\n }\n }\n\n const providerUrl = toFileUrl(providerAbsPath);\n const providerModule = await import(providerUrl);\n\n if (typeof providerModule.default === 'function') {\n await providerModule.default();\n console.log(`[${type}] ✅ Provider loaded: ${providerRelPath}`);\n } else {\n console.warn(`[${type}] ⚠️ Provider ${providerRelPath} has no default export.`);\n }\n } catch (err) {\n console.error(`[${type}] ❌ Failed to load provider ${providerRelPath}:`, err);\n }\n }\n }\n }\n}\n\n\n// export async function loadProviders() {\n// paths = {\n// modules: useSettings.getPaths().modules,\n// templates: useSettings.getPaths().templates,\n// services: useSettings.getPaths().services,\n// events: useSettings.getPaths().events,\n// }\n// const all = [\n// { type: 'module', list: getModules(), basePath: useSettings.getPaths().modules },\n// { type: 'service', list: getServices(), basePath: useSettings.getPaths().services },\n// ];\n\n// for (const { type, list, basePath } of all) {\n// for (const item of list) {\n// if (!item.enabled) continue;\n\n// const metaPath = path.join(basePath, item.lowerName, `${type}.json`);\n// const meta = loadJSON(metaPath);\n// if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n// for (const providerRelPath of meta.providers) {\n// try {\n// const providerPath = path.join(basePath, item.lowerName, providerRelPath);\n// const providerModule = await import(providerPath);\n// if (typeof providerModule.default === 'function') {\n// await providerModule.default(); // Call provider\n// console.log(`[${type}] Provider loaded: ${providerRelPath}`);\n// } else {\n// console.warn(`[${type}] Provider ${providerRelPath} has no default export.`);\n// }\n// } catch (err) {\n// console.error(`[${type}] Failed to load provider ${providerRelPath}:`, err);\n// }\n// }\n// }\n// }\n// }\n","// src/hooks/useServices.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\n\nexport interface ServiceMeta {\n name: string;\n lowerName: string;\n version?: string;\n author?: string;\n icon?: string;\n description?: string;\n enabled: boolean;\n providers?: string[];\n serviceType?: string;\n}\n\nexport const useServices = {\n loadServices(): ServiceMeta[] {\n const servicesDir = useSettings.getPaths().services;\n if (!fs.existsSync(servicesDir)) return [];\n\n const serviceFolders = fs.readdirSync(servicesDir);\n const services: ServiceMeta[] = [];\n\n serviceFolders.forEach((folder) => {\n const servicePath = path.join(servicesDir, folder);\n const serviceJsonPath = path.join(servicePath, 'service.json');\n if (!fs.existsSync(serviceJsonPath)) return;\n\n try {\n const rawData = fs.readFileSync(serviceJsonPath, 'utf-8');\n const serviceData = JSON.parse(rawData) as ServiceMeta;\n services.push(serviceData);\n } catch {\n // ignore invalid json or errors\n }\n });\n\n return services;\n },\n\n isServiceEnabled(serviceName: string): boolean {\n const services = useServices.loadServices();\n const service = services.find((s) => s.lowerName === serviceName.toLowerCase());\n return service ? service.enabled : false;\n },\n\n toggleService(serviceName: string, enabled: boolean): boolean {\n const servicesDir = useSettings.getPaths().services;\n const services = useServices.loadServices();\n const index = services.findIndex((s) => s.lowerName === serviceName.toLowerCase());\n if (index === -1) return false;\n\n const serviceMeta = services[index];\n serviceMeta.enabled = enabled;\n\n const serviceJsonPath = path.join(servicesDir, serviceMeta.lowerName, 'service.json');\n try {\n fs.writeFileSync(serviceJsonPath, JSON.stringify(serviceMeta, null, 2), 'utf-8');\n return true;\n } catch {\n return false;\n }\n },\n\n getServices(): ServiceMeta[] {\n return useServices.loadServices();\n },\n};\n"],"mappings":"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","names":["import_node_fs","import_node_path","import_node_fs","fs","fs","fs","path","import_node_fs","import_node_path","fs","path"]}
|
|
@@ -173,7 +173,12 @@ var useServices = {
|
|
|
173
173
|
};
|
|
174
174
|
|
|
175
175
|
// src/core/hooks/useNav.ts
|
|
176
|
-
var
|
|
176
|
+
var GLOBAL_KEY = "__retrex_navitems__";
|
|
177
|
+
var globalStore = globalThis;
|
|
178
|
+
var navItems = globalStore[GLOBAL_KEY] ?? [];
|
|
179
|
+
if (!globalStore[GLOBAL_KEY]) {
|
|
180
|
+
globalStore[GLOBAL_KEY] = navItems;
|
|
181
|
+
}
|
|
177
182
|
function resolveCategory(item, parent) {
|
|
178
183
|
if (item.category) return item.category;
|
|
179
184
|
if (parent?.category) return parent.category;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/hooks/useModules.ts","../../../src/core/hooks/useSettings.ts","../../../src/core/hooks/useExtensibles.ts","../../../src/core/hooks/useServices.ts","../../../src/core/hooks/useNav.ts"],"sourcesContent":["// src/hooks/useModules.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport { ModuleMeta } from '../types'; // Adjust the import path as necessary\n\nexport const useModules = {\n allEnabled(): ModuleMeta[] {\n const modules = useModules.load();\n if (!modules || modules.length === 0) return [];\n // Filter out modules that are not enabled\n \n // Return only the enabled modules\n // This assumes that each module has an 'enabled' property\n // Adjust the property name if necessary\n if (!Array.isArray(modules)) return [];\n if (modules.length === 0) return [];\n if (!modules.every((mod) => mod && typeof mod.enabled === 'boolean')) {\n throw new Error('Invalid module format: each module must have an \"enabled\" boolean property');\n }\n if (modules.some((mod) => !mod.lowerName)) {\n throw new Error('Invalid module format: each module must have a \"lowerName\" property');\n }\n\n // Filter and return only enabled modules\n return modules.filter((mod) => mod.enabled);\n },\n load(): ModuleMeta[] {\n const modulesDir = useSettings.getPaths().modules;\n if (!fs.existsSync(modulesDir)) return [];\n\n const moduleFolders = fs.readdirSync(modulesDir);\n const modules: ModuleMeta[] = [];\n\n moduleFolders.forEach((folder) => {\n const modulePath = path.join(modulesDir, folder);\n const moduleJsonPath = path.join(modulePath, 'module.json');\n if (!fs.existsSync(moduleJsonPath)) return;\n\n try {\n const rawData = fs.readFileSync(moduleJsonPath, 'utf-8');\n const moduleData = JSON.parse(rawData) as ModuleMeta;\n modules.push(moduleData);\n } catch {\n // invalid json or read error, ignore\n }\n });\n\n return modules;\n },\n\n isEnabled(moduleName: string): boolean {\n const modules = useModules.load();\n const mod = modules.find((m) => m.lowerName === moduleName.toLowerCase());\n return mod ? mod.enabled : false;\n },\n\n toggle(moduleName: string): boolean {\n const modulesDir = useSettings.getPaths().modules;\n const modules = useModules.load();\n const modIndex = modules.findIndex((m) => m.lowerName === moduleName.toLowerCase());\n if (modIndex === -1) return false;\n\n const moduleMeta = modules[modIndex];\n moduleMeta.enabled = !moduleMeta.enabled;\n\n const moduleJsonPath = path.join(modulesDir, moduleMeta.lowerName, 'module.json');\n try {\n fs.writeFileSync(moduleJsonPath, JSON.stringify(moduleMeta, null, 2), 'utf-8');\n return true;\n } catch {\n return false;\n }\n },\n\n getModules(): ModuleMeta[] {\n return useModules.load();\n },\n};\n","// src/hooks/useSettings.ts\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport { loadJSON, saveJSON } from './useExtensibles';\nimport type { ExtensiblePaths } from '../types'; // Adjust the import path as necessary\n\nconst defaultPaths: ExtensiblePaths = {\n modules: './app/modules',\n templates: './app/resources/themes',\n services: './app/services',\n events: './app/events',\n langs: './public/langs',\n providers: './app/providers',\n};\n\nlet extensiblePaths: ExtensiblePaths = { ...defaultPaths };\nconst isServer = typeof window === 'undefined';\n\n\nexport const useSettings = {\n getPaths(): ExtensiblePaths {\n if (isServer) {\n const json = loadJSON('./app/extensiblePaths.json');\n if (json) extensiblePaths = json;\n }\n\n if (isServer) {\n for (const key of Object.keys(defaultPaths) as Array<keyof ExtensiblePaths>) {\n const candidate = extensiblePaths[key];\n if (!candidate || !fs.existsSync(candidate)) {\n extensiblePaths[key] = defaultPaths[key];\n }\n }\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n langs: './langs',\n };\n // extensiblePaths = loadJSON(\"./app/extensiblePaths.json\");\n return extensiblePaths;\n },\n\n setPaths(paths: Partial<ExtensiblePaths>): void {\n if (isServer) {\n saveJSON('./app/extensiblePaths.json', {\n ...extensiblePaths,\n ...paths,\n });\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n ...paths,\n };\n },\n \n getKeyValue(key: string): string {\n return 'Coming Soon'\n },\n setKeyValue(key: string, value: string): void {\n return;\n },\n};\n","// src/hooks/useExtensibles.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport type { TemplateType, ExtensibleMeta, ExtensiblePaths } from '../types';\n\nlet paths: ExtensiblePaths;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport function registerExtensibles(extensiblePaths: ExtensiblePaths) {\n paths = extensiblePaths;\n}\n\nexport function loadJSON(filePath: string): any {\n if (!fs.existsSync(filePath)) return null;\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function saveJSON(filePath: string, data: any) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// --- MODULES ---\n\nexport function getModules(): ExtensibleMeta[] {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().modules)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().modules, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().modules, dir, 'module.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isModuleEnabled(name: string) {\n const modules = getModules();\n return modules.find((m) => m.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleModule(name: string, enabled: boolean) {\n const modules = getModules();\n const mod = modules.find((m) => m.lowerName === name.toLowerCase());\n if (!mod) throw new Error(`Module ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().modules, mod.lowerName, 'module.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onModuleEnabled(mod.name);\n else onModuleDisabled(mod.name);\n}\n\n// --- SERVICES ---\n\nexport function getServices(): ExtensibleMeta[] {\n if (!useSettings.getPaths().services) throw new Error('Services path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().services)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().services, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().services, dir, 'service.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isServiceEnabled(name: string) {\n const services = getServices();\n return services.find((s) => s.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleService(name: string, enabled: boolean) {\n const services = getServices();\n const svc = services.find((s) => s.lowerName === name.toLowerCase());\n if (!svc) throw new Error(`Service ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().services, svc.lowerName, 'service.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onServiceEnabled(svc.name);\n else onServiceDisabled(svc.name);\n}\n\n// --- TEMPLATES ---\n\n// Helper to get base path for a template type\nfunction getTemplateBasePath(type: TemplateType): string {\n if (!useSettings.getPaths().templates) throw new Error('Templates path not registered.');\n switch (type) {\n case 'admin-theme':\n case 'client-theme':\n return path.join(useSettings.getPaths().templates, 'themes');\n case 'portal':\n return path.join(useSettings.getPaths().templates, 'portals');\n case 'email':\n return path.join(useSettings.getPaths().templates, 'emails');\n default:\n throw new Error(`Unknown template type: ${type}`);\n }\n}\n\nexport function getTemplates(type: TemplateType): ExtensibleMeta[] {\n const basePath = getTemplateBasePath(type);\n if (!fs.existsSync(basePath)) return [];\n\n const dirs = fs\n .readdirSync(basePath)\n .filter((d: any) => fs.statSync(path.join(basePath, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(basePath, dir, 'template.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isTemplateActive(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n return templates.find((t) => t.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\n// Toggle templates: only 1 active per type allowed\nexport function toggleTemplate(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n\n templates.forEach((tpl) => {\n const metaPath = path.join(getTemplateBasePath(type), tpl.lowerName, 'template.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = tpl.lowerName === name.toLowerCase();\n saveJSON(metaPath, meta);\n });\n\n onTemplateSelect(type, name);\n}\n\n// --- EVENTS (replace with your event system integration) ---\n\nexport function onModuleEnabled(name: string) {\n console.log(`Module enabled: ${name}`);\n // emit event or custom logic here\n}\n\nexport function onModuleDisabled(name: string) {\n console.log(`Module disabled: ${name}`);\n}\n\nexport function onServiceEnabled(name: string) {\n console.log(`Service enabled: ${name}`);\n}\n\nexport function onServiceDisabled(name: string) {\n console.log(`Service disabled: ${name}`);\n}\n\nexport function onTemplateSelect(type: TemplateType, name: string) {\n console.log(`Template selected: type=${type}, name=${name}`);\n}\n\n// --- REGISTER HOOKS FOR MODULES ---\n\nexport async function registerEvents() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const eventsFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'events.server.js');\n if (fs.existsSync(eventsFile)) {\n const modEvents = await import(eventsFile);\n if (modEvents?.registerEvents) await modEvents.registerEvents();\n }\n }\n}\n\nexport async function registerMiddleware() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const middlewareFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'middleware.server.js');\n if (fs.existsSync(middlewareFile)) {\n const modMiddleware = await import(middlewareFile);\n if (modMiddleware?.registerMiddleware) await modMiddleware.registerMiddleware();\n }\n }\n}\n\n/**\n * Registers routes given a path to routes folder (for pages and API routes)\n * @param routesFolderPath string - path to module/service routes folder\n */\nexport async function registerRoutes(routesFolderPath: string) {\n if (!fs.existsSync(routesFolderPath)) return;\n\n const routeFiles = fs.readdirSync(routesFolderPath).filter((f: any) => /\\.(js|ts|jsx|tsx)$/.test(f));\n for (const file of routeFiles) {\n const routeModule = await import(path.join(routesFolderPath, file));\n if (routeModule?.registerRoute) {\n await routeModule.registerRoute();\n }\n }\n}\n\nexport async function loadProviders() {\n const paths = useSettings.getPaths();\n\n const all = [\n { type: 'module', list: getModules(), basePath: paths.modules },\n { type: 'service', list: getServices(), basePath: paths.services },\n ];\n\n for (const { type, list, basePath } of all) {\n for (const item of list) {\n if (!item.enabled) continue;\n\n const metaPath = path.join(path.resolve(basePath), item.lowerName, `${type}.json`);\n const meta = loadJSON(metaPath);\n if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n for (const providerRelPath of meta.providers) {\n try {\n let providerAbsPath = path.join(path.resolve(basePath), item.lowerName, providerRelPath);\n\n // Try .js fallback if file is .ts or .tsx\n const ext = path.extname(providerAbsPath);\n if (ext === '.ts' || ext === '.tsx') {\n const jsPath = providerAbsPath.replace(/\\.(ts|tsx)$/, '.js');\n try {\n await fs.accessSync(jsPath); // Check if compiled JS file exists\n providerAbsPath = jsPath;\n } catch {\n throw new Error(`Compiled JS version not found for provider: ${providerRelPath}`);\n }\n }\n\n const providerUrl = toFileUrl(providerAbsPath);\n const providerModule = await import(providerUrl);\n\n if (typeof providerModule.default === 'function') {\n await providerModule.default();\n console.log(`[${type}] ✅ Provider loaded: ${providerRelPath}`);\n } else {\n console.warn(`[${type}] ⚠️ Provider ${providerRelPath} has no default export.`);\n }\n } catch (err) {\n console.error(`[${type}] ❌ Failed to load provider ${providerRelPath}:`, err);\n }\n }\n }\n }\n}\n\n\n// export async function loadProviders() {\n// paths = {\n// modules: useSettings.getPaths().modules,\n// templates: useSettings.getPaths().templates,\n// services: useSettings.getPaths().services,\n// events: useSettings.getPaths().events,\n// }\n// const all = [\n// { type: 'module', list: getModules(), basePath: useSettings.getPaths().modules },\n// { type: 'service', list: getServices(), basePath: useSettings.getPaths().services },\n// ];\n\n// for (const { type, list, basePath } of all) {\n// for (const item of list) {\n// if (!item.enabled) continue;\n\n// const metaPath = path.join(basePath, item.lowerName, `${type}.json`);\n// const meta = loadJSON(metaPath);\n// if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n// for (const providerRelPath of meta.providers) {\n// try {\n// const providerPath = path.join(basePath, item.lowerName, providerRelPath);\n// const providerModule = await import(providerPath);\n// if (typeof providerModule.default === 'function') {\n// await providerModule.default(); // Call provider\n// console.log(`[${type}] Provider loaded: ${providerRelPath}`);\n// } else {\n// console.warn(`[${type}] Provider ${providerRelPath} has no default export.`);\n// }\n// } catch (err) {\n// console.error(`[${type}] Failed to load provider ${providerRelPath}:`, err);\n// }\n// }\n// }\n// }\n// }\n","// src/hooks/useServices.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\n\nexport interface ServiceMeta {\n name: string;\n lowerName: string;\n version?: string;\n author?: string;\n icon?: string;\n description?: string;\n enabled: boolean;\n providers?: string[];\n serviceType?: string;\n}\n\nexport const useServices = {\n loadServices(): ServiceMeta[] {\n const servicesDir = useSettings.getPaths().services;\n if (!fs.existsSync(servicesDir)) return [];\n\n const serviceFolders = fs.readdirSync(servicesDir);\n const services: ServiceMeta[] = [];\n\n serviceFolders.forEach((folder) => {\n const servicePath = path.join(servicesDir, folder);\n const serviceJsonPath = path.join(servicePath, 'service.json');\n if (!fs.existsSync(serviceJsonPath)) return;\n\n try {\n const rawData = fs.readFileSync(serviceJsonPath, 'utf-8');\n const serviceData = JSON.parse(rawData) as ServiceMeta;\n services.push(serviceData);\n } catch {\n // ignore invalid json or errors\n }\n });\n\n return services;\n },\n\n isServiceEnabled(serviceName: string): boolean {\n const services = useServices.loadServices();\n const service = services.find((s) => s.lowerName === serviceName.toLowerCase());\n return service ? service.enabled : false;\n },\n\n toggleService(serviceName: string, enabled: boolean): boolean {\n const servicesDir = useSettings.getPaths().services;\n const services = useServices.loadServices();\n const index = services.findIndex((s) => s.lowerName === serviceName.toLowerCase());\n if (index === -1) return false;\n\n const serviceMeta = services[index];\n serviceMeta.enabled = enabled;\n\n const serviceJsonPath = path.join(servicesDir, serviceMeta.lowerName, 'service.json');\n try {\n fs.writeFileSync(serviceJsonPath, JSON.stringify(serviceMeta, null, 2), 'utf-8');\n return true;\n } catch {\n return false;\n }\n },\n\n getServices(): ServiceMeta[] {\n return useServices.loadServices();\n },\n};\n","import { useModules } from './useModules';\nimport { useServices } from './useServices';\n\nexport type NavItemType = 'link' | 'parent';\n\nexport interface NavItem {\n id: string;\n label: string;\n type: NavItemType;\n path?: string;\n icon?: string;\n children?: NavItem[];\n order?: number;\n category?: string;\n module?: string;\n service?: string;\n}\n\nconst navItems: NavItem[] = [];\n\nfunction resolveCategory(item: NavItem, parent?: NavItem): string {\n if (item.category) return item.category;\n if (parent?.category) return parent.category;\n if (item.module || parent?.module) return 'Modules';\n if (item.service || parent?.service) return 'Services';\n return 'Core';\n}\n\nfunction normalizeItem(item: NavItem, parent?: NavItem): NavItem {\n const normalized: NavItem = {\n ...item,\n category: resolveCategory(item, parent),\n module: item.module ?? parent?.module,\n service: item.service ?? parent?.service,\n };\n\n if (item.children && item.children.length > 0) {\n normalized.children = item.children.map((child) => normalizeItem(child, normalized));\n }\n\n return normalized;\n}\n\nfunction isItemActive(item: NavItem): boolean {\n if (item.module) {\n try {\n return useModules.isEnabled(item.module);\n } catch {\n return false;\n }\n }\n if (item.service) {\n try {\n return useServices.isServiceEnabled(item.service);\n } catch {\n return false;\n }\n }\n return true;\n}\n\nfunction filterActive(items: NavItem[]): NavItem[] {\n return items\n .filter((item) => isItemActive(item))\n .map((item) => {\n if (item.type === 'parent' && item.children) {\n return {\n ...item,\n children: filterActive(item.children),\n };\n }\n return item;\n })\n .filter((item) => (item.type === 'parent' ? (item.children?.length ?? 0) > 0 : true));\n}\n\nexport const navitem = {\n create(item: NavItem): void {\n navItems.push(normalizeItem(item));\n },\n};\n\nexport const useNav = {\n register(item: NavItem): void {\n navItems.push(normalizeItem(item));\n },\n getAll(): NavItem[] {\n return navItems;\n },\n getActive(): NavItem[] {\n return filterActive(navItems);\n },\n clear(): void {\n navItems.length = 0;\n },\n};\n"],"mappings":"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","names":["fs","path","fs","fs","fs","path","fs","path","fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/hooks/useModules.ts","../../../src/core/hooks/useSettings.ts","../../../src/core/hooks/useExtensibles.ts","../../../src/core/hooks/useServices.ts","../../../src/core/hooks/useNav.ts"],"sourcesContent":["// src/hooks/useModules.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport { ModuleMeta } from '../types'; // Adjust the import path as necessary\n\nexport const useModules = {\n allEnabled(): ModuleMeta[] {\n const modules = useModules.load();\n if (!modules || modules.length === 0) return [];\n // Filter out modules that are not enabled\n \n // Return only the enabled modules\n // This assumes that each module has an 'enabled' property\n // Adjust the property name if necessary\n if (!Array.isArray(modules)) return [];\n if (modules.length === 0) return [];\n if (!modules.every((mod) => mod && typeof mod.enabled === 'boolean')) {\n throw new Error('Invalid module format: each module must have an \"enabled\" boolean property');\n }\n if (modules.some((mod) => !mod.lowerName)) {\n throw new Error('Invalid module format: each module must have a \"lowerName\" property');\n }\n\n // Filter and return only enabled modules\n return modules.filter((mod) => mod.enabled);\n },\n load(): ModuleMeta[] {\n const modulesDir = useSettings.getPaths().modules;\n if (!fs.existsSync(modulesDir)) return [];\n\n const moduleFolders = fs.readdirSync(modulesDir);\n const modules: ModuleMeta[] = [];\n\n moduleFolders.forEach((folder) => {\n const modulePath = path.join(modulesDir, folder);\n const moduleJsonPath = path.join(modulePath, 'module.json');\n if (!fs.existsSync(moduleJsonPath)) return;\n\n try {\n const rawData = fs.readFileSync(moduleJsonPath, 'utf-8');\n const moduleData = JSON.parse(rawData) as ModuleMeta;\n modules.push(moduleData);\n } catch {\n // invalid json or read error, ignore\n }\n });\n\n return modules;\n },\n\n isEnabled(moduleName: string): boolean {\n const modules = useModules.load();\n const mod = modules.find((m) => m.lowerName === moduleName.toLowerCase());\n return mod ? mod.enabled : false;\n },\n\n toggle(moduleName: string): boolean {\n const modulesDir = useSettings.getPaths().modules;\n const modules = useModules.load();\n const modIndex = modules.findIndex((m) => m.lowerName === moduleName.toLowerCase());\n if (modIndex === -1) return false;\n\n const moduleMeta = modules[modIndex];\n moduleMeta.enabled = !moduleMeta.enabled;\n\n const moduleJsonPath = path.join(modulesDir, moduleMeta.lowerName, 'module.json');\n try {\n fs.writeFileSync(moduleJsonPath, JSON.stringify(moduleMeta, null, 2), 'utf-8');\n return true;\n } catch {\n return false;\n }\n },\n\n getModules(): ModuleMeta[] {\n return useModules.load();\n },\n};\n","// src/hooks/useSettings.ts\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport { loadJSON, saveJSON } from './useExtensibles';\nimport type { ExtensiblePaths } from '../types'; // Adjust the import path as necessary\n\nconst defaultPaths: ExtensiblePaths = {\n modules: './app/modules',\n templates: './app/resources/themes',\n services: './app/services',\n events: './app/events',\n langs: './public/langs',\n providers: './app/providers',\n};\n\nlet extensiblePaths: ExtensiblePaths = { ...defaultPaths };\nconst isServer = typeof window === 'undefined';\n\n\nexport const useSettings = {\n getPaths(): ExtensiblePaths {\n if (isServer) {\n const json = loadJSON('./app/extensiblePaths.json');\n if (json) extensiblePaths = json;\n }\n\n if (isServer) {\n for (const key of Object.keys(defaultPaths) as Array<keyof ExtensiblePaths>) {\n const candidate = extensiblePaths[key];\n if (!candidate || !fs.existsSync(candidate)) {\n extensiblePaths[key] = defaultPaths[key];\n }\n }\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n langs: './langs',\n };\n // extensiblePaths = loadJSON(\"./app/extensiblePaths.json\");\n return extensiblePaths;\n },\n\n setPaths(paths: Partial<ExtensiblePaths>): void {\n if (isServer) {\n saveJSON('./app/extensiblePaths.json', {\n ...extensiblePaths,\n ...paths,\n });\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n ...paths,\n };\n },\n \n getKeyValue(key: string): string {\n return 'Coming Soon'\n },\n setKeyValue(key: string, value: string): void {\n return;\n },\n};\n","// src/hooks/useExtensibles.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport type { TemplateType, ExtensibleMeta, ExtensiblePaths } from '../types';\n\nlet paths: ExtensiblePaths;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport function registerExtensibles(extensiblePaths: ExtensiblePaths) {\n paths = extensiblePaths;\n}\n\nexport function loadJSON(filePath: string): any {\n if (!fs.existsSync(filePath)) return null;\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function saveJSON(filePath: string, data: any) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// --- MODULES ---\n\nexport function getModules(): ExtensibleMeta[] {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().modules)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().modules, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().modules, dir, 'module.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isModuleEnabled(name: string) {\n const modules = getModules();\n return modules.find((m) => m.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleModule(name: string, enabled: boolean) {\n const modules = getModules();\n const mod = modules.find((m) => m.lowerName === name.toLowerCase());\n if (!mod) throw new Error(`Module ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().modules, mod.lowerName, 'module.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onModuleEnabled(mod.name);\n else onModuleDisabled(mod.name);\n}\n\n// --- SERVICES ---\n\nexport function getServices(): ExtensibleMeta[] {\n if (!useSettings.getPaths().services) throw new Error('Services path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().services)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().services, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().services, dir, 'service.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isServiceEnabled(name: string) {\n const services = getServices();\n return services.find((s) => s.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleService(name: string, enabled: boolean) {\n const services = getServices();\n const svc = services.find((s) => s.lowerName === name.toLowerCase());\n if (!svc) throw new Error(`Service ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().services, svc.lowerName, 'service.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onServiceEnabled(svc.name);\n else onServiceDisabled(svc.name);\n}\n\n// --- TEMPLATES ---\n\n// Helper to get base path for a template type\nfunction getTemplateBasePath(type: TemplateType): string {\n if (!useSettings.getPaths().templates) throw new Error('Templates path not registered.');\n switch (type) {\n case 'admin-theme':\n case 'client-theme':\n return path.join(useSettings.getPaths().templates, 'themes');\n case 'portal':\n return path.join(useSettings.getPaths().templates, 'portals');\n case 'email':\n return path.join(useSettings.getPaths().templates, 'emails');\n default:\n throw new Error(`Unknown template type: ${type}`);\n }\n}\n\nexport function getTemplates(type: TemplateType): ExtensibleMeta[] {\n const basePath = getTemplateBasePath(type);\n if (!fs.existsSync(basePath)) return [];\n\n const dirs = fs\n .readdirSync(basePath)\n .filter((d: any) => fs.statSync(path.join(basePath, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(basePath, dir, 'template.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isTemplateActive(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n return templates.find((t) => t.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\n// Toggle templates: only 1 active per type allowed\nexport function toggleTemplate(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n\n templates.forEach((tpl) => {\n const metaPath = path.join(getTemplateBasePath(type), tpl.lowerName, 'template.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = tpl.lowerName === name.toLowerCase();\n saveJSON(metaPath, meta);\n });\n\n onTemplateSelect(type, name);\n}\n\n// --- EVENTS (replace with your event system integration) ---\n\nexport function onModuleEnabled(name: string) {\n console.log(`Module enabled: ${name}`);\n // emit event or custom logic here\n}\n\nexport function onModuleDisabled(name: string) {\n console.log(`Module disabled: ${name}`);\n}\n\nexport function onServiceEnabled(name: string) {\n console.log(`Service enabled: ${name}`);\n}\n\nexport function onServiceDisabled(name: string) {\n console.log(`Service disabled: ${name}`);\n}\n\nexport function onTemplateSelect(type: TemplateType, name: string) {\n console.log(`Template selected: type=${type}, name=${name}`);\n}\n\n// --- REGISTER HOOKS FOR MODULES ---\n\nexport async function registerEvents() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const eventsFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'events.server.js');\n if (fs.existsSync(eventsFile)) {\n const modEvents = await import(eventsFile);\n if (modEvents?.registerEvents) await modEvents.registerEvents();\n }\n }\n}\n\nexport async function registerMiddleware() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const middlewareFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'middleware.server.js');\n if (fs.existsSync(middlewareFile)) {\n const modMiddleware = await import(middlewareFile);\n if (modMiddleware?.registerMiddleware) await modMiddleware.registerMiddleware();\n }\n }\n}\n\n/**\n * Registers routes given a path to routes folder (for pages and API routes)\n * @param routesFolderPath string - path to module/service routes folder\n */\nexport async function registerRoutes(routesFolderPath: string) {\n if (!fs.existsSync(routesFolderPath)) return;\n\n const routeFiles = fs.readdirSync(routesFolderPath).filter((f: any) => /\\.(js|ts|jsx|tsx)$/.test(f));\n for (const file of routeFiles) {\n const routeModule = await import(path.join(routesFolderPath, file));\n if (routeModule?.registerRoute) {\n await routeModule.registerRoute();\n }\n }\n}\n\nexport async function loadProviders() {\n const paths = useSettings.getPaths();\n\n const all = [\n { type: 'module', list: getModules(), basePath: paths.modules },\n { type: 'service', list: getServices(), basePath: paths.services },\n ];\n\n for (const { type, list, basePath } of all) {\n for (const item of list) {\n if (!item.enabled) continue;\n\n const metaPath = path.join(path.resolve(basePath), item.lowerName, `${type}.json`);\n const meta = loadJSON(metaPath);\n if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n for (const providerRelPath of meta.providers) {\n try {\n let providerAbsPath = path.join(path.resolve(basePath), item.lowerName, providerRelPath);\n\n // Try .js fallback if file is .ts or .tsx\n const ext = path.extname(providerAbsPath);\n if (ext === '.ts' || ext === '.tsx') {\n const jsPath = providerAbsPath.replace(/\\.(ts|tsx)$/, '.js');\n try {\n await fs.accessSync(jsPath); // Check if compiled JS file exists\n providerAbsPath = jsPath;\n } catch {\n throw new Error(`Compiled JS version not found for provider: ${providerRelPath}`);\n }\n }\n\n const providerUrl = toFileUrl(providerAbsPath);\n const providerModule = await import(providerUrl);\n\n if (typeof providerModule.default === 'function') {\n await providerModule.default();\n console.log(`[${type}] ✅ Provider loaded: ${providerRelPath}`);\n } else {\n console.warn(`[${type}] ⚠️ Provider ${providerRelPath} has no default export.`);\n }\n } catch (err) {\n console.error(`[${type}] ❌ Failed to load provider ${providerRelPath}:`, err);\n }\n }\n }\n }\n}\n\n\n// export async function loadProviders() {\n// paths = {\n// modules: useSettings.getPaths().modules,\n// templates: useSettings.getPaths().templates,\n// services: useSettings.getPaths().services,\n// events: useSettings.getPaths().events,\n// }\n// const all = [\n// { type: 'module', list: getModules(), basePath: useSettings.getPaths().modules },\n// { type: 'service', list: getServices(), basePath: useSettings.getPaths().services },\n// ];\n\n// for (const { type, list, basePath } of all) {\n// for (const item of list) {\n// if (!item.enabled) continue;\n\n// const metaPath = path.join(basePath, item.lowerName, `${type}.json`);\n// const meta = loadJSON(metaPath);\n// if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n// for (const providerRelPath of meta.providers) {\n// try {\n// const providerPath = path.join(basePath, item.lowerName, providerRelPath);\n// const providerModule = await import(providerPath);\n// if (typeof providerModule.default === 'function') {\n// await providerModule.default(); // Call provider\n// console.log(`[${type}] Provider loaded: ${providerRelPath}`);\n// } else {\n// console.warn(`[${type}] Provider ${providerRelPath} has no default export.`);\n// }\n// } catch (err) {\n// console.error(`[${type}] Failed to load provider ${providerRelPath}:`, err);\n// }\n// }\n// }\n// }\n// }\n","// src/hooks/useServices.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\n\nexport interface ServiceMeta {\n name: string;\n lowerName: string;\n version?: string;\n author?: string;\n icon?: string;\n description?: string;\n enabled: boolean;\n providers?: string[];\n serviceType?: string;\n}\n\nexport const useServices = {\n loadServices(): ServiceMeta[] {\n const servicesDir = useSettings.getPaths().services;\n if (!fs.existsSync(servicesDir)) return [];\n\n const serviceFolders = fs.readdirSync(servicesDir);\n const services: ServiceMeta[] = [];\n\n serviceFolders.forEach((folder) => {\n const servicePath = path.join(servicesDir, folder);\n const serviceJsonPath = path.join(servicePath, 'service.json');\n if (!fs.existsSync(serviceJsonPath)) return;\n\n try {\n const rawData = fs.readFileSync(serviceJsonPath, 'utf-8');\n const serviceData = JSON.parse(rawData) as ServiceMeta;\n services.push(serviceData);\n } catch {\n // ignore invalid json or errors\n }\n });\n\n return services;\n },\n\n isServiceEnabled(serviceName: string): boolean {\n const services = useServices.loadServices();\n const service = services.find((s) => s.lowerName === serviceName.toLowerCase());\n return service ? service.enabled : false;\n },\n\n toggleService(serviceName: string, enabled: boolean): boolean {\n const servicesDir = useSettings.getPaths().services;\n const services = useServices.loadServices();\n const index = services.findIndex((s) => s.lowerName === serviceName.toLowerCase());\n if (index === -1) return false;\n\n const serviceMeta = services[index];\n serviceMeta.enabled = enabled;\n\n const serviceJsonPath = path.join(servicesDir, serviceMeta.lowerName, 'service.json');\n try {\n fs.writeFileSync(serviceJsonPath, JSON.stringify(serviceMeta, null, 2), 'utf-8');\n return true;\n } catch {\n return false;\n }\n },\n\n getServices(): ServiceMeta[] {\n return useServices.loadServices();\n },\n};\n","import { useModules } from './useModules';\nimport { useServices } from './useServices';\n\nexport type NavItemType = 'link' | 'parent';\n\nexport interface NavItem {\n id: string;\n label: string;\n type: NavItemType;\n path?: string;\n icon?: string;\n children?: NavItem[];\n order?: number;\n category?: string;\n module?: string;\n service?: string;\n}\n\nconst GLOBAL_KEY = '__retrex_navitems__';\nconst globalStore = globalThis as unknown as Record<string, NavItem[] | undefined>;\nconst navItems: NavItem[] = globalStore[GLOBAL_KEY] ?? [];\nif (!globalStore[GLOBAL_KEY]) {\n globalStore[GLOBAL_KEY] = navItems;\n}\n\nfunction resolveCategory(item: NavItem, parent?: NavItem): string {\n if (item.category) return item.category;\n if (parent?.category) return parent.category;\n if (item.module || parent?.module) return 'Modules';\n if (item.service || parent?.service) return 'Services';\n return 'Core';\n}\n\nfunction normalizeItem(item: NavItem, parent?: NavItem): NavItem {\n const normalized: NavItem = {\n ...item,\n category: resolveCategory(item, parent),\n module: item.module ?? parent?.module,\n service: item.service ?? parent?.service,\n };\n\n if (item.children && item.children.length > 0) {\n normalized.children = item.children.map((child) => normalizeItem(child, normalized));\n }\n\n return normalized;\n}\n\nfunction isItemActive(item: NavItem): boolean {\n if (item.module) {\n try {\n return useModules.isEnabled(item.module);\n } catch {\n return false;\n }\n }\n if (item.service) {\n try {\n return useServices.isServiceEnabled(item.service);\n } catch {\n return false;\n }\n }\n return true;\n}\n\nfunction filterActive(items: NavItem[]): NavItem[] {\n return items\n .filter((item) => isItemActive(item))\n .map((item) => {\n if (item.type === 'parent' && item.children) {\n return {\n ...item,\n children: filterActive(item.children),\n };\n }\n return item;\n })\n .filter((item) => (item.type === 'parent' ? (item.children?.length ?? 0) > 0 : true));\n}\n\nexport const navitem = {\n create(item: NavItem): void {\n navItems.push(normalizeItem(item));\n },\n};\n\nexport const useNav = {\n register(item: NavItem): void {\n navItems.push(normalizeItem(item));\n },\n getAll(): NavItem[] {\n return navItems;\n },\n getActive(): NavItem[] {\n return filterActive(navItems);\n },\n clear(): void {\n navItems.length = 0;\n },\n};\n"],"mappings":"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","names":["fs","path","fs","fs","fs","path","fs","path","fs","path"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/hooks/useUtils.ts","../../../src/core/hooks/useSettings.ts","../../../src/core/hooks/useExtensibles.ts"],"sourcesContent":["// packages/retrex-extensibles-core/hooks/useUtils.ts\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { useSettings } from './useSettings';\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport const useUtils = {\n async loadThemeComponent(\n componentName: string,\n options: {\n theme: {\n name: string;\n type: 'admin' | 'client' | 'portal' | 'email';\n };\n search?: {\n themes?: boolean;\n modules?: boolean;\n services?: boolean;\n };\n }\n ): Promise<React.ComponentType<any> | null> {\n const { search = {}, theme } = options;\n const searchThemes = search.themes ?? true;\n const searchModules = search.modules ?? true;\n const searchServices = search.services ?? true;\n\n const possiblePaths: string[] = [];\n\n console.log(`Searching for component \"${componentName}\" in theme \"${theme.name}\" of type \"${theme.type}\"...`);\n\n try {\n if (searchThemes) {\n const templatesRoot = path.resolve(useSettings.getPaths().templates);\n possiblePaths.push(\n path.resolve(\n templatesRoot,\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n \n if (searchModules) {\n const modulesRoot = path.resolve(useSettings.getPaths().modules);\n if (fs.existsSync(modulesRoot)) {\n const moduleDirs = await fs.readdirSync(modulesRoot);\n for (const mod of moduleDirs) {\n possiblePaths.push(\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n }\n }\n \n if (searchServices) {\n const servicesRoot = path.resolve(useSettings.getPaths().services);\n if (fs.existsSync(servicesRoot)) {\n const serviceDirs = await fs.readdirSync(servicesRoot);\n for (const svc of serviceDirs) {\n possiblePaths.push(\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n }\n }\n \n for (const filePath of possiblePaths) {\n if (await fs.existsSync(filePath)) {\n try {\n const mod = await import(toFileUrl(filePath));\n if (mod.default) return mod.default;\n } catch (err) {\n console.error(`Error loading component at ${filePath}:`, err);\n }\n }\n }\n \n console.warn(`Component \"${componentName}\" not found in theme \"${theme.name}\" of type \"${theme.type}\".`);\n return null;\n } catch (err) {\n console.error(`Error searching for component \"${componentName}\":`, err);\n console.warn(`Component \"${componentName}\" not found in theme \"${theme.name}\" of type \"${theme.type}\".`);\n console.warn(`Searched paths:`, possiblePaths);\n console.warn(`Ensure the component exists in the specified theme and type.`);\n console.warn(`If the component is in a module or service, ensure the theme structure is correct.`);\n console.warn(`If you are using a custom theme, module, or service, ensure it is properly configured and registered in the settings.`);\n return null;\n }\n }\n}\n","// src/hooks/useSettings.ts\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport { loadJSON, saveJSON } from './useExtensibles';\nimport type { ExtensiblePaths } from '../types'; // Adjust the import path as necessary\n\nconst defaultPaths: ExtensiblePaths = {\n modules: './app/modules',\n templates: './app/resources/themes',\n services: './app/services',\n events: './app/events',\n langs: './public/langs',\n providers: './app/providers',\n};\n\nlet extensiblePaths: ExtensiblePaths = { ...defaultPaths };\nconst isServer = typeof window === 'undefined';\n\n\nexport const useSettings = {\n getPaths(): ExtensiblePaths {\n if (isServer) {\n const json = loadJSON('./app/extensiblePaths.json');\n if (json) extensiblePaths = json;\n }\n\n if (isServer) {\n for (const key of Object.keys(defaultPaths) as Array<keyof ExtensiblePaths>) {\n const candidate = extensiblePaths[key];\n if (!candidate || !fs.existsSync(candidate)) {\n extensiblePaths[key] = defaultPaths[key];\n }\n }\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n langs: './langs',\n };\n // extensiblePaths = loadJSON(\"./app/extensiblePaths.json\");\n return extensiblePaths;\n },\n\n setPaths(paths: Partial<ExtensiblePaths>): void {\n if (isServer) {\n saveJSON('./app/extensiblePaths.json', {\n ...extensiblePaths,\n ...paths,\n });\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n ...paths,\n };\n },\n \n getKeyValue(key: string): string {\n return 'Coming Soon'\n },\n setKeyValue(key: string, value: string): void {\n return;\n },\n};\n","// src/hooks/useExtensibles.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport type { TemplateType, ExtensibleMeta, ExtensiblePaths } from '../types';\n\nlet paths: ExtensiblePaths;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport function registerExtensibles(extensiblePaths: ExtensiblePaths) {\n paths = extensiblePaths;\n}\n\nexport function loadJSON(filePath: string): any {\n if (!fs.existsSync(filePath)) return null;\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function saveJSON(filePath: string, data: any) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// --- MODULES ---\n\nexport function getModules(): ExtensibleMeta[] {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().modules)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().modules, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().modules, dir, 'module.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isModuleEnabled(name: string) {\n const modules = getModules();\n return modules.find((m) => m.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleModule(name: string, enabled: boolean) {\n const modules = getModules();\n const mod = modules.find((m) => m.lowerName === name.toLowerCase());\n if (!mod) throw new Error(`Module ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().modules, mod.lowerName, 'module.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onModuleEnabled(mod.name);\n else onModuleDisabled(mod.name);\n}\n\n// --- SERVICES ---\n\nexport function getServices(): ExtensibleMeta[] {\n if (!useSettings.getPaths().services) throw new Error('Services path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().services)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().services, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().services, dir, 'service.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isServiceEnabled(name: string) {\n const services = getServices();\n return services.find((s) => s.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleService(name: string, enabled: boolean) {\n const services = getServices();\n const svc = services.find((s) => s.lowerName === name.toLowerCase());\n if (!svc) throw new Error(`Service ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().services, svc.lowerName, 'service.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onServiceEnabled(svc.name);\n else onServiceDisabled(svc.name);\n}\n\n// --- TEMPLATES ---\n\n// Helper to get base path for a template type\nfunction getTemplateBasePath(type: TemplateType): string {\n if (!useSettings.getPaths().templates) throw new Error('Templates path not registered.');\n switch (type) {\n case 'admin-theme':\n case 'client-theme':\n return path.join(useSettings.getPaths().templates, 'themes');\n case 'portal':\n return path.join(useSettings.getPaths().templates, 'portals');\n case 'email':\n return path.join(useSettings.getPaths().templates, 'emails');\n default:\n throw new Error(`Unknown template type: ${type}`);\n }\n}\n\nexport function getTemplates(type: TemplateType): ExtensibleMeta[] {\n const basePath = getTemplateBasePath(type);\n if (!fs.existsSync(basePath)) return [];\n\n const dirs = fs\n .readdirSync(basePath)\n .filter((d: any) => fs.statSync(path.join(basePath, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(basePath, dir, 'template.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isTemplateActive(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n return templates.find((t) => t.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\n// Toggle templates: only 1 active per type allowed\nexport function toggleTemplate(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n\n templates.forEach((tpl) => {\n const metaPath = path.join(getTemplateBasePath(type), tpl.lowerName, 'template.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = tpl.lowerName === name.toLowerCase();\n saveJSON(metaPath, meta);\n });\n\n onTemplateSelect(type, name);\n}\n\n// --- EVENTS (replace with your event system integration) ---\n\nexport function onModuleEnabled(name: string) {\n console.log(`Module enabled: ${name}`);\n // emit event or custom logic here\n}\n\nexport function onModuleDisabled(name: string) {\n console.log(`Module disabled: ${name}`);\n}\n\nexport function onServiceEnabled(name: string) {\n console.log(`Service enabled: ${name}`);\n}\n\nexport function onServiceDisabled(name: string) {\n console.log(`Service disabled: ${name}`);\n}\n\nexport function onTemplateSelect(type: TemplateType, name: string) {\n console.log(`Template selected: type=${type}, name=${name}`);\n}\n\n// --- REGISTER HOOKS FOR MODULES ---\n\nexport async function registerEvents() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const eventsFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'events.server.js');\n if (fs.existsSync(eventsFile)) {\n const modEvents = await import(eventsFile);\n if (modEvents?.registerEvents) await modEvents.registerEvents();\n }\n }\n}\n\nexport async function registerMiddleware() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const middlewareFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'middleware.server.js');\n if (fs.existsSync(middlewareFile)) {\n const modMiddleware = await import(middlewareFile);\n if (modMiddleware?.registerMiddleware) await modMiddleware.registerMiddleware();\n }\n }\n}\n\n/**\n * Registers routes given a path to routes folder (for pages and API routes)\n * @param routesFolderPath string - path to module/service routes folder\n */\nexport async function registerRoutes(routesFolderPath: string) {\n if (!fs.existsSync(routesFolderPath)) return;\n\n const routeFiles = fs.readdirSync(routesFolderPath).filter((f: any) => /\\.(js|ts|jsx|tsx)$/.test(f));\n for (const file of routeFiles) {\n const routeModule = await import(path.join(routesFolderPath, file));\n if (routeModule?.registerRoute) {\n await routeModule.registerRoute();\n }\n }\n}\n\nexport async function loadProviders() {\n const paths = useSettings.getPaths();\n\n const all = [\n { type: 'module', list: getModules(), basePath: paths.modules },\n { type: 'service', list: getServices(), basePath: paths.services },\n ];\n\n for (const { type, list, basePath } of all) {\n for (const item of list) {\n if (!item.enabled) continue;\n\n const metaPath = path.join(path.resolve(basePath), item.lowerName, `${type}.json`);\n const meta = loadJSON(metaPath);\n if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n for (const providerRelPath of meta.providers) {\n try {\n let providerAbsPath = path.join(path.resolve(basePath), item.lowerName, providerRelPath);\n\n // Try .js fallback if file is .ts or .tsx\n const ext = path.extname(providerAbsPath);\n if (ext === '.ts' || ext === '.tsx') {\n const jsPath = providerAbsPath.replace(/\\.(ts|tsx)$/, '.js');\n try {\n await fs.accessSync(jsPath); // Check if compiled JS file exists\n providerAbsPath = jsPath;\n } catch {\n throw new Error(`Compiled JS version not found for provider: ${providerRelPath}`);\n }\n }\n\n const providerUrl = toFileUrl(providerAbsPath);\n const providerModule = await import(providerUrl);\n\n if (typeof providerModule.default === 'function') {\n await providerModule.default();\n console.log(`[${type}] ✅ Provider loaded: ${providerRelPath}`);\n } else {\n console.warn(`[${type}] ⚠️ Provider ${providerRelPath} has no default export.`);\n }\n } catch (err) {\n console.error(`[${type}] ❌ Failed to load provider ${providerRelPath}:`, err);\n }\n }\n }\n }\n}\n\n\n// export async function loadProviders() {\n// paths = {\n// modules: useSettings.getPaths().modules,\n// templates: useSettings.getPaths().templates,\n// services: useSettings.getPaths().services,\n// events: useSettings.getPaths().events,\n// }\n// const all = [\n// { type: 'module', list: getModules(), basePath: useSettings.getPaths().modules },\n// { type: 'service', list: getServices(), basePath: useSettings.getPaths().services },\n// ];\n\n// for (const { type, list, basePath } of all) {\n// for (const item of list) {\n// if (!item.enabled) continue;\n\n// const metaPath = path.join(basePath, item.lowerName, `${type}.json`);\n// const meta = loadJSON(metaPath);\n// if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n// for (const providerRelPath of meta.providers) {\n// try {\n// const providerPath = path.join(basePath, item.lowerName, providerRelPath);\n// const providerModule = await import(providerPath);\n// if (typeof providerModule.default === 'function') {\n// await providerModule.default(); // Call provider\n// console.log(`[${type}] Provider loaded: ${providerRelPath}`);\n// } else {\n// console.warn(`[${type}] Provider ${providerRelPath} has no default export.`);\n// }\n// } catch (err) {\n// console.error(`[${type}] Failed to load provider ${providerRelPath}:`, err);\n// }\n// }\n// }\n// }\n// }\n"],"mappings":"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","names":["import_node_path","import_node_fs","import_node_fs","fs","fs","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/hooks/useUtils.ts","../../../src/core/hooks/useSettings.ts","../../../src/core/hooks/useExtensibles.ts"],"sourcesContent":["/* @vite-ignore */\n// packages/retrex-extensibles-core/hooks/useUtils.ts\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { useSettings } from './useSettings';\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport const useUtils = {\n async loadThemeComponent(\n componentName: string,\n options: {\n theme: {\n name: string;\n type: 'admin' | 'client' | 'portal' | 'email';\n };\n search?: {\n themes?: boolean;\n modules?: boolean;\n services?: boolean;\n };\n }\n ): Promise<React.ComponentType<any> | null> {\n const { search = {}, theme } = options;\n const searchThemes = search.themes ?? true;\n const searchModules = search.modules ?? true;\n const searchServices = search.services ?? true;\n\n const possiblePaths: string[] = [];\n\n console.log(`Searching for component \"${componentName}\" in theme \"${theme.name}\" of type \"${theme.type}\"...`);\n\n try {\n if (searchThemes) {\n const templatesRoot = path.resolve(useSettings.getPaths().templates);\n possiblePaths.push(\n path.resolve(\n templatesRoot,\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n \n if (searchModules) {\n const modulesRoot = path.resolve(useSettings.getPaths().modules);\n if (fs.existsSync(modulesRoot)) {\n const moduleDirs = await fs.readdirSync(modulesRoot);\n for (const mod of moduleDirs) {\n possiblePaths.push(\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n }\n }\n \n if (searchServices) {\n const servicesRoot = path.resolve(useSettings.getPaths().services);\n if (fs.existsSync(servicesRoot)) {\n const serviceDirs = await fs.readdirSync(servicesRoot);\n for (const svc of serviceDirs) {\n possiblePaths.push(\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n }\n }\n \n for (const filePath of possiblePaths) {\n if (await fs.existsSync(filePath)) {\n try {\n const mod = await import(toFileUrl(filePath));\n if (mod.default) return mod.default;\n } catch (err) {\n console.error(`Error loading component at ${filePath}:`, err);\n }\n }\n }\n \n console.warn(`Component \"${componentName}\" not found in theme \"${theme.name}\" of type \"${theme.type}\".`);\n return null;\n } catch (err) {\n console.error(`Error searching for component \"${componentName}\":`, err);\n console.warn(`Component \"${componentName}\" not found in theme \"${theme.name}\" of type \"${theme.type}\".`);\n console.warn(`Searched paths:`, possiblePaths);\n console.warn(`Ensure the component exists in the specified theme and type.`);\n console.warn(`If the component is in a module or service, ensure the theme structure is correct.`);\n console.warn(`If you are using a custom theme, module, or service, ensure it is properly configured and registered in the settings.`);\n return null;\n }\n }\n}\n","// src/hooks/useSettings.ts\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport { loadJSON, saveJSON } from './useExtensibles';\nimport type { ExtensiblePaths } from '../types'; // Adjust the import path as necessary\n\nconst defaultPaths: ExtensiblePaths = {\n modules: './app/modules',\n templates: './app/resources/themes',\n services: './app/services',\n events: './app/events',\n langs: './public/langs',\n providers: './app/providers',\n};\n\nlet extensiblePaths: ExtensiblePaths = { ...defaultPaths };\nconst isServer = typeof window === 'undefined';\n\n\nexport const useSettings = {\n getPaths(): ExtensiblePaths {\n if (isServer) {\n const json = loadJSON('./app/extensiblePaths.json');\n if (json) extensiblePaths = json;\n }\n\n if (isServer) {\n for (const key of Object.keys(defaultPaths) as Array<keyof ExtensiblePaths>) {\n const candidate = extensiblePaths[key];\n if (!candidate || !fs.existsSync(candidate)) {\n extensiblePaths[key] = defaultPaths[key];\n }\n }\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n langs: './langs',\n };\n // extensiblePaths = loadJSON(\"./app/extensiblePaths.json\");\n return extensiblePaths;\n },\n\n setPaths(paths: Partial<ExtensiblePaths>): void {\n if (isServer) {\n saveJSON('./app/extensiblePaths.json', {\n ...extensiblePaths,\n ...paths,\n });\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n ...paths,\n };\n },\n \n getKeyValue(key: string): string {\n return 'Coming Soon'\n },\n setKeyValue(key: string, value: string): void {\n return;\n },\n};\n","// src/hooks/useExtensibles.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport type { TemplateType, ExtensibleMeta, ExtensiblePaths } from '../types';\n\nlet paths: ExtensiblePaths;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport function registerExtensibles(extensiblePaths: ExtensiblePaths) {\n paths = extensiblePaths;\n}\n\nexport function loadJSON(filePath: string): any {\n if (!fs.existsSync(filePath)) return null;\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function saveJSON(filePath: string, data: any) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// --- MODULES ---\n\nexport function getModules(): ExtensibleMeta[] {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().modules)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().modules, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().modules, dir, 'module.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isModuleEnabled(name: string) {\n const modules = getModules();\n return modules.find((m) => m.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleModule(name: string, enabled: boolean) {\n const modules = getModules();\n const mod = modules.find((m) => m.lowerName === name.toLowerCase());\n if (!mod) throw new Error(`Module ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().modules, mod.lowerName, 'module.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onModuleEnabled(mod.name);\n else onModuleDisabled(mod.name);\n}\n\n// --- SERVICES ---\n\nexport function getServices(): ExtensibleMeta[] {\n if (!useSettings.getPaths().services) throw new Error('Services path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().services)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().services, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().services, dir, 'service.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isServiceEnabled(name: string) {\n const services = getServices();\n return services.find((s) => s.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleService(name: string, enabled: boolean) {\n const services = getServices();\n const svc = services.find((s) => s.lowerName === name.toLowerCase());\n if (!svc) throw new Error(`Service ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().services, svc.lowerName, 'service.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onServiceEnabled(svc.name);\n else onServiceDisabled(svc.name);\n}\n\n// --- TEMPLATES ---\n\n// Helper to get base path for a template type\nfunction getTemplateBasePath(type: TemplateType): string {\n if (!useSettings.getPaths().templates) throw new Error('Templates path not registered.');\n switch (type) {\n case 'admin-theme':\n case 'client-theme':\n return path.join(useSettings.getPaths().templates, 'themes');\n case 'portal':\n return path.join(useSettings.getPaths().templates, 'portals');\n case 'email':\n return path.join(useSettings.getPaths().templates, 'emails');\n default:\n throw new Error(`Unknown template type: ${type}`);\n }\n}\n\nexport function getTemplates(type: TemplateType): ExtensibleMeta[] {\n const basePath = getTemplateBasePath(type);\n if (!fs.existsSync(basePath)) return [];\n\n const dirs = fs\n .readdirSync(basePath)\n .filter((d: any) => fs.statSync(path.join(basePath, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(basePath, dir, 'template.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isTemplateActive(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n return templates.find((t) => t.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\n// Toggle templates: only 1 active per type allowed\nexport function toggleTemplate(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n\n templates.forEach((tpl) => {\n const metaPath = path.join(getTemplateBasePath(type), tpl.lowerName, 'template.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = tpl.lowerName === name.toLowerCase();\n saveJSON(metaPath, meta);\n });\n\n onTemplateSelect(type, name);\n}\n\n// --- EVENTS (replace with your event system integration) ---\n\nexport function onModuleEnabled(name: string) {\n console.log(`Module enabled: ${name}`);\n // emit event or custom logic here\n}\n\nexport function onModuleDisabled(name: string) {\n console.log(`Module disabled: ${name}`);\n}\n\nexport function onServiceEnabled(name: string) {\n console.log(`Service enabled: ${name}`);\n}\n\nexport function onServiceDisabled(name: string) {\n console.log(`Service disabled: ${name}`);\n}\n\nexport function onTemplateSelect(type: TemplateType, name: string) {\n console.log(`Template selected: type=${type}, name=${name}`);\n}\n\n// --- REGISTER HOOKS FOR MODULES ---\n\nexport async function registerEvents() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const eventsFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'events.server.js');\n if (fs.existsSync(eventsFile)) {\n const modEvents = await import(eventsFile);\n if (modEvents?.registerEvents) await modEvents.registerEvents();\n }\n }\n}\n\nexport async function registerMiddleware() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const middlewareFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'middleware.server.js');\n if (fs.existsSync(middlewareFile)) {\n const modMiddleware = await import(middlewareFile);\n if (modMiddleware?.registerMiddleware) await modMiddleware.registerMiddleware();\n }\n }\n}\n\n/**\n * Registers routes given a path to routes folder (for pages and API routes)\n * @param routesFolderPath string - path to module/service routes folder\n */\nexport async function registerRoutes(routesFolderPath: string) {\n if (!fs.existsSync(routesFolderPath)) return;\n\n const routeFiles = fs.readdirSync(routesFolderPath).filter((f: any) => /\\.(js|ts|jsx|tsx)$/.test(f));\n for (const file of routeFiles) {\n const routeModule = await import(path.join(routesFolderPath, file));\n if (routeModule?.registerRoute) {\n await routeModule.registerRoute();\n }\n }\n}\n\nexport async function loadProviders() {\n const paths = useSettings.getPaths();\n\n const all = [\n { type: 'module', list: getModules(), basePath: paths.modules },\n { type: 'service', list: getServices(), basePath: paths.services },\n ];\n\n for (const { type, list, basePath } of all) {\n for (const item of list) {\n if (!item.enabled) continue;\n\n const metaPath = path.join(path.resolve(basePath), item.lowerName, `${type}.json`);\n const meta = loadJSON(metaPath);\n if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n for (const providerRelPath of meta.providers) {\n try {\n let providerAbsPath = path.join(path.resolve(basePath), item.lowerName, providerRelPath);\n\n // Try .js fallback if file is .ts or .tsx\n const ext = path.extname(providerAbsPath);\n if (ext === '.ts' || ext === '.tsx') {\n const jsPath = providerAbsPath.replace(/\\.(ts|tsx)$/, '.js');\n try {\n await fs.accessSync(jsPath); // Check if compiled JS file exists\n providerAbsPath = jsPath;\n } catch {\n throw new Error(`Compiled JS version not found for provider: ${providerRelPath}`);\n }\n }\n\n const providerUrl = toFileUrl(providerAbsPath);\n const providerModule = await import(providerUrl);\n\n if (typeof providerModule.default === 'function') {\n await providerModule.default();\n console.log(`[${type}] ✅ Provider loaded: ${providerRelPath}`);\n } else {\n console.warn(`[${type}] ⚠️ Provider ${providerRelPath} has no default export.`);\n }\n } catch (err) {\n console.error(`[${type}] ❌ Failed to load provider ${providerRelPath}:`, err);\n }\n }\n }\n }\n}\n\n\n// export async function loadProviders() {\n// paths = {\n// modules: useSettings.getPaths().modules,\n// templates: useSettings.getPaths().templates,\n// services: useSettings.getPaths().services,\n// events: useSettings.getPaths().events,\n// }\n// const all = [\n// { type: 'module', list: getModules(), basePath: useSettings.getPaths().modules },\n// { type: 'service', list: getServices(), basePath: useSettings.getPaths().services },\n// ];\n\n// for (const { type, list, basePath } of all) {\n// for (const item of list) {\n// if (!item.enabled) continue;\n\n// const metaPath = path.join(basePath, item.lowerName, `${type}.json`);\n// const meta = loadJSON(metaPath);\n// if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n// for (const providerRelPath of meta.providers) {\n// try {\n// const providerPath = path.join(basePath, item.lowerName, providerRelPath);\n// const providerModule = await import(providerPath);\n// if (typeof providerModule.default === 'function') {\n// await providerModule.default(); // Call provider\n// console.log(`[${type}] Provider loaded: ${providerRelPath}`);\n// } else {\n// console.warn(`[${type}] Provider ${providerRelPath} has no default export.`);\n// }\n// } catch (err) {\n// console.error(`[${type}] Failed to load provider ${providerRelPath}:`, err);\n// }\n// }\n// }\n// }\n// }\n"],"mappings":"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","names":["import_node_path","import_node_fs","import_node_fs","fs","fs","path","fs"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/hooks/useUtils.ts","../../../src/core/hooks/useSettings.ts","../../../src/core/hooks/useExtensibles.ts"],"sourcesContent":["// packages/retrex-extensibles-core/hooks/useUtils.ts\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { useSettings } from './useSettings';\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport const useUtils = {\n async loadThemeComponent(\n componentName: string,\n options: {\n theme: {\n name: string;\n type: 'admin' | 'client' | 'portal' | 'email';\n };\n search?: {\n themes?: boolean;\n modules?: boolean;\n services?: boolean;\n };\n }\n ): Promise<React.ComponentType<any> | null> {\n const { search = {}, theme } = options;\n const searchThemes = search.themes ?? true;\n const searchModules = search.modules ?? true;\n const searchServices = search.services ?? true;\n\n const possiblePaths: string[] = [];\n\n console.log(`Searching for component \"${componentName}\" in theme \"${theme.name}\" of type \"${theme.type}\"...`);\n\n try {\n if (searchThemes) {\n const templatesRoot = path.resolve(useSettings.getPaths().templates);\n possiblePaths.push(\n path.resolve(\n templatesRoot,\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n \n if (searchModules) {\n const modulesRoot = path.resolve(useSettings.getPaths().modules);\n if (fs.existsSync(modulesRoot)) {\n const moduleDirs = await fs.readdirSync(modulesRoot);\n for (const mod of moduleDirs) {\n possiblePaths.push(\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n }\n }\n \n if (searchServices) {\n const servicesRoot = path.resolve(useSettings.getPaths().services);\n if (fs.existsSync(servicesRoot)) {\n const serviceDirs = await fs.readdirSync(servicesRoot);\n for (const svc of serviceDirs) {\n possiblePaths.push(\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n }\n }\n \n for (const filePath of possiblePaths) {\n if (await fs.existsSync(filePath)) {\n try {\n const mod = await import(toFileUrl(filePath));\n if (mod.default) return mod.default;\n } catch (err) {\n console.error(`Error loading component at ${filePath}:`, err);\n }\n }\n }\n \n console.warn(`Component \"${componentName}\" not found in theme \"${theme.name}\" of type \"${theme.type}\".`);\n return null;\n } catch (err) {\n console.error(`Error searching for component \"${componentName}\":`, err);\n console.warn(`Component \"${componentName}\" not found in theme \"${theme.name}\" of type \"${theme.type}\".`);\n console.warn(`Searched paths:`, possiblePaths);\n console.warn(`Ensure the component exists in the specified theme and type.`);\n console.warn(`If the component is in a module or service, ensure the theme structure is correct.`);\n console.warn(`If you are using a custom theme, module, or service, ensure it is properly configured and registered in the settings.`);\n return null;\n }\n }\n}\n","// src/hooks/useSettings.ts\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport { loadJSON, saveJSON } from './useExtensibles';\nimport type { ExtensiblePaths } from '../types'; // Adjust the import path as necessary\n\nconst defaultPaths: ExtensiblePaths = {\n modules: './app/modules',\n templates: './app/resources/themes',\n services: './app/services',\n events: './app/events',\n langs: './public/langs',\n providers: './app/providers',\n};\n\nlet extensiblePaths: ExtensiblePaths = { ...defaultPaths };\nconst isServer = typeof window === 'undefined';\n\n\nexport const useSettings = {\n getPaths(): ExtensiblePaths {\n if (isServer) {\n const json = loadJSON('./app/extensiblePaths.json');\n if (json) extensiblePaths = json;\n }\n\n if (isServer) {\n for (const key of Object.keys(defaultPaths) as Array<keyof ExtensiblePaths>) {\n const candidate = extensiblePaths[key];\n if (!candidate || !fs.existsSync(candidate)) {\n extensiblePaths[key] = defaultPaths[key];\n }\n }\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n langs: './langs',\n };\n // extensiblePaths = loadJSON(\"./app/extensiblePaths.json\");\n return extensiblePaths;\n },\n\n setPaths(paths: Partial<ExtensiblePaths>): void {\n if (isServer) {\n saveJSON('./app/extensiblePaths.json', {\n ...extensiblePaths,\n ...paths,\n });\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n ...paths,\n };\n },\n \n getKeyValue(key: string): string {\n return 'Coming Soon'\n },\n setKeyValue(key: string, value: string): void {\n return;\n },\n};\n","// src/hooks/useExtensibles.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport type { TemplateType, ExtensibleMeta, ExtensiblePaths } from '../types';\n\nlet paths: ExtensiblePaths;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport function registerExtensibles(extensiblePaths: ExtensiblePaths) {\n paths = extensiblePaths;\n}\n\nexport function loadJSON(filePath: string): any {\n if (!fs.existsSync(filePath)) return null;\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function saveJSON(filePath: string, data: any) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// --- MODULES ---\n\nexport function getModules(): ExtensibleMeta[] {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().modules)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().modules, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().modules, dir, 'module.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isModuleEnabled(name: string) {\n const modules = getModules();\n return modules.find((m) => m.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleModule(name: string, enabled: boolean) {\n const modules = getModules();\n const mod = modules.find((m) => m.lowerName === name.toLowerCase());\n if (!mod) throw new Error(`Module ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().modules, mod.lowerName, 'module.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onModuleEnabled(mod.name);\n else onModuleDisabled(mod.name);\n}\n\n// --- SERVICES ---\n\nexport function getServices(): ExtensibleMeta[] {\n if (!useSettings.getPaths().services) throw new Error('Services path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().services)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().services, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().services, dir, 'service.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isServiceEnabled(name: string) {\n const services = getServices();\n return services.find((s) => s.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleService(name: string, enabled: boolean) {\n const services = getServices();\n const svc = services.find((s) => s.lowerName === name.toLowerCase());\n if (!svc) throw new Error(`Service ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().services, svc.lowerName, 'service.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onServiceEnabled(svc.name);\n else onServiceDisabled(svc.name);\n}\n\n// --- TEMPLATES ---\n\n// Helper to get base path for a template type\nfunction getTemplateBasePath(type: TemplateType): string {\n if (!useSettings.getPaths().templates) throw new Error('Templates path not registered.');\n switch (type) {\n case 'admin-theme':\n case 'client-theme':\n return path.join(useSettings.getPaths().templates, 'themes');\n case 'portal':\n return path.join(useSettings.getPaths().templates, 'portals');\n case 'email':\n return path.join(useSettings.getPaths().templates, 'emails');\n default:\n throw new Error(`Unknown template type: ${type}`);\n }\n}\n\nexport function getTemplates(type: TemplateType): ExtensibleMeta[] {\n const basePath = getTemplateBasePath(type);\n if (!fs.existsSync(basePath)) return [];\n\n const dirs = fs\n .readdirSync(basePath)\n .filter((d: any) => fs.statSync(path.join(basePath, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(basePath, dir, 'template.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isTemplateActive(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n return templates.find((t) => t.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\n// Toggle templates: only 1 active per type allowed\nexport function toggleTemplate(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n\n templates.forEach((tpl) => {\n const metaPath = path.join(getTemplateBasePath(type), tpl.lowerName, 'template.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = tpl.lowerName === name.toLowerCase();\n saveJSON(metaPath, meta);\n });\n\n onTemplateSelect(type, name);\n}\n\n// --- EVENTS (replace with your event system integration) ---\n\nexport function onModuleEnabled(name: string) {\n console.log(`Module enabled: ${name}`);\n // emit event or custom logic here\n}\n\nexport function onModuleDisabled(name: string) {\n console.log(`Module disabled: ${name}`);\n}\n\nexport function onServiceEnabled(name: string) {\n console.log(`Service enabled: ${name}`);\n}\n\nexport function onServiceDisabled(name: string) {\n console.log(`Service disabled: ${name}`);\n}\n\nexport function onTemplateSelect(type: TemplateType, name: string) {\n console.log(`Template selected: type=${type}, name=${name}`);\n}\n\n// --- REGISTER HOOKS FOR MODULES ---\n\nexport async function registerEvents() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const eventsFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'events.server.js');\n if (fs.existsSync(eventsFile)) {\n const modEvents = await import(eventsFile);\n if (modEvents?.registerEvents) await modEvents.registerEvents();\n }\n }\n}\n\nexport async function registerMiddleware() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const middlewareFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'middleware.server.js');\n if (fs.existsSync(middlewareFile)) {\n const modMiddleware = await import(middlewareFile);\n if (modMiddleware?.registerMiddleware) await modMiddleware.registerMiddleware();\n }\n }\n}\n\n/**\n * Registers routes given a path to routes folder (for pages and API routes)\n * @param routesFolderPath string - path to module/service routes folder\n */\nexport async function registerRoutes(routesFolderPath: string) {\n if (!fs.existsSync(routesFolderPath)) return;\n\n const routeFiles = fs.readdirSync(routesFolderPath).filter((f: any) => /\\.(js|ts|jsx|tsx)$/.test(f));\n for (const file of routeFiles) {\n const routeModule = await import(path.join(routesFolderPath, file));\n if (routeModule?.registerRoute) {\n await routeModule.registerRoute();\n }\n }\n}\n\nexport async function loadProviders() {\n const paths = useSettings.getPaths();\n\n const all = [\n { type: 'module', list: getModules(), basePath: paths.modules },\n { type: 'service', list: getServices(), basePath: paths.services },\n ];\n\n for (const { type, list, basePath } of all) {\n for (const item of list) {\n if (!item.enabled) continue;\n\n const metaPath = path.join(path.resolve(basePath), item.lowerName, `${type}.json`);\n const meta = loadJSON(metaPath);\n if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n for (const providerRelPath of meta.providers) {\n try {\n let providerAbsPath = path.join(path.resolve(basePath), item.lowerName, providerRelPath);\n\n // Try .js fallback if file is .ts or .tsx\n const ext = path.extname(providerAbsPath);\n if (ext === '.ts' || ext === '.tsx') {\n const jsPath = providerAbsPath.replace(/\\.(ts|tsx)$/, '.js');\n try {\n await fs.accessSync(jsPath); // Check if compiled JS file exists\n providerAbsPath = jsPath;\n } catch {\n throw new Error(`Compiled JS version not found for provider: ${providerRelPath}`);\n }\n }\n\n const providerUrl = toFileUrl(providerAbsPath);\n const providerModule = await import(providerUrl);\n\n if (typeof providerModule.default === 'function') {\n await providerModule.default();\n console.log(`[${type}] ✅ Provider loaded: ${providerRelPath}`);\n } else {\n console.warn(`[${type}] ⚠️ Provider ${providerRelPath} has no default export.`);\n }\n } catch (err) {\n console.error(`[${type}] ❌ Failed to load provider ${providerRelPath}:`, err);\n }\n }\n }\n }\n}\n\n\n// export async function loadProviders() {\n// paths = {\n// modules: useSettings.getPaths().modules,\n// templates: useSettings.getPaths().templates,\n// services: useSettings.getPaths().services,\n// events: useSettings.getPaths().events,\n// }\n// const all = [\n// { type: 'module', list: getModules(), basePath: useSettings.getPaths().modules },\n// { type: 'service', list: getServices(), basePath: useSettings.getPaths().services },\n// ];\n\n// for (const { type, list, basePath } of all) {\n// for (const item of list) {\n// if (!item.enabled) continue;\n\n// const metaPath = path.join(basePath, item.lowerName, `${type}.json`);\n// const meta = loadJSON(metaPath);\n// if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n// for (const providerRelPath of meta.providers) {\n// try {\n// const providerPath = path.join(basePath, item.lowerName, providerRelPath);\n// const providerModule = await import(providerPath);\n// if (typeof providerModule.default === 'function') {\n// await providerModule.default(); // Call provider\n// console.log(`[${type}] Provider loaded: ${providerRelPath}`);\n// } else {\n// console.warn(`[${type}] Provider ${providerRelPath} has no default export.`);\n// }\n// } catch (err) {\n// console.error(`[${type}] Failed to load provider ${providerRelPath}:`, err);\n// }\n// }\n// }\n// }\n// }\n"],"mappings":"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","names":["path","fs","fs","fs","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/hooks/useUtils.ts","../../../src/core/hooks/useSettings.ts","../../../src/core/hooks/useExtensibles.ts"],"sourcesContent":["/* @vite-ignore */\n// packages/retrex-extensibles-core/hooks/useUtils.ts\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { useSettings } from './useSettings';\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport const useUtils = {\n async loadThemeComponent(\n componentName: string,\n options: {\n theme: {\n name: string;\n type: 'admin' | 'client' | 'portal' | 'email';\n };\n search?: {\n themes?: boolean;\n modules?: boolean;\n services?: boolean;\n };\n }\n ): Promise<React.ComponentType<any> | null> {\n const { search = {}, theme } = options;\n const searchThemes = search.themes ?? true;\n const searchModules = search.modules ?? true;\n const searchServices = search.services ?? true;\n\n const possiblePaths: string[] = [];\n\n console.log(`Searching for component \"${componentName}\" in theme \"${theme.name}\" of type \"${theme.type}\"...`);\n\n try {\n if (searchThemes) {\n const templatesRoot = path.resolve(useSettings.getPaths().templates);\n possiblePaths.push(\n path.resolve(\n templatesRoot,\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n templatesRoot,\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n \n if (searchModules) {\n const modulesRoot = path.resolve(useSettings.getPaths().modules);\n if (fs.existsSync(modulesRoot)) {\n const moduleDirs = await fs.readdirSync(modulesRoot);\n for (const mod of moduleDirs) {\n possiblePaths.push(\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n modulesRoot,\n mod,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n }\n }\n \n if (searchServices) {\n const servicesRoot = path.resolve(useSettings.getPaths().services);\n if (fs.existsSync(servicesRoot)) {\n const serviceDirs = await fs.readdirSync(servicesRoot);\n for (const svc of serviceDirs) {\n possiblePaths.push(\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n theme.name,\n 'components',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'routes',\n `${componentName}.tsx`\n ),\n path.resolve(\n servicesRoot,\n svc,\n 'resources',\n 'themes',\n theme.type,\n 'default',\n 'components',\n `${componentName}.tsx`\n )\n );\n }\n }\n }\n \n for (const filePath of possiblePaths) {\n if (await fs.existsSync(filePath)) {\n try {\n const mod = await import(toFileUrl(filePath));\n if (mod.default) return mod.default;\n } catch (err) {\n console.error(`Error loading component at ${filePath}:`, err);\n }\n }\n }\n \n console.warn(`Component \"${componentName}\" not found in theme \"${theme.name}\" of type \"${theme.type}\".`);\n return null;\n } catch (err) {\n console.error(`Error searching for component \"${componentName}\":`, err);\n console.warn(`Component \"${componentName}\" not found in theme \"${theme.name}\" of type \"${theme.type}\".`);\n console.warn(`Searched paths:`, possiblePaths);\n console.warn(`Ensure the component exists in the specified theme and type.`);\n console.warn(`If the component is in a module or service, ensure the theme structure is correct.`);\n console.warn(`If you are using a custom theme, module, or service, ensure it is properly configured and registered in the settings.`);\n return null;\n }\n }\n}\n","// src/hooks/useSettings.ts\n\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nimport { loadJSON, saveJSON } from './useExtensibles';\nimport type { ExtensiblePaths } from '../types'; // Adjust the import path as necessary\n\nconst defaultPaths: ExtensiblePaths = {\n modules: './app/modules',\n templates: './app/resources/themes',\n services: './app/services',\n events: './app/events',\n langs: './public/langs',\n providers: './app/providers',\n};\n\nlet extensiblePaths: ExtensiblePaths = { ...defaultPaths };\nconst isServer = typeof window === 'undefined';\n\n\nexport const useSettings = {\n getPaths(): ExtensiblePaths {\n if (isServer) {\n const json = loadJSON('./app/extensiblePaths.json');\n if (json) extensiblePaths = json;\n }\n\n if (isServer) {\n for (const key of Object.keys(defaultPaths) as Array<keyof ExtensiblePaths>) {\n const candidate = extensiblePaths[key];\n if (!candidate || !fs.existsSync(candidate)) {\n extensiblePaths[key] = defaultPaths[key];\n }\n }\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n langs: './langs',\n };\n // extensiblePaths = loadJSON(\"./app/extensiblePaths.json\");\n return extensiblePaths;\n },\n\n setPaths(paths: Partial<ExtensiblePaths>): void {\n if (isServer) {\n saveJSON('./app/extensiblePaths.json', {\n ...extensiblePaths,\n ...paths,\n });\n }\n\n extensiblePaths = {\n ...extensiblePaths,\n ...paths,\n };\n },\n \n getKeyValue(key: string): string {\n return 'Coming Soon'\n },\n setKeyValue(key: string, value: string): void {\n return;\n },\n};\n","// src/hooks/useExtensibles.ts\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { useSettings } from './useSettings';\nimport type { TemplateType, ExtensibleMeta, ExtensiblePaths } from '../types';\n\nlet paths: ExtensiblePaths;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, '/');\n if (normalized.startsWith('file://')) return normalized;\n const prefix = normalized.startsWith('/') ? 'file://' : 'file:///';\n return `${prefix}${encodeURI(normalized)}`;\n}\n\nexport function registerExtensibles(extensiblePaths: ExtensiblePaths) {\n paths = extensiblePaths;\n}\n\nexport function loadJSON(filePath: string): any {\n if (!fs.existsSync(filePath)) return null;\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function saveJSON(filePath: string, data: any) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// --- MODULES ---\n\nexport function getModules(): ExtensibleMeta[] {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().modules)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().modules, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().modules, dir, 'module.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isModuleEnabled(name: string) {\n const modules = getModules();\n return modules.find((m) => m.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleModule(name: string, enabled: boolean) {\n const modules = getModules();\n const mod = modules.find((m) => m.lowerName === name.toLowerCase());\n if (!mod) throw new Error(`Module ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().modules, mod.lowerName, 'module.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onModuleEnabled(mod.name);\n else onModuleDisabled(mod.name);\n}\n\n// --- SERVICES ---\n\nexport function getServices(): ExtensibleMeta[] {\n if (!useSettings.getPaths().services) throw new Error('Services path not registered.');\n\n const dirs = fs\n .readdirSync(useSettings.getPaths().services)\n .filter((d: any) => fs.statSync(path.join(useSettings.getPaths().services, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(useSettings.getPaths().services, dir, 'service.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: meta.lowerName || dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isServiceEnabled(name: string) {\n const services = getServices();\n return services.find((s) => s.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\nexport function toggleService(name: string, enabled: boolean) {\n const services = getServices();\n const svc = services.find((s) => s.lowerName === name.toLowerCase());\n if (!svc) throw new Error(`Service ${name} not found`);\n\n const metaPath = path.join(useSettings.getPaths().services, svc.lowerName, 'service.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = enabled;\n saveJSON(metaPath, meta);\n\n if (enabled) onServiceEnabled(svc.name);\n else onServiceDisabled(svc.name);\n}\n\n// --- TEMPLATES ---\n\n// Helper to get base path for a template type\nfunction getTemplateBasePath(type: TemplateType): string {\n if (!useSettings.getPaths().templates) throw new Error('Templates path not registered.');\n switch (type) {\n case 'admin-theme':\n case 'client-theme':\n return path.join(useSettings.getPaths().templates, 'themes');\n case 'portal':\n return path.join(useSettings.getPaths().templates, 'portals');\n case 'email':\n return path.join(useSettings.getPaths().templates, 'emails');\n default:\n throw new Error(`Unknown template type: ${type}`);\n }\n}\n\nexport function getTemplates(type: TemplateType): ExtensibleMeta[] {\n const basePath = getTemplateBasePath(type);\n if (!fs.existsSync(basePath)) return [];\n\n const dirs = fs\n .readdirSync(basePath)\n .filter((d: any) => fs.statSync(path.join(basePath, d)).isDirectory());\n\n return dirs.map((dir: any) => {\n const meta = loadJSON(path.join(basePath, dir, 'template.json')) || {};\n return {\n name: meta.name || dir,\n lowerName: dir.toLowerCase(),\n version: meta.version,\n description: meta.description,\n author: meta.author,\n icon: meta.icon,\n enabled: meta.enabled ?? false,\n };\n });\n}\n\nexport function isTemplateActive(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n return templates.find((t) => t.lowerName === name.toLowerCase())?.enabled ?? false;\n}\n\n// Toggle templates: only 1 active per type allowed\nexport function toggleTemplate(type: TemplateType, name: string) {\n const templates = getTemplates(type);\n\n templates.forEach((tpl) => {\n const metaPath = path.join(getTemplateBasePath(type), tpl.lowerName, 'template.json');\n const meta = loadJSON(metaPath) || {};\n meta.enabled = tpl.lowerName === name.toLowerCase();\n saveJSON(metaPath, meta);\n });\n\n onTemplateSelect(type, name);\n}\n\n// --- EVENTS (replace with your event system integration) ---\n\nexport function onModuleEnabled(name: string) {\n console.log(`Module enabled: ${name}`);\n // emit event or custom logic here\n}\n\nexport function onModuleDisabled(name: string) {\n console.log(`Module disabled: ${name}`);\n}\n\nexport function onServiceEnabled(name: string) {\n console.log(`Service enabled: ${name}`);\n}\n\nexport function onServiceDisabled(name: string) {\n console.log(`Service disabled: ${name}`);\n}\n\nexport function onTemplateSelect(type: TemplateType, name: string) {\n console.log(`Template selected: type=${type}, name=${name}`);\n}\n\n// --- REGISTER HOOKS FOR MODULES ---\n\nexport async function registerEvents() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const eventsFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'events.server.js');\n if (fs.existsSync(eventsFile)) {\n const modEvents = await import(eventsFile);\n if (modEvents?.registerEvents) await modEvents.registerEvents();\n }\n }\n}\n\nexport async function registerMiddleware() {\n if (!useSettings.getPaths().modules) throw new Error('Modules path not registered.');\n const modules = getModules().filter((m) => m.enabled);\n for (const mod of modules) {\n const middlewareFile = path.join(useSettings.getPaths().modules, mod.lowerName, 'middleware.server.js');\n if (fs.existsSync(middlewareFile)) {\n const modMiddleware = await import(middlewareFile);\n if (modMiddleware?.registerMiddleware) await modMiddleware.registerMiddleware();\n }\n }\n}\n\n/**\n * Registers routes given a path to routes folder (for pages and API routes)\n * @param routesFolderPath string - path to module/service routes folder\n */\nexport async function registerRoutes(routesFolderPath: string) {\n if (!fs.existsSync(routesFolderPath)) return;\n\n const routeFiles = fs.readdirSync(routesFolderPath).filter((f: any) => /\\.(js|ts|jsx|tsx)$/.test(f));\n for (const file of routeFiles) {\n const routeModule = await import(path.join(routesFolderPath, file));\n if (routeModule?.registerRoute) {\n await routeModule.registerRoute();\n }\n }\n}\n\nexport async function loadProviders() {\n const paths = useSettings.getPaths();\n\n const all = [\n { type: 'module', list: getModules(), basePath: paths.modules },\n { type: 'service', list: getServices(), basePath: paths.services },\n ];\n\n for (const { type, list, basePath } of all) {\n for (const item of list) {\n if (!item.enabled) continue;\n\n const metaPath = path.join(path.resolve(basePath), item.lowerName, `${type}.json`);\n const meta = loadJSON(metaPath);\n if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n for (const providerRelPath of meta.providers) {\n try {\n let providerAbsPath = path.join(path.resolve(basePath), item.lowerName, providerRelPath);\n\n // Try .js fallback if file is .ts or .tsx\n const ext = path.extname(providerAbsPath);\n if (ext === '.ts' || ext === '.tsx') {\n const jsPath = providerAbsPath.replace(/\\.(ts|tsx)$/, '.js');\n try {\n await fs.accessSync(jsPath); // Check if compiled JS file exists\n providerAbsPath = jsPath;\n } catch {\n throw new Error(`Compiled JS version not found for provider: ${providerRelPath}`);\n }\n }\n\n const providerUrl = toFileUrl(providerAbsPath);\n const providerModule = await import(providerUrl);\n\n if (typeof providerModule.default === 'function') {\n await providerModule.default();\n console.log(`[${type}] ✅ Provider loaded: ${providerRelPath}`);\n } else {\n console.warn(`[${type}] ⚠️ Provider ${providerRelPath} has no default export.`);\n }\n } catch (err) {\n console.error(`[${type}] ❌ Failed to load provider ${providerRelPath}:`, err);\n }\n }\n }\n }\n}\n\n\n// export async function loadProviders() {\n// paths = {\n// modules: useSettings.getPaths().modules,\n// templates: useSettings.getPaths().templates,\n// services: useSettings.getPaths().services,\n// events: useSettings.getPaths().events,\n// }\n// const all = [\n// { type: 'module', list: getModules(), basePath: useSettings.getPaths().modules },\n// { type: 'service', list: getServices(), basePath: useSettings.getPaths().services },\n// ];\n\n// for (const { type, list, basePath } of all) {\n// for (const item of list) {\n// if (!item.enabled) continue;\n\n// const metaPath = path.join(basePath, item.lowerName, `${type}.json`);\n// const meta = loadJSON(metaPath);\n// if (!meta?.providers || !Array.isArray(meta.providers)) continue;\n\n// for (const providerRelPath of meta.providers) {\n// try {\n// const providerPath = path.join(basePath, item.lowerName, providerRelPath);\n// const providerModule = await import(providerPath);\n// if (typeof providerModule.default === 'function') {\n// await providerModule.default(); // Call provider\n// console.log(`[${type}] Provider loaded: ${providerRelPath}`);\n// } else {\n// console.warn(`[${type}] Provider ${providerRelPath} has no default export.`);\n// }\n// } catch (err) {\n// console.error(`[${type}] Failed to load provider ${providerRelPath}:`, err);\n// }\n// }\n// }\n// }\n// }\n"],"mappings":"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","names":["path","fs","fs","fs","path","fs"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1116,7 +1116,12 @@ var useTranslation = {
|
|
|
1116
1116
|
};
|
|
1117
1117
|
|
|
1118
1118
|
// src/core/hooks/useNav.ts
|
|
1119
|
-
var
|
|
1119
|
+
var GLOBAL_KEY = "__retrex_navitems__";
|
|
1120
|
+
var globalStore = globalThis;
|
|
1121
|
+
var navItems = globalStore[GLOBAL_KEY] ?? [];
|
|
1122
|
+
if (!globalStore[GLOBAL_KEY]) {
|
|
1123
|
+
globalStore[GLOBAL_KEY] = navItems;
|
|
1124
|
+
}
|
|
1120
1125
|
function resolveCategory(item, parent) {
|
|
1121
1126
|
if (item.category) return item.category;
|
|
1122
1127
|
if (parent?.category) return parent.category;
|