@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
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Publish a staged artifact directory to a permanent branch (issue #56),
3
+ * encapsulating the git plumbing so a consumer workflow is just "render
4
+ * candidates → use the action". Mirrors the `compose-ai-tools` `apply`
5
+ * `push-branch.sh` helper, in TypeScript and with an injectable git runner so
6
+ * the sequence is unit-testable (the same shape as the `fetch`-injectable REST
7
+ * client).
8
+ *
9
+ * The staged dir is treated as a throwaway repo: `git init` there, write its
10
+ * contents as one tree, re-parent on the branch tip (orphan commit the first
11
+ * time), and push. A race-retry loop re-fetches the tip and re-parents when a
12
+ * concurrent run beat us to the branch. Nothing touches the consumer's working
13
+ * checkout.
14
+ *
15
+ * Requires a token with `contents: write`. The commit identity defaults to the
16
+ * GitHub Actions bot so published commits never claim a human author.
17
+ */
18
+ import { execFile } from "node:child_process";
19
+ import { promisify } from "node:util";
20
+ const execFileAsync = promisify(execFile);
21
+ /** Default runner: shells out to the system `git` in `cwd`. */
22
+ export const execGit = async (args, { cwd }) => {
23
+ try {
24
+ const { stdout, stderr } = await execFileAsync("git", args, {
25
+ cwd,
26
+ maxBuffer: 64 * 1024 * 1024,
27
+ });
28
+ return { stdout, stderr, code: 0 };
29
+ }
30
+ catch (err) {
31
+ const e = err;
32
+ return { stdout: e.stdout ?? "", stderr: e.stderr ?? "", code: e.code ?? 1 };
33
+ }
34
+ };
35
+ const BOT_NAME = "github-actions[bot]";
36
+ const BOT_EMAIL = "41898282+github-actions[bot]@users.noreply.github.com";
37
+ const wait = (ms) => new Promise((r) => setTimeout(r, ms));
38
+ function remoteUrl(serverUrl, repo, token) {
39
+ const host = serverUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
40
+ return `https://x-access-token:${token}@${host}/${repo}.git`;
41
+ }
42
+ /**
43
+ * Stage `sourceDir`'s contents and push them as a single commit on `branch`,
44
+ * re-parented on the existing tip. Retries the push on a non-fast-forward race.
45
+ */
46
+ export async function publishBaseline(options) {
47
+ const git = options.git ?? execGit;
48
+ const sleep = options.sleep ?? wait;
49
+ const cwd = options.sourceDir;
50
+ const maxAttempts = options.maxAttempts ?? 5;
51
+ const run = async (...args) => git(args, { cwd });
52
+ const ok = async (...args) => {
53
+ const r = await run(...args);
54
+ if (r.code !== 0) {
55
+ throw new Error(`git ${args.join(" ")} → ${r.code}: ${r.stderr.trim()}`);
56
+ }
57
+ return r.stdout.trim();
58
+ };
59
+ await ok("init", "-q");
60
+ await ok("config", "user.name", options.authorName ?? BOT_NAME);
61
+ await ok("config", "user.email", options.authorEmail ?? BOT_EMAIL);
62
+ await ok("remote", "add", "origin", remoteUrl(options.serverUrl ?? "https://github.com", options.repo, options.token));
63
+ await ok("add", "-A");
64
+ const tree = await ok("write-tree");
65
+ for (let attempt = 1;; attempt++) {
66
+ // Re-fetch the tip every attempt so a retry re-parents on the latest.
67
+ let parent = "";
68
+ const fetched = await run("fetch", "--depth=1", "--quiet", "origin", options.branch);
69
+ if (fetched.code === 0) {
70
+ parent = await ok("rev-parse", "FETCH_HEAD");
71
+ if (options.skipIfUnchanged) {
72
+ const parentTree = await ok("rev-parse", `${parent}^{tree}`);
73
+ if (parentTree === tree) {
74
+ return { branch: options.branch, pushed: false };
75
+ }
76
+ }
77
+ }
78
+ const commit = parent
79
+ ? await ok("commit-tree", tree, "-p", parent, "-m", options.message)
80
+ : await ok("commit-tree", tree, "-m", options.message); // orphan: first push
81
+ const pushed = await run("push", "--quiet", "origin", `${commit}:refs/heads/${options.branch}`);
82
+ if (pushed.code === 0) {
83
+ return { branch: options.branch, pushed: true, sha: commit };
84
+ }
85
+ if (attempt >= maxAttempts) {
86
+ throw new Error(`push to ${options.branch} failed after ${attempt} attempts: ${pushed.stderr.trim()}`);
87
+ }
88
+ await sleep(attempt * 2000);
89
+ }
90
+ }
91
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/github/publish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * A tiny GitHub REST client over `fetch` — just the few endpoints the Action
3
+ * needs (changed files + issue comments). No SDK dependency, mirroring the figma
4
+ * adapter's hand-rolled client. `fetch` is injectable for tests.
5
+ */
6
+ import type { GitHubCommentClient } from "./surface.js";
7
+ export type FetchLike = (input: string, init?: {
8
+ method?: string;
9
+ headers?: Record<string, string>;
10
+ body?: string;
11
+ }) => Promise<Response>;
12
+ export interface GitHubRestOptions {
13
+ token: string;
14
+ baseUrl?: string;
15
+ fetch?: FetchLike;
16
+ }
17
+ export interface RepoRef {
18
+ owner: string;
19
+ repo: string;
20
+ }
21
+ export declare class GitHubRest {
22
+ #private;
23
+ constructor(opts: GitHubRestOptions);
24
+ /** Changed file paths in a PR (paginated). */
25
+ listPullRequestFiles({ owner, repo }: RepoRef, prNumber: number): Promise<string[]>;
26
+ /** A {@link GitHubCommentClient} bound to one PR's comment thread. */
27
+ commentClient(ref: RepoRef, prNumber: number): GitHubCommentClient;
28
+ }
29
+ //# sourceMappingURL=rest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/github/rest.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAgB,MAAM,cAAc,CAAC;AAEtE,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KACxE,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,UAAU;;gBAKT,IAAI,EAAE,iBAAiB;IAyBnC,8CAA8C;IACxC,oBAAoB,CACxB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,EAAE,CAAC;IAapB,sEAAsE;IACtE,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,mBAAmB;CAwBnE"}
@@ -0,0 +1,64 @@
1
+ export class GitHubRest {
2
+ #base;
3
+ #fetch;
4
+ #headers;
5
+ constructor(opts) {
6
+ const fetchImpl = opts.fetch ?? globalThis.fetch;
7
+ if (!fetchImpl)
8
+ throw new Error("design-parity: no fetch available");
9
+ this.#fetch = fetchImpl;
10
+ this.#base = (opts.baseUrl ?? "https://api.github.com").replace(/\/$/, "");
11
+ this.#headers = {
12
+ Authorization: `Bearer ${opts.token}`,
13
+ Accept: "application/vnd.github+json",
14
+ "X-GitHub-Api-Version": "2022-11-28",
15
+ };
16
+ }
17
+ async #req(method, path, body) {
18
+ const res = await this.#fetch(`${this.#base}${path}`, {
19
+ method,
20
+ headers: this.#headers,
21
+ ...(body ? { body: JSON.stringify(body) } : {}),
22
+ });
23
+ if (!res.ok) {
24
+ const text = await res.text().catch(() => "");
25
+ throw new Error(`github: ${method} ${path} → ${res.status} ${text}`);
26
+ }
27
+ return (res.status === 204 ? undefined : await res.json());
28
+ }
29
+ /** Changed file paths in a PR (paginated). */
30
+ async listPullRequestFiles({ owner, repo }, prNumber) {
31
+ const files = [];
32
+ for (let page = 1; page <= 30; page++) {
33
+ const batch = await this.#req("GET", `/repos/${owner}/${repo}/pulls/${prNumber}/files?per_page=100&page=${page}`);
34
+ files.push(...batch.map((f) => f.filename));
35
+ if (batch.length < 100)
36
+ break;
37
+ }
38
+ return files;
39
+ }
40
+ /** A {@link GitHubCommentClient} bound to one PR's comment thread. */
41
+ commentClient(ref, prNumber) {
42
+ const base = `/repos/${ref.owner}/${ref.repo}/issues/${prNumber}/comments`;
43
+ const commentsBase = `/repos/${ref.owner}/${ref.repo}/issues/comments`;
44
+ return {
45
+ listComments: async () => {
46
+ const out = [];
47
+ for (let page = 1; page <= 10; page++) {
48
+ const batch = await this.#req("GET", `${base}?per_page=100&page=${page}`);
49
+ out.push(...batch.map((c) => ({ id: c.id, body: c.body })));
50
+ if (batch.length < 100)
51
+ break;
52
+ }
53
+ return out;
54
+ },
55
+ createComment: async (body) => {
56
+ await this.#req("POST", base, { body });
57
+ },
58
+ updateComment: async (id, body) => {
59
+ await this.#req("PATCH", `${commentsBase}/${id}`, { body });
60
+ },
61
+ };
62
+ }
63
+ }
64
+ //# sourceMappingURL=rest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest.js","sourceRoot":"","sources":["../../src/github/rest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface IssueComment {
2
+ id: number;
3
+ body: string;
4
+ }
5
+ /** The minimal slice of the GitHub API the surface needs (already PR-scoped). */
6
+ export interface GitHubCommentClient {
7
+ listComments(): Promise<IssueComment[]>;
8
+ createComment(body: string): Promise<void>;
9
+ updateComment(id: number, body: string): Promise<void>;
10
+ }
11
+ export type PostOutcome = "created" | "updated";
12
+ /**
13
+ * Create the report comment, or update the bot's existing one in place. Matches
14
+ * its own comment by {@link REPORT_MARKER}, so re-runs never pile up duplicates.
15
+ */
16
+ export declare function postReport(client: GitHubCommentClient, body: string): Promise<PostOutcome>;
17
+ //# sourceMappingURL=surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/github/surface.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,YAAY,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACxC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;AAEhD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,WAAW,CAAC,CAUtB"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The PR comment surface: post the parity report as a single comment and update
3
+ * that same comment on every re-run (idempotent), found by the stable
4
+ * {@link REPORT_MARKER}. The GitHub client is an injected interface so this is
5
+ * unit-testable with a fake and free of any SDK dependency.
6
+ */
7
+ import { REPORT_MARKER } from "../report.js";
8
+ /**
9
+ * Create the report comment, or update the bot's existing one in place. Matches
10
+ * its own comment by {@link REPORT_MARKER}, so re-runs never pile up duplicates.
11
+ */
12
+ export async function postReport(client, body) {
13
+ const existing = (await client.listComments()).find((c) => c.body.includes(REPORT_MARKER));
14
+ if (existing) {
15
+ await client.updateComment(existing.id, body);
16
+ return "updated";
17
+ }
18
+ await client.createComment(body);
19
+ return "created";
20
+ }
21
+ //# sourceMappingURL=surface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface.js","sourceRoot":"","sources":["../../src/github/surface.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAgB7C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAA2B,EAC3B,IAAY;IAEZ,MAAM,QAAQ,GAAG,CAAC,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACxD,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAC/B,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC9C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `@design-parity/action` — the integration layer.
3
+ *
4
+ * Wires the resolver, the `source → adapter` registry, the candidate renderer,
5
+ * the diff engine (+ checks), and the parity-direction policy into a single
6
+ * {@link orchestrate} pipeline, and renders the aggregate {@link ParityReport}
7
+ * for the PR surface, which it also posts/updates as a single PR comment.
8
+ */
9
+ export { createAdapterRegistry } from "./registry.js";
10
+ export type { AdapterRegistry, RegistryOptions } from "./registry.js";
11
+ export { orchestrate } from "./orchestrate.js";
12
+ export type { OrchestrateOptions, CandidateProvider, ComponentResult, ComponentStatus, ParityReport, } from "./orchestrate.js";
13
+ export { buildCandidateProvider, loadPrecomputed, precomputedSource, providerFromSource, resolveBundlePaths, } from "./candidate.js";
14
+ export type { BuildProviderOptions } from "./candidate.js";
15
+ export { resolveRunConfig } from "./config.js";
16
+ export type { RunConfig } from "./config.js";
17
+ export { renderReport, renderBootstrapNotice, REPORT_MARKER, CMP_PROMOTION, } from "./report.js";
18
+ export { selectMode } from "./mode.js";
19
+ export type { ActionMode, ModeInputs } from "./mode.js";
20
+ export { pushBack, decidePushBack } from "./pushback.js";
21
+ export type { PushBackGate, PushBackOptions, PushBackReport, PushBackComponent, PushBackSkip, PushBackError, PushedImage, } from "./pushback.js";
22
+ export { baselineSummary, renderBaselineIndex, writeBaselineArtifacts, } from "./baseline.js";
23
+ export type { BaselineArtifacts, BaselineComponent, BaselineMeta, BaselineSummary, } from "./baseline.js";
24
+ export { postReport } from "./github/surface.js";
25
+ export type { GitHubCommentClient, IssueComment, PostOutcome, } from "./github/surface.js";
26
+ export { GitHubRest } from "./github/rest.js";
27
+ export type { GitHubRestOptions, RepoRef, FetchLike } from "./github/rest.js";
28
+ export { componentsForChangedFiles, filePathOf, } from "./github/changed-components.js";
29
+ export { checkConclusion, exitCode } from "./github/conclusion.js";
30
+ export type { CheckConclusion } from "./github/conclusion.js";
31
+ export { publishBaseline, execGit } from "./github/publish.js";
32
+ export type { GitRunner, GitResult, PublishOptions, PublishResult, } from "./github/publish.js";
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,aAAa,EACb,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EACL,yBAAyB,EACzB,UAAU,GACX,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC/D,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,aAAa,GACd,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `@design-parity/action` — the integration layer.
3
+ *
4
+ * Wires the resolver, the `source → adapter` registry, the candidate renderer,
5
+ * the diff engine (+ checks), and the parity-direction policy into a single
6
+ * {@link orchestrate} pipeline, and renders the aggregate {@link ParityReport}
7
+ * for the PR surface, which it also posts/updates as a single PR comment.
8
+ */
9
+ export { createAdapterRegistry } from "./registry.js";
10
+ export { orchestrate } from "./orchestrate.js";
11
+ export { buildCandidateProvider, loadPrecomputed, precomputedSource, providerFromSource, resolveBundlePaths, } from "./candidate.js";
12
+ export { resolveRunConfig } from "./config.js";
13
+ export { renderReport, renderBootstrapNotice, REPORT_MARKER, CMP_PROMOTION, } from "./report.js";
14
+ export { selectMode } from "./mode.js";
15
+ export { pushBack, decidePushBack } from "./pushback.js";
16
+ export { baselineSummary, renderBaselineIndex, writeBaselineArtifacts, } from "./baseline.js";
17
+ // GitHub surface
18
+ export { postReport } from "./github/surface.js";
19
+ export { GitHubRest } from "./github/rest.js";
20
+ export { componentsForChangedFiles, filePathOf, } from "./github/changed-components.js";
21
+ export { checkConclusion, exitCode } from "./github/conclusion.js";
22
+ export { publishBaseline, execGit } from "./github/publish.js";
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAS/C,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,aAAa,EACb,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAWzD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAQvB,iBAAiB;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EACL,yBAAyB,EACzB,UAAU,GACX,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/mode.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Decide what the Action does for the triggering event, mirroring the sibling
3
+ * `compose-ai-tools` `apply` selector (issue #56):
4
+ *
5
+ * - **`comment`** — a `pull_request`: render the changed components, run the
6
+ * pipeline, and post/update the single verdict comment (today's behavior).
7
+ * - **`baseline`** — a `push` to the long-lived `development-branch`: render the
8
+ * full mapped surface and publish the browsable artifacts (per-component
9
+ * `report.html` triptychs + a machine-readable `verdict.json`) to a permanent
10
+ * artifact branch, force-updated each run. This is the always-current parity
11
+ * state of the dev branch and the baseline a PR can diff against.
12
+ * - **`skip`** — nothing applies (e.g. a push to a non-dev branch).
13
+ *
14
+ * Pure: no env reads, no I/O — the entrypoint feeds it the resolved inputs.
15
+ */
16
+ export type ActionMode = "baseline" | "comment" | "skip";
17
+ export interface ModeInputs {
18
+ /** `GITHUB_EVENT_NAME` (e.g. `pull_request`, `push`, `workflow_dispatch`). */
19
+ eventName?: string;
20
+ /** `GITHUB_REF_NAME` — the short branch/tag name of the triggering ref. */
21
+ refName?: string;
22
+ /** The long-lived branch whose pushes flip the Action into baseline mode. */
23
+ developmentBranch: string;
24
+ /** `INPUT_MODE` override: `auto` (default), `baseline`, `comment`, or `skip`. */
25
+ override?: string;
26
+ /** A resolved PR number, used only as the `auto` fallback for odd events. */
27
+ prNumber?: number;
28
+ }
29
+ /**
30
+ * Map the event (or an explicit `mode` override) to the Action mode. A
31
+ * `workflow_dispatch` sitting on the development branch behaves like a push
32
+ * (manual baseline refresh); elsewhere it falls back to comment mode.
33
+ */
34
+ export declare function selectMode(inputs: ModeInputs): ActionMode;
35
+ //# sourceMappingURL=mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../src/mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAiBzD"}
package/dist/mode.js ADDED
@@ -0,0 +1,25 @@
1
+ const EXPLICIT = new Set(["baseline", "comment", "skip"]);
2
+ /**
3
+ * Map the event (or an explicit `mode` override) to the Action mode. A
4
+ * `workflow_dispatch` sitting on the development branch behaves like a push
5
+ * (manual baseline refresh); elsewhere it falls back to comment mode.
6
+ */
7
+ export function selectMode(inputs) {
8
+ const override = (inputs.override ?? "auto").trim();
9
+ if (EXPLICIT.has(override))
10
+ return override;
11
+ const onDevBranch = inputs.refName === inputs.developmentBranch;
12
+ switch (inputs.eventName) {
13
+ case "pull_request":
14
+ case "pull_request_target":
15
+ return "comment";
16
+ case "push":
17
+ return onDevBranch ? "baseline" : "skip";
18
+ case "workflow_dispatch":
19
+ return onDevBranch ? "baseline" : "comment";
20
+ default:
21
+ // Unknown event: comment if a PR is in context, otherwise nothing to do.
22
+ return inputs.prNumber ? "comment" : "skip";
23
+ }
24
+ }
25
+ //# sourceMappingURL=mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.js","sourceRoot":"","sources":["../src/mode.ts"],"names":[],"mappings":"AA8BA,MAAM,QAAQ,GAAwB,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAE/E;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAkB;IAC3C,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAsB,CAAC;IAE1D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,iBAAiB,CAAC;IAChE,QAAQ,MAAM,CAAC,SAAS,EAAE,CAAC;QACzB,KAAK,cAAc,CAAC;QACpB,KAAK,qBAAqB;YACxB,OAAO,SAAS,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;QAC3C,KAAK,mBAAmB;YACtB,OAAO,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9C;YACE,yEAAyE;YACzE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The parity pipeline: for each changed component, resolve its design reference
3
+ * (via the adapter the resolver picked), pair it with the candidate render, diff
4
+ * them, and aggregate into one report. The parity direction decides whether a
5
+ * failure blocks.
6
+ *
7
+ * Fail-soft (Principle: a broken adapter must not break the run): a per-component
8
+ * error is captured and surfaced, never thrown, and never escalates the overall
9
+ * status to `fail` — only real verdicts do.
10
+ */
11
+ import type { AdapterContext, CandidateRender, Correspondence, DesignReference, DesignSource, Finding, ResolvedDirection, Verdict, VerdictStatus } from "@design-parity/core";
12
+ import { type DiffConfig, type Triptych } from "@design-parity/diff";
13
+ import type { AdapterRegistry } from "./registry.js";
14
+ /** Supplies the candidate render for a component (compose-preview, or precomputed). */
15
+ export type CandidateProvider = (componentId: string, ctx: AdapterContext) => Promise<CandidateRender | undefined> | CandidateRender | undefined;
16
+ export interface OrchestrateOptions {
17
+ repoRoot: string;
18
+ env?: Record<string, string | undefined>;
19
+ registry: AdapterRegistry;
20
+ /** The (code → source/ref) links the resolver produced for the changed components. */
21
+ correspondences: Correspondence[];
22
+ candidate: CandidateProvider;
23
+ /**
24
+ * Renderer-native a11y/i18n findings for a component (issue #43). When this
25
+ * returns findings, they **supersede** design-parity's own `@design-parity/checks`
26
+ * for that component — injected as the diff's `checks` provider (the parity
27
+ * token/visual/semantic diff still runs). Used by the daemon candidate source,
28
+ * which ingests the renderer's own `a11y/atf` / `text/strings` / … products.
29
+ * Returning `undefined` keeps the default checks.
30
+ */
31
+ nativeChecks?: (componentId: string, ctx: AdapterContext) => Promise<Finding[] | undefined> | Finding[] | undefined;
32
+ /** Concrete direction (already resolved from `.design-parity.json`). */
33
+ direction: ResolvedDirection;
34
+ /** Where triptych PNGs are written (optional). */
35
+ outDir?: string;
36
+ diffConfig?: Partial<DiffConfig>;
37
+ }
38
+ export type ComponentStatus = "ok" | "skipped" | "error";
39
+ export interface ComponentResult {
40
+ code: string;
41
+ source?: DesignSource;
42
+ status: ComponentStatus;
43
+ reference?: DesignReference;
44
+ /**
45
+ * The candidate render that was diffed, retained so a downstream consumer can
46
+ * act on the shipped pixels — notably Code-to-Canvas push-back (issue #9),
47
+ * which writes this image back to the design tool in `code-led` mode. Only
48
+ * present when a candidate was available (i.e. `status` is `ok`).
49
+ */
50
+ candidate?: CandidateRender;
51
+ verdict?: Verdict;
52
+ summary?: string;
53
+ triptychs?: Triptych[];
54
+ /** Path to the self-contained HTML comparison page, when `outDir` was set (#50). */
55
+ reportPath?: string;
56
+ /** Reason for `skipped` (no candidate) or `error` (adapter/diff failure). */
57
+ note?: string;
58
+ }
59
+ export interface ParityReport {
60
+ /** Worst verdict status across components (errors do not escalate this). */
61
+ status: VerdictStatus;
62
+ /** True only when the direction blocks PRs and at least one verdict failed. */
63
+ blocked: boolean;
64
+ direction: ResolvedDirection;
65
+ results: ComponentResult[];
66
+ /** Non-fatal issues: adapter errors, skipped components, resolver warnings. */
67
+ warnings: string[];
68
+ /**
69
+ * The committed CMP capability flag (Principle 6), threaded from
70
+ * `.design-parity.json` for the report layer. `false` ⇒ the repo is
71
+ * Android-only, so the comment carries a non-blocking "could run parity faster
72
+ * on Compose Multiplatform" suggestion; `true`/omitted ⇒ no promotion. Never
73
+ * affects {@link ParityReport.status} or {@link ParityReport.blocked} —
74
+ * advisory only.
75
+ */
76
+ cmpCapable?: boolean;
77
+ }
78
+ /** Run the full parity pipeline over the resolved components. */
79
+ export declare function orchestrate(options: OrchestrateOptions): Promise<ParityReport>;
80
+ //# sourceMappingURL=orchestrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrate.d.ts","sourceRoot":"","sources":["../src/orchestrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,YAAY,EACZ,OAAO,EACP,iBAAiB,EACjB,OAAO,EACP,aAAa,EACd,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,QAAQ,EACd,MAAM,qBAAqB,CAAC;AAI7B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,uFAAuF;AACvF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,cAAc,KAChB,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,eAAe,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,QAAQ,EAAE,eAAe,CAAC;IAC1B,sFAAsF;IACtF,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,SAAS,EAAE,iBAAiB,CAAC;IAC7B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,CACb,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,cAAc,KAChB,OAAO,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,GAAG,OAAO,EAAE,GAAG,SAAS,CAAC;IAC5D,wEAAwE;IACxE,SAAS,EAAE,iBAAiB,CAAC;IAC7B,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC;AAYD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,MAAM,EAAE,aAAa,CAAC;IACtB,+EAA+E;IAC/E,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAOD,iEAAiE;AACjE,wBAAsB,WAAW,CAC/B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CA6FvB"}
@@ -0,0 +1,100 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { diff, } from "@design-parity/diff";
4
+ import { directionPolicy } from "@design-parity/policy";
5
+ import { renderHtmlReport } from "@design-parity/report-html";
6
+ /** Wrap precomputed native findings as a {@link ChecksProvider} for the diff. */
7
+ function nativeChecksProvider(findings) {
8
+ return { run: () => findings };
9
+ }
10
+ /** A filesystem-safe slug for a component id (`ui/Tile.kt#LightOn_Dark` → `ui-Tile-kt-LightOn_Dark`). */
11
+ function sanitizeId(code) {
12
+ return code.replace(/[^a-z0-9_]+/gi, "-").replace(/^-+|-+$/g, "");
13
+ }
14
+ function worst(a, b) {
15
+ const rank = { pass: 0, warn: 1, fail: 2 };
16
+ return rank[a] >= rank[b] ? a : b;
17
+ }
18
+ /** Run the full parity pipeline over the resolved components. */
19
+ export async function orchestrate(options) {
20
+ const ctx = { repoRoot: options.repoRoot, env: options.env ?? {} };
21
+ const results = [];
22
+ const warnings = [];
23
+ let status = "pass";
24
+ for (const corr of options.correspondences) {
25
+ const result = {
26
+ code: corr.code,
27
+ source: corr.source,
28
+ status: "ok",
29
+ };
30
+ try {
31
+ const adapter = options.registry[corr.source];
32
+ if (!adapter) {
33
+ result.status = "error";
34
+ result.note = `no adapter registered for source '${corr.source}'`;
35
+ warnings.push(`${corr.code}: ${result.note}`);
36
+ results.push(result);
37
+ continue;
38
+ }
39
+ const reference = await adapter.resolve(corr.code, corr.ref, ctx);
40
+ result.reference = reference;
41
+ const candidate = await options.candidate(corr.code, ctx);
42
+ if (!candidate) {
43
+ result.status = "skipped";
44
+ result.note = "no candidate render available";
45
+ warnings.push(`${corr.code}: ${result.note}`);
46
+ results.push(result);
47
+ continue;
48
+ }
49
+ // Retain the render so push-back (#9) can write the shipped pixels back.
50
+ result.candidate = candidate;
51
+ // Each component writes into its own subdir so triptychs (keyed only by
52
+ // image variant) and the HTML page don't collide across components (#49).
53
+ const componentOutDir = options.outDir
54
+ ? join(options.outDir, sanitizeId(corr.code))
55
+ : undefined;
56
+ // Renderer-native findings (daemon path) supersede the default checks
57
+ // for this component (issue #43); the parity diff itself is unchanged.
58
+ const native = await options.nativeChecks?.(corr.code, ctx);
59
+ const diffOptions = {
60
+ repoRoot: options.repoRoot,
61
+ ...(componentOutDir ? { outDir: componentOutDir } : {}),
62
+ ...(options.diffConfig ? { config: options.diffConfig } : {}),
63
+ ...(native ? { checks: nativeChecksProvider(native) } : {}),
64
+ };
65
+ const { verdict, summary, triptychs } = await diff(reference, candidate, diffOptions);
66
+ result.verdict = verdict;
67
+ result.summary = summary;
68
+ result.triptychs = triptychs;
69
+ // Emit the self-contained HTML comparison page alongside the triptychs,
70
+ // inlining each pair's diff heatmap when the engine produced one (#50).
71
+ if (componentOutDir) {
72
+ const diffImages = triptychs
73
+ .filter((t) => t.diff !== undefined)
74
+ .map((t) => ({ key: t.key, png: t.diff }));
75
+ const html = renderHtmlReport({
76
+ reference,
77
+ candidate,
78
+ verdict,
79
+ repoRoot: options.repoRoot,
80
+ ...(diffImages.length > 0 ? { diffImages } : {}),
81
+ });
82
+ const reportPath = join(componentOutDir, "report.html");
83
+ await mkdir(componentOutDir, { recursive: true });
84
+ await writeFile(reportPath, html);
85
+ result.reportPath = reportPath;
86
+ }
87
+ status = worst(status, verdict.status);
88
+ }
89
+ catch (err) {
90
+ // Fail soft: surface, never throw, never escalate overall status.
91
+ result.status = "error";
92
+ result.note = err.message;
93
+ warnings.push(`${corr.code}: ${result.note}`);
94
+ }
95
+ results.push(result);
96
+ }
97
+ const blocked = directionPolicy(options.direction).blocksPr && status === "fail";
98
+ return { status, blocked, direction: options.direction, results, warnings };
99
+ }
100
+ //# sourceMappingURL=orchestrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../src/orchestrate.ts"],"names":[],"mappings":"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"}