meno-core 1.0.50 → 1.0.52
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/build-static.ts +8 -1
- package/dist/bin/cli.js +1 -1
- package/dist/build-static.js +3 -3
- package/dist/chunks/{chunk-PQ2HRXDR.js → chunk-2MHDV5BF.js} +11 -1
- package/dist/chunks/chunk-2MHDV5BF.js.map +7 -0
- package/dist/chunks/{chunk-YWJJD5D6.js → chunk-A725KYFK.js} +36 -17
- package/dist/chunks/{chunk-YWJJD5D6.js.map → chunk-A725KYFK.js.map} +3 -3
- package/dist/chunks/{chunk-CVLFID6V.js → chunk-CXCBV2M7.js} +65 -14
- package/dist/chunks/chunk-CXCBV2M7.js.map +7 -0
- package/dist/chunks/{chunk-4OFZP5NQ.js → chunk-HNLUO36W.js} +15 -4
- package/dist/chunks/chunk-HNLUO36W.js.map +7 -0
- package/dist/chunks/{chunk-56EUSC6D.js → chunk-LHLHPYSP.js} +4 -4
- package/dist/chunks/{chunk-56EUSC6D.js.map → chunk-LHLHPYSP.js.map} +2 -2
- package/dist/chunks/{configService-VOY2MY2K.js → configService-R3OGU2UD.js} +2 -2
- package/dist/entries/server-router.js +3 -3
- package/dist/lib/server/index.js +5 -5
- package/lib/server/routes/pages.ts +37 -2
- package/lib/server/services/cmsService.ts +21 -0
- package/lib/server/services/configService.ts +20 -0
- package/lib/server/ssr/buildErrorOverlay.ts +22 -4
- package/lib/server/ssr/errorOverlay.ts +11 -3
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +165 -0
- package/lib/server/ssr/htmlGenerator.ts +25 -6
- package/lib/server/ssr/liveReloadIntegration.test.ts +3 -1
- package/lib/server/ssr/metaTagGenerator.ts +54 -6
- package/lib/server/ssr/ssrRenderer.ts +1 -0
- package/lib/server/ssrRenderer.test.ts +157 -2
- package/package.json +1 -1
- package/dist/chunks/chunk-4OFZP5NQ.js.map +0 -7
- package/dist/chunks/chunk-CVLFID6V.js.map +0 -7
- package/dist/chunks/chunk-PQ2HRXDR.js.map +0 -7
- /package/dist/chunks/{configService-VOY2MY2K.js.map → configService-R3OGU2UD.js.map} +0 -0
package/build-static.ts
CHANGED
|
@@ -812,9 +812,16 @@ export async function buildStaticPages(): Promise<void> {
|
|
|
812
812
|
const extraFonts = cspConfig.fontSrc?.join(' ') || '';
|
|
813
813
|
const extraImgs = cspConfig.imgSrc?.join(' ') || '';
|
|
814
814
|
|
|
815
|
+
// Production-built pages have NO executable inline scripts: page config,
|
|
816
|
+
// component JS, form handler, and MenoFilter all live in external
|
|
817
|
+
// /_scripts/{hash}.js files (returnSeparateJS: true), and the Meno
|
|
818
|
+
// badge's hover effect is pure CSS. Only `<script type="application/json">`
|
|
819
|
+
// remains inline (used as a data island for MenoFilter), which CSP
|
|
820
|
+
// ignores because it has no executable code. So we can drop
|
|
821
|
+
// `'unsafe-inline'` entirely from script-src in the generated _headers.
|
|
815
822
|
const cspDirectives = [
|
|
816
823
|
"default-src 'self'",
|
|
817
|
-
`script-src 'self'
|
|
824
|
+
`script-src 'self' https://f.vimeocdn.com https://player.vimeo.com https://www.youtube.com https://s.ytimg.com ${extraScripts}`.trim(),
|
|
818
825
|
`style-src 'self' 'unsafe-inline' https://f.vimeocdn.com ${extraStyles}`.trim(),
|
|
819
826
|
`img-src 'self' data: https: ${extraImgs}`.trim(),
|
|
820
827
|
`connect-src 'self' https://vimeo.com https://*.vimeocdn.com ${extraConnect}`.trim(),
|
package/dist/bin/cli.js
CHANGED
package/dist/build-static.js
CHANGED
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
hashContent,
|
|
10
10
|
injectTrackingScript,
|
|
11
11
|
isCMSPage
|
|
12
|
-
} from "./chunks/chunk-
|
|
13
|
-
import "./chunks/chunk-
|
|
14
|
-
import "./chunks/chunk-
|
|
12
|
+
} from "./chunks/chunk-LHLHPYSP.js";
|
|
13
|
+
import "./chunks/chunk-CXCBV2M7.js";
|
|
14
|
+
import "./chunks/chunk-2MHDV5BF.js";
|
|
15
15
|
import "./chunks/chunk-I7YIGZXT.js";
|
|
16
16
|
import "./chunks/chunk-WQFG7PAH.js";
|
|
17
17
|
import "./chunks/chunk-J23ZX5AP.js";
|
|
@@ -164,6 +164,16 @@ var ConfigService = class {
|
|
|
164
164
|
}
|
|
165
165
|
return this.config.icons;
|
|
166
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Get site-wide social configuration
|
|
169
|
+
* Returns empty object if not configured
|
|
170
|
+
*/
|
|
171
|
+
getSocial() {
|
|
172
|
+
if (!this.config?.social || typeof this.config.social !== "object") {
|
|
173
|
+
return {};
|
|
174
|
+
}
|
|
175
|
+
return this.config.social;
|
|
176
|
+
}
|
|
167
177
|
/**
|
|
168
178
|
* Get libraries configuration
|
|
169
179
|
* Returns empty arrays if not configured
|
|
@@ -238,4 +248,4 @@ export {
|
|
|
238
248
|
ConfigService,
|
|
239
249
|
configService
|
|
240
250
|
};
|
|
241
|
-
//# sourceMappingURL=chunk-
|
|
251
|
+
//# sourceMappingURL=chunk-2MHDV5BF.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../lib/server/services/configService.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Config Service\n * Centralized configuration loading and access\n *\n * Consolidates multiple config loaders into a single service that loads\n * the project.config.json file once and exposes typed sections.\n */\n\nimport type { BreakpointConfig, BreakpointConfigInput, BreakpointEntry } from '../../shared/breakpoints';\nimport { DEFAULT_BREAKPOINTS, normalizeBreakpointConfig } from '../../shared/breakpoints';\nimport type { ResponsiveScales, BreakpointScales } from '../../shared/responsiveScaling';\nimport { DEFAULT_RESPONSIVE_SCALES } from '../../shared/responsiveScaling';\nimport type { I18nConfig } from '../../shared/types/components';\nimport type { LibrariesConfig, JSLibraryConfig, CSSLibraryConfig } from '../../shared/types/libraries';\nimport type { CSPConfig } from '../../shared/types/config';\nimport type { CustomCodeConfig } from '../../shared/types/api';\nimport type { RemConversionConfig } from '../../shared/pxToRem';\nimport { DEFAULT_REM_CONFIG } from '../../shared/pxToRem';\nimport { DEFAULT_I18N_CONFIG, migrateI18nConfig } from '../../shared/i18n';\nimport { projectPaths } from '../projectContext';\nimport { readTextFile, fileExists } from '../runtime';\n\n/**\n * Icons configuration\n */\nexport interface IconsConfig {\n favicon?: string;\n faviconDark?: string;\n appleTouchIcon?: string;\n}\n\n/**\n * Site-wide social configuration\n */\nexport interface SocialConfig {\n twitterHandle?: string;\n}\n\n/**\n * Raw project config structure from project.config.json\n */\nexport type ImageFormat = 'webp' | 'avif';\n\ninterface RawProjectConfig {\n breakpoints?: BreakpointConfigInput;\n responsiveScales?: Partial<ResponsiveScales>;\n i18n?: unknown;\n icons?: IconsConfig;\n social?: SocialConfig;\n libraries?: LibrariesConfig;\n csp?: CSPConfig;\n baseComponent?: string;\n imageFormat?: ImageFormat;\n remConversion?: Partial<RemConversionConfig>;\n customCode?: CustomCodeConfig;\n showMenoBadge?: boolean;\n}\n\n/**\n * ConfigService\n * Loads project configuration once and provides typed access to sections\n */\nexport class ConfigService {\n private config: RawProjectConfig | null = null;\n private loaded = false;\n\n /**\n * Load configuration from project.config.json\n * Safe to call multiple times - only loads once\n */\n async load(): Promise<void> {\n if (this.loaded) {\n return;\n }\n\n try {\n if (await fileExists(projectPaths.config())) {\n const content = await readTextFile(projectPaths.config());\n this.config = JSON.parse(content);\n }\n } catch {\n // Fall through to defaults\n this.config = null;\n }\n\n this.loaded = true;\n }\n\n /**\n * Check if configuration has been loaded\n */\n isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Reset the service (for testing)\n */\n reset(): void {\n this.config = null;\n this.loaded = false;\n }\n\n /**\n * Get breakpoint configuration\n * Returns validated and normalized breakpoints (always object format)\n * Supports both legacy format { tablet: 1024 } and new format { tablet: { breakpoint: 1024, previewPoint: 768 } }\n */\n getBreakpoints(): BreakpointConfig {\n if (!this.config?.breakpoints || typeof this.config.breakpoints !== 'object') {\n return { ...DEFAULT_BREAKPOINTS };\n }\n\n // Validate breakpoint values before normalization\n const validInput: BreakpointConfigInput = {};\n for (const [key, value] of Object.entries(this.config.breakpoints)) {\n if (typeof value === 'number' && value > 0) {\n // Legacy format: number\n validInput[key] = value;\n } else if (typeof value === 'object' && value !== null) {\n // New format: object with breakpoint and optional previewPoint\n const entry = value as BreakpointEntry;\n if (typeof entry.breakpoint === 'number' && entry.breakpoint > 0) {\n validInput[key] = {\n breakpoint: entry.breakpoint,\n previewPoint: typeof entry.previewPoint === 'number' && entry.previewPoint > 0\n ? entry.previewPoint\n : entry.breakpoint,\n };\n }\n }\n }\n\n // Return normalized breakpoints or defaults if none valid\n if (Object.keys(validInput).length === 0) {\n return { ...DEFAULT_BREAKPOINTS };\n }\n\n return normalizeBreakpointConfig(validInput);\n }\n\n /**\n * Get i18n configuration\n * Automatically migrates old string[] format to LocaleConfig[] format\n */\n getI18n(): I18nConfig {\n if (!this.config?.i18n) {\n return { ...DEFAULT_I18N_CONFIG };\n }\n\n return migrateI18nConfig(this.config.i18n);\n }\n\n /**\n * Deep merge scale categories, preserving user-defined breakpoints\n * while filling in missing values from defaults\n */\n private mergeScaleCategory(\n userScales: BreakpointScales | undefined,\n defaultScales: BreakpointScales | undefined\n ): BreakpointScales | undefined {\n if (!userScales && !defaultScales) return undefined;\n if (!userScales) return defaultScales ? { ...defaultScales } : undefined;\n if (!defaultScales) return { ...userScales };\n\n // User scales take precedence, but include defaults for breakpoints not specified\n return {\n ...defaultScales,\n ...userScales,\n };\n }\n\n /**\n * Get responsive scales configuration\n * Supports dynamic breakpoints - scales are keyed by breakpoint name\n * Deep merges scale categories to preserve user breakpoint definitions\n */\n getResponsiveScales(): ResponsiveScales {\n if (!this.config?.responsiveScales || typeof this.config.responsiveScales !== 'object') {\n return { ...DEFAULT_RESPONSIVE_SCALES };\n }\n\n const userScales = this.config.responsiveScales;\n\n return {\n enabled: userScales.enabled ?? DEFAULT_RESPONSIVE_SCALES.enabled,\n mode: (userScales as { mode?: 'breakpoints' | 'fluid' }).mode ?? DEFAULT_RESPONSIVE_SCALES.mode,\n baseReference: userScales.baseReference ?? DEFAULT_RESPONSIVE_SCALES.baseReference,\n fluidRange: (userScales as { fluidRange?: { min: number; max: number } }).fluidRange\n ?? (DEFAULT_RESPONSIVE_SCALES.fluidRange ? { ...DEFAULT_RESPONSIVE_SCALES.fluidRange } : undefined),\n siteMargin: (userScales as { siteMargin?: { min: number; max: number } }).siteMargin\n ?? (DEFAULT_RESPONSIVE_SCALES.siteMargin ? { ...DEFAULT_RESPONSIVE_SCALES.siteMargin } : undefined),\n fontSize: this.mergeScaleCategory(\n userScales.fontSize as BreakpointScales | undefined,\n DEFAULT_RESPONSIVE_SCALES.fontSize\n ),\n padding: this.mergeScaleCategory(\n userScales.padding as BreakpointScales | undefined,\n DEFAULT_RESPONSIVE_SCALES.padding\n ),\n margin: this.mergeScaleCategory(\n userScales.margin as BreakpointScales | undefined,\n DEFAULT_RESPONSIVE_SCALES.margin\n ),\n gap: this.mergeScaleCategory(\n userScales.gap as BreakpointScales | undefined,\n DEFAULT_RESPONSIVE_SCALES.gap\n ),\n borderRadius: this.mergeScaleCategory(\n userScales.borderRadius as BreakpointScales | undefined,\n DEFAULT_RESPONSIVE_SCALES.borderRadius\n ),\n size: this.mergeScaleCategory(\n userScales.size as BreakpointScales | undefined,\n DEFAULT_RESPONSIVE_SCALES.size\n ),\n };\n }\n\n /**\n * Get rem conversion configuration\n */\n getRemConversion(): RemConversionConfig {\n if (!this.config?.remConversion || typeof this.config.remConversion !== 'object') {\n return { ...DEFAULT_REM_CONFIG };\n }\n return {\n enabled: this.config.remConversion.enabled ?? DEFAULT_REM_CONFIG.enabled,\n baseFontSize: this.config.remConversion.baseFontSize ?? DEFAULT_REM_CONFIG.baseFontSize,\n };\n }\n\n /**\n * Get icons configuration\n * Returns empty object if not configured\n */\n getIcons(): IconsConfig {\n if (!this.config?.icons || typeof this.config.icons !== 'object') {\n return {};\n }\n\n return this.config.icons;\n }\n\n /**\n * Get site-wide social configuration\n * Returns empty object if not configured\n */\n getSocial(): SocialConfig {\n if (!this.config?.social || typeof this.config.social !== 'object') {\n return {};\n }\n\n return this.config.social;\n }\n\n /**\n * Get libraries configuration\n * Returns empty arrays if not configured\n * Normalizes string URLs to object format for backwards compatibility\n */\n getLibraries(): LibrariesConfig {\n if (!this.config?.libraries || typeof this.config.libraries !== 'object') {\n return { js: [], css: [] };\n }\n\n const libs = this.config.libraries;\n\n // Normalize JS libraries: support both string URLs and object format\n const normalizedJs = Array.isArray(libs.js)\n ? libs.js.map((lib) =>\n typeof lib === 'string' ? { url: lib } : lib\n ) as JSLibraryConfig[]\n : [];\n\n // Normalize CSS libraries: support both string URLs and object format\n const normalizedCss = Array.isArray(libs.css)\n ? libs.css.map((lib) =>\n typeof lib === 'string' ? { url: lib } : lib\n ) as CSSLibraryConfig[]\n : [];\n\n return {\n js: normalizedJs,\n css: normalizedCss,\n };\n }\n\n /**\n * Get CSP configuration\n * Returns empty object if not configured\n */\n getCSP(): CSPConfig {\n if (!this.config?.csp || typeof this.config.csp !== 'object') {\n return {};\n }\n\n return this.config.csp;\n }\n\n /**\n * Get base component name for new pages\n * Returns undefined if not configured\n */\n getBaseComponent(): string | undefined {\n if (!this.config?.baseComponent || typeof this.config.baseComponent !== 'string') {\n return undefined;\n }\n return this.config.baseComponent;\n }\n\n /**\n * Get image format setting\n * Returns 'webp' (default) or 'avif'\n */\n getCustomCode(): CustomCodeConfig {\n if (!this.config?.customCode || typeof this.config.customCode !== 'object') {\n return {};\n }\n return this.config.customCode;\n }\n\n getShowMenoBadge(): boolean {\n return this.config?.showMenoBadge === true;\n }\n\n getImageFormat(): ImageFormat {\n if (this.config?.imageFormat === 'avif') return 'avif';\n return 'webp';\n }\n\n /**\n * Get raw config value by key (for extension)\n */\n getRaw<T>(key: string): T | undefined {\n if (!this.config) {\n return undefined;\n }\n return (this.config as Record<string, unknown>)[key] as T | undefined;\n }\n}\n\n/**\n * Singleton instance for global access\n * Use this for convenience, or create your own instance for testing\n */\nexport const configService = new ConfigService();\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;AA8DO,IAAM,gBAAN,MAAoB;AAAA,EACjB,SAAkC;AAAA,EAClC,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjB,MAAM,OAAsB;AAC1B,QAAI,KAAK,QAAQ;AACf;AAAA,IACF;AAEA,QAAI;AACF,UAAI,MAAM,WAAW,aAAa,OAAO,CAAC,GAAG;AAC3C,cAAM,UAAU,MAAM,aAAa,aAAa,OAAO,CAAC;AACxD,aAAK,SAAS,KAAK,MAAM,OAAO;AAAA,MAClC;AAAA,IACF,QAAQ;AAEN,WAAK,SAAS;AAAA,IAChB;AAEA,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,WAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,SAAS;AACd,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAmC;AACjC,QAAI,CAAC,KAAK,QAAQ,eAAe,OAAO,KAAK,OAAO,gBAAgB,UAAU;AAC5E,aAAO,EAAE,GAAG,oBAAoB;AAAA,IAClC;AAGA,UAAM,aAAoC,CAAC;AAC3C,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,OAAO,WAAW,GAAG;AAClE,UAAI,OAAO,UAAU,YAAY,QAAQ,GAAG;AAE1C,mBAAW,GAAG,IAAI;AAAA,MACpB,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AAEtD,cAAM,QAAQ;AACd,YAAI,OAAO,MAAM,eAAe,YAAY,MAAM,aAAa,GAAG;AAChE,qBAAW,GAAG,IAAI;AAAA,YAChB,YAAY,MAAM;AAAA,YAClB,cAAc,OAAO,MAAM,iBAAiB,YAAY,MAAM,eAAe,IACzE,MAAM,eACN,MAAM;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,KAAK,UAAU,EAAE,WAAW,GAAG;AACxC,aAAO,EAAE,GAAG,oBAAoB;AAAA,IAClC;AAEA,WAAO,0BAA0B,UAAU;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAsB;AACpB,QAAI,CAAC,KAAK,QAAQ,MAAM;AACtB,aAAO,EAAE,GAAG,oBAAoB;AAAA,IAClC;AAEA,WAAO,kBAAkB,KAAK,OAAO,IAAI;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,mBACN,YACA,eAC8B;AAC9B,QAAI,CAAC,cAAc,CAAC,cAAe,QAAO;AAC1C,QAAI,CAAC,WAAY,QAAO,gBAAgB,EAAE,GAAG,cAAc,IAAI;AAC/D,QAAI,CAAC,cAAe,QAAO,EAAE,GAAG,WAAW;AAG3C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAAwC;AACtC,QAAI,CAAC,KAAK,QAAQ,oBAAoB,OAAO,KAAK,OAAO,qBAAqB,UAAU;AACtF,aAAO,EAAE,GAAG,0BAA0B;AAAA,IACxC;AAEA,UAAM,aAAa,KAAK,OAAO;AAE/B,WAAO;AAAA,MACL,SAAS,WAAW,WAAW,0BAA0B;AAAA,MACzD,MAAO,WAAkD,QAAQ,0BAA0B;AAAA,MAC3F,eAAe,WAAW,iBAAiB,0BAA0B;AAAA,MACrE,YAAa,WAA6D,eACpE,0BAA0B,aAAa,EAAE,GAAG,0BAA0B,WAAW,IAAI;AAAA,MAC3F,YAAa,WAA6D,eACpE,0BAA0B,aAAa,EAAE,GAAG,0BAA0B,WAAW,IAAI;AAAA,MAC3F,UAAU,KAAK;AAAA,QACb,WAAW;AAAA,QACX,0BAA0B;AAAA,MAC5B;AAAA,MACA,SAAS,KAAK;AAAA,QACZ,WAAW;AAAA,QACX,0BAA0B;AAAA,MAC5B;AAAA,MACA,QAAQ,KAAK;AAAA,QACX,WAAW;AAAA,QACX,0BAA0B;AAAA,MAC5B;AAAA,MACA,KAAK,KAAK;AAAA,QACR,WAAW;AAAA,QACX,0BAA0B;AAAA,MAC5B;AAAA,MACA,cAAc,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,0BAA0B;AAAA,MAC5B;AAAA,MACA,MAAM,KAAK;AAAA,QACT,WAAW;AAAA,QACX,0BAA0B;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAwC;AACtC,QAAI,CAAC,KAAK,QAAQ,iBAAiB,OAAO,KAAK,OAAO,kBAAkB,UAAU;AAChF,aAAO,EAAE,GAAG,mBAAmB;AAAA,IACjC;AACA,WAAO;AAAA,MACL,SAAS,KAAK,OAAO,cAAc,WAAW,mBAAmB;AAAA,MACjE,cAAc,KAAK,OAAO,cAAc,gBAAgB,mBAAmB;AAAA,IAC7E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAwB;AACtB,QAAI,CAAC,KAAK,QAAQ,SAAS,OAAO,KAAK,OAAO,UAAU,UAAU;AAChE,aAAO,CAAC;AAAA,IACV;AAEA,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAA0B;AACxB,QAAI,CAAC,KAAK,QAAQ,UAAU,OAAO,KAAK,OAAO,WAAW,UAAU;AAClE,aAAO,CAAC;AAAA,IACV;AAEA,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAgC;AAC9B,QAAI,CAAC,KAAK,QAAQ,aAAa,OAAO,KAAK,OAAO,cAAc,UAAU;AACxE,aAAO,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,EAAE;AAAA,IAC3B;AAEA,UAAM,OAAO,KAAK,OAAO;AAGzB,UAAM,eAAe,MAAM,QAAQ,KAAK,EAAE,IACtC,KAAK,GAAG;AAAA,MAAI,CAAC,QACX,OAAO,QAAQ,WAAW,EAAE,KAAK,IAAI,IAAI;AAAA,IAC3C,IACA,CAAC;AAGL,UAAM,gBAAgB,MAAM,QAAQ,KAAK,GAAG,IACxC,KAAK,IAAI;AAAA,MAAI,CAAC,QACZ,OAAO,QAAQ,WAAW,EAAE,KAAK,IAAI,IAAI;AAAA,IAC3C,IACA,CAAC;AAEL,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,KAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAoB;AAClB,QAAI,CAAC,KAAK,QAAQ,OAAO,OAAO,KAAK,OAAO,QAAQ,UAAU;AAC5D,aAAO,CAAC;AAAA,IACV;AAEA,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAuC;AACrC,QAAI,CAAC,KAAK,QAAQ,iBAAiB,OAAO,KAAK,OAAO,kBAAkB,UAAU;AAChF,aAAO;AAAA,IACT;AACA,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAkC;AAChC,QAAI,CAAC,KAAK,QAAQ,cAAc,OAAO,KAAK,OAAO,eAAe,UAAU;AAC1E,aAAO,CAAC;AAAA,IACV;AACA,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,mBAA4B;AAC1B,WAAO,KAAK,QAAQ,kBAAkB;AAAA,EACxC;AAAA,EAEA,iBAA8B;AAC5B,QAAI,KAAK,QAAQ,gBAAgB,OAAQ,QAAO;AAChD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAU,KAA4B;AACpC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO;AAAA,IACT;AACA,WAAQ,KAAK,OAAmC,GAAG;AAAA,EACrD;AACF;AAMO,IAAM,gBAAgB,IAAI,cAAc;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
parseJSON,
|
|
16
16
|
resolveSlugToPageId,
|
|
17
17
|
variableService
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-CXCBV2M7.js";
|
|
19
19
|
import {
|
|
20
20
|
configService
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-2MHDV5BF.js";
|
|
22
22
|
import {
|
|
23
23
|
bundleFile,
|
|
24
24
|
createRuntimeServer,
|
|
@@ -2469,6 +2469,9 @@ async function handleApiRoutes(req, url, pageService, componentService, cmsConte
|
|
|
2469
2469
|
}
|
|
2470
2470
|
}
|
|
2471
2471
|
|
|
2472
|
+
// lib/server/routes/pages.ts
|
|
2473
|
+
import { randomBytes } from "crypto";
|
|
2474
|
+
|
|
2472
2475
|
// lib/shared/pathUtils.ts
|
|
2473
2476
|
function getStaticFilePath(pagePath, distDir = "./dist") {
|
|
2474
2477
|
if (pagePath === "/") {
|
|
@@ -2498,10 +2501,11 @@ function escapeHtml(str) {
|
|
|
2498
2501
|
function safeJsonForScript(data) {
|
|
2499
2502
|
return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
2500
2503
|
}
|
|
2501
|
-
function generateErrorPage(error, context) {
|
|
2504
|
+
function generateErrorPage(error, context, cspNonce) {
|
|
2502
2505
|
const errorInfo = extractErrorInfo(error);
|
|
2503
2506
|
const errorMessage = escapeHtml(errorInfo.message);
|
|
2504
2507
|
const errorStack = errorInfo.stack ? escapeHtml(errorInfo.stack) : "";
|
|
2508
|
+
const nonceAttr = cspNonce ? ` nonce="${cspNonce}"` : "";
|
|
2505
2509
|
const errorDataJson = safeJsonForScript({
|
|
2506
2510
|
type: "PREVIEW_ERROR",
|
|
2507
2511
|
error: {
|
|
@@ -2661,7 +2665,7 @@ function generateErrorPage(error, context) {
|
|
|
2661
2665
|
</button>
|
|
2662
2666
|
</div>
|
|
2663
2667
|
</div>
|
|
2664
|
-
<script>
|
|
2668
|
+
<script${nonceAttr}>
|
|
2665
2669
|
(function() {
|
|
2666
2670
|
// Send error to parent editor
|
|
2667
2671
|
if (window.parent && window.parent !== window) {
|
|
@@ -2718,10 +2722,15 @@ function hashContent2(content) {
|
|
|
2718
2722
|
|
|
2719
2723
|
// lib/server/routes/pages.ts
|
|
2720
2724
|
var EDITOR_HEADER = "x-meno-editor";
|
|
2725
|
+
var CSP_NONCE_HEADER = "x-meno-csp-nonce";
|
|
2726
|
+
function generateCspNonce() {
|
|
2727
|
+
return randomBytes(16).toString("base64");
|
|
2728
|
+
}
|
|
2721
2729
|
async function handlePageRoute(url, context, req) {
|
|
2722
2730
|
const { pageService, componentService, cmsService, injectLiveReload, isEditor, serverPort } = context;
|
|
2723
2731
|
const pagePath = url.pathname;
|
|
2724
2732
|
const injectEditorAttrs = req?.headers.get(EDITOR_HEADER) === "1";
|
|
2733
|
+
const cspNonce = generateCspNonce();
|
|
2725
2734
|
const i18nConfig = await loadI18nConfig();
|
|
2726
2735
|
const { locale, pathWithoutLocale } = parseLocaleFromPath(pagePath, i18nConfig);
|
|
2727
2736
|
const slugMappings = pageService.getSlugMappings();
|
|
@@ -2762,7 +2771,8 @@ async function handlePageRoute(url, context, req) {
|
|
|
2762
2771
|
injectEditorAttrs,
|
|
2763
2772
|
isEditor,
|
|
2764
2773
|
serverPort,
|
|
2765
|
-
returnSeparateJS: true
|
|
2774
|
+
returnSeparateJS: true,
|
|
2775
|
+
cspNonce
|
|
2766
2776
|
});
|
|
2767
2777
|
let finalHtml = result.html;
|
|
2768
2778
|
if (result.javascript) {
|
|
@@ -2776,7 +2786,8 @@ async function handlePageRoute(url, context, req) {
|
|
|
2776
2786
|
"Content-Type": "text/html; charset=utf-8",
|
|
2777
2787
|
"Cache-Control": "no-store, max-age=0",
|
|
2778
2788
|
"Pragma": "no-cache",
|
|
2779
|
-
"Expires": "0"
|
|
2789
|
+
"Expires": "0",
|
|
2790
|
+
[CSP_NONCE_HEADER]: cspNonce
|
|
2780
2791
|
}
|
|
2781
2792
|
});
|
|
2782
2793
|
}
|
|
@@ -2794,22 +2805,25 @@ async function handlePageRoute(url, context, req) {
|
|
|
2794
2805
|
pageLibraries: typedPageData.meta?.libraries,
|
|
2795
2806
|
pageCustomCode: typedPageData.meta?.customCode,
|
|
2796
2807
|
injectEditorAttrs,
|
|
2797
|
-
isEditor
|
|
2808
|
+
isEditor,
|
|
2809
|
+
cspNonce
|
|
2798
2810
|
});
|
|
2799
2811
|
return new Response(ssrHTML, {
|
|
2800
2812
|
headers: {
|
|
2801
2813
|
"Content-Type": "text/html; charset=utf-8",
|
|
2802
2814
|
"Cache-Control": "no-store, max-age=0",
|
|
2803
2815
|
"Pragma": "no-cache",
|
|
2804
|
-
"Expires": "0"
|
|
2816
|
+
"Expires": "0",
|
|
2817
|
+
[CSP_NONCE_HEADER]: cspNonce
|
|
2805
2818
|
}
|
|
2806
2819
|
});
|
|
2807
2820
|
} catch (error) {
|
|
2808
2821
|
console.error("Error rendering CMS page:", error);
|
|
2809
|
-
return new Response(generateErrorPage(error, `Error rendering template: ${cmsTemplatePath}
|
|
2822
|
+
return new Response(generateErrorPage(error, `Error rendering template: ${cmsTemplatePath}`, cspNonce), {
|
|
2810
2823
|
headers: {
|
|
2811
2824
|
"Content-Type": "text/html; charset=utf-8",
|
|
2812
|
-
"Cache-Control": "no-store"
|
|
2825
|
+
"Cache-Control": "no-store",
|
|
2826
|
+
[CSP_NONCE_HEADER]: cspNonce
|
|
2813
2827
|
}
|
|
2814
2828
|
});
|
|
2815
2829
|
}
|
|
@@ -2857,7 +2871,8 @@ async function handlePageRoute(url, context, req) {
|
|
|
2857
2871
|
injectEditorAttrs,
|
|
2858
2872
|
isEditor,
|
|
2859
2873
|
serverPort,
|
|
2860
|
-
returnSeparateJS: true
|
|
2874
|
+
returnSeparateJS: true,
|
|
2875
|
+
cspNonce
|
|
2861
2876
|
});
|
|
2862
2877
|
let finalHtml = result.html;
|
|
2863
2878
|
if (result.javascript) {
|
|
@@ -2871,7 +2886,8 @@ async function handlePageRoute(url, context, req) {
|
|
|
2871
2886
|
"Content-Type": "text/html; charset=utf-8",
|
|
2872
2887
|
"Cache-Control": "no-store, max-age=0",
|
|
2873
2888
|
"Pragma": "no-cache",
|
|
2874
|
-
"Expires": "0"
|
|
2889
|
+
"Expires": "0",
|
|
2890
|
+
[CSP_NONCE_HEADER]: cspNonce
|
|
2875
2891
|
}
|
|
2876
2892
|
});
|
|
2877
2893
|
}
|
|
@@ -2887,22 +2903,25 @@ async function handlePageRoute(url, context, req) {
|
|
|
2887
2903
|
pageLibraries: pageData.meta?.libraries,
|
|
2888
2904
|
pageCustomCode: pageData.meta?.customCode,
|
|
2889
2905
|
injectEditorAttrs,
|
|
2890
|
-
isEditor
|
|
2906
|
+
isEditor,
|
|
2907
|
+
cspNonce
|
|
2891
2908
|
});
|
|
2892
2909
|
return new Response(ssrHTML, {
|
|
2893
2910
|
headers: {
|
|
2894
2911
|
"Content-Type": "text/html; charset=utf-8",
|
|
2895
2912
|
"Cache-Control": "no-store, max-age=0",
|
|
2896
2913
|
"Pragma": "no-cache",
|
|
2897
|
-
"Expires": "0"
|
|
2914
|
+
"Expires": "0",
|
|
2915
|
+
[CSP_NONCE_HEADER]: cspNonce
|
|
2898
2916
|
}
|
|
2899
2917
|
});
|
|
2900
2918
|
} catch (error) {
|
|
2901
2919
|
console.error("Error rendering page:", error);
|
|
2902
|
-
return new Response(generateErrorPage(error, `Error rendering page: ${lookupPath}
|
|
2920
|
+
return new Response(generateErrorPage(error, `Error rendering page: ${lookupPath}`, cspNonce), {
|
|
2903
2921
|
headers: {
|
|
2904
2922
|
"Content-Type": "text/html; charset=utf-8",
|
|
2905
|
-
"Cache-Control": "no-store"
|
|
2923
|
+
"Cache-Control": "no-store",
|
|
2924
|
+
[CSP_NONCE_HEADER]: cspNonce
|
|
2906
2925
|
}
|
|
2907
2926
|
});
|
|
2908
2927
|
}
|
|
@@ -3350,4 +3369,4 @@ export {
|
|
|
3350
3369
|
createServer,
|
|
3351
3370
|
FileSystemPageProvider
|
|
3352
3371
|
};
|
|
3353
|
-
//# sourceMappingURL=chunk-
|
|
3372
|
+
//# sourceMappingURL=chunk-A725KYFK.js.map
|