@design-parity/action 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +109 -0
  2. package/dist/baseline.d.ts +48 -0
  3. package/dist/baseline.d.ts.map +1 -0
  4. package/dist/baseline.js +126 -0
  5. package/dist/baseline.js.map +1 -0
  6. package/dist/candidate.d.ts +48 -0
  7. package/dist/candidate.d.ts.map +1 -0
  8. package/dist/candidate.js +109 -0
  9. package/dist/candidate.js.map +1 -0
  10. package/dist/cli/action.d.ts +3 -0
  11. package/dist/cli/action.d.ts.map +1 -0
  12. package/dist/cli/action.js +222 -0
  13. package/dist/cli/action.js.map +1 -0
  14. package/dist/cli/run.d.ts +3 -0
  15. package/dist/cli/run.d.ts.map +1 -0
  16. package/dist/cli/run.js +123 -0
  17. package/dist/cli/run.js.map +1 -0
  18. package/dist/config.d.ts +23 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +46 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/github/changed-components.d.ts +15 -0
  23. package/dist/github/changed-components.d.ts.map +1 -0
  24. package/dist/github/changed-components.js +18 -0
  25. package/dist/github/changed-components.js.map +1 -0
  26. package/dist/github/conclusion.d.ts +16 -0
  27. package/dist/github/conclusion.d.ts.map +1 -0
  28. package/dist/github/conclusion.js +17 -0
  29. package/dist/github/conclusion.js.map +1 -0
  30. package/dist/github/publish.d.ts +50 -0
  31. package/dist/github/publish.d.ts.map +1 -0
  32. package/dist/github/publish.js +91 -0
  33. package/dist/github/publish.js.map +1 -0
  34. package/dist/github/rest.d.ts +29 -0
  35. package/dist/github/rest.d.ts.map +1 -0
  36. package/dist/github/rest.js +64 -0
  37. package/dist/github/rest.js.map +1 -0
  38. package/dist/github/surface.d.ts +17 -0
  39. package/dist/github/surface.d.ts.map +1 -0
  40. package/dist/github/surface.js +21 -0
  41. package/dist/github/surface.js.map +1 -0
  42. package/dist/index.d.ts +33 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +23 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/mode.d.ts +35 -0
  47. package/dist/mode.d.ts.map +1 -0
  48. package/dist/mode.js +25 -0
  49. package/dist/mode.js.map +1 -0
  50. package/dist/orchestrate.d.ts +80 -0
  51. package/dist/orchestrate.d.ts.map +1 -0
  52. package/dist/orchestrate.js +100 -0
  53. package/dist/orchestrate.js.map +1 -0
  54. package/dist/pushback.d.ts +81 -0
  55. package/dist/pushback.d.ts.map +1 -0
  56. package/dist/pushback.js +106 -0
  57. package/dist/pushback.js.map +1 -0
  58. package/dist/registry.d.ts +24 -0
  59. package/dist/registry.d.ts.map +1 -0
  60. package/dist/registry.js +14 -0
  61. package/dist/registry.js.map +1 -0
  62. package/dist/report.d.ts +29 -0
  63. package/dist/report.d.ts.map +1 -0
  64. package/dist/report.js +72 -0
  65. package/dist/report.js.map +1 -0
  66. package/package.json +40 -0
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @design-parity/action
2
+
3
+ The integration layer — it wires every other package into one parity run and
4
+ renders the verdict the bot posts on a PR.
5
+
6
+ ```
7
+ resolver ──(Correspondence[])──┐
8
+
9
+ registry[source].resolve ──(DesignReference)──┐
10
+
11
+ candidate render ──(CandidateRender)──► diff + checks ──(Verdict)──┐
12
+
13
+ policy direction ───────────────► ParityReport ──► markdown
14
+ ```
15
+
16
+ ## What's here (this increment)
17
+
18
+ - **`createAdapterRegistry()`** — the `source → ReferenceAdapter` map (figma /
19
+ stitch / claude-design). It lives here because it's the one place that imports
20
+ all three drivers; `core` must not depend on adapters.
21
+ - **`orchestrate()`** — per component: resolve the reference via its adapter,
22
+ pair with the candidate render, `diff` them, aggregate. **Fail-soft**: an
23
+ adapter/diff error is captured and surfaced, never thrown, and never escalates
24
+ the overall status — only real verdicts do. The parity **direction** decides
25
+ whether a failure blocks (`design-led`) or is advisory (`code-led`).
26
+ - **`resolveRunConfig()`** — loads the committed `design-map.json` and
27
+ `.design-parity.json` (deterministic; no model, no network).
28
+ - **`renderReport()`** — the single markdown comment (with a stable marker so
29
+ the GitHub surface can update its own comment in place).
30
+ - **`design-parity run`** CLI — a local run; candidate renders come from
31
+ `--candidates <file>` for now (reproducible offline).
32
+
33
+ ## GitHub Action
34
+
35
+ `action.yml` + `dist/cli/action.js` **auto-select a mode from the triggering
36
+ event** (mirroring the sibling `compose-ai-tools` `apply` action):
37
+
38
+ - **comment** — a `pull_request`: read the changed files, keep the
39
+ `design-map.json` components whose file changed (a PR that touches none is
40
+ treated as non-UI and skipped), run the pipeline, and **post/update a single
41
+ verdict comment** (idempotent via the report marker). Exits non-zero only when
42
+ the direction blocks (`design-led` + a failure). If the repo has **no
43
+ committed `design-map.json`** (parity isn't set up), it posts a one-time
44
+ notice pointing at the interactive bootstrap (`design-parity-bootstrap`, #11)
45
+ rather than guessing the design ↔ code mapping at run time — and never blocks.
46
+ - **baseline** — a `push` to the **`development_branch`** (default `main`):
47
+ render the **full** mapped surface, run the pipeline, and **publish the
48
+ browsable artifacts** — a top-level `index.html`, each component's
49
+ self-contained `report.html` triptych, and a machine-readable `verdict.json` —
50
+ to a permanent **`artifact_branch`** (default `design-parity/<dev-branch>`),
51
+ force-updated each run. This gives a stable, always-current view of `main`'s
52
+ parity state without committing generated PNGs/HTML onto `main`, and a real
53
+ baseline a PR can diff its candidate against. Requires `contents: write`.
54
+ - **skip** — nothing applies (e.g. a push to a non-dev branch).
55
+
56
+ `mode: baseline|comment|skip` overrides the selector when needed.
57
+
58
+ ```yaml
59
+ # .github/workflows/design-parity.yml
60
+ on:
61
+ pull_request:
62
+ push:
63
+ branches: [main]
64
+ jobs:
65
+ parity:
66
+ runs-on: ubuntu-latest
67
+ permissions:
68
+ contents: write # baseline mode force-updates the artifact branch
69
+ pull-requests: write # comment mode posts the verdict comment
70
+ steps:
71
+ - uses: actions/checkout@v4
72
+ # ... a prior step renders candidates -> candidates.json (or bundles) ...
73
+ - uses: yschimke/design-parity/packages/action@main
74
+ with:
75
+ candidates: candidates.json # CandidateRender[] …
76
+ # candidate_bundles: out/previews # … and/or compose-preview bundles
77
+ # development_branch: main # push here → baseline mode
78
+ # artifact_branch: design-parity/main
79
+ ```
80
+
81
+ The selector + surface logic (`selectMode`, `postReport`,
82
+ `componentsForChangedFiles`, `checkConclusion`) and the artifact builders
83
+ (`baselineSummary`, `renderBaselineIndex`) are pure and unit-tested; the git
84
+ plumbing (`publishBaseline`) takes an injectable `GitRunner` so its
85
+ orphan/re-parent/force-push sequence is unit-tested without a real remote.
86
+
87
+ ## Still to come (issue #8)
88
+
89
+ - Live `compose-preview` rendering in the candidate provider (today candidates
90
+ come from a precomputed `CandidateRender[]` / preview bundles).
91
+ - Bundle + commit `dist/` so the action is directly consumable (e.g. via `ncc`)
92
+ and can be pinned as `yschimke/design-parity/packages/action@<tag>`.
93
+ - Comment mode diffing the PR candidate against the published baseline (the
94
+ `verdict.json` on the artifact branch) for regression detection.
95
+
96
+ ## Use
97
+
98
+ ```ts
99
+ import { createAdapterRegistry, orchestrate, renderReport } from "@design-parity/action";
100
+
101
+ const report = await orchestrate({
102
+ repoRoot,
103
+ registry: createAdapterRegistry(),
104
+ correspondences, // from @design-parity/resolver
105
+ candidate: (id) => renders.get(id),
106
+ direction, // from @design-parity/policy
107
+ });
108
+ console.log(renderReport(report));
109
+ ```
@@ -0,0 +1,48 @@
1
+ import type { VerdictStatus } from "@design-parity/core";
2
+ import type { ParityReport, ComponentResult } from "./orchestrate.js";
3
+ export interface BaselineComponent {
4
+ code: string;
5
+ source?: string;
6
+ status: ComponentResult["status"];
7
+ verdict?: VerdictStatus;
8
+ /** Counts by severity, when a verdict was produced. */
9
+ findings?: number;
10
+ /** Path to the component's `report.html`, relative to the artifact root. */
11
+ report?: string;
12
+ note?: string;
13
+ }
14
+ export interface BaselineSummary {
15
+ /** ISO timestamp the baseline was assembled. */
16
+ generatedAt: string;
17
+ /** The commit the baseline was rendered from, when known. */
18
+ commit?: string;
19
+ direction: ParityReport["direction"];
20
+ status: VerdictStatus;
21
+ blocked: boolean;
22
+ components: BaselineComponent[];
23
+ warnings: string[];
24
+ }
25
+ export interface BaselineMeta {
26
+ /** Source commit SHA (`GITHUB_SHA`), recorded in the summary + index. */
27
+ commit?: string;
28
+ /** Override the timestamp (tests); defaults to `new Date()`. */
29
+ now?: Date;
30
+ }
31
+ /** The machine-readable roll-up written as `verdict.json`. */
32
+ export declare function baselineSummary(report: ParityReport, outDir: string, meta?: BaselineMeta): BaselineSummary;
33
+ /** The self-contained `index.html` landing page. */
34
+ export declare function renderBaselineIndex(summary: BaselineSummary): string;
35
+ export interface BaselineArtifacts {
36
+ /** Relative path written: `index.html`. */
37
+ indexPath: string;
38
+ /** Relative path written: `verdict.json`. */
39
+ verdictPath: string;
40
+ summary: BaselineSummary;
41
+ }
42
+ /**
43
+ * Write `index.html` + `verdict.json` into `outDir` (alongside the per-component
44
+ * subdirs `orchestrate` already populated). Returns the relative paths and the
45
+ * computed summary.
46
+ */
47
+ export declare function writeBaselineArtifacts(outDir: string, report: ParityReport, meta?: BaselineMeta): Promise<BaselineArtifacts>;
48
+ //# sourceMappingURL=baseline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAQtE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAOD,8DAA8D;AAC9D,wBAAgB,eAAe,CAC7B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,YAAiB,GACtB,eAAe,CAsBjB;AAUD,oDAAoD;AACpD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CA4DpE;AAED,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,iBAAiB,CAAC,CAM5B"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Assemble the browsable baseline artifacts published to the permanent branch
3
+ * (issue #56). `orchestrate` already writes each component's self-contained
4
+ * `report.html` triptych into its own subdir of `outDir`; this layer adds the
5
+ * two top-level files that make the branch a stable, always-current view of the
6
+ * dev branch's parity state:
7
+ *
8
+ * - **`index.html`** — the landing page: overall verdict + a row per component
9
+ * linking into its `report.html`. Self-contained, no assets, no JS.
10
+ * - **`verdict.json`** — the machine-readable roll-up (status, direction, per
11
+ * component verdict + relative report path). This is the baseline a PR run can
12
+ * load to diff its candidate against `main` rather than only the static
13
+ * design reference.
14
+ *
15
+ * The HTML/JSON builders are pure (string / object in, no I/O); only
16
+ * {@link writeBaselineArtifacts} touches disk.
17
+ */
18
+ import { mkdir, writeFile } from "node:fs/promises";
19
+ import { join, relative } from "node:path";
20
+ const STATUS_ICON = {
21
+ pass: "✅",
22
+ warn: "⚠️",
23
+ fail: "❌",
24
+ };
25
+ /** Relative path from the artifact root to a component's report, if it has one. */
26
+ function reportRel(outDir, result) {
27
+ return result.reportPath ? relative(outDir, result.reportPath) : undefined;
28
+ }
29
+ /** The machine-readable roll-up written as `verdict.json`. */
30
+ export function baselineSummary(report, outDir, meta = {}) {
31
+ const components = report.results.map((r) => {
32
+ const rel = reportRel(outDir, r);
33
+ return {
34
+ code: r.code,
35
+ ...(r.source ? { source: r.source } : {}),
36
+ status: r.status,
37
+ ...(r.verdict ? { verdict: r.verdict.status } : {}),
38
+ ...(r.verdict ? { findings: r.verdict.findings.length } : {}),
39
+ ...(rel ? { report: rel } : {}),
40
+ ...(r.note ? { note: r.note } : {}),
41
+ };
42
+ });
43
+ return {
44
+ generatedAt: (meta.now ?? new Date()).toISOString(),
45
+ ...(meta.commit ? { commit: meta.commit } : {}),
46
+ direction: report.direction,
47
+ status: report.status,
48
+ blocked: report.blocked,
49
+ components,
50
+ warnings: report.warnings,
51
+ };
52
+ }
53
+ function escapeHtml(text) {
54
+ return text
55
+ .replace(/&/g, "&amp;")
56
+ .replace(/</g, "&lt;")
57
+ .replace(/>/g, "&gt;")
58
+ .replace(/"/g, "&quot;");
59
+ }
60
+ /** The self-contained `index.html` landing page. */
61
+ export function renderBaselineIndex(summary) {
62
+ const headline = summary.blocked
63
+ ? `${STATUS_ICON.fail} Parity failed — blocking (${summary.direction})`
64
+ : `${STATUS_ICON[summary.status]} Parity ${summary.status} (${summary.direction})`;
65
+ const rows = summary.components
66
+ .map((c) => {
67
+ const icon = c.verdict ? STATUS_ICON[c.verdict] : "—";
68
+ const state = c.verdict ?? c.status;
69
+ const label = c.report
70
+ ? `<a href="${escapeHtml(c.report)}">${escapeHtml(c.code)}</a>`
71
+ : escapeHtml(c.code);
72
+ const detail = c.note ? escapeHtml(c.note) : "";
73
+ return (`<tr><td>${icon}</td><td>${label}</td>` +
74
+ `<td>${escapeHtml(c.source ?? "")}</td>` +
75
+ `<td>${escapeHtml(state)}</td>` +
76
+ `<td>${escapeHtml(detail)}</td></tr>`);
77
+ })
78
+ .join("\n");
79
+ const warnings = summary.warnings.length > 0
80
+ ? `<details><summary>${summary.warnings.length} warning(s)</summary><ul>` +
81
+ summary.warnings.map((w) => `<li>${escapeHtml(w)}</li>`).join("") +
82
+ `</ul></details>`
83
+ : "";
84
+ return `<!doctype html>
85
+ <html lang="en">
86
+ <head>
87
+ <meta charset="utf-8">
88
+ <meta name="viewport" content="width=device-width, initial-scale=1">
89
+ <title>Design parity baseline</title>
90
+ <style>
91
+ body { font: 15px/1.5 system-ui, sans-serif; margin: 2rem auto; max-width: 60rem; padding: 0 1rem; }
92
+ h1 { font-size: 1.4rem; }
93
+ .meta { color: #666; font-size: 0.85rem; }
94
+ table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
95
+ th, td { text-align: left; padding: 0.4rem 0.6rem; border-bottom: 1px solid #eee; }
96
+ th { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: #666; }
97
+ code { font-family: ui-monospace, monospace; }
98
+ </style>
99
+ </head>
100
+ <body>
101
+ <h1>${escapeHtml(headline)}</h1>
102
+ <p class="meta">Generated ${escapeHtml(summary.generatedAt)}${summary.commit ? ` · commit <code>${escapeHtml(summary.commit.slice(0, 12))}</code>` : ""}</p>
103
+ <table>
104
+ <thead><tr><th></th><th>Component</th><th>Source</th><th>State</th><th>Note</th></tr></thead>
105
+ <tbody>
106
+ ${rows || '<tr><td colspan="5">No components were checked.</td></tr>'}
107
+ </tbody>
108
+ </table>
109
+ ${warnings}
110
+ </body>
111
+ </html>
112
+ `;
113
+ }
114
+ /**
115
+ * Write `index.html` + `verdict.json` into `outDir` (alongside the per-component
116
+ * subdirs `orchestrate` already populated). Returns the relative paths and the
117
+ * computed summary.
118
+ */
119
+ export async function writeBaselineArtifacts(outDir, report, meta = {}) {
120
+ const summary = baselineSummary(report, outDir, meta);
121
+ await mkdir(outDir, { recursive: true });
122
+ await writeFile(join(outDir, "verdict.json"), JSON.stringify(summary, null, 2));
123
+ await writeFile(join(outDir, "index.html"), renderBaselineIndex(summary));
124
+ return { indexPath: "index.html", verdictPath: "verdict.json", summary };
125
+ }
126
+ //# sourceMappingURL=baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { CandidateRender, DesignMap } from "@design-parity/core";
2
+ import { type CandidateSource } from "@design-parity/candidate";
3
+ import type { CandidateProvider } from "./orchestrate.js";
4
+ /** A {@link CandidateSource} backed by an in-memory `CandidateRender[]`. */
5
+ export declare function precomputedSource(candidates: Iterable<CandidateRender>): CandidateSource;
6
+ /** Load a precomputed `CandidateRender[]` JSON (array or `{ candidates }`). */
7
+ export declare function loadPrecomputed(repoRoot: string, path: string): Promise<CandidateSource>;
8
+ /**
9
+ * Expand a comma-separated list of preview-bundle inputs (each a polyglot
10
+ * `.png` file or a directory of them) into concrete bundle file paths, resolved
11
+ * against `repoRoot`.
12
+ */
13
+ export declare function resolveBundlePaths(repoRoot: string, inputs: string[]): Promise<string[]>;
14
+ /** A {@link CandidateProvider} from a {@link CandidateSource}. */
15
+ export declare function providerFromSource(source: CandidateSource): CandidateProvider;
16
+ export interface BuildProviderOptions {
17
+ repoRoot: string;
18
+ /** Precomputed `CandidateRender[]` JSON path (array or `{ candidates }`). */
19
+ candidatesPath?: string;
20
+ /** Preview-bundle inputs: polyglot `.png` files and/or directories of them. */
21
+ bundlePaths?: string[];
22
+ /**
23
+ * The loaded `design-map.json`, used to reconcile bundle preview ids to code
24
+ * handles so bundle candidates pair with their references (issue #44).
25
+ */
26
+ designMap?: DesignMap;
27
+ }
28
+ /** The candidate provider plus any non-fatal diagnostics from building it. */
29
+ export interface BuiltCandidateProvider {
30
+ /** `undefined` when no candidate source was configured. */
31
+ provider?: CandidateProvider;
32
+ /** e.g. preview ids that couldn't be reconciled to a code handle (#44). */
33
+ warnings: string[];
34
+ }
35
+ /**
36
+ * Assemble a {@link CandidateProvider} from the configured inputs. When both a
37
+ * precomputed JSON and bundles are given, bundles are tried first and the JSON
38
+ * is the fallback (a hand-authored override). The `provider` is `undefined`
39
+ * when neither is configured (the run has no candidate source).
40
+ *
41
+ * Bundle preview ids are reconciled to code handles through
42
+ * `@design-parity/resolver` (the `design-map` `previewId` field, else the
43
+ * `sourceFile#functionName` convention) so a bundle candidate keys on the same
44
+ * id the orchestrator pairs references by (issue #44). Unreconcilable preview
45
+ * ids surface as `warnings` rather than silently failing to pair.
46
+ */
47
+ export declare function buildCandidateProvider(options: BuildProviderOptions): Promise<BuiltCandidateProvider>;
48
+ //# sourceMappingURL=candidate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"candidate.d.ts","sourceRoot":"","sources":["../src/candidate.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAEV,eAAe,EACf,SAAS,EACV,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,QAAQ,CAAC,eAAe,CAAC,GACpC,eAAe,CASjB;AAED,+EAA+E;AAC/E,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,CAK1B;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CAenB;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,iBAAiB,CAG7E;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,sBAAsB,CAAC,CAwCjC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Build the {@link CandidateProvider} a parity run uses to obtain candidate
3
+ * renders.
4
+ *
5
+ * Two committed, offline inputs are supported (Principle 1 — no live model):
6
+ *
7
+ * 1. A precomputed `CandidateRender[]` JSON (today's path) — reproducible, the
8
+ * renderer's output captured as an artifact.
9
+ * 2. A directory / list of compose-ai-tools **preview-bundle** polyglots
10
+ * (issue #38, Phase 1) — the project's own pipeline already rendered these;
11
+ * `@design-parity/candidate`'s {@link bundleCandidateSource} reads them
12
+ * statically (pure JS, no JVM) into the same `CandidateRender`s.
13
+ *
14
+ * Both reduce to a {@link CandidateProvider}; {@link firstAvailable} lets a run
15
+ * combine them (e.g. bundles first, precomputed JSON as a fallback).
16
+ */
17
+ import { readdir, readFile, stat } from "node:fs/promises";
18
+ import { isAbsolute, join, resolve } from "node:path";
19
+ import { bundleCandidateSource, firstAvailable, readPreviewBundle, } from "@design-parity/candidate";
20
+ import { resolvePreviewIds } from "@design-parity/resolver";
21
+ /** A {@link CandidateSource} backed by an in-memory `CandidateRender[]`. */
22
+ export function precomputedSource(candidates) {
23
+ const index = new Map();
24
+ for (const c of candidates)
25
+ index.set(c.componentId, c);
26
+ return {
27
+ kind: "precomputed",
28
+ async getCandidate(componentId) {
29
+ return index.get(componentId);
30
+ },
31
+ };
32
+ }
33
+ /** Load a precomputed `CandidateRender[]` JSON (array or `{ candidates }`). */
34
+ export async function loadPrecomputed(repoRoot, path) {
35
+ const raw = JSON.parse(await readFile(resolve(repoRoot, path), "utf8"));
36
+ return precomputedSource(Array.isArray(raw) ? raw : raw.candidates);
37
+ }
38
+ /**
39
+ * Expand a comma-separated list of preview-bundle inputs (each a polyglot
40
+ * `.png` file or a directory of them) into concrete bundle file paths, resolved
41
+ * against `repoRoot`.
42
+ */
43
+ export async function resolveBundlePaths(repoRoot, inputs) {
44
+ const paths = [];
45
+ for (const input of inputs) {
46
+ const abs = isAbsolute(input) ? input : resolve(repoRoot, input);
47
+ const info = await stat(abs);
48
+ if (info.isDirectory()) {
49
+ const names = await readdir(abs);
50
+ for (const name of names.sort()) {
51
+ if (name.toLowerCase().endsWith(".png"))
52
+ paths.push(join(abs, name));
53
+ }
54
+ }
55
+ else {
56
+ paths.push(abs);
57
+ }
58
+ }
59
+ return paths;
60
+ }
61
+ /** A {@link CandidateProvider} from a {@link CandidateSource}. */
62
+ export function providerFromSource(source) {
63
+ return (componentId, ctx) => source.getCandidate(componentId, ctx);
64
+ }
65
+ /**
66
+ * Assemble a {@link CandidateProvider} from the configured inputs. When both a
67
+ * precomputed JSON and bundles are given, bundles are tried first and the JSON
68
+ * is the fallback (a hand-authored override). The `provider` is `undefined`
69
+ * when neither is configured (the run has no candidate source).
70
+ *
71
+ * Bundle preview ids are reconciled to code handles through
72
+ * `@design-parity/resolver` (the `design-map` `previewId` field, else the
73
+ * `sourceFile#functionName` convention) so a bundle candidate keys on the same
74
+ * id the orchestrator pairs references by (issue #44). Unreconcilable preview
75
+ * ids surface as `warnings` rather than silently failing to pair.
76
+ */
77
+ export async function buildCandidateProvider(options) {
78
+ const sources = [];
79
+ const warnings = [];
80
+ if (options.bundlePaths && options.bundlePaths.length > 0) {
81
+ const paths = await resolveBundlePaths(options.repoRoot, options.bundlePaths);
82
+ if (paths.length > 0) {
83
+ const bundles = [];
84
+ for (const p of paths)
85
+ bundles.push(await readPreviewBundle(p));
86
+ const identities = bundles.flatMap((b) => b.previews.map((e) => ({
87
+ id: e.id,
88
+ ...(e.sourceFile !== undefined ? { sourceFile: e.sourceFile } : {}),
89
+ ...(e.functionName !== undefined
90
+ ? { functionName: e.functionName }
91
+ : {}),
92
+ })));
93
+ const { matches, warnings: mapWarnings } = resolvePreviewIds(identities, options.designMap);
94
+ warnings.push(...mapWarnings);
95
+ sources.push(bundleCandidateSource({
96
+ bundles,
97
+ resolveComponentId: (preview) => matches.get(preview.id)?.code,
98
+ }));
99
+ }
100
+ }
101
+ if (options.candidatesPath) {
102
+ sources.push(await loadPrecomputed(options.repoRoot, options.candidatesPath));
103
+ }
104
+ if (sources.length === 0)
105
+ return { warnings };
106
+ const source = sources.length === 1 ? sources[0] : firstAvailable(sources);
107
+ return { provider: providerFromSource(source), warnings };
108
+ }
109
+ //# sourceMappingURL=candidate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"candidate.js","sourceRoot":"","sources":["../src/candidate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOtD,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,iBAAiB,GAGlB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAwB,MAAM,yBAAyB,CAAC;AAIlF,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAC/B,UAAqC;IAErC,MAAM,KAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,UAAU;QAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACxD,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,KAAK,CAAC,YAAY,CAAC,WAAW;YAC5B,OAAO,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,IAAY;IAEZ,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,MAAM,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CACS,CAAC;IAC3D,OAAO,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,QAAgB,EAChB,MAAgB;IAEhB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;gBAChC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,kBAAkB,CAAC,MAAuB;IACxD,OAAO,CAAC,WAAmB,EAAE,GAAmB,EAAE,EAAE,CAClD,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAuBD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA6B;IAE7B,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QAC9E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,OAAO,GAAoB,EAAE,CAAC;YACpC,KAAK,MAAM,CAAC,IAAI,KAAK;gBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;YAEhE,MAAM,UAAU,GAAsB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1D,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACrB,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,GAAG,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnE,GAAG,CAAC,CAAC,CAAC,YAAY,KAAK,SAAS;oBAC9B,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE;oBAClC,CAAC,CAAC,EAAE,CAAC;aACR,CAAC,CAAC,CACJ,CAAC;YACF,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,iBAAiB,CAC1D,UAAU,EACV,OAAO,CAAC,SAAS,CAClB,CAAC;YACF,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;YAE9B,OAAO,CAAC,IAAI,CACV,qBAAqB,CAAC;gBACpB,OAAO;gBACP,kBAAkB,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,IAAI;aAC/D,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,MAAM,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;IAChF,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC5E,OAAO,EAAE,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/cli/action.ts"],"names":[],"mappings":""}