@intranefr/superbackend 1.4.4 → 1.5.1
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/.env.example +5 -0
- package/README.md +11 -0
- package/index.js +39 -1
- package/package.json +11 -3
- package/public/sdk/ui-components.iife.js +191 -0
- package/sdk/ui-components/browser/src/index.js +228 -0
- package/src/admin/endpointRegistry.js +120 -0
- package/src/controllers/admin.controller.js +111 -5
- package/src/controllers/adminBlockDefinitions.controller.js +127 -0
- package/src/controllers/adminBlockDefinitionsAi.controller.js +54 -0
- package/src/controllers/adminCache.controller.js +342 -0
- package/src/controllers/adminContextBlockDefinitions.controller.js +141 -0
- package/src/controllers/adminCrons.controller.js +388 -0
- package/src/controllers/adminDbBrowser.controller.js +124 -0
- package/src/controllers/adminEjsVirtual.controller.js +13 -3
- package/src/controllers/adminHeadless.controller.js +91 -2
- package/src/controllers/adminHealthChecks.controller.js +570 -0
- package/src/controllers/adminI18n.controller.js +51 -29
- package/src/controllers/adminLlm.controller.js +126 -2
- package/src/controllers/adminPages.controller.js +720 -0
- package/src/controllers/adminPagesContextBlocksAi.controller.js +54 -0
- package/src/controllers/adminProxy.controller.js +113 -0
- package/src/controllers/adminRateLimits.controller.js +138 -0
- package/src/controllers/adminRbac.controller.js +803 -0
- package/src/controllers/adminScripts.controller.js +320 -0
- package/src/controllers/adminSeoConfig.controller.js +71 -48
- package/src/controllers/adminTerminals.controller.js +39 -0
- package/src/controllers/adminUiComponents.controller.js +315 -0
- package/src/controllers/adminUiComponentsAi.controller.js +34 -0
- package/src/controllers/blogAdmin.controller.js +279 -0
- package/src/controllers/blogAiAdmin.controller.js +224 -0
- package/src/controllers/blogAutomationAdmin.controller.js +141 -0
- package/src/controllers/blogInternal.controller.js +26 -0
- package/src/controllers/blogPublic.controller.js +89 -0
- package/src/controllers/fileManager.controller.js +190 -0
- package/src/controllers/fileManagerStoragePolicy.controller.js +23 -0
- package/src/controllers/healthChecksPublic.controller.js +196 -0
- package/src/controllers/metrics.controller.js +64 -4
- package/src/controllers/orgAdmin.controller.js +366 -0
- package/src/controllers/uiComponentsPublic.controller.js +118 -0
- package/src/middleware/auth.js +7 -0
- package/src/middleware/internalCronAuth.js +29 -0
- package/src/middleware/rbac.js +62 -0
- package/src/middleware.js +879 -56
- package/src/models/BlockDefinition.js +27 -0
- package/src/models/BlogAutomationLock.js +14 -0
- package/src/models/BlogAutomationRun.js +39 -0
- package/src/models/BlogPost.js +42 -0
- package/src/models/CacheEntry.js +26 -0
- package/src/models/ConsoleEntry.js +32 -0
- package/src/models/ConsoleLog.js +23 -0
- package/src/models/ContextBlockDefinition.js +33 -0
- package/src/models/CronExecution.js +47 -0
- package/src/models/CronJob.js +70 -0
- package/src/models/ExternalDbConnection.js +49 -0
- package/src/models/FileEntry.js +22 -0
- package/src/models/HeadlessModelDefinition.js +10 -0
- package/src/models/HealthAutoHealAttempt.js +57 -0
- package/src/models/HealthCheck.js +132 -0
- package/src/models/HealthCheckRun.js +51 -0
- package/src/models/HealthIncident.js +49 -0
- package/src/models/Page.js +95 -0
- package/src/models/PageCollection.js +42 -0
- package/src/models/ProxyEntry.js +66 -0
- package/src/models/RateLimitCounter.js +19 -0
- package/src/models/RateLimitMetricBucket.js +20 -0
- package/src/models/RbacGrant.js +25 -0
- package/src/models/RbacGroup.js +16 -0
- package/src/models/RbacGroupMember.js +13 -0
- package/src/models/RbacGroupRole.js +13 -0
- package/src/models/RbacRole.js +25 -0
- package/src/models/RbacUserRole.js +13 -0
- package/src/models/ScriptDefinition.js +42 -0
- package/src/models/ScriptRun.js +22 -0
- package/src/models/UiComponent.js +29 -0
- package/src/models/UiComponentProject.js +26 -0
- package/src/models/UiComponentProjectComponent.js +18 -0
- package/src/routes/admin.routes.js +1 -0
- package/src/routes/adminBlog.routes.js +21 -0
- package/src/routes/adminBlogAi.routes.js +16 -0
- package/src/routes/adminBlogAutomation.routes.js +27 -0
- package/src/routes/adminCache.routes.js +20 -0
- package/src/routes/adminConsoleManager.routes.js +302 -0
- package/src/routes/adminCrons.routes.js +25 -0
- package/src/routes/adminDbBrowser.routes.js +65 -0
- package/src/routes/adminEjsVirtual.routes.js +2 -1
- package/src/routes/adminHeadless.routes.js +8 -1
- package/src/routes/adminHealthChecks.routes.js +28 -0
- package/src/routes/adminI18n.routes.js +4 -3
- package/src/routes/adminLlm.routes.js +4 -2
- package/src/routes/adminPages.routes.js +55 -0
- package/src/routes/adminProxy.routes.js +15 -0
- package/src/routes/adminRateLimits.routes.js +17 -0
- package/src/routes/adminRbac.routes.js +38 -0
- package/src/routes/adminScripts.routes.js +21 -0
- package/src/routes/adminSeoConfig.routes.js +5 -4
- package/src/routes/adminTerminals.routes.js +13 -0
- package/src/routes/adminUiComponents.routes.js +30 -0
- package/src/routes/blogInternal.routes.js +14 -0
- package/src/routes/blogPublic.routes.js +9 -0
- package/src/routes/fileManager.routes.js +62 -0
- package/src/routes/fileManagerStoragePolicy.routes.js +9 -0
- package/src/routes/healthChecksPublic.routes.js +9 -0
- package/src/routes/log.routes.js +43 -60
- package/src/routes/metrics.routes.js +4 -2
- package/src/routes/orgAdmin.routes.js +6 -0
- package/src/routes/pages.routes.js +123 -0
- package/src/routes/proxy.routes.js +46 -0
- package/src/routes/rbac.routes.js +47 -0
- package/src/routes/uiComponentsPublic.routes.js +9 -0
- package/src/routes/webhook.routes.js +2 -1
- package/src/routes/workflows.routes.js +4 -0
- package/src/services/blockDefinitionsAi.service.js +247 -0
- package/src/services/blog.service.js +99 -0
- package/src/services/blogAutomation.service.js +978 -0
- package/src/services/blogCronsBootstrap.service.js +184 -0
- package/src/services/blogPublishing.service.js +58 -0
- package/src/services/cacheLayer.service.js +696 -0
- package/src/services/consoleManager.service.js +700 -0
- package/src/services/consoleOverride.service.js +6 -1
- package/src/services/cronScheduler.service.js +350 -0
- package/src/services/dbBrowser.service.js +536 -0
- package/src/services/ejsVirtual.service.js +102 -32
- package/src/services/fileManager.service.js +475 -0
- package/src/services/fileManagerStoragePolicy.service.js +285 -0
- package/src/services/headlessExternalModels.service.js +292 -0
- package/src/services/headlessModels.service.js +26 -6
- package/src/services/healthChecks.service.js +650 -0
- package/src/services/healthChecksBootstrap.service.js +109 -0
- package/src/services/healthChecksScheduler.service.js +106 -0
- package/src/services/llmDefaults.service.js +190 -0
- package/src/services/migrationAssets/s3.js +2 -2
- package/src/services/pages.service.js +602 -0
- package/src/services/pagesContext.service.js +331 -0
- package/src/services/pagesContextBlocksAi.service.js +349 -0
- package/src/services/proxy.service.js +535 -0
- package/src/services/rateLimiter.service.js +623 -0
- package/src/services/rbac.service.js +212 -0
- package/src/services/scriptsRunner.service.js +259 -0
- package/src/services/terminals.service.js +152 -0
- package/src/services/terminalsWs.service.js +100 -0
- package/src/services/uiComponentsAi.service.js +299 -0
- package/src/services/uiComponentsCrypto.service.js +39 -0
- package/src/services/workflow.service.js +23 -8
- package/src/utils/orgRoles.js +14 -0
- package/src/utils/rbac/engine.js +60 -0
- package/src/utils/rbac/rightsRegistry.js +29 -0
- package/views/admin-blog-automation.ejs +877 -0
- package/views/admin-blog-edit.ejs +542 -0
- package/views/admin-blog.ejs +399 -0
- package/views/admin-cache.ejs +681 -0
- package/views/admin-console-manager.ejs +680 -0
- package/views/admin-crons.ejs +645 -0
- package/views/admin-db-browser.ejs +445 -0
- package/views/admin-ejs-virtual.ejs +16 -10
- package/views/admin-file-manager.ejs +942 -0
- package/views/admin-headless.ejs +294 -24
- package/views/admin-health-checks.ejs +725 -0
- package/views/admin-i18n.ejs +59 -5
- package/views/admin-llm.ejs +99 -1
- package/views/admin-organizations.ejs +528 -10
- package/views/admin-pages.ejs +2424 -0
- package/views/admin-proxy.ejs +491 -0
- package/views/admin-rate-limiter.ejs +625 -0
- package/views/admin-rbac.ejs +1331 -0
- package/views/admin-scripts.ejs +497 -0
- package/views/admin-seo-config.ejs +61 -7
- package/views/admin-terminals.ejs +328 -0
- package/views/admin-ui-components.ejs +741 -0
- package/views/admin-users.ejs +261 -4
- package/views/admin-workflows.ejs +7 -7
- package/views/file-manager.ejs +866 -0
- package/views/pages/blocks/contact.ejs +27 -0
- package/views/pages/blocks/cta.ejs +18 -0
- package/views/pages/blocks/faq.ejs +20 -0
- package/views/pages/blocks/features.ejs +19 -0
- package/views/pages/blocks/hero.ejs +13 -0
- package/views/pages/blocks/html.ejs +5 -0
- package/views/pages/blocks/image.ejs +14 -0
- package/views/pages/blocks/testimonials.ejs +26 -0
- package/views/pages/blocks/text.ejs +10 -0
- package/views/pages/layouts/default.ejs +51 -0
- package/views/pages/layouts/minimal.ejs +42 -0
- package/views/pages/layouts/sidebar.ejs +54 -0
- package/views/pages/partials/footer.ejs +13 -0
- package/views/pages/partials/header.ejs +12 -0
- package/views/pages/partials/sidebar.ejs +8 -0
- package/views/pages/runtime/page.ejs +10 -0
- package/views/pages/templates/article.ejs +20 -0
- package/views/pages/templates/default.ejs +12 -0
- package/views/pages/templates/landing.ejs +14 -0
- package/views/pages/templates/listing.ejs +15 -0
- package/views/partials/admin-image-upload-modal.ejs +221 -0
- package/views/partials/dashboard/nav-items.ejs +14 -0
- package/views/partials/llm-provider-model-picker.ejs +183 -0
|
@@ -8,7 +8,7 @@ const VirtualEjsFileVersion = require('../models/VirtualEjsFileVersion');
|
|
|
8
8
|
const VirtualEjsGroupChange = require('../models/VirtualEjsGroupChange');
|
|
9
9
|
|
|
10
10
|
const llmService = require('./llm.service');
|
|
11
|
-
const {
|
|
11
|
+
const { resolveLlmProviderModel } = require('./llmDefaults.service');
|
|
12
12
|
const { createAuditEvent } = require('./audit.service');
|
|
13
13
|
|
|
14
14
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -151,6 +151,7 @@ async function resolveTemplateSource({ viewsRoot, relPath, allowDb = true }) {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
if (fileDoc && fileDoc.enabled === true && typeof fileDoc.content === 'string' && fileDoc.content.trim() !== '') {
|
|
154
|
+
console.log(`[ejsVirtual] Resolved ${normalized} from DB (${fileDoc.content.length} chars)`);
|
|
154
155
|
return {
|
|
155
156
|
relPath: normalized,
|
|
156
157
|
source: 'db',
|
|
@@ -160,6 +161,7 @@ async function resolveTemplateSource({ viewsRoot, relPath, allowDb = true }) {
|
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
const fsContent = await readFsView(viewsRoot, normalized);
|
|
164
|
+
console.log(`[ejsVirtual] Resolved ${normalized} from FS (${fsContent?.length || 0} chars)`);
|
|
163
165
|
return {
|
|
164
166
|
relPath: normalized,
|
|
165
167
|
source: 'fs',
|
|
@@ -259,25 +261,11 @@ function parseMultiFilePatch(text) {
|
|
|
259
261
|
}
|
|
260
262
|
|
|
261
263
|
async function resolveLlmDefaults({ providerKey, model }) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const envProvider = String(process.env.DEFAULT_LLM_PROVIDER_KEY || '').trim();
|
|
269
|
-
const envModel = String(process.env.DEFAULT_LLM_MODEL || '').trim();
|
|
270
|
-
|
|
271
|
-
const resolvedProviderKey = uiProvider || settingProvider || envProvider;
|
|
272
|
-
if (!resolvedProviderKey) {
|
|
273
|
-
const err = new Error('Missing LLM providerKey (configure ejsVirtual.ai.providerKey or DEFAULT_LLM_PROVIDER_KEY, or send from UI)');
|
|
274
|
-
err.code = 'VALIDATION';
|
|
275
|
-
throw err;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const resolvedModel = uiModel || settingModel || envModel || 'x-ai/grok-code-fast-1';
|
|
279
|
-
|
|
280
|
-
return { providerKey: resolvedProviderKey, model: resolvedModel };
|
|
264
|
+
return resolveLlmProviderModel({
|
|
265
|
+
systemKey: 'ejsVirtual.vibe.apply',
|
|
266
|
+
providerKey,
|
|
267
|
+
model,
|
|
268
|
+
});
|
|
281
269
|
}
|
|
282
270
|
|
|
283
271
|
async function vibeEdit({ prompt, paths, providerKey, model, viewsRoot, actor }) {
|
|
@@ -492,8 +480,9 @@ function resolveIncludeRelPath({ viewsRoot, parentAbsPath, includePath }) {
|
|
|
492
480
|
}
|
|
493
481
|
|
|
494
482
|
const normalizedAbs = path.resolve(includeAbs);
|
|
495
|
-
|
|
496
|
-
|
|
483
|
+
// Use path.sep to ensure we match the full directory name
|
|
484
|
+
if (!normalizedAbs.startsWith(baseRoot + path.sep) && normalizedAbs !== baseRoot) {
|
|
485
|
+
const err = new Error(`Include path escapes views root: ${normalizedAbs} (root: ${baseRoot})`);
|
|
497
486
|
err.code = 'VALIDATION';
|
|
498
487
|
throw err;
|
|
499
488
|
}
|
|
@@ -505,24 +494,62 @@ function resolveIncludeRelPath({ viewsRoot, parentAbsPath, includePath }) {
|
|
|
505
494
|
async function preloadTemplatesForRender({ viewsRoot, entryRelPath }) {
|
|
506
495
|
const templatesByRelPath = new Map();
|
|
507
496
|
const absByRelPath = new Map();
|
|
508
|
-
const queue = [entryRelPath];
|
|
509
497
|
const seen = new Set();
|
|
510
498
|
|
|
499
|
+
// 1. Pre-populate with all enabled DB overrides.
|
|
500
|
+
// This ensures dynamic includes (like blocks) that exist in DB are available.
|
|
501
|
+
try {
|
|
502
|
+
const dbFiles = await VirtualEjsFile.find({ enabled: true }).lean();
|
|
503
|
+
if (dbFiles && dbFiles.length > 0) {
|
|
504
|
+
console.log(`[ejsVirtual] Preloading ${dbFiles.length} enabled DB templates: ${dbFiles.map(f => `${f.path} (${f.content?.length || 0} chars)`).join(', ')}`);
|
|
505
|
+
for (const f of dbFiles) {
|
|
506
|
+
templatesByRelPath.set(f.path, {
|
|
507
|
+
relPath: f.path,
|
|
508
|
+
source: 'db',
|
|
509
|
+
content: f.content,
|
|
510
|
+
updatedAt: f.updatedAt,
|
|
511
|
+
});
|
|
512
|
+
absByRelPath.set(f.path, resolveAbsPath(viewsRoot, f.path));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
} catch (err) {
|
|
516
|
+
console.error('[ejsVirtual] Failed to preload DB templates:', err);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const queue = [entryRelPath];
|
|
520
|
+
// Also crawl static includes of DB-overridden files
|
|
521
|
+
for (const p of templatesByRelPath.keys()) {
|
|
522
|
+
queue.push(p);
|
|
523
|
+
}
|
|
524
|
+
|
|
511
525
|
while (queue.length > 0) {
|
|
512
526
|
const relPath = queue.shift();
|
|
513
527
|
if (!relPath || seen.has(relPath)) continue;
|
|
514
528
|
seen.add(relPath);
|
|
515
529
|
|
|
516
|
-
|
|
517
|
-
|
|
530
|
+
let src = templatesByRelPath.get(relPath);
|
|
531
|
+
if (!src) {
|
|
532
|
+
try {
|
|
533
|
+
src = await resolveTemplateSource({ viewsRoot, relPath, allowDb: true });
|
|
534
|
+
templatesByRelPath.set(relPath, src);
|
|
535
|
+
} catch (err) {
|
|
536
|
+
// If not found, we still want to keep going, it might be on FS or missing
|
|
537
|
+
console.warn(`[ejsVirtual] Failed to resolve template source for ${relPath}:`, err.message);
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
518
541
|
|
|
519
|
-
|
|
542
|
+
const abs = absByRelPath.get(relPath) || resolveAbsPath(viewsRoot, relPath);
|
|
520
543
|
absByRelPath.set(relPath, abs);
|
|
521
544
|
|
|
522
545
|
const includes = extractIncludePaths(src.content);
|
|
523
546
|
for (const inc of includes) {
|
|
524
|
-
|
|
525
|
-
|
|
547
|
+
try {
|
|
548
|
+
const incRel = resolveIncludeRelPath({ viewsRoot, parentAbsPath: abs, includePath: inc });
|
|
549
|
+
if (incRel) queue.push(incRel);
|
|
550
|
+
} catch (err) {
|
|
551
|
+
console.warn(`[ejsVirtual] Failed to resolve include path "${inc}" in ${relPath}:`, err.message);
|
|
552
|
+
}
|
|
526
553
|
}
|
|
527
554
|
}
|
|
528
555
|
|
|
@@ -533,18 +560,26 @@ async function renderToString(res, viewPath, data = {}, options = {}) {
|
|
|
533
560
|
const relPath = normalizeViewPath(viewPath);
|
|
534
561
|
const viewsRoot = path.resolve(options.viewsRoot || (res && res.app ? res.app.get('views') : null) || getDefaultViewsRoot());
|
|
535
562
|
|
|
563
|
+
console.log(`[ejsVirtual] Rendering ${relPath} (viewsRoot: ${viewsRoot})`);
|
|
564
|
+
|
|
536
565
|
const { templatesByRelPath, absByRelPath } = await preloadTemplatesForRender({
|
|
537
566
|
viewsRoot,
|
|
538
567
|
entryRelPath: relPath,
|
|
539
568
|
});
|
|
540
569
|
|
|
570
|
+
if (templatesByRelPath.size === 0) {
|
|
571
|
+
console.warn(`[ejsVirtual] No templates preloaded for ${relPath}`);
|
|
572
|
+
}
|
|
573
|
+
|
|
541
574
|
const entry = templatesByRelPath.get(relPath);
|
|
542
575
|
if (!entry) {
|
|
543
|
-
const err = new Error(
|
|
576
|
+
const err = new Error(`Template not found: ${relPath}`);
|
|
544
577
|
err.code = 'NOT_FOUND';
|
|
545
578
|
throw err;
|
|
546
579
|
}
|
|
547
580
|
|
|
581
|
+
console.log(`[ejsVirtual] Entry template ${relPath} source: ${entry.source}`);
|
|
582
|
+
|
|
548
583
|
const entryAbs = absByRelPath.get(relPath) || resolveAbsPath(viewsRoot, relPath);
|
|
549
584
|
|
|
550
585
|
const versionKey = entry.source === 'db'
|
|
@@ -553,6 +588,13 @@ async function renderToString(res, viewPath, data = {}, options = {}) {
|
|
|
553
588
|
|
|
554
589
|
const cached = getCachedTemplate(entry.relPath, versionKey);
|
|
555
590
|
const entryTemplate = cached ? cached.template : entry.content;
|
|
591
|
+
|
|
592
|
+
if (!entryTemplate || entryTemplate.trim() === '') {
|
|
593
|
+
console.warn(`[ejsVirtual] Entry template ${relPath} is empty! Source: ${entry.source}`);
|
|
594
|
+
} else {
|
|
595
|
+
console.log(`[ejsVirtual] Entry template ${relPath} content start: ${entryTemplate.substring(0, 50).replace(/\n/g, '\\n')}...`);
|
|
596
|
+
}
|
|
597
|
+
|
|
556
598
|
if (!cached) {
|
|
557
599
|
setCachedTemplate(entry.relPath, versionKey, entry.content, {});
|
|
558
600
|
}
|
|
@@ -577,10 +619,29 @@ async function renderToString(res, viewPath, data = {}, options = {}) {
|
|
|
577
619
|
|
|
578
620
|
const incAbs = incRel ? absByRelPath.get(incRel) || resolveAbsPath(viewsRoot, incRel) : parentAbs;
|
|
579
621
|
const src = incRel ? templatesByRelPath.get(incRel) : null;
|
|
622
|
+
|
|
580
623
|
if (!incRel || !src) {
|
|
581
|
-
|
|
624
|
+
// If not in preloaded map, it might be on FS.
|
|
625
|
+
// Check if it exists on FS to avoid ENOENT crash
|
|
626
|
+
try {
|
|
627
|
+
if (incAbs && fs.existsSync(incAbs) && fs.statSync(incAbs).isFile()) {
|
|
628
|
+
console.log(`[ejsVirtual] Include ${incRel || includePath} found on FS (fallback)`);
|
|
629
|
+
return { filename: incAbs };
|
|
630
|
+
}
|
|
631
|
+
} catch (e) {
|
|
632
|
+
// ignore errors
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// If it doesn't exist anywhere, return a comment instead of crashing
|
|
636
|
+
console.warn(`[ejsVirtual] Include not found: ${incRel || includePath} (parent: ${parentAbs})`);
|
|
637
|
+
return { template: `<!-- Include not found: ${incRel || includePath} -->` };
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (src.content === undefined || src.content === null || src.content.trim() === '') {
|
|
641
|
+
console.warn(`[ejsVirtual] Including ${incRel} from ${src.source} but content is empty`);
|
|
582
642
|
}
|
|
583
643
|
|
|
644
|
+
console.log(`[ejsVirtual] Including ${incRel} from ${src.source}`);
|
|
584
645
|
return {
|
|
585
646
|
filename: incAbs,
|
|
586
647
|
template: src.content,
|
|
@@ -589,11 +650,19 @@ async function renderToString(res, viewPath, data = {}, options = {}) {
|
|
|
589
650
|
|
|
590
651
|
await recordIntegratedUsage(relPath, null);
|
|
591
652
|
|
|
592
|
-
|
|
653
|
+
const rendered = ejs.render(entryTemplate, { ...(data || {}), ...(res?.locals || {}) }, {
|
|
593
654
|
filename: entryAbs,
|
|
594
655
|
async: false,
|
|
595
656
|
includer,
|
|
596
657
|
});
|
|
658
|
+
|
|
659
|
+
if (!rendered || rendered.trim() === '') {
|
|
660
|
+
console.warn(`[ejsVirtual] Rendered content for ${relPath} is empty!`);
|
|
661
|
+
} else {
|
|
662
|
+
console.log(`[ejsVirtual] Rendered content for ${relPath} length: ${rendered.length} chars`);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return rendered;
|
|
597
666
|
}
|
|
598
667
|
|
|
599
668
|
async function render(res, viewPath, data = {}, options = {}) {
|
|
@@ -608,7 +677,8 @@ module.exports = {
|
|
|
608
677
|
readFsView,
|
|
609
678
|
invalidateCacheForPath,
|
|
610
679
|
clearCache,
|
|
680
|
+
recordIntegratedUsage,
|
|
611
681
|
vibeEdit,
|
|
612
682
|
renderToString,
|
|
613
683
|
render,
|
|
614
|
-
};
|
|
684
|
+
};
|