@ekanos/cli 0.17.0 → 0.18.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 (71) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/catalog.js +11 -7
  3. package/dist/commands/catalog.js.map +1 -1
  4. package/dist/commands/names.d.ts +19 -0
  5. package/dist/commands/names.js +122 -0
  6. package/dist/commands/names.js.map +1 -0
  7. package/dist/commands/publish.d.ts +53 -3
  8. package/dist/commands/publish.js +58 -16
  9. package/dist/commands/publish.js.map +1 -1
  10. package/dist/commands/sources.js +15 -5
  11. package/dist/commands/sources.js.map +1 -1
  12. package/dist/commands/status.d.ts +2 -0
  13. package/dist/commands/status.js +12 -12
  14. package/dist/commands/status.js.map +1 -1
  15. package/dist/commands/teams-connect.js +1 -1
  16. package/dist/commands/teams-connect.js.map +1 -1
  17. package/dist/experience/boundary.d.ts +2 -2
  18. package/dist/experience/boundary.js +2 -11
  19. package/dist/experience/boundary.js.map +1 -1
  20. package/dist/experience/build.d.ts +3 -1
  21. package/dist/experience/build.js +15 -26
  22. package/dist/experience/build.js.map +1 -1
  23. package/dist/experience/clean-room.d.ts +40 -0
  24. package/dist/experience/clean-room.js +150 -0
  25. package/dist/experience/clean-room.js.map +1 -0
  26. package/dist/experience/definition-checks.d.ts +12 -1
  27. package/dist/experience/definition-checks.js +34 -11
  28. package/dist/experience/definition-checks.js.map +1 -1
  29. package/dist/experience/definition-shape.d.ts +20 -2
  30. package/dist/experience/definition-shape.js.map +1 -1
  31. package/dist/experience/publish.d.ts +42 -5
  32. package/dist/experience/publish.js +195 -11
  33. package/dist/experience/publish.js.map +1 -1
  34. package/dist/harness-scaffold.js +3 -1
  35. package/dist/harness-scaffold.js.map +1 -1
  36. package/dist/index.js +25 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/names-api.d.ts +90 -0
  39. package/dist/names-api.js +123 -0
  40. package/dist/names-api.js.map +1 -0
  41. package/dist/pack.js +5 -0
  42. package/dist/pack.js.map +1 -1
  43. package/dist/product-kinds.d.ts +13 -0
  44. package/dist/product-kinds.js +17 -0
  45. package/dist/product-kinds.js.map +1 -0
  46. package/dist/project-checks.d.ts +7 -0
  47. package/dist/project-checks.js +47 -0
  48. package/dist/project-checks.js.map +1 -1
  49. package/dist/publish-api.d.ts +38 -5
  50. package/dist/publish-api.js +59 -16
  51. package/dist/publish-api.js.map +1 -1
  52. package/dist/repo/publish.d.ts +6 -3
  53. package/dist/repo/publish.js +48 -14
  54. package/dist/repo/publish.js.map +1 -1
  55. package/dist/source-products-api.d.ts +3 -2
  56. package/dist/source-products-api.js +1 -1
  57. package/dist/source-products-api.js.map +1 -1
  58. package/dist/team-connect-api.d.ts +4 -0
  59. package/dist/team-connect-api.js +1 -1
  60. package/dist/team-connect-api.js.map +1 -1
  61. package/dist/toolchain-api.d.ts +15 -0
  62. package/dist/toolchain-api.js +12 -1
  63. package/dist/toolchain-api.js.map +1 -1
  64. package/dist/validate-findings.js +2 -1
  65. package/dist/validate-findings.js.map +1 -1
  66. package/package.json +2 -2
  67. package/templates/experience/package.json.tmpl +7 -4
  68. package/templates/harness/app/experience/layout/page.tsx.tmpl +1 -0
  69. package/templates/harness/app/experience/layout.tsx.tmpl +3 -0
  70. package/templates/harness/app/experience/pages/[id]/[...path]/page.tsx.tmpl +1 -0
  71. package/templates/package.json.tmpl +5 -4
