@deshlo/plugin-github 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 (46) hide show
  1. package/build/src/__tests__/plugin.test.d.ts +2 -0
  2. package/build/src/__tests__/plugin.test.d.ts.map +1 -0
  3. package/build/src/__tests__/plugin.test.js +19 -0
  4. package/build/src/index.d.ts +3 -0
  5. package/build/src/index.d.ts.map +1 -0
  6. package/build/src/index.js +5 -0
  7. package/build/src/lib/__tests__/githubProvider.test.d.ts +2 -0
  8. package/build/src/lib/__tests__/githubProvider.test.d.ts.map +1 -0
  9. package/build/src/lib/__tests__/githubProvider.test.js +54 -0
  10. package/build/src/lib/__tests__/hostConfig.test.d.ts +2 -0
  11. package/build/src/lib/__tests__/hostConfig.test.d.ts.map +1 -0
  12. package/build/src/lib/__tests__/hostConfig.test.js +35 -0
  13. package/build/src/lib/__tests__/jsxTextEdit.test.d.ts +2 -0
  14. package/build/src/lib/__tests__/jsxTextEdit.test.d.ts.map +1 -0
  15. package/build/src/lib/__tests__/jsxTextEdit.test.js +79 -0
  16. package/build/src/lib/__tests__/sourceLoc.test.d.ts +2 -0
  17. package/build/src/lib/__tests__/sourceLoc.test.d.ts.map +1 -0
  18. package/build/src/lib/__tests__/sourceLoc.test.js +26 -0
  19. package/build/src/lib/__tests__/workflow.test.d.ts +2 -0
  20. package/build/src/lib/__tests__/workflow.test.d.ts.map +1 -0
  21. package/build/src/lib/__tests__/workflow.test.js +90 -0
  22. package/build/src/lib/errors.d.ts +8 -0
  23. package/build/src/lib/errors.d.ts.map +1 -0
  24. package/build/src/lib/errors.js +33 -0
  25. package/build/src/lib/githubProvider.d.ts +35 -0
  26. package/build/src/lib/githubProvider.d.ts.map +1 -0
  27. package/build/src/lib/githubProvider.js +167 -0
  28. package/build/src/lib/hostConfig.d.ts +13 -0
  29. package/build/src/lib/hostConfig.d.ts.map +1 -0
  30. package/build/src/lib/hostConfig.js +68 -0
  31. package/build/src/lib/jsxTextEdit.d.ts +15 -0
  32. package/build/src/lib/jsxTextEdit.d.ts.map +1 -0
  33. package/build/src/lib/jsxTextEdit.js +162 -0
  34. package/build/src/lib/sourceLoc.d.ts +4 -0
  35. package/build/src/lib/sourceLoc.d.ts.map +1 -0
  36. package/build/src/lib/sourceLoc.js +27 -0
  37. package/build/src/lib/types.d.ts +44 -0
  38. package/build/src/lib/types.d.ts.map +1 -0
  39. package/build/src/lib/types.js +2 -0
  40. package/build/src/lib/workflow.d.ts +19 -0
  41. package/build/src/lib/workflow.d.ts.map +1 -0
  42. package/build/src/lib/workflow.js +174 -0
  43. package/build/src/plugin.d.ts +9 -0
  44. package/build/src/plugin.d.ts.map +1 -0
  45. package/build/src/plugin.js +83 -0
  46. package/package.json +42 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plugin.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/plugin.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const plugin_1 = require("../plugin");
