@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.
- package/README.md +1 -0
- package/dist/commands/catalog.js +11 -7
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/names.d.ts +19 -0
- package/dist/commands/names.js +122 -0
- package/dist/commands/names.js.map +1 -0
- package/dist/commands/publish.d.ts +53 -3
- package/dist/commands/publish.js +58 -16
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/sources.js +15 -5
- package/dist/commands/sources.js.map +1 -1
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +12 -12
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/teams-connect.js +1 -1
- package/dist/commands/teams-connect.js.map +1 -1
- package/dist/experience/boundary.d.ts +2 -2
- package/dist/experience/boundary.js +2 -11
- package/dist/experience/boundary.js.map +1 -1
- package/dist/experience/build.d.ts +3 -1
- package/dist/experience/build.js +15 -26
- package/dist/experience/build.js.map +1 -1
- package/dist/experience/clean-room.d.ts +40 -0
- package/dist/experience/clean-room.js +150 -0
- package/dist/experience/clean-room.js.map +1 -0
- package/dist/experience/definition-checks.d.ts +12 -1
- package/dist/experience/definition-checks.js +34 -11
- package/dist/experience/definition-checks.js.map +1 -1
- package/dist/experience/definition-shape.d.ts +20 -2
- package/dist/experience/definition-shape.js.map +1 -1
- package/dist/experience/publish.d.ts +42 -5
- package/dist/experience/publish.js +195 -11
- package/dist/experience/publish.js.map +1 -1
- package/dist/harness-scaffold.js +3 -1
- package/dist/harness-scaffold.js.map +1 -1
- package/dist/index.js +25 -2
- package/dist/index.js.map +1 -1
- package/dist/names-api.d.ts +90 -0
- package/dist/names-api.js +123 -0
- package/dist/names-api.js.map +1 -0
- package/dist/pack.js +5 -0
- package/dist/pack.js.map +1 -1
- package/dist/product-kinds.d.ts +13 -0
- package/dist/product-kinds.js +17 -0
- package/dist/product-kinds.js.map +1 -0
- package/dist/project-checks.d.ts +7 -0
- package/dist/project-checks.js +47 -0
- package/dist/project-checks.js.map +1 -1
- package/dist/publish-api.d.ts +38 -5
- package/dist/publish-api.js +59 -16
- package/dist/publish-api.js.map +1 -1
- package/dist/repo/publish.d.ts +6 -3
- package/dist/repo/publish.js +48 -14
- package/dist/repo/publish.js.map +1 -1
- package/dist/source-products-api.d.ts +3 -2
- package/dist/source-products-api.js +1 -1
- package/dist/source-products-api.js.map +1 -1
- package/dist/team-connect-api.d.ts +4 -0
- package/dist/team-connect-api.js +1 -1
- package/dist/team-connect-api.js.map +1 -1
- package/dist/toolchain-api.d.ts +15 -0
- package/dist/toolchain-api.js +12 -1
- package/dist/toolchain-api.js.map +1 -1
- package/dist/validate-findings.js +2 -1
- package/dist/validate-findings.js.map +1 -1
- package/package.json +2 -2
- package/templates/experience/package.json.tmpl +7 -4
- package/templates/harness/app/experience/layout/page.tsx.tmpl +1 -0
- package/templates/harness/app/experience/layout.tsx.tmpl +3 -0
- package/templates/harness/app/experience/pages/[id]/[...path]/page.tsx.tmpl +1 -0
- package/templates/package.json.tmpl +5 -4
package/dist/repo/publish.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/repo/publish.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport {\n type RepoDiscovery,\n type RepoPackage,\n RepoManifestError,\n describeAvailability,\n discoverRepo,\n packageContaining,\n planRepoOrder,\n} from '@ekanos/integration-schema/repo';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport {\n type PublishArgs,\n buildManifest,\n escalateForPublish,\n prepareIntegrationSubmission,\n readProjectVersion,\n submitPrepared,\n} from '../commands/publish';\nimport type { CliContext } from '../context';\nimport {\n CliError,\n gateFailedError,\n preconditionError,\n usageError,\n validationError,\n} from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n EXPERIENCE_NOT_SUPPORTED,\n gateExperience,\n packExperience,\n} from '../experience/publish';\nimport { packProject } from '../pack';\nimport { loadProject } from '../project';\nimport { shortHost } from '../ui';\nimport { collectAllFindings } from '../validate-findings';\n\nexport const PUBLISH_ARTIFACT_DIR = path.join('.ekanos', 'publish');\n\nexport interface RepoPublishArgs {\n repoRoot: string;\n /** Where the command was run: inside a package it narrows the selection. */\n cwd: string;\n /** Package names given on the command line. */\n names: readonly string[];\n all: boolean;\n /** The single-project publish arguments, reused for every integration. */\n publish: PublishArgs;\n}\n\nexport type PackageStatus =\n | 'ready'\n | 'built'\n | 'gate failed'\n | 'refused'\n | 'submitted'\n | 'failed'\n | 'not attempted'\n | 'not supported by the host';\n\nexport interface PackageRow {\n name: string;\n kind: 'integration' | 'experience';\n version: string;\n availability: string;\n /** Artifact directory relative to the repo root, when one was built. */\n artifact: string | null;\n status: PackageStatus;\n /** Why the status is not a success, one line. */\n detail?: string;\n findings?: Finding[];\n /** Integration submissions: what the host answered. */\n receipt?: Record<string, unknown>;\n}\n\nconst AVAILABILITY_NOTE =\n 'availability is set by a super admin today; the host does not take it at submission';\n\nfunction loadRepo(root: string): RepoDiscovery {\n try {\n return discoverRepo(root);\n } catch (error) {\n if (error instanceof RepoManifestError) {\n throw validationError(\n error.message,\n 'Fix the root ekanos.json: { \"kind\": \"repo\", \"packages\": [\"integrations/*\", \"experiences/*\"] }.',\n );\n }\n throw error;\n }\n}\n\nfunction select(repo: RepoDiscovery, args: RepoPublishArgs): string[] {\n if (args.all && args.names.length > 0) {\n throw usageError(\n 'Pass package names or --all, not both.',\n 'e.g. \"ekanos publish acme-crm\" or \"ekanos publish --all\".',\n );\n }\n if (args.names.length > 0) return [...args.names];\n if (args.all) return [];\n const here = packageContaining(repo, args.cwd);\n if (here) return [here.name];\n throw usageError(\n 'This is a repo: say what to publish.',\n `Name packages (\"ekanos publish ${repo.packages[0]?.name ?? '<package>'}\"), pass --all, or run it from inside a package. Packages: ${repo.packages.map((p) => p.name).join(', ') || '(none yet; \"ekanos new integration <slug>\")'}.`,\n );\n}\n\nfunction artifactName(pkg: RepoPackage): string {\n return `${pkg.name}-${pkg.version}`;\n}\n\nfunction renderTable(ctx: CliContext, rows: readonly PackageRow[]): string {\n return ctx.ui.table(\n ['package', 'kind', 'version', 'availability', 'artifact', 'status'],\n rows.map((row) => [\n row.name,\n row.kind,\n row.version,\n row.availability,\n row.artifact ?? '-',\n row.detail ? `${row.status}: ${row.detail}` : row.status,\n ]),\n );\n}\n\n/** Gate one integration and, when asked, build its artifact. No network. */\nasync function checkIntegration(\n pkg: RepoPackage,\n repoRoot: string,\n build: boolean,\n): Promise<PackageRow> {\n const row: PackageRow = {\n name: pkg.name,\n kind: 'integration',\n version: pkg.version,\n availability: describeAvailability(pkg.availability),\n artifact: null,\n status: 'ready',\n };\n try {\n readProjectVersion(pkg.dir);\n const loaded = loadProject(pkg.dir);\n const findings = escalateForPublish(await collectAllFindings(loaded));\n const errors = findings.filter((f) => f.severity === 'error');\n if (errors.length > 0) {\n return {\n ...row,\n status: 'gate failed',\n detail: `${errors.length} error${errors.length === 1 ? '' : 's'}; run \"ekanos validate\" in ${pkg.relDir}`,\n findings,\n };\n }\n if (!build) return row;\n\n const packed = packProject(loaded.projectDir);\n const dir = path.join(repoRoot, PUBLISH_ARTIFACT_DIR, artifactName(pkg));\n fs.rmSync(dir, { recursive: true, force: true });\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(\n path.join(dir, `${artifactName(pkg)}.tgz`),\n packed.archive,\n );\n fs.writeFileSync(\n path.join(dir, 'manifest.json'),\n `${buildManifest(loaded, packed.files)}\\n`,\n );\n const contract = path.join(pkg.dir, 'contract.json');\n if (fs.existsSync(contract)) {\n fs.copyFileSync(contract, path.join(dir, 'contract.json'));\n }\n\n return {\n ...row,\n status: 'built',\n artifact: path.relative(repoRoot, dir).split(path.sep).join('/'),\n ...(findings.length > 0 ? { findings } : {}),\n };\n } catch (error) {\n if (error instanceof CliError) {\n return { ...row, status: 'refused', detail: error.message };\n }\n throw error;\n }\n}\n\n/** Gate one experience and, when asked, build and pack it. No network. */\nasync function checkExperience(\n pkg: RepoPackage,\n repoRoot: string,\n build: boolean,\n env: Record<string, string | undefined>,\n): Promise<PackageRow> {\n const row: PackageRow = {\n name: pkg.name,\n kind: 'experience',\n version: pkg.version,\n availability: describeAvailability(pkg.availability),\n artifact: null,\n status: 'ready',\n };\n try {\n const gate = await gateExperience({ dir: pkg.dir, dryRun: true, env });\n const errors = gate.result.findings.filter((f) => f.severity === 'error');\n if (errors.length > 0) {\n return {\n ...row,\n status: 'gate failed',\n detail: `${errors.length} error${errors.length === 1 ? '' : 's'}; run \"ekanos check\" in ${pkg.relDir}`,\n findings: gate.result.findings,\n };\n }\n if (!build) return row;\n\n const dir = path.join(repoRoot, PUBLISH_ARTIFACT_DIR, artifactName(pkg));\n fs.rmSync(dir, { recursive: true, force: true });\n const packed = await packExperience(gate, dir);\n const manifest = path.join(pkg.dir, 'manifest.json');\n if (fs.existsSync(manifest)) {\n fs.copyFileSync(manifest, path.join(dir, 'manifest.json'));\n }\n\n return {\n ...row,\n version: packed.version,\n status: 'built',\n artifact: path.relative(repoRoot, dir).split(path.sep).join('/'),\n };\n } catch (error) {\n if (error instanceof CliError) {\n return { ...row, status: 'refused', detail: error.message };\n }\n throw error;\n }\n}\n\n/**\n * `ekanos publish [name...] | --all [--dry-run]` in a repo.\n *\n * The plan is the selected packages, integrations before the experiences that\n * use them (a same-repo integration is not an override, so it is ordered\n * first, never refused). Every package passes its own gate before anything is\n * sent. `--dry-run` also builds each artifact under `.ekanos/publish/` and\n * touches no network. A real run submits the integrations in order through the\n * existing path; the host cannot admit an experience yet, so selected\n * experiences end the run with that refusal.\n */\nexport async function runRepoPublish(\n ctx: CliContext,\n args: RepoPublishArgs,\n): Promise<ExitCode> {\n const repo = loadRepo(args.repoRoot);\n const names = select(repo, args);\n const plan = planRepoOrder(repo, names);\n\n if (plan.unknown.length > 0) {\n throw usageError(\n `No package named ${plan.unknown.map((n) => `\"${n}\"`).join(', ')} in this repo.`,\n `Packages: ${repo.packages.map((p) => p.name).join(', ') || '(none)'}.`,\n );\n }\n if (args.all && repo.problems.length > 0) {\n throw validationError(\n `${repo.problems.length} package${repo.problems.length === 1 ? ' is' : 's are'} unreadable: ${repo.problems.map((p) => `${p.where} (${p.message})`).join('; ')}`,\n 'Fix or remove them, then re-run \"ekanos publish --all\".',\n );\n }\n if (plan.order.length === 0) {\n throw preconditionError(\n 'This repo has no packages yet.',\n 'Add one with \"ekanos new integration <slug>\" or \"ekanos new experience <slug>\".',\n );\n }\n\n const dryRun = args.publish.dryRun === true;\n const warnings = plan.unselectedDependencies.map(\n (dep) =>\n `${dep.experience} uses ${dep.integration}, which is in this repo but not in this publish: publish ${dep.integration} first, or make sure it is already published at a compatible version.`,\n );\n const notes = plan.order.flatMap((pkg) =>\n pkg.kind === 'integration' && pkg.availability\n ? [\n `${pkg.name}: ${describeAvailability(pkg.availability)} - ${AVAILABILITY_NOTE}.`,\n ]\n : [],\n );\n\n if (!ctx.jsonMode) {\n ctx.log(\n `${dryRun ? 'Dry run: ' : ''}publishing ${plan.order.length} package${plan.order.length === 1 ? '' : 's'} in this order:`,\n );\n plan.order.forEach((pkg, index) =>\n ctx.log(\n ` ${index + 1}. ${pkg.name} (${pkg.kind} ${pkg.version}, ${describeAvailability(pkg.availability)})`,\n ),\n );\n for (const warning of warnings) ctx.warn(` warning: ${warning}`);\n }\n\n // Gate (and, on a dry run, build) every package before anything is sent.\n const rows: PackageRow[] = [];\n for (const pkg of plan.order) {\n rows.push(\n pkg.kind === 'integration'\n ? await checkIntegration(pkg, repo.root, dryRun)\n : await checkExperience(pkg, repo.root, dryRun, args.publish.env),\n );\n }\n\n const data = (extra: Record<string, unknown> = {}) => ({\n repo: repo.root,\n dryRun,\n order: plan.order.map((p) => p.name),\n warnings,\n notes,\n packages: rows,\n ...extra,\n });\n const summary = (title: string) =>\n [title, '', renderTable(ctx, rows), ...notes.map((n) => `note: ${n}`)].join(\n '\\n',\n );\n\n const failed = rows.filter(\n (row) => row.status === 'gate failed' || row.status === 'refused',\n );\n if (failed.length > 0) {\n if (!ctx.jsonMode) ctx.log(summary('Nothing was published.'));\n return ctx.fail(\n gateFailedError(\n `${failed.length} of ${rows.length} package${rows.length === 1 ? '' : 's'} did not pass: ${failed.map((r) => r.name).join(', ')}.`,\n 'Fix the packages named above (the status column says what), then re-run \"ekanos publish\". Nothing was submitted.',\n ),\n data(),\n );\n }\n\n if (dryRun) {\n return ctx.succeed(\n data({ published: false }),\n summary(\n `Dry run: ${rows.length} package${rows.length === 1 ? '' : 's'} built under ${PUBLISH_ARTIFACT_DIR}. Nothing was published.`,\n ),\n );\n }\n\n // Real publish: integrations in plan order through the existing path.\n let stop: CliError | null = null;\n let host: string | null = null;\n for (const [index, pkg] of plan.order.entries()) {\n const row = rows[index] as PackageRow;\n if (pkg.kind === 'experience') {\n row.status = 'not supported by the host';\n continue;\n }\n if (stop) {\n row.status = 'not attempted';\n continue;\n }\n try {\n const single: PublishArgs = { ...args.publish, dir: pkg.dir };\n const prepared = await prepareIntegrationSubmission(ctx, single);\n host = prepared.env.host;\n const submitted = await submitPrepared(ctx, single, prepared);\n row.status = 'submitted';\n row.receipt = submitted.data;\n if (!ctx.jsonMode) ctx.log(submitted.human);\n } catch (error) {\n if (!(error instanceof CliError)) throw error;\n row.status = 'failed';\n row.detail = error.message;\n stop = error;\n }\n }\n\n const hostNote = host ? ` to ${shortHost(host)}` : '';\n if (stop) {\n if (!ctx.jsonMode) ctx.log(summary('Publish stopped.'));\n return ctx.fail(stop, data());\n }\n if (rows.some((row) => row.status === 'not supported by the host')) {\n if (!ctx.jsonMode) ctx.log(summary(`Submitted${hostNote}.`));\n return ctx.fail(\n gateFailedError(\n EXPERIENCE_NOT_SUPPORTED,\n 'The integrations in this publish were submitted; run \"ekanos publish --dry-run\" to build and pack the experiences. The host has no admission path for experiences until it ships one.',\n ),\n data({ published: true }),\n );\n }\n\n return ctx.succeed(\n data({ published: true }),\n summary(\n `Submitted ${rows.length} package${rows.length === 1 ? '' : 's'}${hostNote}.`,\n ),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/repo/publish.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,aAAa,GACd,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAEL,aAAa,EACb,kBAAkB,EAClB,4BAA4B,EAC5B,kBAAkB,EAClB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,UAAU,EACV,eAAe,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,wBAAwB,EACxB,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AAwCpE,uEAAuE;AACvE,SAAS,gBAAgB,CAAC,GAAgB;;IACxC,OAAO,CAAA,MAAA,GAAG,CAAC,YAAY,0CAAE,IAAI,MAAK,QAAQ;QACxC,CAAC,CAAC,0DAA0D,GAAG,CAAC,IAAI,wCAAwC;QAC5G,CAAC,CAAC,yDAAyD,CAAC;AAChE,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,iBAAiB,EAAE,CAAC;YACvC,MAAM,eAAe,CACnB,KAAK,CAAC,OAAO,EACb,gGAAgG,CACjG,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAmB,EAAE,IAAqB;;;IACxD,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,UAAU,CACd,wCAAwC,EACxC,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,IAAI;QAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,UAAU,CACd,sCAAsC,EACtC,kCAAkC,MAAA,MAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,0CAAE,IAAI,mCAAI,WAAW,8DAA8D,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,6CAA6C,GAAG,CACrO,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,GAAgB;IACpC,OAAO,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,WAAW,CAAC,GAAe,EAAE,IAA2B;IAC/D,OAAO,GAAG,CAAC,EAAE,CAAC,KAAK,CACjB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,CAAC,EACpE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,sBAAC;cAChB,GAAG,CAAC,WAAW,mCAAI,GAAG,CAAC,IAAI;QAC3B,GAAG,CAAC,IAAI;QACR,GAAG,CAAC,OAAO;QACX,GAAG,CAAC,YAAY;cAChB,GAAG,CAAC,QAAQ,mCAAI,GAAG;QACnB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM;KACzD,GAAA,CAAC,CACH,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,KAAK,UAAU,gBAAgB,CAC7B,GAAgB,EAChB,QAAgB,EAChB,KAAc;IAEd,MAAM,GAAG,GAAe;QACtB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,YAAY,EAAE,oBAAoB,CAAC,GAAG,CAAC,YAAY,CAAC;QACpD,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,OAAO;KAChB,CAAC;IACF,IAAI,CAAC;QACH,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;QAC9D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,uCACK,GAAG,KACN,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,8BAA8B,GAAG,CAAC,MAAM,EAAE,EACzG,QAAQ,IACR;QACJ,CAAC;QACD,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC;QAEvB,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,oBAAoB,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QACzE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAC1C,MAAM,CAAC,OAAO,CACf,CAAC;QACF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,EAC/B,GAAG,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAC3C,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QACrD,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC;QAC7D,CAAC;QAED,qDACK,GAAG,KACN,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAC7D,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAC5C;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,uCAAY,GAAG,KAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,IAAG;QAC9D,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,KAAK,UAAU,eAAe,CAC5B,GAAgB,EAChB,QAAgB,EAChB,KAAc,EACd,GAAuC;IAEvC,MAAM,GAAG,GAAe;QACtB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,YAAY,EAAE,oBAAoB,CAAC,GAAG,CAAC,YAAY,CAAC;QACpD,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,OAAO;KAChB,CAAC;IACF,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;QAC1E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,uCACK,GAAG,KACN,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,2BAA2B,GAAG,CAAC,MAAM,EAAE,EACtG,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,IAC9B;QACJ,CAAC;QACD,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC;QAEvB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,oBAAoB,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QACzE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QACrD,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC;QAC7D,CAAC;QAED,uCACK,GAAG,KACN,OAAO,EAAE,MAAM,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAChE;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,uCAAY,GAAG,KAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,IAAG;QAC9D,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAe,EACf,IAAqB;IAErB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAExC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,UAAU,CACd,oBAAoB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAChF,aAAa,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,GAAG,CACxE,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,eAAe,CACnB,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,WAAW,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,gBAAgB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAChK,yDAAyD,CAC1D,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,iBAAiB,CACrB,gCAAgC,EAChC,iFAAiF,CAClF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAC9C,CAAC,GAAG,EAAE,EAAE,CACN,GAAG,GAAG,CAAC,UAAU,SAAS,GAAG,CAAC,WAAW,4DAA4D,GAAG,CAAC,WAAW,uEAAuE,CAC9L,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACvC,GAAG,CAAC,IAAI,KAAK,aAAa,IAAI,GAAG,CAAC,YAAY;QAC5C,CAAC,CAAC;YACE,GAAG,GAAG,CAAC,IAAI,KAAK,oBAAoB,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,gBAAgB,CAAC,GAAG,CAAC,GAAG;SACrF;QACH,CAAC,CAAC,EAAE,CACP,CAAC;IAEF,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAClB,GAAG,CAAC,GAAG,CACL,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,cAAc,IAAI,CAAC,KAAK,CAAC,MAAM,WAAW,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAC1H,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAChC,GAAG,CAAC,GAAG,CACL,KAAK,KAAK,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,KAAK,oBAAoB,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CACtG,CACF,CAAC;QACF,KAAK,MAAM,OAAO,IAAI,QAAQ;YAAE,GAAG,CAAC,IAAI,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,yEAAyE;IACzE,MAAM,IAAI,GAAiB,EAAE,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CACP,GAAG,CAAC,IAAI,KAAK,aAAa;YACxB,CAAC,CAAC,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;YAChD,CAAC,CAAC,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CACpE,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,KAAK,GAA4B,EAAE,EAAE,EAAE,CAAC,iBACpD,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,MAAM,EACN,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EACpC,QAAQ;QACR,KAAK,EACL,QAAQ,EAAE,IAAI,IACX,KAAK,EACR,CAAC;IACH,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE,CAChC,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CACzE,IAAI,CACL,CAAC;IAEJ,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CACxB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,aAAa,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,CAClE,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAC9D,OAAO,GAAG,CAAC,IAAI,CACb,eAAe,CACb,GAAG,MAAM,CAAC,MAAM,OAAO,IAAI,CAAC,MAAM,WAAW,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAClI,kHAAkH,CACnH,EACD,IAAI,EAAE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,OAAO,CAChB,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAC1B,OAAO,CACL,YAAY,IAAI,CAAC,MAAM,WAAW,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,gBAAgB,oBAAoB,0BAA0B,CAC7H,CACF,CAAC;IACJ,CAAC;IAED,sEAAsE;IACtE,IAAI,IAAI,GAAoB,IAAI,CAAC;IACjC,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAe,CAAC;QACtC,IAAI,IAAI,EAAE,CAAC;YACT,GAAG,CAAC,MAAM,GAAG,eAAe,CAAC;YAC7B,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,MAAM,mCAAqB,IAAI,CAAC,OAAO,KAAE,GAAG,EAAE,GAAG,CAAC,GAAG,GAAE,CAAC;gBAC9D,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC;oBAChC,GAAG,EAAE,GAAG,CAAC,GAAG;oBACZ,MAAM,EAAE,KAAK;oBACb,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG;iBACtB,CAAC,CAAC;gBACH,MAAM,UAAU,GAAG,MAAM,gBAAgB,CACvC,GAAG,kCACE,MAAM,KAAE,MAAM,EAAE,KAAK,KAC1B,IAAI,CACL,CAAC;gBACF,IAAI,UAAU,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;oBACxC,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;oBACvB,GAAG,CAAC,MAAM,GAAG,2BAA2B,CAAC;oBACzC,SAAS;gBACX,CAAC;gBACD,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC;gBACzB,GAAG,CAAC,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC;gBAC9B,IAAI,CAAC,GAAG,CAAC,QAAQ;oBAAE,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC/C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,KAAK,YAAY,QAAQ,CAAC;oBAAE,MAAM,KAAK,CAAC;gBAC9C,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC;gBACtB,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC3B,IAAI,GAAG,KAAK,CAAC;YACf,CAAC;YACD,SAAS;QACX,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,mCAAqB,IAAI,CAAC,OAAO,KAAE,GAAG,EAAE,GAAG,CAAC,GAAG,GAAE,CAAC;YAC9D,MAAM,QAAQ,GAAG,MAAM,4BAA4B,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACjE,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;YACzB,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC9D,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC;YACzB,GAAG,CAAC,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC;YAC7B,IAAI,OAAO,SAAS,CAAC,IAAI,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;gBACnD,GAAG,CAAC,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC;YAC/C,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,QAAQ,CAAC;gBAAE,MAAM,KAAK,CAAC;YAC9C,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC;YACtB,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;YAC3B,IAAI,GAAG,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACxD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,2BAA2B,CAAC,EAAE,CAAC;QACnE,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,QAAQ,GAAG,CAAC,CAAC,CAAC;QAC7D,OAAO,GAAG,CAAC,IAAI,CACb,eAAe,CACb,wBAAwB,EACxB,0LAA0L,CAC3L,EACD,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAC1B,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,CAChB,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EACzB,OAAO,CACL,aAAa,IAAI,CAAC,MAAM,WAAW,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,QAAQ,GAAG,CAC9E,CACF,CAAC;AACJ,CAAC","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport {\n type RepoDiscovery,\n type RepoPackage,\n RepoManifestError,\n describeAvailability,\n discoverRepo,\n packageContaining,\n planRepoOrder,\n} from '@ekanos/integration-schema/repo';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport {\n type PublishArgs,\n buildManifest,\n escalateForPublish,\n prepareIntegrationSubmission,\n readProjectVersion,\n submitPrepared,\n} from '../commands/publish';\nimport type { CliContext } from '../context';\nimport {\n CliError,\n gateFailedError,\n preconditionError,\n usageError,\n validationError,\n} from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n EXPERIENCE_NOT_SUPPORTED,\n gateExperience,\n packExperience,\n submitExperience,\n} from '../experience/publish';\nimport { packProject } from '../pack';\nimport { loadProject } from '../project';\nimport { shortHost } from '../ui';\nimport { collectAllFindings } from '../validate-findings';\n\nexport const PUBLISH_ARTIFACT_DIR = path.join('.ekanos', 'publish');\n\nexport interface RepoPublishArgs {\n repoRoot: string;\n /** Where the command was run: inside a package it narrows the selection. */\n cwd: string;\n /** Package names given on the command line. */\n names: readonly string[];\n all: boolean;\n /** The single-project publish arguments, reused for every integration. */\n publish: PublishArgs;\n}\n\nexport type PackageStatus =\n | 'ready'\n | 'built'\n | 'gate failed'\n | 'refused'\n | 'submitted'\n | 'failed'\n | 'not attempted'\n | 'not supported by the host';\n\nexport interface PackageRow {\n name: string;\n kind: 'integration' | 'experience';\n version: string;\n availability: string;\n /** Artifact directory relative to the repo root, when one was built. */\n artifact: string | null;\n status: PackageStatus;\n /** Why the status is not a success, one line. */\n detail?: string;\n findings?: Finding[];\n /** Integration submissions: what the host answered. */\n receipt?: Record<string, unknown>;\n /** The full name the host gave a submitted integration (`acme/google-drive`). */\n displayName?: string;\n}\n\n/** What an integration's declared availability means at submission. */\nfunction availabilityNote(pkg: RepoPackage): string {\n return pkg.availability?.mode === 'global'\n ? `needs an approved global name (run \"ekanos names claim ${pkg.name}\"); without one the publish is refused`\n : 'publishes restricted, named under your Source namespace';\n}\n\nfunction loadRepo(root: string): RepoDiscovery {\n try {\n return discoverRepo(root);\n } catch (error) {\n if (error instanceof RepoManifestError) {\n throw validationError(\n error.message,\n 'Fix the root ekanos.json: { \"kind\": \"repo\", \"packages\": [\"integrations/*\", \"experiences/*\"] }.',\n );\n }\n throw error;\n }\n}\n\nfunction select(repo: RepoDiscovery, args: RepoPublishArgs): string[] {\n if (args.all && args.names.length > 0) {\n throw usageError(\n 'Pass package names or --all, not both.',\n 'e.g. \"ekanos publish acme-crm\" or \"ekanos publish --all\".',\n );\n }\n if (args.names.length > 0) return [...args.names];\n if (args.all) return [];\n const here = packageContaining(repo, args.cwd);\n if (here) return [here.name];\n throw usageError(\n 'This is a repo: say what to publish.',\n `Name packages (\"ekanos publish ${repo.packages[0]?.name ?? '<package>'}\"), pass --all, or run it from inside a package. Packages: ${repo.packages.map((p) => p.name).join(', ') || '(none yet; \"ekanos new integration <slug>\")'}.`,\n );\n}\n\nfunction artifactName(pkg: RepoPackage): string {\n return `${pkg.name}-${pkg.version}`;\n}\n\nfunction renderTable(ctx: CliContext, rows: readonly PackageRow[]): string {\n return ctx.ui.table(\n ['package', 'kind', 'version', 'availability', 'artifact', 'status'],\n rows.map((row) => [\n row.displayName ?? row.name,\n row.kind,\n row.version,\n row.availability,\n row.artifact ?? '-',\n row.detail ? `${row.status}: ${row.detail}` : row.status,\n ]),\n );\n}\n\n/** Gate one integration and, when asked, build its artifact. No network. */\nasync function checkIntegration(\n pkg: RepoPackage,\n repoRoot: string,\n build: boolean,\n): Promise<PackageRow> {\n const row: PackageRow = {\n name: pkg.name,\n kind: 'integration',\n version: pkg.version,\n availability: describeAvailability(pkg.availability),\n artifact: null,\n status: 'ready',\n };\n try {\n readProjectVersion(pkg.dir);\n const loaded = loadProject(pkg.dir);\n const findings = escalateForPublish(await collectAllFindings(loaded));\n const errors = findings.filter((f) => f.severity === 'error');\n if (errors.length > 0) {\n return {\n ...row,\n status: 'gate failed',\n detail: `${errors.length} error${errors.length === 1 ? '' : 's'}; run \"ekanos validate\" in ${pkg.relDir}`,\n findings,\n };\n }\n if (!build) return row;\n\n const packed = packProject(loaded.projectDir);\n const dir = path.join(repoRoot, PUBLISH_ARTIFACT_DIR, artifactName(pkg));\n fs.rmSync(dir, { recursive: true, force: true });\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(\n path.join(dir, `${artifactName(pkg)}.tgz`),\n packed.archive,\n );\n fs.writeFileSync(\n path.join(dir, 'manifest.json'),\n `${buildManifest(loaded, packed.files)}\\n`,\n );\n const contract = path.join(pkg.dir, 'contract.json');\n if (fs.existsSync(contract)) {\n fs.copyFileSync(contract, path.join(dir, 'contract.json'));\n }\n\n return {\n ...row,\n status: 'built',\n artifact: path.relative(repoRoot, dir).split(path.sep).join('/'),\n ...(findings.length > 0 ? { findings } : {}),\n };\n } catch (error) {\n if (error instanceof CliError) {\n return { ...row, status: 'refused', detail: error.message };\n }\n throw error;\n }\n}\n\n/** Gate one experience and, when asked, build and pack it. No network. */\nasync function checkExperience(\n pkg: RepoPackage,\n repoRoot: string,\n build: boolean,\n env: Record<string, string | undefined>,\n): Promise<PackageRow> {\n const row: PackageRow = {\n name: pkg.name,\n kind: 'experience',\n version: pkg.version,\n availability: describeAvailability(pkg.availability),\n artifact: null,\n status: 'ready',\n };\n try {\n const gate = await gateExperience({ dir: pkg.dir, dryRun: true, env });\n const errors = gate.result.findings.filter((f) => f.severity === 'error');\n if (errors.length > 0) {\n return {\n ...row,\n status: 'gate failed',\n detail: `${errors.length} error${errors.length === 1 ? '' : 's'}; run \"ekanos check\" in ${pkg.relDir}`,\n findings: gate.result.findings,\n };\n }\n if (!build) return row;\n\n const dir = path.join(repoRoot, PUBLISH_ARTIFACT_DIR, artifactName(pkg));\n fs.rmSync(dir, { recursive: true, force: true });\n const packed = await packExperience(gate, dir);\n const manifest = path.join(pkg.dir, 'manifest.json');\n if (fs.existsSync(manifest)) {\n fs.copyFileSync(manifest, path.join(dir, 'manifest.json'));\n }\n\n return {\n ...row,\n version: packed.version,\n status: 'built',\n artifact: path.relative(repoRoot, dir).split(path.sep).join('/'),\n };\n } catch (error) {\n if (error instanceof CliError) {\n return { ...row, status: 'refused', detail: error.message };\n }\n throw error;\n }\n}\n\n/**\n * `ekanos publish [name...] | --all [--dry-run]` in a repo.\n *\n * The plan is the selected packages, integrations before the experiences that\n * use them (a same-repo integration is not an override, so it is ordered\n * first, never refused). Every package passes its own gate before anything is\n * sent. `--dry-run` also builds each artifact under `.ekanos/publish/` and\n * touches no network. A real run submits the packages in order through the\n * existing path; an experience goes to a host that advertises experience\n * submissions, and against an older host that cannot admit one the run ends\n * with that refusal after the rest were submitted.\n */\nexport async function runRepoPublish(\n ctx: CliContext,\n args: RepoPublishArgs,\n): Promise<ExitCode> {\n const repo = loadRepo(args.repoRoot);\n const names = select(repo, args);\n const plan = planRepoOrder(repo, names);\n\n if (plan.unknown.length > 0) {\n throw usageError(\n `No package named ${plan.unknown.map((n) => `\"${n}\"`).join(', ')} in this repo.`,\n `Packages: ${repo.packages.map((p) => p.name).join(', ') || '(none)'}.`,\n );\n }\n if (args.all && repo.problems.length > 0) {\n throw validationError(\n `${repo.problems.length} package${repo.problems.length === 1 ? ' is' : 's are'} unreadable: ${repo.problems.map((p) => `${p.where} (${p.message})`).join('; ')}`,\n 'Fix or remove them, then re-run \"ekanos publish --all\".',\n );\n }\n if (plan.order.length === 0) {\n throw preconditionError(\n 'This repo has no packages yet.',\n 'Add one with \"ekanos new integration <slug>\" or \"ekanos new experience <slug>\".',\n );\n }\n\n const dryRun = args.publish.dryRun === true;\n const warnings = plan.unselectedDependencies.map(\n (dep) =>\n `${dep.experience} uses ${dep.integration}, which is in this repo but not in this publish: publish ${dep.integration} first, or make sure it is already published at a compatible version.`,\n );\n const notes = plan.order.flatMap((pkg) =>\n pkg.kind === 'integration' && pkg.availability\n ? [\n `${pkg.name}: ${describeAvailability(pkg.availability)} - ${availabilityNote(pkg)}.`,\n ]\n : [],\n );\n\n if (!ctx.jsonMode) {\n ctx.log(\n `${dryRun ? 'Dry run: ' : ''}publishing ${plan.order.length} package${plan.order.length === 1 ? '' : 's'} in this order:`,\n );\n plan.order.forEach((pkg, index) =>\n ctx.log(\n ` ${index + 1}. ${pkg.name} (${pkg.kind} ${pkg.version}, ${describeAvailability(pkg.availability)})`,\n ),\n );\n for (const warning of warnings) ctx.warn(` warning: ${warning}`);\n }\n\n // Gate (and, on a dry run, build) every package before anything is sent.\n const rows: PackageRow[] = [];\n for (const pkg of plan.order) {\n rows.push(\n pkg.kind === 'integration'\n ? await checkIntegration(pkg, repo.root, dryRun)\n : await checkExperience(pkg, repo.root, dryRun, args.publish.env),\n );\n }\n\n const data = (extra: Record<string, unknown> = {}) => ({\n repo: repo.root,\n dryRun,\n order: plan.order.map((p) => p.name),\n warnings,\n notes,\n packages: rows,\n ...extra,\n });\n const summary = (title: string) =>\n [title, '', renderTable(ctx, rows), ...notes.map((n) => `note: ${n}`)].join(\n '\\n',\n );\n\n const failed = rows.filter(\n (row) => row.status === 'gate failed' || row.status === 'refused',\n );\n if (failed.length > 0) {\n if (!ctx.jsonMode) ctx.log(summary('Nothing was published.'));\n return ctx.fail(\n gateFailedError(\n `${failed.length} of ${rows.length} package${rows.length === 1 ? '' : 's'} did not pass: ${failed.map((r) => r.name).join(', ')}.`,\n 'Fix the packages named above (the status column says what), then re-run \"ekanos publish\". Nothing was submitted.',\n ),\n data(),\n );\n }\n\n if (dryRun) {\n return ctx.succeed(\n data({ published: false }),\n summary(\n `Dry run: ${rows.length} package${rows.length === 1 ? '' : 's'} built under ${PUBLISH_ARTIFACT_DIR}. Nothing was published.`,\n ),\n );\n }\n\n // Real publish: integrations in plan order through the existing path.\n let stop: CliError | null = null;\n let host: string | null = null;\n for (const [index, pkg] of plan.order.entries()) {\n const row = rows[index] as PackageRow;\n if (stop) {\n row.status = 'not attempted';\n continue;\n }\n if (pkg.kind === 'experience') {\n try {\n const single: PublishArgs = { ...args.publish, dir: pkg.dir };\n const gate = await gateExperience({\n dir: pkg.dir,\n dryRun: false,\n env: args.publish.env,\n });\n const submission = await submitExperience(\n ctx,\n { ...single, dryRun: false },\n gate,\n );\n if (submission.status === 'unsupported') {\n host = submission.host;\n row.status = 'not supported by the host';\n continue;\n }\n row.status = 'submitted';\n row.receipt = submission.data;\n if (!ctx.jsonMode) ctx.log(submission.human);\n } catch (error) {\n if (!(error instanceof CliError)) throw error;\n row.status = 'failed';\n row.detail = error.message;\n stop = error;\n }\n continue;\n }\n try {\n const single: PublishArgs = { ...args.publish, dir: pkg.dir };\n const prepared = await prepareIntegrationSubmission(ctx, single);\n host = prepared.env.host;\n const submitted = await submitPrepared(ctx, single, prepared);\n row.status = 'submitted';\n row.receipt = submitted.data;\n if (typeof submitted.data.displayName === 'string') {\n row.displayName = submitted.data.displayName;\n }\n if (!ctx.jsonMode) ctx.log(submitted.human);\n } catch (error) {\n if (!(error instanceof CliError)) throw error;\n row.status = 'failed';\n row.detail = error.message;\n stop = error;\n }\n }\n\n const hostNote = host ? ` to ${shortHost(host)}` : '';\n if (stop) {\n if (!ctx.jsonMode) ctx.log(summary('Publish stopped.'));\n return ctx.fail(stop, data());\n }\n if (rows.some((row) => row.status === 'not supported by the host')) {\n if (!ctx.jsonMode) ctx.log(summary(`Submitted${hostNote}.`));\n return ctx.fail(\n gateFailedError(\n EXPERIENCE_NOT_SUPPORTED,\n 'The other packages in this publish were submitted; run \"ekanos publish --dry-run\" to build and pack the experiences. This host has no admission path for experiences until it ships one.',\n ),\n data({ published: true }),\n );\n }\n\n return ctx.succeed(\n data({ published: true }),\n summary(\n `Submitted ${rows.length} package${rows.length === 1 ? '' : 's'}${hostNote}.`,\n ),\n );\n}\n"]}
|
|
@@ -4,6 +4,7 @@ export declare const SourceProductSchema: z.ZodObject<{
|
|
|
4
4
|
name: z.ZodString;
|
|
5
5
|
kind: z.ZodEnum<{
|
|
6
6
|
experience: "experience";
|
|
7
|
+
feature: "feature";
|
|
7
8
|
integration: "integration";
|
|
8
9
|
}>;
|
|
9
10
|
enabled: z.ZodBoolean;
|
|
@@ -31,7 +32,7 @@ export declare function listSourceProducts(host: string, token: string, source:
|
|
|
31
32
|
products: {
|
|
32
33
|
slug: string;
|
|
33
34
|
name: string;
|
|
34
|
-
kind: "experience" | "integration";
|
|
35
|
+
kind: "experience" | "feature" | "integration";
|
|
35
36
|
enabled: boolean;
|
|
36
37
|
lever: "entitlement" | "flag" | "plan";
|
|
37
38
|
registered: boolean;
|
|
@@ -53,7 +54,7 @@ export declare function setSourceProduct(host: string, token: string, input: {
|
|
|
53
54
|
product: {
|
|
54
55
|
slug: string;
|
|
55
56
|
name: string;
|
|
56
|
-
kind: "experience" | "integration";
|
|
57
|
+
kind: "experience" | "feature" | "integration";
|
|
57
58
|
enabled: boolean;
|
|
58
59
|
lever: "entitlement" | "flag" | "plan";
|
|
59
60
|
registered: boolean;
|
|
@@ -29,7 +29,7 @@ const SourceRefSchema = z.object({
|
|
|
29
29
|
export const SourceProductSchema = z.object({
|
|
30
30
|
slug: z.string(),
|
|
31
31
|
name: z.string(),
|
|
32
|
-
kind: z.enum(['experience', 'integration']),
|
|
32
|
+
kind: z.enum(['feature', 'experience', 'integration']),
|
|
33
33
|
enabled: z.boolean(),
|
|
34
34
|
lever: z.enum(['flag', 'plan', 'entitlement']),
|
|
35
35
|
registered: z.boolean(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-products-api.js","sourceRoot":"","sources":["../src/source-products-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport { request } from './auth/ekanos-api';\nimport {\n authRequiredError,\n forbiddenError,\n invalidStateError,\n networkError,\n notFoundError,\n usageError,\n} from './errors';\n\n/**\n * The CLI's client for a Source's products (`ekanos sources products`).\n *\n * GET /api/partner/sources/products?source=<slug|id>\n * → { source, products: [{ slug, name, kind, enabled, lever, registered, owned }],\n * canEnable, canEnableOwned }\n * PUT /api/partner/sources/products/<product>?source=<slug|id> enable\n * DELETE /api/partner/sources/products/<product>?source=<slug|id> disable\n * → { source, product, changed }\n *\n * → 401 / 3xx refresh once and retry\n * → 403 SOURCE_ADMIN_REQUIRED no Source admin seat on the Source\n * → 403 SUPER_ADMIN_REQUIRED not an integration the Source built: a\n * super-admin grant, which an `ekanos login`\n * session never carries\n * → 409 REFUSED a dependency or demo-only guard refused\n *\n * The shapes are read here rather than from `@ekanos/integration-schema`, so\n * this CLI works against a host without a schema release.\n */\n\nconst SourceRefSchema = z.object({\n id: z.string(),\n slug: z.string(),\n name: z.string().nullable(),\n});\n\nexport const SourceProductSchema = z.object({\n slug: z.string(),\n name: z.string(),\n kind: z.enum(['experience', 'integration']),\n enabled: z.boolean(),\n lever: z.enum(['flag', 'plan', 'entitlement']),\n registered: z.boolean(),\n /** The Source built it, so its Source admin may switch it. Absent on an\n * older host, which allowed no Source admin to. */\n owned: z.boolean().optional().default(false),\n});\nexport type SourceProduct = z.infer<typeof SourceProductSchema>;\n\nconst ListSchema = z.object({\n source: SourceRefSchema,\n products: z.array(SourceProductSchema),\n canEnable: z.boolean(),\n canEnableOwned: z.boolean().optional().default(false),\n});\nconst ChangeSchema = z.object({\n source: SourceRefSchema,\n product: SourceProductSchema,\n changed: z.boolean(),\n});\n\nexport type SourceProductsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nexport function listSourceProducts(\n host: string,\n token: string,\n source: string,\n) {\n return call(host, token, ListSchema, {\n method: 'GET',\n path: `/api/partner/sources/products?${new URLSearchParams({ source })}`,\n });\n}\n\nexport function setSourceProduct(\n host: string,\n token: string,\n input: { source: string; product: string; enabled: boolean },\n) {\n return call(host, token, ChangeSchema, {\n method: input.enabled ? 'PUT' : 'DELETE',\n path: `/api/partner/sources/products/${encodeURIComponent(input.product)}?${new URLSearchParams({ source: input.source })}`,\n });\n}\n\nasync function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: { method: 'GET' | 'PUT' | 'DELETE'; path: string },\n): Promise<SourceProductsApiResult<T>> {\n const response = await request(`${host}${init.path}`, {\n method: init.method,\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\n\n if (response.status === 403 && code === 'MFA_REQUIRED') {\n throw authRequiredError(\n message ?? `${host} requires multi-factor authentication.`,\n 'Complete MFA in the browser, then run \"ekanos login\" again.',\n );\n }\n if (response.status === 403 && code === 'SUPER_ADMIN_REQUIRED') {\n throw forbiddenError(\n message ?? 'Only a super-admin enables this product for a Source.',\n 'A Source admin switches only the integrations their Source built. Anything else a super-admin does in the browser: /admin/sources → the Source → Products (Experiences are switched on the same page).',\n );\n }\n if (response.status === 403 && code === 'SOURCE_ADMIN_REQUIRED') {\n throw forbiddenError(\n message ?? 'Switching a product needs a Source admin seat on the Source.',\n 'Run \"ekanos sources\" to see your seats; a Source admin grants the seat.',\n );\n }\n if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'PRODUCT_NOT_FOUND'\n ? '\"ekanos sources products\" lists the catalog.'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409) {\n throw invalidStateError(\n message ?? 'The host refused the change.',\n '\"ekanos sources products\" shows what this Source has.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos sources --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${init.path.split('?')[0]} responded ${response.status}.`,\n response.status >= 500\n ? 'The Ekanos deployment returned a server error. Retry shortly.'\n : 'Confirm the host runs a build that includes the source products routes.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}/api/partner/sources/products answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the source products contract.',\n );\n }\n return { status: 'ok', data: parsed.data };\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"source-products-api.js","sourceRoot":"","sources":["../src/source-products-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,UAAU,GACX,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;IACtD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE;IACvB;uDACmD;IACnD,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CAC7C,CAAC,CAAC;AAGH,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC;IACtC,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;IACtB,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACtD,CAAC,CAAC;AACH,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,MAAM,EAAE,eAAe;IACvB,OAAO,EAAE,mBAAmB;IAC5B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CACrB,CAAC,CAAC;AAMH,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,KAAa,EACb,MAAc;IAEd,OAAO,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;QACnC,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,iCAAiC,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;KACzE,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,IAAY,EACZ,KAAa,EACb,KAA4D;IAE5D,OAAO,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE;QACrC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;QACxC,IAAI,EAAE,iCAAiC,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;KAC5H,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,IAAI,CACjB,IAAY,EACZ,WAAmB,EACnB,MAAoB,EACpB,IAAwD;IAExD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE;QACpD,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;KACF,CAAC,CAAC;IAEH,IACE,QAAQ,CAAC,MAAM,KAAK,GAAG;QACvB,CAAC,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,EACjD,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IACpC,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,OAAO,GACX,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAEnE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QACvD,MAAM,iBAAiB,CACrB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,GAAG,IAAI,wCAAwC,EAC1D,6DAA6D,CAC9D,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,KAAK,sBAAsB,EAAE,CAAC;QAC/D,MAAM,cAAc,CAClB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,uDAAuD,EAClE,wMAAwM,CACzM,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;QAChE,MAAM,cAAc,CAClB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,8DAA8D,EACzE,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,cAAc,CAClB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,+BAA+B,EAC1C,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,aAAa,CACjB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,YAAY,EACvB,IAAI,KAAK,mBAAmB;YAC1B,CAAC,CAAC,8CAA8C;YAChD,CAAC,CAAC,8DAA8D,CACnE,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,iBAAiB,CACrB,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,8BAA8B,EACzC,uDAAuD,CACxD,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,UAAU,CACd,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,+BAA+B,EAC1C,8BAA8B,CAC/B,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,YAAY,CAChB,GAAG,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,QAAQ,CAAC,MAAM,GAAG,EACjE,QAAQ,CAAC,MAAM,IAAI,GAAG;YACpB,CAAC,CAAC,+DAA+D;YACjE,CAAC,CAAC,yEAAyE,CAC9E,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,YAAY,CAChB,GAAG,IAAI,2EAA2E,EAClF,uFAAuF,CACxF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,QAAkB;IAElB,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC9C,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;YAClD,CAAC,CAAE,MAAkC;YACrC,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;eAAO,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC","sourcesContent":["import { z } from 'zod';\n\nimport { request } from './auth/ekanos-api';\nimport {\n authRequiredError,\n forbiddenError,\n invalidStateError,\n networkError,\n notFoundError,\n usageError,\n} from './errors';\n\n/**\n * The CLI's client for a Source's products (`ekanos sources products`).\n *\n * GET /api/partner/sources/products?source=<slug|id>\n * → { source, products: [{ slug, name, kind, enabled, lever, registered, owned }],\n * canEnable, canEnableOwned }\n * PUT /api/partner/sources/products/<product>?source=<slug|id> enable\n * DELETE /api/partner/sources/products/<product>?source=<slug|id> disable\n * → { source, product, changed }\n *\n * → 401 / 3xx refresh once and retry\n * → 403 SOURCE_ADMIN_REQUIRED no Source admin seat on the Source\n * → 403 SUPER_ADMIN_REQUIRED not an integration the Source built: a\n * super-admin grant, which an `ekanos login`\n * session never carries\n * → 409 REFUSED a dependency or demo-only guard refused\n *\n * The shapes are read here rather than from `@ekanos/integration-schema`, so\n * this CLI works against a host without a schema release.\n */\n\nconst SourceRefSchema = z.object({\n id: z.string(),\n slug: z.string(),\n name: z.string().nullable(),\n});\n\nexport const SourceProductSchema = z.object({\n slug: z.string(),\n name: z.string(),\n kind: z.enum(['feature', 'experience', 'integration']),\n enabled: z.boolean(),\n lever: z.enum(['flag', 'plan', 'entitlement']),\n registered: z.boolean(),\n /** The Source built it, so its Source admin may switch it. Absent on an\n * older host, which allowed no Source admin to. */\n owned: z.boolean().optional().default(false),\n});\nexport type SourceProduct = z.infer<typeof SourceProductSchema>;\n\nconst ListSchema = z.object({\n source: SourceRefSchema,\n products: z.array(SourceProductSchema),\n canEnable: z.boolean(),\n canEnableOwned: z.boolean().optional().default(false),\n});\nconst ChangeSchema = z.object({\n source: SourceRefSchema,\n product: SourceProductSchema,\n changed: z.boolean(),\n});\n\nexport type SourceProductsApiResult<T> =\n | { status: 'ok'; data: T }\n | { status: 'unauthorized' };\n\nexport function listSourceProducts(\n host: string,\n token: string,\n source: string,\n) {\n return call(host, token, ListSchema, {\n method: 'GET',\n path: `/api/partner/sources/products?${new URLSearchParams({ source })}`,\n });\n}\n\nexport function setSourceProduct(\n host: string,\n token: string,\n input: { source: string; product: string; enabled: boolean },\n) {\n return call(host, token, ChangeSchema, {\n method: input.enabled ? 'PUT' : 'DELETE',\n path: `/api/partner/sources/products/${encodeURIComponent(input.product)}?${new URLSearchParams({ source: input.source })}`,\n });\n}\n\nasync function call<T>(\n host: string,\n accessToken: string,\n schema: z.ZodType<T>,\n init: { method: 'GET' | 'PUT' | 'DELETE'; path: string },\n): Promise<SourceProductsApiResult<T>> {\n const response = await request(`${host}${init.path}`, {\n method: init.method,\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (\n response.status === 401 ||\n (response.status >= 300 && response.status < 400)\n ) {\n return { status: 'unauthorized' };\n }\n\n const body = await readOptionalJson(response);\n const code = typeof body.code === 'string' ? body.code : null;\n const message =\n typeof body.error === 'string' && body.error ? body.error : null;\n\n if (response.status === 403 && code === 'MFA_REQUIRED') {\n throw authRequiredError(\n message ?? `${host} requires multi-factor authentication.`,\n 'Complete MFA in the browser, then run \"ekanos login\" again.',\n );\n }\n if (response.status === 403 && code === 'SUPER_ADMIN_REQUIRED') {\n throw forbiddenError(\n message ?? 'Only a super-admin enables this product for a Source.',\n 'A Source admin switches only the integrations their Source built. Anything else a super-admin does in the browser: /admin/sources → the Source → Products (Experiences are switched on the same page).',\n );\n }\n if (response.status === 403 && code === 'SOURCE_ADMIN_REQUIRED') {\n throw forbiddenError(\n message ?? 'Switching a product needs a Source admin seat on the Source.',\n 'Run \"ekanos sources\" to see your seats; a Source admin grants the seat.',\n );\n }\n if (response.status === 403) {\n throw forbiddenError(\n message ?? 'Not permitted on this Source.',\n 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 404) {\n throw notFoundError(\n message ?? 'Not found.',\n code === 'PRODUCT_NOT_FOUND'\n ? '\"ekanos sources products\" lists the catalog.'\n : 'Run \"ekanos sources\" to list the sources you hold a seat on.',\n );\n }\n if (response.status === 409) {\n throw invalidStateError(\n message ?? 'The host refused the change.',\n '\"ekanos sources products\" shows what this Source has.',\n );\n }\n if (response.status === 400) {\n throw usageError(\n message ?? 'The host refused the request.',\n 'See \"ekanos sources --help\".',\n );\n }\n if (!response.ok) {\n throw networkError(\n `${host}${init.path.split('?')[0]} responded ${response.status}.`,\n response.status >= 500\n ? 'The Ekanos deployment returned a server error. Retry shortly.'\n : 'Confirm the host runs a build that includes the source products routes.',\n );\n }\n\n const parsed = schema.safeParse(body.data);\n if (!parsed.success) {\n throw networkError(\n `${host}/api/partner/sources/products answered in a shape this CLI does not read.`,\n 'Run \"ekanos upgrade\" — the CLI and the host disagree on the source products contract.',\n );\n }\n return { status: 'ok', data: parsed.data };\n}\n\nasync function readOptionalJson(\n response: Response,\n): Promise<Record<string, unknown>> {\n try {\n const parsed: unknown = await response.json();\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n"]}
|
|
@@ -31,6 +31,7 @@ export declare const TeamConnectPlanSchema: z.ZodObject<{
|
|
|
31
31
|
name: z.ZodString;
|
|
32
32
|
kind: z.ZodNullable<z.ZodEnum<{
|
|
33
33
|
experience: "experience";
|
|
34
|
+
feature: "feature";
|
|
34
35
|
integration: "integration";
|
|
35
36
|
}>>;
|
|
36
37
|
}, z.core.$strip>;
|
|
@@ -116,6 +117,7 @@ declare const ShowSchema: z.ZodObject<{
|
|
|
116
117
|
name: z.ZodString;
|
|
117
118
|
kind: z.ZodNullable<z.ZodEnum<{
|
|
118
119
|
experience: "experience";
|
|
120
|
+
feature: "feature";
|
|
119
121
|
integration: "integration";
|
|
120
122
|
}>>;
|
|
121
123
|
}, z.core.$strip>;
|
|
@@ -196,6 +198,7 @@ declare const StartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
196
198
|
name: z.ZodString;
|
|
197
199
|
kind: z.ZodNullable<z.ZodEnum<{
|
|
198
200
|
experience: "experience";
|
|
201
|
+
feature: "feature";
|
|
199
202
|
integration: "integration";
|
|
200
203
|
}>>;
|
|
201
204
|
}, z.core.$strip>;
|
|
@@ -276,6 +279,7 @@ declare const StartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
276
279
|
name: z.ZodString;
|
|
277
280
|
kind: z.ZodNullable<z.ZodEnum<{
|
|
278
281
|
experience: "experience";
|
|
282
|
+
feature: "feature";
|
|
279
283
|
integration: "integration";
|
|
280
284
|
}>>;
|
|
281
285
|
}, z.core.$strip>;
|
package/dist/team-connect-api.js
CHANGED
|
@@ -26,7 +26,7 @@ export const TeamConnectPlanSchema = z.object({
|
|
|
26
26
|
product: z.object({
|
|
27
27
|
slug: z.string(),
|
|
28
28
|
name: z.string(),
|
|
29
|
-
kind: z.enum(['experience', 'integration']).nullable(),
|
|
29
|
+
kind: z.enum(['feature', 'experience', 'integration']).nullable(),
|
|
30
30
|
}),
|
|
31
31
|
scope: z.enum(['team', 'user']),
|
|
32
32
|
/** The OAuth connection the plan is for; absent from an older host (`default`). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-connect-api.js","sourceRoot":"","sources":["../src/team-connect-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAuB,IAAI,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;QACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC5B,CAAC;IACF,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IACtD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"team-connect-api.js","sourceRoot":"","sources":["../src/team-connect-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport { type TeamsApiResult, call } from './teams-api';\n\n/**\n * The CLI's client for connecting a product for a team (`ekanos teams\n * connect`), for the team's Source admin.\n *\n * GET /api/partner/teams/<team>/integrations/<product>/connect?source=<slug>[&as=<email>][&connection=<id>]\n * → { plan }\n * POST /api/partner/teams/<team>/integrations/<product>/connect?source=<slug>\n * { as?, fields?, set?, connection? }\n * → { plan, result: \"activated\" } fields / no-fields\n * → { plan, result: \"consent\", authorizeUrl, expiresAt } oauth (fields too, for signIn \"app\")\n *\n * The plan's `mode`, decided by the host, says how the product is\n * connected. The shapes are read here rather than from\n * `@ekanos/integration-schema`, so this CLI works against a host without a\n * schema release.\n */\n\nexport const TeamConnectPlanSchema = z.object({\n source: z.object({\n id: z.string(),\n slug: z.string(),\n name: z.string().nullable(),\n }),\n team: z.object({ slug: z.string(), name: z.string() }),\n product: z.object({\n slug: z.string(),\n name: z.string(),\n kind: z.enum(['feature', 'experience', 'integration']).nullable(),\n }),\n scope: z.enum(['team', 'user']),\n /** The OAuth connection the plan is for; absent from an older host (`default`). */\n connectionId: z.string().optional(),\n mode: z.enum([\n 'unsupported',\n 'not-entitled',\n 'not-self-serve',\n 'works-as-shipped',\n 'oauth',\n 'fields',\n 'no-fields',\n 'browser',\n ]),\n /**\n * `oauth` mode: whose sign-in the link opens — the hosted partner\n * transport, or the app's own connect route (GitHub, ROAM), which then\n * activates with `fields`. Absent from an older host (`partner`).\n */\n signIn: z.enum(['partner', 'app']).optional(),\n reason: z.string().nullable(),\n fields: z.array(\n z.object({\n name: z.string(),\n label: z.string(),\n description: z.string().nullable(),\n sensitive: z.boolean(),\n required: z.boolean(),\n /**\n * How it is entered (`connect-fields.ts`). An older host sends only\n * plain fields, so absent reads as text.\n */\n type: z\n .enum(['text', 'number', 'boolean', 'select', 'list', 'json'])\n .default('text'),\n options: z.array(z.string()).nullable().default(null),\n itemType: z.enum(['text', 'number']).nullable().default(null),\n }),\n ),\n teamApp: z.boolean(),\n member: z\n .object({\n userId: z.string(),\n email: z.string().nullable(),\n isCaller: z.boolean(),\n })\n .nullable(),\n memberProblem: z.string().nullable(),\n connected: z.boolean(),\n connectedAt: z.string().nullable(),\n connectUrl: z.string(),\n /**\n * Whether the team's product scopes hide the product from it in the app.\n * Absent from an older host.\n */\n productScope: z\n .object({\n state: z.enum(['unscoped', 'scoped', 'locked_out']),\n scopes: z.array(z.string()),\n hidden: z.boolean(),\n })\n .optional(),\n});\nexport type TeamConnectPlan = z.infer<typeof TeamConnectPlanSchema>;\nexport type TeamConnectField = TeamConnectPlan['fields'][number];\n\n/** A field's value as sent: text, or typed by `connect-fields.ts`. */\nexport type FieldValue =\n | string\n | number\n | boolean\n | null\n | FieldValue[]\n | { [key: string]: FieldValue };\n\nconst ShowSchema = z.object({ plan: TeamConnectPlanSchema });\nconst StartSchema = z.discriminatedUnion('result', [\n z.object({ plan: TeamConnectPlanSchema, result: z.literal('activated') }),\n z.object({\n plan: TeamConnectPlanSchema,\n result: z.literal('consent'),\n authorizeUrl: z.string(),\n expiresAt: z.string(),\n }),\n]);\nexport type TeamConnectStart = z.infer<typeof StartSchema>;\n\ninterface Target {\n source: string;\n team: string;\n product: string;\n /** A named OAuth connection (`agent`); the main one when absent. */\n connection?: string;\n}\n\nfunction connectPath(target: Target): string {\n return `/api/partner/teams/${encodeURIComponent(target.team)}/integrations/${encodeURIComponent(target.product)}/connect`;\n}\n\nexport function showTeamConnect(\n host: string,\n accessToken: string,\n target: Target,\n asEmail?: string,\n): Promise<TeamsApiResult<z.infer<typeof ShowSchema>>> {\n // `call` puts `source` in the query; the rest ride in the path's own query.\n const query = new URLSearchParams({\n ...(asEmail ? { as: asEmail } : {}),\n ...(target.connection ? { connection: target.connection } : {}),\n }).toString();\n const path = query ? `${connectPath(target)}?${query}` : connectPath(target);\n return call(host, accessToken, ShowSchema, {\n method: 'GET',\n path,\n source: target.source,\n });\n}\n\nexport function startTeamConnect(\n host: string,\n accessToken: string,\n target: Target,\n body: {\n as?: string;\n fields?: Record<string, FieldValue>;\n /** The field names given by `--set`, not a prompt: the host refuses a secret among them. */\n set?: string[];\n },\n): Promise<TeamsApiResult<TeamConnectStart>> {\n return call(host, accessToken, StartSchema, {\n method: 'POST',\n path: connectPath(target),\n source: target.source,\n body: target.connection ? { ...body, connection: target.connection } : body,\n });\n}\n"]}
|
package/dist/toolchain-api.d.ts
CHANGED
|
@@ -23,6 +23,11 @@ import { type ToolchainNote, type ToolchainVersions } from './toolchain.js';
|
|
|
23
23
|
* // host
|
|
24
24
|
* // refuses
|
|
25
25
|
* notes?: [{ level: "warn" | "info", message: string }],
|
|
26
|
+
* capabilities?: { experienceSubmissions?: boolean }, // optional
|
|
27
|
+
* // host features; a host
|
|
28
|
+
* // that predates one omits
|
|
29
|
+
* // the key and a caller
|
|
30
|
+
* // reads that as "no"
|
|
26
31
|
* } }
|
|
27
32
|
* → any other status, or a body that fails to parse or match this shape
|
|
28
33
|
*
|
|
@@ -46,10 +51,20 @@ import { type ToolchainNote, type ToolchainVersions } from './toolchain.js';
|
|
|
46
51
|
* outright rather than quietly papered over by the npm fallback.
|
|
47
52
|
* ---------------------------------------------------------------------------
|
|
48
53
|
*/
|
|
54
|
+
/**
|
|
55
|
+
* Optional host features, advertised additively. A key the host does not send
|
|
56
|
+
* reads as `false`, so an older deployment behaves exactly as before.
|
|
57
|
+
*/
|
|
58
|
+
export interface HostCapabilities {
|
|
59
|
+
/** `POST /api/partner/submissions` accepts `kind=experience` and the host admits it. */
|
|
60
|
+
experienceSubmissions: boolean;
|
|
61
|
+
}
|
|
49
62
|
export interface ToolchainReport {
|
|
50
63
|
current: ToolchainVersions;
|
|
51
64
|
minimum: ToolchainVersions;
|
|
52
65
|
notes: ToolchainNote[];
|
|
66
|
+
/** Absent when the host sent none (an older host). */
|
|
67
|
+
capabilities?: HostCapabilities;
|
|
53
68
|
}
|
|
54
69
|
export type FetchToolchainResult = {
|
|
55
70
|
status: 'ok';
|
package/dist/toolchain-api.js
CHANGED
|
@@ -48,7 +48,18 @@ function parseReport(body) {
|
|
|
48
48
|
}
|
|
49
49
|
const rawNotes = data.notes;
|
|
50
50
|
const notes = Array.isArray(rawNotes) ? rawNotes.filter(isNote) : [];
|
|
51
|
-
|
|
51
|
+
const capabilities = parseCapabilities(data.capabilities);
|
|
52
|
+
return Object.assign({ current,
|
|
53
|
+
minimum,
|
|
54
|
+
notes }, (capabilities ? { capabilities } : {}));
|
|
55
|
+
}
|
|
56
|
+
function parseCapabilities(value) {
|
|
57
|
+
if (typeof value !== 'object' || value === null)
|
|
58
|
+
return null;
|
|
59
|
+
return {
|
|
60
|
+
experienceSubmissions: value.experienceSubmissions ===
|
|
61
|
+
true,
|
|
62
|
+
};
|
|
52
63
|
}
|
|
53
64
|
function parseVersions(value) {
|
|
54
65
|
if (typeof value !== 'object' || value === null)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolchain-api.js","sourceRoot":"","sources":["../src/toolchain-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { request } from './auth/ekanos-api';\nimport {\n TOOLCHAIN_PACKAGES,\n type ToolchainNote,\n type ToolchainVersions,\n firstInexactVersion,\n} from './toolchain';\n\n/**\n * The CLI's client for the Ekanos toolchain-advisory route.\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — the seam between this package and the Ekanos web app.\n *\n * The server side is the host's `/api/partner/toolchain` route and ships on\n * a DIFFERENT BRANCH from this file, same as every other route in\n * `auth/ekanos-api.ts` and `publish-api.ts`. Nothing here imports host code;\n * the two halves are coupled only by HTTP, and the tests stub `fetch`,\n * never the route.\n *\n * GET /api/partner/toolchain [Authorization: Bearer <access_token>]\n * → 200 { ok: true, data: {\n * current: { cli, sdk, ui, integrationSchema, harness }, // the\n * // coherent\n * // set the\n * // host\n * // recommends\n * minimum: { cli, sdk, ui, integrationSchema, harness }, // below\n * // this the\n * // host\n * // refuses\n * notes?: [{ level: \"warn\" | \"info\", message: string }],\n * } }\n * → any other status, or a body that fails to parse or match this shape\n *\n * This endpoint is treated as ADVISORY, not authoritative in the way the auth\n * and submission routes are: a 404 (an older Ekanos deployment that predates\n * this route) or a 501 (deliberately unimplemented) are both entirely\n * ordinary, and a 4xx/5xx/network failure/malformed body all mean exactly the\n * same thing to every caller — \"the endpoint is unavailable, fall back to the\n * npm registry\" — so this module never throws. There is no not-configured\n * distinction to make here the way `cli-config` has: a caller with no way to\n * reach this route at all (no host, no session) never calls it in the first\n * place; see `toolchain-resolve.ts`.\n *\n * Unknown extra fields in the response are tolerated (only the fields above\n * are read); a missing `notes` defaults to an empty array.\n *\n * The one exception to \"never throws, always falls back\": a version that is\n * present but is not an exact semver (`1.2.3`, `1.2.3-rc.1`) is `invalid`, not\n * `unavailable`. Each version becomes a `pnpm add -E` spec, so a value like\n * `github:owner/repo` is a host trying to install code; that is refused\n * outright rather than quietly papered over by the npm fallback.\n * ---------------------------------------------------------------------------\n */\n\nexport interface ToolchainReport {\n current: ToolchainVersions;\n minimum: ToolchainVersions;\n notes: ToolchainNote[];\n}\n\nexport type FetchToolchainResult =\n | { status: 'ok'; report: ToolchainReport }\n | { status: 'invalid'; detail: string }\n | { status: 'unavailable' };\n\nexport async function fetchToolchainReport(\n host: string,\n accessToken: string,\n): Promise<FetchToolchainResult> {\n try {\n const response = await request(`${host}/api/partner/toolchain`, {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) return { status: 'unavailable' };\n\n const body: unknown = await response.json();\n const report = parseReport(body);\n\n if (report === null) return { status: 'unavailable' };\n if ('invalid' in report)\n return { status: 'invalid', detail: report.invalid };\n return { status: 'ok', report };\n } catch {\n // Network failure, timeout, or a body that is not even JSON — all read\n // the same: the endpoint is unavailable this time, fall back to npm.\n return { status: 'unavailable' };\n }\n}\n\nfunction parseReport(\n body: unknown,\n): ToolchainReport | { invalid: string } | null {\n if (typeof body !== 'object' || body === null) return null;\n const data = (body as { data?: unknown }).data;\n if (typeof data !== 'object' || data === null) return null;\n\n const current = parseVersions((data as { current?: unknown }).current);\n const minimum = parseVersions((data as { minimum?: unknown }).minimum);\n if (!current || !minimum) return null;\n\n for (const [label, versions] of [\n ['current', current],\n ['minimum', minimum],\n ] as const) {\n const bad = firstInexactVersion(versions);\n if (bad) {\n return {\n invalid: `the host's ${label} ${TOOLCHAIN_PACKAGES[bad.key]} is ${JSON.stringify(bad.value)}, which is not an exact version`,\n };\n }\n }\n\n const rawNotes = (data as { notes?: unknown }).notes;\n const notes = Array.isArray(rawNotes) ? rawNotes.filter(isNote) : [];\n\n return { current, minimum, notes };\n}\n\nfunction parseVersions(value: unknown): ToolchainVersions | null {\n if (typeof value !== 'object' || value === null) return null;\n const record = value as Record<string, unknown>;\n const result: Partial<ToolchainVersions> = {};\n\n for (const key of Object.keys(\n TOOLCHAIN_PACKAGES,\n ) as (keyof ToolchainVersions)[]) {\n const raw = record[key];\n if (typeof raw !== 'string' || raw.length === 0) return null;\n result[key] = raw;\n }\n\n return result as ToolchainVersions;\n}\n\nfunction isNote(value: unknown): value is ToolchainNote {\n if (typeof value !== 'object' || value === null) return false;\n const note = value as { level?: unknown; message?: unknown };\n return (\n (note.level === 'warn' || note.level === 'info') &&\n typeof note.message === 'string'\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"toolchain-api.js","sourceRoot":"","sources":["../src/toolchain-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { request } from './auth/ekanos-api';\nimport {\n TOOLCHAIN_PACKAGES,\n type ToolchainNote,\n type ToolchainVersions,\n firstInexactVersion,\n} from './toolchain';\n\n/**\n * The CLI's client for the Ekanos toolchain-advisory route.\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — the seam between this package and the Ekanos web app.\n *\n * The server side is the host's `/api/partner/toolchain` route and ships on\n * a DIFFERENT BRANCH from this file, same as every other route in\n * `auth/ekanos-api.ts` and `publish-api.ts`. Nothing here imports host code;\n * the two halves are coupled only by HTTP, and the tests stub `fetch`,\n * never the route.\n *\n * GET /api/partner/toolchain [Authorization: Bearer <access_token>]\n * → 200 { ok: true, data: {\n * current: { cli, sdk, ui, integrationSchema, harness }, // the\n * // coherent\n * // set the\n * // host\n * // recommends\n * minimum: { cli, sdk, ui, integrationSchema, harness }, // below\n * // this the\n * // host\n * // refuses\n * notes?: [{ level: \"warn\" | \"info\", message: string }],\n * capabilities?: { experienceSubmissions?: boolean }, // optional\n * // host features; a host\n * // that predates one omits\n * // the key and a caller\n * // reads that as \"no\"\n * } }\n * → any other status, or a body that fails to parse or match this shape\n *\n * This endpoint is treated as ADVISORY, not authoritative in the way the auth\n * and submission routes are: a 404 (an older Ekanos deployment that predates\n * this route) or a 501 (deliberately unimplemented) are both entirely\n * ordinary, and a 4xx/5xx/network failure/malformed body all mean exactly the\n * same thing to every caller — \"the endpoint is unavailable, fall back to the\n * npm registry\" — so this module never throws. There is no not-configured\n * distinction to make here the way `cli-config` has: a caller with no way to\n * reach this route at all (no host, no session) never calls it in the first\n * place; see `toolchain-resolve.ts`.\n *\n * Unknown extra fields in the response are tolerated (only the fields above\n * are read); a missing `notes` defaults to an empty array.\n *\n * The one exception to \"never throws, always falls back\": a version that is\n * present but is not an exact semver (`1.2.3`, `1.2.3-rc.1`) is `invalid`, not\n * `unavailable`. Each version becomes a `pnpm add -E` spec, so a value like\n * `github:owner/repo` is a host trying to install code; that is refused\n * outright rather than quietly papered over by the npm fallback.\n * ---------------------------------------------------------------------------\n */\n\n/**\n * Optional host features, advertised additively. A key the host does not send\n * reads as `false`, so an older deployment behaves exactly as before.\n */\nexport interface HostCapabilities {\n /** `POST /api/partner/submissions` accepts `kind=experience` and the host admits it. */\n experienceSubmissions: boolean;\n}\n\nexport interface ToolchainReport {\n current: ToolchainVersions;\n minimum: ToolchainVersions;\n notes: ToolchainNote[];\n /** Absent when the host sent none (an older host). */\n capabilities?: HostCapabilities;\n}\n\nexport type FetchToolchainResult =\n | { status: 'ok'; report: ToolchainReport }\n | { status: 'invalid'; detail: string }\n | { status: 'unavailable' };\n\nexport async function fetchToolchainReport(\n host: string,\n accessToken: string,\n): Promise<FetchToolchainResult> {\n try {\n const response = await request(`${host}/api/partner/toolchain`, {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) return { status: 'unavailable' };\n\n const body: unknown = await response.json();\n const report = parseReport(body);\n\n if (report === null) return { status: 'unavailable' };\n if ('invalid' in report)\n return { status: 'invalid', detail: report.invalid };\n return { status: 'ok', report };\n } catch {\n // Network failure, timeout, or a body that is not even JSON — all read\n // the same: the endpoint is unavailable this time, fall back to npm.\n return { status: 'unavailable' };\n }\n}\n\nfunction parseReport(\n body: unknown,\n): ToolchainReport | { invalid: string } | null {\n if (typeof body !== 'object' || body === null) return null;\n const data = (body as { data?: unknown }).data;\n if (typeof data !== 'object' || data === null) return null;\n\n const current = parseVersions((data as { current?: unknown }).current);\n const minimum = parseVersions((data as { minimum?: unknown }).minimum);\n if (!current || !minimum) return null;\n\n for (const [label, versions] of [\n ['current', current],\n ['minimum', minimum],\n ] as const) {\n const bad = firstInexactVersion(versions);\n if (bad) {\n return {\n invalid: `the host's ${label} ${TOOLCHAIN_PACKAGES[bad.key]} is ${JSON.stringify(bad.value)}, which is not an exact version`,\n };\n }\n }\n\n const rawNotes = (data as { notes?: unknown }).notes;\n const notes = Array.isArray(rawNotes) ? rawNotes.filter(isNote) : [];\n\n const capabilities = parseCapabilities(\n (data as { capabilities?: unknown }).capabilities,\n );\n\n return {\n current,\n minimum,\n notes,\n ...(capabilities ? { capabilities } : {}),\n };\n}\n\nfunction parseCapabilities(value: unknown): HostCapabilities | null {\n if (typeof value !== 'object' || value === null) return null;\n\n return {\n experienceSubmissions:\n (value as { experienceSubmissions?: unknown }).experienceSubmissions ===\n true,\n };\n}\n\nfunction parseVersions(value: unknown): ToolchainVersions | null {\n if (typeof value !== 'object' || value === null) return null;\n const record = value as Record<string, unknown>;\n const result: Partial<ToolchainVersions> = {};\n\n for (const key of Object.keys(\n TOOLCHAIN_PACKAGES,\n ) as (keyof ToolchainVersions)[]) {\n const raw = record[key];\n if (typeof raw !== 'string' || raw.length === 0) return null;\n result[key] = raw;\n }\n\n return result as ToolchainVersions;\n}\n\nfunction isNote(value: unknown): value is ToolchainNote {\n if (typeof value !== 'object' || value === null) return false;\n const note = value as { level?: unknown; message?: unknown };\n return (\n (note.level === 'warn' || note.level === 'info') &&\n typeof note.message === 'string'\n );\n}\n"]}
|
|
@@ -5,7 +5,7 @@ import { collectDesignFindings } from './design-findings.js';
|
|
|
5
5
|
import { preconditionError } from './errors.js';
|
|
6
6
|
import { collectIconFindings } from './icon-findings.js';
|
|
7
7
|
import { loadDefinition } from './load-definition.js';
|
|
8
|
-
import { collectDocsFindings, collectHostCompilerRulesFindings, collectLintToolingFindings, collectProjectFindings, } from './project-checks.js';
|
|
8
|
+
import { collectDocsFindings, collectHostCompilerRulesFindings, collectLintToolingFindings, collectNodeTypesFindings, collectProjectFindings, } from './project-checks.js';
|
|
9
9
|
/**
|
|
10
10
|
* The full findings pass shared by `validate` (which reports them) and
|
|
11
11
|
* `publish` (which refuses to submit on any error-severity finding). One
|
|
@@ -62,6 +62,7 @@ export async function collectAllFindings(loaded) {
|
|
|
62
62
|
findings.push(...collectCollisionFindings(definitions));
|
|
63
63
|
findings.push(...collectProjectFindings(loaded.projectDir));
|
|
64
64
|
findings.push(...collectLintToolingFindings(loaded.projectDir));
|
|
65
|
+
findings.push(...collectNodeTypesFindings(loaded.projectDir));
|
|
65
66
|
findings.push(...collectDocsFindings(loaded.project, loaded.projectDir));
|
|
66
67
|
findings.push(...collectIconFindings(loaded.projectDir));
|
|
67
68
|
findings.push(...collectDesignFindings(loaded.projectDir));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-findings.js","sourceRoot":"","sources":["../src/validate-findings.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DefinitionCollisionInput,\n Finding,\n} from '@ekanos/integration-schema';\nimport {\n collectCollisionFindings,\n collectDefinitionFindings,\n} from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { collectDesignFindings } from './design-findings';\nimport { preconditionError } from './errors';\nimport { collectIconFindings } from './icon-findings';\nimport { loadDefinition } from './load-definition';\nimport type { LoadedProject } from './project';\nimport {\n collectDocsFindings,\n collectHostCompilerRulesFindings,\n collectLintToolingFindings,\n collectProjectFindings,\n} from './project-checks';\n\n/**\n * The full findings pass shared by `validate` (which reports them) and\n * `publish` (which refuses to submit on any error-severity finding). One\n * implementation so the publish gate can never drift from what `validate`\n * checks — a project that validates clean is, by construction, a project that\n * publish will accept locally.\n *\n * NOTE on schema skew: an earlier version of this function rewrote the\n * unrecognized-key hint when the project's `@ekanos/integration-schema` was\n * newer than the CLI's (see `schema-skew.ts`). That rewrite was dead code —\n * `assertSchemaNotSkewed` throws PRECONDITION_FAILED before either `validate`\n * or `publish` ever calls this function in that state, so a project-newer\n * finding can never reach here to be rewritten, and the precondition error\n * already names both versions and points at the fix. It was removed rather\n * than kept \"just in case\": if a future caller ever makes validate proceed\n * under skew instead of failing fast, THAT is when a rewrite here becomes\n * necessary again, and it should be reintroduced with a real caller driving\n * it, not tests that fake their own reachability.\n */\nexport async function collectAllFindings(\n loaded: LoadedProject,\n): Promise<Finding[]> {\n const findings: Finding[] = [];\n const definitions: DefinitionCollisionInput[] = [];\n\n for (const integration of loaded.integrations) {\n const result = await loadDefinition(\n integration.entryPath,\n loaded.projectDir,\n );\n\n if (result.ok) {\n findings.push(\n ...collectDefinitionFindings(result.definition, {\n file: integration.entryPath,\n }),\n );\n findings.push(\n ...collectSlugAgreementFindings(\n integration,\n result.definition as { slug?: unknown },\n ),\n );\n findings.push(\n ...collectVersionAgreementFindings(\n integration,\n result.definition as { version?: unknown },\n loaded.projectDir,\n ),\n );\n findings.push(\n ...collectOAuthSetupFindings(integration, result.definition),\n );\n definitions.push(result.definition as DefinitionCollisionInput);\n continue;\n }\n\n if (result.kind === 'rejected') {\n // The module loaded but the SDK/schema rejected the definition at import\n // time — surface it as a validation finding rather than a crash.\n findings.push({\n check: 'definition.load',\n severity: 'error',\n file: integration.entryPath,\n message: result.message,\n hint:\n 'Fix the integration definition so defineIntegration() accepts it, ' +\n 'then re-run validate.',\n });\n continue;\n }\n\n // A genuine module-load failure is a precondition, not a finding.\n throw preconditionError(\n `Could not load the integration definition for \"${integration.slug}\": ` +\n result.message,\n 'Ensure the entry module and its installed dependencies load under ' +\n 'Node, then re-run validate.',\n );\n }\n\n // Cross-checks only mean something with more than one definition in hand.\n findings.push(...collectCollisionFindings(definitions));\n findings.push(...collectProjectFindings(loaded.projectDir));\n findings.push(...collectLintToolingFindings(loaded.projectDir));\n findings.push(...collectDocsFindings(loaded.project, loaded.projectDir));\n findings.push(...collectIconFindings(loaded.projectDir));\n findings.push(...collectDesignFindings(loaded.projectDir));\n findings.push(...collectHostCompilerRulesFindings(loaded.projectDir));\n\n return findings;\n}\n\n/**\n * `ekanos.json` and the definition each carry a slug, and nothing compared\n * them: a project could declare `something-else` while the definition said\n * `repo-activity` and validate would report `{ ok: true, findings: [] }`.\n *\n * That is the identifier every surface addresses the integration by — the\n * harness route, the product slug, the widget id prefix, the MCP server — so\n * two sources of truth disagreeing is not a style question. It is an error,\n * not a warning, for the same reason a silent default is worse than a loud\n * one: the failure it causes shows up somewhere else entirely.\n */\nfunction collectSlugAgreementFindings(\n integration: { slug: string; entry: string; entryPath: string },\n definition: { slug?: unknown },\n): Finding[] {\n const declared = definition.slug;\n if (typeof declared !== 'string' || declared === integration.slug) return [];\n\n return [\n {\n check: 'project.slug-agreement',\n severity: 'error',\n file: integration.entryPath,\n message:\n `ekanos.json declares slug \"${integration.slug}\" for this entry, but ` +\n `the definition says \"${declared}\". The slug addresses the ` +\n 'integration everywhere — its harness route, its product record, its ' +\n 'widget ids — so the two must agree.',\n hint:\n `Change one to match the other: either set \"slug\": \"${declared}\" in ` +\n `ekanos.json, or pass slug: '${integration.slug}' to ` +\n 'defineIntegration().',\n },\n ];\n}\n\n/**\n * `package.json`'s `version` field and the `version` `defineIntegration()`\n * declares are two more independent sources of truth for the same fact —\n * this project shipped weather@0.1.1 with a package.json bump, but the\n * declaration itself still said 0.1.0. Generated seeds embed the\n * DECLARATION's version in their header, so a stale declaration produces a\n * seed that looks stale even though the package itself moved on.\n *\n * WARN, not error, unlike slug agreement: a mismatched version is a quality\n * signal a partner should fix, not a broken cross-reference that breaks\n * something else at runtime, so it must not block `publish`. And a project\n * with no package.json, or a package.json with no `version` field, has\n * nothing to compare the declaration against — skip rather than warn on\n * absence.\n */\nexport function collectVersionAgreementFindings(\n integration: { slug: string; entry: string; entryPath: string },\n definition: { version?: unknown },\n projectDir: string,\n): Finding[] {\n const declared = definition.version;\n if (typeof declared !== 'string') return [];\n\n const manifestPath = path.join(projectDir, 'package.json');\n if (!fs.existsSync(manifestPath)) return [];\n\n let manifest: unknown;\n try {\n manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n } catch {\n // Malformed package.json is npm's problem to report, not a validate\n // finding — nothing here can compare against an unparseable manifest.\n return [];\n }\n\n const packageVersion =\n typeof manifest === 'object' && manifest !== null\n ? (manifest as { version?: unknown }).version\n : undefined;\n if (typeof packageVersion !== 'string' || packageVersion === declared) {\n return [];\n }\n\n return [\n {\n check: 'project.version-agreement',\n severity: 'warn',\n file: integration.entryPath,\n message:\n `package.json declares version \"${packageVersion}\", but the ` +\n `defineIntegration() declaration for \"${integration.slug}\" still ` +\n `says \"${declared}\". Generated seed headers embed the ` +\n \"declaration's version, so a stale declaration ships stale-looking \" +\n 'metadata even though the package itself moved on.',\n hint:\n `Update the \"version\" passed to defineIntegration() in ` +\n `${integration.entryPath} to match package.json's \"${packageVersion}\".`,\n },\n ];\n}\n\n/**\n * An OAuth connection with no `setup` steps leaves whoever creates its app —\n * the Source admin (`ekanos apps`) or each team — to work out the provider's\n * console alone. A warning, not an error: the connection works without it.\n */\nexport function collectOAuthSetupFindings(\n integration: { entryPath: string },\n definition: unknown,\n): Finding[] {\n if (typeof definition !== 'object' || definition === null) return [];\n const { oauth, oauthConnections } = definition as {\n oauth?: { setup?: unknown; teamApp?: unknown };\n oauthConnections?: Record<string, { setup?: unknown; teamApp?: unknown }>;\n };\n\n type Connection = { setup?: unknown; teamApp?: unknown };\n const connections: Array<readonly [string, Connection]> = [\n ...(oauth ? [['oauth', oauth] as const] : []),\n ...Object.entries(oauthConnections ?? {}).map(\n ([id, connection]) => [`oauthConnections.${id}`, connection] as const,\n ),\n ];\n\n return connections\n .filter(([, connection]) => connection.setup === undefined)\n .map(([where, connection]) => ({\n check: 'oauth.setup-missing',\n severity: 'warn' as const,\n file: integration.entryPath,\n message:\n `${where} has no setup steps, so ` +\n (connection.teamApp\n ? 'each team creating its app'\n : \"the Source admin creating the Source's app (ekanos apps set)\") +\n \" gets no guidance in the provider's console.\",\n hint:\n `Add \\`setup: { consoleUrl, steps: [...] }\\` to ${where}: where the app is ` +\n 'created, and the steps in order. Write {callbackUrl} and {scopes} where ' +\n 'Ekanos should fill in the exact redirect URI and scopes.',\n }));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"validate-findings.js","sourceRoot":"","sources":["../src/validate-findings.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EACL,mBAAmB,EACnB,gCAAgC,EAChC,0BAA0B,EAC1B,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAqB;IAErB,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,WAAW,GAA+B,EAAE,CAAC;IAEnD,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,cAAc,CACjC,WAAW,CAAC,SAAS,EACrB,MAAM,CAAC,UAAU,CAClB,CAAC;QAEF,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,QAAQ,CAAC,IAAI,CACX,GAAG,yBAAyB,CAAC,MAAM,CAAC,UAAU,EAAE;gBAC9C,IAAI,EAAE,WAAW,CAAC,SAAS;aAC5B,CAAC,CACH,CAAC;YACF,QAAQ,CAAC,IAAI,CACX,GAAG,4BAA4B,CAC7B,WAAW,EACX,MAAM,CAAC,UAAgC,CACxC,CACF,CAAC;YACF,QAAQ,CAAC,IAAI,CACX,GAAG,+BAA+B,CAChC,WAAW,EACX,MAAM,CAAC,UAAmC,EAC1C,MAAM,CAAC,UAAU,CAClB,CACF,CAAC;YACF,QAAQ,CAAC,IAAI,CACX,GAAG,yBAAyB,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,CAC7D,CAAC;YACF,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,UAAsC,CAAC,CAAC;YAChE,SAAS;QACX,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,yEAAyE;YACzE,iEAAiE;YACjE,QAAQ,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,iBAAiB;gBACxB,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,WAAW,CAAC,SAAS;gBAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EACF,oEAAoE;oBACpE,uBAAuB;aAC1B,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,kEAAkE;QAClE,MAAM,iBAAiB,CACrB,kDAAkD,WAAW,CAAC,IAAI,KAAK;YACrE,MAAM,CAAC,OAAO,EAChB,oEAAoE;YAClE,6BAA6B,CAChC,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,CAAC,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,IAAI,CAAC,GAAG,0BAA0B,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAChE,QAAQ,CAAC,IAAI,CAAC,GAAG,wBAAwB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9D,QAAQ,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACzE,QAAQ,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG,gCAAgC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAEtE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,4BAA4B,CACnC,WAA+D,EAC/D,UAA8B;IAE9B,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;IACjC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,WAAW,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAE7E,OAAO;QACL;YACE,KAAK,EAAE,wBAAwB;YAC/B,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,WAAW,CAAC,SAAS;YAC3B,OAAO,EACL,8BAA8B,WAAW,CAAC,IAAI,wBAAwB;gBACtE,wBAAwB,QAAQ,4BAA4B;gBAC5D,sEAAsE;gBACtE,qCAAqC;YACvC,IAAI,EACF,sDAAsD,QAAQ,OAAO;gBACrE,+BAA+B,WAAW,CAAC,IAAI,OAAO;gBACtD,sBAAsB;SACzB;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,+BAA+B,CAC7C,WAA+D,EAC/D,UAAiC,EACjC,UAAkB;IAElB,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC;IACpC,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAE5C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IAE5C,IAAI,QAAiB,CAAC;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,CAAC;eAAO,CAAC;QACP,oEAAoE;QACpE,sEAAsE;QACtE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,cAAc,GAClB,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI;QAC/C,CAAC,CAAE,QAAkC,CAAC,OAAO;QAC7C,CAAC,CAAC,SAAS,CAAC;IAChB,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,QAAQ,EAAE,CAAC;QACtE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO;QACL;YACE,KAAK,EAAE,2BAA2B;YAClC,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,WAAW,CAAC,SAAS;YAC3B,OAAO,EACL,kCAAkC,cAAc,aAAa;gBAC7D,wCAAwC,WAAW,CAAC,IAAI,UAAU;gBAClE,SAAS,QAAQ,sCAAsC;gBACvD,oEAAoE;gBACpE,mDAAmD;YACrD,IAAI,EACF,wDAAwD;gBACxD,GAAG,WAAW,CAAC,SAAS,6BAA6B,cAAc,IAAI;SAC1E;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,WAAkC,EAClC,UAAmB;IAEnB,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACrE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,UAGnC,CAAC;IAGF,MAAM,WAAW,GAAyC;QACxD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,aAAhB,gBAAgB,cAAhB,gBAAgB,GAAI,EAAE,CAAC,CAAC,GAAG,CAC3C,CAAC,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,oBAAoB,EAAE,EAAE,EAAE,UAAU,CAAU,CACtE;KACF,CAAC;IAEF,OAAO,WAAW;SACf,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,KAAK,SAAS,CAAC;SAC1D,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7B,KAAK,EAAE,qBAAqB;QAC5B,QAAQ,EAAE,MAAe;QACzB,IAAI,EAAE,WAAW,CAAC,SAAS;QAC3B,OAAO,EACL,GAAG,KAAK,0BAA0B;YAClC,CAAC,UAAU,CAAC,OAAO;gBACjB,CAAC,CAAC,4BAA4B;gBAC9B,CAAC,CAAC,8DAA8D,CAAC;YACnE,8CAA8C;QAChD,IAAI,EACF,kDAAkD,KAAK,qBAAqB;YAC5E,0EAA0E;YAC1E,0DAA0D;KAC7D,CAAC,CAAC,CAAC;AACR,CAAC","sourcesContent":["import type {\n DefinitionCollisionInput,\n Finding,\n} from '@ekanos/integration-schema';\nimport {\n collectCollisionFindings,\n collectDefinitionFindings,\n} from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { collectDesignFindings } from './design-findings';\nimport { preconditionError } from './errors';\nimport { collectIconFindings } from './icon-findings';\nimport { loadDefinition } from './load-definition';\nimport type { LoadedProject } from './project';\nimport {\n collectDocsFindings,\n collectHostCompilerRulesFindings,\n collectLintToolingFindings,\n collectNodeTypesFindings,\n collectProjectFindings,\n} from './project-checks';\n\n/**\n * The full findings pass shared by `validate` (which reports them) and\n * `publish` (which refuses to submit on any error-severity finding). One\n * implementation so the publish gate can never drift from what `validate`\n * checks — a project that validates clean is, by construction, a project that\n * publish will accept locally.\n *\n * NOTE on schema skew: an earlier version of this function rewrote the\n * unrecognized-key hint when the project's `@ekanos/integration-schema` was\n * newer than the CLI's (see `schema-skew.ts`). That rewrite was dead code —\n * `assertSchemaNotSkewed` throws PRECONDITION_FAILED before either `validate`\n * or `publish` ever calls this function in that state, so a project-newer\n * finding can never reach here to be rewritten, and the precondition error\n * already names both versions and points at the fix. It was removed rather\n * than kept \"just in case\": if a future caller ever makes validate proceed\n * under skew instead of failing fast, THAT is when a rewrite here becomes\n * necessary again, and it should be reintroduced with a real caller driving\n * it, not tests that fake their own reachability.\n */\nexport async function collectAllFindings(\n loaded: LoadedProject,\n): Promise<Finding[]> {\n const findings: Finding[] = [];\n const definitions: DefinitionCollisionInput[] = [];\n\n for (const integration of loaded.integrations) {\n const result = await loadDefinition(\n integration.entryPath,\n loaded.projectDir,\n );\n\n if (result.ok) {\n findings.push(\n ...collectDefinitionFindings(result.definition, {\n file: integration.entryPath,\n }),\n );\n findings.push(\n ...collectSlugAgreementFindings(\n integration,\n result.definition as { slug?: unknown },\n ),\n );\n findings.push(\n ...collectVersionAgreementFindings(\n integration,\n result.definition as { version?: unknown },\n loaded.projectDir,\n ),\n );\n findings.push(\n ...collectOAuthSetupFindings(integration, result.definition),\n );\n definitions.push(result.definition as DefinitionCollisionInput);\n continue;\n }\n\n if (result.kind === 'rejected') {\n // The module loaded but the SDK/schema rejected the definition at import\n // time — surface it as a validation finding rather than a crash.\n findings.push({\n check: 'definition.load',\n severity: 'error',\n file: integration.entryPath,\n message: result.message,\n hint:\n 'Fix the integration definition so defineIntegration() accepts it, ' +\n 'then re-run validate.',\n });\n continue;\n }\n\n // A genuine module-load failure is a precondition, not a finding.\n throw preconditionError(\n `Could not load the integration definition for \"${integration.slug}\": ` +\n result.message,\n 'Ensure the entry module and its installed dependencies load under ' +\n 'Node, then re-run validate.',\n );\n }\n\n // Cross-checks only mean something with more than one definition in hand.\n findings.push(...collectCollisionFindings(definitions));\n findings.push(...collectProjectFindings(loaded.projectDir));\n findings.push(...collectLintToolingFindings(loaded.projectDir));\n findings.push(...collectNodeTypesFindings(loaded.projectDir));\n findings.push(...collectDocsFindings(loaded.project, loaded.projectDir));\n findings.push(...collectIconFindings(loaded.projectDir));\n findings.push(...collectDesignFindings(loaded.projectDir));\n findings.push(...collectHostCompilerRulesFindings(loaded.projectDir));\n\n return findings;\n}\n\n/**\n * `ekanos.json` and the definition each carry a slug, and nothing compared\n * them: a project could declare `something-else` while the definition said\n * `repo-activity` and validate would report `{ ok: true, findings: [] }`.\n *\n * That is the identifier every surface addresses the integration by — the\n * harness route, the product slug, the widget id prefix, the MCP server — so\n * two sources of truth disagreeing is not a style question. It is an error,\n * not a warning, for the same reason a silent default is worse than a loud\n * one: the failure it causes shows up somewhere else entirely.\n */\nfunction collectSlugAgreementFindings(\n integration: { slug: string; entry: string; entryPath: string },\n definition: { slug?: unknown },\n): Finding[] {\n const declared = definition.slug;\n if (typeof declared !== 'string' || declared === integration.slug) return [];\n\n return [\n {\n check: 'project.slug-agreement',\n severity: 'error',\n file: integration.entryPath,\n message:\n `ekanos.json declares slug \"${integration.slug}\" for this entry, but ` +\n `the definition says \"${declared}\". The slug addresses the ` +\n 'integration everywhere — its harness route, its product record, its ' +\n 'widget ids — so the two must agree.',\n hint:\n `Change one to match the other: either set \"slug\": \"${declared}\" in ` +\n `ekanos.json, or pass slug: '${integration.slug}' to ` +\n 'defineIntegration().',\n },\n ];\n}\n\n/**\n * `package.json`'s `version` field and the `version` `defineIntegration()`\n * declares are two more independent sources of truth for the same fact —\n * this project shipped weather@0.1.1 with a package.json bump, but the\n * declaration itself still said 0.1.0. Generated seeds embed the\n * DECLARATION's version in their header, so a stale declaration produces a\n * seed that looks stale even though the package itself moved on.\n *\n * WARN, not error, unlike slug agreement: a mismatched version is a quality\n * signal a partner should fix, not a broken cross-reference that breaks\n * something else at runtime, so it must not block `publish`. And a project\n * with no package.json, or a package.json with no `version` field, has\n * nothing to compare the declaration against — skip rather than warn on\n * absence.\n */\nexport function collectVersionAgreementFindings(\n integration: { slug: string; entry: string; entryPath: string },\n definition: { version?: unknown },\n projectDir: string,\n): Finding[] {\n const declared = definition.version;\n if (typeof declared !== 'string') return [];\n\n const manifestPath = path.join(projectDir, 'package.json');\n if (!fs.existsSync(manifestPath)) return [];\n\n let manifest: unknown;\n try {\n manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n } catch {\n // Malformed package.json is npm's problem to report, not a validate\n // finding — nothing here can compare against an unparseable manifest.\n return [];\n }\n\n const packageVersion =\n typeof manifest === 'object' && manifest !== null\n ? (manifest as { version?: unknown }).version\n : undefined;\n if (typeof packageVersion !== 'string' || packageVersion === declared) {\n return [];\n }\n\n return [\n {\n check: 'project.version-agreement',\n severity: 'warn',\n file: integration.entryPath,\n message:\n `package.json declares version \"${packageVersion}\", but the ` +\n `defineIntegration() declaration for \"${integration.slug}\" still ` +\n `says \"${declared}\". Generated seed headers embed the ` +\n \"declaration's version, so a stale declaration ships stale-looking \" +\n 'metadata even though the package itself moved on.',\n hint:\n `Update the \"version\" passed to defineIntegration() in ` +\n `${integration.entryPath} to match package.json's \"${packageVersion}\".`,\n },\n ];\n}\n\n/**\n * An OAuth connection with no `setup` steps leaves whoever creates its app —\n * the Source admin (`ekanos apps`) or each team — to work out the provider's\n * console alone. A warning, not an error: the connection works without it.\n */\nexport function collectOAuthSetupFindings(\n integration: { entryPath: string },\n definition: unknown,\n): Finding[] {\n if (typeof definition !== 'object' || definition === null) return [];\n const { oauth, oauthConnections } = definition as {\n oauth?: { setup?: unknown; teamApp?: unknown };\n oauthConnections?: Record<string, { setup?: unknown; teamApp?: unknown }>;\n };\n\n type Connection = { setup?: unknown; teamApp?: unknown };\n const connections: Array<readonly [string, Connection]> = [\n ...(oauth ? [['oauth', oauth] as const] : []),\n ...Object.entries(oauthConnections ?? {}).map(\n ([id, connection]) => [`oauthConnections.${id}`, connection] as const,\n ),\n ];\n\n return connections\n .filter(([, connection]) => connection.setup === undefined)\n .map(([where, connection]) => ({\n check: 'oauth.setup-missing',\n severity: 'warn' as const,\n file: integration.entryPath,\n message:\n `${where} has no setup steps, so ` +\n (connection.teamApp\n ? 'each team creating its app'\n : \"the Source admin creating the Source's app (ekanos apps set)\") +\n \" gets no guidance in the provider's console.\",\n hint:\n `Add \\`setup: { consoleUrl, steps: [...] }\\` to ${where}: where the app is ` +\n 'created, and the steps in order. Write {callbackUrl} and {scopes} where ' +\n 'Ekanos should fill in the exact redirect URI and scopes.',\n }));\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekanos/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Ekanos partner toolchain CLI: scaffold, validate, and test an Ekanos integration against the published @ekanos packages. Agent-native — every verb speaks JSON with a stable exit-code taxonomy.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@clack/prompts": "1.7.0",
|
|
38
|
-
"@ekanos/integration-schema": "0.
|
|
38
|
+
"@ekanos/integration-schema": "0.20.0",
|
|
39
39
|
"esbuild": "0.28.1",
|
|
40
40
|
"picocolors": "1.1.1"
|
|
41
41
|
},
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "An Ekanos experience built with @ekanos/sdk.",
|
|
7
7
|
"files": [
|
|
8
|
+
"src",
|
|
9
|
+
"tsconfig.json",
|
|
10
|
+
"npm-shrinkwrap.json",
|
|
8
11
|
"dist",
|
|
9
12
|
"manifest.json",
|
|
10
13
|
"ekanos.json",
|
|
@@ -18,16 +21,16 @@
|
|
|
18
21
|
"build": "npx ekanos build"
|
|
19
22
|
},
|
|
20
23
|
"dependencies": {
|
|
21
|
-
"@ekanos/sdk": "^0.
|
|
22
|
-
"@ekanos/ui": "^0.
|
|
24
|
+
"@ekanos/sdk": "^0.20.0",
|
|
25
|
+
"@ekanos/ui": "^0.20.0",
|
|
23
26
|
"@tanstack/react-query": "^5.101.4",
|
|
24
27
|
"react": "^19.2.8",
|
|
25
28
|
"react-dom": "^19.2.8",
|
|
26
29
|
"zod": "^3.25.76"
|
|
27
30
|
},
|
|
28
31
|
"devDependencies": {
|
|
29
|
-
"@ekanos/cli": "^0.
|
|
30
|
-
"@ekanos/harness": "^0.
|
|
32
|
+
"@ekanos/cli": "^0.18.0",
|
|
33
|
+
"@ekanos/harness": "^0.20.0",
|
|
31
34
|
"@tailwindcss/postcss": "^4.3.3",
|
|
32
35
|
"@typescript-eslint/parser": "^8.46.2",
|
|
33
36
|
"@types/react": "^19.2.0",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ExperienceLayoutPage as default } from '@ekanos/harness/routes';
|
|
@@ -10,6 +10,9 @@ export default function ExperienceLayout(props: { children: React.ReactNode }) {
|
|
|
10
10
|
experience={{
|
|
11
11
|
widgets: experience.widgets ?? [],
|
|
12
12
|
pages: experience.pages ?? [],
|
|
13
|
+
...(experience.suggestedLayout
|
|
14
|
+
? { suggestedLayout: experience.suggestedLayout }
|
|
15
|
+
: {}),
|
|
13
16
|
}}
|
|
14
17
|
{...props}
|
|
15
18
|
/>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ExperiencePageHost as default } from '@ekanos/harness/routes';
|
|
@@ -12,16 +12,17 @@
|
|
|
12
12
|
"prepare": "node scripts/setup-hooks.mjs"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@ekanos/sdk": "^0.
|
|
16
|
-
"@ekanos/ui": "^0.
|
|
15
|
+
"@ekanos/sdk": "^0.20.0",
|
|
16
|
+
"@ekanos/ui": "^0.20.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@ekanos/cli": "^0.
|
|
20
|
-
"@ekanos/harness": "^0.
|
|
19
|
+
"@ekanos/cli": "^0.18.0",
|
|
20
|
+
"@ekanos/harness": "^0.20.0",
|
|
21
21
|
"@hookform/resolvers": "^5.2.2",
|
|
22
22
|
"@tailwindcss/postcss": "^4.3.3",
|
|
23
23
|
"@tanstack/react-query": "^5.101.4",
|
|
24
24
|
"@typescript-eslint/parser": "^8.46.2",
|
|
25
|
+
"@types/node": "^22.10.0",
|
|
25
26
|
"@types/react": "^19.2.0",
|
|
26
27
|
"@types/react-dom": "^19.2.0",
|
|
27
28
|
"eslint": "^10.11.0",
|