@@ -1 +1 @@
1
- {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../src/experience/boundary.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport { build } from 'esbuild';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { isContained } from '../safe-fs';\nimport type { ExperienceProject } from './project';\n\nexport const BOUNDARY_CHECK = 'checks/experience-boundary';\n\n/** Packages that may stay external at runtime (the host provides them). */\nexport const ALLOWED_EXTERNALS = [\n 'react',\n 'react-dom',\n '@ekanos/sdk',\n '@ekanos/ui',\n 'zod',\n '@tanstack/react-query',\n 'next-intl',\n 'react-hook-form',\n '@hookform/resolvers',\n] as const;\n\nconst INTEGRATION_PACKAGE = /^@companydotcom\\/integration-[a-z0-9-]+/;\n/** A pnpm-store or plain node_modules path of an integration package. */\nconst INTEGRATION_IN_PATH = /@companydotcom[/+]integration-[a-z0-9-]+/;\nconst HOST_INTERNAL = /^@kit\\//;\n\nconst SOURCE_EXT = /\\.(?:[cm]?[jt]sx?)$/;\nconst SKIP_DIRS = new Set(['node_modules', 'dist', '.ekanos', '.git']);\n\nconst finding = (\n message: string,\n hint: string,\n file?: string,\n line?: number,\n): Finding => ({\n check: BOUNDARY_CHECK,\n severity: 'error',\n message,\n hint,\n ...(file ? { file } : {}),\n ...(line ? { line } : {}),\n});\n\nconst INTEGRATION_HINT =\n 'An experience reaches an integration only through its pinned contract: useOperation / operation() / step.tool. Add the integration with \"ekanos add <integration>@<version>\".';\n\n/** Nearest `ekanos.json` at or above `start`, with its kind, or null. */\nexport function nearestEkanosKind(\n start: string,\n): { dir: string; kind: string } | null {\n let current =\n fs.existsSync(start) && fs.statSync(start).isDirectory()\n ? start\n : path.dirname(start);\n for (;;) {\n const file = path.join(current, 'ekanos.json');\n if (fs.existsSync(file)) {\n try {\n const parsed = JSON.parse(fs.readFileSync(file, 'utf8')) as {\n kind?: string;\n };\n\n return { dir: current, kind: parsed.kind ?? 'integration' };\n } catch {\n return { dir: current, kind: 'integration' };\n }\n }\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\n// ─── 1. package.json ────────────────────────────────────────────────────────\n\nconst DEP_FIELDS = [\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n] as const;\n\nfunction checkPackageJson(project: ExperienceProject): Finding[] {\n const file = path.join(project.dir, 'package.json');\n if (!fs.existsSync(file)) return [];\n const findings: Finding[] = [];\n const pkg = JSON.parse(fs.readFileSync(file, 'utf8')) as Record<\n string,\n unknown\n >;\n\n const specs: Array<[string, string]> = [];\n for (const field of DEP_FIELDS) {\n const block = pkg[field];\n if (block && typeof block === 'object') {\n specs.push(...Object.entries(block as Record<string, string>));\n }\n }\n const pnpm = pkg.pnpm as { overrides?: Record<string, string> } | undefined;\n for (const block of [\n pnpm?.overrides,\n pkg.overrides as Record<string, string> | undefined,\n ]) {\n if (block && typeof block === 'object')\n specs.push(...Object.entries(block));\n }\n\n for (const [name, spec] of specs) {\n if (INTEGRATION_PACKAGE.test(name)) {\n findings.push(\n finding(\n `package.json depends on the integration package ${name}.`,\n INTEGRATION_HINT,\n 'package.json',\n ),\n );\n continue;\n }\n const linked = /^(?:file|link|workspace):(.*)$/.exec(String(spec));\n if (linked?.[1] !== undefined && linked[1] !== '*' && linked[1] !== '^') {\n const target = path.resolve(project.dir, linked[1]);\n const kind = nearestEkanosKind(target);\n if (\n kind &&\n kind.kind === 'integration' &&\n path.resolve(kind.dir) !== project.dir\n ) {\n findings.push(\n finding(\n `package.json links ${name} to ${linked[1]}, an integration project.`,\n INTEGRATION_HINT,\n 'package.json',\n ),\n );\n }\n }\n // A resolved dependency that is itself an integration, whatever its name.\n const installed = path.join(\n project.dir,\n 'node_modules',\n name,\n 'ekanos.json',\n );\n if (fs.existsSync(installed)) {\n try {\n const kind = (\n JSON.parse(fs.readFileSync(installed, 'utf8')) as { kind?: string }\n ).kind;\n if ((kind ?? 'integration') === 'integration') {\n findings.push(\n finding(\n `package.json depends on ${name}, which is an integration (its ekanos.json is kind \"integration\").`,\n INTEGRATION_HINT,\n 'package.json',\n ),\n );\n }\n } catch {\n // unreadable ekanos.json in a dependency: the bundle scan still applies\n }\n }\n }\n\n return findings;\n}\n\n// ─── 2. source scan ─────────────────────────────────────────────────────────\n\nconst IMPORT_PATTERN =\n /(?:\\bfrom\\s*|\\bimport\\s*\\(\\s*|\\bimport\\s+|\\brequire\\s*\\(\\s*)(['\"`])([^'\"`\\n]+)\\1/g;\nconst COMPUTED_IMPORT = /\\bimport\\s*\\(\\s*(?!['\"`])/g;\n\nfunction* walkSources(\n dir: string,\n ignore: readonly string[],\n root: string,\n): Generator<string> {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n if (entry.isSymbolicLink()) continue;\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n const rel = path.relative(root, full).split(path.sep).join('/');\n if (ignore.includes(rel)) continue;\n yield* walkSources(full, ignore, root);\n } else if (SOURCE_EXT.test(entry.name)) {\n yield full;\n }\n }\n}\n\nconst lineOf = (text: string, index: number) =>\n text.slice(0, index).split('\\n').length;\n\nfunction checkSources(project: ExperienceProject): Finding[] {\n const findings: Finding[] = [];\n const srcDir = path.join(project.dir, 'src');\n if (!fs.existsSync(srcDir)) return findings;\n const ignore = project.ekanos.ignore ?? [];\n\n for (const file of walkSources(srcDir, ignore, project.dir)) {\n const text = fs.readFileSync(file, 'utf8');\n const rel = path.relative(project.dir, file).split(path.sep).join('/');\n\n for (const match of text.matchAll(IMPORT_PATTERN)) {\n const spec = match[2] as string;\n const line = lineOf(text, match.index ?? 0);\n\n if (INTEGRATION_PACKAGE.test(spec) || INTEGRATION_IN_PATH.test(spec)) {\n findings.push(\n finding(\n `${rel}:${line} imports the integration \"${spec}\".`,\n INTEGRATION_HINT,\n rel,\n line,\n ),\n );\n } else if (HOST_INTERNAL.test(spec)) {\n findings.push(\n finding(\n `${rel}:${line} imports \"${spec}\", which is host-internal.`,\n 'Build with @ekanos/ui and @ekanos/sdk; @kit/* does not exist for a partner.',\n rel,\n line,\n ),\n );\n } else if (spec.startsWith('.')) {\n const target = path.resolve(path.dirname(file), spec);\n if (!isContained(project.dir, target)) {\n findings.push(\n finding(\n `${rel}:${line} imports \"${spec}\", a path outside the project.`,\n `${INTEGRATION_HINT} Code outside this project is not part of the experience.`,\n rel,\n line,\n ),\n );\n } else {\n const owner = nearestEkanosKind(target);\n if (owner && owner.kind === 'integration') {\n findings.push(\n finding(\n `${rel}:${line} imports \"${spec}\", inside an integration project.`,\n INTEGRATION_HINT,\n rel,\n line,\n ),\n );\n }\n }\n }\n }\n\n for (const match of text.matchAll(COMPUTED_IMPORT)) {\n const line = lineOf(text, match.index ?? 0);\n findings.push(\n finding(\n `${rel}:${line} uses a computed dynamic import(), which cannot be checked.`,\n 'Import a static specifier.',\n rel,\n line,\n ),\n );\n }\n }\n\n return findings;\n}\n\n// ─── 3. bundle scan ─────────────────────────────────────────────────────────\n\nfunction externalsFor(project: ExperienceProject): string[] {\n const names = [...ALLOWED_EXTERNALS, ...(project.ekanos.allowImports ?? [])];\n\n return names.flatMap((name) => [name, `${name}/*`]);\n}\n\n/** Bundle the entry and fail on any input that is integration code. */\nasync function checkBundle(project: ExperienceProject): Promise<Finding[]> {\n const entry = path.join(project.dir, project.entry);\n if (!fs.existsSync(entry)) {\n return [\n finding(\n `entry ${project.entry} does not exist.`,\n 'Fix \"entry\" in ekanos.json.',\n 'ekanos.json',\n ),\n ];\n }\n\n let result;\n try {\n result = await build({\n entryPoints: [entry],\n bundle: true,\n write: false,\n metafile: true,\n platform: 'browser',\n format: 'esm',\n jsx: 'automatic',\n logLevel: 'silent',\n absWorkingDir: project.dir,\n external: externalsFor(project),\n loader: { '.css': 'empty', '.svg': 'dataurl', '.png': 'dataurl' },\n });\n } catch (error) {\n const errors =\n (\n error as {\n errors?: Array<{\n text: string;\n location?: { file: string; line: number } | null;\n }>;\n }\n ).errors ?? [];\n if (errors.length === 0) {\n return [\n finding(\n `the entry could not be bundled: ${error instanceof Error ? error.message : String(error)}`,\n 'Fix the error and re-run.',\n ),\n ];\n }\n\n return errors.map((e) =>\n finding(\n `bundle: ${e.text}`,\n 'Install the dependency, fix the import, or add a reviewed specifier to ekanos.json \"allowImports\".',\n e.location?.file,\n e.location?.line,\n ),\n );\n }\n\n const findings: Finding[] = [];\n const reported = new Set<string>();\n for (const input of Object.keys(result.metafile.inputs)) {\n const absolute = path.resolve(project.dir, input);\n let problem: string | null = null;\n\n if (INTEGRATION_IN_PATH.test(absolute)) {\n problem = 'is integration package code';\n } else if (\n !isContained(project.dir, absolute) &&\n !absolute.includes(`${path.sep}node_modules${path.sep}`)\n ) {\n problem = 'is outside the project';\n } else if (absolute.includes(`${path.sep}node_modules${path.sep}`)) {\n const owner = packageRootOf(absolute);\n const kind = owner ? nearestEkanosKind(owner) : null;\n if (kind && kind.dir === owner && kind.kind === 'integration') {\n problem = 'is code of an integration package';\n }\n } else {\n const owner = nearestEkanosKind(absolute);\n if (owner && owner.kind === 'integration')\n problem = 'is inside an integration project';\n }\n\n if (problem && !reported.has(input)) {\n reported.add(input);\n findings.push(\n finding(\n `the bundle contains ${input}, which ${problem}.`,\n `${INTEGRATION_HINT} It reached the bundle through an import chain; the source scan cannot always see this.`,\n input,\n ),\n );\n }\n }\n\n return findings;\n}\n\nfunction packageRootOf(file: string): string | null {\n let current = path.dirname(file);\n for (;;) {\n if (fs.existsSync(path.join(current, 'package.json'))) return current;\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\n/** The authoritative boundary: package.json, source scan, bundle scan. */\nexport async function checkExperienceBoundary(\n project: ExperienceProject,\n): Promise<Finding[]> {\n return [\n ...checkPackageJson(project),\n ...checkSources(project),\n ...(await checkBundle(project)),\n ];\n}\n"]}
1
+ {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../src/experience/boundary.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n EXPERIENCE_HOST_EXTERNALS,\n type Finding,\n} from '@ekanos/integration-schema';\nimport { build } from 'esbuild';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { isContained } from '../safe-fs';\nimport type { ExperienceProject } from './project';\n\nexport const BOUNDARY_CHECK = 'checks/experience-boundary';\n\n/** Packages that may stay external at runtime (the host provides them). */\nexport const ALLOWED_EXTERNALS = EXPERIENCE_HOST_EXTERNALS;\n\nconst INTEGRATION_PACKAGE = /^@companydotcom\\/integration-[a-z0-9-]+/;\n/** A pnpm-store or plain node_modules path of an integration package. */\nconst INTEGRATION_IN_PATH = /@companydotcom[/+]integration-[a-z0-9-]+/;\nconst HOST_INTERNAL = /^@kit\\//;\n\nconst SOURCE_EXT = /\\.(?:[cm]?[jt]sx?)$/;\nconst SKIP_DIRS = new Set(['node_modules', 'dist', '.ekanos', '.git']);\n\nconst finding = (\n message: string,\n hint: string,\n file?: string,\n line?: number,\n): Finding => ({\n check: BOUNDARY_CHECK,\n severity: 'error',\n message,\n hint,\n ...(file ? { file } : {}),\n ...(line ? { line } : {}),\n});\n\nconst INTEGRATION_HINT =\n 'An experience reaches an integration only through its pinned contract: useOperation / operation() / step.tool. Add the integration with \"ekanos add <integration>@<version>\".';\n\n/** Nearest `ekanos.json` at or above `start`, with its kind, or null. */\nexport function nearestEkanosKind(\n start: string,\n): { dir: string; kind: string } | null {\n let current =\n fs.existsSync(start) && fs.statSync(start).isDirectory()\n ? start\n : path.dirname(start);\n for (;;) {\n const file = path.join(current, 'ekanos.json');\n if (fs.existsSync(file)) {\n try {\n const parsed = JSON.parse(fs.readFileSync(file, 'utf8')) as {\n kind?: string;\n };\n\n return { dir: current, kind: parsed.kind ?? 'integration' };\n } catch {\n return { dir: current, kind: 'integration' };\n }\n }\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\n// ─── 1. package.json ────────────────────────────────────────────────────────\n\nconst DEP_FIELDS = [\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n] as const;\n\nfunction checkPackageJson(project: ExperienceProject): Finding[] {\n const file = path.join(project.dir, 'package.json');\n if (!fs.existsSync(file)) return [];\n const findings: Finding[] = [];\n const pkg = JSON.parse(fs.readFileSync(file, 'utf8')) as Record<\n string,\n unknown\n >;\n\n const specs: Array<[string, string]> = [];\n for (const field of DEP_FIELDS) {\n const block = pkg[field];\n if (block && typeof block === 'object') {\n specs.push(...Object.entries(block as Record<string, string>));\n }\n }\n const pnpm = pkg.pnpm as { overrides?: Record<string, string> } | undefined;\n for (const block of [\n pnpm?.overrides,\n pkg.overrides as Record<string, string> | undefined,\n ]) {\n if (block && typeof block === 'object')\n specs.push(...Object.entries(block));\n }\n\n for (const [name, spec] of specs) {\n if (INTEGRATION_PACKAGE.test(name)) {\n findings.push(\n finding(\n `package.json depends on the integration package ${name}.`,\n INTEGRATION_HINT,\n 'package.json',\n ),\n );\n continue;\n }\n const linked = /^(?:file|link|workspace):(.*)$/.exec(String(spec));\n if (linked?.[1] !== undefined && linked[1] !== '*' && linked[1] !== '^') {\n const target = path.resolve(project.dir, linked[1]);\n const kind = nearestEkanosKind(target);\n if (\n kind &&\n kind.kind === 'integration' &&\n path.resolve(kind.dir) !== project.dir\n ) {\n findings.push(\n finding(\n `package.json links ${name} to ${linked[1]}, an integration project.`,\n INTEGRATION_HINT,\n 'package.json',\n ),\n );\n }\n }\n // A resolved dependency that is itself an integration, whatever its name.\n const installed = path.join(\n project.dir,\n 'node_modules',\n name,\n 'ekanos.json',\n );\n if (fs.existsSync(installed)) {\n try {\n const kind = (\n JSON.parse(fs.readFileSync(installed, 'utf8')) as { kind?: string }\n ).kind;\n if ((kind ?? 'integration') === 'integration') {\n findings.push(\n finding(\n `package.json depends on ${name}, which is an integration (its ekanos.json is kind \"integration\").`,\n INTEGRATION_HINT,\n 'package.json',\n ),\n );\n }\n } catch {\n // unreadable ekanos.json in a dependency: the bundle scan still applies\n }\n }\n }\n\n return findings;\n}\n\n// ─── 2. source scan ─────────────────────────────────────────────────────────\n\nconst IMPORT_PATTERN =\n /(?:\\bfrom\\s*|\\bimport\\s*\\(\\s*|\\bimport\\s+|\\brequire\\s*\\(\\s*)(['\"`])([^'\"`\\n]+)\\1/g;\nconst COMPUTED_IMPORT = /\\bimport\\s*\\(\\s*(?!['\"`])/g;\n\nfunction* walkSources(\n dir: string,\n ignore: readonly string[],\n root: string,\n): Generator<string> {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n if (entry.isSymbolicLink()) continue;\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n const rel = path.relative(root, full).split(path.sep).join('/');\n if (ignore.includes(rel)) continue;\n yield* walkSources(full, ignore, root);\n } else if (SOURCE_EXT.test(entry.name)) {\n yield full;\n }\n }\n}\n\nconst lineOf = (text: string, index: number) =>\n text.slice(0, index).split('\\n').length;\n\nfunction checkSources(project: ExperienceProject): Finding[] {\n const findings: Finding[] = [];\n const srcDir = path.join(project.dir, 'src');\n if (!fs.existsSync(srcDir)) return findings;\n const ignore = project.ekanos.ignore ?? [];\n\n for (const file of walkSources(srcDir, ignore, project.dir)) {\n const text = fs.readFileSync(file, 'utf8');\n const rel = path.relative(project.dir, file).split(path.sep).join('/');\n\n for (const match of text.matchAll(IMPORT_PATTERN)) {\n const spec = match[2] as string;\n const line = lineOf(text, match.index ?? 0);\n\n if (INTEGRATION_PACKAGE.test(spec) || INTEGRATION_IN_PATH.test(spec)) {\n findings.push(\n finding(\n `${rel}:${line} imports the integration \"${spec}\".`,\n INTEGRATION_HINT,\n rel,\n line,\n ),\n );\n } else if (HOST_INTERNAL.test(spec)) {\n findings.push(\n finding(\n `${rel}:${line} imports \"${spec}\", which is host-internal.`,\n 'Build with @ekanos/ui and @ekanos/sdk; @kit/* does not exist for a partner.',\n rel,\n line,\n ),\n );\n } else if (spec.startsWith('.')) {\n const target = path.resolve(path.dirname(file), spec);\n if (!isContained(project.dir, target)) {\n findings.push(\n finding(\n `${rel}:${line} imports \"${spec}\", a path outside the project.`,\n `${INTEGRATION_HINT} Code outside this project is not part of the experience.`,\n rel,\n line,\n ),\n );\n } else {\n const owner = nearestEkanosKind(target);\n if (owner && owner.kind === 'integration') {\n findings.push(\n finding(\n `${rel}:${line} imports \"${spec}\", inside an integration project.`,\n INTEGRATION_HINT,\n rel,\n line,\n ),\n );\n }\n }\n }\n }\n\n for (const match of text.matchAll(COMPUTED_IMPORT)) {\n const line = lineOf(text, match.index ?? 0);\n findings.push(\n finding(\n `${rel}:${line} uses a computed dynamic import(), which cannot be checked.`,\n 'Import a static specifier.',\n rel,\n line,\n ),\n );\n }\n }\n\n return findings;\n}\n\n// ─── 3. bundle scan ─────────────────────────────────────────────────────────\n\nfunction externalsFor(project: ExperienceProject): string[] {\n const names = [...ALLOWED_EXTERNALS, ...(project.ekanos.allowImports ?? [])];\n\n return names.flatMap((name) => [name, `${name}/*`]);\n}\n\n/** Bundle the entry and fail on any input that is integration code. */\nasync function checkBundle(project: ExperienceProject): Promise<Finding[]> {\n const entry = path.join(project.dir, project.entry);\n if (!fs.existsSync(entry)) {\n return [\n finding(\n `entry ${project.entry} does not exist.`,\n 'Fix \"entry\" in ekanos.json.',\n 'ekanos.json',\n ),\n ];\n }\n\n let result;\n try {\n result = await build({\n entryPoints: [entry],\n bundle: true,\n write: false,\n metafile: true,\n platform: 'browser',\n format: 'esm',\n jsx: 'automatic',\n logLevel: 'silent',\n absWorkingDir: project.dir,\n external: externalsFor(project),\n loader: { '.css': 'empty', '.svg': 'dataurl', '.png': 'dataurl' },\n });\n } catch (error) {\n const errors =\n (\n error as {\n errors?: Array<{\n text: string;\n location?: { file: string; line: number } | null;\n }>;\n }\n ).errors ?? [];\n if (errors.length === 0) {\n return [\n finding(\n `the entry could not be bundled: ${error instanceof Error ? error.message : String(error)}`,\n 'Fix the error and re-run.',\n ),\n ];\n }\n\n return errors.map((e) =>\n finding(\n `bundle: ${e.text}`,\n 'Install the dependency, fix the import, or add a reviewed specifier to ekanos.json \"allowImports\".',\n e.location?.file,\n e.location?.line,\n ),\n );\n }\n\n const findings: Finding[] = [];\n const reported = new Set<string>();\n for (const input of Object.keys(result.metafile.inputs)) {\n const absolute = path.resolve(project.dir, input);\n let problem: string | null = null;\n\n if (INTEGRATION_IN_PATH.test(absolute)) {\n problem = 'is integration package code';\n } else if (\n !isContained(project.dir, absolute) &&\n !absolute.includes(`${path.sep}node_modules${path.sep}`)\n ) {\n problem = 'is outside the project';\n } else if (absolute.includes(`${path.sep}node_modules${path.sep}`)) {\n const owner = packageRootOf(absolute);\n const kind = owner ? nearestEkanosKind(owner) : null;\n if (kind && kind.dir === owner && kind.kind === 'integration') {\n problem = 'is code of an integration package';\n }\n } else {\n const owner = nearestEkanosKind(absolute);\n if (owner && owner.kind === 'integration')\n problem = 'is inside an integration project';\n }\n\n if (problem && !reported.has(input)) {\n reported.add(input);\n findings.push(\n finding(\n `the bundle contains ${input}, which ${problem}.`,\n `${INTEGRATION_HINT} It reached the bundle through an import chain; the source scan cannot always see this.`,\n input,\n ),\n );\n }\n }\n\n return findings;\n}\n\nfunction packageRootOf(file: string): string | null {\n let current = path.dirname(file);\n for (;;) {\n if (fs.existsSync(path.join(current, 'package.json'))) return current;\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\n/** The authoritative boundary: package.json, source scan, bundle scan. */\nexport async function checkExperienceBoundary(\n project: ExperienceProject,\n): Promise<Finding[]> {\n return [\n ...checkPackageJson(project),\n ...checkSources(project),\n ...(await checkBundle(project)),\n ];\n}\n"]}
@@ -3,6 +3,7 @@ import type { ExperienceCheckResult } from './checks.js';
3
3
  import { type ExperienceProject } from './project.js';
4
4
  export declare const DIST_ENTRY = "dist/experience.js";
5
5
  export declare const DIST_TYPES = "dist/experience.d.ts";
6
+ export declare const DIST_SURFACES = "dist/surfaces.json";
6
7
  export declare const MANIFEST_FILE = "manifest.json";
7
8
  export interface BuildOutput {
8
9
  manifest: ExperienceManifest;
@@ -11,6 +12,7 @@ export interface BuildOutput {
11
12
  }
12
13
  /**
13
14
  * Emit `dist/experience.js` (browser ESM, runtime packages left external),
14
- * `dist/experience.d.ts` and `manifest.json`. Assumes the checks passed.
15
+ * `dist/experience.d.ts`, `dist/surfaces.json` (the surfaces and suggested
16
+ * layout as data, so admission reads them without running code) and `manifest.json`. Assumes the checks passed.
15
17
  */
16
18
  export declare function emitExperience(project: ExperienceProject, result: ExperienceCheckResult): Promise<BuildOutput>;
@@ -1,13 +1,13 @@
1
- import { ExperienceManifestSchema, } from '@ekanos/integration-schema';
1
+ import { EXPERIENCE_BUNDLE_OPTIONS, EXPERIENCE_DTS, ExperienceManifestSchema, experienceExternals, } from '@ekanos/integration-schema';
2
2
  import { build as esbuild } from 'esbuild';
3
3
  import * as fs from 'node:fs';
4
4
  import * as path from 'node:path';
5
5
  import { preconditionError } from '../errors.js';
6
- import { ALLOWED_EXTERNALS } from './boundary.js';
7
- import { buildManifest } from './definition-checks.js';
6
+ import { buildManifest, buildSurfaces } from './definition-checks.js';
8
7
  import { formatJson, writeIfChanged } from './project.js';
9
8
  export const DIST_ENTRY = 'dist/experience.js';
10
9
  export const DIST_TYPES = 'dist/experience.d.ts';
10
+ export const DIST_SURFACES = 'dist/surfaces.json';
11
11
  export const MANIFEST_FILE = 'manifest.json';
12
12
  function readPackageJson(project) {
13
13
  try {
@@ -31,11 +31,12 @@ function sdkVersion(project, declared) {
31
31
  }
32
32
  /**
33
33
  * Emit `dist/experience.js` (browser ESM, runtime packages left external),
34
- * `dist/experience.d.ts` and `manifest.json`. Assumes the checks passed.
34
+ * `dist/experience.d.ts`, `dist/surfaces.json` (the surfaces and suggested
35
+ * layout as data, so admission reads them without running code) and `manifest.json`. Assumes the checks passed.
35
36
  */
36
37
  export async function emitExperience(project, result) {
37
38
  var _a;
38
- var _b, _c, _d;
39
+ var _b, _c;
39
40
  const loaded = result.loaded;
40
41
  if (!loaded) {
41
42
  throw preconditionError('The experience definition was not loaded.', 'Run "ekanos check" to see why.');
@@ -45,7 +46,9 @@ export async function emitExperience(project, result) {
45
46
  if (!/^\d+\.\d+\.\d+/.test(version)) {
46
47
  throw preconditionError(`package.json "version" is "${version}", not semver.`, 'Set a version such as 0.1.0.');
47
48
  }
49
+ const surfaces = buildSurfaces(project, loaded.definition);
48
50
  const manifest = buildManifest({
51
+ surfaces,
49
52
  project,
50
53
  definition: loaded.definition,
51
54
  pinned: result.pinned,
@@ -57,33 +60,19 @@ export async function emitExperience(project, result) {
57
60
  if (!parsed.success) {
58
61
  throw preconditionError(`The generated manifest is invalid: ${parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ')}`, 'This is a bug in @ekanos/cli or an unsupported definition; report it.');
59
62
  }
60
- const bundle = await esbuild({
61
- entryPoints: [path.join(project.dir, project.entry)],
62
- bundle: true,
63
- write: false,
64
- format: 'esm',
65
- platform: 'browser',
66
- jsx: 'automatic',
67
- logLevel: 'silent',
68
- absWorkingDir: project.dir,
69
- external: [
70
- ...ALLOWED_EXTERNALS,
71
- ...((_c = project.ekanos.allowImports) !== null && _c !== void 0 ? _c : []),
72
- ].flatMap((n) => [n, `${n}/*`]),
73
- loader: { '.css': 'empty', '.svg': 'dataurl', '.png': 'dataurl' },
74
- });
75
- const js = (_d = (_a = bundle.outputFiles[0]) === null || _a === void 0 ? void 0 : _a.text) !== null && _d !== void 0 ? _d : '';
76
- const dts = "import type { ExperienceDefinition } from '@ekanos/sdk/experience';\n" +
77
- 'declare const experience: ExperienceDefinition;\n' +
78
- 'export default experience;\n';
63
+ // The options are shared with the host's rebuild stage, which must produce
64
+ // these exact bytes from the shipped source.
65
+ const bundle = await esbuild(Object.assign(Object.assign({}, EXPERIENCE_BUNDLE_OPTIONS), { entryPoints: [path.join(project.dir, project.entry)], absWorkingDir: project.dir, external: experienceExternals(project.ekanos.allowImports) }));
66
+ const js = (_c = (_a = bundle.outputFiles[0]) === null || _a === void 0 ? void 0 : _a.text) !== null && _c !== void 0 ? _c : '';
79
67
  const changed = [
80
68
  writeIfChanged(path.join(project.dir, DIST_ENTRY), js),
81
- writeIfChanged(path.join(project.dir, DIST_TYPES), dts),
69
+ writeIfChanged(path.join(project.dir, DIST_TYPES), EXPERIENCE_DTS),
70
+ writeIfChanged(path.join(project.dir, DIST_SURFACES), formatJson(surfaces)),
82
71
  writeIfChanged(path.join(project.dir, MANIFEST_FILE), formatJson(manifest)),
83
72
  ].some(Boolean);
84
73
  return {
85
74
  manifest,
86
- files: [DIST_ENTRY, DIST_TYPES, MANIFEST_FILE],
75
+ files: [DIST_ENTRY, DIST_TYPES, DIST_SURFACES, MANIFEST_FILE],
87
76
  changed,
88
77
  };
89
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/experience/build.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ExperienceManifest,\n ExperienceManifestSchema,\n} from '@ekanos/integration-schema';\nimport { build as esbuild } from 'esbuild';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { preconditionError } from '../errors';\nimport { ALLOWED_EXTERNALS } from './boundary';\nimport type { ExperienceCheckResult } from './checks';\nimport { buildManifest } from './definition-checks';\nimport { type ExperienceProject, formatJson, writeIfChanged } from './project';\n\nexport const DIST_ENTRY = 'dist/experience.js';\nexport const DIST_TYPES = 'dist/experience.d.ts';\nexport const MANIFEST_FILE = 'manifest.json';\n\nfunction readPackageJson(project: ExperienceProject): {\n version?: string;\n dependencies?: Record<string, string>;\n} {\n try {\n return JSON.parse(\n fs.readFileSync(path.join(project.dir, 'package.json'), 'utf8'),\n ) as { version?: string; dependencies?: Record<string, string> };\n } catch {\n throw preconditionError(\n 'package.json is missing or unreadable.',\n 'An experience is an npm package; run \"ekanos init --kind experience\" or add a package.json.',\n );\n }\n}\n\nfunction sdkVersion(\n project: ExperienceProject,\n declared: Record<string, string> | undefined,\n): string {\n try {\n const installed = JSON.parse(\n fs.readFileSync(\n path.join(\n project.dir,\n 'node_modules',\n '@ekanos',\n 'sdk',\n 'package.json',\n ),\n 'utf8',\n ),\n ) as { version?: string };\n if (installed.version) return installed.version;\n } catch {\n // not installed: fall back to the declared range\n }\n\n return (declared?.['@ekanos/sdk'] ?? 'unknown').replace(/^[\\^~]/, '');\n}\n\nexport interface BuildOutput {\n manifest: ExperienceManifest;\n files: string[];\n changed: boolean;\n}\n\n/**\n * Emit `dist/experience.js` (browser ESM, runtime packages left external),\n * `dist/experience.d.ts` and `manifest.json`. Assumes the checks passed.\n */\nexport async function emitExperience(\n project: ExperienceProject,\n result: ExperienceCheckResult,\n): Promise<BuildOutput> {\n const loaded = result.loaded;\n if (!loaded) {\n throw preconditionError(\n 'The experience definition was not loaded.',\n 'Run \"ekanos check\" to see why.',\n );\n }\n const pkg = readPackageJson(project);\n const version = pkg.version ?? '';\n if (!/^\\d+\\.\\d+\\.\\d+/.test(version)) {\n throw preconditionError(\n `package.json \"version\" is \"${version}\", not semver.`,\n 'Set a version such as 0.1.0.',\n );\n }\n\n const manifest = buildManifest({\n project,\n definition: loaded.definition,\n pinned: result.pinned,\n workflows: result.workflows,\n version,\n sdk: sdkVersion(project, pkg.dependencies),\n });\n const parsed = ExperienceManifestSchema.safeParse(manifest);\n if (!parsed.success) {\n throw preconditionError(\n `The generated manifest is invalid: ${parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ')}`,\n 'This is a bug in @ekanos/cli or an unsupported definition; report it.',\n );\n }\n\n const bundle = await esbuild({\n entryPoints: [path.join(project.dir, project.entry)],\n bundle: true,\n write: false,\n format: 'esm',\n platform: 'browser',\n jsx: 'automatic',\n logLevel: 'silent',\n absWorkingDir: project.dir,\n external: [\n ...ALLOWED_EXTERNALS,\n ...(project.ekanos.allowImports ?? []),\n ].flatMap((n) => [n, `${n}/*`]),\n loader: { '.css': 'empty', '.svg': 'dataurl', '.png': 'dataurl' },\n });\n const js = bundle.outputFiles[0]?.text ?? '';\n\n const dts =\n \"import type { ExperienceDefinition } from '@ekanos/sdk/experience';\\n\" +\n 'declare const experience: ExperienceDefinition;\\n' +\n 'export default experience;\\n';\n\n const changed = [\n writeIfChanged(path.join(project.dir, DIST_ENTRY), js),\n writeIfChanged(path.join(project.dir, DIST_TYPES), dts),\n writeIfChanged(path.join(project.dir, MANIFEST_FILE), formatJson(manifest)),\n ].some(Boolean);\n\n return {\n manifest,\n files: [DIST_ENTRY, DIST_TYPES, MANIFEST_FILE],\n changed,\n };\n}\n"]}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/experience/build.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n EXPERIENCE_BUNDLE_OPTIONS,\n EXPERIENCE_DTS,\n type ExperienceManifest,\n ExperienceManifestSchema,\n experienceExternals,\n} from '@ekanos/integration-schema';\nimport { build as esbuild } from 'esbuild';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { preconditionError } from '../errors';\nimport type { ExperienceCheckResult } from './checks';\nimport { buildManifest, buildSurfaces } from './definition-checks';\nimport { type ExperienceProject, formatJson, writeIfChanged } from './project';\n\nexport const DIST_ENTRY = 'dist/experience.js';\nexport const DIST_TYPES = 'dist/experience.d.ts';\nexport const DIST_SURFACES = 'dist/surfaces.json';\nexport const MANIFEST_FILE = 'manifest.json';\n\nfunction readPackageJson(project: ExperienceProject): {\n version?: string;\n dependencies?: Record<string, string>;\n} {\n try {\n return JSON.parse(\n fs.readFileSync(path.join(project.dir, 'package.json'), 'utf8'),\n ) as { version?: string; dependencies?: Record<string, string> };\n } catch {\n throw preconditionError(\n 'package.json is missing or unreadable.',\n 'An experience is an npm package; run \"ekanos init --kind experience\" or add a package.json.',\n );\n }\n}\n\nfunction sdkVersion(\n project: ExperienceProject,\n declared: Record<string, string> | undefined,\n): string {\n try {\n const installed = JSON.parse(\n fs.readFileSync(\n path.join(\n project.dir,\n 'node_modules',\n '@ekanos',\n 'sdk',\n 'package.json',\n ),\n 'utf8',\n ),\n ) as { version?: string };\n if (installed.version) return installed.version;\n } catch {\n // not installed: fall back to the declared range\n }\n\n return (declared?.['@ekanos/sdk'] ?? 'unknown').replace(/^[\\^~]/, '');\n}\n\nexport interface BuildOutput {\n manifest: ExperienceManifest;\n files: string[];\n changed: boolean;\n}\n\n/**\n * Emit `dist/experience.js` (browser ESM, runtime packages left external),\n * `dist/experience.d.ts`, `dist/surfaces.json` (the surfaces and suggested\n * layout as data, so admission reads them without running code) and `manifest.json`. Assumes the checks passed.\n */\nexport async function emitExperience(\n project: ExperienceProject,\n result: ExperienceCheckResult,\n): Promise<BuildOutput> {\n const loaded = result.loaded;\n if (!loaded) {\n throw preconditionError(\n 'The experience definition was not loaded.',\n 'Run \"ekanos check\" to see why.',\n );\n }\n const pkg = readPackageJson(project);\n const version = pkg.version ?? '';\n if (!/^\\d+\\.\\d+\\.\\d+/.test(version)) {\n throw preconditionError(\n `package.json \"version\" is \"${version}\", not semver.`,\n 'Set a version such as 0.1.0.',\n );\n }\n\n const surfaces = buildSurfaces(project, loaded.definition);\n const manifest = buildManifest({\n surfaces,\n project,\n definition: loaded.definition,\n pinned: result.pinned,\n workflows: result.workflows,\n version,\n sdk: sdkVersion(project, pkg.dependencies),\n });\n const parsed = ExperienceManifestSchema.safeParse(manifest);\n if (!parsed.success) {\n throw preconditionError(\n `The generated manifest is invalid: ${parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ')}`,\n 'This is a bug in @ekanos/cli or an unsupported definition; report it.',\n );\n }\n\n // The options are shared with the host's rebuild stage, which must produce\n // these exact bytes from the shipped source.\n const bundle = await esbuild({\n ...EXPERIENCE_BUNDLE_OPTIONS,\n entryPoints: [path.join(project.dir, project.entry)],\n absWorkingDir: project.dir,\n external: experienceExternals(project.ekanos.allowImports),\n });\n const js = bundle.outputFiles[0]?.text ?? '';\n\n const changed = [\n writeIfChanged(path.join(project.dir, DIST_ENTRY), js),\n writeIfChanged(path.join(project.dir, DIST_TYPES), EXPERIENCE_DTS),\n writeIfChanged(path.join(project.dir, DIST_SURFACES), formatJson(surfaces)),\n writeIfChanged(path.join(project.dir, MANIFEST_FILE), formatJson(manifest)),\n ].some(Boolean);\n\n return {\n manifest,\n files: [DIST_ENTRY, DIST_TYPES, DIST_SURFACES, MANIFEST_FILE],\n changed,\n };\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { type ExperienceSurfaces } from '@ekanos/integration-schema';
2
+ import type { ExperienceProject } from './project.js';
3
+ /** Set to `off` to skip the clean-room build (offline work, tests); the host's gate still rebuilds. */
4
+ export declare const CLEAN_ROOM_ENV = "EKANOS_PUBLISH_CLEAN_ROOM";
5
+ /** The npm binary the clean room runs. Defaults to `npm`; overridden by tests. */
6
+ export declare const NPM_BIN_ENV = "EKANOS_NPM_BIN";
7
+ export type LockfileSource = 'package-lock' | 'npm-shrinkwrap' | 'generated-from-pnpm';
8
+ export interface CleanRoomBuild {
9
+ /** `dist/experience.js` exactly as the host's gate will rebuild it. */
10
+ bundle: string;
11
+ /** The standalone lockfile the bundle was built against; ships as npm-shrinkwrap.json. */
12
+ lockfile: string;
13
+ lockfileSource: LockfileSource;
14
+ /** `dist/surfaces.json` as the rebuilt bundle's own definition declares it. */
15
+ surfaces: ExperienceSurfaces;
16
+ }
17
+ /** A `pnpm-lock.yaml` at or above the project: it lives in a pnpm workspace. */
18
+ export declare function findPnpmLockfile(dir: string): string | null;
19
+ /** Whether the project has a lockfile of either kind to build from. */
20
+ export declare function hasBuildableLockfile(dir: string): boolean;
21
+ /**
22
+ * Builds the experience bundle the way the host's gate will, so the bytes match
23
+ * by construction: in a temp directory holding only what ships (package.json,
24
+ * tsconfig, `src/`), with dependencies installed by `npm ci --ignore-scripts`
25
+ * from a standalone lockfile, and the shared bundle options.
26
+ *
27
+ * The lockfile is the project's own `package-lock.json`/`npm-shrinkwrap.json`
28
+ * when it has one. A project inside a pnpm workspace (a `pnpm-lock.yaml` at the
29
+ * repo root, no per-package npm lockfile) gets one generated for just its
30
+ * `package.json` (`npm install`), and that generated file
31
+ * is what ships, so the host resolves the dependencies the bundle was built
32
+ * with. It resolves the declared ranges from the registry, which can differ from
33
+ * the versions the workspace's pnpm lockfile pins; the bundle you ship is the
34
+ * one built from the shipped lockfile, not from your workspace's tree.
35
+ *
36
+ * The surfaces are recomputed from the rebuilt bundle's own definition (the host
37
+ * does the same), so a project whose local tree declares something else is
38
+ * caught here instead of at the gate.
39
+ */
40
+ export declare function buildInCleanRoom(project: ExperienceProject, env?: Record<string, string | undefined>): Promise<CleanRoomBuild>;
@@ -0,0 +1,150 @@
1
+ import { EXPERIENCE_BUNDLE_OPTIONS, buildExperienceSurfaces, experienceExternals, } from '@ekanos/integration-schema';
2
+ import { build } from 'esbuild';
3
+ import { execFileSync } from 'node:child_process';
4
+ import * as fs from 'node:fs';
5
+ import * as os from 'node:os';
6
+ import * as path from 'node:path';
7
+ import { pathToFileURL } from 'node:url';
8
+ import { preconditionError, validationError } from '../errors.js';
9
+ /** Set to `off` to skip the clean-room build (offline work, tests); the host's gate still rebuilds. */
10
+ export const CLEAN_ROOM_ENV = 'EKANOS_PUBLISH_CLEAN_ROOM';
11
+ /** The npm binary the clean room runs. Defaults to `npm`; overridden by tests. */
12
+ export const NPM_BIN_ENV = 'EKANOS_NPM_BIN';
13
+ const LOCKFILE_ARGS = ['--ignore-scripts', '--no-audit', '--no-fund'];
14
+ /** The lockfile the project itself carries, if it carries one. */
15
+ function ownLockfile(dir) {
16
+ for (const [name, source] of [
17
+ ['npm-shrinkwrap.json', 'npm-shrinkwrap'],
18
+ ['package-lock.json', 'package-lock'],
19
+ ]) {
20
+ const file = path.join(dir, name);
21
+ if (fs.existsSync(file))
22
+ return { file, source };
23
+ }
24
+ return null;
25
+ }
26
+ /** A `pnpm-lock.yaml` at or above the project: it lives in a pnpm workspace. */
27
+ export function findPnpmLockfile(dir) {
28
+ let current = path.resolve(dir);
29
+ for (;;) {
30
+ const candidate = path.join(current, 'pnpm-lock.yaml');
31
+ if (fs.existsSync(candidate))
32
+ return candidate;
33
+ const parent = path.dirname(current);
34
+ if (parent === current)
35
+ return null;
36
+ current = parent;
37
+ }
38
+ }
39
+ /** Whether the project has a lockfile of either kind to build from. */
40
+ export function hasBuildableLockfile(dir) {
41
+ return ownLockfile(dir) !== null || findPnpmLockfile(dir) !== null;
42
+ }
43
+ function npm(bin, args, cwd, what) {
44
+ try {
45
+ execFileSync(bin, [...args], {
46
+ cwd,
47
+ encoding: 'utf8',
48
+ stdio: ['ignore', 'pipe', 'pipe'],
49
+ });
50
+ }
51
+ catch (error) {
52
+ const detail = error instanceof Error && 'stderr' in error
53
+ ? String(error.stderr)
54
+ .trim()
55
+ .slice(-1500)
56
+ : String(error);
57
+ throw preconditionError(`${what} failed in the clean room: ${detail}`, 'The host rebuilds your package the same way, from the public registry; fix the dependency problem and re-run.');
58
+ }
59
+ }
60
+ function copyIfExists(from, to) {
61
+ if (fs.existsSync(from))
62
+ fs.cpSync(from, to, { recursive: true });
63
+ }
64
+ /**
65
+ * Builds the experience bundle the way the host's gate will, so the bytes match
66
+ * by construction: in a temp directory holding only what ships (package.json,
67
+ * tsconfig, `src/`), with dependencies installed by `npm ci --ignore-scripts`
68
+ * from a standalone lockfile, and the shared bundle options.
69
+ *
70
+ * The lockfile is the project's own `package-lock.json`/`npm-shrinkwrap.json`
71
+ * when it has one. A project inside a pnpm workspace (a `pnpm-lock.yaml` at the
72
+ * repo root, no per-package npm lockfile) gets one generated for just its
73
+ * `package.json` (`npm install`), and that generated file
74
+ * is what ships, so the host resolves the dependencies the bundle was built
75
+ * with. It resolves the declared ranges from the registry, which can differ from
76
+ * the versions the workspace's pnpm lockfile pins; the bundle you ship is the
77
+ * one built from the shipped lockfile, not from your workspace's tree.
78
+ *
79
+ * The surfaces are recomputed from the rebuilt bundle's own definition (the host
80
+ * does the same), so a project whose local tree declares something else is
81
+ * caught here instead of at the gate.
82
+ */
83
+ export async function buildInCleanRoom(project, env = {}) {
84
+ var _a;
85
+ var _b, _c;
86
+ const npmBin = (_b = env[NPM_BIN_ENV]) !== null && _b !== void 0 ? _b : 'npm';
87
+ const own = ownLockfile(project.dir);
88
+ const pnpm = own ? null : findPnpmLockfile(project.dir);
89
+ if (!own && !pnpm) {
90
+ throw preconditionError('There is no lockfile to build from: no package-lock.json, npm-shrinkwrap.json or pnpm-lock.yaml.', 'Run "npm install --package-lock-only" in the project (or install with pnpm in a workspace) and commit the lockfile.');
91
+ }
92
+ const room = fs.mkdtempSync(path.join(os.tmpdir(), 'ekanos-exp-room-'));
93
+ try {
94
+ fs.copyFileSync(path.join(project.dir, 'package.json'), path.join(room, 'package.json'));
95
+ copyIfExists(path.join(project.dir, 'src'), path.join(room, 'src'));
96
+ for (const name of fs.readdirSync(project.dir)) {
97
+ if (/^tsconfig[^/]*\.json$/.test(name)) {
98
+ fs.copyFileSync(path.join(project.dir, name), path.join(room, name));
99
+ }
100
+ }
101
+ let lockfileSource;
102
+ if (own) {
103
+ fs.copyFileSync(own.file, path.join(room, 'package-lock.json'));
104
+ lockfileSource = own.source;
105
+ }
106
+ else {
107
+ const manifest = fs.readFileSync(path.join(room, 'package.json'), 'utf8');
108
+ if (/"(?:workspace|catalog|link|file):/.test(manifest)) {
109
+ throw validationError('package.json depends on a workspace:, catalog:, link: or file: package, which a standalone lockfile cannot resolve.', 'An experience reaches integrations through pinned operations, not packages; publish a package that depends only on registry versions.');
110
+ }
111
+ // A real install, twice, not `--package-lock-only`: npm resolving an
112
+ // empty tree writes lockfiles that `npm ci` then rejects as out of sync
113
+ // (a peer range resolved two ways), and a first install over a fresh tree
114
+ // can too (seen with eslint's ajv). The second install reconciles the
115
+ // tree against its own lockfile; the `npm ci` below, after the tree is
116
+ // thrown away, is the install the host will repeat and proves it did.
117
+ for (let pass = 0; pass < 2; pass += 1) {
118
+ npm(npmBin, ['install', ...LOCKFILE_ARGS], room, 'generating a standalone lockfile from package.json');
119
+ }
120
+ fs.rmSync(path.join(room, 'node_modules'), {
121
+ recursive: true,
122
+ force: true,
123
+ });
124
+ lockfileSource = 'generated-from-pnpm';
125
+ }
126
+ const lockfile = fs.readFileSync(path.join(room, 'package-lock.json'), 'utf8');
127
+ npm(npmBin, ['ci', ...LOCKFILE_ARGS], room, '`npm ci`');
128
+ const built = await build(Object.assign(Object.assign({}, EXPERIENCE_BUNDLE_OPTIONS), { entryPoints: [path.join(room, project.entry)], absWorkingDir: room, external: experienceExternals(project.ekanos.allowImports) }));
129
+ const bundle = (_c = (_a = built.outputFiles[0]) === null || _a === void 0 ? void 0 : _a.text) !== null && _c !== void 0 ? _c : '';
130
+ const file = path.join(room, '.clean-room-experience.mjs');
131
+ fs.writeFileSync(file, bundle);
132
+ const loaded = (await import(pathToFileURL(file).href));
133
+ if (typeof loaded.default !== 'object' ||
134
+ loaded.default === null ||
135
+ !Array.isArray(loaded.default.widgets) ||
136
+ !Array.isArray(loaded.default.pages)) {
137
+ throw validationError('The rebuilt bundle does not export a defineExperience() result as default.', `Export the definition from ${project.entry} as the default export.`);
138
+ }
139
+ return {
140
+ bundle,
141
+ lockfile,
142
+ lockfileSource,
143
+ surfaces: buildExperienceSurfaces(project.slug, loaded.default),
144
+ };
145
+ }
146
+ finally {
147
+ fs.rmSync(room, { recursive: true, force: true });
148
+ }
149
+ }
150
+ //# sourceMappingURL=clean-room.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clean-room.js","sourceRoot":"","sources":["../../src/experience/clean-room.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n EXPERIENCE_BUNDLE_OPTIONS,\n type ExperienceSurfaces,\n type SurfacesDefinitionInput,\n buildExperienceSurfaces,\n experienceExternals,\n} from '@ekanos/integration-schema';\nimport { build } from 'esbuild';\nimport { execFileSync } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { preconditionError, validationError } from '../errors';\nimport type { ExperienceProject } from './project';\n\n/** Set to `off` to skip the clean-room build (offline work, tests); the host's gate still rebuilds. */\nexport const CLEAN_ROOM_ENV = 'EKANOS_PUBLISH_CLEAN_ROOM';\n/** The npm binary the clean room runs. Defaults to `npm`; overridden by tests. */\nexport const NPM_BIN_ENV = 'EKANOS_NPM_BIN';\n\nexport type LockfileSource =\n | 'package-lock'\n | 'npm-shrinkwrap'\n | 'generated-from-pnpm';\n\nexport interface CleanRoomBuild {\n /** `dist/experience.js` exactly as the host's gate will rebuild it. */\n bundle: string;\n /** The standalone lockfile the bundle was built against; ships as npm-shrinkwrap.json. */\n lockfile: string;\n lockfileSource: LockfileSource;\n /** `dist/surfaces.json` as the rebuilt bundle's own definition declares it. */\n surfaces: ExperienceSurfaces;\n}\n\nconst LOCKFILE_ARGS = ['--ignore-scripts', '--no-audit', '--no-fund'] as const;\n\n/** The lockfile the project itself carries, if it carries one. */\nfunction ownLockfile(\n dir: string,\n): { file: string; source: 'package-lock' | 'npm-shrinkwrap' } | null {\n for (const [name, source] of [\n ['npm-shrinkwrap.json', 'npm-shrinkwrap'],\n ['package-lock.json', 'package-lock'],\n ] as const) {\n const file = path.join(dir, name);\n if (fs.existsSync(file)) return { file, source };\n }\n\n return null;\n}\n\n/** A `pnpm-lock.yaml` at or above the project: it lives in a pnpm workspace. */\nexport function findPnpmLockfile(dir: string): string | null {\n let current = path.resolve(dir);\n for (;;) {\n const candidate = path.join(current, 'pnpm-lock.yaml');\n if (fs.existsSync(candidate)) return candidate;\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\n/** Whether the project has a lockfile of either kind to build from. */\nexport function hasBuildableLockfile(dir: string): boolean {\n return ownLockfile(dir) !== null || findPnpmLockfile(dir) !== null;\n}\n\nfunction npm(\n bin: string,\n args: readonly string[],\n cwd: string,\n what: string,\n): void {\n try {\n execFileSync(bin, [...args], {\n cwd,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n } catch (error) {\n const detail =\n error instanceof Error && 'stderr' in error\n ? String((error as { stderr: unknown }).stderr)\n .trim()\n .slice(-1500)\n : String(error);\n throw preconditionError(\n `${what} failed in the clean room: ${detail}`,\n 'The host rebuilds your package the same way, from the public registry; fix the dependency problem and re-run.',\n );\n }\n}\n\nfunction copyIfExists(from: string, to: string): void {\n if (fs.existsSync(from)) fs.cpSync(from, to, { recursive: true });\n}\n\n/**\n * Builds the experience bundle the way the host's gate will, so the bytes match\n * by construction: in a temp directory holding only what ships (package.json,\n * tsconfig, `src/`), with dependencies installed by `npm ci --ignore-scripts`\n * from a standalone lockfile, and the shared bundle options.\n *\n * The lockfile is the project's own `package-lock.json`/`npm-shrinkwrap.json`\n * when it has one. A project inside a pnpm workspace (a `pnpm-lock.yaml` at the\n * repo root, no per-package npm lockfile) gets one generated for just its\n * `package.json` (`npm install`), and that generated file\n * is what ships, so the host resolves the dependencies the bundle was built\n * with. It resolves the declared ranges from the registry, which can differ from\n * the versions the workspace's pnpm lockfile pins; the bundle you ship is the\n * one built from the shipped lockfile, not from your workspace's tree.\n *\n * The surfaces are recomputed from the rebuilt bundle's own definition (the host\n * does the same), so a project whose local tree declares something else is\n * caught here instead of at the gate.\n */\nexport async function buildInCleanRoom(\n project: ExperienceProject,\n env: Record<string, string | undefined> = {},\n): Promise<CleanRoomBuild> {\n const npmBin = env[NPM_BIN_ENV] ?? 'npm';\n const own = ownLockfile(project.dir);\n const pnpm = own ? null : findPnpmLockfile(project.dir);\n\n if (!own && !pnpm) {\n throw preconditionError(\n 'There is no lockfile to build from: no package-lock.json, npm-shrinkwrap.json or pnpm-lock.yaml.',\n 'Run \"npm install --package-lock-only\" in the project (or install with pnpm in a workspace) and commit the lockfile.',\n );\n }\n\n const room = fs.mkdtempSync(path.join(os.tmpdir(), 'ekanos-exp-room-'));\n try {\n fs.copyFileSync(\n path.join(project.dir, 'package.json'),\n path.join(room, 'package.json'),\n );\n copyIfExists(path.join(project.dir, 'src'), path.join(room, 'src'));\n for (const name of fs.readdirSync(project.dir)) {\n if (/^tsconfig[^/]*\\.json$/.test(name)) {\n fs.copyFileSync(path.join(project.dir, name), path.join(room, name));\n }\n }\n\n let lockfileSource: LockfileSource;\n if (own) {\n fs.copyFileSync(own.file, path.join(room, 'package-lock.json'));\n lockfileSource = own.source;\n } else {\n const manifest = fs.readFileSync(path.join(room, 'package.json'), 'utf8');\n if (/\"(?:workspace|catalog|link|file):/.test(manifest)) {\n throw validationError(\n 'package.json depends on a workspace:, catalog:, link: or file: package, which a standalone lockfile cannot resolve.',\n 'An experience reaches integrations through pinned operations, not packages; publish a package that depends only on registry versions.',\n );\n }\n // A real install, twice, not `--package-lock-only`: npm resolving an\n // empty tree writes lockfiles that `npm ci` then rejects as out of sync\n // (a peer range resolved two ways), and a first install over a fresh tree\n // can too (seen with eslint's ajv). The second install reconciles the\n // tree against its own lockfile; the `npm ci` below, after the tree is\n // thrown away, is the install the host will repeat and proves it did.\n for (let pass = 0; pass < 2; pass += 1) {\n npm(\n npmBin,\n ['install', ...LOCKFILE_ARGS],\n room,\n 'generating a standalone lockfile from package.json',\n );\n }\n fs.rmSync(path.join(room, 'node_modules'), {\n recursive: true,\n force: true,\n });\n lockfileSource = 'generated-from-pnpm';\n }\n\n const lockfile = fs.readFileSync(\n path.join(room, 'package-lock.json'),\n 'utf8',\n );\n npm(npmBin, ['ci', ...LOCKFILE_ARGS], room, '`npm ci`');\n\n const built = await build({\n ...EXPERIENCE_BUNDLE_OPTIONS,\n entryPoints: [path.join(room, project.entry)],\n absWorkingDir: room,\n external: experienceExternals(project.ekanos.allowImports),\n });\n const bundle = built.outputFiles[0]?.text ?? '';\n\n const file = path.join(room, '.clean-room-experience.mjs');\n fs.writeFileSync(file, bundle);\n const loaded = (await import(pathToFileURL(file).href)) as {\n default?: SurfacesDefinitionInput;\n };\n if (\n typeof loaded.default !== 'object' ||\n loaded.default === null ||\n !Array.isArray(loaded.default.widgets) ||\n !Array.isArray(loaded.default.pages)\n ) {\n throw validationError(\n 'The rebuilt bundle does not export a defineExperience() result as default.',\n `Export the definition from ${project.entry} as the default export.`,\n );\n }\n\n return {\n bundle,\n lockfile,\n lockfileSource,\n surfaces: buildExperienceSurfaces(project.slug, loaded.default),\n };\n } finally {\n fs.rmSync(room, { recursive: true, force: true });\n }\n}\n"]}
@@ -1,8 +1,9 @@
1
- import { type ExperienceManifest, type Finding, type WorkflowTemplate } from '@ekanos/integration-schema';
1
+ import { type ExperienceManifest, type ExperienceSurfaces, type Finding, type WorkflowTemplate } from '@ekanos/integration-schema';
2
2
  import type { LoadedExperienceDefinition, LoadedExperienceModule } from './definition-shape.js';
3
3
  import { type PinnedIntegration } from './pins.js';
4
4
  import type { ExperienceProject } from './project.js';
5
5
  export declare const DEFINITION_CHECK = "checks/experience-definition";
6
+ export declare const SURFACES_CHECK: "checks/experience-surfaces";
6
7
  export interface DefinitionAnalysis {
7
8
  findings: Finding[];
8
9
  workflows: WorkflowTemplate[];
@@ -14,6 +15,15 @@ export interface DefinitionAnalysis {
14
15
  * review queue reads only, and each template serializes to the engine's DSL.
15
16
  */
16
17
  export declare function analyzeDefinition(project: ExperienceProject, loaded: LoadedExperienceModule, pinned: readonly PinnedIntegration[]): DefinitionAnalysis;
18
+ /**
19
+ * The surfaces contract: page kinds, canvas widgets, routes and the suggested
20
+ * layout must be well formed and refer only to what the experience declares.
21
+ * The same rules `defineExperience` enforces, re-run on the loaded definition
22
+ * so a definition built by an older SDK, or by hand, cannot slip past.
23
+ */
24
+ export declare function checkSurfaces(project: ExperienceProject, definition: LoadedExperienceDefinition): Finding[];
25
+ /** `dist/surfaces.json`: the surfaces and suggested layout as data, no code. */
26
+ export declare function buildSurfaces(project: ExperienceProject, definition: LoadedExperienceDefinition): ExperienceSurfaces;
17
27
  /** `fixtures/<integration>/<kind>-<name>.<variant>.json` must satisfy the pinned output schema. */
18
28
  export declare function checkFixtures(project: ExperienceProject, pinned: readonly PinnedIntegration[]): Finding[];
19
29
  export declare function buildManifest(input: {
@@ -23,4 +33,5 @@ export declare function buildManifest(input: {
23
33
  workflows: readonly WorkflowTemplate[];
24
34
  version: string;
25
35
  sdk: string;
36
+ surfaces: ExperienceSurfaces;
26
37
  }): ExperienceManifest;
@@ -1,9 +1,10 @@
1
- import { WorkflowTemplateSchema, canonicalJson, sha256Hex, validateAgainstSchema, workflowTemplateProblems, } from '@ekanos/integration-schema';
1
+ import { ExperienceSurfacesSchema, WorkflowTemplateSchema, EKANOS_CHECK_STAGE_IDS, buildExperienceSurfaces, canonicalJson, sha256Hex, validateAgainstSchema, workflowTemplateProblems, } from '@ekanos/integration-schema';
2
2
  import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
4
  import { declaredOperationKeys } from './integrations-codegen.js';
5
5
  import { effectLookup } from './pins.js';
6
6
  export const DEFINITION_CHECK = 'checks/experience-definition';
7
+ export const SURFACES_CHECK = EKANOS_CHECK_STAGE_IDS.experienceSurfaces;
7
8
  const err = (message, hint, file) => (Object.assign({ check: DEFINITION_CHECK, severity: 'error', message,
8
9
  hint }, (file ? { file } : {})));
9
10
  const warn = (message, hint, file) => (Object.assign({ check: DEFINITION_CHECK, severity: 'warn', message,
@@ -16,7 +17,7 @@ const warn = (message, hint, file) => (Object.assign({ check: DEFINITION_CHECK,
16
17
  */
17
18
  export function analyzeDefinition(project, loaded, pinned) {
18
19
  var _a, _b, _c, _d, _e;
19
- var _f, _g;
20
+ var _f, _g, _h;
20
21
  const { definition } = loaded;
21
22
  const findings = [];
22
23
  const workflows = [];
@@ -44,10 +45,11 @@ export function analyzeDefinition(project, loaded, pinned) {
44
45
  checkIntegration(integration, `widget "${widget.id}"`);
45
46
  }
46
47
  for (const page of definition.pages) {
47
- for (const integration of page.integrations)
48
+ for (const integration of (_f = page.integrations) !== null && _f !== void 0 ? _f : [])
48
49
  checkIntegration(integration, `page "${page.id}"`);
49
50
  }
50
- for (const section of (_f = (_a = definition.reviewQueue) === null || _a === void 0 ? void 0 : _a.sections) !== null && _f !== void 0 ? _f : []) {
51
+ findings.push(...checkSurfaces(project, definition));
52
+ for (const section of (_g = (_a = definition.reviewQueue) === null || _a === void 0 ? void 0 : _a.sections) !== null && _g !== void 0 ? _g : []) {
51
53
  if (!checkIntegration(section.integration, `review section "${section.id}"`))
52
54
  continue;
53
55
  if (!((_b = declared.get(section.integration)) === null || _b === void 0 ? void 0 : _b.has(section.read))) {
@@ -101,7 +103,7 @@ export function analyzeDefinition(project, loaded, pinned) {
101
103
  const gated = new Set();
102
104
  const preds = new Map();
103
105
  for (const e of parsed.data.edges)
104
- preds.set(e.to, [...((_g = preds.get(e.to)) !== null && _g !== void 0 ? _g : []), e.from]);
106
+ preds.set(e.to, [...((_h = preds.get(e.to)) !== null && _h !== void 0 ? _h : []), e.from]);
105
107
  const hasGate = (id, seen = new Set()) => {
106
108
  var _a;
107
109
  if (seen.has(id))
@@ -122,6 +124,30 @@ export function analyzeDefinition(project, loaded, pinned) {
122
124
  }
123
125
  return { findings, workflows };
124
126
  }
127
+ // ─── surfaces ───────────────────────────────────────────────────────────────
128
+ /**
129
+ * The surfaces contract: page kinds, canvas widgets, routes and the suggested
130
+ * layout must be well formed and refer only to what the experience declares.
131
+ * The same rules `defineExperience` enforces, re-run on the loaded definition
132
+ * so a definition built by an older SDK, or by hand, cannot slip past.
133
+ */
134
+ export function checkSurfaces(project, definition) {
135
+ const built = buildSurfaces(project, definition);
136
+ const parsed = ExperienceSurfacesSchema.safeParse(built);
137
+ if (parsed.success)
138
+ return [];
139
+ return parsed.error.issues.map((issue) => ({
140
+ check: SURFACES_CHECK,
141
+ severity: 'error',
142
+ message: `${issue.path.length > 0 ? `${issue.path.join('.')}: ` : ''}${issue.message}`,
143
+ hint: 'Fix the page kinds, canvas widgets or suggestedLayout in the experience definition.',
144
+ file: project.entry,
145
+ }));
146
+ }
147
+ /** `dist/surfaces.json`: the surfaces and suggested layout as data, no code. */
148
+ export function buildSurfaces(project, definition) {
149
+ return buildExperienceSurfaces(project.slug, definition);
150
+ }
125
151
  // ─── fixtures ───────────────────────────────────────────────────────────────
126
152
  const FIXTURE_NAME = /^(tool|action)-(.+)\.(default|empty|error)\.json$/;
127
153
  /** `fixtures/<integration>/<kind>-<name>.<variant>.json` must satisfy the pinned output schema. */
@@ -179,12 +205,9 @@ export function buildManifest(input) {
179
205
  ? {}
180
206
  : { isCollapsible: w.isCollapsible })), (w.aiFooterEnabled === undefined
181
207
  ? {}
182
- : { aiFooterEnabled: w.aiFooterEnabled })))), pages: definition.pages.map((p) => ({
183
- id: p.id,
184
- path: p.path,
185
- title: p.title,
186
- integrations: [...p.integrations],
187
- })), workflowTemplates: [...input.workflows] }), (definition.reviewQueue
208
+ : { aiFooterEnabled: w.aiFooterEnabled })))), pages: definition.pages.map((p) => { var _a; return (Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ id: p.id }, (p.path === undefined ? {} : { path: p.path })), { title: p.title, integrations: [...((_a = p.integrations) !== null && _a !== void 0 ? _a : [])] }), (p.kind === undefined ? {} : { kind: p.kind })), (p.widgets === undefined ? {} : { widgets: [...p.widgets] })), (p.routes && p.routes.length > 0
209
+ ? { routes: p.routes.map((r) => ({ path: r.path, title: r.title })) }
210
+ : {}))); }), surfacesDigest: input.surfaces.digest, workflowTemplates: [...input.workflows] }), (definition.reviewQueue
188
211
  ? {
189
212
  reviewQueue: {
190
213
  readsOnly: true,