5
+ (0, vitest_1.describe)("createGitHubBrowserPlugin", () => {
6
+ (0, vitest_1.it)("returns AUTH_REQUIRED when token is missing", async () => {
7
+ const plugin = (0, plugin_1.createGitHubBrowserPlugin)({
8
+ token: "",
9
+ });
10
+ const result = await plugin.submit({
11
+ sourceLoc: "app/page.tsx:1:1",
12
+ tagName: "h1",
13
+ selectedText: "Old",
14
+ proposedText: "New",
15
+ }, { host: "localhost:3000" });
16
+ (0, vitest_1.expect)(result.ok).toBe(false);
17
+ (0, vitest_1.expect)(result.message).toContain("AUTH_REQUIRED");
18
+ });
19
+ });
@@ -0,0 +1,3 @@
1
+ export type { GitHubBrowserPluginConfig, SourceInspectorRuntimeOptions, } from "./plugin";
2
+ export { createGitHubBrowserPlugin } from "./plugin";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGitHubBrowserPlugin = void 0;
4
+ var plugin_1 = require("./plugin");
5
+ Object.defineProperty(exports, "createGitHubBrowserPlugin", { enumerable: true, get: function () { return plugin_1.createGitHubBrowserPlugin; } });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=githubProvider.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"githubProvider.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/__tests__/githubProvider.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const errors_1 = require("../errors");
5
+ const githubProvider_1 = require("../githubProvider");
6
+ const REPO_CONFIG = {
7
+ host: "example.com",
8
+ apiBaseUrl: "https://api.github.com",
9
+ owner: "acme",
10
+ repo: "demo",
11
+ };
12
+ (0, vitest_1.describe)("createGitHubProvider", () => {
13
+ (0, vitest_1.it)("throws AUTH_REQUIRED when token is missing", () => {
14
+ (0, vitest_1.expect)(() => (0, githubProvider_1.createGitHubProvider)(REPO_CONFIG, "")).toThrow(errors_1.SourceInspectorError);
15
+ });
16
+ (0, vitest_1.it)("creates branch, updates file, and opens draft PR", async () => {
17
+ const request = vitest_1.vi
18
+ .fn()
19
+ .mockResolvedValueOnce({})
20
+ .mockResolvedValueOnce({ data: { commit: { sha: "commit-sha" } } })
21
+ .mockResolvedValueOnce({ data: { number: 7, html_url: "https://example.com/pr/7" } });
22
+ const provider = (0, githubProvider_1.createGitHubProvider)(REPO_CONFIG, "token", { request });
23
+ await provider.createBranch("source-inspector/h1-1", "base-sha");
24
+ const commit = await provider.updateFile({
25
+ path: "app/page.tsx",
26
+ branch: "source-inspector/h1-1",
27
+ sha: "file-sha",
28
+ content: "hello",
29
+ message: "update",
30
+ });
31
+ const pr = await provider.createDraftPullRequest({
32
+ title: "title",
33
+ body: "body",
34
+ head: "source-inspector/h1-1",
35
+ base: "main",
36
+ });
37
+ (0, vitest_1.expect)(commit.commitSha).toBe("commit-sha");
38
+ (0, vitest_1.expect)(pr.prNumber).toBe(7);
39
+ (0, vitest_1.expect)(pr.prUrl).toBe("https://example.com/pr/7");
40
+ (0, vitest_1.expect)(request).toHaveBeenCalledTimes(3);
41
+ });
42
+ (0, vitest_1.it)("maps missing base branch to BASE_BRANCH_NOT_FOUND", async () => {
43
+ const request = vitest_1.vi.fn().mockRejectedValue({ status: 404 });
44
+ const provider = (0, githubProvider_1.createGitHubProvider)(REPO_CONFIG, "token", { request });
45
+ await (0, vitest_1.expect)(provider.getBranchHeadSha("does-not-exist")).rejects.toMatchObject({
46
+ code: "BASE_BRANCH_NOT_FOUND",
47
+ });
48
+ });
49
+ (0, vitest_1.it)("maps unauthorized response to AUTH_REQUIRED", async () => {
50
+ const request = vitest_1.vi.fn().mockRejectedValue({ status: 401 });
51
+ const provider = (0, githubProvider_1.createGitHubProvider)(REPO_CONFIG, "token", { request });
52
+ await (0, vitest_1.expect)(provider.listBranches()).rejects.toMatchObject({ code: "AUTH_REQUIRED" });
53
+ });
54
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hostConfig.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostConfig.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/__tests__/hostConfig.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const errors_1 = require("../errors");
5
+ const hostConfig_1 = require("../hostConfig");
6
+ const ENV_KEY = "NEXT_PUBLIC_SOURCE_INSPECTOR_HOST_CONFIG";
7
+ (0, vitest_1.afterEach)(() => {
8
+ delete process.env[ENV_KEY];
9
+ });
10
+ (0, vitest_1.describe)("resolveRepoConfigForCurrentHost", () => {
11
+ (0, vitest_1.it)("resolves host config and strips port for lookup", () => {
12
+ process.env[ENV_KEY] = JSON.stringify({
13
+ "example.com": {
14
+ apiBaseUrl: "https://api.github.com",
15
+ owner: "acme",
16
+ repo: "demo",
17
+ defaultBaseBranch: "main",
18
+ },
19
+ });
20
+ const config = (0, hostConfig_1.resolveRepoConfigForCurrentHost)("example.com:3000");
21
+ (0, vitest_1.expect)(config.host).toBe("example.com:3000");
22
+ (0, vitest_1.expect)(config.owner).toBe("acme");
23
+ (0, vitest_1.expect)(config.repo).toBe("demo");
24
+ (0, vitest_1.expect)(config.defaultBaseBranch).toBe("main");
25
+ });
26
+ (0, vitest_1.it)("throws UNMAPPED_HOST when no host mapping exists", () => {
27
+ process.env[ENV_KEY] = JSON.stringify({
28
+ "example.com": {
29
+ owner: "acme",
30
+ repo: "demo",
31
+ },
32
+ });
33
+ (0, vitest_1.expect)(() => (0, hostConfig_1.resolveRepoConfigForCurrentHost)("other.com")).toThrow(errors_1.SourceInspectorError);
34
+ });
35
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=jsxTextEdit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsxTextEdit.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/__tests__/jsxTextEdit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const errors_1 = require("../errors");
5
+ const jsxTextEdit_1 = require("../jsxTextEdit");
6
+ const SIMPLE_SOURCE = `export default function Page() {
7
+ return (
8
+ <main>
9
+ <h1>Hello title</h1>
10
+ </main>
11
+ );
12
+ }
13
+ `;
14
+ const NO_TEXT_SOURCE = `export default function Page() {
15
+ return (
16
+ <main>
17
+ <h1><span>Hello</span></h1>
18
+ </main>
19
+ );
20
+ }
21
+ `;
22
+ const MULTI_TEXT_SOURCE = `export default function Page() {
23
+ return (
24
+ <main>
25
+ <h1>Hello <strong>team</strong></h1>
26
+ </main>
27
+ );
28
+ }
29
+ `;
30
+ (0, vitest_1.describe)("applyTextReplacement", () => {
31
+ (0, vitest_1.it)("replaces direct text for matched JSX element", () => {
32
+ const result = (0, jsxTextEdit_1.applyTextReplacement)({
33
+ sourceCode: SIMPLE_SOURCE,
34
+ sourceLoc: { filePath: "app/page.tsx", line: 4, column: 7 },
35
+ tagName: "h1",
36
+ selectedText: "Hello title",
37
+ proposedText: "Updated title",
38
+ });
39
+ (0, vitest_1.expect)(result.oldText).toBe("Hello title");
40
+ (0, vitest_1.expect)(result.newText).toBe("Updated title");
41
+ (0, vitest_1.expect)(result.updatedSourceCode).toContain("<h1>Updated title</h1>");
42
+ });
43
+ (0, vitest_1.it)("throws NON_TEXT_NODE for nested element children", () => {
44
+ (0, vitest_1.expect)(() => (0, jsxTextEdit_1.applyTextReplacement)({
45
+ sourceCode: NO_TEXT_SOURCE,
46
+ sourceLoc: { filePath: "app/page.tsx", line: 4, column: 7 },
47
+ tagName: "h1",
48
+ selectedText: "Hello",
49
+ proposedText: "Updated",
50
+ })).toThrow(errors_1.SourceInspectorError);
51
+ });
52
+ (0, vitest_1.it)("throws NON_TEXT_NODE for mixed/ambiguous text", () => {
53
+ (0, vitest_1.expect)(() => (0, jsxTextEdit_1.applyTextReplacement)({
54
+ sourceCode: MULTI_TEXT_SOURCE,
55
+ sourceLoc: { filePath: "app/page.tsx", line: 4, column: 7 },
56
+ tagName: "h1",
57
+ selectedText: "Hello team",
58
+ proposedText: "Updated",
59
+ })).toThrow(errors_1.SourceInspectorError);
60
+ });
61
+ (0, vitest_1.it)("throws TEXT_MISMATCH when selected text differs from source", () => {
62
+ (0, vitest_1.expect)(() => (0, jsxTextEdit_1.applyTextReplacement)({
63
+ sourceCode: SIMPLE_SOURCE,
64
+ sourceLoc: { filePath: "app/page.tsx", line: 4, column: 7 },
65
+ tagName: "h1",
66
+ selectedText: "Different",
67
+ proposedText: "Updated",
68
+ })).toThrow(errors_1.SourceInspectorError);
69
+ });
70
+ (0, vitest_1.it)("throws NO_DIFF when proposed text is unchanged", () => {
71
+ (0, vitest_1.expect)(() => (0, jsxTextEdit_1.applyTextReplacement)({
72
+ sourceCode: SIMPLE_SOURCE,
73
+ sourceLoc: { filePath: "app/page.tsx", line: 4, column: 7 },
74
+ tagName: "h1",
75
+ selectedText: "Hello title",
76
+ proposedText: "Hello title",
77
+ })).toThrow(errors_1.SourceInspectorError);
78
+ });
79
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sourceLoc.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sourceLoc.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/__tests__/sourceLoc.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const errors_1 = require("../errors");
5
+ const sourceLoc_1 = require("../sourceLoc");
6
+ (0, vitest_1.describe)("parseSourceLoc", () => {
7
+ (0, vitest_1.it)("parses a valid source location", () => {
8
+ (0, vitest_1.expect)((0, sourceLoc_1.parseSourceLoc)("app/page.tsx:12:7")).toEqual({
9
+ filePath: "app/page.tsx",
10
+ line: 12,
11
+ column: 7,
12
+ });
13
+ });
14
+ (0, vitest_1.it)("throws on invalid format", () => {
15
+ (0, vitest_1.expect)(() => (0, sourceLoc_1.parseSourceLoc)("app/page.tsx:12")).toThrow(errors_1.SourceInspectorError);
16
+ });
17
+ (0, vitest_1.it)("throws on non-positive values", () => {
18
+ (0, vitest_1.expect)(() => (0, sourceLoc_1.parseSourceLoc)("app/page.tsx:0:1")).toThrow(errors_1.SourceInspectorError);
19
+ (0, vitest_1.expect)(() => (0, sourceLoc_1.parseSourceLoc)("app/page.tsx:1:0")).toThrow(errors_1.SourceInspectorError);
20
+ });
21
+ });
22
+ (0, vitest_1.describe)("normalizeTextForComparison", () => {
23
+ (0, vitest_1.it)("collapses whitespace", () => {
24
+ (0, vitest_1.expect)((0, sourceLoc_1.normalizeTextForComparison)(" Hello\n world \t")).toBe("Hello world");
25
+ });
26
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=workflow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow.test.d.ts","sourceRoot":"","sources":["../../../../src/lib/__tests__/workflow.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const errors_1 = require("../errors");
5
+ const workflow_1 = require("../workflow");
6
+ const SOURCE = `export default function Page() {
7
+ return (
8
+ <main>
9
+ <h1>Hello title</h1>
10
+ </main>
11
+ );
12
+ }
13
+ `;
14
+ function createProviderMock() {
15
+ return {
16
+ listBranches: vitest_1.vi.fn().mockResolvedValue(["main"]),
17
+ getBranchHeadSha: vitest_1.vi.fn().mockResolvedValue("base-sha"),
18
+ getFileContent: vitest_1.vi.fn().mockResolvedValue({
19
+ content: SOURCE,
20
+ sha: "file-sha",
21
+ }),
22
+ createBranch: vitest_1.vi.fn().mockResolvedValue(undefined),
23
+ updateFile: vitest_1.vi.fn().mockResolvedValue({ commitSha: "commit-sha" }),
24
+ createDraftPullRequest: vitest_1.vi.fn().mockResolvedValue({
25
+ prNumber: 42,
26
+ prUrl: "https://example.com/pr/42",
27
+ }),
28
+ };
29
+ }
30
+ (0, vitest_1.describe)("workflow provider orchestration", () => {
31
+ (0, vitest_1.beforeEach)(() => {
32
+ vitest_1.vi.restoreAllMocks();
33
+ vitest_1.vi.spyOn(Date, "now").mockReturnValue(1700000000000);
34
+ delete process.env.NEXT_PUBLIC_SOURCE_INSPECTOR_GITHUB_PATH_PREFIX;
35
+ });
36
+ (0, vitest_1.it)("previews deterministic replacement", async () => {
37
+ const provider = createProviderMock();
38
+ const preview = await (0, workflow_1.previewProposedChangeWithProvider)({
39
+ sourceLoc: "app/page.tsx:4:7",
40
+ tagName: "h1",
41
+ selectedText: "Hello title",
42
+ proposedText: "Updated title",
43
+ baseBranch: "main",
44
+ }, provider);
45
+ (0, vitest_1.expect)(preview.filePath).toBe("app/page.tsx");
46
+ (0, vitest_1.expect)(preview.oldText).toBe("Hello title");
47
+ (0, vitest_1.expect)(preview.newText).toBe("Updated title");
48
+ (0, vitest_1.expect)(preview.branchNamePreview).toContain("source-inspector/h1-1700000000000");
49
+ });
50
+ (0, vitest_1.it)("creates branch, commit and draft PR", async () => {
51
+ const provider = createProviderMock();
52
+ const result = await (0, workflow_1.createDraftPrFromProposedChangeWithProvider)({
53
+ sourceLoc: "app/page.tsx:4:7",
54
+ tagName: "h1",
55
+ selectedText: "Hello title",
56
+ proposedText: "Updated title",
57
+ baseBranch: "main",
58
+ }, provider);
59
+ (0, vitest_1.expect)(provider.createBranch).toHaveBeenCalledTimes(1);
60
+ (0, vitest_1.expect)(provider.updateFile).toHaveBeenCalledTimes(1);
61
+ (0, vitest_1.expect)(provider.createDraftPullRequest).toHaveBeenCalledTimes(1);
62
+ (0, vitest_1.expect)(result.branchName).toContain("source-inspector/h1-1700000000000");
63
+ (0, vitest_1.expect)(result.commitSha).toBe("commit-sha");
64
+ (0, vitest_1.expect)(result.prNumber).toBe(42);
65
+ });
66
+ (0, vitest_1.it)("surfaces BASE_BRANCH_NOT_FOUND from provider", async () => {
67
+ const provider = createProviderMock();
68
+ provider.getBranchHeadSha.mockRejectedValueOnce(new errors_1.SourceInspectorError("BASE_BRANCH_NOT_FOUND", "Missing base branch"));
69
+ await (0, vitest_1.expect)((0, workflow_1.previewProposedChangeWithProvider)({
70
+ sourceLoc: "app/page.tsx:4:7",
71
+ tagName: "h1",
72
+ selectedText: "Hello title",
73
+ proposedText: "Updated title",
74
+ baseBranch: "main",
75
+ }, provider)).rejects.toThrow(errors_1.SourceInspectorError);
76
+ });
77
+ (0, vitest_1.it)("applies NEXT_PUBLIC_SOURCE_INSPECTOR_GITHUB_PATH_PREFIX to target file path", async () => {
78
+ process.env.NEXT_PUBLIC_SOURCE_INSPECTOR_GITHUB_PATH_PREFIX = "apps/next-test-app";
79
+ const provider = createProviderMock();
80
+ const preview = await (0, workflow_1.previewProposedChangeWithProvider)({
81
+ sourceLoc: "app/page.tsx:4:7",
82
+ tagName: "h1",
83
+ selectedText: "Hello title",
84
+ proposedText: "Updated title",
85
+ baseBranch: "main",
86
+ }, provider);
87
+ (0, vitest_1.expect)(preview.filePath).toBe("apps/next-test-app/app/page.tsx");
88
+ (0, vitest_1.expect)(provider.getFileContent).toHaveBeenCalledWith("apps/next-test-app/app/page.tsx", "main");
89
+ });
90
+ });
@@ -0,0 +1,8 @@
1
+ import type { ActionFailure, ActionResult, SourceInspectorErrorCode } from "./types";
2
+ export declare class SourceInspectorError extends Error {
3
+ code: SourceInspectorErrorCode;
4
+ constructor(code: SourceInspectorErrorCode, message: string);
5
+ }
6
+ export declare function toActionError(error: unknown): ActionFailure;
7
+ export declare function toActionSuccess<T>(data: T): ActionResult<T>;
8
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAErF,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,IAAI,EAAE,wBAAwB,CAAC;gBAEnB,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM;CAI5D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAgB3D;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAK3D"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SourceInspectorError = void 0;
4
+ exports.toActionError = toActionError;
5
+ exports.toActionSuccess = toActionSuccess;
6
+ class SourceInspectorError extends Error {
7
+ constructor(code, message) {
8
+ super(message);
9
+ this.code = code;
10
+ }
11
+ }
12
+ exports.SourceInspectorError = SourceInspectorError;
13
+ function toActionError(error) {
14
+ if (error instanceof SourceInspectorError) {
15
+ return {
16
+ ok: false,
17
+ code: error.code,
18
+ message: error.message,
19
+ };
20
+ }
21
+ const message = error instanceof Error ? error.message : "Unexpected provider error.";
22
+ return {
23
+ ok: false,
24
+ code: "PROVIDER_ERROR",
25
+ message,
26
+ };
27
+ }
28
+ function toActionSuccess(data) {
29
+ return {
30
+ ok: true,
31
+ data,
32
+ };
33
+ }
@@ -0,0 +1,35 @@
1
+ import { Octokit } from "@octokit/core";
2
+ import type { ResolvedRepoConfig } from "./hostConfig";
3
+ export interface FileContentResponse {
4
+ content: string;
5
+ sha: string;
6
+ }
7
+ export interface PullRequestResponse {
8
+ prNumber: number;
9
+ prUrl: string;
10
+ }
11
+ export interface RepoProvider {
12
+ listBranches(): Promise<string[]>;
13
+ getBranchHeadSha(branch: string): Promise<string>;
14
+ getFileContent(path: string, ref: string): Promise<FileContentResponse>;
15
+ createBranch(branch: string, baseSha: string): Promise<void>;
16
+ updateFile(params: {
17
+ path: string;
18
+ branch: string;
19
+ sha: string;
20
+ content: string;
21
+ message: string;
22
+ }): Promise<{
23
+ commitSha: string;
24
+ }>;
25
+ createDraftPullRequest(params: {
26
+ title: string;
27
+ body: string;
28
+ head: string;
29
+ base: string;
30
+ }): Promise<PullRequestResponse>;
31
+ }
32
+ type OctokitLike = Pick<Octokit, "request">;
33
+ export declare function createGitHubProvider(config: ResolvedRepoConfig, token: string, client?: OctokitLike): RepoProvider;
34
+ export {};
35
+ //# sourceMappingURL=githubProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"githubProvider.d.ts","sourceRoot":"","sources":["../../../src/lib/githubProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAClC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACxE,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,UAAU,CAAC,MAAM,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnC,sBAAsB,CAAC,MAAM,EAAE;QAC7B,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAClC;AAED,KAAK,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AA6C5C,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,YAAY,CAoKd"}
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGitHubProvider = createGitHubProvider;
4
+ const core_1 = require("@octokit/core");
5
+ const errors_1 = require("./errors");
6
+ function decodeBase64Utf8(value) {
7
+ if (typeof Buffer !== "undefined") {
8
+ return Buffer.from(value, "base64").toString("utf8");
9
+ }
10
+ const binary = atob(value);
11
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
12
+ return new TextDecoder().decode(bytes);
13
+ }
14
+ function encodeBase64Utf8(value) {
15
+ if (typeof Buffer !== "undefined") {
16
+ return Buffer.from(value, "utf8").toString("base64");
17
+ }
18
+ const bytes = new TextEncoder().encode(value);
19
+ let binary = "";
20
+ for (const byte of bytes) {
21
+ binary += String.fromCharCode(byte);
22
+ }
23
+ return btoa(binary);
24
+ }
25
+ function toProviderError(error) {
26
+ const status = error?.status;
27
+ if (status === 401 || status === 403) {
28
+ throw new errors_1.SourceInspectorError("AUTH_REQUIRED", "GitHub authentication failed. Provide a valid token with repo and pull request access.");
29
+ }
30
+ if (status === 404) {
31
+ throw new errors_1.SourceInspectorError("PROVIDER_ERROR", "GitHub resource was not found.");
32
+ }
33
+ throw new errors_1.SourceInspectorError("PROVIDER_ERROR", error instanceof Error ? error.message : "GitHub API request failed.");
34
+ }
35
+ function createGitHubProvider(config, token, client) {
36
+ const authToken = token.trim();
37
+ if (!authToken) {
38
+ throw new errors_1.SourceInspectorError("AUTH_REQUIRED", "GitHub token is required.");
39
+ }
40
+ const octokit = client ||
41
+ new core_1.Octokit({
42
+ baseUrl: config.apiBaseUrl,
43
+ auth: authToken,
44
+ });
45
+ return {
46
+ async listBranches() {
47
+ try {
48
+ const response = await octokit.request("GET /repos/{owner}/{repo}/branches", {
49
+ owner: config.owner,
50
+ repo: config.repo,
51
+ per_page: 100,
52
+ });
53
+ return response.data.map((branch) => branch.name);
54
+ }
55
+ catch (error) {
56
+ toProviderError(error);
57
+ }
58
+ },
59
+ async getBranchHeadSha(branch) {
60
+ try {
61
+ const response = await octokit.request("GET /repos/{owner}/{repo}/git/ref/{ref}", {
62
+ owner: config.owner,
63
+ repo: config.repo,
64
+ ref: `heads/${branch}`,
65
+ });
66
+ return response.data.object.sha;
67
+ }
68
+ catch (error) {
69
+ const status = error?.status;
70
+ if (status === 404) {
71
+ throw new errors_1.SourceInspectorError("BASE_BRANCH_NOT_FOUND", `Base branch ${branch} was not found.`);
72
+ }
73
+ toProviderError(error);
74
+ }
75
+ },
76
+ async getFileContent(path, ref) {
77
+ try {
78
+ const response = await octokit.request("GET /repos/{owner}/{repo}/contents/{path}", {
79
+ owner: config.owner,
80
+ repo: config.repo,
81
+ path,
82
+ ref,
83
+ });
84
+ if (Array.isArray(response.data) ||
85
+ !("content" in response.data) ||
86
+ typeof response.data.content !== "string") {
87
+ throw new errors_1.SourceInspectorError("FILE_NOT_FOUND", `Path ${path} is not a regular text file in branch ${ref}.`);
88
+ }
89
+ const decoded = decodeBase64Utf8(response.data.content.replace(/\n/g, ""));
90
+ return {
91
+ content: decoded,
92
+ sha: response.data.sha,
93
+ };
94
+ }
95
+ catch (error) {
96
+ const status = error?.status;
97
+ if (status === 404) {
98
+ throw new errors_1.SourceInspectorError("FILE_NOT_FOUND", `File ${path} was not found on branch ${ref}.`);
99
+ }
100
+ if (error instanceof errors_1.SourceInspectorError) {
101
+ throw error;
102
+ }
103
+ toProviderError(error);
104
+ }
105
+ },
106
+ async createBranch(branch, baseSha) {
107
+ try {
108
+ await octokit.request("POST /repos/{owner}/{repo}/git/refs", {
109
+ owner: config.owner,
110
+ repo: config.repo,
111
+ ref: `refs/heads/${branch}`,
112
+ sha: baseSha,
113
+ });
114
+ }
115
+ catch (error) {
116
+ const status = error?.status;
117
+ if (status === 422) {
118
+ throw new errors_1.SourceInspectorError("PROVIDER_ERROR", `Branch ${branch} already exists. Retry the request.`);
119
+ }
120
+ toProviderError(error);
121
+ }
122
+ },
123
+ async updateFile({ path, branch, sha, content, message }) {
124
+ try {
125
+ const response = await octokit.request("PUT /repos/{owner}/{repo}/contents/{path}", {
126
+ owner: config.owner,
127
+ repo: config.repo,
128
+ path,
129
+ branch,
130
+ message,
131
+ sha,
132
+ content: encodeBase64Utf8(content),
133
+ });
134
+ const commitSha = response.data.commit?.sha;
135
+ if (!commitSha) {
136
+ throw new errors_1.SourceInspectorError("PROVIDER_ERROR", "GitHub did not return a commit SHA for the file update.");
137
+ }
138
+ return {
139
+ commitSha,
140
+ };
141
+ }
142
+ catch (error) {
143
+ toProviderError(error);
144
+ }
145
+ },
146
+ async createDraftPullRequest({ title, body, head, base }) {
147
+ try {
148
+ const response = await octokit.request("POST /repos/{owner}/{repo}/pulls", {
149
+ owner: config.owner,
150
+ repo: config.repo,
151
+ title,
152
+ body,
153
+ head,
154
+ base,
155
+ draft: true,
156
+ });
157
+ return {
158
+ prNumber: response.data.number,
159
+ prUrl: response.data.html_url,
160
+ };
161
+ }
162
+ catch (error) {
163
+ toProviderError(error);
164
+ }
165
+ },
166
+ };
167
+ }
@@ -0,0 +1,13 @@
1
+ export interface HostRepoConfig {
2
+ apiBaseUrl?: string;
3
+ owner: string;
4
+ repo: string;
5
+ defaultBaseBranch?: string;
6
+ }
7
+ export interface ResolvedRepoConfig extends HostRepoConfig {
8
+ host: string;
9
+ apiBaseUrl: string;
10
+ }
11
+ export type HostConfigInput = string | Record<string, HostRepoConfig>;
12
+ export declare function resolveRepoConfigForCurrentHost(hostOverride?: string, hostConfigInput?: HostConfigInput): ResolvedRepoConfig;
13
+ //# sourceMappingURL=hostConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostConfig.d.ts","sourceRoot":"","sources":["../../../src/lib/hostConfig.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAqFtE,wBAAgB,+BAA+B,CAC7C,YAAY,CAAC,EAAE,MAAM,EACrB,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB,CAcpB"}