meno-core 1.0.54 → 1.1.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/bin/cli.ts +19 -205
- package/build-astro.ts +31 -27
- package/dist/bin/cli.js +15 -154
- package/dist/bin/cli.js.map +2 -2
- package/dist/chunks/{chunk-3XER4E5W.js → chunk-4ZRU52J2.js} +6 -5
- package/dist/chunks/{chunk-3XER4E5W.js.map → chunk-4ZRU52J2.js.map} +2 -2
- package/dist/chunks/{chunk-NVRBTSQG.js → chunk-5X4HCN7J.js} +4 -4
- package/dist/chunks/chunk-5X4HCN7J.js.map +7 -0
- package/dist/chunks/{chunk-FZITJSSS.js → chunk-7CCVOL5M.js} +14 -14
- package/dist/chunks/chunk-7CCVOL5M.js.map +7 -0
- package/dist/chunks/{chunk-AE3QK5QW.js → chunk-7ZLF4NE5.js} +31 -26
- package/dist/chunks/chunk-7ZLF4NE5.js.map +7 -0
- package/dist/chunks/{chunk-HIZMY3EP.js → chunk-AR7LHIFY.js} +8 -2
- package/dist/chunks/chunk-AR7LHIFY.js.map +7 -0
- package/dist/chunks/{chunk-7HWQUVTU.js → chunk-J4IPTP5X.js} +1839 -443
- package/dist/chunks/chunk-J4IPTP5X.js.map +7 -0
- package/dist/chunks/{chunk-5ETZFREW.js → chunk-QWTQZHG3.js} +186 -79
- package/dist/chunks/chunk-QWTQZHG3.js.map +7 -0
- package/dist/chunks/{chunk-STDY3OVM.js → chunk-XTKNX4FW.js} +3 -3
- package/dist/chunks/{chunk-STDY3OVM.js.map → chunk-XTKNX4FW.js.map} +2 -2
- package/dist/chunks/{fs-ZI5JEU7V.js → fs-G3ANWEFP.js} +2 -2
- package/dist/lib/client/index.js +381 -159
- package/dist/lib/client/index.js.map +3 -3
- package/dist/lib/server/index.js +14365 -6276
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +296 -87
- package/dist/lib/shared/index.js.map +4 -4
- package/dist/lib/shared/richtext/index.js +1 -1
- package/dist/lib/test-utils/index.js +27 -21
- package/dist/lib/test-utils/index.js.map +2 -2
- package/lib/client/ErrorBoundary.test.tsx +147 -90
- package/lib/client/ErrorBoundary.tsx +8 -5
- package/lib/client/componentRegistry.ts +0 -1
- package/lib/client/core/ComponentBuilder.test.ts +150 -0
- package/lib/client/core/ComponentBuilder.ts +112 -12
- package/lib/client/core/ComponentRenderer.test.tsx +1 -2
- package/lib/client/core/builders/embedBuilder.ts +1 -1
- package/lib/client/core/builders/linkBuilder.ts +2 -2
- package/lib/client/core/builders/linkNodeBuilder.ts +1 -1
- package/lib/client/core/builders/listBuilder.ts +41 -4
- package/lib/client/core/builders/localeListBuilder.ts +2 -2
- package/lib/client/core/cmsTemplateProcessor.ts +1 -2
- package/lib/client/hmr/HMRManager.tsx +39 -30
- package/lib/client/hmrCssReload.ts +35 -6
- package/lib/client/hmrWebSocket.ts +5 -5
- package/lib/client/hooks/useColorVariables.ts +12 -9
- package/lib/client/hooks/usePropertyAutocomplete.ts +1 -1
- package/lib/client/hooks/useVariables.ts +3 -1
- package/lib/client/meno-filter/MenoFilter.test.ts +28 -28
- package/lib/client/meno-filter/MenoFilter.ts +9 -6
- package/lib/client/meno-filter/bindings.ts +3 -3
- package/lib/client/meno-filter/init.ts +3 -3
- package/lib/client/meno-filter/renderer.ts +8 -4
- package/lib/client/meno-filter/ui.ts +1 -1
- package/lib/client/meno-filter/updates.ts +3 -2
- package/lib/client/meno-filter/utils.ts +1 -1
- package/lib/client/navigation.test.ts +40 -44
- package/lib/client/responsiveStyleResolver.knownTokens.test.ts +71 -0
- package/lib/client/responsiveStyleResolver.test.ts +5 -5
- package/lib/client/responsiveStyleResolver.ts +61 -4
- package/lib/client/routing/RouteLoader.test.ts +1 -1
- package/lib/client/routing/RouteLoader.ts +8 -3
- package/lib/client/routing/Router.tsx +181 -152
- package/lib/client/scripts/ScriptExecutor.ts +3 -3
- package/lib/client/services/PrefetchService.test.ts +1 -1
- package/lib/client/services/PrefetchService.ts +13 -5
- package/lib/client/styleProcessor.ts +9 -3
- package/lib/client/styles/StyleInjector.ts +1 -1
- package/lib/client/styles/UtilityClassCollector.ts +54 -13
- package/lib/client/templateEngine.test.ts +17 -23
- package/lib/client/templateEngine.ts +7 -13
- package/lib/client/theme.test.ts +1 -1
- package/lib/server/__integration__/server-lifecycle.test.ts +3 -1
- package/lib/server/__integration__/static-assets.test.ts +2 -2
- package/lib/server/__integration__/test-helpers.ts +3 -4
- package/lib/server/astro/cmsPageEmitter.ts +4 -5
- package/lib/server/astro/componentEmitter.ts +11 -8
- package/lib/server/astro/nodeToAstro.test.ts +4 -4
- package/lib/server/astro/nodeToAstro.ts +41 -36
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +11 -11
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +7 -6
- package/lib/server/astro/pageEmitter.ts +1 -1
- package/lib/server/astro/templateTransformer.ts +3 -3
- package/lib/server/createServer.ts +2 -2
- package/lib/server/cssGenerator.ts +2 -2
- package/lib/server/fileWatcher.test.ts +41 -4
- package/lib/server/fileWatcher.ts +102 -23
- package/lib/server/index.ts +7 -16
- package/lib/server/jsonLoader.test.ts +3 -3
- package/lib/server/jsonLoader.ts +8 -5
- package/lib/server/middleware/cors.test.ts +1 -1
- package/lib/server/middleware/cors.ts +2 -2
- package/lib/server/middleware/errorHandler.test.ts +2 -2
- package/lib/server/middleware/logger.test.ts +3 -3
- package/lib/server/middleware/logger.ts +7 -7
- package/lib/server/migrateTemplates.ts +3 -3
- package/lib/server/projectContext.ts +1 -1
- package/lib/server/providers/fileSystemCMSProvider.test.ts +18 -4
- package/lib/server/providers/fileSystemCMSProvider.ts +27 -18
- package/lib/server/routes/api/cms.ts +1 -1
- package/lib/server/routes/api/colors.test.ts +2 -2
- package/lib/server/routes/api/components.ts +1 -1
- package/lib/server/routes/api/config.ts +3 -3
- package/lib/server/routes/api/core-routes.ts +2 -2
- package/lib/server/routes/api/enums.test.ts +2 -2
- package/lib/server/routes/api/functions.ts +1 -1
- package/lib/server/routes/api/variables.test.ts +2 -2
- package/lib/server/routes/index.ts +2 -2
- package/lib/server/routes/pages.ts +1 -2
- package/lib/server/routes/static.ts +1 -2
- package/lib/server/runtime/bundler.ts +65 -43
- package/lib/server/runtime/fs.ts +13 -13
- package/lib/server/runtime/httpServer.ts +3 -3
- package/lib/server/services/ColorService.test.ts +3 -3
- package/lib/server/services/ColorService.ts +5 -2
- package/lib/server/services/EnumService.test.ts +2 -2
- package/lib/server/services/EnumService.ts +4 -1
- package/lib/server/services/VariableService.test.ts +7 -7
- package/lib/server/services/VariableService.ts +5 -2
- package/lib/server/services/cmsService.test.ts +44 -44
- package/lib/server/services/cmsService.ts +17 -97
- package/lib/server/services/componentService.test.ts +10 -16
- package/lib/server/services/componentService.ts +20 -12
- package/lib/server/services/configService.test.ts +13 -14
- package/lib/server/services/configService.ts +30 -0
- package/lib/server/services/fileWatcherService.ts +2 -2
- package/lib/server/services/pageService.test.ts +7 -7
- package/lib/server/services/pageService.ts +18 -5
- package/lib/server/ssr/attributeBuilder.ts +2 -2
- package/lib/server/ssr/cmsSSRProcessor.ts +1 -2
- package/lib/server/ssr/errorOverlay.ts +1 -1
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +6 -5
- package/lib/server/ssr/htmlGenerator.test.ts +0 -1
- package/lib/server/ssr/htmlGenerator.ts +25 -9
- package/lib/server/ssr/imageMetadata.ts +2 -2
- package/lib/server/ssr/jsCollector.test.ts +5 -5
- package/lib/server/ssr/liveReloadIntegration.test.ts +7 -8
- package/lib/server/ssr/ssrRenderer.branches.test.ts +9 -9
- package/lib/server/ssr/ssrRenderer.test.ts +150 -15
- package/lib/server/ssr/ssrRenderer.ts +218 -40
- package/lib/server/ssrRenderer.test.ts +6 -6
- package/lib/server/utils/jsonLineMapper.ts +7 -7
- package/lib/server/validateStyleCoverage.ts +0 -1
- package/lib/server/websocketManager.ts +12 -0
- package/lib/shared/breakpoints.test.ts +5 -5
- package/lib/shared/cmsQuery.test.ts +81 -0
- package/lib/shared/cmsQuery.ts +92 -0
- package/lib/shared/cmsQueryParser.ts +11 -7
- package/lib/shared/colorConversions.ts +9 -9
- package/lib/shared/colorVariableUtils.test.ts +22 -0
- package/lib/shared/colorVariableUtils.ts +19 -5
- package/lib/shared/constants.ts +4 -0
- package/lib/shared/cssGeneration.test.ts +33 -0
- package/lib/shared/cssGeneration.ts +50 -35
- package/lib/shared/cssProperties.test.ts +64 -64
- package/lib/shared/cssProperties.ts +2 -2
- package/lib/shared/elementClassName.ts +2 -2
- package/lib/shared/errorLogger.test.ts +12 -12
- package/lib/shared/expressionEvaluator.test.ts +27 -1
- package/lib/shared/expressionEvaluator.ts +9 -0
- package/lib/shared/fontLoader.test.ts +13 -13
- package/lib/shared/fontLoader.ts +2 -2
- package/lib/shared/gradientUtils.test.ts +2 -2
- package/lib/shared/gradientUtils.ts +15 -8
- package/lib/shared/hrefRefs.test.ts +2 -2
- package/lib/shared/hrefRefs.ts +1 -1
- package/lib/shared/i18n.test.ts +3 -3
- package/lib/shared/i18n.ts +5 -4
- package/lib/shared/index.ts +29 -0
- package/lib/shared/inlineSvgStyleRules.ts +12 -9
- package/lib/shared/interactiveStyleMappings.test.ts +21 -21
- package/lib/shared/interactiveStyleMappings.ts +2 -8
- package/lib/shared/interactiveStyles.test.ts +3 -1
- package/lib/shared/itemTemplateUtils.test.ts +5 -5
- package/lib/shared/itemTemplateUtils.ts +9 -5
- package/lib/shared/jsonRepair.ts +8 -8
- package/lib/shared/libraryLoader.test.ts +6 -6
- package/lib/shared/linkUtils.ts +1 -1
- package/lib/shared/logger.test.ts +66 -0
- package/lib/shared/logger.ts +94 -0
- package/lib/shared/markdown.ts +25 -0
- package/lib/shared/nodeUtils.test.ts +13 -9
- package/lib/shared/nodeUtils.ts +27 -4
- package/lib/shared/pathSecurity.ts +1 -1
- package/lib/shared/pathUtils.ts +1 -2
- package/lib/shared/paths/PathConverter.ts +3 -3
- package/lib/shared/paths/PathUtils.ts +4 -3
- package/lib/shared/paths/PathValidator.ts +1 -1
- package/lib/shared/permissions.test.ts +168 -0
- package/lib/shared/permissions.ts +162 -0
- package/lib/shared/propResolver.test.ts +1 -1
- package/lib/shared/propResolver.ts +1 -1
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +5 -3
- package/lib/shared/registry/ClientRegistry.ts +0 -1
- package/lib/shared/registry/ComponentRegistry.test.ts +3 -3
- package/lib/shared/registry/ComponentRegistry.ts +6 -1
- package/lib/shared/registry/NodeTypeDefinition.ts +2 -2
- package/lib/shared/registry/SSRRegistry.ts +0 -1
- package/lib/shared/registry/fieldPresets.ts +3 -1
- package/lib/shared/registry/nodeTypes/CustomNodeType.ts +103 -0
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +1 -1
- package/lib/shared/registry/nodeTypes/IslandNodeType.ts +129 -0
- package/lib/shared/registry/nodeTypes/ListNodeType.ts +43 -7
- package/lib/shared/registry/nodeTypes/MarkdownNodeType.ts +78 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -1
- package/lib/shared/registry/nodeTypes/index.ts +16 -1
- package/lib/shared/responsiveScaling.test.ts +1 -3
- package/lib/shared/responsiveScaling.ts +6 -5
- package/lib/shared/responsiveStyleUtils.test.ts +0 -1
- package/lib/shared/richtext/htmlToTiptap.test.ts +116 -116
- package/lib/shared/richtext/htmlToTiptap.ts +1 -1
- package/lib/shared/styleNodeUtils.ts +17 -14
- package/lib/shared/tailwindThemeScale.ts +256 -0
- package/lib/shared/themeDefaults.test.ts +0 -3
- package/lib/shared/tree/PathBuilder.test.ts +1 -7
- package/lib/shared/tree/PathBuilder.ts +15 -13
- package/lib/shared/treePathUtils.test.ts +49 -2
- package/lib/shared/treePathUtils.ts +40 -4
- package/lib/shared/types/api.ts +105 -7
- package/lib/shared/types/cms.test.ts +34 -0
- package/lib/shared/types/cms.ts +65 -13
- package/lib/shared/types/components.ts +24 -0
- package/lib/shared/types/errors.test.ts +1 -1
- package/lib/shared/types/index.ts +23 -0
- package/lib/shared/types/permissions.ts +139 -0
- package/lib/shared/types/variables.ts +3 -3
- package/lib/shared/utilityClassConfig.ts +219 -0
- package/lib/shared/utilityClassMapper.durability.test.ts +117 -0
- package/lib/shared/utilityClassMapper.test.ts +401 -26
- package/lib/shared/utilityClassMapper.ts +193 -29
- package/lib/shared/utilityClassNames.test.ts +38 -0
- package/lib/shared/utilityClassNames.ts +301 -23
- package/lib/shared/validation/index.ts +1 -0
- package/lib/shared/validation/permissionsValidators.test.ts +49 -0
- package/lib/shared/validation/permissionsValidators.ts +49 -0
- package/lib/shared/validation/propValidator.test.ts +1 -1
- package/lib/shared/validation/propValidator.ts +1 -1
- package/lib/shared/validation/schemas.test.ts +41 -0
- package/lib/shared/validation/schemas.ts +159 -13
- package/lib/shared/validation/validators.test.ts +2 -2
- package/lib/shared/viewportUnits.test.ts +13 -0
- package/lib/shared/viewportUnits.ts +12 -5
- package/lib/test-utils/factories/ConsoleMockFactory.ts +7 -7
- package/lib/test-utils/factories/DomMockFactory.ts +10 -4
- package/lib/test-utils/factories/ServerMockFactory.ts +1 -1
- package/lib/test-utils/factories/StoreMockFactory.ts +5 -5
- package/lib/test-utils/mockFactories.ts +1 -1
- package/lib/test-utils/mocks.ts +3 -3
- package/package.json +3 -5
- package/scripts/build-for-publish.mjs +2 -4
- package/scripts/build-meno-filter.ts +3 -3
- package/tsconfig.json +1 -0
- package/vite.config.ts +1 -1
- package/build-next.ts +0 -1374
- package/build-static.test.ts +0 -453
- package/build-static.ts +0 -1072
- package/dist/build-static.js +0 -37
- package/dist/chunks/chunk-2FN4UOVO.js +0 -6465
- package/dist/chunks/chunk-2FN4UOVO.js.map +0 -7
- package/dist/chunks/chunk-5ETZFREW.js.map +0 -7
- package/dist/chunks/chunk-7E4IF5L7.js +0 -245
- package/dist/chunks/chunk-7E4IF5L7.js.map +0 -7
- package/dist/chunks/chunk-7HWQUVTU.js.map +0 -7
- package/dist/chunks/chunk-AE3QK5QW.js.map +0 -7
- package/dist/chunks/chunk-F6KTJYGV.js +0 -322
- package/dist/chunks/chunk-F6KTJYGV.js.map +0 -7
- package/dist/chunks/chunk-FZITJSSS.js.map +0 -7
- package/dist/chunks/chunk-GSYYA5GX.js +0 -77
- package/dist/chunks/chunk-GSYYA5GX.js.map +0 -7
- package/dist/chunks/chunk-HIZMY3EP.js.map +0 -7
- package/dist/chunks/chunk-I2WEGYA7.js +0 -424
- package/dist/chunks/chunk-I2WEGYA7.js.map +0 -7
- package/dist/chunks/chunk-JNO3CNLJ.js +0 -464
- package/dist/chunks/chunk-JNO3CNLJ.js.map +0 -7
- package/dist/chunks/chunk-NVRBTSQG.js.map +0 -7
- package/dist/chunks/chunk-Q4OBWKXG.js +0 -1143
- package/dist/chunks/chunk-Q4OBWKXG.js.map +0 -7
- package/dist/chunks/chunk-QTE32Y53.js +0 -3829
- package/dist/chunks/chunk-QTE32Y53.js.map +0 -7
- package/dist/chunks/configService-PRJZF7Y6.js +0 -14
- package/dist/chunks/configService-PRJZF7Y6.js.map +0 -7
- package/dist/chunks/constants-KIQEYMAM.js +0 -46
- package/dist/chunks/constants-KIQEYMAM.js.map +0 -7
- package/dist/chunks/fs-ZI5JEU7V.js.map +0 -7
- package/dist/entries/server-router.js +0 -62
- package/dist/entries/server-router.js.map +0 -7
- package/entries/server-router.tsx +0 -75
- package/lib/server/providers/fileSystemPageProvider.test.ts +0 -84
- package/lib/server/providers/fileSystemPageProvider.ts +0 -179
- package/lib/server/webflow/buildWebflow.ts +0 -623
- package/lib/server/webflow/index.ts +0 -23
- package/lib/server/webflow/nodeToWebflow.test.ts +0 -3102
- package/lib/server/webflow/nodeToWebflow.ts +0 -2372
- package/lib/server/webflow/styleMapper.test.ts +0 -348
- package/lib/server/webflow/styleMapper.ts +0 -687
- package/lib/server/webflow/templateWrapper.ts +0 -49
- package/lib/server/webflow/types.ts +0 -389
- /package/dist/{build-static.js.map → chunks/fs-G3ANWEFP.js.map} +0 -0
package/dist/bin/cli.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../bin/cli.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n/**\n * meno CLI\n * Commands: dev, build, init\n */\n\nimport { resolve, join } from 'path';\nimport { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync } from 'fs';\nimport { setProjectRoot } from '../lib/server/projectContext';\nimport { migrateI18nConfig } from '../lib/shared/i18n';\nimport { syncNetlifyLocale404Block } from '../lib/shared/netlifyLocale404';\nimport { generateBuildErrorPage, type BuildErrorsData } from '../lib/server/ssr/buildErrorOverlay';\nimport { createRuntimeServer, serveFile, fileExists as runtimeFileExists } from '../lib/server/runtime';\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printHelp() {\n console.log(`\nmeno - Visual editor for JSON-based pages\n\nRequirements:\n - Bun or Node.js 18+\n\nUsage:\n meno <command> [options]\n\nCommands:\n dev Start development server with hot reload\n build Build static HTML files for production\n serve Serve built files from ./dist on port 8080\n init Initialize a new project\n\nOptions:\n build --dev Include draft pages in build (for local preview)\n\nExamples:\n meno dev Start dev server in current directory\n meno build Build static files to ./dist (excludes drafts)\n meno build --dev Build including draft pages\n meno serve Serve built files on port 8080\n meno init my-project Create new project in ./my-project\n`);\n}\n\n// Parse _headers file (Netlify/Cloudflare format)\nfunction parseHeadersFile(distPath: string): Map<string, Record<string, string>> {\n const headersPath = join(distPath, '_headers');\n const headers = new Map<string, Record<string, string>>();\n\n if (!existsSync(headersPath)) return headers;\n\n const content = readFileSync(headersPath, 'utf-8');\n let currentPath = '';\n\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n if (!line.startsWith(' ') && !line.startsWith('\\t')) {\n // Path line\n currentPath = trimmed;\n if (!headers.has(currentPath)) {\n headers.set(currentPath, {});\n }\n } else if (currentPath && trimmed.includes(':')) {\n // Header line\n const colonIndex = trimmed.indexOf(':');\n const name = trimmed.substring(0, colonIndex).trim();\n const value = trimmed.substring(colonIndex + 1).trim();\n headers.get(currentPath)![name] = value;\n }\n }\n\n return headers;\n}\n\n// Get headers matching a pathname\nfunction getMatchingHeaders(pathname: string, headersMap: Map<string, Record<string, string>>): Record<string, string> {\n const result: Record<string, string> = {};\n\n headersMap.forEach((headers, pattern) => {\n if (pattern === pathname || pattern === '/*') {\n Object.assign(result, headers);\n } else if (pattern.endsWith('/*')) {\n const prefix = pattern.slice(0, -1);\n if (pathname.startsWith(prefix)) {\n Object.assign(result, headers);\n }\n }\n });\n\n return result;\n}\n\n// Start static file server on port 8080 (non-blocking)\nasync function startStaticServer(distPath: string) {\n const { SERVE_PORT } = await import('../lib/shared/constants');\n\n // Parse _headers file once on startup\n const headersMap = parseHeadersFile(distPath);\n\n const server = await createRuntimeServer({\n port: SERVE_PORT,\n hostname: 'localhost',\n async fetch(req: Request) {\n const url = new URL(req.url);\n let pathname = url.pathname;\n\n // Check for build errors and show overlay (except for _errors.json itself)\n if (pathname !== '/_errors.json') {\n const errorsPath = join(distPath, '_errors.json');\n if (existsSync(errorsPath)) {\n try {\n const errorsData: BuildErrorsData = JSON.parse(readFileSync(errorsPath, 'utf-8'));\n return new Response(generateBuildErrorPage(errorsData), {\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n });\n } catch {\n // If we can't parse errors file, continue normally\n }\n }\n }\n\n // Default to index.html for root\n if (pathname === '/') {\n pathname = '/index.html';\n }\n\n // Get custom headers for this path\n const customHeaders = getMatchingHeaders(pathname, headersMap);\n\n // Try to serve the file\n const filePath = join(distPath, pathname);\n\n // Check if file exists and serve it\n if (await runtimeFileExists(filePath)) {\n return await serveFile(filePath, customHeaders);\n }\n\n // Try with .html extension for clean URLs\n const htmlPath = filePath.endsWith('.html') ? filePath : `${filePath}.html`;\n if (await runtimeFileExists(htmlPath)) {\n return await serveFile(htmlPath, customHeaders);\n }\n\n // Try index.html in directory\n const indexPath = join(filePath, 'index.html');\n if (await runtimeFileExists(indexPath)) {\n return await serveFile(indexPath, customHeaders);\n }\n\n return new Response('Not Found', { status: 404 });\n },\n });\n\n return server;\n}\n\nasync function runDev() {\n const projectRoot = process.cwd();\n\n // Validate project structure\n if (!existsSync(join(projectRoot, 'pages'))) {\n console.error('\u274C No pages directory found. Are you in a valid project directory?');\n console.error(' Run \"meno init <project-name>\" to create a new project.');\n process.exit(1);\n }\n\n // Set project root for path resolution\n setProjectRoot(projectRoot);\n\n console.log(`\uD83D\uDCC1 Project root: ${projectRoot}`);\n\n // Start static server on 8080 if dist exists\n const distPath = join(projectRoot, 'dist');\n if (existsSync(distPath)) {\n const staticServer = await startStaticServer(distPath);\n console.log(`\uD83D\uDE80 Static server running at http://localhost:${staticServer.port}`);\n }\n\n // Import and run the dev server (it handles all initialization internally)\n await import('../entries/server-router');\n}\n\nasync function runBuild(isDev: boolean = false) {\n const projectRoot = process.cwd();\n\n // Validate project structure\n if (!existsSync(join(projectRoot, 'pages'))) {\n console.error('\u274C No pages directory found. Are you in a valid project directory?');\n process.exit(1);\n }\n\n // Set project root for path resolution\n setProjectRoot(projectRoot);\n\n // Set dev mode environment variable (drafts are built in dev mode)\n if (isDev) {\n process.env.MENO_DEV_BUILD = 'true';\n }\n\n console.log(`\uD83D\uDCC1 Building project: ${projectRoot}${isDev ? ' (dev mode - including drafts)' : ''}`);\n\n // Import and run build\n const { buildStaticPages } = await import('../build-static.ts');\n await buildStaticPages();\n}\n\nasync function runServe() {\n const projectRoot = process.cwd();\n const distPath = join(projectRoot, 'dist');\n\n // Validate dist directory exists\n if (!existsSync(distPath)) {\n console.error('\u274C No dist directory found. Run \"meno build\" first.');\n process.exit(1);\n }\n\n console.log(`\uD83D\uDCC1 Serving built files from: ${distPath}`);\n const server = await startStaticServer(distPath);\n console.log(`\uD83D\uDE80 Static server running at http://localhost:${server.port}`);\n\n // Keep the process alive\n await new Promise(() => {});\n}\n\nasync function runInit(projectName?: string) {\n if (!projectName) {\n console.error('\u274C Please provide a project name: meno init <project-name>');\n process.exit(1);\n }\n\n const projectPath = resolve(process.cwd(), projectName);\n\n if (existsSync(projectPath)) {\n console.error(`\u274C Directory \"${projectName}\" already exists.`);\n process.exit(1);\n }\n\n console.log(`\\n\uD83D\uDE80 Creating new meno project: ${projectName}\\n`);\n\n // Create project structure\n mkdirSync(projectPath, { recursive: true });\n mkdirSync(join(projectPath, 'pages'));\n mkdirSync(join(projectPath, 'components'));\n mkdirSync(join(projectPath, 'fonts'));\n mkdirSync(join(projectPath, 'images'));\n\n // Create default project.config.json\n const defaultConfig = {\n fonts: [],\n editor: {\n theme: 'dark',\n },\n };\n writeFileSync(join(projectPath, 'project.config.json'), JSON.stringify(defaultConfig, null, 2));\n\n // Create default colors.json\n const defaultColors = {\n default: 'light',\n themes: {\n light: {\n label: 'Light',\n colors: {\n text: '#1f2937',\n bg: '#ffffff',\n },\n },\n },\n };\n writeFileSync(join(projectPath, 'colors.json'), JSON.stringify(defaultColors, null, 2));\n\n // Create package.json for deployment\n const packageJson = {\n name: projectName,\n version: '1.0.0',\n private: true,\n scripts: {\n dev: 'meno dev',\n build: 'meno build',\n },\n dependencies: {\n 'meno-core': '^1.0.0',\n },\n };\n writeFileSync(join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2));\n\n // Create .claude/tools/ with screenshot tool\n mkdirSync(join(projectPath, '.claude', 'tools'), { recursive: true });\n\n const screenshotToolSrc = join(\n import.meta.dirname,\n '..',\n '..',\n '..',\n 'example',\n '.claude',\n 'tools',\n 'screenshot.mjs',\n );\n if (existsSync(screenshotToolSrc)) {\n cpSync(screenshotToolSrc, join(projectPath, '.claude', 'tools', 'screenshot.mjs'));\n }\n\n // Create CLAUDE.md with project instructions\n const claudeMd = `# ${projectName}\n\nA Meno project - visual editor that generates websites from JSON files.\n\n## Dev Server\n\n\\`\\`\\`bash\nbun run dev # Start dev server on port 3000\n\\`\\`\\`\n\n## Screenshot Tool\n\nTake a screenshot of any element by its node ID:\n\\`\\`\\`bash\nnpx puppeteer browsers install chrome && node .claude/tools/screenshot.mjs <nodeId> --page /about\n\\`\\`\\`\nNode IDs can be found in the page JSON files under the \\`id\\` field of each node.\n`;\n writeFileSync(join(projectPath, 'CLAUDE.md'), claudeMd);\n\n // Create .gitignore\n const gitignore = `# Dependencies\nnode_modules/\n\n# Build output\ndist/\n\n# OS files\n.DS_Store\n`;\n writeFileSync(join(projectPath, '.gitignore'), gitignore);\n\n // Create netlify.toml. Routed through the shared locale-404 sync for uniformity\n // with the other netlify.toml producers \u2014 a no-op here (new projects are\n // single-locale with no 404 page), but the managed block appears automatically\n // if this scaffold ever grows locales/404 defaults.\n const netlifyConfig = syncNetlifyLocale404Block(\n `[build]\n command = \"bun install && bun run build\"\n publish = \"dist\"\n\n[build.environment]\n BUN_VERSION = \"1\"\n`,\n migrateI18nConfig((defaultConfig as { i18n?: unknown }).i18n),\n false, // the scaffold ships pages/index.json only \u2014 no 404 page\n );\n writeFileSync(join(projectPath, 'netlify.toml'), netlifyConfig);\n\n // Create vercel.json\n const vercelConfig = {\n buildCommand: 'bun run build',\n outputDirectory: 'dist',\n installCommand: 'bun install',\n };\n writeFileSync(join(projectPath, 'vercel.json'), JSON.stringify(vercelConfig, null, 2));\n\n // Create wrangler.toml for Cloudflare Pages\n const cloudflareConfig = `name = \"${projectName}\"\ncompatibility_date = \"2024-01-01\"\npages_build_output_dir = \"dist\"\n\n[build]\ncommand = \"bun install && bun run build\"\n`;\n writeFileSync(join(projectPath, 'wrangler.toml'), cloudflareConfig);\n\n // Create default index page\n const defaultPage = {\n meta: {\n title: 'Your Project Title',\n description: 'Your Project Description',\n keywords: '',\n ogTitle: 'Your Project Title',\n ogDescription: 'Your Project Description',\n ogType: 'website',\n },\n root: {\n type: 'node',\n tag: 'div',\n style: {\n base: {\n padding: '40px',\n },\n tablet: {},\n mobile: {},\n },\n children: [\n {\n type: 'node',\n tag: 'h1',\n style: {\n base: {},\n },\n children: 'Hello',\n },\n ],\n },\n };\n writeFileSync(join(projectPath, 'pages', 'index.json'), JSON.stringify(defaultPage, null, 2));\n\n console.log('\u2705 Created project structure:');\n console.log(` ${projectName}/`);\n console.log(' \u251C\u2500\u2500 .claude/');\n console.log(' \u2502 \u2514\u2500\u2500 tools/');\n console.log(' \u2502 \u2514\u2500\u2500 screenshot.mjs');\n console.log(' \u251C\u2500\u2500 pages/');\n console.log(' \u2502 \u2514\u2500\u2500 index.json');\n console.log(' \u251C\u2500\u2500 components/');\n console.log(' \u251C\u2500\u2500 fonts/');\n console.log(' \u251C\u2500\u2500 images/');\n console.log(' \u251C\u2500\u2500 colors.json');\n console.log(' \u251C\u2500\u2500 project.config.json');\n console.log(' \u251C\u2500\u2500 package.json');\n console.log(' \u251C\u2500\u2500 CLAUDE.md');\n console.log(' \u251C\u2500\u2500 netlify.toml');\n console.log(' \u251C\u2500\u2500 vercel.json');\n console.log(' \u251C\u2500\u2500 wrangler.toml');\n console.log(' \u2514\u2500\u2500 .gitignore');\n console.log('\\n\uD83C\uDF89 Project created successfully!\\n');\n console.log('Requirements:');\n console.log(' Bun - https://bun.sh');\n console.log('');\n console.log('Next steps:');\n console.log(` cd ${projectName}`);\n console.log(' bun install # Install dependencies');\n console.log(' bun run dev # Start dev server');\n console.log('');\n console.log('Deploy:');\n console.log(' Push to GitHub and connect to Netlify/Vercel/Cloudflare');\n console.log(' Config files included - auto-detected!');\n console.log('');\n}\n\n// Main\nswitch (command) {\n case 'dev':\n runDev();\n break;\n case 'build':\n runBuild(args.includes('--dev'));\n break;\n case 'serve':\n runServe();\n break;\n case 'init':\n runInit(args[1]);\n break;\n case '--help':\n case '-h':\n case undefined:\n printHelp();\n break;\n default:\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n/**\n * meno CLI\n *\n * The bespoke JSON renderer (`meno dev`/`build`/`serve`) has been retired \u2014 Meno\n * targets Astro exclusively now. The only remaining command is `init` (scaffold a\n * project). To run/build a project, convert it to meno-astro and use the Astro\n * toolchain (`@meno/studio` in the editor, or `astro dev`/`astro build`).\n */\n\nimport { resolve, join } from 'node:path';\nimport { existsSync, mkdirSync, writeFileSync, cpSync } from 'node:fs';\nimport { migrateI18nConfig } from '../lib/shared/i18n';\nimport { syncNetlifyLocale404Block } from '../lib/shared/netlifyLocale404';\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printHelp() {\n console.log(`\nmeno - Visual editor for websites (meno-astro)\n\nRequirements:\n - Bun or Node.js 18+\n\nUsage:\n meno <command> [options]\n\nCommands:\n init Initialize a new project\n\nNote:\n The legacy JSON runtime (dev/build/serve) has been retired. Convert a JSON\n project to meno-astro and use the Astro toolchain instead.\n\nExamples:\n meno init my-project Create new project in ./my-project\n`);\n}\n\nconst RETIRED_NOTICE = `The legacy JSON \"${command}\" command has been retired.\nMeno targets Astro exclusively now. Convert this project to meno-astro and use the\nAstro toolchain instead (open it in @meno/studio, or run \"astro dev\" / \"astro build\").`;\n\nasync function runInit(projectName?: string) {\n if (!projectName) {\n console.error('\u274C Please provide a project name: meno init <project-name>');\n process.exit(1);\n }\n\n const projectPath = resolve(process.cwd(), projectName);\n\n if (existsSync(projectPath)) {\n console.error(`\u274C Directory \"${projectName}\" already exists.`);\n process.exit(1);\n }\n\n console.log(`\\n\uD83D\uDE80 Creating new meno project: ${projectName}\\n`);\n\n // Create project structure\n mkdirSync(projectPath, { recursive: true });\n mkdirSync(join(projectPath, 'pages'));\n mkdirSync(join(projectPath, 'components'));\n mkdirSync(join(projectPath, 'fonts'));\n mkdirSync(join(projectPath, 'images'));\n\n // Create default project.config.json\n const defaultConfig = {\n fonts: [],\n editor: {\n theme: 'dark',\n },\n };\n writeFileSync(join(projectPath, 'project.config.json'), JSON.stringify(defaultConfig, null, 2));\n\n // Create default colors.json\n const defaultColors = {\n default: 'light',\n themes: {\n light: {\n label: 'Light',\n colors: {\n text: '#1f2937',\n bg: '#ffffff',\n },\n },\n },\n };\n writeFileSync(join(projectPath, 'colors.json'), JSON.stringify(defaultColors, null, 2));\n\n // Create package.json for deployment\n const packageJson = {\n name: projectName,\n version: '1.0.0',\n private: true,\n scripts: {\n dev: 'meno dev',\n build: 'meno build',\n },\n dependencies: {\n 'meno-core': '^1.0.0',\n },\n };\n writeFileSync(join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2));\n\n // Create .claude/tools/ with screenshot tool\n mkdirSync(join(projectPath, '.claude', 'tools'), { recursive: true });\n\n const screenshotToolSrc = join(\n import.meta.dirname,\n '..',\n '..',\n '..',\n 'example',\n '.claude',\n 'tools',\n 'screenshot.mjs',\n );\n if (existsSync(screenshotToolSrc)) {\n cpSync(screenshotToolSrc, join(projectPath, '.claude', 'tools', 'screenshot.mjs'));\n }\n\n // Create CLAUDE.md with project instructions\n const claudeMd = `# ${projectName}\n\nA Meno project - visual editor that generates websites from JSON files.\n\n## Dev Server\n\n\\`\\`\\`bash\nbun run dev # Start dev server on port 3000\n\\`\\`\\`\n\n## Screenshot Tool\n\nTake a screenshot of any element by its node ID:\n\\`\\`\\`bash\nnpx puppeteer browsers install chrome && node .claude/tools/screenshot.mjs <nodeId> --page /about\n\\`\\`\\`\nNode IDs can be found in the page JSON files under the \\`id\\` field of each node.\n`;\n writeFileSync(join(projectPath, 'CLAUDE.md'), claudeMd);\n\n // Create .gitignore\n const gitignore = `# Dependencies\nnode_modules/\n\n# Build output\ndist/\n\n# OS files\n.DS_Store\n`;\n writeFileSync(join(projectPath, '.gitignore'), gitignore);\n\n // Create netlify.toml. Routed through the shared locale-404 sync for uniformity\n // with the other netlify.toml producers \u2014 a no-op here (new projects are\n // single-locale with no 404 page), but the managed block appears automatically\n // if this scaffold ever grows locales/404 defaults.\n const netlifyConfig = syncNetlifyLocale404Block(\n `[build]\n command = \"bun install && bun run build\"\n publish = \"dist\"\n\n[build.environment]\n BUN_VERSION = \"1\"\n`,\n migrateI18nConfig((defaultConfig as { i18n?: unknown }).i18n),\n false, // the scaffold ships pages/index.json only \u2014 no 404 page\n );\n writeFileSync(join(projectPath, 'netlify.toml'), netlifyConfig);\n\n // Create vercel.json\n const vercelConfig = {\n buildCommand: 'bun run build',\n outputDirectory: 'dist',\n installCommand: 'bun install',\n };\n writeFileSync(join(projectPath, 'vercel.json'), JSON.stringify(vercelConfig, null, 2));\n\n // Create wrangler.toml for Cloudflare Pages\n const cloudflareConfig = `name = \"${projectName}\"\ncompatibility_date = \"2024-01-01\"\npages_build_output_dir = \"dist\"\n\n[build]\ncommand = \"bun install && bun run build\"\n`;\n writeFileSync(join(projectPath, 'wrangler.toml'), cloudflareConfig);\n\n // Create default index page\n const defaultPage = {\n meta: {\n title: 'Your Project Title',\n description: 'Your Project Description',\n keywords: '',\n ogTitle: 'Your Project Title',\n ogDescription: 'Your Project Description',\n ogType: 'website',\n },\n root: {\n type: 'node',\n tag: 'div',\n style: {\n base: {\n padding: '40px',\n },\n tablet: {},\n mobile: {},\n },\n children: [\n {\n type: 'node',\n tag: 'h1',\n style: {\n base: {},\n },\n children: 'Hello',\n },\n ],\n },\n };\n writeFileSync(join(projectPath, 'pages', 'index.json'), JSON.stringify(defaultPage, null, 2));\n\n console.log('\u2705 Created project structure:');\n console.log(` ${projectName}/`);\n console.log(' \u251C\u2500\u2500 .claude/');\n console.log(' \u2502 \u2514\u2500\u2500 tools/');\n console.log(' \u2502 \u2514\u2500\u2500 screenshot.mjs');\n console.log(' \u251C\u2500\u2500 pages/');\n console.log(' \u2502 \u2514\u2500\u2500 index.json');\n console.log(' \u251C\u2500\u2500 components/');\n console.log(' \u251C\u2500\u2500 fonts/');\n console.log(' \u251C\u2500\u2500 images/');\n console.log(' \u251C\u2500\u2500 colors.json');\n console.log(' \u251C\u2500\u2500 project.config.json');\n console.log(' \u251C\u2500\u2500 package.json');\n console.log(' \u251C\u2500\u2500 CLAUDE.md');\n console.log(' \u251C\u2500\u2500 netlify.toml');\n console.log(' \u251C\u2500\u2500 vercel.json');\n console.log(' \u251C\u2500\u2500 wrangler.toml');\n console.log(' \u2514\u2500\u2500 .gitignore');\n console.log('\\n\uD83C\uDF89 Project created successfully!\\n');\n console.log('Requirements:');\n console.log(' Bun - https://bun.sh');\n console.log('');\n console.log('Next steps:');\n console.log(` cd ${projectName}`);\n console.log(' bun install # Install dependencies');\n console.log(' bun run dev # Start dev server');\n console.log('');\n console.log('Deploy:');\n console.log(' Push to GitHub and connect to Netlify/Vercel/Cloudflare');\n console.log(' Config files included - auto-detected!');\n console.log('');\n}\n\n// Main\nswitch (command) {\n case 'init':\n runInit(args[1]);\n break;\n case 'dev':\n case 'build':\n case 'serve':\n console.error(RETIRED_NOTICE);\n process.exit(1);\n break;\n case '--help':\n case '-h':\n case undefined:\n printHelp();\n break;\n default:\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -90,15 +90,16 @@ function resolveI18nInProps(props, locale, config) {
|
|
|
90
90
|
function extractLocaleFromPath(path, config) {
|
|
91
91
|
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
|
|
92
92
|
const segments = cleanPath.split("/");
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
93
|
+
const firstSegment = segments[0];
|
|
94
|
+
if (firstSegment !== void 0 && isValidLocaleCode(config, firstSegment)) {
|
|
95
|
+
const locale = firstSegment;
|
|
96
|
+
const pathWithoutLocale = `/${segments.slice(1).join("/")}`;
|
|
96
97
|
return { locale, pathWithoutLocale: pathWithoutLocale || "/" };
|
|
97
98
|
}
|
|
98
99
|
return { locale: null, pathWithoutLocale: path };
|
|
99
100
|
}
|
|
100
101
|
function buildLocalizedPath(path, locale) {
|
|
101
|
-
const cleanPath = path.startsWith("/") ? path :
|
|
102
|
+
const cleanPath = path.startsWith("/") ? path : `/${path}`;
|
|
102
103
|
return `/${locale}${cleanPath === "/" ? "" : cleanPath}`;
|
|
103
104
|
}
|
|
104
105
|
function parseLocaleFromPath(path, config) {
|
|
@@ -165,4 +166,4 @@ export {
|
|
|
165
166
|
setStoredLocale,
|
|
166
167
|
clearStoredLocale
|
|
167
168
|
};
|
|
168
|
-
//# sourceMappingURL=chunk-
|
|
169
|
+
//# sourceMappingURL=chunk-4ZRU52J2.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../lib/shared/i18n.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Internationalization (i18n) utilities\n * Handles inline translation resolution for component props\n */\n\nimport type { I18nValue, I18nConfig, LocaleConfig } from './types/components';\n\n/**\n * Default i18n configuration\n */\nexport const DEFAULT_I18N_CONFIG: I18nConfig = {\n defaultLocale: 'en',\n locales: [{ code: 'en', name: 'English', nativeName: 'English', langTag: 'en-US' }],\n};\n\n// ============================================\n// Locale helper functions\n// ============================================\n\n/**\n * Get array of locale codes from config\n */\nexport function getLocaleCodes(config: I18nConfig): string[] {\n return config.locales.map((loc) => loc.code);\n}\n\n/**\n * Find a locale config by its code\n */\nexport function findLocaleByCode(config: I18nConfig, code: string): LocaleConfig | undefined {\n return config.locales.find((loc) => loc.code === code);\n}\n\n/**\n * Check if a locale code is valid/exists in config\n */\nexport function isValidLocaleCode(config: I18nConfig, code: string): boolean {\n return config.locales.some((loc) => loc.code === code);\n}\n\n// ============================================\n// Config Migration (old string[] -> new LocaleConfig[])\n// ============================================\n\n/**\n * Convert old locale format (string) to new format (LocaleConfig)\n */\nfunction migrateLocaleString(code: string): LocaleConfig {\n const upperCode = code.toUpperCase();\n return {\n code: code.toLowerCase(),\n name: upperCode,\n nativeName: upperCode,\n langTag: `${code.toLowerCase()}-${upperCode}`,\n };\n}\n\n/**\n * Check if locales array is in old string format\n */\nfunction isOldLocaleFormat(locales: unknown): locales is string[] {\n return Array.isArray(locales) && locales.length > 0 && typeof locales[0] === 'string';\n}\n\n/**\n * Migrate i18n config from old format to new format\n * Old: { defaultLocale: \"en\", locales: [\"en\", \"pl\"] }\n * New: { defaultLocale: \"en\", locales: [{ code: \"en\", name: \"EN\", ... }] }\n */\nexport function migrateI18nConfig(i18n: unknown): I18nConfig {\n if (!i18n || typeof i18n !== 'object') {\n return DEFAULT_I18N_CONFIG;\n }\n\n const config = i18n as Record<string, unknown>;\n const defaultLocale = (config.defaultLocale as string) || 'en';\n const locales = config.locales;\n\n if (!locales || !Array.isArray(locales)) {\n return DEFAULT_I18N_CONFIG;\n }\n\n // Migrate old string[] format\n if (isOldLocaleFormat(locales)) {\n return {\n defaultLocale,\n locales: locales.map(migrateLocaleString),\n };\n }\n\n // Already in new format\n return {\n defaultLocale,\n locales: locales as LocaleConfig[],\n };\n}\n\n/**\n * Type guard to check if a value is an I18nValue object\n */\nexport function isI18nValue(value: unknown): value is I18nValue {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n return '_i18n' in value && (value as Record<string, unknown>)._i18n === true;\n}\n\n/**\n * Resolve a single translation value for the given locale\n * Fallback order: exact locale -> default locale -> first available -> empty\n * Works with any value type (strings, arrays for list props, etc.)\n */\nexport function resolveTranslation(value: I18nValue, locale: string, config: I18nConfig): unknown {\n // Try exact locale match (any type - string, array, etc.)\n if (locale in value && value[locale] !== undefined && locale !== '_i18n') {\n return value[locale];\n }\n\n // Try default locale\n if (config.defaultLocale in value && value[config.defaultLocale] !== undefined) {\n return value[config.defaultLocale];\n }\n\n // Get first available value (skip _i18n marker)\n for (const key of Object.keys(value)) {\n if (key !== '_i18n' && value[key] !== undefined) {\n return value[key];\n }\n }\n\n // Return empty string for string i18n, empty array for list i18n\n // Check if any value is an array to determine the type\n const hasArrayValue = Object.entries(value).some(([k, v]) => k !== '_i18n' && Array.isArray(v));\n return hasArrayValue ? [] : '';\n}\n\n/**\n * Resolve a value that might be an I18nValue or a regular value\n * Returns the original value if not an I18nValue\n */\nexport function resolveI18nValue(value: unknown, locale: string, config: I18nConfig): unknown {\n if (isI18nValue(value)) {\n return resolveTranslation(value, locale, config);\n }\n return value;\n}\n\n/**\n * Recursively resolve all I18nValue objects in a props object\n */\nexport function resolveI18nInProps(\n props: Record<string, unknown>,\n locale: string,\n config: I18nConfig,\n): Record<string, unknown> {\n const resolved: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (isI18nValue(value)) {\n resolved[key] = resolveTranslation(value, locale, config);\n } else if (Array.isArray(value)) {\n resolved[key] = value.map((item) => (isI18nValue(item) ? resolveTranslation(item, locale, config) : item));\n } else if (typeof value === 'object' && value !== null) {\n // Recursively resolve nested objects (but not I18nValue which is already handled)\n resolved[key] = resolveI18nInProps(value as Record<string, unknown>, locale, config);\n } else {\n resolved[key] = value;\n }\n }\n\n return resolved;\n}\n\n/**\n * Extract locale from URL path prefix\n * e.g., '/en/about' -> 'en', '/pl/' -> 'pl', '/about' -> null\n */\nexport function extractLocaleFromPath(\n path: string,\n config: I18nConfig,\n): { locale: string | null; pathWithoutLocale: string } {\n // Remove leading slash and split\n const cleanPath = path.startsWith('/') ? path.slice(1) : path;\n const segments = cleanPath.split('/');\n\n if (
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * Internationalization (i18n) utilities\n * Handles inline translation resolution for component props\n */\n\nimport type { I18nValue, I18nConfig, LocaleConfig } from './types/components';\n\n/**\n * Default i18n configuration\n */\nexport const DEFAULT_I18N_CONFIG: I18nConfig = {\n defaultLocale: 'en',\n locales: [{ code: 'en', name: 'English', nativeName: 'English', langTag: 'en-US' }],\n};\n\n// ============================================\n// Locale helper functions\n// ============================================\n\n/**\n * Get array of locale codes from config\n */\nexport function getLocaleCodes(config: I18nConfig): string[] {\n return config.locales.map((loc) => loc.code);\n}\n\n/**\n * Find a locale config by its code\n */\nexport function findLocaleByCode(config: I18nConfig, code: string): LocaleConfig | undefined {\n return config.locales.find((loc) => loc.code === code);\n}\n\n/**\n * Check if a locale code is valid/exists in config\n */\nexport function isValidLocaleCode(config: I18nConfig, code: string): boolean {\n return config.locales.some((loc) => loc.code === code);\n}\n\n// ============================================\n// Config Migration (old string[] -> new LocaleConfig[])\n// ============================================\n\n/**\n * Convert old locale format (string) to new format (LocaleConfig)\n */\nfunction migrateLocaleString(code: string): LocaleConfig {\n const upperCode = code.toUpperCase();\n return {\n code: code.toLowerCase(),\n name: upperCode,\n nativeName: upperCode,\n langTag: `${code.toLowerCase()}-${upperCode}`,\n };\n}\n\n/**\n * Check if locales array is in old string format\n */\nfunction isOldLocaleFormat(locales: unknown): locales is string[] {\n return Array.isArray(locales) && locales.length > 0 && typeof locales[0] === 'string';\n}\n\n/**\n * Migrate i18n config from old format to new format\n * Old: { defaultLocale: \"en\", locales: [\"en\", \"pl\"] }\n * New: { defaultLocale: \"en\", locales: [{ code: \"en\", name: \"EN\", ... }] }\n */\nexport function migrateI18nConfig(i18n: unknown): I18nConfig {\n if (!i18n || typeof i18n !== 'object') {\n return DEFAULT_I18N_CONFIG;\n }\n\n const config = i18n as Record<string, unknown>;\n const defaultLocale = (config.defaultLocale as string) || 'en';\n const locales = config.locales;\n\n if (!locales || !Array.isArray(locales)) {\n return DEFAULT_I18N_CONFIG;\n }\n\n // Migrate old string[] format\n if (isOldLocaleFormat(locales)) {\n return {\n defaultLocale,\n locales: locales.map(migrateLocaleString),\n };\n }\n\n // Already in new format\n return {\n defaultLocale,\n locales: locales as LocaleConfig[],\n };\n}\n\n/**\n * Type guard to check if a value is an I18nValue object\n */\nexport function isI18nValue(value: unknown): value is I18nValue {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n return '_i18n' in value && (value as Record<string, unknown>)._i18n === true;\n}\n\n/**\n * Resolve a single translation value for the given locale\n * Fallback order: exact locale -> default locale -> first available -> empty\n * Works with any value type (strings, arrays for list props, etc.)\n */\nexport function resolveTranslation(value: I18nValue, locale: string, config: I18nConfig): unknown {\n // Try exact locale match (any type - string, array, etc.)\n if (locale in value && value[locale] !== undefined && locale !== '_i18n') {\n return value[locale];\n }\n\n // Try default locale\n if (config.defaultLocale in value && value[config.defaultLocale] !== undefined) {\n return value[config.defaultLocale];\n }\n\n // Get first available value (skip _i18n marker)\n for (const key of Object.keys(value)) {\n if (key !== '_i18n' && value[key] !== undefined) {\n return value[key];\n }\n }\n\n // Return empty string for string i18n, empty array for list i18n\n // Check if any value is an array to determine the type\n const hasArrayValue = Object.entries(value).some(([k, v]) => k !== '_i18n' && Array.isArray(v));\n return hasArrayValue ? [] : '';\n}\n\n/**\n * Resolve a value that might be an I18nValue or a regular value\n * Returns the original value if not an I18nValue\n */\nexport function resolveI18nValue(value: unknown, locale: string, config: I18nConfig): unknown {\n if (isI18nValue(value)) {\n return resolveTranslation(value, locale, config);\n }\n return value;\n}\n\n/**\n * Recursively resolve all I18nValue objects in a props object\n */\nexport function resolveI18nInProps(\n props: Record<string, unknown>,\n locale: string,\n config: I18nConfig,\n): Record<string, unknown> {\n const resolved: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (isI18nValue(value)) {\n resolved[key] = resolveTranslation(value, locale, config);\n } else if (Array.isArray(value)) {\n resolved[key] = value.map((item) => (isI18nValue(item) ? resolveTranslation(item, locale, config) : item));\n } else if (typeof value === 'object' && value !== null) {\n // Recursively resolve nested objects (but not I18nValue which is already handled)\n resolved[key] = resolveI18nInProps(value as Record<string, unknown>, locale, config);\n } else {\n resolved[key] = value;\n }\n }\n\n return resolved;\n}\n\n/**\n * Extract locale from URL path prefix\n * e.g., '/en/about' -> 'en', '/pl/' -> 'pl', '/about' -> null\n */\nexport function extractLocaleFromPath(\n path: string,\n config: I18nConfig,\n): { locale: string | null; pathWithoutLocale: string } {\n // Remove leading slash and split\n const cleanPath = path.startsWith('/') ? path.slice(1) : path;\n const segments = cleanPath.split('/');\n\n const firstSegment = segments[0];\n if (firstSegment !== undefined && isValidLocaleCode(config, firstSegment)) {\n const locale = firstSegment;\n const pathWithoutLocale = `/${segments.slice(1).join('/')}`;\n return { locale, pathWithoutLocale: pathWithoutLocale || '/' };\n }\n\n return { locale: null, pathWithoutLocale: path };\n}\n\n/**\n * Build a localized path\n * e.g., ('/about', 'pl') -> '/pl/about'\n */\nexport function buildLocalizedPath(path: string, locale: string): string {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return `/${locale}${cleanPath === '/' ? '' : cleanPath}`;\n}\n\n/**\n * Locale context containing resolved locale info\n */\nexport interface LocaleContext {\n /** The effective locale (never null) */\n locale: string;\n /** Path with locale prefix removed */\n pathWithoutLocale: string;\n /** Whether the current locale is the default */\n isDefaultLocale: boolean;\n}\n\n/**\n * Parse locale from path and return full context with effective locale\n * Combines extractLocaleFromPath + defaultLocale resolution in one call\n */\nexport function parseLocaleFromPath(path: string, config: I18nConfig): LocaleContext {\n const { locale, pathWithoutLocale } = extractLocaleFromPath(path, config);\n const effectiveLocale = locale || config.defaultLocale;\n\n return {\n locale: effectiveLocale,\n pathWithoutLocale,\n isDefaultLocale: effectiveLocale === config.defaultLocale,\n };\n}\n\n// ============================================\n// Client-side locale persistence utilities\n// ============================================\n\nconst LOCALE_STORAGE_KEY = 'meno_locale_preference';\nconst OLD_LOCALE_STORAGE_KEY = 'uplo_locale_preference';\n\n/**\n * Migrate locale storage key from old to new (one-time migration)\n */\nfunction migrateLocaleStorageKey(): void {\n if (typeof window === 'undefined') return;\n try {\n const oldValue = localStorage.getItem(OLD_LOCALE_STORAGE_KEY);\n const newValue = localStorage.getItem(LOCALE_STORAGE_KEY);\n if (oldValue && !newValue) {\n localStorage.setItem(LOCALE_STORAGE_KEY, oldValue);\n localStorage.removeItem(OLD_LOCALE_STORAGE_KEY);\n }\n } catch {\n // Storage not available\n }\n}\n\n/**\n * Get stored locale preference from localStorage (client-side only)\n */\nexport function getStoredLocale(): string | null {\n if (typeof window === 'undefined') return null;\n try {\n // Migrate from old key if needed\n migrateLocaleStorageKey();\n return localStorage.getItem(LOCALE_STORAGE_KEY);\n } catch {\n return null;\n }\n}\n\n/**\n * Store locale preference to localStorage (client-side only)\n */\nexport function setStoredLocale(locale: string): void {\n if (typeof window === 'undefined') return;\n try {\n localStorage.setItem(LOCALE_STORAGE_KEY, locale);\n } catch {\n // Storage not available\n }\n}\n\n/**\n * Clear stored locale preference (client-side only)\n */\nexport function clearStoredLocale(): void {\n if (typeof window === 'undefined') return;\n try {\n localStorage.removeItem(LOCALE_STORAGE_KEY);\n } catch {\n // Storage not available\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAUO,IAAM,sBAAkC;AAAA,EAC7C,eAAe;AAAA,EACf,SAAS,CAAC,EAAE,MAAM,MAAM,MAAM,WAAW,YAAY,WAAW,SAAS,QAAQ,CAAC;AACpF;AASO,SAAS,eAAe,QAA8B;AAC3D,SAAO,OAAO,QAAQ,IAAI,CAAC,QAAQ,IAAI,IAAI;AAC7C;AAKO,SAAS,iBAAiB,QAAoB,MAAwC;AAC3F,SAAO,OAAO,QAAQ,KAAK,CAAC,QAAQ,IAAI,SAAS,IAAI;AACvD;AAKO,SAAS,kBAAkB,QAAoB,MAAuB;AAC3E,SAAO,OAAO,QAAQ,KAAK,CAAC,QAAQ,IAAI,SAAS,IAAI;AACvD;AASA,SAAS,oBAAoB,MAA4B;AACvD,QAAM,YAAY,KAAK,YAAY;AACnC,SAAO;AAAA,IACL,MAAM,KAAK,YAAY;AAAA,IACvB,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,SAAS,GAAG,KAAK,YAAY,CAAC,IAAI,SAAS;AAAA,EAC7C;AACF;AAKA,SAAS,kBAAkB,SAAuC;AAChE,SAAO,MAAM,QAAQ,OAAO,KAAK,QAAQ,SAAS,KAAK,OAAO,QAAQ,CAAC,MAAM;AAC/E;AAOO,SAAS,kBAAkB,MAA2B;AAC3D,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AAEA,QAAM,SAAS;AACf,QAAM,gBAAiB,OAAO,iBAA4B;AAC1D,QAAM,UAAU,OAAO;AAEvB,MAAI,CAAC,WAAW,CAAC,MAAM,QAAQ,OAAO,GAAG;AACvC,WAAO;AAAA,EACT;AAGA,MAAI,kBAAkB,OAAO,GAAG;AAC9B,WAAO;AAAA,MACL;AAAA,MACA,SAAS,QAAQ,IAAI,mBAAmB;AAAA,IAC1C;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAKO,SAAS,YAAY,OAAoC;AAC9D,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO;AAAA,EACT;AACA,SAAO,WAAW,SAAU,MAAkC,UAAU;AAC1E;AAOO,SAAS,mBAAmB,OAAkB,QAAgB,QAA6B;AAEhG,MAAI,UAAU,SAAS,MAAM,MAAM,MAAM,UAAa,WAAW,SAAS;AACxE,WAAO,MAAM,MAAM;AAAA,EACrB;AAGA,MAAI,OAAO,iBAAiB,SAAS,MAAM,OAAO,aAAa,MAAM,QAAW;AAC9E,WAAO,MAAM,OAAO,aAAa;AAAA,EACnC;AAGA,aAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,QAAI,QAAQ,WAAW,MAAM,GAAG,MAAM,QAAW;AAC/C,aAAO,MAAM,GAAG;AAAA,IAClB;AAAA,EACF;AAIA,QAAM,gBAAgB,OAAO,QAAQ,KAAK,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,WAAW,MAAM,QAAQ,CAAC,CAAC;AAC9F,SAAO,gBAAgB,CAAC,IAAI;AAC9B;AAMO,SAAS,iBAAiB,OAAgB,QAAgB,QAA6B;AAC5F,MAAI,YAAY,KAAK,GAAG;AACtB,WAAO,mBAAmB,OAAO,QAAQ,MAAM;AAAA,EACjD;AACA,SAAO;AACT;AAKO,SAAS,mBACd,OACA,QACA,QACyB;AACzB,QAAM,WAAoC,CAAC;AAE3C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,YAAY,KAAK,GAAG;AACtB,eAAS,GAAG,IAAI,mBAAmB,OAAO,QAAQ,MAAM;AAAA,IAC1D,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,eAAS,GAAG,IAAI,MAAM,IAAI,CAAC,SAAU,YAAY,IAAI,IAAI,mBAAmB,MAAM,QAAQ,MAAM,IAAI,IAAK;AAAA,IAC3G,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AAEtD,eAAS,GAAG,IAAI,mBAAmB,OAAkC,QAAQ,MAAM;AAAA,IACrF,OAAO;AACL,eAAS,GAAG,IAAI;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,sBACd,MACA,QACsD;AAEtD,QAAM,YAAY,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,CAAC,IAAI;AACzD,QAAM,WAAW,UAAU,MAAM,GAAG;AAEpC,QAAM,eAAe,SAAS,CAAC;AAC/B,MAAI,iBAAiB,UAAa,kBAAkB,QAAQ,YAAY,GAAG;AACzE,UAAM,SAAS;AACf,UAAM,oBAAoB,IAAI,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC;AACzD,WAAO,EAAE,QAAQ,mBAAmB,qBAAqB,IAAI;AAAA,EAC/D;AAEA,SAAO,EAAE,QAAQ,MAAM,mBAAmB,KAAK;AACjD;AAMO,SAAS,mBAAmB,MAAc,QAAwB;AACvE,QAAM,YAAY,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AACxD,SAAO,IAAI,MAAM,GAAG,cAAc,MAAM,KAAK,SAAS;AACxD;AAkBO,SAAS,oBAAoB,MAAc,QAAmC;AACnF,QAAM,EAAE,QAAQ,kBAAkB,IAAI,sBAAsB,MAAM,MAAM;AACxE,QAAM,kBAAkB,UAAU,OAAO;AAEzC,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,iBAAiB,oBAAoB,OAAO;AAAA,EAC9C;AACF;AAMA,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;AAK/B,SAAS,0BAAgC;AACvC,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI;AACF,UAAM,WAAW,aAAa,QAAQ,sBAAsB;AAC5D,UAAM,WAAW,aAAa,QAAQ,kBAAkB;AACxD,QAAI,YAAY,CAAC,UAAU;AACzB,mBAAa,QAAQ,oBAAoB,QAAQ;AACjD,mBAAa,WAAW,sBAAsB;AAAA,IAChD;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAKO,SAAS,kBAAiC;AAC/C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AAEF,4BAAwB;AACxB,WAAO,aAAa,QAAQ,kBAAkB;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,gBAAgB,QAAsB;AACpD,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI;AACF,iBAAa,QAAQ,oBAAoB,MAAM;AAAA,EACjD,QAAQ;AAAA,EAER;AACF;AAKO,SAAS,oBAA0B;AACxC,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI;AACF,iBAAa,WAAW,kBAAkB;AAAA,EAC5C,QAAQ;AAAA,EAER;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -198,7 +198,7 @@ function parseParagraph(element) {
|
|
|
198
198
|
return node;
|
|
199
199
|
}
|
|
200
200
|
function parseHeading(element) {
|
|
201
|
-
const level = parseInt(element.tagName[1], 10);
|
|
201
|
+
const level = parseInt(element.tagName[1] ?? "", 10);
|
|
202
202
|
const content = parseInlineChildren(element);
|
|
203
203
|
const node = {
|
|
204
204
|
type: "heading",
|
|
@@ -696,9 +696,9 @@ function applyMark(content, mark) {
|
|
|
696
696
|
export {
|
|
697
697
|
isTiptapDocument,
|
|
698
698
|
detectRichTextFormat,
|
|
699
|
+
htmlToTiptap,
|
|
699
700
|
RICH_TEXT_ALLOWED_TAGS,
|
|
700
701
|
RICH_TEXT_ALLOWED_ATTRS,
|
|
701
|
-
tiptapToHtml
|
|
702
|
-
htmlToTiptap
|
|
702
|
+
tiptapToHtml
|
|
703
703
|
};
|
|
704
|
-
//# sourceMappingURL=chunk-
|
|
704
|
+
//# sourceMappingURL=chunk-5X4HCN7J.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../lib/shared/richtext/types.ts", "../../lib/shared/richtext/htmlToTiptap.ts", "../../lib/shared/richtext/tiptapToHtml.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Tiptap Rich Text Type Definitions\n * Defines the JSON structure used by Tiptap for rich text content\n */\n\n/**\n * Mark applied to text (bold, italic, link, etc.)\n */\nexport interface TiptapMark {\n type: string;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * A node in the Tiptap document tree\n */\nexport interface TiptapNode {\n type: string;\n attrs?: Record<string, unknown>;\n content?: TiptapNode[];\n marks?: TiptapMark[];\n text?: string;\n}\n\n/**\n * Root document structure for Tiptap content\n */\nexport interface TiptapDocument {\n type: 'doc';\n content: TiptapNode[];\n}\n\n/**\n * Type guard to check if a value is a Tiptap document\n */\nexport function isTiptapDocument(value: unknown): value is TiptapDocument {\n if (!value || typeof value !== 'object') return false;\n const doc = value as Record<string, unknown>;\n return doc.type === 'doc' && Array.isArray(doc.content);\n}\n\n/**\n * Detect the format of rich text content\n */\nexport function detectRichTextFormat(value: unknown): 'html' | 'tiptap' | 'empty' {\n if (!value) return 'empty';\n if (typeof value === 'string') return value.length > 0 ? 'html' : 'empty';\n if (isTiptapDocument(value)) return 'tiptap';\n return 'empty';\n}\n\n/**\n * Known Tiptap node types\n */\nexport type TiptapNodeType =\n | 'doc'\n | 'paragraph'\n | 'text'\n | 'heading'\n | 'bulletList'\n | 'orderedList'\n | 'listItem'\n | 'blockquote'\n | 'codeBlock'\n | 'hardBreak'\n | 'horizontalRule'\n | 'image'\n | 'table'\n | 'tableRow'\n | 'tableCell'\n | 'tableHeader'\n | 'iframe'\n | 'rawHtml'\n | 'menoComponent';\n\n/**\n * Known Tiptap mark types\n */\nexport type TiptapMarkType = 'bold' | 'italic' | 'underline' | 'strike' | 'code' | 'link' | 'textStyle';\n", "/**\n * HTML to Tiptap JSON Converter\n * Converts legacy HTML rich text content to Tiptap document format\n */\n\nimport type { TiptapDocument, TiptapNode, TiptapMark } from './types';\n\n/**\n * Parse HTML string and convert to Tiptap document\n */\nexport function htmlToTiptap(html: string): TiptapDocument {\n if (!html || typeof html !== 'string') {\n return { type: 'doc', content: [] };\n }\n\n // Use DOMParser if available (browser), otherwise use a simple regex-based approach\n if (typeof DOMParser !== 'undefined') {\n return parseWithDOM(html);\n }\n\n // Fallback for server-side: wrap in paragraph\n return {\n type: 'doc',\n content: [{ type: 'paragraph', content: [{ type: 'text', text: stripHtml(html) }] }],\n };\n}\n\n/**\n * Strip HTML tags for fallback text extraction\n */\nfunction stripHtml(html: string): string {\n return html.replace(/<[^>]*>/g, '').trim();\n}\n\n/**\n * Parse HTML using DOMParser (browser environment)\n */\nfunction parseWithDOM(html: string): TiptapDocument {\n const parser = new DOMParser();\n const doc = parser.parseFromString(`<div>${html}</div>`, 'text/html');\n const container = doc.body.firstChild as HTMLElement;\n\n if (!container) {\n return { type: 'doc', content: [] };\n }\n\n const content = parseChildren(container);\n\n // If content is empty or only has inline content, wrap in paragraph\n if (content.length === 0) {\n return { type: 'doc', content: [] };\n }\n\n // Ensure top-level nodes are block-level\n const blockContent = wrapInlineContent(content);\n\n return { type: 'doc', content: blockContent };\n}\n\n/**\n * Wrap consecutive inline nodes in paragraphs\n */\nfunction wrapInlineContent(nodes: TiptapNode[]): TiptapNode[] {\n const result: TiptapNode[] = [];\n let inlineBuffer: TiptapNode[] = [];\n\n const flushInline = () => {\n if (inlineBuffer.length > 0) {\n result.push({ type: 'paragraph', content: inlineBuffer });\n inlineBuffer = [];\n }\n };\n\n for (const node of nodes) {\n if (isBlockNode(node.type)) {\n flushInline();\n result.push(node);\n } else {\n inlineBuffer.push(node);\n }\n }\n\n flushInline();\n return result;\n}\n\n/**\n * Check if a node type is block-level\n */\nfunction isBlockNode(type: string): boolean {\n return [\n 'paragraph',\n 'heading',\n 'bulletList',\n 'orderedList',\n 'listItem',\n 'blockquote',\n 'codeBlock',\n 'horizontalRule',\n 'image',\n 'table',\n 'tableRow',\n 'tableCell',\n 'tableHeader',\n 'iframe',\n 'rawHtml',\n 'menoComponent',\n ].includes(type);\n}\n\n/**\n * Parse child nodes of an element\n */\nfunction parseChildren(element: HTMLElement): TiptapNode[] {\n const nodes: TiptapNode[] = [];\n\n for (const child of Array.from(element.childNodes)) {\n const parsed = parseNode(child);\n if (parsed) {\n if (Array.isArray(parsed)) {\n nodes.push(...parsed);\n } else {\n nodes.push(parsed);\n }\n }\n }\n\n return nodes;\n}\n\n/**\n * Parse a single DOM node\n */\nfunction parseNode(node: Node): TiptapNode | TiptapNode[] | null {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent;\n if (!text) return null;\n return { type: 'text', text };\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return null;\n }\n\n const element = node as HTMLElement;\n const tagName = element.tagName.toLowerCase();\n\n switch (tagName) {\n // Block elements\n case 'p':\n return parseParagraph(element);\n case 'h1':\n case 'h2':\n case 'h3':\n case 'h4':\n case 'h5':\n case 'h6':\n return parseHeading(element);\n case 'ul':\n return parseBulletList(element);\n case 'ol':\n return parseOrderedList(element);\n case 'li':\n return parseListItem(element);\n case 'blockquote':\n return parseBlockquote(element);\n case 'pre':\n return parseCodeBlock(element);\n case 'hr':\n return { type: 'horizontalRule' };\n case 'br':\n return { type: 'hardBreak' };\n case 'img':\n return parseImage(element);\n case 'iframe':\n return parseIframe(element);\n case 'table':\n return parseTable(element);\n\n // Inline elements - return text with marks\n case 'b':\n case 'strong':\n return parseInlineWithMark(element, 'bold');\n case 'i':\n case 'em':\n return parseInlineWithMark(element, 'italic');\n case 'u':\n return parseInlineWithMark(element, 'underline');\n case 's':\n case 'strike':\n case 'del':\n return parseInlineWithMark(element, 'strike');\n case 'code':\n return parseInlineWithMark(element, 'code');\n case 'a':\n return parseLink(element);\n case 'span':\n return parseSpan(element);\n\n // Container elements - check for special data attributes first\n case 'div': {\n // Raw HTML block\n if (element.hasAttribute('data-raw-html')) {\n return { type: 'rawHtml', attrs: { content: element.innerHTML } };\n }\n // Meno component embed\n if (element.hasAttribute('data-meno-component')) {\n return parseMenoComponent(element);\n }\n // Regular div - parse children\n return parseChildren(element);\n }\n case 'article':\n case 'section':\n case 'main':\n case 'header':\n case 'footer':\n case 'thead':\n case 'tbody':\n case 'tfoot':\n return parseChildren(element);\n\n case 'tr':\n return parseTableRow(element);\n case 'td':\n return parseTableCell(element, 'tableCell');\n case 'th':\n return parseTableCell(element, 'tableHeader');\n\n default:\n // Unknown elements - try to parse children\n return parseChildren(element);\n }\n}\n\nfunction parseParagraph(element: HTMLElement): TiptapNode {\n const content = parseInlineChildren(element);\n const node: TiptapNode = { type: 'paragraph' };\n if (content.length > 0) {\n node.content = content;\n }\n\n // Handle text alignment\n const align = element.style.textAlign;\n if (align && ['left', 'center', 'right', 'justify'].includes(align)) {\n node.attrs = { textAlign: align };\n }\n\n return node;\n}\n\nfunction parseHeading(element: HTMLElement): TiptapNode {\n const level = parseInt(element.tagName[1] ?? '', 10);\n const content = parseInlineChildren(element);\n const node: TiptapNode = {\n type: 'heading',\n attrs: { level },\n };\n if (content.length > 0) {\n node.content = content;\n }\n\n // Handle text alignment\n const align = element.style.textAlign;\n if (align && ['left', 'center', 'right', 'justify'].includes(align)) {\n node.attrs = { ...node.attrs, textAlign: align };\n }\n\n return node;\n}\n\nfunction parseBulletList(element: HTMLElement): TiptapNode {\n const items = Array.from(element.children)\n .filter((child) => child.tagName.toLowerCase() === 'li')\n .map((li) => parseListItem(li as HTMLElement));\n return { type: 'bulletList', content: items };\n}\n\nfunction parseOrderedList(element: HTMLElement): TiptapNode {\n const items = Array.from(element.children)\n .filter((child) => child.tagName.toLowerCase() === 'li')\n .map((li) => parseListItem(li as HTMLElement));\n const node: TiptapNode = { type: 'orderedList', content: items };\n\n // Preserve start attribute\n const start = element.getAttribute('start');\n if (start) {\n node.attrs = { start: parseInt(start, 10) };\n }\n\n return node;\n}\n\nfunction parseListItem(element: HTMLElement): TiptapNode {\n // List items can contain block content or inline content\n const children = parseChildren(element);\n\n // If children are all inline, wrap in paragraph\n const hasBlockContent = children.some((child) => isBlockNode(child.type));\n\n if (hasBlockContent) {\n return { type: 'listItem', content: wrapInlineContent(children) };\n }\n\n // Wrap inline content in paragraph\n if (children.length > 0) {\n return { type: 'listItem', content: [{ type: 'paragraph', content: children }] };\n }\n\n return { type: 'listItem', content: [{ type: 'paragraph' }] };\n}\n\nfunction parseBlockquote(element: HTMLElement): TiptapNode {\n const children = parseChildren(element);\n const content = wrapInlineContent(children);\n return { type: 'blockquote', content: content.length > 0 ? content : [{ type: 'paragraph' }] };\n}\n\nfunction parseCodeBlock(element: HTMLElement): TiptapNode {\n // Get language from class (e.g., \"language-javascript\")\n const codeElement = element.querySelector('code');\n const targetElement = codeElement || element;\n\n let language: string | null = null;\n const classList = targetElement.className.split(' ');\n for (const cls of classList) {\n if (cls.startsWith('language-')) {\n language = cls.replace('language-', '');\n break;\n }\n }\n\n const text = targetElement.textContent || '';\n const node: TiptapNode = {\n type: 'codeBlock',\n content: text ? [{ type: 'text', text }] : [],\n };\n\n if (language) {\n node.attrs = { language };\n }\n\n return node;\n}\n\nfunction parseImage(element: HTMLElement): TiptapNode {\n const src = element.getAttribute('src') || '';\n const alt = element.getAttribute('alt') || undefined;\n const title = element.getAttribute('title') || undefined;\n const width = element.getAttribute('width');\n const height = element.getAttribute('height');\n\n const attrs: Record<string, unknown> = { src };\n if (alt) attrs.alt = alt;\n if (title) attrs.title = title;\n if (width) attrs.width = parseInt(width, 10);\n if (height) attrs.height = parseInt(height, 10);\n\n return { type: 'image', attrs };\n}\n\nfunction parseIframe(element: HTMLElement): TiptapNode {\n const src = element.getAttribute('src') || '';\n const width = element.getAttribute('width') || '100%';\n const height = element.getAttribute('height') || '315';\n\n return {\n type: 'iframe',\n attrs: { src, width, height },\n };\n}\n\nfunction parseMenoComponent(element: HTMLElement): TiptapNode {\n const component = element.getAttribute('data-meno-component') || '';\n let props: Record<string, unknown> = {};\n const propsStr = element.getAttribute('data-meno-props');\n if (propsStr) {\n try {\n props = JSON.parse(propsStr);\n } catch {\n // ignore parse errors\n }\n }\n return {\n type: 'menoComponent',\n attrs: { component, props },\n };\n}\n\nfunction parseTable(element: HTMLElement): TiptapNode {\n const rows: TiptapNode[] = [];\n\n // Parse thead, tbody, tfoot\n for (const section of ['thead', 'tbody', 'tfoot']) {\n const sectionEl = element.querySelector(section);\n if (sectionEl) {\n const sectionRows = Array.from(sectionEl.querySelectorAll('tr'));\n for (const tr of sectionRows) {\n const row = parseTableRow(tr as HTMLElement, section === 'thead');\n if (row) rows.push(row);\n }\n }\n }\n\n // Also check direct tr children\n const directRows = Array.from(element.children).filter((child) => child.tagName.toLowerCase() === 'tr');\n for (const tr of directRows) {\n const row = parseTableRow(tr as HTMLElement);\n if (row) rows.push(row);\n }\n\n return { type: 'table', content: rows };\n}\n\nfunction parseTableRow(element: HTMLElement, isHeader = false): TiptapNode {\n const cells: TiptapNode[] = [];\n\n for (const child of Array.from(element.children)) {\n const tagName = child.tagName.toLowerCase();\n if (tagName === 'td' || tagName === 'th') {\n const cellType = tagName === 'th' || isHeader ? 'tableHeader' : 'tableCell';\n const cell = parseTableCell(child as HTMLElement, cellType);\n cells.push(cell);\n }\n }\n\n return { type: 'tableRow', content: cells };\n}\n\nfunction parseTableCell(element: HTMLElement, type: 'tableCell' | 'tableHeader'): TiptapNode {\n const children = parseChildren(element);\n const content = wrapInlineContent(children);\n\n const attrs: Record<string, unknown> = {};\n const colspan = element.getAttribute('colspan');\n const rowspan = element.getAttribute('rowspan');\n if (colspan) attrs.colspan = parseInt(colspan, 10);\n if (rowspan) attrs.rowspan = parseInt(rowspan, 10);\n\n const node: TiptapNode = {\n type,\n content: content.length > 0 ? content : [{ type: 'paragraph' }],\n };\n\n if (Object.keys(attrs).length > 0) {\n node.attrs = attrs;\n }\n\n return node;\n}\n\n/**\n * Parse inline children, returning text nodes with marks\n */\nfunction parseInlineChildren(element: HTMLElement): TiptapNode[] {\n const nodes: TiptapNode[] = [];\n\n for (const child of Array.from(element.childNodes)) {\n if (child.nodeType === Node.TEXT_NODE) {\n const text = child.textContent;\n if (text) {\n nodes.push({ type: 'text', text });\n }\n } else if (child.nodeType === Node.ELEMENT_NODE) {\n const parsed = parseNode(child);\n if (parsed) {\n if (Array.isArray(parsed)) {\n nodes.push(...parsed);\n } else {\n nodes.push(parsed);\n }\n }\n }\n }\n\n return nodes;\n}\n\n/**\n * Parse inline element with a specific mark\n */\nfunction parseInlineWithMark(element: HTMLElement, markType: string): TiptapNode[] {\n const children = parseInlineChildren(element);\n return addMarkToNodes(children, { type: markType });\n}\n\n/**\n * Parse a link element\n */\nfunction parseLink(element: HTMLElement): TiptapNode[] {\n const href = element.getAttribute('href') || '';\n const target = element.getAttribute('target');\n const rel = element.getAttribute('rel');\n\n const attrs: Record<string, unknown> = { href };\n if (target) attrs.target = target;\n if (rel) attrs.rel = rel;\n\n const children = parseInlineChildren(element);\n return addMarkToNodes(children, { type: 'link', attrs });\n}\n\n/**\n * Parse a span element (may have custom class)\n */\nfunction parseSpan(element: HTMLElement): TiptapNode[] {\n const children = parseInlineChildren(element);\n\n // Check for data-meno-span attribute (custom styling)\n const menoSpan = element.getAttribute('data-meno-span');\n if (menoSpan) {\n return addMarkToNodes(children, { type: 'textStyle', attrs: { class: menoSpan } });\n }\n\n // Check for inline styles\n const style = element.getAttribute('style');\n if (style) {\n // Could add textStyle mark with color, background, etc.\n // For now, just return children\n }\n\n return children;\n}\n\n/**\n * Add a mark to all text nodes in a list\n */\nfunction addMarkToNodes(nodes: TiptapNode[], mark: TiptapMark): TiptapNode[] {\n return nodes.map((node) => {\n if (node.type === 'text') {\n const existingMarks = node.marks || [];\n return { ...node, marks: [...existingMarks, mark] };\n }\n // For non-text nodes, recursively add mark to content\n if (node.content) {\n return { ...node, content: addMarkToNodes(node.content, mark) };\n }\n return node;\n });\n}\n", "/**\n * Tiptap JSON to HTML Converter\n * Converts Tiptap document format to sanitized HTML for rendering\n */\n\nimport type { TiptapDocument, TiptapNode, TiptapMark } from './types';\nimport { isTiptapDocument } from './types';\n\n/**\n * DOMPurify-compatible sanitization config for rich text output\n */\nexport const RICH_TEXT_ALLOWED_TAGS = [\n 'b',\n 'i',\n 'u',\n 'strong',\n 'em',\n 'a',\n 'span',\n 'p',\n 'br',\n 'sub',\n 'sup',\n 'mark',\n 's',\n 'small',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'ul',\n 'ol',\n 'li',\n 'blockquote',\n // Extended features\n 'table',\n 'thead',\n 'tbody',\n 'tr',\n 'th',\n 'td',\n 'img',\n 'pre',\n 'code',\n 'hr',\n 'figure',\n 'figcaption',\n // Embeds\n 'iframe',\n 'div',\n];\n\nexport const RICH_TEXT_ALLOWED_ATTRS = [\n 'class',\n 'href',\n 'target',\n 'rel',\n 'data-meno-span',\n // Table attributes\n 'colspan',\n 'rowspan',\n // Image attributes\n 'src',\n 'alt',\n 'width',\n 'height',\n 'title',\n // Code block attributes\n 'data-language',\n // Alignment (via style)\n 'style',\n // Iframe attributes\n 'frameborder',\n 'allowfullscreen',\n 'allow',\n // Raw HTML / Component attributes\n 'data-raw-html',\n 'data-meno-component',\n 'data-meno-props',\n];\n\n/**\n * Allowed URL protocols for links\n */\nconst ALLOWED_URL_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:'];\n\n/**\n * Validate URL protocol for security\n */\nfunction isValidUrl(url: string): boolean {\n if (!url || typeof url !== 'string') return false;\n if (url.startsWith('/') || url.startsWith('#')) return true;\n\n try {\n const parsed = new URL(url, 'https://example.com');\n return ALLOWED_URL_PROTOCOLS.includes(parsed.protocol);\n } catch {\n return !url.includes(':');\n }\n}\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Escape attribute value\n */\nfunction escapeAttr(value: string): string {\n return value.replace(/\"/g, '"').replace(/'/g, ''');\n}\n\n/**\n * Convert Tiptap document to HTML string\n */\nexport function tiptapToHtml(doc: TiptapDocument | unknown): string {\n if (!isTiptapDocument(doc)) {\n // If it's already HTML string, return as-is\n if (typeof doc === 'string') {\n return doc;\n }\n return '';\n }\n\n return renderNodes(doc.content);\n}\n\n/**\n * Render an array of nodes to HTML\n */\nfunction renderNodes(nodes: TiptapNode[]): string {\n return nodes.map(renderNode).join('');\n}\n\n/**\n * Render a single node to HTML\n */\nfunction renderNode(node: TiptapNode): string {\n switch (node.type) {\n case 'doc':\n return node.content ? renderNodes(node.content) : '';\n\n case 'paragraph':\n return renderParagraph(node);\n\n case 'heading':\n return renderHeading(node);\n\n case 'text':\n return renderText(node);\n\n case 'hardBreak':\n return '<br>';\n\n case 'horizontalRule':\n return '<hr>';\n\n case 'bulletList':\n return `<ul>${node.content ? renderNodes(node.content) : ''}</ul>`;\n\n case 'orderedList':\n return renderOrderedList(node);\n\n case 'listItem':\n return `<li>${node.content ? renderNodes(node.content) : ''}</li>`;\n\n case 'blockquote':\n return `<blockquote>${node.content ? renderNodes(node.content) : ''}</blockquote>`;\n\n case 'codeBlock':\n return renderCodeBlock(node);\n\n case 'image':\n return renderImage(node);\n\n case 'table':\n return `<table>${node.content ? renderNodes(node.content) : ''}</table>`;\n\n case 'tableRow':\n return `<tr>${node.content ? renderNodes(node.content) : ''}</tr>`;\n\n case 'tableCell':\n return renderTableCell(node, 'td');\n\n case 'tableHeader':\n return renderTableCell(node, 'th');\n\n case 'iframe':\n return renderIframe(node);\n\n case 'rawHtml':\n return renderRawHtml(node);\n\n case 'menoComponent':\n return renderMenoComponent(node);\n\n default:\n // Unknown node type - try to render content\n if (node.content) {\n return renderNodes(node.content);\n }\n return '';\n }\n}\n\n/**\n * Render paragraph with optional text alignment\n */\nfunction renderParagraph(node: TiptapNode): string {\n const content = node.content ? renderNodes(node.content) : '';\n const align = node.attrs?.textAlign as string | undefined;\n\n if (align && ['left', 'center', 'right', 'justify'].includes(align)) {\n return `<p style=\"text-align: ${align}\">${content}</p>`;\n }\n\n return `<p>${content}</p>`;\n}\n\n/**\n * Render heading with level and optional text alignment\n */\nfunction renderHeading(node: TiptapNode): string {\n const level = (node.attrs?.level as number) || 1;\n const tag = `h${Math.min(Math.max(level, 1), 6)}`;\n const content = node.content ? renderNodes(node.content) : '';\n const align = node.attrs?.textAlign as string | undefined;\n\n if (align && ['left', 'center', 'right', 'justify'].includes(align)) {\n return `<${tag} style=\"text-align: ${align}\">${content}</${tag}>`;\n }\n\n return `<${tag}>${content}</${tag}>`;\n}\n\n/**\n * Render ordered list with optional start attribute\n */\nfunction renderOrderedList(node: TiptapNode): string {\n const content = node.content ? renderNodes(node.content) : '';\n const start = node.attrs?.start as number | undefined;\n\n if (start && start !== 1) {\n return `<ol start=\"${start}\">${content}</ol>`;\n }\n\n return `<ol>${content}</ol>`;\n}\n\n/**\n * Render code block with optional language\n */\nfunction renderCodeBlock(node: TiptapNode): string {\n const language = node.attrs?.language as string | undefined;\n const content = node.content\n ? node.content.map((n) => (n.type === 'text' ? escapeHtml(n.text || '') : '')).join('')\n : '';\n\n if (language) {\n return `<pre><code class=\"language-${escapeAttr(language)}\" data-language=\"${escapeAttr(language)}\">${content}</code></pre>`;\n }\n\n return `<pre><code>${content}</code></pre>`;\n}\n\n/**\n * Render image with attributes\n */\nfunction renderImage(node: TiptapNode): string {\n const src = node.attrs?.src as string | undefined;\n if (!src) return '';\n\n // Validate image src for security\n if (!isValidUrl(src) && !src.startsWith('data:image/')) {\n return '';\n }\n\n const attrs: string[] = [`src=\"${escapeAttr(src)}\"`];\n\n const alt = node.attrs?.alt as string | undefined;\n if (alt) attrs.push(`alt=\"${escapeAttr(alt)}\"`);\n\n const title = node.attrs?.title as string | undefined;\n if (title) attrs.push(`title=\"${escapeAttr(title)}\"`);\n\n const width = node.attrs?.width as number | undefined;\n if (width) attrs.push(`width=\"${width}\"`);\n\n const height = node.attrs?.height as number | undefined;\n if (height) attrs.push(`height=\"${height}\"`);\n\n return `<img ${attrs.join(' ')}>`;\n}\n\n/**\n * Render table cell (td or th)\n */\nfunction renderTableCell(node: TiptapNode, tag: 'td' | 'th'): string {\n const content = node.content ? renderNodes(node.content) : '';\n const attrs: string[] = [];\n\n const colspan = node.attrs?.colspan as number | undefined;\n if (colspan && colspan > 1) attrs.push(`colspan=\"${colspan}\"`);\n\n const rowspan = node.attrs?.rowspan as number | undefined;\n if (rowspan && rowspan > 1) attrs.push(`rowspan=\"${rowspan}\"`);\n\n const attrStr = attrs.length > 0 ? ` ${attrs.join(' ')}` : '';\n return `<${tag}${attrStr}>${content}</${tag}>`;\n}\n\n/**\n * Render iframe embed\n */\nfunction renderIframe(node: TiptapNode): string {\n const src = node.attrs?.src as string | undefined;\n if (!src) return '';\n\n // Only allow https:// URLs for security\n if (!src.startsWith('https://')) return '';\n\n const width = (node.attrs?.width as string | undefined) || '100%';\n const height = (node.attrs?.height as string | undefined) || '315';\n\n return `<iframe src=\"${escapeAttr(src)}\" width=\"${escapeAttr(String(width))}\" height=\"${escapeAttr(String(height))}\" frameborder=\"0\" allowfullscreen></iframe>`;\n}\n\n/**\n * Render raw HTML block\n */\nfunction renderRawHtml(node: TiptapNode): string {\n const content = node.attrs?.content as string | undefined;\n if (!content) return '';\n\n // Strip script tags and event handlers for safety\n const sanitized = content\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n .replace(/\\son\\w+\\s*=\\s*(['\"])[^'\"]*\\1/gi, '')\n .replace(/\\son\\w+\\s*=\\s*[^\\s>]*/gi, '');\n\n return `<div data-raw-html=\"true\">${sanitized}</div>`;\n}\n\n/**\n * Render Meno component marker for SSR expansion\n */\nfunction renderMenoComponent(node: TiptapNode): string {\n const component = node.attrs?.component as string | undefined;\n if (!component) return '';\n\n const props = node.attrs?.props as Record<string, unknown> | undefined;\n const propsJson = props ? escapeAttr(JSON.stringify(props)) : '{}';\n\n return `<div data-meno-component=\"${escapeAttr(component)}\" data-meno-props=\"${propsJson}\"></div>`;\n}\n\n/**\n * Render text node with marks\n */\nfunction renderText(node: TiptapNode): string {\n const text = escapeHtml(node.text || '');\n\n if (!node.marks || node.marks.length === 0) {\n return text;\n }\n\n // Apply marks from innermost to outermost\n let result = text;\n for (const mark of node.marks) {\n result = applyMark(result, mark);\n }\n\n return result;\n}\n\n/**\n * Apply a mark to text content\n */\nfunction applyMark(content: string, mark: TiptapMark): string {\n switch (mark.type) {\n case 'bold':\n return `<strong>${content}</strong>`;\n\n case 'italic':\n return `<em>${content}</em>`;\n\n case 'underline':\n return `<u>${content}</u>`;\n\n case 'strike':\n return `<s>${content}</s>`;\n\n case 'code':\n return `<code>${content}</code>`;\n\n case 'link': {\n const href = mark.attrs?.href as string | undefined;\n if (!href || !isValidUrl(href)) {\n return content;\n }\n\n const attrs: string[] = [`href=\"${escapeAttr(href)}\"`];\n\n const target = mark.attrs?.target as string | undefined;\n if (target) attrs.push(`target=\"${escapeAttr(target)}\"`);\n\n // Add rel for external links\n if (target === '_blank') {\n attrs.push('rel=\"noopener noreferrer\"');\n } else {\n const rel = mark.attrs?.rel as string | undefined;\n if (rel) attrs.push(`rel=\"${escapeAttr(rel)}\"`);\n }\n\n return `<a ${attrs.join(' ')}>${content}</a>`;\n }\n\n case 'textStyle': {\n const className = mark.attrs?.class as string | undefined;\n if (className) {\n return `<span class=\"${escapeAttr(className)}\" data-meno-span=\"${escapeAttr(className)}\">${content}</span>`;\n }\n return content;\n }\n\n case 'subscript':\n return `<sub>${content}</sub>`;\n\n case 'superscript':\n return `<sup>${content}</sup>`;\n\n case 'highlight':\n return `<mark>${content}</mark>`;\n\n default:\n return content;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -3,21 +3,21 @@ import {
|
|
|
3
3
|
} from "./chunk-KSBZ2L7C.js";
|
|
4
4
|
|
|
5
5
|
// lib/server/runtime/fs.ts
|
|
6
|
-
import { dirname } from "path";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
6
|
+
import { dirname } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
8
|
var isBun = typeof globalThis.Bun !== "undefined";
|
|
9
9
|
async function readTextFile(path) {
|
|
10
10
|
if (isBun) {
|
|
11
11
|
return Bun.file(path).text();
|
|
12
12
|
}
|
|
13
|
-
const { readFile } = await import("fs/promises");
|
|
13
|
+
const { readFile } = await import("node:fs/promises");
|
|
14
14
|
return readFile(path, "utf-8");
|
|
15
15
|
}
|
|
16
16
|
async function readJsonFile(path) {
|
|
17
17
|
if (isBun) {
|
|
18
18
|
return Bun.file(path).json();
|
|
19
19
|
}
|
|
20
|
-
const { readFile } = await import("fs/promises");
|
|
20
|
+
const { readFile } = await import("node:fs/promises");
|
|
21
21
|
const text = await readFile(path, "utf-8");
|
|
22
22
|
return JSON.parse(text);
|
|
23
23
|
}
|
|
@@ -25,7 +25,7 @@ async function fileExists(path) {
|
|
|
25
25
|
if (isBun) {
|
|
26
26
|
return Bun.file(path).exists();
|
|
27
27
|
}
|
|
28
|
-
const { access } = await import("fs/promises");
|
|
28
|
+
const { access } = await import("node:fs/promises");
|
|
29
29
|
try {
|
|
30
30
|
await access(path);
|
|
31
31
|
return true;
|
|
@@ -38,7 +38,7 @@ async function writeFile(path, content) {
|
|
|
38
38
|
await Bun.write(path, content);
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
const { writeFile: fsWrite } = await import("fs/promises");
|
|
41
|
+
const { writeFile: fsWrite } = await import("node:fs/promises");
|
|
42
42
|
if (typeof content === "string") {
|
|
43
43
|
await fsWrite(path, content, "utf-8");
|
|
44
44
|
} else if (content instanceof ArrayBuffer) {
|
|
@@ -87,8 +87,8 @@ async function serveFile(path, headers) {
|
|
|
87
87
|
if (isBun) {
|
|
88
88
|
return new Response(Bun.file(path), { headers });
|
|
89
89
|
}
|
|
90
|
-
const { createReadStream } = await import("fs");
|
|
91
|
-
const { Readable } = await import("stream");
|
|
90
|
+
const { createReadStream } = await import("node:fs");
|
|
91
|
+
const { Readable } = await import("node:stream");
|
|
92
92
|
const nodeStream = createReadStream(path);
|
|
93
93
|
const webStream = Readable.toWeb(nodeStream);
|
|
94
94
|
const contentType = getMimeType(path);
|
|
@@ -103,18 +103,18 @@ function hashContent(content) {
|
|
|
103
103
|
if (isBun) {
|
|
104
104
|
return Bun.hash(content).toString(16).slice(0, 8);
|
|
105
105
|
}
|
|
106
|
-
const { createHash } = __require("crypto");
|
|
106
|
+
const { createHash } = __require("node:crypto");
|
|
107
107
|
return createHash("sha256").update(content).digest("hex").slice(0, 8);
|
|
108
108
|
}
|
|
109
109
|
function inspect(value) {
|
|
110
110
|
if (isBun) {
|
|
111
111
|
return Bun.inspect(value);
|
|
112
112
|
}
|
|
113
|
-
const { inspect: nodeInspect } = __require("util");
|
|
113
|
+
const { inspect: nodeInspect } = __require("node:util");
|
|
114
114
|
return nodeInspect(value, { depth: 4 });
|
|
115
115
|
}
|
|
116
116
|
async function ensureDir(path) {
|
|
117
|
-
const { mkdir } = await import("fs/promises");
|
|
117
|
+
const { mkdir } = await import("node:fs/promises");
|
|
118
118
|
await mkdir(path, { recursive: true });
|
|
119
119
|
}
|
|
120
120
|
function getModuleDir(importMetaUrl) {
|
|
@@ -130,7 +130,7 @@ async function spawnProcess(cmd, args, opts) {
|
|
|
130
130
|
const exitCode = await proc.exited;
|
|
131
131
|
return { exitCode };
|
|
132
132
|
}
|
|
133
|
-
const { spawn } = await import("child_process");
|
|
133
|
+
const { spawn } = await import("node:child_process");
|
|
134
134
|
return new Promise((resolve, reject) => {
|
|
135
135
|
const child = spawn(cmd, args, {
|
|
136
136
|
cwd: opts?.cwd,
|
|
@@ -158,7 +158,7 @@ async function getFileSize(path) {
|
|
|
158
158
|
if (isBun) {
|
|
159
159
|
return Bun.file(path).size;
|
|
160
160
|
}
|
|
161
|
-
const { stat } = await import("fs/promises");
|
|
161
|
+
const { stat } = await import("node:fs/promises");
|
|
162
162
|
const stats = await stat(path);
|
|
163
163
|
return stats.size;
|
|
164
164
|
}
|
|
@@ -177,4 +177,4 @@ export {
|
|
|
177
177
|
spawnProcess,
|
|
178
178
|
getFileSize
|
|
179
179
|
};
|
|
180
|
-
//# sourceMappingURL=chunk-
|
|
180
|
+
//# sourceMappingURL=chunk-7CCVOL5M.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../lib/server/runtime/fs.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Runtime-agnostic file I/O and utilities.\n * Uses Bun APIs when available, falls back to Node.js APIs.\n */\n\nimport { dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst isBun = typeof globalThis.Bun !== 'undefined';\n\n// \u2500\u2500 File reading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function readTextFile(path: string): Promise<string> {\n if (isBun) {\n return Bun.file(path).text();\n }\n const { readFile } = await import('node:fs/promises');\n return readFile(path, 'utf-8');\n}\n\nexport async function readJsonFile<T = unknown>(path: string): Promise<T> {\n if (isBun) {\n return Bun.file(path).json() as Promise<T>;\n }\n const { readFile } = await import('node:fs/promises');\n const text = await readFile(path, 'utf-8');\n return JSON.parse(text) as T;\n}\n\n// \u2500\u2500 File existence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function fileExists(path: string): Promise<boolean> {\n if (isBun) {\n return Bun.file(path).exists();\n }\n const { access } = await import('node:fs/promises');\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n// \u2500\u2500 File writing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function writeFile(path: string, content: string | Uint8Array | ArrayBuffer): Promise<void> {\n if (isBun) {\n await Bun.write(path, content);\n return;\n }\n const { writeFile: fsWrite } = await import('node:fs/promises');\n if (typeof content === 'string') {\n await fsWrite(path, content, 'utf-8');\n } else if (content instanceof ArrayBuffer) {\n await fsWrite(path, new Uint8Array(content));\n } else {\n await fsWrite(path, content);\n }\n}\n\n// \u2500\u2500 Serve file as HTTP Response \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** MIME type lookup by extension */\nconst MIME_MAP: Record<string, string> = {\n '.html': 'text/html',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.bmp': 'image/bmp',\n '.tiff': 'image/tiff',\n '.tif': 'image/tiff',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.eot': 'application/vnd.ms-fontobject',\n '.xml': 'application/xml',\n '.txt': 'text/plain',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.mov': 'video/quicktime',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.pdf': 'application/pdf',\n '.zip': 'application/zip',\n};\n\nexport function getMimeType(filePath: string): string {\n const ext = filePath.slice(filePath.lastIndexOf('.')).toLowerCase();\n return MIME_MAP[ext] || 'application/octet-stream';\n}\n\nexport async function serveFile(path: string, headers?: Record<string, string>): Promise<Response> {\n if (isBun) {\n return new Response(Bun.file(path), { headers });\n }\n\n // Node.js path: stream the file via Web ReadableStream\n const { createReadStream } = await import('node:fs');\n const { Readable } = await import('node:stream');\n const nodeStream = createReadStream(path);\n const webStream = Readable.toWeb(nodeStream) as unknown as ReadableStream;\n\n const contentType = getMimeType(path);\n return new Response(webStream, {\n headers: {\n 'Content-Type': contentType,\n ...headers,\n },\n });\n}\n\n// \u2500\u2500 Hashing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function hashContent(content: string): string {\n if (isBun) {\n return Bun.hash(content).toString(16).slice(0, 8);\n }\n const { createHash } = require('node:crypto') as typeof import('crypto');\n return createHash('sha256').update(content).digest('hex').slice(0, 8);\n}\n\n// \u2500\u2500 Inspect (debug formatting) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function inspect(value: unknown): string {\n if (isBun) {\n return Bun.inspect(value);\n }\n const { inspect: nodeInspect } = require('node:util') as typeof import('util');\n return nodeInspect(value, { depth: 4 });\n}\n\n// \u2500\u2500 Directory helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function ensureDir(path: string): Promise<void> {\n const { mkdir } = await import('node:fs/promises');\n await mkdir(path, { recursive: true });\n}\n\n/**\n * Get the directory of the calling module.\n * Replaces Bun-only `import.meta.dir`.\n * Pass `import.meta.url` from the call site.\n */\nexport function getModuleDir(importMetaUrl: string): string {\n return dirname(fileURLToPath(importMetaUrl));\n}\n\n// \u2500\u2500 Process spawning \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SpawnResult {\n exitCode: number;\n stdout?: string;\n stderr?: string;\n}\n\nexport async function spawnProcess(\n cmd: string,\n args: string[],\n opts?: {\n cwd?: string;\n stdout?: 'inherit' | 'pipe';\n stderr?: 'inherit' | 'pipe';\n },\n): Promise<SpawnResult> {\n if (isBun) {\n const proc = Bun.spawn([cmd, ...args], {\n cwd: opts?.cwd,\n stdout: opts?.stdout === 'pipe' ? 'pipe' : 'inherit',\n stderr: opts?.stderr === 'pipe' ? 'pipe' : 'inherit',\n });\n const exitCode = await proc.exited;\n return { exitCode };\n }\n\n const { spawn } = await import('node:child_process');\n return new Promise((resolve, reject) => {\n const child = spawn(cmd, args, {\n cwd: opts?.cwd,\n stdio: ['ignore', opts?.stdout === 'pipe' ? 'pipe' : 'inherit', opts?.stderr === 'pipe' ? 'pipe' : 'inherit'],\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout?.on('data', (data: Buffer) => {\n stdout += data.toString();\n });\n child.stderr?.on('data', (data: Buffer) => {\n stderr += data.toString();\n });\n\n child.on('error', reject);\n child.on('close', (code) => {\n resolve({\n exitCode: code ?? 1,\n stdout: opts?.stdout === 'pipe' ? stdout : undefined,\n stderr: opts?.stderr === 'pipe' ? stderr : undefined,\n });\n });\n });\n}\n\n// \u2500\u2500 File size (for image processing service) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function getFileSize(path: string): Promise<number> {\n if (isBun) {\n return Bun.file(path).size;\n }\n const { stat } = await import('node:fs/promises');\n const stats = await stat(path);\n return stats.size;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|