@agimon-ai/doompi-build 0.0.1-alpha.2 → 0.0.1-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.
Files changed (90) hide show
  1. package/dist/constants/contributions.cjs +1 -1
  2. package/dist/constants/contributions.cjs.map +1 -1
  3. package/dist/constants/contributions.mjs +1 -1
  4. package/dist/constants/contributions.mjs.map +1 -1
  5. package/dist/constants/layout.cjs +1 -1
  6. package/dist/constants/layout.cjs.map +1 -1
  7. package/dist/constants/layout.d.mts.map +1 -1
  8. package/dist/constants/layout.mjs +1 -1
  9. package/dist/constants/layout.mjs.map +1 -1
  10. package/dist/services/generate/index.cjs +1 -1
  11. package/dist/services/generate/index.cjs.map +1 -1
  12. package/dist/services/generate/index.d.mts.map +1 -1
  13. package/dist/services/generate/index.mjs +1 -1
  14. package/dist/services/generate/index.mjs.map +1 -1
  15. package/dist/services/generate/type.d.mts +2 -0
  16. package/dist/services/generate/type.d.mts.map +1 -1
  17. package/dist/services/renderClient/index.cjs +1 -1
  18. package/dist/services/renderClient/index.cjs.map +1 -1
  19. package/dist/services/renderClient/index.mjs +1 -1
  20. package/dist/services/renderClient/index.mjs.map +1 -1
  21. package/dist/services/renderEntry/index.cjs +4 -3
  22. package/dist/services/renderEntry/index.cjs.map +1 -1
  23. package/dist/services/renderEntry/index.d.mts.map +1 -1
  24. package/dist/services/renderEntry/index.mjs +4 -3
  25. package/dist/services/renderEntry/index.mjs.map +1 -1
  26. package/dist/services/renderEntry/type.d.mts +2 -0
  27. package/dist/services/renderEntry/type.d.mts.map +1 -1
  28. package/dist/services/renderMcpWidgets/index.cjs +3 -0
  29. package/dist/services/renderMcpWidgets/index.cjs.map +1 -0
  30. package/dist/services/renderMcpWidgets/index.mjs +3 -0
  31. package/dist/services/renderMcpWidgets/index.mjs.map +1 -0
  32. package/dist/services/resolveTarget/index.cjs +1 -1
  33. package/dist/services/resolveTarget/index.cjs.map +1 -1
  34. package/dist/services/resolveTarget/index.d.mts.map +1 -1
  35. package/dist/services/resolveTarget/index.mjs +1 -1
  36. package/dist/services/resolveTarget/index.mjs.map +1 -1
  37. package/dist/services/resolveTarget/type.d.mts +2 -2
  38. package/dist/services/scan/index.cjs +1 -1
  39. package/dist/services/scan/index.cjs.map +1 -1
  40. package/dist/services/scan/index.mjs +1 -1
  41. package/dist/services/scan/index.mjs.map +1 -1
  42. package/dist/services/syncManifest/index.cjs +1 -1
  43. package/dist/services/syncManifest/index.cjs.map +1 -1
  44. package/dist/services/syncManifest/index.d.mts.map +1 -1
  45. package/dist/services/syncManifest/index.mjs +1 -1
  46. package/dist/services/syncManifest/index.mjs.map +1 -1
  47. package/dist/services/tsdownPreset/index.cjs +1 -1
  48. package/dist/services/tsdownPreset/index.cjs.map +1 -1
  49. package/dist/services/tsdownPreset/index.d.mts +24 -5
  50. package/dist/services/tsdownPreset/index.d.mts.map +1 -1
  51. package/dist/services/tsdownPreset/index.mjs +1 -1
  52. package/dist/services/tsdownPreset/index.mjs.map +1 -1
  53. package/package.json +5 -5
  54. package/dist/browserFile.d.cts +0 -2
  55. package/dist/constants/layout.d.cts +0 -59
  56. package/dist/constants/layout.d.cts.map +0 -1
  57. package/dist/index.d.cts +0 -16
  58. package/dist/services/browserFile/index.d.cts +0 -28
  59. package/dist/services/browserFile/index.d.cts.map +0 -1
  60. package/dist/services/filename/index.d.cts +0 -29
  61. package/dist/services/filename/index.d.cts.map +0 -1
  62. package/dist/services/generate/index.d.cts +0 -15
  63. package/dist/services/generate/index.d.cts.map +0 -1
  64. package/dist/services/generate/type.d.cts +0 -26
  65. package/dist/services/generate/type.d.cts.map +0 -1
  66. package/dist/services/renderEntry/index.d.cts +0 -24
  67. package/dist/services/renderEntry/index.d.cts.map +0 -1
  68. package/dist/services/renderEntry/type.d.cts +0 -14
  69. package/dist/services/renderEntry/type.d.cts.map +0 -1
  70. package/dist/services/resolveTarget/index.d.cts +0 -16
  71. package/dist/services/resolveTarget/index.d.cts.map +0 -1
  72. package/dist/services/resolveTarget/type.d.cts +0 -34
  73. package/dist/services/resolveTarget/type.d.cts.map +0 -1
  74. package/dist/services/scan/index.d.cts +0 -19
  75. package/dist/services/scan/index.d.cts.map +0 -1
  76. package/dist/services/scan/type.d.cts +0 -25
  77. package/dist/services/scan/type.d.cts.map +0 -1
  78. package/dist/services/segments/index.d.cts +0 -26
  79. package/dist/services/segments/index.d.cts.map +0 -1
  80. package/dist/services/syncManifest/index.d.cts +0 -21
  81. package/dist/services/syncManifest/index.d.cts.map +0 -1
  82. package/dist/services/syncManifest/type.d.cts +0 -15
  83. package/dist/services/syncManifest/type.d.cts.map +0 -1
  84. package/dist/services/tsdownPreset/index.d.cts +0 -63
  85. package/dist/services/tsdownPreset/index.d.cts.map +0 -1
  86. package/dist/services/writeGenerated/index.d.cts +0 -23
  87. package/dist/services/writeGenerated/index.d.cts.map +0 -1
  88. package/dist/tsdown.d.cts +0 -2
  89. package/dist/types/extensionGraph.d.cts +0 -75
  90. package/dist/types/extensionGraph.d.cts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["path","fs","toKebab","GENERATED_DIR","generateExtension","writeManifest"],"sources":["../../../src/services/tsdownPreset/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GENERATED_DIR } from '../../constants/layout';\nimport { generateExtension } from '../generate';\nimport type { GenerateOptions } from '../generate/type';\nimport { toKebab } from '../identity';\nimport { writeManifest } from '../syncManifest';\n\ninterface EmittedFile {\n readonly type: 'asset' | 'chunk';\n readonly id?: string;\n readonly name: string;\n readonly source?: Buffer;\n}\n\ninterface QueryAssetPlugin {\n readonly name: string;\n resolveId(source: string, importer: string | undefined): string | undefined;\n load(this: { emitFile(file: EmittedFile): string }, id: string): string | undefined;\n}\n\n/** The shape a tsdown config needs from this preset, without importing tsdown's types. */\ninterface BrowserPresetConfig {\n entry: Record<string, string>;\n clean: boolean;\n dts: false;\n exports: boolean;\n format: 'esm'[];\n platform: 'browser';\n fixedExtension: boolean;\n external: (id: string) => boolean;\n plugins: QueryAssetPlugin[];\n sourcemap: boolean;\n unbundle: boolean;\n}\n\ninterface PresetConfig {\n entry: Record<string, string>;\n clean: boolean;\n dts: { incremental: boolean; parallel: boolean; eager: boolean };\n exports: boolean;\n format: ('esm' | 'cjs')[];\n platform: 'node';\n sourcemap: boolean;\n unbundle: boolean;\n hooks: { 'build:done': () => void };\n}\n\nconst DEFAULT_EXPORTS_DIR = 'src/exports';\n\n/** `src/exports/apiContracts.ts` becomes the `api-contracts` entry; index keeps its name. */\nfunction exportEntries(packageDir: string, exportsDir: string): Record<string, string> {\n const absolute = path.join(packageDir, exportsDir);\n if (!fs.existsSync(absolute)) return {};\n const entries: Record<string, string> = {};\n for (const file of fs.readdirSync(absolute)) {\n if (!file.endsWith('.ts') || file.endsWith('.d.ts')) continue;\n const stem = file.slice(0, -'.ts'.length);\n entries[stem === 'index' ? 'index' : toKebab(stem)] = `${exportsDir}/${file}`;\n }\n return entries;\n}\n\n/**\n * One tsdown config for a folder-routed extension.\n *\n * Entries come from the tree rather than a hand-written list, and the manifest\n * blocks that restate the tree are written after the build rather than kept in\n * step by hand. Browser contributions are emitted through a second bundled\n * config because their routed source is not published.\n */\nexport interface ExtensionPresetOptions extends GenerateOptions {\n /** Extra tsdown entries beyond the derived ones. */\n readonly entry?: Record<string, string>;\n /** Public export modules, package-relative. Defaults to `src/exports`. */\n readonly exportsDir?: string;\n}\n\n/**\n * Implements the Vite query imports extension browser code already uses.\n *\n * `?url` copies an opaque asset and `?worker&url` emits a separately bundled\n * worker chunk. Rolldown then replaces its file reference with an URL relative\n * to the extension bundle, which remains valid when the cockpit composes it.\n */\nfunction queryAssetPlugin(): QueryAssetPlugin {\n return {\n name: 'doompi-query-assets',\n resolveId(source, importer) {\n const queryAt = source.indexOf('?');\n if (queryAt < 0 || importer === undefined || !source.startsWith('.')) return undefined;\n const importerFile = importer.split('?')[0] as string;\n return `${path.resolve(path.dirname(importerFile), source.slice(0, queryAt))}${source.slice(queryAt)}`;\n },\n load(id) {\n const queryAt = id.indexOf('?');\n if (queryAt < 0) return undefined;\n const file = id.slice(0, queryAt);\n const query = new URLSearchParams(id.slice(queryAt + 1));\n if (!query.has('url')) return undefined;\n const worker = query.has('worker');\n const reference = this.emitFile(\n worker\n ? { type: 'chunk', id: file, name: path.basename(file, path.extname(file)) }\n : { type: 'asset', name: path.basename(file), source: fs.readFileSync(file) },\n );\n return `export default import.meta.ROLLUP_FILE_URL_${reference};`;\n },\n };\n}\n\n/** The separately bundled browser half of a routed extension. */\nfunction browserConfig(): BrowserPresetConfig {\n return {\n entry: { 'extensions/web': `${GENERATED_DIR}/web.ts` },\n clean: false,\n // No declarations. Nothing imports this as a typed module: it has no\n // export subpath, and the cockpit consumes it as a bundle. The browser\n // half is still typechecked, by tsconfig.web.json in the typecheck script.\n dts: false,\n exports: false,\n format: ['esm'],\n platform: 'browser',\n // platform browser turns tsdown's fixedExtension off, which would emit\n // .js. The manifest names .mjs like the other two entries, so ask for it.\n fixedExtension: true,\n // Every bare specifier stays external and the cockpit resolves it, which\n // is what keeps one React and one store in the page no matter how many\n // plugins are installed. An allowlist would have to be kept in step with\n // the cockpit's; this cannot drift.\n //\n // A predicate rather than a pattern: rolldown tests `external` against the\n // resolved id, so /^[^.]/ also matches the absolute path of this package's\n // own routed file and would leave a dangling import to unbuilt .tsx.\n external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id),\n plugins: [queryAssetPlugin()],\n sourcemap: true,\n // Bundled, unlike the node half. Unbundling would emit an import to the\n // routed source file, which is not published and is .tsx besides. One\n // module with only bare specifiers external is what the cockpit wants.\n unbundle: false,\n };\n}\n\nexport function doompiExtension(\n options: ExtensionPresetOptions = { packageDir: process.cwd() },\n): PresetConfig | (PresetConfig | BrowserPresetConfig)[] {\n const packageDir = options.packageDir ?? process.cwd();\n const result = generateExtension({ ...options, packageDir, check: options.check ?? false });\n\n for (const notice of result.notices) process.stderr.write(`[doompi-build] ${notice.path}: ${notice.message}\\n`);\n\n const entry: Record<string, string> = {\n ...exportEntries(packageDir, options.exportsDir ?? DEFAULT_EXPORTS_DIR),\n ...(result.targets.includes('cli') ? { 'extensions/pi': `${GENERATED_DIR}/pi.ts` } : {}),\n ...(result.targets.includes('server') ? { 'extensions/server': `${GENERATED_DIR}/server.ts` } : {}),\n ...options.entry,\n };\n\n const node: PresetConfig = {\n entry,\n clean: true,\n dts: { incremental: true, parallel: false, eager: true },\n exports: true,\n format: ['esm', 'cjs'],\n // No minify. This is a library build, and a mangled stack trace inside a\n // published extension is far more expensive than the bytes it saves.\n // Node output uses fixed extensions, so pi.extensions and doompiServer.dist\n // both land on the .mjs filenames their hosts require.\n platform: 'node',\n sourcemap: true,\n unbundle: true,\n hooks: {\n 'build:done': () =>\n void writeManifest({\n packageDir,\n manifest: JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8')) as Record<\n string,\n unknown\n >,\n graph: result.graph,\n targets: result.targets,\n pluginId: result.pluginId,\n }),\n },\n };\n\n return result.targets.includes('web') ? [node, browserConfig()] : node;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["path","fs","GENERATED_DIR","generateExtension","renderMcpWidgets"],"sources":["../../../src/services/tsdownPreset/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GENERATED_DIR } from '../../constants/layout';\nimport { generateExtension } from '../generate';\nimport type { GenerateOptions } from '../generate/type';\nimport { renderMcpWidgets } from '../renderMcpWidgets';\nimport { writeManifest, writeMcpManifest } from '../syncManifest';\n\ninterface EmittedFile {\n readonly type: 'asset' | 'chunk';\n readonly id?: string;\n readonly name: string;\n readonly source?: Buffer;\n}\n\ninterface QueryAssetPlugin {\n readonly name: string;\n resolveId(source: string, importer: string | undefined): string | undefined;\n load(this: { emitFile(file: EmittedFile): string }, id: string): string | undefined;\n}\n\n/** The shape a tsdown config needs from this preset, without importing tsdown's types. */\ninterface BrowserPresetConfig {\n entry: Record<string, string>;\n clean: boolean;\n dts: false;\n exports: boolean;\n format: 'esm'[];\n platform: 'browser';\n fixedExtension: boolean;\n external: (id: string) => boolean;\n plugins: QueryAssetPlugin[];\n sourcemap: boolean;\n unbundle: boolean;\n tsconfig: string;\n}\n\ninterface GeneratedExports {\n customExports: (exports: Record<string, unknown>) => Record<string, unknown>;\n}\n\ninterface BasePresetConfig {\n entry: Record<string, string>;\n clean: boolean;\n dts: false | { incremental: boolean; parallel: boolean; eager: boolean };\n write?: false;\n plugins?: QueryAssetPlugin[];\n exports: boolean | GeneratedExports;\n format: { esm: Record<string, never>; cjs: { dts: false } };\n platform: 'node';\n sourcemap: boolean;\n unbundle: boolean;\n outExtensions: ({ format }: { format: string }) => {\n js: string;\n dts: string;\n };\n}\n\ninterface PresetConfig extends BasePresetConfig {\n hooks?: { 'build:done': () => void };\n}\n\nconst DEFAULT_EXPORTS_DIR = 'src/exports';\nconst API_CONTRACTS_ENTRY = 'apiContracts';\n\n/** `src/exports/apiContracts.ts` becomes the `apiContracts` entry; index keeps its name. */\nfunction exportEntries(packageDir: string, exportsDir: string): Record<string, string> {\n const absolute = path.join(packageDir, exportsDir);\n if (!fs.existsSync(absolute)) return {};\n const entries: Record<string, string> = {};\n for (const file of fs.readdirSync(absolute)) {\n if (!file.endsWith('.ts') || file.endsWith('.d.ts')) continue;\n const stem = file.slice(0, -'.ts'.length);\n entries[stem] = `${exportsDir}/${file}`;\n }\n return entries;\n}\n\n/** Static resources receive package export entries without becoming tsdown inputs. */\nfunction resourceEntries(\n packageDir: string,\n directory: string,\n matches: (relative: string) => boolean,\n): Record<string, string> {\n const root = path.join(packageDir, directory);\n if (!fs.existsSync(root)) return {};\n const entries: Record<string, string> = {};\n for (const relative of fs.readdirSync(root, { encoding: 'utf8', recursive: true })) {\n if (!matches(relative)) continue;\n const normalized = relative.split(path.sep).join('/');\n entries[`./${directory}/${normalized}`] = `./${directory}/${normalized}`;\n }\n return entries;\n}\n\n/** Preserves declared resources and discovers default skill and theme resource exports. */\nfunction staticExports(packageDir: string): Record<string, unknown> {\n const manifest = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8')) as Record<\n string,\n unknown\n >;\n const declared =\n manifest.exports !== null && typeof manifest.exports === 'object' && !Array.isArray(manifest.exports)\n ? Object.fromEntries(Object.entries(manifest.exports).filter(([, target]) => typeof target === 'string'))\n : {};\n return {\n ...resourceEntries(packageDir, 'skills', (relative) => path.basename(relative) === 'SKILL.md'),\n ...resourceEntries(packageDir, 'themes', (relative) => relative.endsWith('.json')),\n ...declared,\n };\n}\n/**\n * One tsdown config for a folder-routed extension.\n *\n * Entries come from the tree rather than a hand-written list, and the manifest\n * blocks that restate the tree are written after the build rather than kept in\n * step by hand. Browser contributions are emitted through a second bundled\n * config because their routed source is not published.\n */\nexport interface ExtensionPresetOptions extends GenerateOptions {\n /** Extra tsdown entries beyond the derived ones. */\n readonly entry?: Record<string, string>;\n /** Public export modules, package-relative. Defaults to `src/exports`. */\n readonly exportsDir?: string;\n}\n\n/**\n * Implements the Vite query imports extension browser code already uses.\n *\n * `?url` copies an opaque asset and `?worker&url` emits a separately bundled\n * worker chunk. Rolldown then replaces its file reference with an URL relative\n * to the extension bundle, which remains valid when the cockpit composes it.\n */\nfunction queryAssetPlugin(): QueryAssetPlugin {\n return {\n name: 'doompi-query-assets',\n resolveId(source, importer) {\n const queryAt = source.indexOf('?');\n if (queryAt < 0 || importer === undefined || !source.startsWith('.')) return undefined;\n const importerFile = importer.split('?')[0] as string;\n return `${path.resolve(path.dirname(importerFile), source.slice(0, queryAt))}${source.slice(queryAt)}`;\n },\n load(id) {\n const queryAt = id.indexOf('?');\n if (queryAt < 0) return undefined;\n const file = id.slice(0, queryAt);\n const query = new URLSearchParams(id.slice(queryAt + 1));\n if (!query.has('url')) return undefined;\n const worker = query.has('worker');\n const reference = this.emitFile(\n worker\n ? { type: 'chunk', id: file, name: path.basename(file, path.extname(file)) }\n : { type: 'asset', name: path.basename(file), source: fs.readFileSync(file) },\n );\n return `export default import.meta.ROLLUP_FILE_URL_${reference};`;\n },\n };\n}\n\n/** The separately bundled browser half of a routed extension. */\nfunction browserConfig(target: 'web' | 'mcp-ui' = 'web'): BrowserPresetConfig {\n return {\n entry: { [`extensions/${target}`]: `${GENERATED_DIR}/${target}.ts` },\n clean: false,\n // No declarations. Nothing imports this as a typed module: it has no\n // export subpath, and the cockpit consumes it as a bundle. The browser\n // half is still typechecked, by tsconfig.web.json in the typecheck script.\n dts: false,\n exports: false,\n format: ['esm'],\n platform: 'browser',\n // platform browser turns tsdown's fixedExtension off, which would emit\n // .js. The manifest names .mjs like the other two entries, so ask for it.\n fixedExtension: true,\n // Every bare specifier stays external and the cockpit resolves it, which\n // is what keeps one React and one store in the page no matter how many\n // plugins are installed. An allowlist would have to be kept in step with\n // the cockpit's; this cannot drift.\n //\n // A predicate rather than a pattern: rolldown tests `external` against the\n // resolved id, so /^[^.]/ also matches the absolute path of this package's\n // own routed file and would leave a dangling import to unbuilt .tsx.\n external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id),\n plugins: [queryAssetPlugin()],\n sourcemap: true,\n // Bundled, unlike the node half. Unbundling would emit an import to the\n // routed source file, which is not published and is .tsx besides. One\n // module with only bare specifiers external is what the cockpit wants.\n unbundle: false,\n // Browser routes are excluded from the node tsconfig. Every browser-capable\n // extension owns this focused config for its matching typecheck.\n tsconfig: target === 'web' ? 'tsconfig.web.json' : 'tsconfig.mcp.json',\n };\n}\n\n/** Removes only filenames owned by the isolated MCP build, including obsolete declarations. */\nfunction cleanMcpOutput(packageDir: string): void {\n const dist = path.join(packageDir, 'dist');\n if (!fs.existsSync(dist)) return;\n for (const relative of fs.readdirSync(dist, { recursive: true, encoding: 'utf8' })) {\n const normalized = relative.split(path.sep).join('/');\n if (!/^(?:extensions\\/mcp(?:-ui)?|.+\\.mcp)\\.(?:mjs|cjs|d\\.mts|d\\.cts)(?:\\.map)?$/u.test(normalized)) continue;\n const absolute = path.join(dist, relative);\n if (fs.lstatSync(absolute).isFile()) fs.rmSync(absolute);\n }\n}\n\nexport function doompiExtension(\n options: ExtensionPresetOptions = { packageDir: process.cwd() },\n): PresetConfig | (PresetConfig | BrowserPresetConfig)[] {\n const packageDir = options.packageDir ?? process.cwd();\n const result = generateExtension({ ...options, packageDir, check: options.check ?? false });\n\n for (const notice of result.notices) process.stderr.write(`[doompi-build] ${notice.path}: ${notice.message}\\n`);\n\n const mcpOnly = options.target === 'mcp';\n if (mcpOnly && !options.check) cleanMcpOutput(packageDir);\n const emptyMcp = mcpOnly && !result.targets.includes('mcp');\n const emptyEntry = '\\0doompi-empty-mcp';\n if (emptyMcp && !options.check) writeMcpManifest(packageDir, false);\n const entry: Record<string, string> = mcpOnly\n ? result.targets.includes('mcp')\n ? { 'extensions/mcp': `${GENERATED_DIR}/mcp.ts` }\n : { 'extensions/mcp': emptyEntry }\n : {\n ...exportEntries(packageDir, options.exportsDir ?? DEFAULT_EXPORTS_DIR),\n ...(result.targets.includes('cli') ? { 'extensions/pi': `${GENERATED_DIR}/pi.ts` } : {}),\n ...(result.targets.includes('server') ? { 'extensions/server': `${GENERATED_DIR}/server.ts` } : {}),\n ...options.entry,\n };\n\n const baseNode: Omit<BasePresetConfig, 'entry' | 'unbundle'> = {\n // MCP shares dist/ with the normal build, so neither build may erase the other.\n clean: false,\n dts: emptyMcp ? false : { incremental: true, parallel: false, eager: true },\n // tsdown rejects both an empty entry and an empty config array. A virtual\n // input completes its normal lifecycle without publishing an empty facet.\n ...(emptyMcp\n ? {\n write: false as const,\n plugins: [\n {\n name: 'doompi-empty-mcp',\n resolveId: (source: string) => (source === emptyEntry ? emptyEntry : undefined),\n load: (id: string) => (id === emptyEntry ? 'export {};' : undefined),\n },\n ],\n }\n : {}),\n exports: mcpOnly\n ? false\n : {\n customExports: (generated) => ({ ...staticExports(packageDir), ...generated }),\n },\n // CommonJS keeps its JavaScript but emits no declarations. Every export map\n // resolves types through .d.mts, and a CJS declaration build would rerun\n // the same whole-program tsgo emit a second time.\n format: { esm: {}, cjs: { dts: false } },\n // No minify. This is a library build, and a mangled stack trace inside a\n // published extension is far more expensive than the bytes it saves.\n // Node output uses fixed extensions, so pi.extensions and doompiServer.dist\n // both land on the .mjs filenames their hosts require.\n platform: 'node',\n outExtensions: ({ format }) => ({\n js: format === 'es' ? '.mjs' : '.cjs',\n dts: format === 'es' ? '.d.mts' : '.d.cts',\n }),\n sourcemap: true,\n };\n\n // Contract bundles are published as a self-contained graph. Normal extension\n // entries remain unbundled for routed runtime resources, while MCP remains\n // isolated from the shared dist directory.\n const node = (entries: Record<string, string>, _unbundle: boolean): PresetConfig => ({\n ...baseNode,\n entry: entries,\n // Public entry facades must remain bundled. Otherwise their relative\n // imports target unbundled implementation chunks, which Vite treats as\n // source files when resolving a workspace package.\n unbundle: false,\n hooks: {\n 'build:done': () => {\n if (mcpOnly) {\n writeMcpManifest(\n packageDir,\n result.targets.includes('mcp'),\n renderMcpWidgets(result.graph, result.packageName, GENERATED_DIR).names,\n );\n return;\n }\n writeManifest({\n packageDir,\n manifest: JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8')) as Record<\n string,\n unknown\n >,\n graph: result.graph,\n targets: result.targets,\n pluginId: result.pluginId,\n });\n },\n },\n });\n const unsyncedNode = (entries: Record<string, string>, unbundle: boolean): PresetConfig => ({\n ...baseNode,\n entry: entries,\n unbundle,\n });\n const configs =\n !mcpOnly && API_CONTRACTS_ENTRY in entry\n ? [\n unsyncedNode({ [API_CONTRACTS_ENTRY]: entry[API_CONTRACTS_ENTRY] }, false),\n node(Object.fromEntries(Object.entries(entry).filter(([name]) => name !== API_CONTRACTS_ENTRY)), true),\n ]\n : [node(entry, !mcpOnly)];\n if (mcpOnly && result.files.has(`${GENERATED_DIR}/mcp-ui.ts`)) return [...configs, browserConfig('mcp-ui')];\n if (!mcpOnly && result.targets.includes('web')) return [...configs, browserConfig()];\n return configs.length === 1 ? configs[0]! : configs;\n}\n"],"mappings":"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"}
@@ -26,21 +26,40 @@ interface BrowserPresetConfig {
26
26
  plugins: QueryAssetPlugin[];
27
27
  sourcemap: boolean;
28
28
  unbundle: boolean;
29
+ tsconfig: string;
29
30
  }
30
- interface PresetConfig {
31
+ interface GeneratedExports {
32
+ customExports: (exports: Record<string, unknown>) => Record<string, unknown>;
33
+ }
34
+ interface BasePresetConfig {
31
35
  entry: Record<string, string>;
32
36
  clean: boolean;
33
- dts: {
37
+ dts: false | {
34
38
  incremental: boolean;
35
39
  parallel: boolean;
36
40
  eager: boolean;
37
41
  };
38
- exports: boolean;
39
- format: ('esm' | 'cjs')[];
42
+ write?: false;
43
+ plugins?: QueryAssetPlugin[];
44
+ exports: boolean | GeneratedExports;
45
+ format: {
46
+ esm: Record<string, never>;
47
+ cjs: {
48
+ dts: false;
49
+ };
50
+ };
40
51
  platform: 'node';
41
52
  sourcemap: boolean;
42
53
  unbundle: boolean;
43
- hooks: {
54
+ outExtensions: ({ format }: {
55
+ format: string;
56
+ }) => {
57
+ js: string;
58
+ dts: string;
59
+ };
60
+ }
61
+ interface PresetConfig extends BasePresetConfig {
62
+ hooks?: {
44
63
  'build:done': () => void;
45
64
  };
46
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/tsdownPreset/index.ts"],"mappings":";;UASU;WACC;WACA;WACA;WACA,SAAS;;UAGV;WACC;EACT,UAAU,gBAAgB;EAC1B,KAAK;IAAQ,SAAS,MAAM;KAAwB;;;UAI5C;EACR,OAAO;EACP;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX,SAAS;EACT;EACA;;UAGQ;EACR,OAAO;EACP;EACA;IAAO;IAAsB;IAAmB;;EAChD;EACA;EACA;EACA;EACA;EACA;IAAS;;;;;;;;;;;iBA0BM,+BAA+B;;WAErC,QAAQ;;WAER;;wBAqEK,gBACd,UAAS,yBACR,gBAAgB,eAAe"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/tsdownPreset/index.ts"],"mappings":";;UASU;WACC;WACA;WACA;WACA,SAAS;;UAGV;WACC;EACT,UAAU,gBAAgB;EAC1B,KAAK;IAAQ,SAAS,MAAM;KAAwB;;;UAI5C;EACR,OAAO;EACP;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX,SAAS;EACT;EACA;EACA;;UAGQ;EACR,gBAAgB,SAAS,4BAA4B;;UAG7C;EACR,OAAO;EACP;EACA;IAAe;IAAsB;IAAmB;;EACxD;EACA,UAAU;EACV,mBAAmB;EACnB;IAAU,KAAK;IAAuB;MAAO;;;EAC7C;EACA;EACA;EACA,kBAAkB;IAAY;;IAC5B;IACA;;;UAIM,qBAAqB;EAC7B;IAAU;;;;;;;;;;;iBA4DK,+BAA+B;;WAErC,QAAQ;;WAER;;wBAoFK,gBACd,UAAS,yBACR,gBAAgB,eAAe"}
@@ -1,2 +1,2 @@
1
- import{GENERATED_DIR as e}from"../../constants/layout.mjs";import{toKebab as t}from"../identity/index.mjs";import{generateExtension as n}from"../generate/index.mjs";import{writeManifest as r}from"../syncManifest/index.mjs";import i from"node:fs";import a from"node:path";function o(e,n){let r=a.join(e,n);if(!i.existsSync(r))return{};let o={};for(let e of i.readdirSync(r)){if(!e.endsWith(`.ts`)||e.endsWith(`.d.ts`))continue;let r=e.slice(0,-3);o[r===`index`?`index`:t(r)]=`${n}/${e}`}return o}function s(){return{name:`doompi-query-assets`,resolveId(e,t){let n=e.indexOf(`?`);if(n<0||t===void 0||!e.startsWith(`.`))return;let r=t.split(`?`)[0];return`${a.resolve(a.dirname(r),e.slice(0,n))}${e.slice(n)}`},load(e){let t=e.indexOf(`?`);if(t<0)return;let n=e.slice(0,t),r=new URLSearchParams(e.slice(t+1));if(!r.has(`url`))return;let o=r.has(`worker`);return`export default import.meta.ROLLUP_FILE_URL_${this.emitFile(o?{type:`chunk`,id:n,name:a.basename(n,a.extname(n))}:{type:`asset`,name:a.basename(n),source:i.readFileSync(n)})};`}}}function c(){return{entry:{"extensions/web":`${e}/web.ts`},clean:!1,dts:!1,exports:!1,format:[`esm`],platform:`browser`,fixedExtension:!0,external:e=>!e.startsWith(`.`)&&!a.isAbsolute(e),plugins:[s()],sourcemap:!0,unbundle:!1}}function l(t={packageDir:process.cwd()}){let s=t.packageDir??process.cwd(),l=n({...t,packageDir:s,check:t.check??!1});for(let e of l.notices)process.stderr.write(`[doompi-build] ${e.path}: ${e.message}\n`);let u={entry:{...o(s,t.exportsDir??`src/exports`),...l.targets.includes(`cli`)?{"extensions/pi":`${e}/pi.ts`}:{},...l.targets.includes(`server`)?{"extensions/server":`${e}/server.ts`}:{},...t.entry},clean:!0,dts:{incremental:!0,parallel:!1,eager:!0},exports:!0,format:[`esm`,`cjs`],platform:`node`,sourcemap:!0,unbundle:!0,hooks:{"build:done":()=>void r({packageDir:s,manifest:JSON.parse(i.readFileSync(a.join(s,`package.json`),`utf8`)),graph:l.graph,targets:l.targets,pluginId:l.pluginId})}};return l.targets.includes(`web`)?[u,c()]:u}export{l as doompiExtension};
1
+ import{GENERATED_DIR as e}from"../../constants/layout.mjs";import{renderMcpWidgets as t}from"../renderMcpWidgets/index.mjs";import{generateExtension as n}from"../generate/index.mjs";import{writeManifest as r,writeMcpManifest as i}from"../syncManifest/index.mjs";import a from"node:fs";import o from"node:path";const s=`apiContracts`;function c(e,t){let n=o.join(e,t);if(!a.existsSync(n))return{};let r={};for(let e of a.readdirSync(n)){if(!e.endsWith(`.ts`)||e.endsWith(`.d.ts`))continue;let n=e.slice(0,-3);r[n]=`${t}/${e}`}return r}function l(e,t,n){let r=o.join(e,t);if(!a.existsSync(r))return{};let i={};for(let e of a.readdirSync(r,{encoding:`utf8`,recursive:!0})){if(!n(e))continue;let r=e.split(o.sep).join(`/`);i[`./${t}/${r}`]=`./${t}/${r}`}return i}function u(e){let t=JSON.parse(a.readFileSync(o.join(e,`package.json`),`utf8`)),n=t.exports!==null&&typeof t.exports==`object`&&!Array.isArray(t.exports)?Object.fromEntries(Object.entries(t.exports).filter(([,e])=>typeof e==`string`)):{};return{...l(e,`skills`,e=>o.basename(e)===`SKILL.md`),...l(e,`themes`,e=>e.endsWith(`.json`)),...n}}function d(){return{name:`doompi-query-assets`,resolveId(e,t){let n=e.indexOf(`?`);if(n<0||t===void 0||!e.startsWith(`.`))return;let r=t.split(`?`)[0];return`${o.resolve(o.dirname(r),e.slice(0,n))}${e.slice(n)}`},load(e){let t=e.indexOf(`?`);if(t<0)return;let n=e.slice(0,t),r=new URLSearchParams(e.slice(t+1));if(!r.has(`url`))return;let i=r.has(`worker`);return`export default import.meta.ROLLUP_FILE_URL_${this.emitFile(i?{type:`chunk`,id:n,name:o.basename(n,o.extname(n))}:{type:`asset`,name:o.basename(n),source:a.readFileSync(n)})};`}}}function f(t=`web`){return{entry:{[`extensions/${t}`]:`${e}/${t}.ts`},clean:!1,dts:!1,exports:!1,format:[`esm`],platform:`browser`,fixedExtension:!0,external:e=>!e.startsWith(`.`)&&!o.isAbsolute(e),plugins:[d()],sourcemap:!0,unbundle:!1,tsconfig:t===`web`?`tsconfig.web.json`:`tsconfig.mcp.json`}}function p(e){let t=o.join(e,`dist`);if(a.existsSync(t))for(let e of a.readdirSync(t,{recursive:!0,encoding:`utf8`})){let n=e.split(o.sep).join(`/`);if(!/^(?:extensions\/mcp(?:-ui)?|.+\.mcp)\.(?:mjs|cjs|d\.mts|d\.cts)(?:\.map)?$/u.test(n))continue;let r=o.join(t,e);a.lstatSync(r).isFile()&&a.rmSync(r)}}function m(l={packageDir:process.cwd()}){let d=l.packageDir??process.cwd(),m=n({...l,packageDir:d,check:l.check??!1});for(let e of m.notices)process.stderr.write(`[doompi-build] ${e.path}: ${e.message}\n`);let h=l.target===`mcp`;h&&!l.check&&p(d);let g=h&&!m.targets.includes(`mcp`),_=`\0doompi-empty-mcp`;g&&!l.check&&i(d,!1);let v=h?m.targets.includes(`mcp`)?{"extensions/mcp":`${e}/mcp.ts`}:{"extensions/mcp":_}:{...c(d,l.exportsDir??`src/exports`),...m.targets.includes(`cli`)?{"extensions/pi":`${e}/pi.ts`}:{},...m.targets.includes(`server`)?{"extensions/server":`${e}/server.ts`}:{},...l.entry},y={clean:!1,dts:!g&&{incremental:!0,parallel:!1,eager:!0},...g?{write:!1,plugins:[{name:`doompi-empty-mcp`,resolveId:e=>e===_?_:void 0,load:e=>e===_?`export {};`:void 0}]}:{},exports:!h&&{customExports:e=>({...u(d),...e})},format:{esm:{},cjs:{dts:!1}},platform:`node`,outExtensions:({format:e})=>({js:e===`es`?`.mjs`:`.cjs`,dts:e===`es`?`.d.mts`:`.d.cts`}),sourcemap:!0},b=(n,s)=>({...y,entry:n,unbundle:!1,hooks:{"build:done":()=>{if(h){i(d,m.targets.includes(`mcp`),t(m.graph,m.packageName,e).names);return}r({packageDir:d,manifest:JSON.parse(a.readFileSync(o.join(d,`package.json`),`utf8`)),graph:m.graph,targets:m.targets,pluginId:m.pluginId})}}}),x=!h&&s in v?[((e,t)=>({...y,entry:e,unbundle:t}))({[s]:v[s]},!1),b(Object.fromEntries(Object.entries(v).filter(([e])=>e!==s)),!0)]:[b(v,!h)];return h&&m.files.has(`generated/mcp-ui.ts`)?[...x,f(`mcp-ui`)]:!h&&m.targets.includes(`web`)?[...x,f()]:x.length===1?x[0]:x}export{m as doompiExtension};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/services/tsdownPreset/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GENERATED_DIR } from '../../constants/layout';\nimport { generateExtension } from '../generate';\nimport type { GenerateOptions } from '../generate/type';\nimport { toKebab } from '../identity';\nimport { writeManifest } from '../syncManifest';\n\ninterface EmittedFile {\n readonly type: 'asset' | 'chunk';\n readonly id?: string;\n readonly name: string;\n readonly source?: Buffer;\n}\n\ninterface QueryAssetPlugin {\n readonly name: string;\n resolveId(source: string, importer: string | undefined): string | undefined;\n load(this: { emitFile(file: EmittedFile): string }, id: string): string | undefined;\n}\n\n/** The shape a tsdown config needs from this preset, without importing tsdown's types. */\ninterface BrowserPresetConfig {\n entry: Record<string, string>;\n clean: boolean;\n dts: false;\n exports: boolean;\n format: 'esm'[];\n platform: 'browser';\n fixedExtension: boolean;\n external: (id: string) => boolean;\n plugins: QueryAssetPlugin[];\n sourcemap: boolean;\n unbundle: boolean;\n}\n\ninterface PresetConfig {\n entry: Record<string, string>;\n clean: boolean;\n dts: { incremental: boolean; parallel: boolean; eager: boolean };\n exports: boolean;\n format: ('esm' | 'cjs')[];\n platform: 'node';\n sourcemap: boolean;\n unbundle: boolean;\n hooks: { 'build:done': () => void };\n}\n\nconst DEFAULT_EXPORTS_DIR = 'src/exports';\n\n/** `src/exports/apiContracts.ts` becomes the `api-contracts` entry; index keeps its name. */\nfunction exportEntries(packageDir: string, exportsDir: string): Record<string, string> {\n const absolute = path.join(packageDir, exportsDir);\n if (!fs.existsSync(absolute)) return {};\n const entries: Record<string, string> = {};\n for (const file of fs.readdirSync(absolute)) {\n if (!file.endsWith('.ts') || file.endsWith('.d.ts')) continue;\n const stem = file.slice(0, -'.ts'.length);\n entries[stem === 'index' ? 'index' : toKebab(stem)] = `${exportsDir}/${file}`;\n }\n return entries;\n}\n\n/**\n * One tsdown config for a folder-routed extension.\n *\n * Entries come from the tree rather than a hand-written list, and the manifest\n * blocks that restate the tree are written after the build rather than kept in\n * step by hand. Browser contributions are emitted through a second bundled\n * config because their routed source is not published.\n */\nexport interface ExtensionPresetOptions extends GenerateOptions {\n /** Extra tsdown entries beyond the derived ones. */\n readonly entry?: Record<string, string>;\n /** Public export modules, package-relative. Defaults to `src/exports`. */\n readonly exportsDir?: string;\n}\n\n/**\n * Implements the Vite query imports extension browser code already uses.\n *\n * `?url` copies an opaque asset and `?worker&url` emits a separately bundled\n * worker chunk. Rolldown then replaces its file reference with an URL relative\n * to the extension bundle, which remains valid when the cockpit composes it.\n */\nfunction queryAssetPlugin(): QueryAssetPlugin {\n return {\n name: 'doompi-query-assets',\n resolveId(source, importer) {\n const queryAt = source.indexOf('?');\n if (queryAt < 0 || importer === undefined || !source.startsWith('.')) return undefined;\n const importerFile = importer.split('?')[0] as string;\n return `${path.resolve(path.dirname(importerFile), source.slice(0, queryAt))}${source.slice(queryAt)}`;\n },\n load(id) {\n const queryAt = id.indexOf('?');\n if (queryAt < 0) return undefined;\n const file = id.slice(0, queryAt);\n const query = new URLSearchParams(id.slice(queryAt + 1));\n if (!query.has('url')) return undefined;\n const worker = query.has('worker');\n const reference = this.emitFile(\n worker\n ? { type: 'chunk', id: file, name: path.basename(file, path.extname(file)) }\n : { type: 'asset', name: path.basename(file), source: fs.readFileSync(file) },\n );\n return `export default import.meta.ROLLUP_FILE_URL_${reference};`;\n },\n };\n}\n\n/** The separately bundled browser half of a routed extension. */\nfunction browserConfig(): BrowserPresetConfig {\n return {\n entry: { 'extensions/web': `${GENERATED_DIR}/web.ts` },\n clean: false,\n // No declarations. Nothing imports this as a typed module: it has no\n // export subpath, and the cockpit consumes it as a bundle. The browser\n // half is still typechecked, by tsconfig.web.json in the typecheck script.\n dts: false,\n exports: false,\n format: ['esm'],\n platform: 'browser',\n // platform browser turns tsdown's fixedExtension off, which would emit\n // .js. The manifest names .mjs like the other two entries, so ask for it.\n fixedExtension: true,\n // Every bare specifier stays external and the cockpit resolves it, which\n // is what keeps one React and one store in the page no matter how many\n // plugins are installed. An allowlist would have to be kept in step with\n // the cockpit's; this cannot drift.\n //\n // A predicate rather than a pattern: rolldown tests `external` against the\n // resolved id, so /^[^.]/ also matches the absolute path of this package's\n // own routed file and would leave a dangling import to unbuilt .tsx.\n external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id),\n plugins: [queryAssetPlugin()],\n sourcemap: true,\n // Bundled, unlike the node half. Unbundling would emit an import to the\n // routed source file, which is not published and is .tsx besides. One\n // module with only bare specifiers external is what the cockpit wants.\n unbundle: false,\n };\n}\n\nexport function doompiExtension(\n options: ExtensionPresetOptions = { packageDir: process.cwd() },\n): PresetConfig | (PresetConfig | BrowserPresetConfig)[] {\n const packageDir = options.packageDir ?? process.cwd();\n const result = generateExtension({ ...options, packageDir, check: options.check ?? false });\n\n for (const notice of result.notices) process.stderr.write(`[doompi-build] ${notice.path}: ${notice.message}\\n`);\n\n const entry: Record<string, string> = {\n ...exportEntries(packageDir, options.exportsDir ?? DEFAULT_EXPORTS_DIR),\n ...(result.targets.includes('cli') ? { 'extensions/pi': `${GENERATED_DIR}/pi.ts` } : {}),\n ...(result.targets.includes('server') ? { 'extensions/server': `${GENERATED_DIR}/server.ts` } : {}),\n ...options.entry,\n };\n\n const node: PresetConfig = {\n entry,\n clean: true,\n dts: { incremental: true, parallel: false, eager: true },\n exports: true,\n format: ['esm', 'cjs'],\n // No minify. This is a library build, and a mangled stack trace inside a\n // published extension is far more expensive than the bytes it saves.\n // Node output uses fixed extensions, so pi.extensions and doompiServer.dist\n // both land on the .mjs filenames their hosts require.\n platform: 'node',\n sourcemap: true,\n unbundle: true,\n hooks: {\n 'build:done': () =>\n void writeManifest({\n packageDir,\n manifest: JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8')) as Record<\n string,\n unknown\n >,\n graph: result.graph,\n targets: result.targets,\n pluginId: result.pluginId,\n }),\n },\n };\n\n return result.targets.includes('web') ? [node, browserConfig()] : node;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/services/tsdownPreset/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GENERATED_DIR } from '../../constants/layout';\nimport { generateExtension } from '../generate';\nimport type { GenerateOptions } from '../generate/type';\nimport { renderMcpWidgets } from '../renderMcpWidgets';\nimport { writeManifest, writeMcpManifest } from '../syncManifest';\n\ninterface EmittedFile {\n readonly type: 'asset' | 'chunk';\n readonly id?: string;\n readonly name: string;\n readonly source?: Buffer;\n}\n\ninterface QueryAssetPlugin {\n readonly name: string;\n resolveId(source: string, importer: string | undefined): string | undefined;\n load(this: { emitFile(file: EmittedFile): string }, id: string): string | undefined;\n}\n\n/** The shape a tsdown config needs from this preset, without importing tsdown's types. */\ninterface BrowserPresetConfig {\n entry: Record<string, string>;\n clean: boolean;\n dts: false;\n exports: boolean;\n format: 'esm'[];\n platform: 'browser';\n fixedExtension: boolean;\n external: (id: string) => boolean;\n plugins: QueryAssetPlugin[];\n sourcemap: boolean;\n unbundle: boolean;\n tsconfig: string;\n}\n\ninterface GeneratedExports {\n customExports: (exports: Record<string, unknown>) => Record<string, unknown>;\n}\n\ninterface BasePresetConfig {\n entry: Record<string, string>;\n clean: boolean;\n dts: false | { incremental: boolean; parallel: boolean; eager: boolean };\n write?: false;\n plugins?: QueryAssetPlugin[];\n exports: boolean | GeneratedExports;\n format: { esm: Record<string, never>; cjs: { dts: false } };\n platform: 'node';\n sourcemap: boolean;\n unbundle: boolean;\n outExtensions: ({ format }: { format: string }) => {\n js: string;\n dts: string;\n };\n}\n\ninterface PresetConfig extends BasePresetConfig {\n hooks?: { 'build:done': () => void };\n}\n\nconst DEFAULT_EXPORTS_DIR = 'src/exports';\nconst API_CONTRACTS_ENTRY = 'apiContracts';\n\n/** `src/exports/apiContracts.ts` becomes the `apiContracts` entry; index keeps its name. */\nfunction exportEntries(packageDir: string, exportsDir: string): Record<string, string> {\n const absolute = path.join(packageDir, exportsDir);\n if (!fs.existsSync(absolute)) return {};\n const entries: Record<string, string> = {};\n for (const file of fs.readdirSync(absolute)) {\n if (!file.endsWith('.ts') || file.endsWith('.d.ts')) continue;\n const stem = file.slice(0, -'.ts'.length);\n entries[stem] = `${exportsDir}/${file}`;\n }\n return entries;\n}\n\n/** Static resources receive package export entries without becoming tsdown inputs. */\nfunction resourceEntries(\n packageDir: string,\n directory: string,\n matches: (relative: string) => boolean,\n): Record<string, string> {\n const root = path.join(packageDir, directory);\n if (!fs.existsSync(root)) return {};\n const entries: Record<string, string> = {};\n for (const relative of fs.readdirSync(root, { encoding: 'utf8', recursive: true })) {\n if (!matches(relative)) continue;\n const normalized = relative.split(path.sep).join('/');\n entries[`./${directory}/${normalized}`] = `./${directory}/${normalized}`;\n }\n return entries;\n}\n\n/** Preserves declared resources and discovers default skill and theme resource exports. */\nfunction staticExports(packageDir: string): Record<string, unknown> {\n const manifest = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8')) as Record<\n string,\n unknown\n >;\n const declared =\n manifest.exports !== null && typeof manifest.exports === 'object' && !Array.isArray(manifest.exports)\n ? Object.fromEntries(Object.entries(manifest.exports).filter(([, target]) => typeof target === 'string'))\n : {};\n return {\n ...resourceEntries(packageDir, 'skills', (relative) => path.basename(relative) === 'SKILL.md'),\n ...resourceEntries(packageDir, 'themes', (relative) => relative.endsWith('.json')),\n ...declared,\n };\n}\n/**\n * One tsdown config for a folder-routed extension.\n *\n * Entries come from the tree rather than a hand-written list, and the manifest\n * blocks that restate the tree are written after the build rather than kept in\n * step by hand. Browser contributions are emitted through a second bundled\n * config because their routed source is not published.\n */\nexport interface ExtensionPresetOptions extends GenerateOptions {\n /** Extra tsdown entries beyond the derived ones. */\n readonly entry?: Record<string, string>;\n /** Public export modules, package-relative. Defaults to `src/exports`. */\n readonly exportsDir?: string;\n}\n\n/**\n * Implements the Vite query imports extension browser code already uses.\n *\n * `?url` copies an opaque asset and `?worker&url` emits a separately bundled\n * worker chunk. Rolldown then replaces its file reference with an URL relative\n * to the extension bundle, which remains valid when the cockpit composes it.\n */\nfunction queryAssetPlugin(): QueryAssetPlugin {\n return {\n name: 'doompi-query-assets',\n resolveId(source, importer) {\n const queryAt = source.indexOf('?');\n if (queryAt < 0 || importer === undefined || !source.startsWith('.')) return undefined;\n const importerFile = importer.split('?')[0] as string;\n return `${path.resolve(path.dirname(importerFile), source.slice(0, queryAt))}${source.slice(queryAt)}`;\n },\n load(id) {\n const queryAt = id.indexOf('?');\n if (queryAt < 0) return undefined;\n const file = id.slice(0, queryAt);\n const query = new URLSearchParams(id.slice(queryAt + 1));\n if (!query.has('url')) return undefined;\n const worker = query.has('worker');\n const reference = this.emitFile(\n worker\n ? { type: 'chunk', id: file, name: path.basename(file, path.extname(file)) }\n : { type: 'asset', name: path.basename(file), source: fs.readFileSync(file) },\n );\n return `export default import.meta.ROLLUP_FILE_URL_${reference};`;\n },\n };\n}\n\n/** The separately bundled browser half of a routed extension. */\nfunction browserConfig(target: 'web' | 'mcp-ui' = 'web'): BrowserPresetConfig {\n return {\n entry: { [`extensions/${target}`]: `${GENERATED_DIR}/${target}.ts` },\n clean: false,\n // No declarations. Nothing imports this as a typed module: it has no\n // export subpath, and the cockpit consumes it as a bundle. The browser\n // half is still typechecked, by tsconfig.web.json in the typecheck script.\n dts: false,\n exports: false,\n format: ['esm'],\n platform: 'browser',\n // platform browser turns tsdown's fixedExtension off, which would emit\n // .js. The manifest names .mjs like the other two entries, so ask for it.\n fixedExtension: true,\n // Every bare specifier stays external and the cockpit resolves it, which\n // is what keeps one React and one store in the page no matter how many\n // plugins are installed. An allowlist would have to be kept in step with\n // the cockpit's; this cannot drift.\n //\n // A predicate rather than a pattern: rolldown tests `external` against the\n // resolved id, so /^[^.]/ also matches the absolute path of this package's\n // own routed file and would leave a dangling import to unbuilt .tsx.\n external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id),\n plugins: [queryAssetPlugin()],\n sourcemap: true,\n // Bundled, unlike the node half. Unbundling would emit an import to the\n // routed source file, which is not published and is .tsx besides. One\n // module with only bare specifiers external is what the cockpit wants.\n unbundle: false,\n // Browser routes are excluded from the node tsconfig. Every browser-capable\n // extension owns this focused config for its matching typecheck.\n tsconfig: target === 'web' ? 'tsconfig.web.json' : 'tsconfig.mcp.json',\n };\n}\n\n/** Removes only filenames owned by the isolated MCP build, including obsolete declarations. */\nfunction cleanMcpOutput(packageDir: string): void {\n const dist = path.join(packageDir, 'dist');\n if (!fs.existsSync(dist)) return;\n for (const relative of fs.readdirSync(dist, { recursive: true, encoding: 'utf8' })) {\n const normalized = relative.split(path.sep).join('/');\n if (!/^(?:extensions\\/mcp(?:-ui)?|.+\\.mcp)\\.(?:mjs|cjs|d\\.mts|d\\.cts)(?:\\.map)?$/u.test(normalized)) continue;\n const absolute = path.join(dist, relative);\n if (fs.lstatSync(absolute).isFile()) fs.rmSync(absolute);\n }\n}\n\nexport function doompiExtension(\n options: ExtensionPresetOptions = { packageDir: process.cwd() },\n): PresetConfig | (PresetConfig | BrowserPresetConfig)[] {\n const packageDir = options.packageDir ?? process.cwd();\n const result = generateExtension({ ...options, packageDir, check: options.check ?? false });\n\n for (const notice of result.notices) process.stderr.write(`[doompi-build] ${notice.path}: ${notice.message}\\n`);\n\n const mcpOnly = options.target === 'mcp';\n if (mcpOnly && !options.check) cleanMcpOutput(packageDir);\n const emptyMcp = mcpOnly && !result.targets.includes('mcp');\n const emptyEntry = '\\0doompi-empty-mcp';\n if (emptyMcp && !options.check) writeMcpManifest(packageDir, false);\n const entry: Record<string, string> = mcpOnly\n ? result.targets.includes('mcp')\n ? { 'extensions/mcp': `${GENERATED_DIR}/mcp.ts` }\n : { 'extensions/mcp': emptyEntry }\n : {\n ...exportEntries(packageDir, options.exportsDir ?? DEFAULT_EXPORTS_DIR),\n ...(result.targets.includes('cli') ? { 'extensions/pi': `${GENERATED_DIR}/pi.ts` } : {}),\n ...(result.targets.includes('server') ? { 'extensions/server': `${GENERATED_DIR}/server.ts` } : {}),\n ...options.entry,\n };\n\n const baseNode: Omit<BasePresetConfig, 'entry' | 'unbundle'> = {\n // MCP shares dist/ with the normal build, so neither build may erase the other.\n clean: false,\n dts: emptyMcp ? false : { incremental: true, parallel: false, eager: true },\n // tsdown rejects both an empty entry and an empty config array. A virtual\n // input completes its normal lifecycle without publishing an empty facet.\n ...(emptyMcp\n ? {\n write: false as const,\n plugins: [\n {\n name: 'doompi-empty-mcp',\n resolveId: (source: string) => (source === emptyEntry ? emptyEntry : undefined),\n load: (id: string) => (id === emptyEntry ? 'export {};' : undefined),\n },\n ],\n }\n : {}),\n exports: mcpOnly\n ? false\n : {\n customExports: (generated) => ({ ...staticExports(packageDir), ...generated }),\n },\n // CommonJS keeps its JavaScript but emits no declarations. Every export map\n // resolves types through .d.mts, and a CJS declaration build would rerun\n // the same whole-program tsgo emit a second time.\n format: { esm: {}, cjs: { dts: false } },\n // No minify. This is a library build, and a mangled stack trace inside a\n // published extension is far more expensive than the bytes it saves.\n // Node output uses fixed extensions, so pi.extensions and doompiServer.dist\n // both land on the .mjs filenames their hosts require.\n platform: 'node',\n outExtensions: ({ format }) => ({\n js: format === 'es' ? '.mjs' : '.cjs',\n dts: format === 'es' ? '.d.mts' : '.d.cts',\n }),\n sourcemap: true,\n };\n\n // Contract bundles are published as a self-contained graph. Normal extension\n // entries remain unbundled for routed runtime resources, while MCP remains\n // isolated from the shared dist directory.\n const node = (entries: Record<string, string>, _unbundle: boolean): PresetConfig => ({\n ...baseNode,\n entry: entries,\n // Public entry facades must remain bundled. Otherwise their relative\n // imports target unbundled implementation chunks, which Vite treats as\n // source files when resolving a workspace package.\n unbundle: false,\n hooks: {\n 'build:done': () => {\n if (mcpOnly) {\n writeMcpManifest(\n packageDir,\n result.targets.includes('mcp'),\n renderMcpWidgets(result.graph, result.packageName, GENERATED_DIR).names,\n );\n return;\n }\n writeManifest({\n packageDir,\n manifest: JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8')) as Record<\n string,\n unknown\n >,\n graph: result.graph,\n targets: result.targets,\n pluginId: result.pluginId,\n });\n },\n },\n });\n const unsyncedNode = (entries: Record<string, string>, unbundle: boolean): PresetConfig => ({\n ...baseNode,\n entry: entries,\n unbundle,\n });\n const configs =\n !mcpOnly && API_CONTRACTS_ENTRY in entry\n ? [\n unsyncedNode({ [API_CONTRACTS_ENTRY]: entry[API_CONTRACTS_ENTRY] }, false),\n node(Object.fromEntries(Object.entries(entry).filter(([name]) => name !== API_CONTRACTS_ENTRY)), true),\n ]\n : [node(entry, !mcpOnly)];\n if (mcpOnly && result.files.has(`${GENERATED_DIR}/mcp-ui.ts`)) return [...configs, browserConfig('mcp-ui')];\n if (!mcpOnly && result.targets.includes('web')) return [...configs, browserConfig()];\n return configs.length === 1 ? configs[0]! : configs;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-build",
3
- "version": "0.0.1-alpha.2",
3
+ "version": "0.0.1-alpha.21",
4
4
  "description": "Folder-convention scanner and build presets for DoomPi extensions.",
5
5
  "keywords": [
6
6
  "ai",
@@ -37,7 +37,7 @@
37
37
  "import": "./dist/index.mjs",
38
38
  "require": "./dist/index.cjs"
39
39
  },
40
- "./browser-file": {
40
+ "./browserFile": {
41
41
  "types": "./dist/browserFile.d.mts",
42
42
  "import": "./dist/browserFile.mjs",
43
43
  "require": "./dist/browserFile.cjs"
@@ -53,11 +53,11 @@
53
53
  "access": "public"
54
54
  },
55
55
  "devDependencies": {
56
- "@types/node": "26.5.1",
57
- "@vitest/coverage-v8": "5.0.0",
56
+ "@types/node": "26.6.2",
57
+ "@vitest/coverage-v8": "5.0.1",
58
58
  "tsdown": "0.23.0",
59
59
  "typescript": "7.0.2",
60
- "vitest": "5.0.0"
60
+ "vitest": "5.0.1"
61
61
  },
62
62
  "engines": {
63
63
  "node": ">=22.19.0"
@@ -1,2 +0,0 @@
1
- import { isBrowserFile } from "./services/browserFile/index.cjs";
2
- export { isBrowserFile };
@@ -1,59 +0,0 @@
1
- //#region src/constants/layout.d.ts
2
- /**
3
- * The folder-convention vocabulary, in one place.
4
- *
5
- * Every name here is part of the authoring contract in
6
- * docs/extension-layout.md. Adding a surface means adding it to both.
7
- */
8
- /** Where the scan starts, package-relative. */
9
- export declare const DEFAULT_ROUTING_ROOT = "src/extensions";
10
- /** Accepted spelling of the same root, for packages that prefer the singular. */
11
- export declare const ROUTING_ROOT_ALIAS = "src/extension";
12
- /** Gate folders. Each is followed by exactly one id segment. */
13
- export declare const GATE_SEGMENTS: readonly string[];
14
- /** The leaf filename of an HTTP route, as Next.js spells it. */
15
- export declare const ROUTE_FILE_NAME = "route";
16
- /**
17
- * Where generated entries are written, package-relative.
18
- *
19
- * Not `src`. That directory is authored code, and a build that writes into it
20
- * makes every package carry output its author did not write and must not edit.
21
- * This one is gitignored per package, and the repository's formatter already
22
- * skips a directory by this name.
23
- */
24
- export declare const GENERATED_DIR = "generated";
25
- /**
26
- * Platform suffixes, scoped to their side. A target may not reuse one of these words.
27
- *
28
- * Every public routed file names one of these, so which host reads it is read
29
- * off the filename rather than inferred. A file that names none is a notice.
30
- *
31
- * `cli` appears on both sides, because the interactive host has both. Its
32
- * backend files hold a tool's `execute` and its services; its frontend files
33
- * hold the TUI that draws them. A terminal is a frontend that happens not to
34
- * be a browser, which is why the side axis is logic against presentation
35
- * rather than Node against browser.
36
- */
37
- export declare const BACKEND_PLATFORMS: readonly string[];
38
- export declare const FRONTEND_PLATFORMS: readonly string[];
39
- /**
40
- * Surfaces that produce contributions on the backend side.
41
- *
42
- * `mode` is absent on purpose: it is a gate folder, so it is consumed before
43
- * the surface check and its declaration file synthesises the surface instead.
44
- */
45
- export declare const BACKEND_SURFACES: readonly string[];
46
- /**
47
- * Surfaces that produce contributions on the frontend side.
48
- *
49
- * `message` is terminal-only today: the cockpit renders a timeline entry
50
- * through a tool renderer or a fill, while the TUI has a separate registry
51
- * keyed by custom message type.
52
- */
53
- export declare const FRONTEND_SURFACES: readonly string[];
54
- /** Surfaces whose filename carries a relationship target. */
55
- export declare const TARGETED_SURFACES: readonly string[];
56
- /** Surfaces whose folders below the surface spell a route path. */
57
- export declare const ROUTED_SURFACES: readonly string[];
58
- //#endregion
59
- //# sourceMappingURL=layout.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"layout.d.cts","names":[],"sources":["../../src/constants/layout.ts"],"mappings":";;;;;;;;qBAQa;;qBAEA;;qBAWA;;qBAGA;;;;;;;;;qBAoBA;;;;;;;;;;;;;qBAoDA;qBACA;;;;;;;qBAWA;;;;;;;;qBAoBA;;qBAwBA;;qBAGA"}
package/dist/index.d.cts DELETED
@@ -1,16 +0,0 @@
1
- import { ExtensionEntry, ExtensionGate, ExtensionGraph, ExtensionNotice, ExtensionScope, ExtensionSide, RouteParam, RouteSegment } from "./types/extensionGraph.cjs";
2
- import { Segment, classifySegment } from "./services/segments/index.cjs";
3
- import { ParsedFilename, parseFilename } from "./services/filename/index.cjs";
4
- import { ScanOptions } from "./services/scan/type.cjs";
5
- import { resolveRoutingRoot, scanExtensions } from "./services/scan/index.cjs";
6
- import { BuildTarget, ResolvedContribution, TargetResolution } from "./services/resolveTarget/type.cjs";
7
- import { resolveTarget } from "./services/resolveTarget/index.cjs";
8
- import { BACKEND_PLATFORMS, BACKEND_SURFACES, DEFAULT_ROUTING_ROOT, FRONTEND_PLATFORMS, FRONTEND_SURFACES, GATE_SEGMENTS, GENERATED_DIR, ROUTED_SURFACES, ROUTE_FILE_NAME, ROUTING_ROOT_ALIAS, TARGETED_SURFACES } from "./constants/layout.cjs";
9
- import { GenerateOptions, GenerateResult } from "./services/generate/type.cjs";
10
- import { defaultPluginId, generateExtension } from "./services/generate/index.cjs";
11
- import { RenderOptions } from "./services/renderEntry/type.cjs";
12
- import { renderCliEntry, renderServerEntry, renderWebEntry } from "./services/renderEntry/index.cjs";
13
- import { StaleGeneratedError, WriteResult, writeGenerated } from "./services/writeGenerated/index.cjs";
14
- import { ManifestSync } from "./services/syncManifest/type.cjs";
15
- import { syncManifest, writeManifest } from "./services/syncManifest/index.cjs";
16
- export { BACKEND_PLATFORMS, BACKEND_SURFACES, type BuildTarget, DEFAULT_ROUTING_ROOT, type ExtensionEntry, type ExtensionGate, type ExtensionGraph, type ExtensionNotice, type ExtensionScope, type ExtensionSide, FRONTEND_PLATFORMS, FRONTEND_SURFACES, GATE_SEGMENTS, GENERATED_DIR, type GenerateOptions, type GenerateResult, type ManifestSync, type ParsedFilename, ROUTED_SURFACES, ROUTE_FILE_NAME, ROUTING_ROOT_ALIAS, type RenderOptions, type ResolvedContribution, type RouteParam, type RouteSegment, type ScanOptions, type Segment, StaleGeneratedError, TARGETED_SURFACES, type TargetResolution, type WriteResult, classifySegment, defaultPluginId, generateExtension, parseFilename, renderCliEntry, renderServerEntry, renderWebEntry, resolveRoutingRoot, resolveTarget, scanExtensions, syncManifest, writeGenerated, writeManifest };
@@ -1,28 +0,0 @@
1
- //#region src/services/browserFile/index.d.ts
2
- /**
3
- * Whether a package-relative path holds code the cockpit bundles.
4
- *
5
- * Browser-bound is narrower than `(frontend)`. That side carries both
6
- * presentations, and the terminal's half legitimately imports src/services and
7
- * pi-tui, so treating the whole side as browser would reject every one of those
8
- * imports. Two things separate the halves, in this order:
9
- *
10
- * - a surface the CLI frontend table names and the web table does not is
11
- * terminal-only, whatever the filename says;
12
- * - on a surface both hosts share, the platform in the filename decides, which
13
- * is what tells `tool/x.cli.tsx` from `tool/x.web.tsx`.
14
- *
15
- * Everything else under `(frontend)` is browser, including the private folders
16
- * colocated beside a surface, and a private folder at the side root, which
17
- * names no surface to read.
18
- *
19
- * That default is why a private terminal helper carries `.cli` even though the
20
- * scan never routes it: on a shared surface such as `tool/`, the folder says
21
- * nothing about which host draws the file, so `_lib/bashRender.cli.ts` is the
22
- * only thing distinguishing a TUI renderer from a cockpit one sitting beside it.
23
- *
24
- * Path in, answer out: nothing here reads the file or the filesystem.
25
- */
26
- export declare function isBrowserFile(relativePath: string): boolean;
27
- //#endregion
28
- //# sourceMappingURL=index.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/browserFile/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;wBA0DgB,cAAc"}
@@ -1,29 +0,0 @@
1
- //#region src/services/filename/index.d.ts
2
- /** One filename split into the four things the grammar encodes. */
3
- export interface ParsedFilename {
4
- /** The contribution's identity. */
5
- readonly name: string;
6
- /** The relationship target, such as the slot a fill names. */
7
- readonly target: string | undefined;
8
- /** The platform this file serves, or undefined when the filename names none. */
9
- readonly platform: string | undefined;
10
- readonly extension: string;
11
- }
12
- /**
13
- * Parses `{name}[.{target}][.{platform}].{ext}`.
14
- *
15
- * Right to left, because the platform set is closed and the target is not: a
16
- * dotted slot name such as `task.detail` would otherwise be ambiguous. A file
17
- * whose extension is not a source extension, or that carries an excluded
18
- * infix, returns undefined rather than an entry the caller has to filter.
19
- *
20
- * The one word a target may not be is a platform name for its own side, since
21
- * the last segment is read as a platform whenever it matches.
22
- *
23
- * A filename that names no platform still parses, with `platform: undefined`.
24
- * Requiring one is the scan's job, since only the scan knows whether the file
25
- * is public: a private `_lib` helper and an `api/` route sibling name none.
26
- */
27
- export declare function parseFilename(fileName: string, platforms: readonly string[]): ParsedFilename | undefined;
28
- //#endregion
29
- //# sourceMappingURL=index.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/filename/index.ts"],"mappings":";;iBAGiB;;WAEN;;WAEA;;WAEA;WACA;;;;;;;;;;;;;;;;;wBAkBK,cAAc,kBAAkB,+BAA+B"}
@@ -1,15 +0,0 @@
1
- import { GenerateOptions, GenerateResult } from "./type.cjs";
2
- //#region src/services/generate/index.d.ts
3
- /** `@agimon-ai/doompi-plan` becomes `plan`. */
4
- export declare function defaultPluginId(packageName: string): string;
5
- /**
6
- * Scans a package and renders every entry its tree calls for.
7
- *
8
- * A target with nothing in it produces no file, so a backend-only package
9
- * never grows an empty cockpit entry. Notices from the scan and from each
10
- * resolution are gathered rather than thrown: an unrecognised folder costs
11
- * that folder, never the build.
12
- */
13
- export declare function generateExtension(options: GenerateOptions): GenerateResult;
14
- //#endregion
15
- //# sourceMappingURL=index.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/generate/index.ts"],"mappings":";;;wBAqBgB,gBAAgB;;;;;;;;;wBA6BhB,kBAAkB,SAAS,kBAAkB"}
@@ -1,26 +0,0 @@
1
- import { ExtensionGraph, ExtensionNotice } from "../../types/extensionGraph.cjs";
2
- import { ScanOptions } from "../scan/type.cjs";
3
- import { BuildTarget } from "../resolveTarget/type.cjs";
4
- //#region src/services/generate/type.d.ts
5
- export interface GenerateOptions extends ScanOptions {
6
- /** Defaults to the name in the package's own package.json. */
7
- readonly packageName?: string;
8
- /** Defaults to the package name without its scope and `doompi-` prefix. */
9
- readonly pluginId?: string;
10
- /** Throw on stale output instead of writing it. */
11
- readonly check?: boolean;
12
- }
13
- export interface GenerateResult {
14
- readonly graph: ExtensionGraph;
15
- readonly packageName: string;
16
- readonly pluginId: string;
17
- /** Package-relative path to rendered source, for every entry the tree calls for. */
18
- readonly files: ReadonlyMap<string, string>;
19
- /** Package-relative paths whose contents changed. */
20
- readonly changed: readonly string[];
21
- readonly notices: readonly ExtensionNotice[];
22
- /** The hosts this package contributes to. */
23
- readonly targets: readonly BuildTarget[];
24
- }
25
- //#endregion
26
- //# sourceMappingURL=type.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"type.d.cts","names":[],"sources":["../../../src/services/generate/type.ts"],"mappings":";;;;iBAIiB,wBAAwB;;WAE9B;;WAEA;;WAEA;;iBAGM;WACN,OAAO;WACP;WACA;;WAEA,OAAO;;WAEP;WACA,kBAAkB;;WAElB,kBAAkB"}
@@ -1,24 +0,0 @@
1
- import { TargetResolution } from "../resolveTarget/type.cjs";
2
- import { RenderOptions } from "./type.cjs";
3
- //#region src/services/renderEntry/index.d.ts
4
- /** The interactive terminal entry. One mount, so every backend contribution lands in it. */
5
- export declare function renderCliEntry(resolution: TargetResolution, options: RenderOptions): string;
6
- /**
7
- * The headless entry, one declaration per scope.
8
- *
9
- * Contributions cascade downward here because the server does not do it
10
- * itself: a facet reads exactly one `plugin[host.scope]` and nothing from the
11
- * scopes above it. Emitting the repetition is what lets an author declare a
12
- * channel once and have it mount at all three scopes.
13
- */
14
- export declare function renderServerEntry(resolution: TargetResolution, options: RenderOptions): string;
15
- /**
16
- * The cockpit entry, one contributions object per scope.
17
- *
18
- * No cascade here, unlike the server: the registry already folds global into
19
- * workspace into session before install, so repeating a contribution would
20
- * install it twice.
21
- */
22
- export declare function renderWebEntry(resolution: TargetResolution, options: RenderOptions): string;
23
- //#endregion
24
- //# sourceMappingURL=index.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/renderEntry/index.ts"],"mappings":";;;;wBA2jBgB,eAAe,YAAY,kBAAkB,SAAS;;;;;;;;;wBAmDtD,kBAAkB,YAAY,kBAAkB,SAAS;;;;;;;;wBA6DzD,eAAe,YAAY,kBAAkB,SAAS"}
@@ -1,14 +0,0 @@
1
- //#region src/services/renderEntry/type.d.ts
2
- /** What the renderers need beyond the resolved contributions themselves. */
3
- export interface RenderOptions {
4
- /** The package's npm name, used as the extension and facet name. */
5
- readonly packageName: string;
6
- /** The cockpit plugin id. */
7
- readonly pluginId: string;
8
- /** Routing root, package-relative. */
9
- readonly root: string;
10
- /** Where the generated entry is written, package-relative, so its imports can be made relative to it. */
11
- readonly entryDir: string;
12
- }
13
- //#endregion
14
- //# sourceMappingURL=type.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"type.d.cts","names":[],"sources":["../../../src/services/renderEntry/type.ts"],"mappings":";;iBACiB;;WAEN;;WAEA;;WAEA;;WAEA"}
@@ -1,16 +0,0 @@
1
- import { ExtensionGraph } from "../../types/extensionGraph.cjs";
2
- import { BuildTarget, TargetResolution } from "./type.cjs";
3
- //#region src/services/resolveTarget/index.d.ts
4
- /**
5
- * Picks the winning file per logical contribution for one host.
6
- *
7
- * Metro and Expo semantics: a file naming this platform replaces a sibling
8
- * that names none, and a file naming none serves every platform on its side
9
- * that has no more specific sibling. The scan reports the unnamed one, so this
10
- * is a fallback rather than a supported way to author. Two files naming the
11
- * same platform for the same contribution is an authoring mistake, so the
12
- * first wins and the second becomes a notice rather than silently disappearing.
13
- */
14
- export declare function resolveTarget(graph: ExtensionGraph, target: BuildTarget): TargetResolution;
15
- //#endregion
16
- //# sourceMappingURL=index.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/resolveTarget/index.ts"],"mappings":";;;;;;;;;;;;;wBAsHgB,cAAc,OAAO,gBAAgB,QAAQ,cAAc"}
@@ -1,34 +0,0 @@
1
- import { ExtensionEntry, ExtensionNotice } from "../../types/extensionGraph.cjs";
2
- //#region src/services/resolveTarget/type.d.ts
3
- /** The three hosts an entry can be generated for. */
4
- export type BuildTarget = 'cli' | 'server' | 'web';
5
- /** One file and the host contribution array it belongs in. */
6
- export interface ResolvedContribution {
7
- /** The winning file for this logical contribution on this host. */
8
- readonly entry: ExtensionEntry;
9
- /** The host contribution array this belongs in. */
10
- readonly field: string;
11
- /**
12
- * A presentation file folded into this contribution rather than emitted
13
- * beside it. Only the terminal has one: its tool renderers are fields of the
14
- * tool declaration, so the pair becomes a single registration.
15
- */
16
- readonly renderers?: ExtensionEntry;
17
- }
18
- /** Everything one host needs generated, plus anything skipped on the way. */
19
- export interface TargetResolution {
20
- readonly target: BuildTarget;
21
- readonly contributions: readonly ResolvedContribution[];
22
- /** Escape-hatch files whose raw contributions are merged in wholesale. */
23
- readonly escapeHatches: readonly ExtensionEntry[];
24
- /**
25
- * Scope constructors, outermost first.
26
- *
27
- * Each one runs before the contributions beneath it and its value joins
28
- * their mount context, so the order is the nesting order and nothing else.
29
- */
30
- readonly roots: readonly ExtensionEntry[];
31
- readonly notices: readonly ExtensionNotice[];
32
- }
33
- //#endregion
34
- //# sourceMappingURL=type.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"type.d.cts","names":[],"sources":["../../../src/services/resolveTarget/type.ts"],"mappings":";;;YAGY;;iBAGK;;WAEN,OAAO;;WAEP;;;;;;WAMA,YAAY;;;iBAIN;WACN,QAAQ;WACR,wBAAwB;;WAExB,wBAAwB;;;;;;;WAOxB,gBAAgB;WAChB,kBAAkB"}
@@ -1,19 +0,0 @@
1
- import { ExtensionGraph } from "../../types/extensionGraph.cjs";
2
- import { ScanOptions } from "./type.cjs";
3
- //#region src/services/scan/index.d.ts
4
- /** Resolves the routing root, accepting the singular alias when only it exists. */
5
- export declare function resolveRoutingRoot(packageDir: string, root: string | undefined): string | undefined;
6
- /**
7
- * Reads one package's routing root into a description of what it declares.
8
- *
9
- * Filesystem in, data out. No globbing, which is what keeps parenthesised
10
- * group folders safe: tsdown's globber would read a bare `(name)` as an
11
- * extglob group, and a directory walk never sees a pattern at all.
12
- *
13
- * Nothing here throws. A folder the convention does not recognise becomes a
14
- * notice and its subtree is skipped, so one bad directory never costs a
15
- * package its other contributions.
16
- */
17
- export declare function scanExtensions(options: ScanOptions): ExtensionGraph;
18
- //#endregion
19
- //# sourceMappingURL=index.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/scan/index.ts"],"mappings":";;;;wBA+EgB,mBAAmB,oBAAoB;;;;;;;;;;;;wBAgBvC,eAAe,SAAS,cAAc"}
@@ -1,25 +0,0 @@
1
- //#region src/services/scan/type.d.ts
2
- /** What the scan needs to know before it walks a package. */
3
- export interface ScanOptions {
4
- /** Absolute path of the package being scanned. */
5
- readonly packageDir: string;
6
- /**
7
- * Routing root, package-relative. Defaults to `src/extensions`, falling back
8
- * to `src/extension` when only that one exists.
9
- */
10
- readonly root?: string;
11
- /** Group folder names that select a build side. */
12
- readonly sides?: {
13
- readonly backend?: string;
14
- readonly frontend?: string;
15
- };
16
- /** Recognised platform suffixes per side. */
17
- readonly platforms?: {
18
- readonly backend?: readonly string[];
19
- readonly frontend?: readonly string[];
20
- };
21
- /** Base names of the generated entries at the root, which the scan skips. */
22
- readonly generatedEntries?: readonly string[];
23
- }
24
- //#endregion
25
- //# sourceMappingURL=type.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"type.d.cts","names":[],"sources":["../../../src/services/scan/type.ts"],"mappings":";;iBACiB;;WAEN;;;;;WAKA;;WAEA;aAAmB;aAA2B;;;WAE9C;aAAuB;aAAsC;;;WAE7D"}