@agimon-ai/po-build 0.0.0-alpha.0

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 (115) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +96 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/browserFile.cjs +1 -0
  5. package/dist/browserFile.d.mts +2 -0
  6. package/dist/browserFile.mjs +1 -0
  7. package/dist/constants/contributions.cjs +2 -0
  8. package/dist/constants/contributions.cjs.map +1 -0
  9. package/dist/constants/contributions.mjs +2 -0
  10. package/dist/constants/contributions.mjs.map +1 -0
  11. package/dist/constants/layout.cjs +2 -0
  12. package/dist/constants/layout.cjs.map +1 -0
  13. package/dist/constants/layout.d.mts +56 -0
  14. package/dist/constants/layout.d.mts.map +1 -0
  15. package/dist/constants/layout.mjs +2 -0
  16. package/dist/constants/layout.mjs.map +1 -0
  17. package/dist/index.cjs +1 -0
  18. package/dist/index.d.mts +17 -0
  19. package/dist/index.mjs +1 -0
  20. package/dist/services/browserFile/index.cjs +2 -0
  21. package/dist/services/browserFile/index.cjs.map +1 -0
  22. package/dist/services/browserFile/index.d.mts +22 -0
  23. package/dist/services/browserFile/index.d.mts.map +1 -0
  24. package/dist/services/browserFile/index.mjs +2 -0
  25. package/dist/services/browserFile/index.mjs.map +1 -0
  26. package/dist/services/filename/index.cjs +2 -0
  27. package/dist/services/filename/index.cjs.map +1 -0
  28. package/dist/services/filename/index.d.mts +29 -0
  29. package/dist/services/filename/index.d.mts.map +1 -0
  30. package/dist/services/filename/index.mjs +2 -0
  31. package/dist/services/filename/index.mjs.map +1 -0
  32. package/dist/services/generate/index.cjs +3 -0
  33. package/dist/services/generate/index.cjs.map +1 -0
  34. package/dist/services/generate/index.d.mts +15 -0
  35. package/dist/services/generate/index.d.mts.map +1 -0
  36. package/dist/services/generate/index.mjs +3 -0
  37. package/dist/services/generate/index.mjs.map +1 -0
  38. package/dist/services/generate/type.d.mts +28 -0
  39. package/dist/services/generate/type.d.mts.map +1 -0
  40. package/dist/services/identity/index.cjs +2 -0
  41. package/dist/services/identity/index.cjs.map +1 -0
  42. package/dist/services/identity/index.mjs +2 -0
  43. package/dist/services/identity/index.mjs.map +1 -0
  44. package/dist/services/renderClient/index.cjs +3 -0
  45. package/dist/services/renderClient/index.cjs.map +1 -0
  46. package/dist/services/renderClient/index.mjs +3 -0
  47. package/dist/services/renderClient/index.mjs.map +1 -0
  48. package/dist/services/renderEntry/index.cjs +7 -0
  49. package/dist/services/renderEntry/index.cjs.map +1 -0
  50. package/dist/services/renderEntry/index.d.mts +18 -0
  51. package/dist/services/renderEntry/index.d.mts.map +1 -0
  52. package/dist/services/renderEntry/index.mjs +7 -0
  53. package/dist/services/renderEntry/index.mjs.map +1 -0
  54. package/dist/services/renderEntry/type.d.mts +16 -0
  55. package/dist/services/renderEntry/type.d.mts.map +1 -0
  56. package/dist/services/renderMcpWidgets/index.cjs +3 -0
  57. package/dist/services/renderMcpWidgets/index.cjs.map +1 -0
  58. package/dist/services/renderMcpWidgets/index.mjs +3 -0
  59. package/dist/services/renderMcpWidgets/index.mjs.map +1 -0
  60. package/dist/services/resolveTarget/index.cjs +2 -0
  61. package/dist/services/resolveTarget/index.cjs.map +1 -0
  62. package/dist/services/resolveTarget/index.d.mts +18 -0
  63. package/dist/services/resolveTarget/index.d.mts.map +1 -0
  64. package/dist/services/resolveTarget/index.mjs +2 -0
  65. package/dist/services/resolveTarget/index.mjs.map +1 -0
  66. package/dist/services/resolveTarget/type.d.mts +30 -0
  67. package/dist/services/resolveTarget/type.d.mts.map +1 -0
  68. package/dist/services/scan/index.cjs +2 -0
  69. package/dist/services/scan/index.cjs.map +1 -0
  70. package/dist/services/scan/index.d.mts +8 -0
  71. package/dist/services/scan/index.d.mts.map +1 -0
  72. package/dist/services/scan/index.mjs +2 -0
  73. package/dist/services/scan/index.mjs.map +1 -0
  74. package/dist/services/scan/type.d.mts +25 -0
  75. package/dist/services/scan/type.d.mts.map +1 -0
  76. package/dist/services/segments/index.cjs +2 -0
  77. package/dist/services/segments/index.cjs.map +1 -0
  78. package/dist/services/segments/index.d.mts +26 -0
  79. package/dist/services/segments/index.d.mts.map +1 -0
  80. package/dist/services/segments/index.mjs +2 -0
  81. package/dist/services/segments/index.mjs.map +1 -0
  82. package/dist/services/syncManifest/index.cjs +2 -0
  83. package/dist/services/syncManifest/index.cjs.map +1 -0
  84. package/dist/services/syncManifest/index.d.mts +21 -0
  85. package/dist/services/syncManifest/index.d.mts.map +1 -0
  86. package/dist/services/syncManifest/index.mjs +2 -0
  87. package/dist/services/syncManifest/index.mjs.map +1 -0
  88. package/dist/services/syncManifest/type.d.mts +15 -0
  89. package/dist/services/syncManifest/type.d.mts.map +1 -0
  90. package/dist/services/tsdownPreset/index.cjs +2 -0
  91. package/dist/services/tsdownPreset/index.cjs.map +1 -0
  92. package/dist/services/tsdownPreset/index.d.mts +89 -0
  93. package/dist/services/tsdownPreset/index.d.mts.map +1 -0
  94. package/dist/services/tsdownPreset/index.mjs +2 -0
  95. package/dist/services/tsdownPreset/index.mjs.map +1 -0
  96. package/dist/services/unistylesBabel/index.cjs +2 -0
  97. package/dist/services/unistylesBabel/index.cjs.map +1 -0
  98. package/dist/services/unistylesBabel/index.d.mts +17 -0
  99. package/dist/services/unistylesBabel/index.d.mts.map +1 -0
  100. package/dist/services/unistylesBabel/index.mjs +2 -0
  101. package/dist/services/unistylesBabel/index.mjs.map +1 -0
  102. package/dist/services/unistylesBabel/type.d.mts +13 -0
  103. package/dist/services/unistylesBabel/type.d.mts.map +1 -0
  104. package/dist/services/writeGenerated/index.cjs +2 -0
  105. package/dist/services/writeGenerated/index.cjs.map +1 -0
  106. package/dist/services/writeGenerated/index.d.mts +23 -0
  107. package/dist/services/writeGenerated/index.d.mts.map +1 -0
  108. package/dist/services/writeGenerated/index.mjs +2 -0
  109. package/dist/services/writeGenerated/index.mjs.map +1 -0
  110. package/dist/tsdown.cjs +1 -0
  111. package/dist/tsdown.d.mts +3 -0
  112. package/dist/tsdown.mjs +1 -0
  113. package/dist/types/extensionGraph.d.mts +75 -0
  114. package/dist/types/extensionGraph.d.mts.map +1 -0
  115. package/package.json +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["BACKEND_SURFACES","FRONTEND_SURFACES","BACKEND_PLATFORMS","FRONTEND_PLATFORMS","DEFAULT_ROUTING_ROOT","ROUTING_ROOT_ALIAS","fs","path","GENERATED_ENTRY_NAMES","classifySegment","ROUTED_SURFACES","GATE_SEGMENTS","parseFilename","ROOT_FILE_NAME","require_runtime","require_layout","require_index","require_index$1","node_fs","node_path","INITIAL","surfacesFor","side","platformsFor","options","nearestSurface","surfaces","name","singular","routedCardinality","source","resolveRoutingRoot","root","packageDir","candidate","RETIRED_PLATFORM_NOTICE","scanExtensions","backendGroup","frontendGroup","generated","entries","notices","note","relativePath","notePlatform","parsed","relative","walk","absolute","relativeDir","listing","a","b","item","walkDirectory","state","readFile","segment","gate","nearest","base","fileName","platforms","cardinality"],"sources":["../../../src/services/scan/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n BACKEND_GROUP,\n BACKEND_PLATFORMS,\n BACKEND_SURFACES,\n DEFAULT_ROUTING_ROOT,\n FRONTEND_GROUP,\n FRONTEND_PLATFORMS,\n FRONTEND_SURFACES,\n GATE_SEGMENTS,\n GENERATED_ENTRY_NAMES,\n RETIRED_PLATFORM,\n ROOT_FILE_NAME,\n ROUTE_FILE_NAME,\n ROUTED_SURFACES,\n ROUTING_ROOT_ALIAS,\n SESSION_SEGMENT,\n WORKSPACE_SEGMENT,\n} from '../../constants/layout';\nimport type {\n ExtensionEntry,\n ExtensionGate,\n ExtensionGraph,\n ExtensionNotice,\n ExtensionScope,\n ExtensionSide,\n RouteSegment,\n} from '../../types/extensionGraph';\nimport { type ParsedFilename, parseFilename } from '../filename';\nimport { classifySegment } from '../segments';\nimport type { ScanOptions } from './type';\n\ninterface WalkState {\n readonly scope: ExtensionScope;\n readonly side: ExtensionSide | undefined;\n readonly gates: readonly ExtensionGate[];\n readonly surface: string | undefined;\n readonly route: readonly RouteSegment[];\n /** A gate folder has been entered and the next plain segment names its id. */\n readonly pendingGate: string | undefined;\n}\n\nconst INITIAL: WalkState = {\n scope: 'global',\n side: undefined,\n gates: [],\n surface: undefined,\n route: [],\n pendingGate: undefined,\n};\n\nfunction surfacesFor(side: ExtensionSide): readonly string[] {\n return side === 'backend' ? BACKEND_SURFACES : FRONTEND_SURFACES;\n}\n\nfunction platformsFor(side: ExtensionSide, options: ScanOptions): readonly string[] {\n const configured = side === 'backend' ? options.platforms?.backend : options.platforms?.frontend;\n return configured ?? (side === 'backend' ? BACKEND_PLATFORMS : FRONTEND_PLATFORMS);\n}\n\n/** The nearest surface a misspelled folder probably meant, or undefined. */\nfunction nearestSurface(name: string, side: ExtensionSide): string | undefined {\n const surfaces = surfacesFor(side);\n if (surfaces.includes(name)) return undefined;\n const singular = name.endsWith('s') ? name.slice(0, -1) : `${name}s`;\n return surfaces.includes(singular) ? singular : undefined;\n}\n\n/** Reads cardinality only from the routed helper's options argument. */\nfunction routedCardinality(source: string): ExtensionEntry['cardinality'] | undefined {\n const declaration =\n /export\\s+default\\s+defineRoutedContribution\\s*\\([\\s\\S]*,\\s*\\{\\s*cardinality\\s*:\\s*['\"](optional|many|collection)['\"]/u.exec(\n source,\n );\n return declaration?.[1] as ExtensionEntry['cardinality'] | undefined;\n}\n\n/** Resolves the routing root, accepting the singular alias when only it exists. */\nexport function resolveRoutingRoot(packageDir: string, root: string | undefined): string | undefined {\n const candidates = root !== undefined ? [root] : [DEFAULT_ROUTING_ROOT, ROUTING_ROOT_ALIAS];\n return candidates.find((candidate) => fs.existsSync(path.join(packageDir, candidate)));\n}\n\n/**\n * Reads one package's routing root into a description of what it declares.\n *\n * Filesystem in, data out. No globbing, which is what keeps parenthesised\n * group folders safe: tsdown's globber would read a bare `(name)` as an\n * extglob group, and a directory walk never sees a pattern at all.\n *\n * Nothing here throws. A folder the convention does not recognise becomes a\n * notice and its subtree is skipped, so one bad directory never costs a\n * package its other contributions.\n */\n/** Reported for a routed file that names the retired terminal platform; generate refuses it. */\nexport const RETIRED_PLATFORM_NOTICE = `the .${RETIRED_PLATFORM} platform is retired: Po has no terminal host, so move this file to .server or delete it`;\n\nexport function scanExtensions(options: ScanOptions): ExtensionGraph {\n const root = resolveRoutingRoot(options.packageDir, options.root);\n if (root === undefined) return { root: options.root ?? DEFAULT_ROUTING_ROOT, entries: [], notices: [] };\n\n const backendGroup = options.sides?.backend ?? BACKEND_GROUP;\n const frontendGroup = options.sides?.frontend ?? FRONTEND_GROUP;\n const generated = options.generatedEntries ?? GENERATED_ENTRY_NAMES;\n\n const entries: ExtensionEntry[] = [];\n const notices: ExtensionNotice[] = [];\n const note = (relativePath: string, message: string): void => void notices.push({ path: relativePath, message });\n\n /**\n * Every public routed file names its platform, so a host is never inferred.\n *\n * Checked at each push rather than once at the top of readFile, because a\n * file sitting somewhere the convention does not define has a position\n * problem, and reporting the missing suffix instead would hide it.\n */\n const notePlatform = (relative: string, parsed: ParsedFilename, side: ExtensionSide): void => {\n if (parsed.platform !== undefined) return;\n note(\n relative,\n `names no platform; add one of ${platformsFor(side, options).join(', ')} before .${parsed.extension} (without one the host is inferred from the side rather than declared)`,\n );\n };\n\n const walk = (relativeDir: string, state: WalkState): void => {\n const absolute = path.join(options.packageDir, relativeDir);\n const listing = fs.readdirSync(absolute, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));\n\n for (const item of listing) {\n const relative = `${relativeDir}/${item.name}`;\n if (item.isDirectory()) walkDirectory(relative, item.name, state);\n else if (item.isFile()) readFile(relative, item.name, state);\n }\n };\n\n const walkDirectory = (relative: string, name: string, state: WalkState): void => {\n const segment = classifySegment(name);\n\n if (segment.kind === 'private') return;\n\n if (segment.kind === 'group') {\n if (state.side === undefined && segment.name === backendGroup)\n return walk(relative, { ...state, side: 'backend' });\n if (state.side === undefined && segment.name === frontendGroup)\n return walk(relative, { ...state, side: 'frontend' });\n return walk(relative, state);\n }\n\n if (segment.kind === 'dynamic') {\n if (state.surface !== undefined && ROUTED_SURFACES.includes(state.surface)) {\n return walk(relative, { ...state, route: [...state.route, { param: segment.param }] });\n }\n return note(relative, 'a dynamic segment is only meaningful inside a routed surface such as api/');\n }\n\n if (state.pendingGate !== undefined) {\n const gate: ExtensionGate = { kind: state.pendingGate, id: segment.name };\n return walk(relative, { ...state, gates: [...state.gates, gate], pendingGate: undefined });\n }\n\n if (state.surface !== undefined) {\n if (ROUTED_SURFACES.includes(state.surface)) {\n return walk(relative, { ...state, route: [...state.route, { literal: segment.name }] });\n }\n return note(relative, `${state.surface}/ takes files directly; put helpers in a _private folder`);\n }\n\n if (state.side === undefined) {\n if (segment.name === WORKSPACE_SEGMENT && state.scope === 'global') {\n return walk(relative, { ...state, scope: 'workspace' });\n }\n if (segment.name === SESSION_SEGMENT && state.scope === 'workspace') {\n return walk(relative, { ...state, scope: 'session' });\n }\n return note(relative, `expected a scope segment or a side group, such as (${backendGroup})`);\n }\n\n if (GATE_SEGMENTS.includes(segment.name)) return walk(relative, { ...state, pendingGate: segment.name });\n\n if (surfacesFor(state.side).includes(segment.name)) return walk(relative, { ...state, surface: segment.name });\n\n const nearest = nearestSurface(segment.name, state.side);\n return note(\n relative,\n nearest === undefined\n ? `'${segment.name}' is not a ${state.side} surface`\n : `'${segment.name}' is not a surface, did you mean '${nearest}'`,\n );\n };\n\n const readFile = (relative: string, fileName: string, state: WalkState): void => {\n if (state.side === undefined) {\n const base = fileName.slice(0, fileName.lastIndexOf('.'));\n if (state.scope === 'global' && generated.includes(base)) return;\n // A direct child of the routing root is a host entry, not a\n // contribution: either one this build generates, or one the package\n // hand-writes and names itself. A package that has not adopted the\n // layout has all of its native host entries here. A contribution\n // always sits at least one directory deep, inside a side group.\n if (state.scope === 'global') return;\n if (parseFilename(fileName, []) === undefined) return;\n return note(relative, 'a contribution needs a side group and a surface folder');\n }\n\n const platforms = platformsFor(state.side, options);\n const parsed = parseFilename(fileName, [...platforms, 'mcp', RETIRED_PLATFORM]);\n if (parsed === undefined) return;\n if (parsed.platform === RETIRED_PLATFORM) return note(relative, RETIRED_PLATFORM_NOTICE);\n if (\n parsed.platform === 'mcp' &&\n (state.scope !== 'session' ||\n (state.side === 'frontend'\n ? state.surface !== 'tool' || !fileName.endsWith('.mcp.tsx')\n : state.surface !== 'tool' && state.surface !== 'skill' && state.surface !== 'resource'))\n ) {\n return note(\n relative,\n 'MCP declarations belong in a session backend tool/, skill/, resource/, or frontend tool/*.mcp.tsx surface',\n );\n }\n const source = fs.readFileSync(path.join(options.packageDir, relative), 'utf8');\n const cardinality = routedCardinality(source);\n\n if (state.surface === undefined) {\n // Inside `mode/plan/`, a file called `mode.*` declares the gate itself\n // rather than a contribution gated by it, and the gate's id names it.\n const gate = state.gates[state.gates.length - 1];\n if (gate !== undefined && parsed.name === gate.kind) {\n notePlatform(relative, parsed, state.side);\n entries.push({\n file: relative,\n scope: state.scope,\n side: state.side,\n gates: state.gates.slice(0, -1),\n surface: gate.kind,\n route: [],\n name: gate.id,\n target: parsed.target,\n platform: parsed.platform,\n ...(cardinality ? { cardinality } : {}),\n role: 'contribution',\n });\n return;\n }\n\n // The scope's constructor. Not gated: it owns the whole subtree's\n // lifetime, and a gate that could switch it off would leave everything\n // below it holding a context nothing built.\n if (parsed.name === ROOT_FILE_NAME && gate === undefined) {\n notePlatform(relative, parsed, state.side);\n entries.push({\n file: relative,\n scope: state.scope,\n side: state.side,\n gates: [],\n surface: undefined,\n route: [],\n name: parsed.name,\n target: parsed.target,\n platform: parsed.platform,\n role: 'root',\n });\n return;\n }\n\n return note(\n relative,\n gate === undefined\n ? `only ${ROOT_FILE_NAME}.* is read at a side root; move each contribution into its named surface folder`\n : `inside ${gate.kind}/${gate.id}/ a contribution needs a surface folder, or name the file ${gate.kind}.* to declare the gate`,\n );\n }\n\n // Inside a routed surface only the leaf file is a route. Anything beside\n // it is colocation, which is the whole reason Next.js reserves the name.\n if (ROUTED_SURFACES.includes(state.surface) && parsed.name !== ROUTE_FILE_NAME) return;\n\n notePlatform(relative, parsed, state.side);\n entries.push({\n file: relative,\n scope: state.scope,\n side: state.side,\n gates: state.gates,\n surface: state.surface,\n route: state.route,\n name: parsed.name,\n target: parsed.target,\n platform: parsed.platform,\n ...(cardinality ? { cardinality } : {}),\n role: 'contribution',\n });\n };\n\n walk(root, INITIAL);\n return { root, entries, notices };\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { ExtensionGraph } from "../../types/extensionGraph.mjs";
2
+ import { ScanOptions } from "./type.mjs";
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
+ export declare function scanExtensions(options: ScanOptions): ExtensionGraph;
7
+ //#endregion
8
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/scan/index.ts"],"mappings":";;;;wBAgFgB,mBAAmB,oBAAoB;wBAmBvC,eAAe,SAAS,cAAc"}
@@ -0,0 +1,2 @@
1
+ import{BACKEND_PLATFORMS as e,BACKEND_SURFACES as t,DEFAULT_ROUTING_ROOT as n,FRONTEND_PLATFORMS as r,FRONTEND_SURFACES as i,GATE_SEGMENTS as a,GENERATED_ENTRY_NAMES as o,ROOT_FILE_NAME as s,ROUTED_SURFACES as c,ROUTING_ROOT_ALIAS as l}from"../../constants/layout.mjs";import{classifySegment as u}from"../segments/index.mjs";import{parseFilename as d}from"../filename/index.mjs";import f from"node:fs";import p from"node:path";const m={scope:`global`,side:void 0,gates:[],surface:void 0,route:[],pendingGate:void 0};function h(e){return e===`backend`?t:i}function g(t,n){return(t===`backend`?n.platforms?.backend:n.platforms?.frontend)??(t===`backend`?e:r)}function _(e,t){let n=h(t);if(n.includes(e))return;let r=e.endsWith(`s`)?e.slice(0,-1):`${e}s`;return n.includes(r)?r:void 0}function v(e){return/export\s+default\s+defineRoutedContribution\s*\([\s\S]*,\s*\{\s*cardinality\s*:\s*['"](optional|many|collection)['"]/u.exec(e)?.[1]}function y(e,t){return(t===void 0?[n,l]:[t]).find(t=>f.existsSync(p.join(e,t)))}const b=`the .cli platform is retired: Po has no terminal host, so move this file to .server or delete it`;function x(e){let t=y(e.packageDir,e.root);if(t===void 0)return{root:e.root??`src/extensions`,entries:[],notices:[]};let n=e.sides?.backend??`backend`,r=e.sides?.frontend??`frontend`,i=e.generatedEntries??o,l=[],x=[],S=(e,t)=>void x.push({path:e,message:t}),C=(t,n,r)=>{n.platform===void 0&&S(t,`names no platform; add one of ${g(r,e).join(`, `)} before .${n.extension} (without one the host is inferred from the side rather than declared)`)},w=(t,n)=>{let r=p.join(e.packageDir,t),i=f.readdirSync(r,{withFileTypes:!0}).sort((e,t)=>e.name.localeCompare(t.name));for(let e of i){let r=`${t}/${e.name}`;e.isDirectory()?T(r,e.name,n):e.isFile()&&E(r,e.name,n)}},T=(e,t,i)=>{let o=u(t);if(o.kind===`private`)return;if(o.kind===`group`)return i.side===void 0&&o.name===n?w(e,{...i,side:`backend`}):i.side===void 0&&o.name===r?w(e,{...i,side:`frontend`}):w(e,i);if(o.kind===`dynamic`)return i.surface!==void 0&&c.includes(i.surface)?w(e,{...i,route:[...i.route,{param:o.param}]}):S(e,`a dynamic segment is only meaningful inside a routed surface such as api/`);if(i.pendingGate!==void 0){let t={kind:i.pendingGate,id:o.name};return w(e,{...i,gates:[...i.gates,t],pendingGate:void 0})}if(i.surface!==void 0)return c.includes(i.surface)?w(e,{...i,route:[...i.route,{literal:o.name}]}):S(e,`${i.surface}/ takes files directly; put helpers in a _private folder`);if(i.side===void 0)return o.name===`workspaces`&&i.scope===`global`?w(e,{...i,scope:`workspace`}):o.name===`sessions`&&i.scope===`workspace`?w(e,{...i,scope:`session`}):S(e,`expected a scope segment or a side group, such as (${n})`);if(a.includes(o.name))return w(e,{...i,pendingGate:o.name});if(h(i.side).includes(o.name))return w(e,{...i,surface:o.name});let s=_(o.name,i.side);return S(e,s===void 0?`'${o.name}' is not a ${i.side} surface`:`'${o.name}' is not a surface, did you mean '${s}'`)},E=(t,n,r)=>{if(r.side===void 0){let e=n.slice(0,n.lastIndexOf(`.`));return r.scope===`global`&&i.includes(e)||r.scope===`global`||d(n,[])===void 0?void 0:S(t,`a contribution needs a side group and a surface folder`)}let a=g(r.side,e),o=d(n,[...a,`mcp`,`cli`]);if(o===void 0)return;if(o.platform===`cli`)return S(t,b);if(o.platform===`mcp`&&(r.scope!==`session`||(r.side===`frontend`?r.surface!==`tool`||!n.endsWith(`.mcp.tsx`):r.surface!==`tool`&&r.surface!==`skill`&&r.surface!==`resource`)))return S(t,`MCP declarations belong in a session backend tool/, skill/, resource/, or frontend tool/*.mcp.tsx surface`);let u=v(f.readFileSync(p.join(e.packageDir,t),`utf8`));if(r.surface===void 0){let e=r.gates[r.gates.length-1];if(e!==void 0&&o.name===e.kind){C(t,o,r.side),l.push({file:t,scope:r.scope,side:r.side,gates:r.gates.slice(0,-1),surface:e.kind,route:[],name:e.id,target:o.target,platform:o.platform,...u?{cardinality:u}:{},role:`contribution`});return}if(o.name===`root`&&e===void 0){C(t,o,r.side),l.push({file:t,scope:r.scope,side:r.side,gates:[],surface:void 0,route:[],name:o.name,target:o.target,platform:o.platform,role:`root`});return}return S(t,e===void 0?`only ${s}.* is read at a side root; move each contribution into its named surface folder`:`inside ${e.kind}/${e.id}/ a contribution needs a surface folder, or name the file ${e.kind}.* to declare the gate`)}c.includes(r.surface)&&o.name!==`route`||(C(t,o,r.side),l.push({file:t,scope:r.scope,side:r.side,gates:r.gates,surface:r.surface,route:r.route,name:o.name,target:o.target,platform:o.platform,...u?{cardinality:u}:{},role:`contribution`}))};return w(t,m),{root:t,entries:l,notices:x}}export{b as RETIRED_PLATFORM_NOTICE,y as resolveRoutingRoot,x as scanExtensions};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["fs","path","INITIAL","surfacesFor","side","BACKEND_SURFACES","FRONTEND_SURFACES","platformsFor","options","BACKEND_PLATFORMS","FRONTEND_PLATFORMS","nearestSurface","surfaces","name","singular","routedCardinality","source","resolveRoutingRoot","root","DEFAULT_ROUTING_ROOT","ROUTING_ROOT_ALIAS","packageDir","candidate","RETIRED_PLATFORM_NOTICE","scanExtensions","backendGroup","frontendGroup","generated","GENERATED_ENTRY_NAMES","entries","notices","note","relativePath","notePlatform","parsed","relative","walk","absolute","relativeDir","listing","a","b","item","walkDirectory","state","readFile","segment","classifySegment","ROUTED_SURFACES","gate","GATE_SEGMENTS","nearest","base","fileName","parseFilename","platforms","cardinality","ROOT_FILE_NAME"],"sources":["../../../src/services/scan/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n BACKEND_GROUP,\n BACKEND_PLATFORMS,\n BACKEND_SURFACES,\n DEFAULT_ROUTING_ROOT,\n FRONTEND_GROUP,\n FRONTEND_PLATFORMS,\n FRONTEND_SURFACES,\n GATE_SEGMENTS,\n GENERATED_ENTRY_NAMES,\n RETIRED_PLATFORM,\n ROOT_FILE_NAME,\n ROUTE_FILE_NAME,\n ROUTED_SURFACES,\n ROUTING_ROOT_ALIAS,\n SESSION_SEGMENT,\n WORKSPACE_SEGMENT,\n} from '../../constants/layout';\nimport type {\n ExtensionEntry,\n ExtensionGate,\n ExtensionGraph,\n ExtensionNotice,\n ExtensionScope,\n ExtensionSide,\n RouteSegment,\n} from '../../types/extensionGraph';\nimport { type ParsedFilename, parseFilename } from '../filename';\nimport { classifySegment } from '../segments';\nimport type { ScanOptions } from './type';\n\ninterface WalkState {\n readonly scope: ExtensionScope;\n readonly side: ExtensionSide | undefined;\n readonly gates: readonly ExtensionGate[];\n readonly surface: string | undefined;\n readonly route: readonly RouteSegment[];\n /** A gate folder has been entered and the next plain segment names its id. */\n readonly pendingGate: string | undefined;\n}\n\nconst INITIAL: WalkState = {\n scope: 'global',\n side: undefined,\n gates: [],\n surface: undefined,\n route: [],\n pendingGate: undefined,\n};\n\nfunction surfacesFor(side: ExtensionSide): readonly string[] {\n return side === 'backend' ? BACKEND_SURFACES : FRONTEND_SURFACES;\n}\n\nfunction platformsFor(side: ExtensionSide, options: ScanOptions): readonly string[] {\n const configured = side === 'backend' ? options.platforms?.backend : options.platforms?.frontend;\n return configured ?? (side === 'backend' ? BACKEND_PLATFORMS : FRONTEND_PLATFORMS);\n}\n\n/** The nearest surface a misspelled folder probably meant, or undefined. */\nfunction nearestSurface(name: string, side: ExtensionSide): string | undefined {\n const surfaces = surfacesFor(side);\n if (surfaces.includes(name)) return undefined;\n const singular = name.endsWith('s') ? name.slice(0, -1) : `${name}s`;\n return surfaces.includes(singular) ? singular : undefined;\n}\n\n/** Reads cardinality only from the routed helper's options argument. */\nfunction routedCardinality(source: string): ExtensionEntry['cardinality'] | undefined {\n const declaration =\n /export\\s+default\\s+defineRoutedContribution\\s*\\([\\s\\S]*,\\s*\\{\\s*cardinality\\s*:\\s*['\"](optional|many|collection)['\"]/u.exec(\n source,\n );\n return declaration?.[1] as ExtensionEntry['cardinality'] | undefined;\n}\n\n/** Resolves the routing root, accepting the singular alias when only it exists. */\nexport function resolveRoutingRoot(packageDir: string, root: string | undefined): string | undefined {\n const candidates = root !== undefined ? [root] : [DEFAULT_ROUTING_ROOT, ROUTING_ROOT_ALIAS];\n return candidates.find((candidate) => fs.existsSync(path.join(packageDir, candidate)));\n}\n\n/**\n * Reads one package's routing root into a description of what it declares.\n *\n * Filesystem in, data out. No globbing, which is what keeps parenthesised\n * group folders safe: tsdown's globber would read a bare `(name)` as an\n * extglob group, and a directory walk never sees a pattern at all.\n *\n * Nothing here throws. A folder the convention does not recognise becomes a\n * notice and its subtree is skipped, so one bad directory never costs a\n * package its other contributions.\n */\n/** Reported for a routed file that names the retired terminal platform; generate refuses it. */\nexport const RETIRED_PLATFORM_NOTICE = `the .${RETIRED_PLATFORM} platform is retired: Po has no terminal host, so move this file to .server or delete it`;\n\nexport function scanExtensions(options: ScanOptions): ExtensionGraph {\n const root = resolveRoutingRoot(options.packageDir, options.root);\n if (root === undefined) return { root: options.root ?? DEFAULT_ROUTING_ROOT, entries: [], notices: [] };\n\n const backendGroup = options.sides?.backend ?? BACKEND_GROUP;\n const frontendGroup = options.sides?.frontend ?? FRONTEND_GROUP;\n const generated = options.generatedEntries ?? GENERATED_ENTRY_NAMES;\n\n const entries: ExtensionEntry[] = [];\n const notices: ExtensionNotice[] = [];\n const note = (relativePath: string, message: string): void => void notices.push({ path: relativePath, message });\n\n /**\n * Every public routed file names its platform, so a host is never inferred.\n *\n * Checked at each push rather than once at the top of readFile, because a\n * file sitting somewhere the convention does not define has a position\n * problem, and reporting the missing suffix instead would hide it.\n */\n const notePlatform = (relative: string, parsed: ParsedFilename, side: ExtensionSide): void => {\n if (parsed.platform !== undefined) return;\n note(\n relative,\n `names no platform; add one of ${platformsFor(side, options).join(', ')} before .${parsed.extension} (without one the host is inferred from the side rather than declared)`,\n );\n };\n\n const walk = (relativeDir: string, state: WalkState): void => {\n const absolute = path.join(options.packageDir, relativeDir);\n const listing = fs.readdirSync(absolute, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));\n\n for (const item of listing) {\n const relative = `${relativeDir}/${item.name}`;\n if (item.isDirectory()) walkDirectory(relative, item.name, state);\n else if (item.isFile()) readFile(relative, item.name, state);\n }\n };\n\n const walkDirectory = (relative: string, name: string, state: WalkState): void => {\n const segment = classifySegment(name);\n\n if (segment.kind === 'private') return;\n\n if (segment.kind === 'group') {\n if (state.side === undefined && segment.name === backendGroup)\n return walk(relative, { ...state, side: 'backend' });\n if (state.side === undefined && segment.name === frontendGroup)\n return walk(relative, { ...state, side: 'frontend' });\n return walk(relative, state);\n }\n\n if (segment.kind === 'dynamic') {\n if (state.surface !== undefined && ROUTED_SURFACES.includes(state.surface)) {\n return walk(relative, { ...state, route: [...state.route, { param: segment.param }] });\n }\n return note(relative, 'a dynamic segment is only meaningful inside a routed surface such as api/');\n }\n\n if (state.pendingGate !== undefined) {\n const gate: ExtensionGate = { kind: state.pendingGate, id: segment.name };\n return walk(relative, { ...state, gates: [...state.gates, gate], pendingGate: undefined });\n }\n\n if (state.surface !== undefined) {\n if (ROUTED_SURFACES.includes(state.surface)) {\n return walk(relative, { ...state, route: [...state.route, { literal: segment.name }] });\n }\n return note(relative, `${state.surface}/ takes files directly; put helpers in a _private folder`);\n }\n\n if (state.side === undefined) {\n if (segment.name === WORKSPACE_SEGMENT && state.scope === 'global') {\n return walk(relative, { ...state, scope: 'workspace' });\n }\n if (segment.name === SESSION_SEGMENT && state.scope === 'workspace') {\n return walk(relative, { ...state, scope: 'session' });\n }\n return note(relative, `expected a scope segment or a side group, such as (${backendGroup})`);\n }\n\n if (GATE_SEGMENTS.includes(segment.name)) return walk(relative, { ...state, pendingGate: segment.name });\n\n if (surfacesFor(state.side).includes(segment.name)) return walk(relative, { ...state, surface: segment.name });\n\n const nearest = nearestSurface(segment.name, state.side);\n return note(\n relative,\n nearest === undefined\n ? `'${segment.name}' is not a ${state.side} surface`\n : `'${segment.name}' is not a surface, did you mean '${nearest}'`,\n );\n };\n\n const readFile = (relative: string, fileName: string, state: WalkState): void => {\n if (state.side === undefined) {\n const base = fileName.slice(0, fileName.lastIndexOf('.'));\n if (state.scope === 'global' && generated.includes(base)) return;\n // A direct child of the routing root is a host entry, not a\n // contribution: either one this build generates, or one the package\n // hand-writes and names itself. A package that has not adopted the\n // layout has all of its native host entries here. A contribution\n // always sits at least one directory deep, inside a side group.\n if (state.scope === 'global') return;\n if (parseFilename(fileName, []) === undefined) return;\n return note(relative, 'a contribution needs a side group and a surface folder');\n }\n\n const platforms = platformsFor(state.side, options);\n const parsed = parseFilename(fileName, [...platforms, 'mcp', RETIRED_PLATFORM]);\n if (parsed === undefined) return;\n if (parsed.platform === RETIRED_PLATFORM) return note(relative, RETIRED_PLATFORM_NOTICE);\n if (\n parsed.platform === 'mcp' &&\n (state.scope !== 'session' ||\n (state.side === 'frontend'\n ? state.surface !== 'tool' || !fileName.endsWith('.mcp.tsx')\n : state.surface !== 'tool' && state.surface !== 'skill' && state.surface !== 'resource'))\n ) {\n return note(\n relative,\n 'MCP declarations belong in a session backend tool/, skill/, resource/, or frontend tool/*.mcp.tsx surface',\n );\n }\n const source = fs.readFileSync(path.join(options.packageDir, relative), 'utf8');\n const cardinality = routedCardinality(source);\n\n if (state.surface === undefined) {\n // Inside `mode/plan/`, a file called `mode.*` declares the gate itself\n // rather than a contribution gated by it, and the gate's id names it.\n const gate = state.gates[state.gates.length - 1];\n if (gate !== undefined && parsed.name === gate.kind) {\n notePlatform(relative, parsed, state.side);\n entries.push({\n file: relative,\n scope: state.scope,\n side: state.side,\n gates: state.gates.slice(0, -1),\n surface: gate.kind,\n route: [],\n name: gate.id,\n target: parsed.target,\n platform: parsed.platform,\n ...(cardinality ? { cardinality } : {}),\n role: 'contribution',\n });\n return;\n }\n\n // The scope's constructor. Not gated: it owns the whole subtree's\n // lifetime, and a gate that could switch it off would leave everything\n // below it holding a context nothing built.\n if (parsed.name === ROOT_FILE_NAME && gate === undefined) {\n notePlatform(relative, parsed, state.side);\n entries.push({\n file: relative,\n scope: state.scope,\n side: state.side,\n gates: [],\n surface: undefined,\n route: [],\n name: parsed.name,\n target: parsed.target,\n platform: parsed.platform,\n role: 'root',\n });\n return;\n }\n\n return note(\n relative,\n gate === undefined\n ? `only ${ROOT_FILE_NAME}.* is read at a side root; move each contribution into its named surface folder`\n : `inside ${gate.kind}/${gate.id}/ a contribution needs a surface folder, or name the file ${gate.kind}.* to declare the gate`,\n );\n }\n\n // Inside a routed surface only the leaf file is a route. Anything beside\n // it is colocation, which is the whole reason Next.js reserves the name.\n if (ROUTED_SURFACES.includes(state.surface) && parsed.name !== ROUTE_FILE_NAME) return;\n\n notePlatform(relative, parsed, state.side);\n entries.push({\n file: relative,\n scope: state.scope,\n side: state.side,\n gates: state.gates,\n surface: state.surface,\n route: state.route,\n name: parsed.name,\n target: parsed.target,\n platform: parsed.platform,\n ...(cardinality ? { cardinality } : {}),\n role: 'contribution',\n });\n };\n\n walk(root, INITIAL);\n return { root, entries, notices };\n}\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
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.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.d.mts","names":[],"sources":["../../../src/services/scan/type.ts"],"mappings":";;iBACiB;;WAEN;;;;;WAKA;;WAEA;aAAmB;aAA2B;;;WAE9C;aAAuB;aAAsC;;;WAE7D"}
@@ -0,0 +1,2 @@
1
+ function e(e,t,n){if(!(e.length<=t.length+n.length)&&e.startsWith(t)&&e.endsWith(n))return e.slice(t.length,e.length-n.length)}function t(t){if(t.startsWith(`_`))return{kind:`private`,name:t.slice(1)};let n=e(t,`(`,`)`);if(n!==void 0)return{kind:`group`,name:n};let r=e(t,`[`,`]`);if(r!==void 0){let e=r.startsWith(`...`);return{kind:`dynamic`,param:{name:e?r.slice(3):r,catchAll:e}}}return{kind:`plain`,name:t}}exports.classifySegment=t;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["wrapped","open","close","segment","classifySegment","group","dynamic","catchAll"],"sources":["../../../src/services/segments/index.ts"],"sourcesContent":["import {\n CATCH_ALL_MARKER,\n DYNAMIC_PREFIX,\n DYNAMIC_SUFFIX,\n GROUP_PREFIX,\n GROUP_SUFFIX,\n PRIVATE_PREFIX,\n} from '../../constants/layout';\nimport type { RouteParam } from '../../types/extensionGraph';\n\n/**\n * What one folder name means, using Next.js spelling throughout.\n *\n * A group is organisational and invisible to the path. A private folder is\n * colocation and is never scanned. A dynamic segment is a route parameter.\n * Everything else is a plain segment whose meaning depends on where it sits.\n */\nexport type Segment =\n | { readonly kind: 'group'; readonly name: string }\n | { readonly kind: 'private'; readonly name: string }\n | { readonly kind: 'dynamic'; readonly param: RouteParam }\n | { readonly kind: 'plain'; readonly name: string };\n\nfunction wrapped(segment: string, open: string, close: string): string | undefined {\n if (segment.length <= open.length + close.length) return undefined;\n if (!segment.startsWith(open) || !segment.endsWith(close)) return undefined;\n return segment.slice(open.length, segment.length - close.length);\n}\n\n/** Classifies one folder name. Never throws: an unusable name is simply plain. */\nexport function classifySegment(segment: string): Segment {\n if (segment.startsWith(PRIVATE_PREFIX)) return { kind: 'private', name: segment.slice(PRIVATE_PREFIX.length) };\n\n const group = wrapped(segment, GROUP_PREFIX, GROUP_SUFFIX);\n if (group !== undefined) return { kind: 'group', name: group };\n\n const dynamic = wrapped(segment, DYNAMIC_PREFIX, DYNAMIC_SUFFIX);\n if (dynamic !== undefined) {\n const catchAll = dynamic.startsWith(CATCH_ALL_MARKER);\n return {\n kind: 'dynamic',\n param: { name: catchAll ? dynamic.slice(CATCH_ALL_MARKER.length) : dynamic, catchAll },\n };\n }\n\n return { kind: 'plain', name: segment };\n}\n"],"mappings":"AAuBA,SAASA,EAAQ,EAAiB,EAAc,EAAmC,CACjF,GAAI,IAAQ,QAAUC,EAAK,OAASC,EAAM,SACrCC,EAAQ,WAAWF,CAAI,GAAME,EAAQ,SAASD,CAAK,EACxD,OAAOC,EAAQ,MAAMF,EAAK,OAAQE,EAAQ,OAASD,EAAM,MAAM,CACjE,CAGA,SAAgBE,EAAgB,EAA0B,CACxD,GAAID,EAAQ,WAAA,GAAyB,EAAG,MAAO,CAAE,KAAM,UAAW,KAAMA,EAAQ,MAAA,CAA2B,CAAE,EAE7G,IAAME,EAAQL,EAAQG,EAAA,IAAA,GAAmC,EACzD,GAAIE,IAAU,IAAA,GAAW,MAAO,CAAE,KAAM,QAAS,KAAMA,CAAM,EAE7D,IAAMC,EAAUN,EAAQG,EAAA,IAAA,GAAuC,EAC/D,GAAIG,IAAY,IAAA,GAAW,CACzB,IAAMC,EAAWD,EAAQ,WAAA,KAA2B,EACpD,MAAO,CACL,KAAM,UACN,MAAO,CAAE,KAAMC,EAAWD,EAAQ,MAAA,CAA6B,EAAIA,EAAS,UAAS,CACvF,CACF,CAEA,MAAO,CAAE,KAAM,QAAS,KAAMH,CAAQ,CACxC"}
@@ -0,0 +1,26 @@
1
+ import { RouteParam } from "../../types/extensionGraph.mjs";
2
+ //#region src/services/segments/index.d.ts
3
+ /**
4
+ * What one folder name means, using Next.js spelling throughout.
5
+ *
6
+ * A group is organisational and invisible to the path. A private folder is
7
+ * colocation and is never scanned. A dynamic segment is a route parameter.
8
+ * Everything else is a plain segment whose meaning depends on where it sits.
9
+ */
10
+ export type Segment = {
11
+ readonly kind: 'group';
12
+ readonly name: string;
13
+ } | {
14
+ readonly kind: 'private';
15
+ readonly name: string;
16
+ } | {
17
+ readonly kind: 'dynamic';
18
+ readonly param: RouteParam;
19
+ } | {
20
+ readonly kind: 'plain';
21
+ readonly name: string;
22
+ };
23
+ /** Classifies one folder name. Never throws: an unusable name is simply plain. */
24
+ export declare function classifySegment(segment: string): Segment;
25
+ //#endregion
26
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/segments/index.ts"],"mappings":";;;;;;;;;YAiBY;WACG;WAAwB;;WACxB;WAA0B;;WAC1B;WAA0B,OAAO;;WACjC;WAAwB;;;wBASvB,gBAAgB,kBAAkB"}
@@ -0,0 +1,2 @@
1
+ function e(e,t,n){if(!(e.length<=t.length+n.length)&&e.startsWith(t)&&e.endsWith(n))return e.slice(t.length,e.length-n.length)}function t(t){if(t.startsWith(`_`))return{kind:`private`,name:t.slice(1)};let n=e(t,`(`,`)`);if(n!==void 0)return{kind:`group`,name:n};let r=e(t,`[`,`]`);if(r!==void 0){let e=r.startsWith(`...`);return{kind:`dynamic`,param:{name:e?r.slice(3):r,catchAll:e}}}return{kind:`plain`,name:t}}export{t as classifySegment};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["wrapped","open","close","segment","classifySegment","group","dynamic","catchAll"],"sources":["../../../src/services/segments/index.ts"],"sourcesContent":["import {\n CATCH_ALL_MARKER,\n DYNAMIC_PREFIX,\n DYNAMIC_SUFFIX,\n GROUP_PREFIX,\n GROUP_SUFFIX,\n PRIVATE_PREFIX,\n} from '../../constants/layout';\nimport type { RouteParam } from '../../types/extensionGraph';\n\n/**\n * What one folder name means, using Next.js spelling throughout.\n *\n * A group is organisational and invisible to the path. A private folder is\n * colocation and is never scanned. A dynamic segment is a route parameter.\n * Everything else is a plain segment whose meaning depends on where it sits.\n */\nexport type Segment =\n | { readonly kind: 'group'; readonly name: string }\n | { readonly kind: 'private'; readonly name: string }\n | { readonly kind: 'dynamic'; readonly param: RouteParam }\n | { readonly kind: 'plain'; readonly name: string };\n\nfunction wrapped(segment: string, open: string, close: string): string | undefined {\n if (segment.length <= open.length + close.length) return undefined;\n if (!segment.startsWith(open) || !segment.endsWith(close)) return undefined;\n return segment.slice(open.length, segment.length - close.length);\n}\n\n/** Classifies one folder name. Never throws: an unusable name is simply plain. */\nexport function classifySegment(segment: string): Segment {\n if (segment.startsWith(PRIVATE_PREFIX)) return { kind: 'private', name: segment.slice(PRIVATE_PREFIX.length) };\n\n const group = wrapped(segment, GROUP_PREFIX, GROUP_SUFFIX);\n if (group !== undefined) return { kind: 'group', name: group };\n\n const dynamic = wrapped(segment, DYNAMIC_PREFIX, DYNAMIC_SUFFIX);\n if (dynamic !== undefined) {\n const catchAll = dynamic.startsWith(CATCH_ALL_MARKER);\n return {\n kind: 'dynamic',\n param: { name: catchAll ? dynamic.slice(CATCH_ALL_MARKER.length) : dynamic, catchAll },\n };\n }\n\n return { kind: 'plain', name: segment };\n}\n"],"mappings":"AAuBA,SAASA,EAAQ,EAAiB,EAAc,EAAmC,CACjF,GAAI,IAAQ,QAAUC,EAAK,OAASC,EAAM,SACrCC,EAAQ,WAAWF,CAAI,GAAME,EAAQ,SAASD,CAAK,EACxD,OAAOC,EAAQ,MAAMF,EAAK,OAAQE,EAAQ,OAASD,EAAM,MAAM,CACjE,CAGA,SAAgBE,EAAgB,EAA0B,CACxD,GAAID,EAAQ,WAAA,GAAyB,EAAG,MAAO,CAAE,KAAM,UAAW,KAAMA,EAAQ,MAAA,CAA2B,CAAE,EAE7G,IAAME,EAAQL,EAAQG,EAAA,IAAA,GAAmC,EACzD,GAAIE,IAAU,IAAA,GAAW,MAAO,CAAE,KAAM,QAAS,KAAMA,CAAM,EAE7D,IAAMC,EAAUN,EAAQG,EAAA,IAAA,GAAuC,EAC/D,GAAIG,IAAY,IAAA,GAAW,CACzB,IAAMC,EAAWD,EAAQ,WAAA,KAA2B,EACpD,MAAO,CACL,KAAM,UACN,MAAO,CAAE,KAAMC,EAAWD,EAAQ,MAAA,CAA6B,EAAIA,EAAS,UAAS,CACvF,CACF,CAEA,MAAO,CAAE,KAAM,QAAS,KAAMH,CAAQ,CACxC"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../constants/layout.cjs"),n=require("../identity/index.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);const a=[`global`,`workspace`,`session`],o=`apiContracts`,s=`src/exports/${o}.ts`,c=`./extensions/web`,l=`./dist/extensions/web.mjs`,u=`./extensions/native`,d=`./dist/extensions/native.mjs`,f=`./${t.GENERATED_DIR}/mcp.ts`;function p(e){let t=e.entries.filter(e=>e.side===`backend`);if(t.length===0)return[];let n=Math.min(...t.map(e=>a.indexOf(e.scope)));return a.slice(n)}function m(e,t){return e.entries.filter(e=>e.side===`frontend`&&e.platform===`native`==(t===`native`))}function h(e){let t=new Set(e.map(e=>e.scope));return a.filter(e=>t.has(e))}function g(e){return[...new Set(e.filter(e=>e.surface===`channel`).map(e=>n.toSnake(e.name)))].sort()}function _(e,t){let n={};for(let[a,o]of Object.entries(t)){if(a===`./package.json`||typeof o!=`object`||!o){n[a]=o;continue}let t=o,s=t.import??t.default,c=s?.endsWith(`.mjs`)===!0?`${s.slice(0,-4)}.d.mts`:void 0;n[a]=t.types===void 0&&c!==void 0&&r.default.existsSync(i.default.join(e,c))?{types:c,...t}:t}return n}function v(e){let{packageDir:n,graph:a,targets:f,pluginId:v}=e,y={...e.manifest};if(y.exports!==void 0&&typeof y.exports==`object`){let e=_(n,y.exports);delete e[`./extensions/pi`],f.includes(`web`)?e[c]={import:l}:delete e[c],f.includes(`native`)?e[u]={import:d}:delete e[u],y.exports=Object.fromEntries(Object.entries(e).sort(([e],[t])=>e===`./package.json`?1:t===`./package.json`?-1:e.localeCompare(t)))}if(delete y.pi,f.includes(`server`)){let e=r.default.existsSync(i.default.join(n,s))?{contracts:{entry:`./${s}`,dist:`./dist/${o}.mjs`}}:{};y.poServer={entry:`./${t.GENERATED_DIR}/server.ts`,dist:`./dist/extensions/server.mjs`,scopes:p(a),...e}}else delete y.poServer;if(f.includes(`web`)){let e=m(a,`web`);y.poWeb={pluginId:v,channels:g(e),client:l,scopes:h(e)}}else delete y.poWeb;if(f.includes(`native`)){let e=m(a,`native`);y.poNative={pluginId:v,channels:g(e),client:d,scopes:h(e)}}else delete y.poNative;return y}function y(e,n,r=[]){let i={...e};return n?i.poMcp={entry:f,dist:`./dist/extensions/mcp.mjs`,scopes:[`session`],...r.length===0?{}:{ui:{entry:`./${t.GENERATED_DIR}/mcp-ui.ts`,dist:`./dist/extensions/mcp-ui.mjs`,widgets:r}}}:delete i.poMcp,i}function b(e){let t=i.default.join(e.packageDir,`package.json`),n=`${JSON.stringify(v(e),null,2)}\n`;return r.default.readFileSync(t,`utf8`)!==n&&(r.default.writeFileSync(t,n),!0)}function x(e,t,n=[]){let a=i.default.join(e,`package.json`),o=JSON.parse(r.default.readFileSync(a,`utf8`)),s=`${JSON.stringify(y(o,t,n),null,2)}\n`;return r.default.readFileSync(a,`utf8`)!==s&&(r.default.writeFileSync(a,s),!0)}exports.syncManifest=v,exports.syncMcpManifest=y,exports.writeManifest=b,exports.writeMcpManifest=x;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["GENERATED_DIR","toSnake","fs","path","require_runtime","require_layout","require_index","node_fs","node_path","SCOPE_ORDER","API_CONTRACTS_ENTRY","API_CONTRACTS_SOURCE","WEB_EXPORT","WEB_DIST","NATIVE_EXPORT","NATIVE_DIST","MCP_ENTRY","serverScopes","declared","graph","entry","shallowest","frontendEntries","host","frontendScopes","entries","scope","channels","withTypeConditions","next","subpath","target","exportsMap","conditions","esm","types","packageDir","syncManifest","input","manifest","targets","left","right","contracts","syncMcpManifest","enabled","widgets","writeManifest","manifestPath","writeMcpManifest"],"sources":["../../../src/services/syncManifest/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GENERATED_DIR, NATIVE_PLATFORM } from '../../constants/layout';\nimport type { ExtensionEntry, ExtensionGraph, ExtensionScope } from '../../types/extensionGraph';\nimport { toSnake } from '../identity';\nimport type { BuildTarget } from '../resolveTarget/type';\nimport type { ManifestSync } from './type';\n\nconst SCOPE_ORDER: readonly ExtensionScope[] = ['global', 'workspace', 'session'];\n\nconst API_CONTRACTS_ENTRY = 'apiContracts';\nconst API_CONTRACTS_SOURCE = `src/exports/${API_CONTRACTS_ENTRY}.ts`;\n\n/** The retired terminal entry, removed from any manifest built before the terminal host was. */\nconst PI_EXPORT = './extensions/pi';\nconst WEB_EXPORT = './extensions/web';\nconst WEB_DIST = './dist/extensions/web.mjs';\nconst NATIVE_EXPORT = './extensions/native';\nconst NATIVE_DIST = './dist/extensions/native.mjs';\nconst MCP_DIST = './dist/extensions/mcp.mjs';\nconst MCP_ENTRY = `./${GENERATED_DIR}/mcp.ts`;\n\n/**\n * Which scopes a server facet declares.\n *\n * The generated facet cascades: a contribution declared at global registers at\n * global, workspace and session alike, because the server reads exactly one\n * scope declaration and nothing above it. So the declared set runs from the\n * shallowest contribution downward.\n */\nfunction serverScopes(graph: ExtensionGraph): ExtensionScope[] {\n const declared = graph.entries.filter((entry) => entry.side === 'backend');\n if (declared.length === 0) return [];\n const shallowest = Math.min(...declared.map((entry) => SCOPE_ORDER.indexOf(entry.scope)));\n return SCOPE_ORDER.slice(shallowest);\n}\n\n/**\n * The frontend files one frontend host reads.\n *\n * Native reads only `.native` files, and the cockpit never reads them, so the\n * two hosts' blocks describe disjoint halves of the same `(frontend)` side.\n */\nfunction frontendEntries(graph: ExtensionGraph, host: 'web' | 'native'): ExtensionEntry[] {\n return graph.entries.filter(\n (entry) => entry.side === 'frontend' && (entry.platform === NATIVE_PLATFORM) === (host === NATIVE_PLATFORM),\n );\n}\n\n/**\n * Which scopes a frontend plugin declares.\n *\n * The cockpit does not cascade the way the server does. It merges the scopes\n * it is given, so a plugin declares exactly the ones it contributes to. The\n * native app reuses the same registry, so the same holds there.\n */\nfunction frontendScopes(entries: readonly ExtensionEntry[]): ExtensionScope[] {\n const declared = new Set(entries.map((entry) => entry.scope));\n return SCOPE_ORDER.filter((scope) => declared.has(scope));\n}\n\n/** The frame types a frontend half claims, which its host reads before loading it. */\nfunction channels(entries: readonly ExtensionEntry[]): string[] {\n return [\n ...new Set(entries.filter((entry) => entry.surface === 'channel').map((entry) => toSnake(entry.name))),\n ].sort();\n}\n\n/**\n * Restores the `types` condition tsdown's exports generation omits.\n *\n * tsdown derives the map from the built chunks, which is what makes it follow\n * the folder tree. But its `genSubExport` only ever emits `import`, `require`\n * or `default`: a declaration chunk contributes nothing to a subpath, so only\n * the root gets a top-level `types`. TypeScript still resolves a sibling\n * `.d.mts` under node16 and bundler resolution, but publint and attw read the\n * condition, so it belongs in the map.\n */\nfunction withTypeConditions(packageDir: string, exportsMap: Record<string, unknown>): Record<string, unknown> {\n const next: Record<string, unknown> = {};\n for (const [subpath, target] of Object.entries(exportsMap)) {\n if (subpath === './package.json' || target === null || typeof target !== 'object') {\n next[subpath] = target;\n continue;\n }\n const conditions = target as Record<string, string>;\n const esm = conditions.import ?? conditions.default;\n const types = esm?.endsWith('.mjs') === true ? `${esm.slice(0, -'.mjs'.length)}.d.mts` : undefined;\n // Ahead of import and require: a resolver takes the first condition it\n // matches, so a types-aware one must not fall through to the runtime file.\n next[subpath] =\n conditions.types === undefined && types !== undefined && fs.existsSync(path.join(packageDir, types))\n ? { types, ...conditions }\n : conditions;\n }\n return next;\n}\n\n/**\n * Everything the manifest can say about a package that its tree already says.\n *\n * The host blocks are pure restatements of the folder tree: which hosts\n * the package contributes to, at which scopes, and which frame types the\n * browser claims. Leaving them hand-written is what made a package declare the\n * same thing in six places, so the build writes them.\n *\n * Returns the manifest rather than writing it, so a caller can diff it and a\n * test can read it without a filesystem.\n */\nexport function syncManifest(input: ManifestSync): Record<string, unknown> {\n const { packageDir, graph, targets, pluginId } = input;\n const manifest = { ...input.manifest };\n\n if (manifest.exports !== undefined && typeof manifest.exports === 'object') {\n const exportsMap = withTypeConditions(packageDir, manifest.exports as Record<string, unknown>);\n // The browser bundle is a real entry point now that it is built rather\n // than shipped as source, so it is exported like the other two. Import\n // only: its own config emits ESM and no declarations, because nothing\n // imports it as a typed module.\n //\n // Added from the target rather than from the file existing, because this\n // runs on the node build's build:done and the browser build follows it.\n delete exportsMap[PI_EXPORT];\n if (targets.includes('web')) exportsMap[WEB_EXPORT] = { import: WEB_DIST };\n else delete exportsMap[WEB_EXPORT];\n if (targets.includes('native')) exportsMap[NATIVE_EXPORT] = { import: NATIVE_DIST };\n else delete exportsMap[NATIVE_EXPORT];\n manifest.exports = Object.fromEntries(\n Object.entries(exportsMap).sort(([left], [right]) =>\n left === './package.json' ? 1 : right === './package.json' ? -1 : left.localeCompare(right),\n ),\n );\n }\n\n delete manifest.pi;\n\n if (targets.includes('server')) {\n const contracts = fs.existsSync(path.join(packageDir, API_CONTRACTS_SOURCE))\n ? {\n contracts: {\n entry: `./${API_CONTRACTS_SOURCE}`,\n dist: `./dist/${API_CONTRACTS_ENTRY}.mjs`,\n },\n }\n : {};\n manifest.poServer = {\n entry: `./${GENERATED_DIR}/server.ts`,\n dist: './dist/extensions/server.mjs',\n scopes: serverScopes(graph),\n ...contracts,\n };\n } else delete manifest.poServer;\n\n if (targets.includes('web')) {\n const entries = frontendEntries(graph, 'web');\n manifest.poWeb = {\n pluginId,\n channels: channels(entries),\n // The built browser bundle, not the source. Its own tsdown config\n // targets a browser and leaves every bare specifier external, so the\n // cockpit resolves one React rather than bundling a copy per plugin.\n client: WEB_DIST,\n scopes: frontendScopes(entries),\n };\n } else delete manifest.poWeb;\n\n if (targets.includes('native')) {\n const entries = frontendEntries(graph, 'native');\n manifest.poNative = {\n pluginId,\n channels: channels(entries),\n // The bundled native half. Every bare specifier stays external, and the\n // CLI's composition build checks each one against the host module list.\n client: NATIVE_DIST,\n scopes: frontendScopes(entries),\n };\n } else delete manifest.poNative;\n\n return manifest;\n}\n\n/** Updates only the MCP-owned manifest block, leaving normal build metadata untouched. */\nexport function syncMcpManifest(\n manifest: Record<string, unknown>,\n enabled: boolean,\n widgets: readonly string[] = [],\n): Record<string, unknown> {\n const next = { ...manifest };\n if (enabled)\n next.poMcp = {\n entry: MCP_ENTRY,\n dist: MCP_DIST,\n scopes: ['session'],\n ...(widgets.length === 0\n ? {}\n : { ui: { entry: `./${GENERATED_DIR}/mcp-ui.ts`, dist: './dist/extensions/mcp-ui.mjs', widgets } }),\n };\n else delete next.poMcp;\n return next;\n}\n\n/** Writes the synced manifest when it differs, so a second build is a no-op. */\nexport function writeManifest(input: ManifestSync & { readonly targets: readonly BuildTarget[] }): boolean {\n const manifestPath = path.join(input.packageDir, 'package.json');\n const next = `${JSON.stringify(syncManifest(input), null, 2)}\\n`;\n if (fs.readFileSync(manifestPath, 'utf8') === next) return false;\n fs.writeFileSync(manifestPath, next);\n return true;\n}\n\n/** Writes only MCP metadata after an isolated build. */\nexport function writeMcpManifest(packageDir: string, enabled: boolean, widgets: readonly string[] = []): boolean {\n const manifestPath = path.join(packageDir, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as Record<string, unknown>;\n const next = `${JSON.stringify(syncMcpManifest(manifest, enabled, widgets), null, 2)}\\n`;\n if (fs.readFileSync(manifestPath, 'utf8') === next) return false;\n fs.writeFileSync(manifestPath, next);\n return true;\n}\n"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { BuildTarget } from "../resolveTarget/type.mjs";
2
+ import { ManifestSync } from "./type.mjs";
3
+ //#region src/services/syncManifest/index.d.ts
4
+ /**
5
+ * Everything the manifest can say about a package that its tree already says.
6
+ *
7
+ * The host blocks are pure restatements of the folder tree: which hosts
8
+ * the package contributes to, at which scopes, and which frame types the
9
+ * browser claims. Leaving them hand-written is what made a package declare the
10
+ * same thing in six places, so the build writes them.
11
+ *
12
+ * Returns the manifest rather than writing it, so a caller can diff it and a
13
+ * test can read it without a filesystem.
14
+ */
15
+ export declare function syncManifest(input: ManifestSync): Record<string, unknown>;
16
+ /** Writes the synced manifest when it differs, so a second build is a no-op. */
17
+ export declare function writeManifest(input: ManifestSync & {
18
+ readonly targets: readonly BuildTarget[];
19
+ }): boolean;
20
+ //#endregion
21
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/syncManifest/index.ts"],"mappings":";;;;;;;;;;;;;;wBA8GgB,aAAa,OAAO,eAAe;;wBA6FnC,cAAc,OAAO;WAA0B,kBAAkB"}
@@ -0,0 +1,2 @@
1
+ import{GENERATED_DIR as e}from"../../constants/layout.mjs";import{toSnake as t}from"../identity/index.mjs";import n from"node:fs";import r from"node:path";const i=[`global`,`workspace`,`session`],a=`apiContracts`,o=`src/exports/${a}.ts`,s=`./extensions/web`,c=`./dist/extensions/web.mjs`,l=`./extensions/native`,u=`./dist/extensions/native.mjs`,d=`./${e}/mcp.ts`;function f(e){let t=e.entries.filter(e=>e.side===`backend`);if(t.length===0)return[];let n=Math.min(...t.map(e=>i.indexOf(e.scope)));return i.slice(n)}function p(e,t){return e.entries.filter(e=>e.side===`frontend`&&e.platform===`native`==(t===`native`))}function m(e){let t=new Set(e.map(e=>e.scope));return i.filter(e=>t.has(e))}function h(e){return[...new Set(e.filter(e=>e.surface===`channel`).map(e=>t(e.name)))].sort()}function g(e,t){let i={};for(let[a,o]of Object.entries(t)){if(a===`./package.json`||typeof o!=`object`||!o){i[a]=o;continue}let t=o,s=t.import??t.default,c=s?.endsWith(`.mjs`)===!0?`${s.slice(0,-4)}.d.mts`:void 0;i[a]=t.types===void 0&&c!==void 0&&n.existsSync(r.join(e,c))?{types:c,...t}:t}return i}function _(t){let{packageDir:i,graph:d,targets:_,pluginId:v}=t,y={...t.manifest};if(y.exports!==void 0&&typeof y.exports==`object`){let e=g(i,y.exports);delete e[`./extensions/pi`],_.includes(`web`)?e[s]={import:c}:delete e[s],_.includes(`native`)?e[l]={import:u}:delete e[l],y.exports=Object.fromEntries(Object.entries(e).sort(([e],[t])=>e===`./package.json`?1:t===`./package.json`?-1:e.localeCompare(t)))}if(delete y.pi,_.includes(`server`)){let t=n.existsSync(r.join(i,o))?{contracts:{entry:`./${o}`,dist:`./dist/${a}.mjs`}}:{};y.poServer={entry:`./${e}/server.ts`,dist:`./dist/extensions/server.mjs`,scopes:f(d),...t}}else delete y.poServer;if(_.includes(`web`)){let e=p(d,`web`);y.poWeb={pluginId:v,channels:h(e),client:c,scopes:m(e)}}else delete y.poWeb;if(_.includes(`native`)){let e=p(d,`native`);y.poNative={pluginId:v,channels:h(e),client:u,scopes:m(e)}}else delete y.poNative;return y}function v(t,n,r=[]){let i={...t};return n?i.poMcp={entry:d,dist:`./dist/extensions/mcp.mjs`,scopes:[`session`],...r.length===0?{}:{ui:{entry:`./${e}/mcp-ui.ts`,dist:`./dist/extensions/mcp-ui.mjs`,widgets:r}}}:delete i.poMcp,i}function y(e){let t=r.join(e.packageDir,`package.json`),i=`${JSON.stringify(_(e),null,2)}\n`;return n.readFileSync(t,`utf8`)!==i&&(n.writeFileSync(t,i),!0)}function b(e,t,i=[]){let a=r.join(e,`package.json`),o=JSON.parse(n.readFileSync(a,`utf8`)),s=`${JSON.stringify(v(o,t,i),null,2)}\n`;return n.readFileSync(a,`utf8`)!==s&&(n.writeFileSync(a,s),!0)}export{_ as syncManifest,v as syncMcpManifest,y as writeManifest,b as writeMcpManifest};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["fs","path","SCOPE_ORDER","API_CONTRACTS_ENTRY","API_CONTRACTS_SOURCE","WEB_EXPORT","WEB_DIST","NATIVE_EXPORT","NATIVE_DIST","MCP_ENTRY","GENERATED_DIR","serverScopes","declared","graph","entry","shallowest","frontendEntries","host","frontendScopes","entries","scope","channels","toSnake","withTypeConditions","next","subpath","target","exportsMap","conditions","esm","types","packageDir","syncManifest","input","manifest","targets","left","right","contracts","syncMcpManifest","enabled","widgets","writeManifest","manifestPath","writeMcpManifest"],"sources":["../../../src/services/syncManifest/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GENERATED_DIR, NATIVE_PLATFORM } from '../../constants/layout';\nimport type { ExtensionEntry, ExtensionGraph, ExtensionScope } from '../../types/extensionGraph';\nimport { toSnake } from '../identity';\nimport type { BuildTarget } from '../resolveTarget/type';\nimport type { ManifestSync } from './type';\n\nconst SCOPE_ORDER: readonly ExtensionScope[] = ['global', 'workspace', 'session'];\n\nconst API_CONTRACTS_ENTRY = 'apiContracts';\nconst API_CONTRACTS_SOURCE = `src/exports/${API_CONTRACTS_ENTRY}.ts`;\n\n/** The retired terminal entry, removed from any manifest built before the terminal host was. */\nconst PI_EXPORT = './extensions/pi';\nconst WEB_EXPORT = './extensions/web';\nconst WEB_DIST = './dist/extensions/web.mjs';\nconst NATIVE_EXPORT = './extensions/native';\nconst NATIVE_DIST = './dist/extensions/native.mjs';\nconst MCP_DIST = './dist/extensions/mcp.mjs';\nconst MCP_ENTRY = `./${GENERATED_DIR}/mcp.ts`;\n\n/**\n * Which scopes a server facet declares.\n *\n * The generated facet cascades: a contribution declared at global registers at\n * global, workspace and session alike, because the server reads exactly one\n * scope declaration and nothing above it. So the declared set runs from the\n * shallowest contribution downward.\n */\nfunction serverScopes(graph: ExtensionGraph): ExtensionScope[] {\n const declared = graph.entries.filter((entry) => entry.side === 'backend');\n if (declared.length === 0) return [];\n const shallowest = Math.min(...declared.map((entry) => SCOPE_ORDER.indexOf(entry.scope)));\n return SCOPE_ORDER.slice(shallowest);\n}\n\n/**\n * The frontend files one frontend host reads.\n *\n * Native reads only `.native` files, and the cockpit never reads them, so the\n * two hosts' blocks describe disjoint halves of the same `(frontend)` side.\n */\nfunction frontendEntries(graph: ExtensionGraph, host: 'web' | 'native'): ExtensionEntry[] {\n return graph.entries.filter(\n (entry) => entry.side === 'frontend' && (entry.platform === NATIVE_PLATFORM) === (host === NATIVE_PLATFORM),\n );\n}\n\n/**\n * Which scopes a frontend plugin declares.\n *\n * The cockpit does not cascade the way the server does. It merges the scopes\n * it is given, so a plugin declares exactly the ones it contributes to. The\n * native app reuses the same registry, so the same holds there.\n */\nfunction frontendScopes(entries: readonly ExtensionEntry[]): ExtensionScope[] {\n const declared = new Set(entries.map((entry) => entry.scope));\n return SCOPE_ORDER.filter((scope) => declared.has(scope));\n}\n\n/** The frame types a frontend half claims, which its host reads before loading it. */\nfunction channels(entries: readonly ExtensionEntry[]): string[] {\n return [\n ...new Set(entries.filter((entry) => entry.surface === 'channel').map((entry) => toSnake(entry.name))),\n ].sort();\n}\n\n/**\n * Restores the `types` condition tsdown's exports generation omits.\n *\n * tsdown derives the map from the built chunks, which is what makes it follow\n * the folder tree. But its `genSubExport` only ever emits `import`, `require`\n * or `default`: a declaration chunk contributes nothing to a subpath, so only\n * the root gets a top-level `types`. TypeScript still resolves a sibling\n * `.d.mts` under node16 and bundler resolution, but publint and attw read the\n * condition, so it belongs in the map.\n */\nfunction withTypeConditions(packageDir: string, exportsMap: Record<string, unknown>): Record<string, unknown> {\n const next: Record<string, unknown> = {};\n for (const [subpath, target] of Object.entries(exportsMap)) {\n if (subpath === './package.json' || target === null || typeof target !== 'object') {\n next[subpath] = target;\n continue;\n }\n const conditions = target as Record<string, string>;\n const esm = conditions.import ?? conditions.default;\n const types = esm?.endsWith('.mjs') === true ? `${esm.slice(0, -'.mjs'.length)}.d.mts` : undefined;\n // Ahead of import and require: a resolver takes the first condition it\n // matches, so a types-aware one must not fall through to the runtime file.\n next[subpath] =\n conditions.types === undefined && types !== undefined && fs.existsSync(path.join(packageDir, types))\n ? { types, ...conditions }\n : conditions;\n }\n return next;\n}\n\n/**\n * Everything the manifest can say about a package that its tree already says.\n *\n * The host blocks are pure restatements of the folder tree: which hosts\n * the package contributes to, at which scopes, and which frame types the\n * browser claims. Leaving them hand-written is what made a package declare the\n * same thing in six places, so the build writes them.\n *\n * Returns the manifest rather than writing it, so a caller can diff it and a\n * test can read it without a filesystem.\n */\nexport function syncManifest(input: ManifestSync): Record<string, unknown> {\n const { packageDir, graph, targets, pluginId } = input;\n const manifest = { ...input.manifest };\n\n if (manifest.exports !== undefined && typeof manifest.exports === 'object') {\n const exportsMap = withTypeConditions(packageDir, manifest.exports as Record<string, unknown>);\n // The browser bundle is a real entry point now that it is built rather\n // than shipped as source, so it is exported like the other two. Import\n // only: its own config emits ESM and no declarations, because nothing\n // imports it as a typed module.\n //\n // Added from the target rather than from the file existing, because this\n // runs on the node build's build:done and the browser build follows it.\n delete exportsMap[PI_EXPORT];\n if (targets.includes('web')) exportsMap[WEB_EXPORT] = { import: WEB_DIST };\n else delete exportsMap[WEB_EXPORT];\n if (targets.includes('native')) exportsMap[NATIVE_EXPORT] = { import: NATIVE_DIST };\n else delete exportsMap[NATIVE_EXPORT];\n manifest.exports = Object.fromEntries(\n Object.entries(exportsMap).sort(([left], [right]) =>\n left === './package.json' ? 1 : right === './package.json' ? -1 : left.localeCompare(right),\n ),\n );\n }\n\n delete manifest.pi;\n\n if (targets.includes('server')) {\n const contracts = fs.existsSync(path.join(packageDir, API_CONTRACTS_SOURCE))\n ? {\n contracts: {\n entry: `./${API_CONTRACTS_SOURCE}`,\n dist: `./dist/${API_CONTRACTS_ENTRY}.mjs`,\n },\n }\n : {};\n manifest.poServer = {\n entry: `./${GENERATED_DIR}/server.ts`,\n dist: './dist/extensions/server.mjs',\n scopes: serverScopes(graph),\n ...contracts,\n };\n } else delete manifest.poServer;\n\n if (targets.includes('web')) {\n const entries = frontendEntries(graph, 'web');\n manifest.poWeb = {\n pluginId,\n channels: channels(entries),\n // The built browser bundle, not the source. Its own tsdown config\n // targets a browser and leaves every bare specifier external, so the\n // cockpit resolves one React rather than bundling a copy per plugin.\n client: WEB_DIST,\n scopes: frontendScopes(entries),\n };\n } else delete manifest.poWeb;\n\n if (targets.includes('native')) {\n const entries = frontendEntries(graph, 'native');\n manifest.poNative = {\n pluginId,\n channels: channels(entries),\n // The bundled native half. Every bare specifier stays external, and the\n // CLI's composition build checks each one against the host module list.\n client: NATIVE_DIST,\n scopes: frontendScopes(entries),\n };\n } else delete manifest.poNative;\n\n return manifest;\n}\n\n/** Updates only the MCP-owned manifest block, leaving normal build metadata untouched. */\nexport function syncMcpManifest(\n manifest: Record<string, unknown>,\n enabled: boolean,\n widgets: readonly string[] = [],\n): Record<string, unknown> {\n const next = { ...manifest };\n if (enabled)\n next.poMcp = {\n entry: MCP_ENTRY,\n dist: MCP_DIST,\n scopes: ['session'],\n ...(widgets.length === 0\n ? {}\n : { ui: { entry: `./${GENERATED_DIR}/mcp-ui.ts`, dist: './dist/extensions/mcp-ui.mjs', widgets } }),\n };\n else delete next.poMcp;\n return next;\n}\n\n/** Writes the synced manifest when it differs, so a second build is a no-op. */\nexport function writeManifest(input: ManifestSync & { readonly targets: readonly BuildTarget[] }): boolean {\n const manifestPath = path.join(input.packageDir, 'package.json');\n const next = `${JSON.stringify(syncManifest(input), null, 2)}\\n`;\n if (fs.readFileSync(manifestPath, 'utf8') === next) return false;\n fs.writeFileSync(manifestPath, next);\n return true;\n}\n\n/** Writes only MCP metadata after an isolated build. */\nexport function writeMcpManifest(packageDir: string, enabled: boolean, widgets: readonly string[] = []): boolean {\n const manifestPath = path.join(packageDir, 'package.json');\n const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as Record<string, unknown>;\n const next = `${JSON.stringify(syncMcpManifest(manifest, enabled, widgets), null, 2)}\\n`;\n if (fs.readFileSync(manifestPath, 'utf8') === next) return false;\n fs.writeFileSync(manifestPath, next);\n return true;\n}\n"],"mappings":"2JASA,MAAME,EAAyC,CAAC,SAAU,YAAa,SAAS,EAE1EC,EAAsB,eACtBC,EAAuB,eAAeD,EAAoB,KAI1DE,EAAa,mBACbC,EAAW,4BACXC,EAAgB,sBAChBC,EAAc,+BAEdC,EAAY,KAAKC,EAAc,SAUrC,SAASC,EAAa,EAAyC,CAC7D,IAAMC,EAAWC,EAAM,QAAQ,OAAQ,GAAUC,EAAM,OAAS,SAAS,EACzE,GAAIF,EAAS,SAAW,EAAG,MAAO,CAAC,EACnC,IAAMG,EAAa,KAAK,IAAI,GAAGH,EAAS,IAAK,GAAUV,EAAY,QAAQY,EAAM,KAAK,CAAC,CAAC,EACxF,OAAOZ,EAAY,MAAMa,CAAU,CACrC,CAQA,SAASC,EAAgB,EAAuB,EAA0C,CACxF,OAAOH,EAAM,QAAQ,OAClB,GAAUC,EAAM,OAAS,YAAeA,EAAM,WAAA,WAAmCG,IAAA,SACpF,CACF,CASA,SAASC,EAAe,EAAsD,CAC5E,IAAMN,EAAW,IAAI,IAAIO,EAAQ,IAAK,GAAUL,EAAM,KAAK,CAAC,EAC5D,OAAOZ,EAAY,OAAQ,GAAUU,EAAS,IAAIQ,CAAK,CAAC,CAC1D,CAGA,SAASC,EAAS,EAA8C,CAC9D,MAAO,CACL,GAAG,IAAI,IAAIF,EAAQ,OAAQ,GAAUL,EAAM,UAAY,SAAS,CAAC,CAAC,IAAK,GAAUQ,EAAQR,EAAM,IAAI,CAAC,CAAC,CACvG,CAAC,CAAC,KAAK,CACT,CAYA,SAASS,EAAmB,EAAoB,EAA8D,CAC5G,IAAMC,EAAgC,CAAC,EACvC,IAAK,GAAM,CAACC,EAASC,KAAW,OAAO,QAAQC,CAAU,EAAG,CAC1D,GAAIF,IAAY,kBAAuC,OAAOC,GAAW,WAArCA,EAA+C,CACjF,EAAKD,GAAWC,EAChB,QACF,CACA,IAAME,EAAaF,EACbG,EAAMD,EAAW,QAAUA,EAAW,QACtCE,EAAQD,GAAK,SAAS,MAAM,IAAM,GAAO,GAAGA,EAAI,MAAM,EAAG,EAAc,EAAE,QAAU,IAAA,GAGzF,EAAKJ,GACHG,EAAW,QAAU,IAAA,IAAaE,IAAU,IAAA,IAAa9B,EAAG,WAAWC,EAAK,KAAK8B,EAAYD,CAAK,CAAC,EAC/F,CAAE,QAAO,GAAGF,CAAW,EACvBA,CACR,CACA,OAAOJ,CACT,CAaA,SAAgBQ,EAAa,EAA8C,CACzE,GAAM,CAAE,aAAY,QAAO,UAAS,YAAaC,EAC3CC,EAAW,CAAE,GAAGD,EAAM,QAAS,EAErC,GAAIC,EAAS,UAAY,IAAA,IAAa,OAAOA,EAAS,SAAY,SAAU,CAC1E,IAAMP,EAAaJ,EAAmBQ,EAAYG,EAAS,OAAkC,EAQ7F,OAAOP,EAAW,mBACdQ,EAAQ,SAAS,KAAK,EAAG,EAAW9B,GAAc,CAAE,OAAQC,CAAS,EACpE,OAAOqB,EAAWtB,GACnB8B,EAAQ,SAAS,QAAQ,EAAG,EAAW5B,GAAiB,CAAE,OAAQC,CAAY,EAC7E,OAAOmB,EAAWpB,GACvB,EAAS,QAAU,OAAO,YACxB,OAAO,QAAQoB,CAAU,CAAC,CAAC,MAAM,CAACS,GAAO,CAACC,KACxCD,IAAS,iBAAmB,EAAIC,IAAU,iBAAmB,GAAKD,EAAK,cAAcC,CAAK,CAC5F,CACF,CACF,CAIA,GAFA,OAAOH,EAAS,GAEZC,EAAQ,SAAS,QAAQ,EAAG,CAC9B,IAAMG,EAAYtC,EAAG,WAAWC,EAAK,KAAK8B,EAAY3B,CAAoB,CAAC,EACvE,CACE,UAAW,CACT,MAAO,KAAKA,IACZ,KAAM,UAAUD,EAAoB,KACtC,CACF,EACA,CAAC,EACL,EAAS,SAAW,CAClB,MAAO,KAAKO,EAAc,YAC1B,KAAM,+BACN,OAAQC,EAAaE,CAAK,EAC1B,GAAGyB,CACL,CACF,MAAO,OAAOJ,EAAS,SAEvB,GAAIC,EAAQ,SAAS,KAAK,EAAG,CAC3B,IAAMhB,EAAUH,EAAgBH,EAAO,KAAK,EAC5C,EAAS,MAAQ,CACf,WACA,SAAUQ,EAASF,CAAO,EAI1B,OAAQb,EACR,OAAQY,EAAeC,CAAO,CAChC,CACF,MAAO,OAAOe,EAAS,MAEvB,GAAIC,EAAQ,SAAS,QAAQ,EAAG,CAC9B,IAAMhB,EAAUH,EAAgBH,EAAO,QAAQ,EAC/C,EAAS,SAAW,CAClB,WACA,SAAUQ,EAASF,CAAO,EAG1B,OAAQX,EACR,OAAQU,EAAeC,CAAO,CAChC,CACF,MAAO,OAAOe,EAAS,SAEvB,OAAOA,CACT,CAGA,SAAgBK,EACd,EACA,EACA,EAA6B,CAAC,EACL,CACzB,IAAMf,EAAO,CAAE,GAAGU,CAAS,EAW3B,OAVIM,EACF,EAAK,MAAQ,CACX,MAAO/B,EACP,KAAM,4BACN,OAAQ,CAAC,SAAS,EAClB,GAAIgC,EAAQ,SAAW,EACnB,CAAC,EACD,CAAE,GAAI,CAAE,MAAO,KAAK/B,EAAc,YAAa,KAAM,+BAAgC,SAAQ,CAAE,CACrG,EACG,OAAOc,EAAK,MACVA,CACT,CAGA,SAAgBkB,EAAc,EAA6E,CACzG,IAAMC,EAAe1C,EAAK,KAAKgC,EAAM,WAAY,cAAc,EACzDT,EAAO,GAAG,KAAK,UAAUQ,EAAaC,CAAK,EAAG,KAAM,CAAC,EAAE,IAG7D,OAFIjC,EAAG,aAAa2C,EAAc,MAAM,IAAMnB,IAC9CxB,EAAG,cAAc2C,EAAcnB,CAAI,EAC5B,GACT,CAGA,SAAgBoB,EAAiB,EAAoB,EAAkB,EAA6B,CAAC,EAAY,CAC/G,IAAMD,EAAe1C,EAAK,KAAK8B,EAAY,cAAc,EACnDG,EAAW,KAAK,MAAMlC,EAAG,aAAa2C,EAAc,MAAM,CAAC,EAC3DnB,EAAO,GAAG,KAAK,UAAUe,EAAgBL,EAAUM,EAASC,CAAO,EAAG,KAAM,CAAC,EAAE,IAGrF,OAFIzC,EAAG,aAAa2C,EAAc,MAAM,IAAMnB,IAC9CxB,EAAG,cAAc2C,EAAcnB,CAAI,EAC5B,GACT"}
@@ -0,0 +1,15 @@
1
+ import { ExtensionGraph } from "../../types/extensionGraph.mjs";
2
+ import { BuildTarget } from "../resolveTarget/type.mjs";
3
+ //#region src/services/syncManifest/type.d.ts
4
+ /** Everything deriving a manifest needs, so nothing has to rescan. */
5
+ export interface ManifestSync {
6
+ readonly packageDir: string;
7
+ /** The manifest as read, which the sync returns a copy of. */
8
+ readonly manifest: Record<string, unknown>;
9
+ readonly graph: ExtensionGraph;
10
+ /** Hosts this package contributes to, as the generator resolved them. */
11
+ readonly targets: readonly BuildTarget[];
12
+ readonly pluginId: string;
13
+ }
14
+ //#endregion
15
+ //# sourceMappingURL=type.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.d.mts","names":[],"sources":["../../../src/services/syncManifest/type.ts"],"mappings":";;;;iBAIiB;WACN;;WAEA,UAAU;WACV,OAAO;;WAEP,kBAAkB;WAClB"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../constants/layout.cjs"),n=require("../renderMcpWidgets/index.cjs"),r=require("../generate/index.cjs"),i=require("../syncManifest/index.cjs"),a=require("../unistylesBabel/index.cjs");let o=require("node:fs");o=e.__toESM(o,1);let s=require("node:path");s=e.__toESM(s,1);const c=`apiContracts`;function l(e,t){let n=s.default.join(e,t);if(!o.default.existsSync(n))return{};let r={};for(let e of o.default.readdirSync(n)){if(!e.endsWith(`.ts`)||e.endsWith(`.d.ts`))continue;let n=e.slice(0,-3);r[n]=`${t}/${e}`}return r}function u(e,t,n){let r=s.default.join(e,t);if(!o.default.existsSync(r))return{};let i={};for(let e of o.default.readdirSync(r,{encoding:`utf8`,recursive:!0})){if(!n(e))continue;let r=e.split(s.default.sep).join(`/`);i[`./${t}/${r}`]=`./${t}/${r}`}return i}function d(e){let t=JSON.parse(o.default.readFileSync(s.default.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{...u(e,`skills`,e=>s.default.basename(e)===`SKILL.md`),...u(e,`themes`,e=>e.endsWith(`.json`)),...n}}function f(){return{name:`po-query-assets`,resolveId(e,t){let n=e.indexOf(`?`);if(n<0||t===void 0||!e.startsWith(`.`))return;let r=t.split(`?`)[0];return`${s.default.resolve(s.default.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:s.default.basename(n,s.default.extname(n))}:{type:`asset`,name:s.default.basename(n),source:o.default.readFileSync(n)})};`}}}function p(e=`web`){return{entry:{[`extensions/${e}`]:`${t.GENERATED_DIR}/${e}.ts`},clean:!1,dts:!1,exports:!1,format:[`esm`],platform:`browser`,fixedExtension:!0,external:e=>!e.startsWith(`.`)&&!s.default.isAbsolute(e),plugins:[f()],sourcemap:!0,unbundle:!1,tsconfig:e===`web`?`tsconfig.web.json`:`tsconfig.mcp.json`}}function m(e){return{entry:{"extensions/native":`${t.GENERATED_DIR}/native.ts`},clean:!1,dts:!1,exports:!1,format:[`esm`],platform:`neutral`,fixedExtension:!0,external:e=>!e.startsWith(`.`)&&!s.default.isAbsolute(e),plugins:[a.unistylesBabel(e)],sourcemap:!0,unbundle:!1,tsconfig:`tsconfig.native.json`}}function h(e){let t=s.default.join(e,`dist`);if(o.default.existsSync(t))for(let e of o.default.readdirSync(t,{recursive:!0,encoding:`utf8`})){let n=e.split(s.default.sep).join(`/`);if(!/^(?:extensions\/mcp(?:-ui)?|.+\.mcp)\.(?:mjs|cjs|d\.mts|d\.cts)(?:\.map)?$/u.test(n))continue;let r=s.default.join(t,e);o.default.lstatSync(r).isFile()&&o.default.rmSync(r)}}function g(e={packageDir:process.cwd()}){let a=e.packageDir??process.cwd(),u=r.generateExtension({...e,packageDir:a,check:e.check??!1});for(let e of u.notices)process.stderr.write(`[po-build] ${e.path}: ${e.message}\n`);let f=e.target===`mcp`;f&&!e.check&&h(a);let g=f&&!u.targets.includes(`mcp`),_=`\0po-empty-mcp`;g&&!e.check&&i.writeMcpManifest(a,!1);let v=f?u.targets.includes(`mcp`)?{"extensions/mcp":`${t.GENERATED_DIR}/mcp.ts`}:{"extensions/mcp":_}:{...l(a,e.exportsDir??`src/exports`),...u.targets.includes(`server`)?{"extensions/server":`${t.GENERATED_DIR}/server.ts`}:{},...e.entry},y={clean:!1,dts:!g&&{incremental:!0,parallel:!1,eager:!0},...g?{write:!1,plugins:[{name:`po-empty-mcp`,resolveId:e=>e===_?_:void 0,load:e=>e===_?`export {};`:void 0}]}:{},exports:!f&&{customExports:e=>{let t=Object.keys(v),n=t[0];return{...d(a),...e,...t.length===1&&n!==void 0&&n!==`index`&&!n.startsWith(`extensions/`)?{[`./${n}`]: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=(e,r)=>({...y,entry:e,unbundle:!1,hooks:{"build:done":()=>{if(f){i.writeMcpManifest(a,u.targets.includes(`mcp`),n.renderMcpWidgets(u.graph,u.packageName,t.GENERATED_DIR).names);return}i.writeManifest({packageDir:a,manifest:JSON.parse(o.default.readFileSync(s.default.join(a,`package.json`),`utf8`)),graph:u.graph,targets:u.targets,pluginId:u.pluginId})}}}),x=!f&&c in v&&Object.keys(v).length>1?[((e,t)=>({...y,entry:e,unbundle:t}))({[c]:v[c]},!1),b(Object.fromEntries(Object.entries(v).filter(([e])=>e!==c)),!0)]:[b(v,!f)];if(f&&u.files.has(`generated/mcp-ui.ts`))return[...x,p(`mcp-ui`)];let S=f?[]:[...u.targets.includes(`web`)?[p()]:[],...u.targets.includes(`native`)?[m(a)]:[]];if(S.length>0){if(Object.keys(v).length>0)return[...x,...S];let[e,...t]=S;return[{...e,hooks:x[0].hooks},...t]}return x.length===1?x[0]:x}exports.poExtension=g;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["path","fs","GENERATED_DIR","unistylesBabel","generateExtension","renderMcpWidgets","require_runtime","require_layout","require_index","require_index$1","require_index$2","require_index$3","node_fs","node_path","API_CONTRACTS_ENTRY","exportEntries","absolute","packageDir","exportsDir","entries","file","stem","resourceEntries","root","directory","relative","matches","normalized","staticExports","manifest","declared","target","queryAssetPlugin","queryAt","source","importer","importerFile","id","query","worker","browserConfig","nativeConfig","cleanMcpOutput","dist","poExtension","options","result","notice","mcpOnly","emptyMcp","emptyEntry","entry","baseNode","names","name","generated","format","node","configs","frontends","first","rest"],"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';\nimport { unistylesBabel } from '../unistylesBabel';\nimport type { UnistylesBabelPlugin } from '../unistylesBabel/type';\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 hooks?: PresetConfig['hooks'];\n}\n\n/** The separately bundled native half, shaped like the browser one. */\ninterface NativePresetConfig extends Omit<BrowserPresetConfig, 'platform' | 'plugins'> {\n platform: 'neutral';\n plugins: UnistylesBabelPlugin[];\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: 'po-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/**\n * The separately bundled native half of a routed extension.\n *\n * Neutral rather than browser: React Native has neither the DOM nor Node, so\n * no condition set fits it better, and every bare specifier stays external\n * anyway. The CLI bundles this module into the signed composition and refuses\n * any import outside the native host module list, so nothing here has to.\n */\nfunction nativeConfig(packageDir: string): NativePresetConfig {\n return {\n entry: { 'extensions/native': `${GENERATED_DIR}/native.ts` },\n clean: false,\n dts: false,\n exports: false,\n format: ['esm'],\n platform: 'neutral',\n fixedExtension: true,\n // The same predicate as the browser half, for the same reason: the host\n // supplies React, React Native and Unistyles, one copy each.\n external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id),\n plugins: [unistylesBabel(packageDir)],\n sourcemap: true,\n unbundle: false,\n // Native routes import react-native, which the DOM-typed web config must\n // not see, so they typecheck under their own config.\n tsconfig: 'tsconfig.native.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 poExtension(\n options: ExtensionPresetOptions = { packageDir: process.cwd() },\n): PresetConfig | (PresetConfig | BrowserPresetConfig | NativePresetConfig)[] {\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(`[po-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 = '\\0po-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('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: 'po-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) => {\n const names = Object.keys(entry);\n const name = names[0];\n return {\n ...staticExports(packageDir),\n ...generated,\n ...(names.length === 1 && name !== undefined && name !== 'index' && !name.startsWith('extensions/')\n ? { [`./${name}`]: generated['.'] }\n : {}),\n };\n },\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 // Native server entries use 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 && Object.keys(entry).length > 1\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 const frontends: (BrowserPresetConfig | NativePresetConfig)[] = mcpOnly\n ? []\n : [\n ...(result.targets.includes('web') ? [browserConfig()] : []),\n ...(result.targets.includes('native') ? [nativeConfig(packageDir)] : []),\n ];\n if (frontends.length > 0) {\n if (Object.keys(entry).length > 0) return [...configs, ...frontends];\n // No node entry to carry the manifest sync, so the first frontend build does.\n const [first, ...rest] = frontends;\n return [{ ...first!, hooks: configs[0]!.hooks }, ...rest];\n }\n return configs.length === 1 ? configs[0]! : configs;\n}\n"],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import { GenerateOptions } from "../generate/type.mjs";
2
+ import { UnistylesBabelPlugin } from "../unistylesBabel/type.mjs";
3
+ //#region src/services/tsdownPreset/index.d.ts
4
+ interface EmittedFile {
5
+ readonly type: 'asset' | 'chunk';
6
+ readonly id?: string;
7
+ readonly name: string;
8
+ readonly source?: Buffer;
9
+ }
10
+ interface QueryAssetPlugin {
11
+ readonly name: string;
12
+ resolveId(source: string, importer: string | undefined): string | undefined;
13
+ load(this: {
14
+ emitFile(file: EmittedFile): string;
15
+ }, id: string): string | undefined;
16
+ }
17
+ /** The shape a tsdown config needs from this preset, without importing tsdown's types. */
18
+ interface BrowserPresetConfig {
19
+ entry: Record<string, string>;
20
+ clean: boolean;
21
+ dts: false;
22
+ exports: boolean;
23
+ format: 'esm'[];
24
+ platform: 'browser';
25
+ fixedExtension: boolean;
26
+ external: (id: string) => boolean;
27
+ plugins: QueryAssetPlugin[];
28
+ sourcemap: boolean;
29
+ unbundle: boolean;
30
+ tsconfig: string;
31
+ hooks?: PresetConfig['hooks'];
32
+ }
33
+ /** The separately bundled native half, shaped like the browser one. */
34
+ interface NativePresetConfig extends Omit<BrowserPresetConfig, 'platform' | 'plugins'> {
35
+ platform: 'neutral';
36
+ plugins: UnistylesBabelPlugin[];
37
+ }
38
+ interface GeneratedExports {
39
+ customExports: (exports: Record<string, unknown>) => Record<string, unknown>;
40
+ }
41
+ interface BasePresetConfig {
42
+ entry: Record<string, string>;
43
+ clean: boolean;
44
+ dts: false | {
45
+ incremental: boolean;
46
+ parallel: boolean;
47
+ eager: boolean;
48
+ };
49
+ write?: false;
50
+ plugins?: QueryAssetPlugin[];
51
+ exports: boolean | GeneratedExports;
52
+ format: {
53
+ esm: Record<string, never>;
54
+ cjs: {
55
+ dts: false;
56
+ };
57
+ };
58
+ platform: 'node';
59
+ sourcemap: boolean;
60
+ unbundle: boolean;
61
+ outExtensions: ({ format }: {
62
+ format: string;
63
+ }) => {
64
+ js: string;
65
+ dts: string;
66
+ };
67
+ }
68
+ interface PresetConfig extends BasePresetConfig {
69
+ hooks?: {
70
+ 'build:done': () => void;
71
+ };
72
+ }
73
+ /**
74
+ * One tsdown config for a folder-routed extension.
75
+ *
76
+ * Entries come from the tree rather than a hand-written list, and the manifest
77
+ * blocks that restate the tree are written after the build rather than kept in
78
+ * step by hand. Browser contributions are emitted through a second bundled
79
+ * config because their routed source is not published.
80
+ */
81
+ export interface ExtensionPresetOptions extends GenerateOptions {
82
+ /** Extra tsdown entries beyond the derived ones. */
83
+ readonly entry?: Record<string, string>;
84
+ /** Public export modules, package-relative. Defaults to `src/exports`. */
85
+ readonly exportsDir?: string;
86
+ }
87
+ export declare function poExtension(options?: ExtensionPresetOptions): PresetConfig | (PresetConfig | BrowserPresetConfig | NativePresetConfig)[];
88
+ //#endregion
89
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/tsdownPreset/index.ts"],"mappings":";;;UAWU;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;EACA,QAAQ;;;UAIA,2BAA2B,KAAK;EACxC;EACA,SAAS;;UAGD;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;;wBAiHK,YACd,UAAS,yBACR,gBAAgB,eAAe,sBAAsB"}
@@ -0,0 +1,2 @@
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{unistylesBabel as a}from"../unistylesBabel/index.mjs";import o from"node:fs";import s from"node:path";const c=`apiContracts`;function l(e,t){let n=s.join(e,t);if(!o.existsSync(n))return{};let r={};for(let e of o.readdirSync(n)){if(!e.endsWith(`.ts`)||e.endsWith(`.d.ts`))continue;let n=e.slice(0,-3);r[n]=`${t}/${e}`}return r}function u(e,t,n){let r=s.join(e,t);if(!o.existsSync(r))return{};let i={};for(let e of o.readdirSync(r,{encoding:`utf8`,recursive:!0})){if(!n(e))continue;let r=e.split(s.sep).join(`/`);i[`./${t}/${r}`]=`./${t}/${r}`}return i}function d(e){let t=JSON.parse(o.readFileSync(s.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{...u(e,`skills`,e=>s.basename(e)===`SKILL.md`),...u(e,`themes`,e=>e.endsWith(`.json`)),...n}}function f(){return{name:`po-query-assets`,resolveId(e,t){let n=e.indexOf(`?`);if(n<0||t===void 0||!e.startsWith(`.`))return;let r=t.split(`?`)[0];return`${s.resolve(s.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:s.basename(n,s.extname(n))}:{type:`asset`,name:s.basename(n),source:o.readFileSync(n)})};`}}}function p(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(`.`)&&!s.isAbsolute(e),plugins:[f()],sourcemap:!0,unbundle:!1,tsconfig:t===`web`?`tsconfig.web.json`:`tsconfig.mcp.json`}}function m(t){return{entry:{"extensions/native":`${e}/native.ts`},clean:!1,dts:!1,exports:!1,format:[`esm`],platform:`neutral`,fixedExtension:!0,external:e=>!e.startsWith(`.`)&&!s.isAbsolute(e),plugins:[a(t)],sourcemap:!0,unbundle:!1,tsconfig:`tsconfig.native.json`}}function h(e){let t=s.join(e,`dist`);if(o.existsSync(t))for(let e of o.readdirSync(t,{recursive:!0,encoding:`utf8`})){let n=e.split(s.sep).join(`/`);if(!/^(?:extensions\/mcp(?:-ui)?|.+\.mcp)\.(?:mjs|cjs|d\.mts|d\.cts)(?:\.map)?$/u.test(n))continue;let r=s.join(t,e);o.lstatSync(r).isFile()&&o.rmSync(r)}}function g(a={packageDir:process.cwd()}){let u=a.packageDir??process.cwd(),f=n({...a,packageDir:u,check:a.check??!1});for(let e of f.notices)process.stderr.write(`[po-build] ${e.path}: ${e.message}\n`);let g=a.target===`mcp`;g&&!a.check&&h(u);let _=g&&!f.targets.includes(`mcp`),v=`\0po-empty-mcp`;_&&!a.check&&i(u,!1);let y=g?f.targets.includes(`mcp`)?{"extensions/mcp":`${e}/mcp.ts`}:{"extensions/mcp":v}:{...l(u,a.exportsDir??`src/exports`),...f.targets.includes(`server`)?{"extensions/server":`${e}/server.ts`}:{},...a.entry},b={clean:!1,dts:!_&&{incremental:!0,parallel:!1,eager:!0},..._?{write:!1,plugins:[{name:`po-empty-mcp`,resolveId:e=>e===v?v:void 0,load:e=>e===v?`export {};`:void 0}]}:{},exports:!g&&{customExports:e=>{let t=Object.keys(y),n=t[0];return{...d(u),...e,...t.length===1&&n!==void 0&&n!==`index`&&!n.startsWith(`extensions/`)?{[`./${n}`]: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},x=(n,a)=>({...b,entry:n,unbundle:!1,hooks:{"build:done":()=>{if(g){i(u,f.targets.includes(`mcp`),t(f.graph,f.packageName,e).names);return}r({packageDir:u,manifest:JSON.parse(o.readFileSync(s.join(u,`package.json`),`utf8`)),graph:f.graph,targets:f.targets,pluginId:f.pluginId})}}}),S=!g&&c in y&&Object.keys(y).length>1?[((e,t)=>({...b,entry:e,unbundle:t}))({[c]:y[c]},!1),x(Object.fromEntries(Object.entries(y).filter(([e])=>e!==c)),!0)]:[x(y,!g)];if(g&&f.files.has(`generated/mcp-ui.ts`))return[...S,p(`mcp-ui`)];let C=g?[]:[...f.targets.includes(`web`)?[p()]:[],...f.targets.includes(`native`)?[m(u)]:[]];if(C.length>0){if(Object.keys(y).length>0)return[...S,...C];let[e,...t]=C;return[{...e,hooks:S[0].hooks},...t]}return S.length===1?S[0]:S}export{g as poExtension};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["fs","path","API_CONTRACTS_ENTRY","exportEntries","absolute","packageDir","exportsDir","entries","file","stem","resourceEntries","root","directory","relative","matches","normalized","staticExports","manifest","declared","target","queryAssetPlugin","queryAt","source","importer","importerFile","id","query","worker","browserConfig","GENERATED_DIR","nativeConfig","unistylesBabel","cleanMcpOutput","dist","poExtension","options","result","generateExtension","notice","mcpOnly","emptyMcp","emptyEntry","writeMcpManifest","entry","baseNode","names","name","generated","format","node","renderMcpWidgets","writeManifest","configs","frontends","first","rest"],"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';\nimport { unistylesBabel } from '../unistylesBabel';\nimport type { UnistylesBabelPlugin } from '../unistylesBabel/type';\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 hooks?: PresetConfig['hooks'];\n}\n\n/** The separately bundled native half, shaped like the browser one. */\ninterface NativePresetConfig extends Omit<BrowserPresetConfig, 'platform' | 'plugins'> {\n platform: 'neutral';\n plugins: UnistylesBabelPlugin[];\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: 'po-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/**\n * The separately bundled native half of a routed extension.\n *\n * Neutral rather than browser: React Native has neither the DOM nor Node, so\n * no condition set fits it better, and every bare specifier stays external\n * anyway. The CLI bundles this module into the signed composition and refuses\n * any import outside the native host module list, so nothing here has to.\n */\nfunction nativeConfig(packageDir: string): NativePresetConfig {\n return {\n entry: { 'extensions/native': `${GENERATED_DIR}/native.ts` },\n clean: false,\n dts: false,\n exports: false,\n format: ['esm'],\n platform: 'neutral',\n fixedExtension: true,\n // The same predicate as the browser half, for the same reason: the host\n // supplies React, React Native and Unistyles, one copy each.\n external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id),\n plugins: [unistylesBabel(packageDir)],\n sourcemap: true,\n unbundle: false,\n // Native routes import react-native, which the DOM-typed web config must\n // not see, so they typecheck under their own config.\n tsconfig: 'tsconfig.native.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 poExtension(\n options: ExtensionPresetOptions = { packageDir: process.cwd() },\n): PresetConfig | (PresetConfig | BrowserPresetConfig | NativePresetConfig)[] {\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(`[po-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 = '\\0po-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('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: 'po-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) => {\n const names = Object.keys(entry);\n const name = names[0];\n return {\n ...staticExports(packageDir),\n ...generated,\n ...(names.length === 1 && name !== undefined && name !== 'index' && !name.startsWith('extensions/')\n ? { [`./${name}`]: generated['.'] }\n : {}),\n };\n },\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 // Native server entries use 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 && Object.keys(entry).length > 1\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 const frontends: (BrowserPresetConfig | NativePresetConfig)[] = mcpOnly\n ? []\n : [\n ...(result.targets.includes('web') ? [browserConfig()] : []),\n ...(result.targets.includes('native') ? [nativeConfig(packageDir)] : []),\n ];\n if (frontends.length > 0) {\n if (Object.keys(entry).length > 0) return [...configs, ...frontends];\n // No node entry to carry the manifest sync, so the first frontend build does.\n const [first, ...rest] = frontends;\n return [{ ...first!, hooks: configs[0]!.hooks }, ...rest];\n }\n return configs.length === 1 ? configs[0]! : configs;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("node:module");const i=`@babel/core`,a=`react-native-unistyles/plugin`,o=[`.ts`,`.tsx`];function s(e){let t=(0,r.createRequire)(n.default.join(e,`package.json`));try{let e=t(i),n=t(a),r=typeof n==`function`?n:n?.default;if(typeof r!=`function`)throw Error(`${a} does not export a Babel plugin`);return{babel:e,plugin:r}}catch(t){let n=t instanceof Error?t.message:String(t);throw Error(`[po-build] The native build needs ${i} and react-native-unistyles, resolved from ${e}. Add both to that package's devDependencies. ${n}`)}}function c(e){if(e.startsWith(`\0`))return;let t=e.indexOf(`?`);return t<0?e:e.slice(0,t)}function l(e,t){if(e.endsWith(`.d.ts`)||!o.includes(n.default.extname(e)))return!1;let r=n.default.relative(t,e);return r!==``&&!r.startsWith(`..`)&&!n.default.isAbsolute(r)}function u(e){let r=t.default.realpathSync(e),i=n.default.join(r,`src`),a;return{name:`po-unistyles-babel`,async transform(e,t){let n=c(t);if(n===void 0||!l(n,i)||!e.includes(`react-native`))return;a??=s(r);let o=await a.babel.transformAsync(e,{filename:n,cwd:r,root:r,babelrc:!1,configFile:!1,sourceMaps:!0,parserOpts:{plugins:[`typescript`,`jsx`]},plugins:[[a.plugin,{root:`src`}]]});if(typeof o?.code==`string`)return{code:o.code,map:o.map??null}}}}exports.unistylesBabel=u;
2
+ //# sourceMappingURL=index.cjs.map