[{"data":1,"prerenderedAt":748},["ShallowReactive",2],{"navigation_docs_en":3,"-en-docs-architecture-overview":62,"-en-docs-architecture-overview-surround":743},[4,9,26,37,51],{"title":5,"path":6,"stem":7,"icon":8},"Introduction","\u002Fen\u002Fdocs\u002Fintroduction","en\u002Fdocs\u002F1.introduction","i-heroicons-book-open",{"title":10,"icon":11,"path":12,"stem":13,"children":14,"page":25},"Getting Started","i-heroicons-bolt","\u002Fen\u002Fdocs\u002Fgetting-started","en\u002Fdocs\u002F2.getting-started",[15,20],{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fen\u002Fdocs\u002Fgetting-started\u002Finstallation","en\u002Fdocs\u002F2.getting-started\u002Finstallation","i-heroicons-cloud-arrow-down",{"title":21,"path":22,"stem":23,"icon":24},"Project Structure","\u002Fen\u002Fdocs\u002Fgetting-started\u002Fproject-structure","en\u002Fdocs\u002F2.getting-started\u002Fproject-structure","i-heroicons-folder",false,{"title":27,"icon":28,"path":29,"stem":30,"children":31,"page":25},"Architecture","i-heroicons-square-3-stack-3d","\u002Fen\u002Fdocs\u002Farchitecture","en\u002Fdocs\u002F3.architecture",[32],{"title":33,"path":34,"stem":35,"icon":36},"Architecture: Nuxt Layers","\u002Fen\u002Fdocs\u002Farchitecture\u002Foverview","en\u002Fdocs\u002F3.architecture\u002Foverview","i-heroicons-cpu-chip",{"title":38,"icon":39,"path":40,"stem":41,"children":42,"page":25},"Plugin System","i-heroicons-puzzle-piece","\u002Fen\u002Fdocs\u002Fplugin-system","en\u002Fdocs\u002F4.plugin-system",[43,46],{"title":38,"path":44,"stem":45,"icon":39},"\u002Fen\u002Fdocs\u002Fplugin-system\u002Fbasics","en\u002Fdocs\u002F4.plugin-system\u002Fbasics",{"title":47,"path":48,"stem":49,"icon":50},"Override System","\u002Fen\u002Fdocs\u002Fplugin-system\u002Foverride-modes","en\u002Fdocs\u002F4.plugin-system\u002Foverride-modes","i-heroicons-arrow-path",{"title":52,"icon":53,"path":54,"stem":55,"children":56,"page":25},"Guides","i-heroicons-academic-cap","\u002Fen\u002Fdocs\u002Fguides","en\u002Fdocs\u002F5.guides",[57],{"title":58,"path":59,"stem":60,"icon":61},"Plugin Creation Guide","\u002Fen\u002Fdocs\u002Fguides\u002Fcreate-plugin","en\u002Fdocs\u002F5.guides\u002Fcreate-plugin","i-heroicons-command-line",{"id":63,"title":33,"body":64,"description":736,"extension":737,"links":738,"meta":739,"navigation":740,"path":34,"seo":741,"stem":35,"__hash__":742},"docs_en\u002Fen\u002Fdocs\u002F3.architecture\u002Foverview.md",{"type":65,"value":66,"toc":728},"minimark",[67,71,80,85,93,326,329,351,367,371,374,408,422,426,429,451,462,466,479,617,626,630,640,643,677,681,688,710,713,716,724],[68,69,33],"h1",{"id":70},"architecture-nuxt-layers",[72,73,74,75,79],"p",{},"Alkademi LMS Open Source is built as a ",[76,77,78],"strong",{},"modular monolith",". Every feature still runs inside one Nuxt application, but each domain feature is isolated as a Nuxt Layer so it can be maintained, replaced, or extended more easily.",[81,82,84],"h2",{"id":83},"layer-composition","Layer Composition",[72,86,87,88,92],{},"The main layer composition lives in the root ",[89,90,91],"code",{},"nuxt.config.ts",":",[94,95,100],"pre",{"className":96,"code":97,"language":98,"meta":99,"style":99},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { resolve } from 'path'\nimport { getLayerDirs } from '.\u002Fshared\u002Futils\u002Flayers'\n\nconst rootDir = resolve(__dirname)\n\nexport default defineNuxtConfig({\n  extends: [\n    '.\u002Fweb',\n    ...getLayerDirs(rootDir, 'plugins'),\n    ...getLayerDirs(rootDir, 'cores')\n  ],\n  modules: ['@pinia\u002Fnuxt', '@nuxt\u002Fui']\n})\n","typescript","",[89,101,102,135,156,163,182,187,205,217,232,259,280,288,318],{"__ignoreMap":99},[103,104,107,111,115,119,122,125,128,132],"span",{"class":105,"line":106},"line",1,[103,108,110],{"class":109},"s7zQu","import",[103,112,114],{"class":113},"sMK4o"," {",[103,116,118],{"class":117},"sTEyZ"," resolve",[103,120,121],{"class":113}," }",[103,123,124],{"class":109}," from",[103,126,127],{"class":113}," '",[103,129,131],{"class":130},"sfazB","path",[103,133,134],{"class":113},"'\n",[103,136,138,140,142,145,147,149,151,154],{"class":105,"line":137},2,[103,139,110],{"class":109},[103,141,114],{"class":113},[103,143,144],{"class":117}," getLayerDirs",[103,146,121],{"class":113},[103,148,124],{"class":109},[103,150,127],{"class":113},[103,152,153],{"class":130},".\u002Fshared\u002Futils\u002Flayers",[103,155,134],{"class":113},[103,157,159],{"class":105,"line":158},3,[103,160,162],{"emptyLinePlaceholder":161},true,"\n",[103,164,166,170,173,176,179],{"class":105,"line":165},4,[103,167,169],{"class":168},"spNyl","const",[103,171,172],{"class":117}," rootDir ",[103,174,175],{"class":113},"=",[103,177,118],{"class":178},"s2Zo4",[103,180,181],{"class":117},"(__dirname)\n",[103,183,185],{"class":105,"line":184},5,[103,186,162],{"emptyLinePlaceholder":161},[103,188,190,193,196,199,202],{"class":105,"line":189},6,[103,191,192],{"class":109},"export",[103,194,195],{"class":109}," default",[103,197,198],{"class":178}," defineNuxtConfig",[103,200,201],{"class":117},"(",[103,203,204],{"class":113},"{\n",[103,206,208,212,214],{"class":105,"line":207},7,[103,209,211],{"class":210},"swJcz","  extends",[103,213,92],{"class":113},[103,215,216],{"class":117}," [\n",[103,218,220,223,226,229],{"class":105,"line":219},8,[103,221,222],{"class":113},"    '",[103,224,225],{"class":130},".\u002Fweb",[103,227,228],{"class":113},"'",[103,230,231],{"class":113},",\n",[103,233,235,238,241,244,247,249,252,254,257],{"class":105,"line":234},9,[103,236,237],{"class":113},"    ...",[103,239,240],{"class":178},"getLayerDirs",[103,242,243],{"class":117},"(rootDir",[103,245,246],{"class":113},",",[103,248,127],{"class":113},[103,250,251],{"class":130},"plugins",[103,253,228],{"class":113},[103,255,256],{"class":117},")",[103,258,231],{"class":113},[103,260,262,264,266,268,270,272,275,277],{"class":105,"line":261},10,[103,263,237],{"class":113},[103,265,240],{"class":178},[103,267,243],{"class":117},[103,269,246],{"class":113},[103,271,127],{"class":113},[103,273,274],{"class":130},"cores",[103,276,228],{"class":113},[103,278,279],{"class":117},")\n",[103,281,283,286],{"class":105,"line":282},11,[103,284,285],{"class":117},"  ]",[103,287,231],{"class":113},[103,289,291,294,296,299,301,304,306,308,310,313,315],{"class":105,"line":290},12,[103,292,293],{"class":210},"  modules",[103,295,92],{"class":113},[103,297,298],{"class":117}," [",[103,300,228],{"class":113},[103,302,303],{"class":130},"@pinia\u002Fnuxt",[103,305,228],{"class":113},[103,307,246],{"class":113},[103,309,127],{"class":113},[103,311,312],{"class":130},"@nuxt\u002Fui",[103,314,228],{"class":113},[103,316,317],{"class":117},"]\n",[103,319,321,324],{"class":105,"line":320},13,[103,322,323],{"class":113},"}",[103,325,279],{"class":117},[72,327,328],{},"The actual project order is:",[330,331,332,338,345],"ol",{},[333,334,335,337],"li",{},[89,336,225],{}," for the landing\u002Fpublic site.",[333,339,340,341,344],{},"Active plugins from ",[89,342,343],{},"plugins\u002F",".",[333,346,347,348,344],{},"Active cores from ",[89,349,350],{},"cores\u002F",[72,352,353,354,357,358,360,361,363,364,366],{},"Layers are not listed manually one by one. ",[89,355,356],{},"getLayerDirs(rootDir, baseDir)"," scans the ",[89,359,343],{}," and ",[89,362,350],{}," folders, then only includes folders with ",[89,365,91],{}," and an enabled config.",[81,368,370],{"id":369},"core-auto-discovery","Core Auto-Discovery",[72,372,373],{},"A core is active when:",[375,376,377,384,389,394,402],"ul",{},[333,378,379,380,383],{},"it is located at ",[89,381,382],{},"cores\u002F{core-id}",";",[333,385,386,387,383],{},"it has ",[89,388,91],{},[333,390,386,391,383],{},[89,392,393],{},"core.config.ts",[333,395,396,398,399,383],{},[89,397,393],{}," contains ",[89,400,401],{},"enabled: true",[333,403,404,405,344],{},"the core is not replaced by an active plugin through ",[89,406,407],{},"replaces",[72,409,410,411,413,414,417,418,421],{},"If a core does not have ",[89,412,393],{},", does not have an ",[89,415,416],{},"enabled"," flag, or has ",[89,419,420],{},"enabled: false",", the layer is skipped.",[81,423,425],{"id":424},"plugin-auto-discovery","Plugin Auto-Discovery",[72,427,428],{},"A plugin is active when:",[375,430,431,436,440,445],{},[333,432,379,433,383],{},[89,434,435],{},"plugins\u002F{plugin-id}",[333,437,386,438,383],{},[89,439,91],{},[333,441,386,442,383],{},[89,443,444],{},"plugin.config.ts",[333,446,447,398,449,344],{},[89,448,444],{},[89,450,401],{},[72,452,453,454,457,458,461],{},"Active plugins are also read to determine replaced cores. If a plugin has ",[89,455,456],{},"replaces: ['program']",", the ",[89,459,460],{},"program"," core is not included in the layer composition.",[81,463,465],{"id":464},"core-replacement","Core Replacement",[72,467,468,469,471,472,475,476,344],{},"The current replacement mechanism is based on the ",[89,470,407],{}," field, not ",[89,473,474],{},"overrideMode"," or ",[89,477,478],{},"targetCore",[94,480,482],{"className":96,"code":481,"language":98,"meta":99,"style":99},"export default {\n  enabled: true,\n  replaces: ['program'],\n  routes: [\n    {\n      key: 'custom-class',\n      path: '\u002Fdashboard\u002Fcustom-class',\n      icon: 'i-heroicons-sparkles',\n      label: 'Custom Class'\n    }\n  ]\n}\n",[89,483,484,493,506,526,535,540,556,572,588,602,607,612],{"__ignoreMap":99},[103,485,486,488,490],{"class":105,"line":106},[103,487,192],{"class":109},[103,489,195],{"class":109},[103,491,492],{"class":113}," {\n",[103,494,495,498,500,504],{"class":105,"line":137},[103,496,497],{"class":210},"  enabled",[103,499,92],{"class":113},[103,501,503],{"class":502},"sfNiH"," true",[103,505,231],{"class":113},[103,507,508,511,513,515,517,519,521,524],{"class":105,"line":158},[103,509,510],{"class":210},"  replaces",[103,512,92],{"class":113},[103,514,298],{"class":117},[103,516,228],{"class":113},[103,518,460],{"class":130},[103,520,228],{"class":113},[103,522,523],{"class":117},"]",[103,525,231],{"class":113},[103,527,528,531,533],{"class":105,"line":165},[103,529,530],{"class":210},"  routes",[103,532,92],{"class":113},[103,534,216],{"class":117},[103,536,537],{"class":105,"line":184},[103,538,539],{"class":113},"    {\n",[103,541,542,545,547,549,552,554],{"class":105,"line":189},[103,543,544],{"class":210},"      key",[103,546,92],{"class":113},[103,548,127],{"class":113},[103,550,551],{"class":130},"custom-class",[103,553,228],{"class":113},[103,555,231],{"class":113},[103,557,558,561,563,565,568,570],{"class":105,"line":207},[103,559,560],{"class":210},"      path",[103,562,92],{"class":113},[103,564,127],{"class":113},[103,566,567],{"class":130},"\u002Fdashboard\u002Fcustom-class",[103,569,228],{"class":113},[103,571,231],{"class":113},[103,573,574,577,579,581,584,586],{"class":105,"line":219},[103,575,576],{"class":210},"      icon",[103,578,92],{"class":113},[103,580,127],{"class":113},[103,582,583],{"class":130},"i-heroicons-sparkles",[103,585,228],{"class":113},[103,587,231],{"class":113},[103,589,590,593,595,597,600],{"class":105,"line":234},[103,591,592],{"class":210},"      label",[103,594,92],{"class":113},[103,596,127],{"class":113},[103,598,599],{"class":130},"Custom Class",[103,601,134],{"class":113},[103,603,604],{"class":105,"line":261},[103,605,606],{"class":113},"    }\n",[103,608,609],{"class":105,"line":282},[103,610,611],{"class":117},"  ]\n",[103,613,614],{"class":105,"line":290},[103,615,616],{"class":113},"}\n",[72,618,619,620,622,623,344],{},"When this plugin is active, discovery skips the ",[89,621,460],{}," core. This lets a plugin take over a core feature without changing source files under ",[89,624,625],{},"cores\u002Fprogram",[81,627,629],{"id":628},"runtime-registry","Runtime Registry",[72,631,632,633,636,637,344],{},"In addition to build-time discovery in ",[89,634,635],{},"shared\u002Futils\u002Flayers.ts",", the app has a runtime registry in ",[89,638,639],{},"app\u002Fplugins\u002Fplugin-registry.ts",[72,641,642],{},"The registry:",[375,644,645,655,667,670],{},[333,646,647,648,651,652,383],{},"reads all ",[89,649,650],{},"plugins\u002F*\u002Fplugin.config.ts"," files with ",[89,653,654],{},"import.meta.glob",[333,656,657,658,660,661,663,664,383],{},"validates ",[89,659,416],{},", ",[89,662,407],{},", and ",[89,665,666],{},"routes",[333,668,669],{},"only registers enabled plugins;",[333,671,672,673,676],{},"exposes ",[89,674,675],{},"usePluginRegistry()"," for other composables such as the sidebar.",[81,678,680],{"id":679},"sidebar-in-the-architecture","Sidebar in the Architecture",[72,682,683,684,687],{},"The sidebar is not hardcoded in the component. ",[89,685,686],{},"app\u002Fcomposables\u002FuseSidebarMenu.ts"," builds menu items from:",[375,689,690,695,700,707],{},[333,691,692,694],{},[89,693,393],{}," for core menus;",[333,696,697,699],{},[89,698,444],{}," for plugin menus;",[333,701,702,703,706],{},"optional ",[89,704,705],{},"sidebar.ts"," files from cores\u002Fplugins for dynamic menus;",[333,708,709],{},"the list of cores replaced by active plugins.",[72,711,712],{},"This keeps routes, plugin registry, and dashboard navigation aligned with feature configuration.",[714,715],"hr",{},[72,717,718,719,723],{},"Learn more about the ",[720,721,38],"a",{"href":722},"\u002Fplugin-system\u002Fbasics"," to see how customization is done.",[725,726,727],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":99,"searchDepth":137,"depth":137,"links":729},[730,731,732,733,734,735],{"id":83,"depth":137,"text":84},{"id":369,"depth":137,"text":370},{"id":424,"depth":137,"text":425},{"id":464,"depth":137,"text":465},{"id":628,"depth":137,"text":629},{"id":679,"depth":137,"text":680},"Alkademi LMS Open Source is built as a modular monolith. Every feature still runs inside one Nuxt application, but each domain feature is isolated as a Nuxt Layer so it can be maintained, replaced, or extended more easily.","md",null,{},{"icon":36},{"title":33,"description":736},"_6fDOdwP7Prus_-iwl05DEM1_R6fCtShng4_a5g_EIU",[744,746],{"title":21,"path":22,"stem":23,"description":745,"icon":24,"children":-1},"Alkademi LMS Open Source uses a Nuxt Layers based modular monolith structure. The application is still one codebase and one build, but features are separated into cores\u002F and plugins\u002F.",{"title":38,"path":44,"stem":45,"description":747,"icon":39,"children":-1},"A plugin is an additional Nuxt Layer placed under plugins\u002F. Plugins are used to add new features, replace selected cores, or enrich navigation without changing source files under cores\u002F.",1780026928387]