@agimon-ai/po 0.0.0-alpha.2 → 0.0.0-alpha.21
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/README.md +14 -11
- package/dist/antigravity.mjs +1 -2
- package/dist/baseCommand.mjs +1 -2
- package/dist/bin/cli.cjs +37 -14
- package/dist/bin/cli.cjs.map +1 -1
- package/dist/bin/cli.mjs +34 -14
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/bin/{doomRunner.cjs → poRunner.cjs} +5 -5
- package/dist/bin/poRunner.cjs.map +1 -0
- package/dist/bin/{doomRunner.d.mts → poRunner.d.mts} +2 -2
- package/dist/bin/poRunner.d.mts.map +1 -0
- package/dist/bin/{doomRunner.mjs → poRunner.mjs} +5 -5
- package/dist/bin/poRunner.mjs.map +1 -0
- package/dist/builders/cli/extensionAssembler/index.cjs +14 -12
- package/dist/builders/cli/extensionAssembler/index.cjs.map +1 -1
- package/dist/builders/cli/extensionAssembler/index.d.mts +2 -1
- package/dist/builders/cli/extensionAssembler/index.d.mts.map +1 -1
- package/dist/builders/cli/extensionAssembler/index.mjs +14 -12
- package/dist/builders/cli/extensionAssembler/index.mjs.map +1 -1
- package/dist/catalog/index.json +68 -51
- package/dist/cliApp.mjs +1 -2
- package/dist/codex.mjs +1 -2
- package/dist/compatibilityCommand.mjs +1 -2
- package/dist/compatibilityContext.mjs +1 -2
- package/dist/compatibilityOptions.mjs +1 -2
- package/dist/config.cjs +57 -12
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +3 -3
- package/dist/doctorCommand.mjs +1 -2
- package/dist/emitMcpCommand.mjs +1 -2
- package/dist/explainCommand.mjs +1 -2
- package/dist/extensions/server.cjs +11 -7
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +2 -2
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +12 -8
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/harnessContext.mjs +1 -2
- package/dist/harnessState.cjs +66 -11
- package/dist/harnessState.d.mts +2 -2
- package/dist/harnessState.mjs +1 -2
- package/dist/index.cjs +60 -15
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/initCommand.mjs +1 -2
- package/dist/matrixOptions.mjs +1 -2
- package/dist/options.mjs +1 -2
- package/dist/process.mjs +1 -2
- package/dist/projectTrust.cjs +2 -2
- package/dist/projectTrust.d.mts +2 -2
- package/dist/projectTrust.mjs +1 -2
- package/dist/repository.cjs +1 -1
- package/dist/repository.d.mts +2 -2
- package/dist/repository.mjs +1 -2
- package/dist/router.mjs +1 -2
- package/dist/shared.mjs +1 -2
- package/dist/src/bin/agentDirectory.cjs +18 -12
- package/dist/src/bin/agentDirectory.cjs.map +1 -1
- package/dist/src/bin/agentDirectory.mjs +15 -12
- package/dist/src/bin/agentDirectory.mjs.map +1 -1
- package/dist/src/builders/apiContractExport/index.cjs +6 -6
- package/dist/src/builders/apiContractExport/index.cjs.map +1 -1
- package/dist/src/builders/apiContractExport/index.mjs +9 -9
- package/dist/src/builders/apiContractExport/index.mjs.map +1 -1
- package/dist/src/builders/cli/entryResolution.cjs +3 -3
- package/dist/src/builders/cli/entryResolution.cjs.map +1 -1
- package/dist/src/builders/cli/entryResolution.mjs +3 -3
- package/dist/src/builders/cli/entryResolution.mjs.map +1 -1
- package/dist/src/builders/cli/harnessContext.cjs +5 -5
- package/dist/src/builders/cli/harnessContext.cjs.map +1 -1
- package/dist/src/builders/cli/harnessContext.mjs +7 -7
- package/dist/src/builders/cli/harnessContext.mjs.map +1 -1
- package/dist/src/builders/hooks/index.cjs +1 -1
- package/dist/src/builders/hooks/index.cjs.map +1 -1
- package/dist/src/builders/hooks/index.mjs +2 -2
- package/dist/src/builders/hooks/index.mjs.map +1 -1
- package/dist/src/builders/server/backgroundSync.cjs +13 -1
- package/dist/src/builders/server/backgroundSync.cjs.map +1 -1
- package/dist/src/builders/server/backgroundSync.mjs +13 -1
- package/dist/src/builders/server/backgroundSync.mjs.map +1 -1
- package/dist/src/builders/server/bundlerService.cjs +151 -0
- package/dist/src/builders/server/bundlerService.cjs.map +1 -0
- package/dist/src/builders/server/bundlerService.mjs +151 -0
- package/dist/src/builders/server/bundlerService.mjs.map +1 -0
- package/dist/src/builders/server/computerUseBinding.cjs +2 -2
- package/dist/src/builders/server/computerUseBinding.cjs.map +1 -1
- package/dist/src/builders/server/computerUseBinding.mjs +3 -3
- package/dist/src/builders/server/computerUseBinding.mjs.map +1 -1
- package/dist/src/builders/server/defaultWorkspace.cjs +1 -1
- package/dist/src/builders/server/defaultWorkspace.cjs.map +1 -1
- package/dist/src/builders/server/defaultWorkspace.mjs +2 -2
- package/dist/src/builders/server/defaultWorkspace.mjs.map +1 -1
- package/dist/src/builders/server/idleRestart.cjs +3 -3
- package/dist/src/builders/server/idleRestart.cjs.map +1 -1
- package/dist/src/builders/server/idleRestart.mjs +3 -3
- package/dist/src/builders/server/idleRestart.mjs.map +1 -1
- package/dist/src/builders/server/index.cjs +6 -6
- package/dist/src/builders/server/index.cjs.map +1 -1
- package/dist/src/builders/server/index.mjs +6 -6
- package/dist/src/builders/server/index.mjs.map +1 -1
- package/dist/src/builders/server/logSink.cjs +3 -3
- package/dist/src/builders/server/logSink.cjs.map +1 -1
- package/dist/src/builders/server/logSink.mjs +2 -2
- package/dist/src/builders/server/logSink.mjs.map +1 -1
- package/dist/src/builders/server/mcpBundle.cjs.map +1 -1
- package/dist/src/builders/server/mcpBundle.d.mts +2 -2
- package/dist/src/builders/server/mcpBundle.mjs.map +1 -1
- package/dist/src/builders/server/packageUrl.cjs +25 -18
- package/dist/src/builders/server/packageUrl.cjs.map +1 -1
- package/dist/src/builders/server/packageUrl.mjs +25 -18
- package/dist/src/builders/server/packageUrl.mjs.map +1 -1
- package/dist/src/builders/server/runtime.cjs +157 -168
- package/dist/src/builders/server/runtime.cjs.map +1 -1
- package/dist/src/builders/server/runtime.mjs +161 -171
- package/dist/src/builders/server/runtime.mjs.map +1 -1
- package/dist/src/builders/server/selectionStatus.cjs +3 -3
- package/dist/src/builders/server/selectionStatus.cjs.map +1 -1
- package/dist/src/builders/server/selectionStatus.mjs +3 -3
- package/dist/src/builders/server/selectionStatus.mjs.map +1 -1
- package/dist/src/builders/server/sessionArguments.cjs.map +1 -1
- package/dist/src/builders/server/sessionArguments.mjs.map +1 -1
- package/dist/src/builders/server/watcherService.cjs +65 -0
- package/dist/src/builders/server/watcherService.cjs.map +1 -0
- package/dist/src/builders/server/watcherService.mjs +61 -0
- package/dist/src/builders/server/watcherService.mjs.map +1 -0
- package/dist/src/builders/server/webTemplateDecision.cjs +1 -1
- package/dist/src/builders/server/webTemplateDecision.cjs.map +1 -1
- package/dist/src/builders/server/webTemplateDecision.mjs +2 -2
- package/dist/src/builders/server/webTemplateDecision.mjs.map +1 -1
- package/dist/src/builders/web/bundle.cjs.map +1 -1
- package/dist/src/builders/web/bundle.d.mts +4 -3
- package/dist/src/builders/web/bundle.mjs.map +1 -1
- package/dist/src/builders/web/bundleAssetPolicy.d.mts +1 -0
- package/dist/src/builders/web/index.cjs +1 -1
- package/dist/src/builders/web/index.cjs.map +1 -1
- package/dist/src/builders/web/index.d.mts +1 -0
- package/dist/src/builders/web/index.mjs +1 -1
- package/dist/src/builders/web/index.mjs.map +1 -1
- package/dist/src/builders/web/webPluginGenerate.cjs +4 -15
- package/dist/src/builders/web/webPluginGenerate.cjs.map +1 -1
- package/dist/src/builders/web/webPluginGenerate.d.mts +5 -9
- package/dist/src/builders/web/webPluginGenerate.d.mts.map +1 -1
- package/dist/src/builders/web/webPluginGenerate.mjs +4 -15
- package/dist/src/builders/web/webPluginGenerate.mjs.map +1 -1
- package/dist/src/builders/web/webPluginVite.d.mts +1 -0
- package/dist/src/cli/cliApp.cjs +1 -1
- package/dist/src/cli/cliApp.cjs.map +1 -1
- package/dist/src/cli/cliApp.mjs +1 -1
- package/dist/src/cli/cliApp.mjs.map +1 -1
- package/dist/src/cli/commands/compat/index.cjs +1 -1
- package/dist/src/cli/commands/compat/index.cjs.map +1 -1
- package/dist/src/cli/commands/compat/index.mjs +1 -1
- package/dist/src/cli/commands/compat/index.mjs.map +1 -1
- package/dist/src/cli/commands/doctor/index.cjs +2 -2
- package/dist/src/cli/commands/doctor/index.cjs.map +1 -1
- package/dist/src/cli/commands/doctor/index.d.mts +1 -0
- package/dist/src/cli/commands/doctor/index.d.mts.map +1 -1
- package/dist/src/cli/commands/doctor/index.mjs +4 -4
- package/dist/src/cli/commands/doctor/index.mjs.map +1 -1
- package/dist/src/cli/commands/explain/index.cjs +3 -1
- package/dist/src/cli/commands/explain/index.cjs.map +1 -1
- package/dist/src/cli/commands/explain/index.d.mts.map +1 -1
- package/dist/src/cli/commands/explain/index.mjs +4 -2
- package/dist/src/cli/commands/explain/index.mjs.map +1 -1
- package/dist/src/cli/commands/init/index.cjs +2 -2
- package/dist/src/cli/commands/init/index.cjs.map +1 -1
- package/dist/src/cli/commands/init/index.mjs +3 -3
- package/dist/src/cli/commands/init/index.mjs.map +1 -1
- package/dist/src/cli/commands/init/presenter.cjs +1 -1
- package/dist/src/cli/commands/init/presenter.cjs.map +1 -1
- package/dist/src/cli/commands/init/presenter.mjs +2 -2
- package/dist/src/cli/commands/init/presenter.mjs.map +1 -1
- package/dist/src/cli/commands/sync/index.cjs +4 -4
- package/dist/src/cli/commands/sync/index.cjs.map +1 -1
- package/dist/src/cli/commands/sync/index.d.mts +1 -0
- package/dist/src/cli/commands/sync/index.mjs +7 -7
- package/dist/src/cli/commands/sync/index.mjs.map +1 -1
- package/dist/src/cli/commands/sync/presenter.cjs +1 -1
- package/dist/src/cli/commands/sync/presenter.cjs.map +1 -1
- package/dist/src/cli/commands/sync/presenter.d.mts +1 -1
- package/dist/src/cli/commands/sync/presenter.mjs +1 -1
- package/dist/src/cli/commands/sync/presenter.mjs.map +1 -1
- package/dist/src/cli/commands/web/index.cjs +2 -1
- package/dist/src/cli/commands/web/index.cjs.map +1 -1
- package/dist/src/cli/commands/web/index.mjs +2 -1
- package/dist/src/cli/commands/web/index.mjs.map +1 -1
- package/dist/src/cli/harnessOptions.cjs +1 -1
- package/dist/src/cli/harnessOptions.cjs.map +1 -1
- package/dist/src/cli/harnessOptions.mjs +2 -2
- package/dist/src/cli/harnessOptions.mjs.map +1 -1
- package/dist/src/cli/help.cjs +2 -0
- package/dist/src/cli/help.cjs.map +1 -1
- package/dist/src/cli/help.mjs +2 -0
- package/dist/src/cli/help.mjs.map +1 -1
- package/dist/src/cli/matrixOptions.cjs +1 -1
- package/dist/src/cli/matrixOptions.cjs.map +1 -1
- package/dist/src/cli/matrixOptions.d.mts +1 -1
- package/dist/src/cli/matrixOptions.mjs +1 -1
- package/dist/src/cli/matrixOptions.mjs.map +1 -1
- package/dist/src/cli/server/run.cjs +1 -1
- package/dist/src/cli/server/run.cjs.map +1 -1
- package/dist/src/cli/server/run.mjs +2 -2
- package/dist/src/cli/server/run.mjs.map +1 -1
- package/dist/src/composition/harnessState/index.cjs +66 -11
- package/dist/src/composition/harnessState/index.cjs.map +1 -1
- package/dist/src/composition/harnessState/index.d.mts +3 -3
- package/dist/src/composition/harnessState/index.mjs +2 -2
- package/dist/src/composition/harnessState/index.mjs.map +1 -1
- package/dist/src/composition/layerPackageInstaller/index.cjs +5 -4
- package/dist/src/composition/layerPackageInstaller/index.cjs.map +1 -1
- package/dist/src/composition/layerPackageInstaller/index.mjs +6 -5
- package/dist/src/composition/layerPackageInstaller/index.mjs.map +1 -1
- package/dist/src/composition/poHomeMigration/index.cjs +1 -1
- package/dist/src/composition/poHomeMigration/index.cjs.map +1 -1
- package/dist/src/composition/poHomeMigration/index.mjs +1 -1
- package/dist/src/composition/poHomeMigration/index.mjs.map +1 -1
- package/dist/src/composition/projectTrust/index.cjs +6 -6
- package/dist/src/composition/projectTrust/index.cjs.map +1 -1
- package/dist/src/composition/projectTrust/index.d.mts +7 -7
- package/dist/src/composition/projectTrust/index.d.mts.map +1 -1
- package/dist/src/composition/projectTrust/index.mjs +6 -6
- package/dist/src/composition/projectTrust/index.mjs.map +1 -1
- package/dist/src/composition/repository/index.cjs +8 -8
- package/dist/src/composition/repository/index.cjs.map +1 -1
- package/dist/src/composition/repository/index.d.mts +5 -5
- package/dist/src/composition/repository/index.d.mts.map +1 -1
- package/dist/src/composition/repository/index.mjs +10 -10
- package/dist/src/composition/repository/index.mjs.map +1 -1
- package/dist/src/composition/syncService/index.cjs +29 -9
- package/dist/src/composition/syncService/index.cjs.map +1 -1
- package/dist/src/composition/syncService/index.d.mts.map +1 -1
- package/dist/src/composition/syncService/index.mjs +31 -11
- package/dist/src/composition/syncService/index.mjs.map +1 -1
- package/dist/src/composition/syncService/localBuilds.cjs +7 -2
- package/dist/src/composition/syncService/localBuilds.cjs.map +1 -1
- package/dist/src/composition/syncService/localBuilds.mjs +7 -2
- package/dist/src/composition/syncService/localBuilds.mjs.map +1 -1
- package/dist/src/extensions/setupDiagnostics.cjs +3 -3
- package/dist/src/extensions/setupDiagnostics.cjs.map +1 -1
- package/dist/src/extensions/setupDiagnostics.mjs +3 -3
- package/dist/src/extensions/setupDiagnostics.mjs.map +1 -1
- package/dist/utils.mjs +1 -2
- package/docs/server/api-export.md +3 -3
- package/docs/server/api.md +23 -22
- package/docs/server/getting-started.md +7 -7
- package/docs/server/index.md +4 -4
- package/docs/server/ipc.md +7 -11
- package/docs/server/lifecycle.md +17 -17
- package/docs/server/security.md +8 -8
- package/package.json +42 -45
- package/src/prompts/po-author-extension/SKILL.md +1 -1
- package/src/prompts/po-author-extension/references/extension-contract.md +4 -4
- package/dist/bin/doomRunner.cjs.map +0 -1
- package/dist/bin/doomRunner.d.mts.map +0 -1
- package/dist/bin/doomRunner.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path","fs","createRequire","snapshotFiles","statSignature","crypto","compareCodeUnits","scanWebPlugins","pluginSlot"],"sources":["../../../../src/builders/web/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { GlobalWebBuild, GlobalWebBuildPlugin } from '@agimon-ai/po-core/webCompositions';\n\nimport { snapshotFiles, statSignature } from '../server/packageUrl';\nimport { compareCodeUnits, type DeclaredWebPlugin, pluginSlot } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst WEB_PACKAGE = '@agimon-ai/po-web';\nconst WEB_PACKAGE_ROOT_ENV = 'PO_WEB_PACKAGE_ROOT';\n\n/** The package roots a set of compositions activates, each once. */\nexport function compositionPackageRoots(compositions: readonly { packageEntries: readonly string[] }[]): string[] {\n return [\n ...new Set(\n compositions\n .flatMap((composition) => composition.packageEntries)\n .map(packageRootOf)\n .filter((root): root is string => root !== undefined),\n ),\n ];\n}\n\n/** Walks from a resolved module path up to the directory owning its package.json. */\nexport function packageRootOf(entryPath: string): string | undefined {\n let dir = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(dir, 'package.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nfunction resolveWebPackageRoot(repoRoot: string, environment: NodeJS.ProcessEnv): string | undefined {\n const override = environment[WEB_PACKAGE_ROOT_ENV];\n if (override !== undefined && override !== '') return path.resolve(override);\n try {\n return path.dirname(createRequire(path.join(repoRoot, 'package.json')).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n try {\n return path.dirname(createRequire(import.meta.url).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n return undefined;\n }\n }\n}\n\n/** The installed po-web host version the global web build would use, or `none` when it is not installed. */\nexport function webHostVersion(poHome: string, environment: NodeJS.ProcessEnv = process.env): string {\n const webRoot = resolveWebPackageRoot(poHome, environment);\n if (webRoot === undefined) return 'none';\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, 'package.json'), 'utf8')) as { version?: unknown };\n return typeof manifest.version === 'string' ? manifest.version : 'unversioned';\n } catch (error) {\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** Bumped whenever the build's output layout changes, so an old layout is never reused. */\nconst GLOBAL_BUILD_FORMAT = 'global-web-4';\n/** The record every keyed global build writes last, so a key directory holding it is complete. */\nexport const BUILD_RECORD = 'build.json';\nconst BUILD_CHILD_SCRIPT = `\nconst [bundleUrl, request] = process.argv.slice(1);\ntry {\n const { bundleCockpitWeb } = await import(bundleUrl);\n const { hostRoot, pluginRoots, outDir } = JSON.parse(request);\n const result = await bundleCockpitWeb({\n hostRoot,\n pluginRoots,\n outDir,\n onNotice: (message) => process.stderr.write(message + '\\\\n'),\n });\n process.stdout.write(JSON.stringify(result));\n} catch (error) {\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exitCode = 1;\n}\n`;\n\n/** One build's record: the publishable build, whose plugins carry each copy's package and slot closure. */\nexport type GlobalWebBuildRecord = GlobalWebBuild;\n\nexport interface GlobalWebBuildInput {\n /** Po's global home; builds land in `<home>/build/web/<key>/`. */\n poHome: string;\n /** The union of every scope's package roots; roots without a poWeb block are ignored. */\n pluginRoots: readonly string[];\n environment?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n /** Test seam over the child-process build. */\n runBuild?: (request: { hostRoot: string; pluginRoots: string[]; outDir: string }) => Promise<BuildOutput>;\n /** Test seam over package resolution. */\n resolveWebRoot?: (repoRoot: string, environment: NodeJS.ProcessEnv) => string | undefined;\n}\n\ninterface BuildOutput {\n assetsDir: string;\n pluginsDir: string;\n plugins: GlobalWebBuildPlugin[];\n pluginStyles: Record<string, string>;\n baseStyle: string;\n}\n\nexport type GlobalWebBuildResult =\n | { status: 'built' | 'cached'; build: GlobalWebBuildRecord }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/**\n * The size+mtime signature of every file a plugin package hands the build: its manifest\n * plus the whole top-level folder of the client entry and of each declared export, so an\n * edited import or a rebuilt dist chunk changes the key, not only the entry file.\n */\nfunction packageSignature(packageDir: string, entries: readonly string[]): string {\n try {\n const files = new Set<string>();\n for (const entry of entries) for (const file of snapshotFiles(packageDir, entry)) files.add(file);\n return statSignature(packageDir, [...files].sort());\n } catch (error) {\n // A declared export that is not built yet must not block the build; it only weakens the key.\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/**\n * The content key of a build: the format, the host package when there is one, then every\n * plugin package's file signature. A changed source or dist file or a changed plugin set\n * yields a new key; roots that declare no plugin do not take part.\n */\nexport function globalBuildKey(\n format: string,\n hostRoot: string | undefined,\n plugins: readonly DeclaredWebPlugin[],\n): string {\n const hash = crypto.createHash('sha256').update(format);\n if (hostRoot !== undefined)\n hash.update(`host:${hostRoot}:${fs.readFileSync(path.join(hostRoot, 'package.json'), 'utf8')}`);\n const entries = new Map<string, string[]>();\n for (const plugin of [...plugins].sort((a, b) => compareCodeUnits(a.pluginId, b.pluginId))) {\n hash.update(`\\0${plugin.pluginId}:${plugin.packageDir}:${plugin.client.entry}`);\n const list = entries.get(plugin.packageDir) ?? [];\n list.push(path.join(plugin.packageDir, plugin.client.entry));\n entries.set(plugin.packageDir, list);\n }\n for (const packageDir of [...entries.keys()].sort())\n hash.update(`\\0${packageDir}\\0${packageSignature(packageDir, entries.get(packageDir) ?? [])}`);\n return hash.digest('hex').slice(0, 32);\n}\n\nfunction bundleModuleUrl(): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./bundle.${extension}`, import.meta.url).href;\n}\n\n/** Runs Vite in a child process so its module graph and caches never stay in the server heap. */\nfunction runBuildProcess(\n request: { hostRoot: string; pluginRoots: string[]; outDir: string },\n environment: NodeJS.ProcessEnv,\n onNotice: (message: string) => void,\n): Promise<BuildOutput> {\n const args = [\n ...process.execArgv,\n '--input-type=module',\n '--eval',\n BUILD_CHILD_SCRIPT,\n bundleModuleUrl(),\n JSON.stringify(request),\n ];\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n args,\n { cwd: request.hostRoot, env: environment, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(new Error(`${stderr || stdout}`.trim() || `Web build exited with code ${String(error.code ?? 1)}`));\n return;\n }\n for (const line of stderr.split('\\n')) if (line.trim()) onNotice(line.trim());\n resolve(JSON.parse(stdout) as BuildOutput);\n },\n );\n });\n}\n\n/** A key directory's parsed build record, or undefined when the build is not there (yet). */\nexport function readBuildJson<T>(directory: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(path.join(directory, BUILD_RECORD), 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nfunction readBuildRecord(directory: string): GlobalWebBuildRecord | undefined {\n const record = readBuildJson<Omit<GlobalWebBuildRecord, 'webDirectory' | 'pluginsDirectory'>>(directory);\n if (record === undefined) return undefined;\n return { ...record, webDirectory: path.join(directory, 'web'), pluginsDirectory: path.join(directory, 'plugins') };\n}\n\nfunction webBuildsRoot(poHome: string): string {\n return path.join(poHome, 'build', 'web');\n}\n\n/** The complete global web build of one key, or undefined when it is not there. Never builds. */\nexport function readGlobalWebBuildRecord(poHome: string, key: string): GlobalWebBuildRecord | undefined {\n return readBuildRecord(path.join(webBuildsRoot(poHome), key));\n}\n\n/**\n * The key of the most recently written complete global web build, for a server that starts\n * before sync ever wrote a pointer. Temporary build folders start with a dot and are skipped.\n */\nexport function newestGlobalWebBuildKey(poHome: string): string | undefined {\n const root = webBuildsRoot(poHome);\n if (!fs.existsSync(root)) return undefined;\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => ({ name: entry.name, file: path.join(root, entry.name, BUILD_RECORD) }))\n .filter((entry) => fs.existsSync(entry.file))\n .map((entry) => ({ name: entry.name, modified: fs.statSync(entry.file).mtimeMs }))\n .sort((left, right) => right.modified - left.modified)[0]?.name;\n}\n\n/**\n * Builds into a temporary sibling of `directory`, writes the build record last, and renames\n * the result into place, so a key directory is always complete. Losing the rename to another\n * process that finished the same key is fine: its bytes are identical.\n */\nexport async function buildKeyDirectory(\n buildsRoot: string,\n key: string,\n directory: string,\n build: (temporary: string) => Promise<object>,\n): Promise<void> {\n fs.mkdirSync(buildsRoot, { recursive: true });\n const temporary = fs.mkdtempSync(path.join(buildsRoot, `.${key}-`));\n try {\n const record = await build(temporary);\n fs.writeFileSync(path.join(temporary, BUILD_RECORD), `${JSON.stringify(record, null, 2)}\\n`);\n try {\n fs.renameSync(temporary, directory);\n } catch (error) {\n if (readBuildJson(directory) === undefined) throw error;\n }\n } finally {\n fs.rmSync(temporary, { recursive: true, force: true });\n }\n}\n\nconst pendingBuilds = new Map<string, Promise<unknown>>();\n\n/** Joins a build of the same key that is already running in this process instead of starting another. */\nexport async function singleFlightBuild<T>(key: string, run: () => Promise<T>): Promise<T> {\n const pending = pendingBuilds.get(key);\n if (pending !== undefined) return pending as Promise<T>;\n const started = run();\n pendingBuilds.set(key, started);\n try {\n return await started;\n } finally {\n pendingBuilds.delete(key);\n }\n}\n\n/**\n * Ensures the one global web build for the given plugin roots exists and returns it.\n *\n * The output is `<home>/build/web/<key>/`, built in a temporary directory and renamed into\n * place, so a key directory is always complete; an existing key builds nothing. Each\n * non-host plugin gets its own content-hashed stylesheet, so a mount loads only its own.\n */\ntype ResolvedGlobalWebBuild =\n | { status: 'resolved'; webRoot: string; pluginRoots: string[]; key: string; buildsRoot: string; directory: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/** The build key for these plugin roots and where its build lives, without building anything. */\nfunction resolveGlobalWebBuild(input: GlobalWebBuildInput): ResolvedGlobalWebBuild {\n const environment = input.environment ?? process.env;\n const webRoot = (input.resolveWebRoot ?? resolveWebPackageRoot)(input.poHome, environment);\n if (webRoot === undefined || !fs.existsSync(path.join(webRoot, 'package.json')))\n return { status: 'skipped', reason: `${WEB_PACKAGE} is not installed; no web composition is published` };\n const pluginRoots = [...new Set(input.pluginRoots.map((root) => path.resolve(root)))].sort();\n let key: string;\n try {\n key = globalBuildKey(\n GLOBAL_BUILD_FORMAT,\n webRoot,\n scanWebPlugins(webRoot, pluginRoots, input.onNotice ?? ((): void => {})),\n );\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n const buildsRoot = path.join(input.poHome, 'build', 'web');\n return { status: 'resolved', webRoot, pluginRoots, key, buildsRoot, directory: path.join(buildsRoot, key) };\n}\n\n/** Whether the global build for these plugin roots exists, for a check that must not build. */\nexport function readGlobalWebBuildStatus(\n input: GlobalWebBuildInput,\n):\n | { status: 'current' | 'missing'; key: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string } {\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n return { status: readBuildRecord(resolved.directory) === undefined ? 'missing' : 'current', key: resolved.key };\n}\n\nexport async function ensureGlobalWebBuild(input: GlobalWebBuildInput): Promise<GlobalWebBuildResult> {\n const environment = input.environment ?? process.env;\n const notice = input.onNotice ?? ((): void => {});\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n const { webRoot, pluginRoots, key, buildsRoot, directory } = resolved;\n const existing = readBuildRecord(directory);\n if (existing !== undefined) return { status: 'cached', build: existing };\n return singleFlightBuild(key, async (): Promise<GlobalWebBuildResult> => {\n try {\n await buildKeyDirectory(buildsRoot, key, directory, async (temporary) => {\n const request = { hostRoot: webRoot, pluginRoots, outDir: temporary };\n const output = await (input.runBuild ?? ((value) => runBuildProcess(value, environment, notice)))(request);\n return {\n key,\n plugins: output.plugins,\n pluginStyles: output.pluginStyles,\n baseStyle: output.baseStyle,\n };\n });\n const build = readBuildRecord(directory);\n if (build === undefined) throw new Error(`The web build '${key}' was not recorded.`);\n return { status: 'built', build };\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n });\n}\n\ntype MountPluginRecord = { pluginId: string; packageDir: string; isHost: boolean };\n\n/** The plugin copies a mount runs, host plugins plus its own roots', at most one per pluginId (first wins). */\nfunction mountPluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): MountPluginRecord[] {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const seen = new Set<string>();\n return build.plugins.filter((plugin) => {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) return false;\n if (seen.has(plugin.pluginId)) return false;\n seen.add(plugin.pluginId);\n return true;\n });\n}\n\n/** The `pluginId` values a mount runs: the host's plugins plus those of its own package roots, deduplicated. */\nexport function mountWebPluginIds(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map((plugin) => plugin.pluginId);\n}\n\n/** The slots a mount runs: one copy per pluginId, the first in build order. */\nexport function mountWebPluginSlots(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map(pluginSlot);\n}\n\n/**\n * The package roots among `packageRoots` that declare a web plugin the build has no slot for:\n * a package that was not synced yet, or was left out of the build. Their names, else their roots.\n */\nexport function unslottedWebPackages(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n const slotted = new Set(build.plugins.map((plugin) => path.resolve(plugin.packageDir)));\n const missing: string[] = [];\n for (const root of new Set(packageRoots.map((entry) => path.resolve(entry)))) {\n if (slotted.has(root)) continue;\n let manifest: { name?: unknown; poWeb?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as typeof manifest;\n } catch {\n // Unreadable manifests are reported by the build's own scan, not as a missing slot.\n continue;\n }\n if (manifest.poWeb === undefined) continue;\n missing.push(typeof manifest.name === 'string' ? manifest.name : root);\n }\n return missing;\n}\n\n/** The pluginIds with more than one installed copy among the host and these roots, each with its slots in build order. */\nexport function duplicatePluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): Map<string, string[]> {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const slotsById = new Map<string, string[]>();\n for (const plugin of build.plugins) {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) continue;\n const slot = pluginSlot(plugin);\n const slots = slotsById.get(plugin.pluginId) ?? [];\n if (!slots.includes(slot)) slots.push(slot);\n slotsById.set(plugin.pluginId, slots);\n }\n return new Map([...slotsById].filter(([, slots]) => slots.length > 1));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path","fs","createRequire","snapshotFiles","statSignature","crypto","compareCodeUnits","scanWebPlugins","pluginSlot"],"sources":["../../../../src/builders/web/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { GlobalWebBuild, GlobalWebBuildPlugin } from '@agimon-ai/po-core/webCompositions';\n\nimport { snapshotFiles, statSignature } from '../server/packageUrl';\nimport { compareCodeUnits, type DeclaredWebPlugin, pluginSlot } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst WEB_PACKAGE = '@agimon-ai/po-web';\nconst WEB_PACKAGE_ROOT_ENV = 'PO_WEB_PACKAGE_ROOT';\n\n/** The package roots a set of compositions activates, each once. */\nexport function compositionPackageRoots(compositions: readonly { packageEntries: readonly string[] }[]): string[] {\n return [\n ...new Set(\n compositions\n .flatMap((composition) => composition.packageEntries)\n .map(packageRootOf)\n .filter((root): root is string => root !== undefined),\n ),\n ];\n}\n\n/** Walks from a resolved module path up to the directory owning its package.json. */\nexport function packageRootOf(entryPath: string): string | undefined {\n let dir = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(dir, 'package.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nfunction resolveWebPackageRoot(repoRoot: string, environment: NodeJS.ProcessEnv): string | undefined {\n const override = environment[WEB_PACKAGE_ROOT_ENV];\n if (override !== undefined && override !== '') return path.resolve(override);\n try {\n return path.dirname(createRequire(path.join(repoRoot, 'package.json')).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n try {\n return path.dirname(createRequire(import.meta.url).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n return undefined;\n }\n }\n}\n\n/** The installed po-web host version the global web build would use, or `none` when it is not installed. */\nexport function webHostVersion(poHome: string, environment: NodeJS.ProcessEnv = process.env): string {\n const webRoot = resolveWebPackageRoot(poHome, environment);\n if (webRoot === undefined) return 'none';\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, 'package.json'), 'utf8')) as { version?: unknown };\n return typeof manifest.version === 'string' ? manifest.version : 'unversioned';\n } catch (error) {\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** Bumped whenever the build's output layout changes, so an old layout is never reused. */\nconst GLOBAL_BUILD_FORMAT = 'global-web-5';\n/** The record every keyed global build writes last, so a key directory holding it is complete. */\nexport const BUILD_RECORD = 'build.json';\nconst BUILD_CHILD_SCRIPT = `\nconst [bundleUrl, request] = process.argv.slice(1);\ntry {\n const { bundleCockpitWeb } = await import(bundleUrl);\n const { hostRoot, pluginRoots, outDir } = JSON.parse(request);\n const result = await bundleCockpitWeb({\n hostRoot,\n pluginRoots,\n outDir,\n onNotice: (message) => process.stderr.write(message + '\\\\n'),\n });\n process.stdout.write(JSON.stringify(result));\n} catch (error) {\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exitCode = 1;\n}\n`;\n\n/** One build's record: the publishable build, whose plugins carry each copy's package and slot closure. */\nexport type GlobalWebBuildRecord = GlobalWebBuild;\n\nexport interface GlobalWebBuildInput {\n /** Po's global home; builds land in `<home>/build/web/<key>/`. */\n poHome: string;\n /** The union of every scope's package roots; roots without a poWeb block are ignored. */\n pluginRoots: readonly string[];\n environment?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n /** Test seam over the child-process build. */\n runBuild?: (request: { hostRoot: string; pluginRoots: string[]; outDir: string }) => Promise<BuildOutput>;\n /** Test seam over package resolution. */\n resolveWebRoot?: (repoRoot: string, environment: NodeJS.ProcessEnv) => string | undefined;\n}\n\ninterface BuildOutput {\n assetsDir: string;\n pluginsDir: string;\n plugins: GlobalWebBuildPlugin[];\n pluginStyles: Record<string, string>;\n baseStyle: string;\n}\n\nexport type GlobalWebBuildResult =\n | { status: 'built' | 'cached'; build: GlobalWebBuildRecord }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/**\n * The size+mtime signature of every file a plugin package hands the build: its manifest\n * plus the whole top-level folder of the client entry and of each declared export, so an\n * edited import or a rebuilt dist chunk changes the key, not only the entry file.\n */\nfunction packageSignature(packageDir: string, entries: readonly string[]): string {\n try {\n const files = new Set<string>();\n for (const entry of entries) for (const file of snapshotFiles(packageDir, entry)) files.add(file);\n return statSignature(packageDir, [...files].sort());\n } catch (error) {\n // A declared export that is not built yet must not block the build; it only weakens the key.\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/**\n * The content key of a build: the format, the host package when there is one, then every\n * plugin package's file signature. A changed source or dist file or a changed plugin set\n * yields a new key; roots that declare no plugin do not take part.\n */\nexport function globalBuildKey(\n format: string,\n hostRoot: string | undefined,\n plugins: readonly DeclaredWebPlugin[],\n): string {\n const hash = crypto.createHash('sha256').update(format);\n if (hostRoot !== undefined)\n hash.update(`host:${hostRoot}:${fs.readFileSync(path.join(hostRoot, 'package.json'), 'utf8')}`);\n const entries = new Map<string, string[]>();\n for (const plugin of [...plugins].sort((a, b) => compareCodeUnits(a.pluginId, b.pluginId))) {\n hash.update(`\\0${plugin.pluginId}:${plugin.packageDir}:${plugin.client.entry}`);\n const list = entries.get(plugin.packageDir) ?? [];\n list.push(path.join(plugin.packageDir, plugin.client.entry));\n entries.set(plugin.packageDir, list);\n }\n for (const packageDir of [...entries.keys()].sort())\n hash.update(`\\0${packageDir}\\0${packageSignature(packageDir, entries.get(packageDir) ?? [])}`);\n return hash.digest('hex').slice(0, 32);\n}\n\nfunction bundleModuleUrl(): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./bundle.${extension}`, import.meta.url).href;\n}\n\n/** Runs Vite in a child process so its module graph and caches never stay in the server heap. */\nfunction runBuildProcess(\n request: { hostRoot: string; pluginRoots: string[]; outDir: string },\n environment: NodeJS.ProcessEnv,\n onNotice: (message: string) => void,\n): Promise<BuildOutput> {\n const args = [\n ...process.execArgv,\n '--input-type=module',\n '--eval',\n BUILD_CHILD_SCRIPT,\n bundleModuleUrl(),\n JSON.stringify(request),\n ];\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n args,\n { cwd: request.hostRoot, env: environment, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(new Error(`${stderr || stdout}`.trim() || `Web build exited with code ${String(error.code ?? 1)}`));\n return;\n }\n for (const line of stderr.split('\\n')) if (line.trim()) onNotice(line.trim());\n resolve(JSON.parse(stdout) as BuildOutput);\n },\n );\n });\n}\n\n/** A key directory's parsed build record, or undefined when the build is not there (yet). */\nexport function readBuildJson<T>(directory: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(path.join(directory, BUILD_RECORD), 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nfunction readBuildRecord(directory: string): GlobalWebBuildRecord | undefined {\n const record = readBuildJson<Omit<GlobalWebBuildRecord, 'webDirectory' | 'pluginsDirectory'>>(directory);\n if (record === undefined) return undefined;\n return { ...record, webDirectory: path.join(directory, 'web'), pluginsDirectory: path.join(directory, 'plugins') };\n}\n\nfunction webBuildsRoot(poHome: string): string {\n return path.join(poHome, 'build', 'web');\n}\n\n/** The complete global web build of one key, or undefined when it is not there. Never builds. */\nexport function readGlobalWebBuildRecord(poHome: string, key: string): GlobalWebBuildRecord | undefined {\n return readBuildRecord(path.join(webBuildsRoot(poHome), key));\n}\n\n/**\n * The key of the most recently written complete global web build, for a server that starts\n * before sync ever wrote a pointer. Temporary build folders start with a dot and are skipped.\n */\nexport function newestGlobalWebBuildKey(poHome: string): string | undefined {\n const root = webBuildsRoot(poHome);\n if (!fs.existsSync(root)) return undefined;\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => ({ name: entry.name, file: path.join(root, entry.name, BUILD_RECORD) }))\n .filter((entry) => fs.existsSync(entry.file))\n .map((entry) => ({ name: entry.name, modified: fs.statSync(entry.file).mtimeMs }))\n .sort((left, right) => right.modified - left.modified)[0]?.name;\n}\n\n/**\n * Builds into a temporary sibling of `directory`, writes the build record last, and renames\n * the result into place, so a key directory is always complete. Losing the rename to another\n * process that finished the same key is fine: its bytes are identical.\n */\nexport async function buildKeyDirectory(\n buildsRoot: string,\n key: string,\n directory: string,\n build: (temporary: string) => Promise<object>,\n): Promise<void> {\n fs.mkdirSync(buildsRoot, { recursive: true });\n const temporary = fs.mkdtempSync(path.join(buildsRoot, `.${key}-`));\n try {\n const record = await build(temporary);\n fs.writeFileSync(path.join(temporary, BUILD_RECORD), `${JSON.stringify(record, null, 2)}\\n`);\n try {\n fs.renameSync(temporary, directory);\n } catch (error) {\n if (readBuildJson(directory) === undefined) throw error;\n }\n } finally {\n fs.rmSync(temporary, { recursive: true, force: true });\n }\n}\n\nconst pendingBuilds = new Map<string, Promise<unknown>>();\n\n/** Joins a build of the same key that is already running in this process instead of starting another. */\nexport async function singleFlightBuild<T>(key: string, run: () => Promise<T>): Promise<T> {\n const pending = pendingBuilds.get(key);\n if (pending !== undefined) return pending as Promise<T>;\n const started = run();\n pendingBuilds.set(key, started);\n try {\n return await started;\n } finally {\n pendingBuilds.delete(key);\n }\n}\n\n/**\n * Ensures the one global web build for the given plugin roots exists and returns it.\n *\n * The output is `<home>/build/web/<key>/`, built in a temporary directory and renamed into\n * place, so a key directory is always complete; an existing key builds nothing. Each\n * plugin shares one content-hashed Tailwind stylesheet with the shell and fallback template.\n */\ntype ResolvedGlobalWebBuild =\n | { status: 'resolved'; webRoot: string; pluginRoots: string[]; key: string; buildsRoot: string; directory: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/** The build key for these plugin roots and where its build lives, without building anything. */\nfunction resolveGlobalWebBuild(input: GlobalWebBuildInput): ResolvedGlobalWebBuild {\n const environment = input.environment ?? process.env;\n const webRoot = (input.resolveWebRoot ?? resolveWebPackageRoot)(input.poHome, environment);\n if (webRoot === undefined || !fs.existsSync(path.join(webRoot, 'package.json')))\n return { status: 'skipped', reason: `${WEB_PACKAGE} is not installed; no web composition is published` };\n const pluginRoots = [...new Set(input.pluginRoots.map((root) => path.resolve(root)))].sort();\n let key: string;\n try {\n key = globalBuildKey(\n GLOBAL_BUILD_FORMAT,\n webRoot,\n scanWebPlugins(webRoot, pluginRoots, input.onNotice ?? ((): void => {})),\n );\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n const buildsRoot = path.join(input.poHome, 'build', 'web');\n return { status: 'resolved', webRoot, pluginRoots, key, buildsRoot, directory: path.join(buildsRoot, key) };\n}\n\n/** Whether the global build for these plugin roots exists, for a check that must not build. */\nexport function readGlobalWebBuildStatus(\n input: GlobalWebBuildInput,\n):\n | { status: 'current' | 'missing'; key: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string } {\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n return { status: readBuildRecord(resolved.directory) === undefined ? 'missing' : 'current', key: resolved.key };\n}\n\nexport async function ensureGlobalWebBuild(input: GlobalWebBuildInput): Promise<GlobalWebBuildResult> {\n const environment = input.environment ?? process.env;\n const notice = input.onNotice ?? ((): void => {});\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n const { webRoot, pluginRoots, key, buildsRoot, directory } = resolved;\n const existing = readBuildRecord(directory);\n if (existing !== undefined) return { status: 'cached', build: existing };\n return singleFlightBuild(key, async (): Promise<GlobalWebBuildResult> => {\n try {\n await buildKeyDirectory(buildsRoot, key, directory, async (temporary) => {\n const request = { hostRoot: webRoot, pluginRoots, outDir: temporary };\n const output = await (input.runBuild ?? ((value) => runBuildProcess(value, environment, notice)))(request);\n return {\n key,\n plugins: output.plugins,\n pluginStyles: output.pluginStyles,\n baseStyle: output.baseStyle,\n };\n });\n const build = readBuildRecord(directory);\n if (build === undefined) throw new Error(`The web build '${key}' was not recorded.`);\n return { status: 'built', build };\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n });\n}\n\ntype MountPluginRecord = { pluginId: string; packageDir: string; isHost: boolean };\n\n/** The plugin copies a mount runs, host plugins plus its own roots', at most one per pluginId (first wins). */\nfunction mountPluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): MountPluginRecord[] {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const seen = new Set<string>();\n return build.plugins.filter((plugin) => {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) return false;\n if (seen.has(plugin.pluginId)) return false;\n seen.add(plugin.pluginId);\n return true;\n });\n}\n\n/** The `pluginId` values a mount runs: the host's plugins plus those of its own package roots, deduplicated. */\nexport function mountWebPluginIds(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map((plugin) => plugin.pluginId);\n}\n\n/** The slots a mount runs: one copy per pluginId, the first in build order. */\nexport function mountWebPluginSlots(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map(pluginSlot);\n}\n\n/**\n * The package roots among `packageRoots` that declare a web plugin the build has no slot for:\n * a package that was not synced yet, or was left out of the build. Their names, else their roots.\n */\nexport function unslottedWebPackages(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n const slotted = new Set(build.plugins.map((plugin) => path.resolve(plugin.packageDir)));\n const missing: string[] = [];\n for (const root of new Set(packageRoots.map((entry) => path.resolve(entry)))) {\n if (slotted.has(root)) continue;\n let manifest: { name?: unknown; poWeb?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as typeof manifest;\n } catch {\n // Unreadable manifests are reported by the build's own scan, not as a missing slot.\n continue;\n }\n if (manifest.poWeb === undefined) continue;\n missing.push(typeof manifest.name === 'string' ? manifest.name : root);\n }\n return missing;\n}\n\n/** The pluginIds with more than one installed copy among the host and these roots, each with its slots in build order. */\nexport function duplicatePluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): Map<string, string[]> {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const slotsById = new Map<string, string[]>();\n for (const plugin of build.plugins) {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) continue;\n const slot = pluginSlot(plugin);\n const slots = slotsById.get(plugin.pluginId) ?? [];\n if (!slots.includes(slot)) slots.push(slot);\n slotsById.set(plugin.pluginId, slots);\n }\n return new Map([...slotsById].filter(([, slots]) => slots.length > 1));\n}\n"],"mappings":"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"}
|
|
@@ -48,7 +48,7 @@ function webHostVersion(poHome, environment = process.env) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
/** Bumped whenever the build's output layout changes, so an old layout is never reused. */
|
|
51
|
-
const GLOBAL_BUILD_FORMAT = "global-web-
|
|
51
|
+
const GLOBAL_BUILD_FORMAT = "global-web-5";
|
|
52
52
|
/** The record every keyed global build writes last, so a key directory holding it is complete. */
|
|
53
53
|
const BUILD_RECORD = "build.json";
|
|
54
54
|
const BUILD_CHILD_SCRIPT = `
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/builders/web/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { GlobalWebBuild, GlobalWebBuildPlugin } from '@agimon-ai/po-core/webCompositions';\n\nimport { snapshotFiles, statSignature } from '../server/packageUrl';\nimport { compareCodeUnits, type DeclaredWebPlugin, pluginSlot } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst WEB_PACKAGE = '@agimon-ai/po-web';\nconst WEB_PACKAGE_ROOT_ENV = 'PO_WEB_PACKAGE_ROOT';\n\n/** The package roots a set of compositions activates, each once. */\nexport function compositionPackageRoots(compositions: readonly { packageEntries: readonly string[] }[]): string[] {\n return [\n ...new Set(\n compositions\n .flatMap((composition) => composition.packageEntries)\n .map(packageRootOf)\n .filter((root): root is string => root !== undefined),\n ),\n ];\n}\n\n/** Walks from a resolved module path up to the directory owning its package.json. */\nexport function packageRootOf(entryPath: string): string | undefined {\n let dir = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(dir, 'package.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nfunction resolveWebPackageRoot(repoRoot: string, environment: NodeJS.ProcessEnv): string | undefined {\n const override = environment[WEB_PACKAGE_ROOT_ENV];\n if (override !== undefined && override !== '') return path.resolve(override);\n try {\n return path.dirname(createRequire(path.join(repoRoot, 'package.json')).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n try {\n return path.dirname(createRequire(import.meta.url).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n return undefined;\n }\n }\n}\n\n/** The installed po-web host version the global web build would use, or `none` when it is not installed. */\nexport function webHostVersion(poHome: string, environment: NodeJS.ProcessEnv = process.env): string {\n const webRoot = resolveWebPackageRoot(poHome, environment);\n if (webRoot === undefined) return 'none';\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, 'package.json'), 'utf8')) as { version?: unknown };\n return typeof manifest.version === 'string' ? manifest.version : 'unversioned';\n } catch (error) {\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** Bumped whenever the build's output layout changes, so an old layout is never reused. */\nconst GLOBAL_BUILD_FORMAT = 'global-web-4';\n/** The record every keyed global build writes last, so a key directory holding it is complete. */\nexport const BUILD_RECORD = 'build.json';\nconst BUILD_CHILD_SCRIPT = `\nconst [bundleUrl, request] = process.argv.slice(1);\ntry {\n const { bundleCockpitWeb } = await import(bundleUrl);\n const { hostRoot, pluginRoots, outDir } = JSON.parse(request);\n const result = await bundleCockpitWeb({\n hostRoot,\n pluginRoots,\n outDir,\n onNotice: (message) => process.stderr.write(message + '\\\\n'),\n });\n process.stdout.write(JSON.stringify(result));\n} catch (error) {\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exitCode = 1;\n}\n`;\n\n/** One build's record: the publishable build, whose plugins carry each copy's package and slot closure. */\nexport type GlobalWebBuildRecord = GlobalWebBuild;\n\nexport interface GlobalWebBuildInput {\n /** Po's global home; builds land in `<home>/build/web/<key>/`. */\n poHome: string;\n /** The union of every scope's package roots; roots without a poWeb block are ignored. */\n pluginRoots: readonly string[];\n environment?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n /** Test seam over the child-process build. */\n runBuild?: (request: { hostRoot: string; pluginRoots: string[]; outDir: string }) => Promise<BuildOutput>;\n /** Test seam over package resolution. */\n resolveWebRoot?: (repoRoot: string, environment: NodeJS.ProcessEnv) => string | undefined;\n}\n\ninterface BuildOutput {\n assetsDir: string;\n pluginsDir: string;\n plugins: GlobalWebBuildPlugin[];\n pluginStyles: Record<string, string>;\n baseStyle: string;\n}\n\nexport type GlobalWebBuildResult =\n | { status: 'built' | 'cached'; build: GlobalWebBuildRecord }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/**\n * The size+mtime signature of every file a plugin package hands the build: its manifest\n * plus the whole top-level folder of the client entry and of each declared export, so an\n * edited import or a rebuilt dist chunk changes the key, not only the entry file.\n */\nfunction packageSignature(packageDir: string, entries: readonly string[]): string {\n try {\n const files = new Set<string>();\n for (const entry of entries) for (const file of snapshotFiles(packageDir, entry)) files.add(file);\n return statSignature(packageDir, [...files].sort());\n } catch (error) {\n // A declared export that is not built yet must not block the build; it only weakens the key.\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/**\n * The content key of a build: the format, the host package when there is one, then every\n * plugin package's file signature. A changed source or dist file or a changed plugin set\n * yields a new key; roots that declare no plugin do not take part.\n */\nexport function globalBuildKey(\n format: string,\n hostRoot: string | undefined,\n plugins: readonly DeclaredWebPlugin[],\n): string {\n const hash = crypto.createHash('sha256').update(format);\n if (hostRoot !== undefined)\n hash.update(`host:${hostRoot}:${fs.readFileSync(path.join(hostRoot, 'package.json'), 'utf8')}`);\n const entries = new Map<string, string[]>();\n for (const plugin of [...plugins].sort((a, b) => compareCodeUnits(a.pluginId, b.pluginId))) {\n hash.update(`\\0${plugin.pluginId}:${plugin.packageDir}:${plugin.client.entry}`);\n const list = entries.get(plugin.packageDir) ?? [];\n list.push(path.join(plugin.packageDir, plugin.client.entry));\n entries.set(plugin.packageDir, list);\n }\n for (const packageDir of [...entries.keys()].sort())\n hash.update(`\\0${packageDir}\\0${packageSignature(packageDir, entries.get(packageDir) ?? [])}`);\n return hash.digest('hex').slice(0, 32);\n}\n\nfunction bundleModuleUrl(): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./bundle.${extension}`, import.meta.url).href;\n}\n\n/** Runs Vite in a child process so its module graph and caches never stay in the server heap. */\nfunction runBuildProcess(\n request: { hostRoot: string; pluginRoots: string[]; outDir: string },\n environment: NodeJS.ProcessEnv,\n onNotice: (message: string) => void,\n): Promise<BuildOutput> {\n const args = [\n ...process.execArgv,\n '--input-type=module',\n '--eval',\n BUILD_CHILD_SCRIPT,\n bundleModuleUrl(),\n JSON.stringify(request),\n ];\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n args,\n { cwd: request.hostRoot, env: environment, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(new Error(`${stderr || stdout}`.trim() || `Web build exited with code ${String(error.code ?? 1)}`));\n return;\n }\n for (const line of stderr.split('\\n')) if (line.trim()) onNotice(line.trim());\n resolve(JSON.parse(stdout) as BuildOutput);\n },\n );\n });\n}\n\n/** A key directory's parsed build record, or undefined when the build is not there (yet). */\nexport function readBuildJson<T>(directory: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(path.join(directory, BUILD_RECORD), 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nfunction readBuildRecord(directory: string): GlobalWebBuildRecord | undefined {\n const record = readBuildJson<Omit<GlobalWebBuildRecord, 'webDirectory' | 'pluginsDirectory'>>(directory);\n if (record === undefined) return undefined;\n return { ...record, webDirectory: path.join(directory, 'web'), pluginsDirectory: path.join(directory, 'plugins') };\n}\n\nfunction webBuildsRoot(poHome: string): string {\n return path.join(poHome, 'build', 'web');\n}\n\n/** The complete global web build of one key, or undefined when it is not there. Never builds. */\nexport function readGlobalWebBuildRecord(poHome: string, key: string): GlobalWebBuildRecord | undefined {\n return readBuildRecord(path.join(webBuildsRoot(poHome), key));\n}\n\n/**\n * The key of the most recently written complete global web build, for a server that starts\n * before sync ever wrote a pointer. Temporary build folders start with a dot and are skipped.\n */\nexport function newestGlobalWebBuildKey(poHome: string): string | undefined {\n const root = webBuildsRoot(poHome);\n if (!fs.existsSync(root)) return undefined;\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => ({ name: entry.name, file: path.join(root, entry.name, BUILD_RECORD) }))\n .filter((entry) => fs.existsSync(entry.file))\n .map((entry) => ({ name: entry.name, modified: fs.statSync(entry.file).mtimeMs }))\n .sort((left, right) => right.modified - left.modified)[0]?.name;\n}\n\n/**\n * Builds into a temporary sibling of `directory`, writes the build record last, and renames\n * the result into place, so a key directory is always complete. Losing the rename to another\n * process that finished the same key is fine: its bytes are identical.\n */\nexport async function buildKeyDirectory(\n buildsRoot: string,\n key: string,\n directory: string,\n build: (temporary: string) => Promise<object>,\n): Promise<void> {\n fs.mkdirSync(buildsRoot, { recursive: true });\n const temporary = fs.mkdtempSync(path.join(buildsRoot, `.${key}-`));\n try {\n const record = await build(temporary);\n fs.writeFileSync(path.join(temporary, BUILD_RECORD), `${JSON.stringify(record, null, 2)}\\n`);\n try {\n fs.renameSync(temporary, directory);\n } catch (error) {\n if (readBuildJson(directory) === undefined) throw error;\n }\n } finally {\n fs.rmSync(temporary, { recursive: true, force: true });\n }\n}\n\nconst pendingBuilds = new Map<string, Promise<unknown>>();\n\n/** Joins a build of the same key that is already running in this process instead of starting another. */\nexport async function singleFlightBuild<T>(key: string, run: () => Promise<T>): Promise<T> {\n const pending = pendingBuilds.get(key);\n if (pending !== undefined) return pending as Promise<T>;\n const started = run();\n pendingBuilds.set(key, started);\n try {\n return await started;\n } finally {\n pendingBuilds.delete(key);\n }\n}\n\n/**\n * Ensures the one global web build for the given plugin roots exists and returns it.\n *\n * The output is `<home>/build/web/<key>/`, built in a temporary directory and renamed into\n * place, so a key directory is always complete; an existing key builds nothing. Each\n * non-host plugin gets its own content-hashed stylesheet, so a mount loads only its own.\n */\ntype ResolvedGlobalWebBuild =\n | { status: 'resolved'; webRoot: string; pluginRoots: string[]; key: string; buildsRoot: string; directory: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/** The build key for these plugin roots and where its build lives, without building anything. */\nfunction resolveGlobalWebBuild(input: GlobalWebBuildInput): ResolvedGlobalWebBuild {\n const environment = input.environment ?? process.env;\n const webRoot = (input.resolveWebRoot ?? resolveWebPackageRoot)(input.poHome, environment);\n if (webRoot === undefined || !fs.existsSync(path.join(webRoot, 'package.json')))\n return { status: 'skipped', reason: `${WEB_PACKAGE} is not installed; no web composition is published` };\n const pluginRoots = [...new Set(input.pluginRoots.map((root) => path.resolve(root)))].sort();\n let key: string;\n try {\n key = globalBuildKey(\n GLOBAL_BUILD_FORMAT,\n webRoot,\n scanWebPlugins(webRoot, pluginRoots, input.onNotice ?? ((): void => {})),\n );\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n const buildsRoot = path.join(input.poHome, 'build', 'web');\n return { status: 'resolved', webRoot, pluginRoots, key, buildsRoot, directory: path.join(buildsRoot, key) };\n}\n\n/** Whether the global build for these plugin roots exists, for a check that must not build. */\nexport function readGlobalWebBuildStatus(\n input: GlobalWebBuildInput,\n):\n | { status: 'current' | 'missing'; key: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string } {\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n return { status: readBuildRecord(resolved.directory) === undefined ? 'missing' : 'current', key: resolved.key };\n}\n\nexport async function ensureGlobalWebBuild(input: GlobalWebBuildInput): Promise<GlobalWebBuildResult> {\n const environment = input.environment ?? process.env;\n const notice = input.onNotice ?? ((): void => {});\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n const { webRoot, pluginRoots, key, buildsRoot, directory } = resolved;\n const existing = readBuildRecord(directory);\n if (existing !== undefined) return { status: 'cached', build: existing };\n return singleFlightBuild(key, async (): Promise<GlobalWebBuildResult> => {\n try {\n await buildKeyDirectory(buildsRoot, key, directory, async (temporary) => {\n const request = { hostRoot: webRoot, pluginRoots, outDir: temporary };\n const output = await (input.runBuild ?? ((value) => runBuildProcess(value, environment, notice)))(request);\n return {\n key,\n plugins: output.plugins,\n pluginStyles: output.pluginStyles,\n baseStyle: output.baseStyle,\n };\n });\n const build = readBuildRecord(directory);\n if (build === undefined) throw new Error(`The web build '${key}' was not recorded.`);\n return { status: 'built', build };\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n });\n}\n\ntype MountPluginRecord = { pluginId: string; packageDir: string; isHost: boolean };\n\n/** The plugin copies a mount runs, host plugins plus its own roots', at most one per pluginId (first wins). */\nfunction mountPluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): MountPluginRecord[] {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const seen = new Set<string>();\n return build.plugins.filter((plugin) => {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) return false;\n if (seen.has(plugin.pluginId)) return false;\n seen.add(plugin.pluginId);\n return true;\n });\n}\n\n/** The `pluginId` values a mount runs: the host's plugins plus those of its own package roots, deduplicated. */\nexport function mountWebPluginIds(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map((plugin) => plugin.pluginId);\n}\n\n/** The slots a mount runs: one copy per pluginId, the first in build order. */\nexport function mountWebPluginSlots(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map(pluginSlot);\n}\n\n/**\n * The package roots among `packageRoots` that declare a web plugin the build has no slot for:\n * a package that was not synced yet, or was left out of the build. Their names, else their roots.\n */\nexport function unslottedWebPackages(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n const slotted = new Set(build.plugins.map((plugin) => path.resolve(plugin.packageDir)));\n const missing: string[] = [];\n for (const root of new Set(packageRoots.map((entry) => path.resolve(entry)))) {\n if (slotted.has(root)) continue;\n let manifest: { name?: unknown; poWeb?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as typeof manifest;\n } catch {\n // Unreadable manifests are reported by the build's own scan, not as a missing slot.\n continue;\n }\n if (manifest.poWeb === undefined) continue;\n missing.push(typeof manifest.name === 'string' ? manifest.name : root);\n }\n return missing;\n}\n\n/** The pluginIds with more than one installed copy among the host and these roots, each with its slots in build order. */\nexport function duplicatePluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): Map<string, string[]> {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const slotsById = new Map<string, string[]>();\n for (const plugin of build.plugins) {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) continue;\n const slot = pluginSlot(plugin);\n const slots = slotsById.get(plugin.pluginId) ?? [];\n if (!slots.includes(slot)) slots.push(slot);\n slotsById.set(plugin.pluginId, slots);\n }\n return new Map([...slotsById].filter(([, slots]) => slots.length > 1));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/builders/web/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { GlobalWebBuild, GlobalWebBuildPlugin } from '@agimon-ai/po-core/webCompositions';\n\nimport { snapshotFiles, statSignature } from '../server/packageUrl';\nimport { compareCodeUnits, type DeclaredWebPlugin, pluginSlot } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst WEB_PACKAGE = '@agimon-ai/po-web';\nconst WEB_PACKAGE_ROOT_ENV = 'PO_WEB_PACKAGE_ROOT';\n\n/** The package roots a set of compositions activates, each once. */\nexport function compositionPackageRoots(compositions: readonly { packageEntries: readonly string[] }[]): string[] {\n return [\n ...new Set(\n compositions\n .flatMap((composition) => composition.packageEntries)\n .map(packageRootOf)\n .filter((root): root is string => root !== undefined),\n ),\n ];\n}\n\n/** Walks from a resolved module path up to the directory owning its package.json. */\nexport function packageRootOf(entryPath: string): string | undefined {\n let dir = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(dir, 'package.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nfunction resolveWebPackageRoot(repoRoot: string, environment: NodeJS.ProcessEnv): string | undefined {\n const override = environment[WEB_PACKAGE_ROOT_ENV];\n if (override !== undefined && override !== '') return path.resolve(override);\n try {\n return path.dirname(createRequire(path.join(repoRoot, 'package.json')).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n try {\n return path.dirname(createRequire(import.meta.url).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n return undefined;\n }\n }\n}\n\n/** The installed po-web host version the global web build would use, or `none` when it is not installed. */\nexport function webHostVersion(poHome: string, environment: NodeJS.ProcessEnv = process.env): string {\n const webRoot = resolveWebPackageRoot(poHome, environment);\n if (webRoot === undefined) return 'none';\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, 'package.json'), 'utf8')) as { version?: unknown };\n return typeof manifest.version === 'string' ? manifest.version : 'unversioned';\n } catch (error) {\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** Bumped whenever the build's output layout changes, so an old layout is never reused. */\nconst GLOBAL_BUILD_FORMAT = 'global-web-5';\n/** The record every keyed global build writes last, so a key directory holding it is complete. */\nexport const BUILD_RECORD = 'build.json';\nconst BUILD_CHILD_SCRIPT = `\nconst [bundleUrl, request] = process.argv.slice(1);\ntry {\n const { bundleCockpitWeb } = await import(bundleUrl);\n const { hostRoot, pluginRoots, outDir } = JSON.parse(request);\n const result = await bundleCockpitWeb({\n hostRoot,\n pluginRoots,\n outDir,\n onNotice: (message) => process.stderr.write(message + '\\\\n'),\n });\n process.stdout.write(JSON.stringify(result));\n} catch (error) {\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exitCode = 1;\n}\n`;\n\n/** One build's record: the publishable build, whose plugins carry each copy's package and slot closure. */\nexport type GlobalWebBuildRecord = GlobalWebBuild;\n\nexport interface GlobalWebBuildInput {\n /** Po's global home; builds land in `<home>/build/web/<key>/`. */\n poHome: string;\n /** The union of every scope's package roots; roots without a poWeb block are ignored. */\n pluginRoots: readonly string[];\n environment?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n /** Test seam over the child-process build. */\n runBuild?: (request: { hostRoot: string; pluginRoots: string[]; outDir: string }) => Promise<BuildOutput>;\n /** Test seam over package resolution. */\n resolveWebRoot?: (repoRoot: string, environment: NodeJS.ProcessEnv) => string | undefined;\n}\n\ninterface BuildOutput {\n assetsDir: string;\n pluginsDir: string;\n plugins: GlobalWebBuildPlugin[];\n pluginStyles: Record<string, string>;\n baseStyle: string;\n}\n\nexport type GlobalWebBuildResult =\n | { status: 'built' | 'cached'; build: GlobalWebBuildRecord }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/**\n * The size+mtime signature of every file a plugin package hands the build: its manifest\n * plus the whole top-level folder of the client entry and of each declared export, so an\n * edited import or a rebuilt dist chunk changes the key, not only the entry file.\n */\nfunction packageSignature(packageDir: string, entries: readonly string[]): string {\n try {\n const files = new Set<string>();\n for (const entry of entries) for (const file of snapshotFiles(packageDir, entry)) files.add(file);\n return statSignature(packageDir, [...files].sort());\n } catch (error) {\n // A declared export that is not built yet must not block the build; it only weakens the key.\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/**\n * The content key of a build: the format, the host package when there is one, then every\n * plugin package's file signature. A changed source or dist file or a changed plugin set\n * yields a new key; roots that declare no plugin do not take part.\n */\nexport function globalBuildKey(\n format: string,\n hostRoot: string | undefined,\n plugins: readonly DeclaredWebPlugin[],\n): string {\n const hash = crypto.createHash('sha256').update(format);\n if (hostRoot !== undefined)\n hash.update(`host:${hostRoot}:${fs.readFileSync(path.join(hostRoot, 'package.json'), 'utf8')}`);\n const entries = new Map<string, string[]>();\n for (const plugin of [...plugins].sort((a, b) => compareCodeUnits(a.pluginId, b.pluginId))) {\n hash.update(`\\0${plugin.pluginId}:${plugin.packageDir}:${plugin.client.entry}`);\n const list = entries.get(plugin.packageDir) ?? [];\n list.push(path.join(plugin.packageDir, plugin.client.entry));\n entries.set(plugin.packageDir, list);\n }\n for (const packageDir of [...entries.keys()].sort())\n hash.update(`\\0${packageDir}\\0${packageSignature(packageDir, entries.get(packageDir) ?? [])}`);\n return hash.digest('hex').slice(0, 32);\n}\n\nfunction bundleModuleUrl(): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./bundle.${extension}`, import.meta.url).href;\n}\n\n/** Runs Vite in a child process so its module graph and caches never stay in the server heap. */\nfunction runBuildProcess(\n request: { hostRoot: string; pluginRoots: string[]; outDir: string },\n environment: NodeJS.ProcessEnv,\n onNotice: (message: string) => void,\n): Promise<BuildOutput> {\n const args = [\n ...process.execArgv,\n '--input-type=module',\n '--eval',\n BUILD_CHILD_SCRIPT,\n bundleModuleUrl(),\n JSON.stringify(request),\n ];\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n args,\n { cwd: request.hostRoot, env: environment, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(new Error(`${stderr || stdout}`.trim() || `Web build exited with code ${String(error.code ?? 1)}`));\n return;\n }\n for (const line of stderr.split('\\n')) if (line.trim()) onNotice(line.trim());\n resolve(JSON.parse(stdout) as BuildOutput);\n },\n );\n });\n}\n\n/** A key directory's parsed build record, or undefined when the build is not there (yet). */\nexport function readBuildJson<T>(directory: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(path.join(directory, BUILD_RECORD), 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nfunction readBuildRecord(directory: string): GlobalWebBuildRecord | undefined {\n const record = readBuildJson<Omit<GlobalWebBuildRecord, 'webDirectory' | 'pluginsDirectory'>>(directory);\n if (record === undefined) return undefined;\n return { ...record, webDirectory: path.join(directory, 'web'), pluginsDirectory: path.join(directory, 'plugins') };\n}\n\nfunction webBuildsRoot(poHome: string): string {\n return path.join(poHome, 'build', 'web');\n}\n\n/** The complete global web build of one key, or undefined when it is not there. Never builds. */\nexport function readGlobalWebBuildRecord(poHome: string, key: string): GlobalWebBuildRecord | undefined {\n return readBuildRecord(path.join(webBuildsRoot(poHome), key));\n}\n\n/**\n * The key of the most recently written complete global web build, for a server that starts\n * before sync ever wrote a pointer. Temporary build folders start with a dot and are skipped.\n */\nexport function newestGlobalWebBuildKey(poHome: string): string | undefined {\n const root = webBuildsRoot(poHome);\n if (!fs.existsSync(root)) return undefined;\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => ({ name: entry.name, file: path.join(root, entry.name, BUILD_RECORD) }))\n .filter((entry) => fs.existsSync(entry.file))\n .map((entry) => ({ name: entry.name, modified: fs.statSync(entry.file).mtimeMs }))\n .sort((left, right) => right.modified - left.modified)[0]?.name;\n}\n\n/**\n * Builds into a temporary sibling of `directory`, writes the build record last, and renames\n * the result into place, so a key directory is always complete. Losing the rename to another\n * process that finished the same key is fine: its bytes are identical.\n */\nexport async function buildKeyDirectory(\n buildsRoot: string,\n key: string,\n directory: string,\n build: (temporary: string) => Promise<object>,\n): Promise<void> {\n fs.mkdirSync(buildsRoot, { recursive: true });\n const temporary = fs.mkdtempSync(path.join(buildsRoot, `.${key}-`));\n try {\n const record = await build(temporary);\n fs.writeFileSync(path.join(temporary, BUILD_RECORD), `${JSON.stringify(record, null, 2)}\\n`);\n try {\n fs.renameSync(temporary, directory);\n } catch (error) {\n if (readBuildJson(directory) === undefined) throw error;\n }\n } finally {\n fs.rmSync(temporary, { recursive: true, force: true });\n }\n}\n\nconst pendingBuilds = new Map<string, Promise<unknown>>();\n\n/** Joins a build of the same key that is already running in this process instead of starting another. */\nexport async function singleFlightBuild<T>(key: string, run: () => Promise<T>): Promise<T> {\n const pending = pendingBuilds.get(key);\n if (pending !== undefined) return pending as Promise<T>;\n const started = run();\n pendingBuilds.set(key, started);\n try {\n return await started;\n } finally {\n pendingBuilds.delete(key);\n }\n}\n\n/**\n * Ensures the one global web build for the given plugin roots exists and returns it.\n *\n * The output is `<home>/build/web/<key>/`, built in a temporary directory and renamed into\n * place, so a key directory is always complete; an existing key builds nothing. Each\n * plugin shares one content-hashed Tailwind stylesheet with the shell and fallback template.\n */\ntype ResolvedGlobalWebBuild =\n | { status: 'resolved'; webRoot: string; pluginRoots: string[]; key: string; buildsRoot: string; directory: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/** The build key for these plugin roots and where its build lives, without building anything. */\nfunction resolveGlobalWebBuild(input: GlobalWebBuildInput): ResolvedGlobalWebBuild {\n const environment = input.environment ?? process.env;\n const webRoot = (input.resolveWebRoot ?? resolveWebPackageRoot)(input.poHome, environment);\n if (webRoot === undefined || !fs.existsSync(path.join(webRoot, 'package.json')))\n return { status: 'skipped', reason: `${WEB_PACKAGE} is not installed; no web composition is published` };\n const pluginRoots = [...new Set(input.pluginRoots.map((root) => path.resolve(root)))].sort();\n let key: string;\n try {\n key = globalBuildKey(\n GLOBAL_BUILD_FORMAT,\n webRoot,\n scanWebPlugins(webRoot, pluginRoots, input.onNotice ?? ((): void => {})),\n );\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n const buildsRoot = path.join(input.poHome, 'build', 'web');\n return { status: 'resolved', webRoot, pluginRoots, key, buildsRoot, directory: path.join(buildsRoot, key) };\n}\n\n/** Whether the global build for these plugin roots exists, for a check that must not build. */\nexport function readGlobalWebBuildStatus(\n input: GlobalWebBuildInput,\n):\n | { status: 'current' | 'missing'; key: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string } {\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n return { status: readBuildRecord(resolved.directory) === undefined ? 'missing' : 'current', key: resolved.key };\n}\n\nexport async function ensureGlobalWebBuild(input: GlobalWebBuildInput): Promise<GlobalWebBuildResult> {\n const environment = input.environment ?? process.env;\n const notice = input.onNotice ?? ((): void => {});\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n const { webRoot, pluginRoots, key, buildsRoot, directory } = resolved;\n const existing = readBuildRecord(directory);\n if (existing !== undefined) return { status: 'cached', build: existing };\n return singleFlightBuild(key, async (): Promise<GlobalWebBuildResult> => {\n try {\n await buildKeyDirectory(buildsRoot, key, directory, async (temporary) => {\n const request = { hostRoot: webRoot, pluginRoots, outDir: temporary };\n const output = await (input.runBuild ?? ((value) => runBuildProcess(value, environment, notice)))(request);\n return {\n key,\n plugins: output.plugins,\n pluginStyles: output.pluginStyles,\n baseStyle: output.baseStyle,\n };\n });\n const build = readBuildRecord(directory);\n if (build === undefined) throw new Error(`The web build '${key}' was not recorded.`);\n return { status: 'built', build };\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n });\n}\n\ntype MountPluginRecord = { pluginId: string; packageDir: string; isHost: boolean };\n\n/** The plugin copies a mount runs, host plugins plus its own roots', at most one per pluginId (first wins). */\nfunction mountPluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): MountPluginRecord[] {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const seen = new Set<string>();\n return build.plugins.filter((plugin) => {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) return false;\n if (seen.has(plugin.pluginId)) return false;\n seen.add(plugin.pluginId);\n return true;\n });\n}\n\n/** The `pluginId` values a mount runs: the host's plugins plus those of its own package roots, deduplicated. */\nexport function mountWebPluginIds(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map((plugin) => plugin.pluginId);\n}\n\n/** The slots a mount runs: one copy per pluginId, the first in build order. */\nexport function mountWebPluginSlots(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map(pluginSlot);\n}\n\n/**\n * The package roots among `packageRoots` that declare a web plugin the build has no slot for:\n * a package that was not synced yet, or was left out of the build. Their names, else their roots.\n */\nexport function unslottedWebPackages(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n const slotted = new Set(build.plugins.map((plugin) => path.resolve(plugin.packageDir)));\n const missing: string[] = [];\n for (const root of new Set(packageRoots.map((entry) => path.resolve(entry)))) {\n if (slotted.has(root)) continue;\n let manifest: { name?: unknown; poWeb?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as typeof manifest;\n } catch {\n // Unreadable manifests are reported by the build's own scan, not as a missing slot.\n continue;\n }\n if (manifest.poWeb === undefined) continue;\n missing.push(typeof manifest.name === 'string' ? manifest.name : root);\n }\n return missing;\n}\n\n/** The pluginIds with more than one installed copy among the host and these roots, each with its slots in build order. */\nexport function duplicatePluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): Map<string, string[]> {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const slotsById = new Map<string, string[]>();\n for (const plugin of build.plugins) {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) continue;\n const slot = pluginSlot(plugin);\n const slots = slotsById.get(plugin.pluginId) ?? [];\n if (!slots.includes(slot)) slots.push(slot);\n slotsById.set(plugin.pluginId, slots);\n }\n return new Map([...slotsById].filter(([, slots]) => slots.length > 1));\n}\n"],"mappings":"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"}
|
|
@@ -109,34 +109,23 @@ function ensureBuiltinWebPluginModules({ packageRoot, check = false }) {
|
|
|
109
109
|
}
|
|
110
110
|
if (stale.length > 0) throw new Error(`Stale generated web plugin modules: ${stale.join(", ")}. Run pnpm build in po-web and commit them.`);
|
|
111
111
|
}
|
|
112
|
-
/** The
|
|
112
|
+
/** The shared stylesheet's Rollup input name. */
|
|
113
113
|
const BASE_STYLE_ENTRY = "base";
|
|
114
114
|
const pluginStyleEntryName = (slot) => `plugin-${slot}`;
|
|
115
|
-
/**
|
|
116
|
-
* One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,
|
|
117
|
-
* so a mount that loads its plugins' files never receives another workspace's classes.
|
|
118
|
-
* Host plugins live in the shell's sources and share the base entry.
|
|
119
|
-
*/
|
|
115
|
+
/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */
|
|
120
116
|
function writeWebPluginStyleEntries(plugins, generatedDir, packageRoot) {
|
|
121
117
|
const stylesDir = node_path.default.join(generatedDir, "styles");
|
|
122
118
|
node_fs.default.mkdirSync(stylesDir, { recursive: true });
|
|
123
119
|
const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);
|
|
124
120
|
const baseSourcesPath = node_path.default.join(generatedDir, "webPluginBaseSources.generated.css");
|
|
125
|
-
node_fs.default.writeFileSync(baseSourcesPath, renderCssModule(
|
|
121
|
+
node_fs.default.writeFileSync(baseSourcesPath, renderCssModule(plugins, [node_path.default.join(packageRoot, "src", "web"), ...fallbackTemplate === void 0 ? [] : [fallbackTemplate]]));
|
|
126
122
|
const appCss = relativeImport(node_fs.default.realpathSync(stylesDir), node_fs.default.realpathSync(node_path.default.join(packageRoot, "src", "web", "styles", "app.css")));
|
|
127
123
|
const baseEntryPath = node_path.default.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);
|
|
128
124
|
node_fs.default.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\n@import '${appCss}';\n`);
|
|
129
|
-
const pluginEntryPaths = /* @__PURE__ */ new Map();
|
|
130
|
-
for (const plugin of plugins) {
|
|
131
|
-
if (plugin.isHost) continue;
|
|
132
|
-
const entryPath = node_path.default.join(stylesDir, `${pluginStyleEntryName(plugin.slot)}.css`);
|
|
133
|
-
node_fs.default.writeFileSync(entryPath, `/* ${HEADER.slice(3)} */\n@import '${appCss}';\n@source "${pluginSourceRoot(plugin)}";\n`);
|
|
134
|
-
pluginEntryPaths.set(plugin.slot, entryPath);
|
|
135
|
-
}
|
|
136
125
|
return {
|
|
137
126
|
baseSourcesPath,
|
|
138
127
|
baseEntryPath,
|
|
139
|
-
pluginEntryPaths
|
|
128
|
+
pluginEntryPaths: /* @__PURE__ */ new Map()
|
|
140
129
|
};
|
|
141
130
|
}
|
|
142
131
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webPluginGenerate.cjs","names":["path","pathToFileURL","fs","createRequire","scanWebPlugins"],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { DeclaredWebPlugin } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst HEADER = '// Generated by po web builder. Do not edit by hand.';\n/** The template po-web renders when a composition names none, so its classes always compile. */\nconst FALLBACK_TEMPLATE_PACKAGE = '@agimon-ai/po-template-advanced';\nexport const BUILTIN_CLIENT_MODULE = path.join('src', 'web', 'app', 'webPlugins.generated.ts');\nexport const BUILTIN_CSS_MODULE = path.join('src', 'web', 'styles', 'webPluginSources.generated.css');\n\n/**\n * The import name of one plugin copy, derived from its slot so two copies of one pluginId\n * never collide. A host slot is its kebab-case pluginId and keeps the committed module's\n * names; a copy slot's '.' becomes '_', which no host name can contain.\n */\nfunction slotIdentifier(slot: string): string {\n return slot.replace(/-([a-z0-9])/g, (_, ch: string) => ch.toUpperCase()).replace(/[^A-Za-z0-9_$]/g, '_');\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n const spec = path.relative(fromDir, toFile).split(path.sep).join('/');\n return spec.startsWith('.') ? spec : `./${spec}`;\n}\n\nfunction renderClientModule(\n plugins: readonly DeclaredWebPlugin[],\n style: 'relative' | 'absolute',\n packageRoot: string,\n): string {\n const lines = [HEADER, \"import type { WebPluginDefinition } from '@agimon-ai/po-core/web';\"];\n const names: string[] = [];\n const fromDir = path.join(packageRoot, 'src', 'web', 'app');\n for (const plugin of plugins) {\n const name = `${slotIdentifier(plugin.slot)}Plugin`;\n names.push(name);\n const entry = path.join(plugin.packageDir, plugin.client.entry);\n // The committed builtin module keeps machine-neutral relative imports; the\n // sync-generated module lives outside src, is aliased over the committed\n // path, and resolves imports against its real location, so every entry\n // becomes an absolute file URL into the installed packages.\n const specifier =\n style === 'relative' ? relativeImport(fromDir, entry) : pathToFileURL(fs.realpathSync(entry)).href;\n lines.push(`import { webPlugin as ${name} } from '${specifier}';`);\n }\n lines.push('', `export const webPlugins: readonly WebPluginDefinition[] = [${names.join(', ')}];`, '');\n return lines.join('\\n');\n}\n\n/**\n * The directory Tailwind scans for a plugin's class names.\n *\n * `src/extensions` wins, because a plugin's components colocate with the routed\n * file that renders them, in private `_components` and `_lib` folders. The\n * legacy `src/web` root is only a fallback: `existsSync` is true for a\n * half-emptied directory holding nothing but a tsconfig, and packages have been\n * in exactly that state, so preferring it would hand Tailwind a directory with\n * no components in it and silently drop every class.\n *\n * The client entry's own directory is the last resort. It is published through\n * `src/exports/webClient.ts`, a one-line re-export, or generated outside `src`\n * entirely, so it holds no components either.\n */\nfunction pluginSourceRoot(plugin: DeclaredWebPlugin): string {\n const routingRoot = path.join(plugin.packageDir, 'src', 'extensions');\n if (fs.existsSync(routingRoot)) return routingRoot;\n const webRoot = path.join(plugin.packageDir, 'src', 'web');\n if (fs.existsSync(webRoot)) return webRoot;\n return path.dirname(path.join(plugin.packageDir, plugin.client.entry));\n}\n\n/**\n * Where Tailwind finds the fallback template's classes. app.css names it by a\n * path from its own folder into po-web's node_modules, which holds only in\n * this repository; an installed po-web has the template beside it instead,\n * so a sync build that scanned only that path shipped the cockpit without the\n * template's responsive layout. Resolving it as Node does works in both.\n */\nfunction fallbackTemplateSourceRoot(packageRoot: string): string | undefined {\n let manifest: string;\n try {\n manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(\n `${FALLBACK_TEMPLATE_PACKAGE}/package.json`,\n );\n } catch {\n // Not installed: the cockpit then has no fallback template to render either.\n return undefined;\n }\n const extensions = path.join(path.dirname(manifest), 'dist', 'extensions');\n return fs.existsSync(extensions) ? extensions : undefined;\n}\n\nfunction renderCssModule(plugins: readonly DeclaredWebPlugin[], leadingSourceRoots: readonly string[]): string {\n // Sync CSS is compiled from a generated directory, so Tailwind cannot infer\n // the stable shell tree from app.css. Its caller names that tree explicitly\n // so later plugin utilities do not override missing responsive shell utilities.\n const lines = [`/* ${HEADER.slice(3)} */`, ...leadingSourceRoots.map((root) => `@source \"${root}\";`)];\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n lines.push(`@source \"${pluginSourceRoot(plugin)}\";`);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\n/** The committed client registry, rendered from the host package alone. */\nexport function renderBuiltinWebPluginModules(packageRoot: string): Map<string, string> {\n const plugins = scanWebPlugins(packageRoot);\n return new Map([\n [BUILTIN_CLIENT_MODULE, renderClientModule(plugins, 'relative', packageRoot)],\n [BUILTIN_CSS_MODULE, renderCssModule(plugins, [])],\n ]);\n}\n\n/**\n * Keeps the committed client registry fresh: write-if-changed locally,\n * check-only (throw) in CI. Both vite.config.ts and tsdown.config.ts call\n * this at config-load time.\n */\nexport function ensureBuiltinWebPluginModules({\n packageRoot,\n check = false,\n}: {\n packageRoot: string;\n check?: boolean;\n}): void {\n const stale: string[] = [];\n for (const [relativePath, content] of renderBuiltinWebPluginModules(packageRoot)) {\n const filePath = path.join(packageRoot, relativePath);\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined;\n if (existing === content) continue;\n if (check) {\n stale.push(relativePath);\n continue;\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n if (stale.length > 0) {\n throw new Error(\n `Stale generated web plugin modules: ${stale.join(', ')}. Run pnpm build in po-web and commit them.`,\n );\n }\n}\n\n/** A client plugin registry and the Tailwind source list that goes with it. */\nexport interface WebPluginRegistryModules {\n clientModulePath: string;\n cssModulePath: string;\n}\n\nexport interface SyncGeneratedModules extends WebPluginRegistryModules {\n /** One entry module per slot, each re-exporting only that copy's `webPlugin`. */\n slotEntryPaths: Record<string, string>;\n /**\n * The signed shell's registry: host plugins only. Every other plugin reaches the\n * browser through its slot entry, and the global mount replaces the shell's\n * registry once it loads, so compiling them into the shell too shipped each plugin's\n * code and emitted assets (the voice VAD model among them) a second time.\n */\n shell: WebPluginRegistryModules;\n}\n\nexport interface WebPluginStyleEntries {\n /** Shell and fallback-template sources only, aliased over app.css's plugin source list. */\n baseSourcesPath: string;\n /** The stylesheet every plugin entry extends; a mount with no plugin styles loads it alone. */\n baseEntryPath: string;\n /** One stylesheet entry per non-host plugin copy, keyed by its slot. */\n pluginEntryPaths: Map<string, string>;\n}\n\n/** The style entry of each plugin's Rollup input name, so each emits one content-hashed file. */\nexport const BASE_STYLE_ENTRY = 'base';\nexport const pluginStyleEntryName = (slot: string): string => `plugin-${slot}`;\n\n/**\n * One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,\n * so a mount that loads its plugins' files never receives another workspace's classes.\n * Host plugins live in the shell's sources and share the base entry.\n */\nexport function writeWebPluginStyleEntries(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): WebPluginStyleEntries {\n const stylesDir = path.join(generatedDir, 'styles');\n fs.mkdirSync(stylesDir, { recursive: true });\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const baseSourcesPath = path.join(generatedDir, 'webPluginBaseSources.generated.css');\n fs.writeFileSync(\n baseSourcesPath,\n renderCssModule(\n [],\n [path.join(packageRoot, 'src', 'web'), ...(fallbackTemplate === undefined ? [] : [fallbackTemplate])],\n ),\n );\n // Resolvers follow real paths (a temp dir under /var is /private/var), so relate real paths.\n const appCss = relativeImport(\n fs.realpathSync(stylesDir),\n fs.realpathSync(path.join(packageRoot, 'src', 'web', 'styles', 'app.css')),\n );\n const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);\n fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n`);\n const pluginEntryPaths = new Map<string, string>();\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n const entryPath = path.join(stylesDir, `${pluginStyleEntryName(plugin.slot)}.css`);\n fs.writeFileSync(\n entryPath,\n `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n@source \"${pluginSourceRoot(plugin)}\";\\n`,\n );\n pluginEntryPaths.set(plugin.slot, entryPath);\n }\n return { baseSourcesPath, baseEntryPath, pluginEntryPaths };\n}\n\n/**\n * The sync-time variant: renders the full client plugin set (host built-ins plus\n * the installed composition's packages) into a generation directory the bundler\n * aliases over the committed builtin modules.\n */\nexport function writeSyncWebPluginModules(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): SyncGeneratedModules {\n fs.mkdirSync(generatedDir, { recursive: true });\n const clientModulePath = path.join(generatedDir, 'webPlugins.generated.ts');\n const cssModulePath = path.join(generatedDir, 'webPluginSources.generated.css');\n fs.writeFileSync(clientModulePath, renderClientModule(plugins, 'absolute', packageRoot));\n // Styles are built per plugin by writeWebPluginStyleEntries, so slot entries carry none.\n const slotsDir = path.join(generatedDir, 'slots');\n fs.mkdirSync(slotsDir, { recursive: true });\n const slotEntryPaths: Record<string, string> = {};\n for (const plugin of plugins) {\n const entryPath = path.join(slotsDir, `${plugin.slot}.ts`);\n const specifier = pathToFileURL(fs.realpathSync(path.join(plugin.packageDir, plugin.client.entry))).href;\n fs.writeFileSync(entryPath, `${HEADER}\\nexport { webPlugin } from '${specifier}';\\n`);\n slotEntryPaths[plugin.slot] = entryPath;\n }\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const leadingSourceRoots = [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ];\n fs.writeFileSync(cssModulePath, renderCssModule(plugins, leadingSourceRoots));\n const hostPlugins = plugins.filter((plugin) => plugin.isHost);\n const shell = {\n clientModulePath: path.join(generatedDir, 'webPluginShell.generated.ts'),\n cssModulePath: path.join(generatedDir, 'webPluginShellSources.generated.css'),\n };\n fs.writeFileSync(shell.clientModulePath, renderClientModule(hostPlugins, 'absolute', packageRoot));\n fs.writeFileSync(shell.cssModulePath, renderCssModule(hostPlugins, leadingSourceRoots));\n return {\n clientModulePath,\n slotEntryPaths,\n cssModulePath,\n shell,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webPluginGenerate.cjs","names":["path","pathToFileURL","fs","createRequire","scanWebPlugins"],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { DeclaredWebPlugin } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst HEADER = '// Generated by po web builder. Do not edit by hand.';\n/** The template po-web renders when a composition names none, so its classes always compile. */\nconst FALLBACK_TEMPLATE_PACKAGE = '@agimon-ai/po-template-advanced';\nexport const BUILTIN_CLIENT_MODULE = path.join('src', 'web', 'app', 'webPlugins.generated.ts');\nexport const BUILTIN_CSS_MODULE = path.join('src', 'web', 'styles', 'webPluginSources.generated.css');\n\n/**\n * The import name of one plugin copy, derived from its slot so two copies of one pluginId\n * never collide. A host slot is its kebab-case pluginId and keeps the committed module's\n * names; a copy slot's '.' becomes '_', which no host name can contain.\n */\nfunction slotIdentifier(slot: string): string {\n return slot.replace(/-([a-z0-9])/g, (_, ch: string) => ch.toUpperCase()).replace(/[^A-Za-z0-9_$]/g, '_');\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n const spec = path.relative(fromDir, toFile).split(path.sep).join('/');\n return spec.startsWith('.') ? spec : `./${spec}`;\n}\n\nfunction renderClientModule(\n plugins: readonly DeclaredWebPlugin[],\n style: 'relative' | 'absolute',\n packageRoot: string,\n): string {\n const lines = [HEADER, \"import type { WebPluginDefinition } from '@agimon-ai/po-core/web';\"];\n const names: string[] = [];\n const fromDir = path.join(packageRoot, 'src', 'web', 'app');\n for (const plugin of plugins) {\n const name = `${slotIdentifier(plugin.slot)}Plugin`;\n names.push(name);\n const entry = path.join(plugin.packageDir, plugin.client.entry);\n // The committed builtin module keeps machine-neutral relative imports; the\n // sync-generated module lives outside src, is aliased over the committed\n // path, and resolves imports against its real location, so every entry\n // becomes an absolute file URL into the installed packages.\n const specifier =\n style === 'relative' ? relativeImport(fromDir, entry) : pathToFileURL(fs.realpathSync(entry)).href;\n lines.push(`import { webPlugin as ${name} } from '${specifier}';`);\n }\n lines.push('', `export const webPlugins: readonly WebPluginDefinition[] = [${names.join(', ')}];`, '');\n return lines.join('\\n');\n}\n\n/**\n * The directory Tailwind scans for a plugin's class names.\n *\n * `src/extensions` wins, because a plugin's components colocate with the routed\n * file that renders them, in private `_components` and `_lib` folders. The\n * legacy `src/web` root is only a fallback: `existsSync` is true for a\n * half-emptied directory holding nothing but a tsconfig, and packages have been\n * in exactly that state, so preferring it would hand Tailwind a directory with\n * no components in it and silently drop every class.\n *\n * The client entry's own directory is the last resort. It is published through\n * `src/exports/webClient.ts`, a one-line re-export, or generated outside `src`\n * entirely, so it holds no components either.\n */\nfunction pluginSourceRoot(plugin: DeclaredWebPlugin): string {\n const routingRoot = path.join(plugin.packageDir, 'src', 'extensions');\n if (fs.existsSync(routingRoot)) return routingRoot;\n const webRoot = path.join(plugin.packageDir, 'src', 'web');\n if (fs.existsSync(webRoot)) return webRoot;\n return path.dirname(path.join(plugin.packageDir, plugin.client.entry));\n}\n\n/**\n * Where Tailwind finds the fallback template's classes. app.css names it by a\n * path from its own folder into po-web's node_modules, which holds only in\n * this repository; an installed po-web has the template beside it instead,\n * so a sync build that scanned only that path shipped the cockpit without the\n * template's responsive layout. Resolving it as Node does works in both.\n */\nfunction fallbackTemplateSourceRoot(packageRoot: string): string | undefined {\n let manifest: string;\n try {\n manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(\n `${FALLBACK_TEMPLATE_PACKAGE}/package.json`,\n );\n } catch {\n // Not installed: the cockpit then has no fallback template to render either.\n return undefined;\n }\n const extensions = path.join(path.dirname(manifest), 'dist', 'extensions');\n return fs.existsSync(extensions) ? extensions : undefined;\n}\n\nfunction renderCssModule(plugins: readonly DeclaredWebPlugin[], leadingSourceRoots: readonly string[]): string {\n // Sync CSS is compiled from a generated directory, so Tailwind cannot infer\n // the stable shell tree from app.css. Its caller names that tree explicitly\n // so later plugin utilities do not override missing responsive shell utilities.\n const lines = [`/* ${HEADER.slice(3)} */`, ...leadingSourceRoots.map((root) => `@source \"${root}\";`)];\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n lines.push(`@source \"${pluginSourceRoot(plugin)}\";`);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\n/** The committed client registry, rendered from the host package alone. */\nexport function renderBuiltinWebPluginModules(packageRoot: string): Map<string, string> {\n const plugins = scanWebPlugins(packageRoot);\n return new Map([\n [BUILTIN_CLIENT_MODULE, renderClientModule(plugins, 'relative', packageRoot)],\n [BUILTIN_CSS_MODULE, renderCssModule(plugins, [])],\n ]);\n}\n\n/**\n * Keeps the committed client registry fresh: write-if-changed locally,\n * check-only (throw) in CI. Both vite.config.ts and tsdown.config.ts call\n * this at config-load time.\n */\nexport function ensureBuiltinWebPluginModules({\n packageRoot,\n check = false,\n}: {\n packageRoot: string;\n check?: boolean;\n}): void {\n const stale: string[] = [];\n for (const [relativePath, content] of renderBuiltinWebPluginModules(packageRoot)) {\n const filePath = path.join(packageRoot, relativePath);\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined;\n if (existing === content) continue;\n if (check) {\n stale.push(relativePath);\n continue;\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n if (stale.length > 0) {\n throw new Error(\n `Stale generated web plugin modules: ${stale.join(', ')}. Run pnpm build in po-web and commit them.`,\n );\n }\n}\n\n/** A client plugin registry and the Tailwind source list that goes with it. */\nexport interface WebPluginRegistryModules {\n clientModulePath: string;\n cssModulePath: string;\n}\n\nexport interface SyncGeneratedModules extends WebPluginRegistryModules {\n /** One entry module per slot, each re-exporting only that copy's `webPlugin`. */\n slotEntryPaths: Record<string, string>;\n /**\n * The signed shell's registry: host plugins only. Every other plugin reaches the\n * browser through its slot entry, and the global mount replaces the shell's\n * registry once it loads, so compiling them into the shell too shipped each plugin's\n * code and emitted assets (the voice VAD model among them) a second time.\n */\n shell: WebPluginRegistryModules;\n}\n\nexport interface WebPluginStyleEntries {\n /** Shell, fallback-template, and plugin sources, aliased over app.css's source list. */\n baseSourcesPath: string;\n /** The shared Tailwind entry every mount loads. */\n baseEntryPath: string;\n /** ponytail: empty compatibility field; remove only with a public webBuilder API change. */\n pluginEntryPaths: Map<string, string>;\n}\n\n/** The shared stylesheet's Rollup input name. */\nexport const BASE_STYLE_ENTRY = 'base';\nexport const pluginStyleEntryName = (slot: string): string => `plugin-${slot}`;\n\n/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */\nexport function writeWebPluginStyleEntries(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): WebPluginStyleEntries {\n const stylesDir = path.join(generatedDir, 'styles');\n fs.mkdirSync(stylesDir, { recursive: true });\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const baseSourcesPath = path.join(generatedDir, 'webPluginBaseSources.generated.css');\n fs.writeFileSync(\n baseSourcesPath,\n renderCssModule(plugins, [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ]),\n );\n // Resolvers follow real paths (a temp dir under /var is /private/var), so relate real paths.\n const appCss = relativeImport(\n fs.realpathSync(stylesDir),\n fs.realpathSync(path.join(packageRoot, 'src', 'web', 'styles', 'app.css')),\n );\n const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);\n fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n`);\n const pluginEntryPaths = new Map<string, string>();\n return { baseSourcesPath, baseEntryPath, pluginEntryPaths };\n}\n\n/**\n * The sync-time variant: renders the full client plugin set (host built-ins plus\n * the installed composition's packages) into a generation directory the bundler\n * aliases over the committed builtin modules.\n */\nexport function writeSyncWebPluginModules(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): SyncGeneratedModules {\n fs.mkdirSync(generatedDir, { recursive: true });\n const clientModulePath = path.join(generatedDir, 'webPlugins.generated.ts');\n const cssModulePath = path.join(generatedDir, 'webPluginSources.generated.css');\n fs.writeFileSync(clientModulePath, renderClientModule(plugins, 'absolute', packageRoot));\n // Tailwind styles compile separately as one shared sheet, so slot entries carry none.\n const slotsDir = path.join(generatedDir, 'slots');\n fs.mkdirSync(slotsDir, { recursive: true });\n const slotEntryPaths: Record<string, string> = {};\n for (const plugin of plugins) {\n const entryPath = path.join(slotsDir, `${plugin.slot}.ts`);\n const specifier = pathToFileURL(fs.realpathSync(path.join(plugin.packageDir, plugin.client.entry))).href;\n fs.writeFileSync(entryPath, `${HEADER}\\nexport { webPlugin } from '${specifier}';\\n`);\n slotEntryPaths[plugin.slot] = entryPath;\n }\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const leadingSourceRoots = [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ];\n fs.writeFileSync(cssModulePath, renderCssModule(plugins, leadingSourceRoots));\n const hostPlugins = plugins.filter((plugin) => plugin.isHost);\n const shell = {\n clientModulePath: path.join(generatedDir, 'webPluginShell.generated.ts'),\n cssModulePath: path.join(generatedDir, 'webPluginShellSources.generated.css'),\n };\n fs.writeFileSync(shell.clientModulePath, renderClientModule(hostPlugins, 'absolute', packageRoot));\n fs.writeFileSync(shell.cssModulePath, renderCssModule(hostPlugins, leadingSourceRoots));\n return {\n clientModulePath,\n slotEntryPaths,\n cssModulePath,\n shell,\n };\n}\n"],"mappings":"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"}
|
|
@@ -30,21 +30,17 @@ export interface SyncGeneratedModules extends WebPluginRegistryModules {
|
|
|
30
30
|
shell: WebPluginRegistryModules;
|
|
31
31
|
}
|
|
32
32
|
export interface WebPluginStyleEntries {
|
|
33
|
-
/** Shell
|
|
33
|
+
/** Shell, fallback-template, and plugin sources, aliased over app.css's source list. */
|
|
34
34
|
baseSourcesPath: string;
|
|
35
|
-
/** The
|
|
35
|
+
/** The shared Tailwind entry every mount loads. */
|
|
36
36
|
baseEntryPath: string;
|
|
37
|
-
/**
|
|
37
|
+
/** ponytail: empty compatibility field; remove only with a public webBuilder API change. */
|
|
38
38
|
pluginEntryPaths: Map<string, string>;
|
|
39
39
|
}
|
|
40
|
-
/** The
|
|
40
|
+
/** The shared stylesheet's Rollup input name. */
|
|
41
41
|
export declare const BASE_STYLE_ENTRY = "base";
|
|
42
42
|
export declare const pluginStyleEntryName: (slot: string) => string;
|
|
43
|
-
/**
|
|
44
|
-
* One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,
|
|
45
|
-
* so a mount that loads its plugins' files never receives another workspace's classes.
|
|
46
|
-
* Host plugins live in the shell's sources and share the base entry.
|
|
47
|
-
*/
|
|
43
|
+
/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */
|
|
48
44
|
export declare function writeWebPluginStyleEntries(plugins: readonly DeclaredWebPlugin[], generatedDir: string, packageRoot: string): WebPluginStyleEntries;
|
|
49
45
|
/**
|
|
50
46
|
* The sync-time variant: renders the full client plugin set (host built-ins plus
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webPluginGenerate.d.mts","names":[],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"mappings":";;qBAWa;qBACA;;wBAiGG,8BAA8B,sBAAsB;;;;;;wBAapD,gCACd,aACA;EAEA;EACA;;;iBAsBe;EACf;EACA;;iBAGe,6BAA6B;;EAE5C,gBAAgB;;;;;;;EAOhB,OAAO;;iBAGQ;;EAEf;;EAEA;;EAEA,kBAAkB;;;qBAIP;qBACA,uBAAoB
|
|
1
|
+
{"version":3,"file":"webPluginGenerate.d.mts","names":[],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"mappings":";;qBAWa;qBACA;;wBAiGG,8BAA8B,sBAAsB;;;;;;wBAapD,gCACd,aACA;EAEA;EACA;;;iBAsBe;EACf;EACA;;iBAGe,6BAA6B;;EAE5C,gBAAgB;;;;;;;EAOhB,OAAO;;iBAGQ;;EAEf;;EAEA;;EAEA,kBAAkB;;;qBAIP;qBACA,uBAAoB;;wBAGjB,2BACd,kBAAkB,qBAClB,sBACA,sBACC;;;;;;wBA4Ba,0BACd,kBAAkB,qBAClB,sBACA,sBACC"}
|
|
@@ -106,34 +106,23 @@ function ensureBuiltinWebPluginModules({ packageRoot, check = false }) {
|
|
|
106
106
|
}
|
|
107
107
|
if (stale.length > 0) throw new Error(`Stale generated web plugin modules: ${stale.join(", ")}. Run pnpm build in po-web and commit them.`);
|
|
108
108
|
}
|
|
109
|
-
/** The
|
|
109
|
+
/** The shared stylesheet's Rollup input name. */
|
|
110
110
|
const BASE_STYLE_ENTRY = "base";
|
|
111
111
|
const pluginStyleEntryName = (slot) => `plugin-${slot}`;
|
|
112
|
-
/**
|
|
113
|
-
* One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,
|
|
114
|
-
* so a mount that loads its plugins' files never receives another workspace's classes.
|
|
115
|
-
* Host plugins live in the shell's sources and share the base entry.
|
|
116
|
-
*/
|
|
112
|
+
/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */
|
|
117
113
|
function writeWebPluginStyleEntries(plugins, generatedDir, packageRoot) {
|
|
118
114
|
const stylesDir = path.join(generatedDir, "styles");
|
|
119
115
|
fs.mkdirSync(stylesDir, { recursive: true });
|
|
120
116
|
const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);
|
|
121
117
|
const baseSourcesPath = path.join(generatedDir, "webPluginBaseSources.generated.css");
|
|
122
|
-
fs.writeFileSync(baseSourcesPath, renderCssModule(
|
|
118
|
+
fs.writeFileSync(baseSourcesPath, renderCssModule(plugins, [path.join(packageRoot, "src", "web"), ...fallbackTemplate === void 0 ? [] : [fallbackTemplate]]));
|
|
123
119
|
const appCss = relativeImport(fs.realpathSync(stylesDir), fs.realpathSync(path.join(packageRoot, "src", "web", "styles", "app.css")));
|
|
124
120
|
const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);
|
|
125
121
|
fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\n@import '${appCss}';\n`);
|
|
126
|
-
const pluginEntryPaths = /* @__PURE__ */ new Map();
|
|
127
|
-
for (const plugin of plugins) {
|
|
128
|
-
if (plugin.isHost) continue;
|
|
129
|
-
const entryPath = path.join(stylesDir, `${pluginStyleEntryName(plugin.slot)}.css`);
|
|
130
|
-
fs.writeFileSync(entryPath, `/* ${HEADER.slice(3)} */\n@import '${appCss}';\n@source "${pluginSourceRoot(plugin)}";\n`);
|
|
131
|
-
pluginEntryPaths.set(plugin.slot, entryPath);
|
|
132
|
-
}
|
|
133
122
|
return {
|
|
134
123
|
baseSourcesPath,
|
|
135
124
|
baseEntryPath,
|
|
136
|
-
pluginEntryPaths
|
|
125
|
+
pluginEntryPaths: /* @__PURE__ */ new Map()
|
|
137
126
|
};
|
|
138
127
|
}
|
|
139
128
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webPluginGenerate.mjs","names":[],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { DeclaredWebPlugin } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst HEADER = '// Generated by po web builder. Do not edit by hand.';\n/** The template po-web renders when a composition names none, so its classes always compile. */\nconst FALLBACK_TEMPLATE_PACKAGE = '@agimon-ai/po-template-advanced';\nexport const BUILTIN_CLIENT_MODULE = path.join('src', 'web', 'app', 'webPlugins.generated.ts');\nexport const BUILTIN_CSS_MODULE = path.join('src', 'web', 'styles', 'webPluginSources.generated.css');\n\n/**\n * The import name of one plugin copy, derived from its slot so two copies of one pluginId\n * never collide. A host slot is its kebab-case pluginId and keeps the committed module's\n * names; a copy slot's '.' becomes '_', which no host name can contain.\n */\nfunction slotIdentifier(slot: string): string {\n return slot.replace(/-([a-z0-9])/g, (_, ch: string) => ch.toUpperCase()).replace(/[^A-Za-z0-9_$]/g, '_');\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n const spec = path.relative(fromDir, toFile).split(path.sep).join('/');\n return spec.startsWith('.') ? spec : `./${spec}`;\n}\n\nfunction renderClientModule(\n plugins: readonly DeclaredWebPlugin[],\n style: 'relative' | 'absolute',\n packageRoot: string,\n): string {\n const lines = [HEADER, \"import type { WebPluginDefinition } from '@agimon-ai/po-core/web';\"];\n const names: string[] = [];\n const fromDir = path.join(packageRoot, 'src', 'web', 'app');\n for (const plugin of plugins) {\n const name = `${slotIdentifier(plugin.slot)}Plugin`;\n names.push(name);\n const entry = path.join(plugin.packageDir, plugin.client.entry);\n // The committed builtin module keeps machine-neutral relative imports; the\n // sync-generated module lives outside src, is aliased over the committed\n // path, and resolves imports against its real location, so every entry\n // becomes an absolute file URL into the installed packages.\n const specifier =\n style === 'relative' ? relativeImport(fromDir, entry) : pathToFileURL(fs.realpathSync(entry)).href;\n lines.push(`import { webPlugin as ${name} } from '${specifier}';`);\n }\n lines.push('', `export const webPlugins: readonly WebPluginDefinition[] = [${names.join(', ')}];`, '');\n return lines.join('\\n');\n}\n\n/**\n * The directory Tailwind scans for a plugin's class names.\n *\n * `src/extensions` wins, because a plugin's components colocate with the routed\n * file that renders them, in private `_components` and `_lib` folders. The\n * legacy `src/web` root is only a fallback: `existsSync` is true for a\n * half-emptied directory holding nothing but a tsconfig, and packages have been\n * in exactly that state, so preferring it would hand Tailwind a directory with\n * no components in it and silently drop every class.\n *\n * The client entry's own directory is the last resort. It is published through\n * `src/exports/webClient.ts`, a one-line re-export, or generated outside `src`\n * entirely, so it holds no components either.\n */\nfunction pluginSourceRoot(plugin: DeclaredWebPlugin): string {\n const routingRoot = path.join(plugin.packageDir, 'src', 'extensions');\n if (fs.existsSync(routingRoot)) return routingRoot;\n const webRoot = path.join(plugin.packageDir, 'src', 'web');\n if (fs.existsSync(webRoot)) return webRoot;\n return path.dirname(path.join(plugin.packageDir, plugin.client.entry));\n}\n\n/**\n * Where Tailwind finds the fallback template's classes. app.css names it by a\n * path from its own folder into po-web's node_modules, which holds only in\n * this repository; an installed po-web has the template beside it instead,\n * so a sync build that scanned only that path shipped the cockpit without the\n * template's responsive layout. Resolving it as Node does works in both.\n */\nfunction fallbackTemplateSourceRoot(packageRoot: string): string | undefined {\n let manifest: string;\n try {\n manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(\n `${FALLBACK_TEMPLATE_PACKAGE}/package.json`,\n );\n } catch {\n // Not installed: the cockpit then has no fallback template to render either.\n return undefined;\n }\n const extensions = path.join(path.dirname(manifest), 'dist', 'extensions');\n return fs.existsSync(extensions) ? extensions : undefined;\n}\n\nfunction renderCssModule(plugins: readonly DeclaredWebPlugin[], leadingSourceRoots: readonly string[]): string {\n // Sync CSS is compiled from a generated directory, so Tailwind cannot infer\n // the stable shell tree from app.css. Its caller names that tree explicitly\n // so later plugin utilities do not override missing responsive shell utilities.\n const lines = [`/* ${HEADER.slice(3)} */`, ...leadingSourceRoots.map((root) => `@source \"${root}\";`)];\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n lines.push(`@source \"${pluginSourceRoot(plugin)}\";`);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\n/** The committed client registry, rendered from the host package alone. */\nexport function renderBuiltinWebPluginModules(packageRoot: string): Map<string, string> {\n const plugins = scanWebPlugins(packageRoot);\n return new Map([\n [BUILTIN_CLIENT_MODULE, renderClientModule(plugins, 'relative', packageRoot)],\n [BUILTIN_CSS_MODULE, renderCssModule(plugins, [])],\n ]);\n}\n\n/**\n * Keeps the committed client registry fresh: write-if-changed locally,\n * check-only (throw) in CI. Both vite.config.ts and tsdown.config.ts call\n * this at config-load time.\n */\nexport function ensureBuiltinWebPluginModules({\n packageRoot,\n check = false,\n}: {\n packageRoot: string;\n check?: boolean;\n}): void {\n const stale: string[] = [];\n for (const [relativePath, content] of renderBuiltinWebPluginModules(packageRoot)) {\n const filePath = path.join(packageRoot, relativePath);\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined;\n if (existing === content) continue;\n if (check) {\n stale.push(relativePath);\n continue;\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n if (stale.length > 0) {\n throw new Error(\n `Stale generated web plugin modules: ${stale.join(', ')}. Run pnpm build in po-web and commit them.`,\n );\n }\n}\n\n/** A client plugin registry and the Tailwind source list that goes with it. */\nexport interface WebPluginRegistryModules {\n clientModulePath: string;\n cssModulePath: string;\n}\n\nexport interface SyncGeneratedModules extends WebPluginRegistryModules {\n /** One entry module per slot, each re-exporting only that copy's `webPlugin`. */\n slotEntryPaths: Record<string, string>;\n /**\n * The signed shell's registry: host plugins only. Every other plugin reaches the\n * browser through its slot entry, and the global mount replaces the shell's\n * registry once it loads, so compiling them into the shell too shipped each plugin's\n * code and emitted assets (the voice VAD model among them) a second time.\n */\n shell: WebPluginRegistryModules;\n}\n\nexport interface WebPluginStyleEntries {\n /** Shell and fallback-template sources only, aliased over app.css's plugin source list. */\n baseSourcesPath: string;\n /** The stylesheet every plugin entry extends; a mount with no plugin styles loads it alone. */\n baseEntryPath: string;\n /** One stylesheet entry per non-host plugin copy, keyed by its slot. */\n pluginEntryPaths: Map<string, string>;\n}\n\n/** The style entry of each plugin's Rollup input name, so each emits one content-hashed file. */\nexport const BASE_STYLE_ENTRY = 'base';\nexport const pluginStyleEntryName = (slot: string): string => `plugin-${slot}`;\n\n/**\n * One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,\n * so a mount that loads its plugins' files never receives another workspace's classes.\n * Host plugins live in the shell's sources and share the base entry.\n */\nexport function writeWebPluginStyleEntries(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): WebPluginStyleEntries {\n const stylesDir = path.join(generatedDir, 'styles');\n fs.mkdirSync(stylesDir, { recursive: true });\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const baseSourcesPath = path.join(generatedDir, 'webPluginBaseSources.generated.css');\n fs.writeFileSync(\n baseSourcesPath,\n renderCssModule(\n [],\n [path.join(packageRoot, 'src', 'web'), ...(fallbackTemplate === undefined ? [] : [fallbackTemplate])],\n ),\n );\n // Resolvers follow real paths (a temp dir under /var is /private/var), so relate real paths.\n const appCss = relativeImport(\n fs.realpathSync(stylesDir),\n fs.realpathSync(path.join(packageRoot, 'src', 'web', 'styles', 'app.css')),\n );\n const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);\n fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n`);\n const pluginEntryPaths = new Map<string, string>();\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n const entryPath = path.join(stylesDir, `${pluginStyleEntryName(plugin.slot)}.css`);\n fs.writeFileSync(\n entryPath,\n `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n@source \"${pluginSourceRoot(plugin)}\";\\n`,\n );\n pluginEntryPaths.set(plugin.slot, entryPath);\n }\n return { baseSourcesPath, baseEntryPath, pluginEntryPaths };\n}\n\n/**\n * The sync-time variant: renders the full client plugin set (host built-ins plus\n * the installed composition's packages) into a generation directory the bundler\n * aliases over the committed builtin modules.\n */\nexport function writeSyncWebPluginModules(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): SyncGeneratedModules {\n fs.mkdirSync(generatedDir, { recursive: true });\n const clientModulePath = path.join(generatedDir, 'webPlugins.generated.ts');\n const cssModulePath = path.join(generatedDir, 'webPluginSources.generated.css');\n fs.writeFileSync(clientModulePath, renderClientModule(plugins, 'absolute', packageRoot));\n // Styles are built per plugin by writeWebPluginStyleEntries, so slot entries carry none.\n const slotsDir = path.join(generatedDir, 'slots');\n fs.mkdirSync(slotsDir, { recursive: true });\n const slotEntryPaths: Record<string, string> = {};\n for (const plugin of plugins) {\n const entryPath = path.join(slotsDir, `${plugin.slot}.ts`);\n const specifier = pathToFileURL(fs.realpathSync(path.join(plugin.packageDir, plugin.client.entry))).href;\n fs.writeFileSync(entryPath, `${HEADER}\\nexport { webPlugin } from '${specifier}';\\n`);\n slotEntryPaths[plugin.slot] = entryPath;\n }\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const leadingSourceRoots = [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ];\n fs.writeFileSync(cssModulePath, renderCssModule(plugins, leadingSourceRoots));\n const hostPlugins = plugins.filter((plugin) => plugin.isHost);\n const shell = {\n clientModulePath: path.join(generatedDir, 'webPluginShell.generated.ts'),\n cssModulePath: path.join(generatedDir, 'webPluginShellSources.generated.css'),\n };\n fs.writeFileSync(shell.clientModulePath, renderClientModule(hostPlugins, 'absolute', packageRoot));\n fs.writeFileSync(shell.cssModulePath, renderCssModule(hostPlugins, leadingSourceRoots));\n return {\n clientModulePath,\n slotEntryPaths,\n cssModulePath,\n shell,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webPluginGenerate.mjs","names":[],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { DeclaredWebPlugin } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst HEADER = '// Generated by po web builder. Do not edit by hand.';\n/** The template po-web renders when a composition names none, so its classes always compile. */\nconst FALLBACK_TEMPLATE_PACKAGE = '@agimon-ai/po-template-advanced';\nexport const BUILTIN_CLIENT_MODULE = path.join('src', 'web', 'app', 'webPlugins.generated.ts');\nexport const BUILTIN_CSS_MODULE = path.join('src', 'web', 'styles', 'webPluginSources.generated.css');\n\n/**\n * The import name of one plugin copy, derived from its slot so two copies of one pluginId\n * never collide. A host slot is its kebab-case pluginId and keeps the committed module's\n * names; a copy slot's '.' becomes '_', which no host name can contain.\n */\nfunction slotIdentifier(slot: string): string {\n return slot.replace(/-([a-z0-9])/g, (_, ch: string) => ch.toUpperCase()).replace(/[^A-Za-z0-9_$]/g, '_');\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n const spec = path.relative(fromDir, toFile).split(path.sep).join('/');\n return spec.startsWith('.') ? spec : `./${spec}`;\n}\n\nfunction renderClientModule(\n plugins: readonly DeclaredWebPlugin[],\n style: 'relative' | 'absolute',\n packageRoot: string,\n): string {\n const lines = [HEADER, \"import type { WebPluginDefinition } from '@agimon-ai/po-core/web';\"];\n const names: string[] = [];\n const fromDir = path.join(packageRoot, 'src', 'web', 'app');\n for (const plugin of plugins) {\n const name = `${slotIdentifier(plugin.slot)}Plugin`;\n names.push(name);\n const entry = path.join(plugin.packageDir, plugin.client.entry);\n // The committed builtin module keeps machine-neutral relative imports; the\n // sync-generated module lives outside src, is aliased over the committed\n // path, and resolves imports against its real location, so every entry\n // becomes an absolute file URL into the installed packages.\n const specifier =\n style === 'relative' ? relativeImport(fromDir, entry) : pathToFileURL(fs.realpathSync(entry)).href;\n lines.push(`import { webPlugin as ${name} } from '${specifier}';`);\n }\n lines.push('', `export const webPlugins: readonly WebPluginDefinition[] = [${names.join(', ')}];`, '');\n return lines.join('\\n');\n}\n\n/**\n * The directory Tailwind scans for a plugin's class names.\n *\n * `src/extensions` wins, because a plugin's components colocate with the routed\n * file that renders them, in private `_components` and `_lib` folders. The\n * legacy `src/web` root is only a fallback: `existsSync` is true for a\n * half-emptied directory holding nothing but a tsconfig, and packages have been\n * in exactly that state, so preferring it would hand Tailwind a directory with\n * no components in it and silently drop every class.\n *\n * The client entry's own directory is the last resort. It is published through\n * `src/exports/webClient.ts`, a one-line re-export, or generated outside `src`\n * entirely, so it holds no components either.\n */\nfunction pluginSourceRoot(plugin: DeclaredWebPlugin): string {\n const routingRoot = path.join(plugin.packageDir, 'src', 'extensions');\n if (fs.existsSync(routingRoot)) return routingRoot;\n const webRoot = path.join(plugin.packageDir, 'src', 'web');\n if (fs.existsSync(webRoot)) return webRoot;\n return path.dirname(path.join(plugin.packageDir, plugin.client.entry));\n}\n\n/**\n * Where Tailwind finds the fallback template's classes. app.css names it by a\n * path from its own folder into po-web's node_modules, which holds only in\n * this repository; an installed po-web has the template beside it instead,\n * so a sync build that scanned only that path shipped the cockpit without the\n * template's responsive layout. Resolving it as Node does works in both.\n */\nfunction fallbackTemplateSourceRoot(packageRoot: string): string | undefined {\n let manifest: string;\n try {\n manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(\n `${FALLBACK_TEMPLATE_PACKAGE}/package.json`,\n );\n } catch {\n // Not installed: the cockpit then has no fallback template to render either.\n return undefined;\n }\n const extensions = path.join(path.dirname(manifest), 'dist', 'extensions');\n return fs.existsSync(extensions) ? extensions : undefined;\n}\n\nfunction renderCssModule(plugins: readonly DeclaredWebPlugin[], leadingSourceRoots: readonly string[]): string {\n // Sync CSS is compiled from a generated directory, so Tailwind cannot infer\n // the stable shell tree from app.css. Its caller names that tree explicitly\n // so later plugin utilities do not override missing responsive shell utilities.\n const lines = [`/* ${HEADER.slice(3)} */`, ...leadingSourceRoots.map((root) => `@source \"${root}\";`)];\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n lines.push(`@source \"${pluginSourceRoot(plugin)}\";`);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\n/** The committed client registry, rendered from the host package alone. */\nexport function renderBuiltinWebPluginModules(packageRoot: string): Map<string, string> {\n const plugins = scanWebPlugins(packageRoot);\n return new Map([\n [BUILTIN_CLIENT_MODULE, renderClientModule(plugins, 'relative', packageRoot)],\n [BUILTIN_CSS_MODULE, renderCssModule(plugins, [])],\n ]);\n}\n\n/**\n * Keeps the committed client registry fresh: write-if-changed locally,\n * check-only (throw) in CI. Both vite.config.ts and tsdown.config.ts call\n * this at config-load time.\n */\nexport function ensureBuiltinWebPluginModules({\n packageRoot,\n check = false,\n}: {\n packageRoot: string;\n check?: boolean;\n}): void {\n const stale: string[] = [];\n for (const [relativePath, content] of renderBuiltinWebPluginModules(packageRoot)) {\n const filePath = path.join(packageRoot, relativePath);\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined;\n if (existing === content) continue;\n if (check) {\n stale.push(relativePath);\n continue;\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n if (stale.length > 0) {\n throw new Error(\n `Stale generated web plugin modules: ${stale.join(', ')}. Run pnpm build in po-web and commit them.`,\n );\n }\n}\n\n/** A client plugin registry and the Tailwind source list that goes with it. */\nexport interface WebPluginRegistryModules {\n clientModulePath: string;\n cssModulePath: string;\n}\n\nexport interface SyncGeneratedModules extends WebPluginRegistryModules {\n /** One entry module per slot, each re-exporting only that copy's `webPlugin`. */\n slotEntryPaths: Record<string, string>;\n /**\n * The signed shell's registry: host plugins only. Every other plugin reaches the\n * browser through its slot entry, and the global mount replaces the shell's\n * registry once it loads, so compiling them into the shell too shipped each plugin's\n * code and emitted assets (the voice VAD model among them) a second time.\n */\n shell: WebPluginRegistryModules;\n}\n\nexport interface WebPluginStyleEntries {\n /** Shell, fallback-template, and plugin sources, aliased over app.css's source list. */\n baseSourcesPath: string;\n /** The shared Tailwind entry every mount loads. */\n baseEntryPath: string;\n /** ponytail: empty compatibility field; remove only with a public webBuilder API change. */\n pluginEntryPaths: Map<string, string>;\n}\n\n/** The shared stylesheet's Rollup input name. */\nexport const BASE_STYLE_ENTRY = 'base';\nexport const pluginStyleEntryName = (slot: string): string => `plugin-${slot}`;\n\n/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */\nexport function writeWebPluginStyleEntries(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): WebPluginStyleEntries {\n const stylesDir = path.join(generatedDir, 'styles');\n fs.mkdirSync(stylesDir, { recursive: true });\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const baseSourcesPath = path.join(generatedDir, 'webPluginBaseSources.generated.css');\n fs.writeFileSync(\n baseSourcesPath,\n renderCssModule(plugins, [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ]),\n );\n // Resolvers follow real paths (a temp dir under /var is /private/var), so relate real paths.\n const appCss = relativeImport(\n fs.realpathSync(stylesDir),\n fs.realpathSync(path.join(packageRoot, 'src', 'web', 'styles', 'app.css')),\n );\n const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);\n fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n`);\n const pluginEntryPaths = new Map<string, string>();\n return { baseSourcesPath, baseEntryPath, pluginEntryPaths };\n}\n\n/**\n * The sync-time variant: renders the full client plugin set (host built-ins plus\n * the installed composition's packages) into a generation directory the bundler\n * aliases over the committed builtin modules.\n */\nexport function writeSyncWebPluginModules(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): SyncGeneratedModules {\n fs.mkdirSync(generatedDir, { recursive: true });\n const clientModulePath = path.join(generatedDir, 'webPlugins.generated.ts');\n const cssModulePath = path.join(generatedDir, 'webPluginSources.generated.css');\n fs.writeFileSync(clientModulePath, renderClientModule(plugins, 'absolute', packageRoot));\n // Tailwind styles compile separately as one shared sheet, so slot entries carry none.\n const slotsDir = path.join(generatedDir, 'slots');\n fs.mkdirSync(slotsDir, { recursive: true });\n const slotEntryPaths: Record<string, string> = {};\n for (const plugin of plugins) {\n const entryPath = path.join(slotsDir, `${plugin.slot}.ts`);\n const specifier = pathToFileURL(fs.realpathSync(path.join(plugin.packageDir, plugin.client.entry))).href;\n fs.writeFileSync(entryPath, `${HEADER}\\nexport { webPlugin } from '${specifier}';\\n`);\n slotEntryPaths[plugin.slot] = entryPath;\n }\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const leadingSourceRoots = [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ];\n fs.writeFileSync(cssModulePath, renderCssModule(plugins, leadingSourceRoots));\n const hostPlugins = plugins.filter((plugin) => plugin.isHost);\n const shell = {\n clientModulePath: path.join(generatedDir, 'webPluginShell.generated.ts'),\n cssModulePath: path.join(generatedDir, 'webPluginShellSources.generated.css'),\n };\n fs.writeFileSync(shell.clientModulePath, renderClientModule(hostPlugins, 'absolute', packageRoot));\n fs.writeFileSync(shell.cssModulePath, renderCssModule(hostPlugins, leadingSourceRoots));\n return {\n clientModulePath,\n slotEntryPaths,\n cssModulePath,\n shell,\n };\n}\n"],"mappings":"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"}
|
|
@@ -61,4 +61,5 @@ export declare function webPluginCssAlias(generated: WebPluginRegistryModules):
|
|
|
61
61
|
/** The plugin package roots the dev server should serve, from an explicit override or PO_ROOT. */
|
|
62
62
|
export declare function readDevPluginRoots(environment: NodeJS.ProcessEnv, homeDirectory: string): string[];
|
|
63
63
|
//#endregion
|
|
64
|
+
export {};
|
|
64
65
|
//# sourceMappingURL=webPluginVite.d.mts.map
|
package/dist/src/cli/cliApp.cjs
CHANGED
|
@@ -64,7 +64,7 @@ function createCliApplication(initialTelemetry) {
|
|
|
64
64
|
async function runHarness(options) {
|
|
65
65
|
const telemetry = await getTelemetry();
|
|
66
66
|
const [{ buildHarnessContext }, { HARNESS_EVENT }] = await Promise.all([Promise.resolve().then(() => require("../builders/cli/harnessContext.cjs")), import("@agimon-ai/po-core/runtimeLogSinkTelemetry")]);
|
|
67
|
-
return telemetry.runInSpan("
|
|
67
|
+
return telemetry.runInSpan("po_pi.run", {
|
|
68
68
|
"harness.major_mode": options.majorMode,
|
|
69
69
|
"harness.domain_count": options.domains.length,
|
|
70
70
|
"harness.agents": options.agents,
|