diff --git a/bun.lockb b/bun.lockb index 87070a2..0abc4a4 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..c85834c --- /dev/null +++ b/jest.config.js @@ -0,0 +1,19 @@ +// jest.config.js +/** @type {import('jest').Config} */ +module.exports = { + testEnvironment: "node", + roots: ["./tests"], + coveragePathIgnorePatterns: ["node_modules", "mocks"], + collectCoverage: true, + coverageReporters: ["json", "lcov", "text", "clover", "json-summary"], + reporters: ["default", "jest-junit"], + coverageDirectory: "coverage", + verbose: true, + transformIgnorePatterns: [], + transform: { + "^.+\\.[j|t]s$": "@swc/jest", + }, + moduleNameMapper: { + "@octokit/webhooks-methods": "/node_modules/@octokit/webhooks-methods/dist-node/index.js", + }, +}; diff --git a/jest.config.json b/jest.config.json deleted file mode 100644 index cf62beb..0000000 --- a/jest.config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "preset": "ts-jest", - "testEnvironment": "node", - "roots": ["./tests"], - "coveragePathIgnorePatterns": ["node_modules", "mocks"], - "collectCoverage": true, - "coverageReporters": ["json", "lcov", "text", "clover", "json-summary"], - "reporters": ["default", "jest-junit"], - "coverageDirectory": "coverage" -} diff --git a/package.json b/package.json index 0798a84..828c06e 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,10 @@ "proxy": "tsx src/proxy.ts", "knip": "knip --config .github/knip.ts", "knip-ci": "knip --no-exit-code --reporter json --config .github/knip.ts", - "test": "jest test", + "test": "jest --coverage", "plugin:hello-world": "tsx tests/__mocks__/hello-world-plugin.ts", - "setup-kv": "bun --env-file=.dev.vars deploy/setup_kv_namespace.ts" + "setup-kv": "bun --env-file=.dev.vars deploy/setup_kv_namespace.ts", + "sdk:build": "tsup src/sdk/index.ts --format cjs,esm" }, "keywords": [ "typescript", @@ -43,6 +44,7 @@ "@octokit/plugin-throttling": "^9.3.0", "@octokit/types": "^13.5.0", "@octokit/webhooks": "^13.2.7", + "@octokit/webhooks-methods": "^5.1.0", "@octokit/webhooks-types": "^7.5.1", "@sinclair/typebox": "^0.32.5", "dotenv": "^16.4.4", @@ -52,6 +54,10 @@ "yaml": "^2.4.1" }, "devDependencies": { + "@swc/core": "^1.6.5", + "@swc/jest": "^0.2.36", + "tsup": "^8.1.0", + "esbuild-jest": "^0.5.0", "@jest/globals": "^29.7.0", "@types/jest": "^29.5.12", "jest": "^29.7.0", @@ -70,7 +76,7 @@ "@typescript-eslint/eslint-plugin": "^7.0.1", "@typescript-eslint/parser": "^7.0.1", "cspell": "^8.4.0", - "esbuild": "^0.20.1", + "esbuild": "^0.21.5", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", @@ -83,7 +89,7 @@ "toml": "3.0.0", "tomlify-j0.4": "3.0.0", "tsx": "^4.7.1", - "typescript": "^5.3.3", + "typescript": "~5.5.0", "wrangler": "3.58.0" }, "lint-staged": { @@ -99,6 +105,5 @@ "extends": [ "@commitlint/config-conventional" ] - }, - "packageManager": "bun@1.1.0" + } } diff --git a/src/sdk/context.ts b/src/sdk/context.ts index 3d880d0..7f59e34 100644 --- a/src/sdk/context.ts +++ b/src/sdk/context.ts @@ -1,12 +1,12 @@ import { EmitterWebhookEvent as WebhookEvent, EmitterWebhookEventName as WebhookEventName } from "@octokit/webhooks"; -import { Octokit } from "@octokit/rest"; +import { customOctokit } from "./octokit"; export interface Context { eventName: TSupportedEvents; payload: { [K in TSupportedEvents]: K extends WebhookEventName ? WebhookEvent : never; }[TSupportedEvents]["payload"]; - octokit: InstanceType; + octokit: InstanceType; config: TConfig; env: TEnv; logger: { diff --git a/src/sdk/octokit.ts b/src/sdk/octokit.ts new file mode 100644 index 0000000..31ed90a --- /dev/null +++ b/src/sdk/octokit.ts @@ -0,0 +1,27 @@ +import { Octokit } from "@octokit/core"; +import { RequestOptions } from "@octokit/types"; +import { paginateRest } from "@octokit/plugin-paginate-rest"; +import { restEndpointMethods } from "@octokit/plugin-rest-endpoint-methods"; +import { retry } from "@octokit/plugin-retry"; +import { throttling } from "@octokit/plugin-throttling"; + +const defaultOptions = { + throttle: { + onAbuseLimit: (retryAfter: number, options: RequestOptions, octokit: Octokit) => { + octokit.log.warn(`Abuse limit hit with "${options.method} ${options.url}", retrying in ${retryAfter} seconds.`); + return true; + }, + onRateLimit: (retryAfter: number, options: RequestOptions, octokit: Octokit) => { + octokit.log.warn(`Rate limit hit with "${options.method} ${options.url}", retrying in ${retryAfter} seconds.`); + return true; + }, + onSecondaryRateLimit: (retryAfter: number, options: RequestOptions, octokit: Octokit) => { + octokit.log.warn(`Secondary rate limit hit with "${options.method} ${options.url}", retrying in ${retryAfter} seconds.`); + return true; + }, + }, +}; + +export const customOctokit = Octokit.plugin(throttling, retry, paginateRest, restEndpointMethods).defaults((instanceOptions: object) => { + return Object.assign({}, defaultOptions, instanceOptions); +}); diff --git a/src/sdk/server.ts b/src/sdk/server.ts index 52a00e7..bb19c73 100644 --- a/src/sdk/server.ts +++ b/src/sdk/server.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; import { Context } from "./context"; -import { customOctokit } from "../github/github-client"; +import { customOctokit } from "./octokit"; import { EmitterWebhookEventName as WebhookEventName } from "@octokit/webhooks"; import { verifySignature } from "./signature"; import { UBIQUIBOT_KERNEL_PUBLIC_KEY } from "./constants"; @@ -32,10 +32,16 @@ export async function createPlugin = { eventName: payload.eventName, payload: payload.payload, @@ -52,7 +58,6 @@ export async function createPlugin ({ +void jest.mock("@octokit/webhooks", () => ({ Webhooks: WebhooksMocked, })); diff --git a/tests/events.test.ts b/tests/events.test.ts index 5180559..cc2bb44 100644 --- a/tests/events.test.ts +++ b/tests/events.test.ts @@ -1,5 +1,5 @@ import { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"; -import { afterAll, afterEach, beforeAll, describe, expect, it, mock, spyOn } from "bun:test"; +import { afterAll, afterEach, beforeAll, describe, expect, it, jest } from "@jest/globals"; import { config } from "dotenv"; import { GitHubContext } from "../src/github/github-context"; import { GitHubEventHandler } from "../src/github/github-event-handler"; @@ -7,7 +7,7 @@ import issueCommentCreated from "../src/github/handlers/issue-comment-created"; import { server } from "./__mocks__/node"; import { WebhooksMocked } from "./__mocks__/webhooks"; -void mock.module("@octokit/webhooks", () => ({ +void jest.mock("@octokit/webhooks", () => ({ Webhooks: WebhooksMocked, })); @@ -30,7 +30,7 @@ describe("Event related tests", () => { return params; }, }; - const spy = spyOn(issues, "createComment"); + const spy = jest.spyOn(issues, "createComment"); await issueCommentCreated({ id: "", key: "issue_comment.created", diff --git a/tests/main.test.ts b/tests/main.test.ts index 66be6c4..842788f 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -56,6 +56,7 @@ describe("Worker tests", () => { PRIVATE_KEY: "private-key", PLUGIN_CHAIN_STATE: {} as KVNamespace, }); + expect(await res.text()).toEqual("ok\n"); expect(res.status).toEqual(200); }); diff --git a/tests/sdk.test.ts b/tests/sdk.test.ts index 66b1424..7314a88 100644 --- a/tests/sdk.test.ts +++ b/tests/sdk.test.ts @@ -106,7 +106,7 @@ describe("SDK tests", () => { const data = { ...issueCommented, stateId: "stateId", - authToken: process.env.GITHUB_TOKEN, + authToken: "test", settings: { shouldFail: false, }, diff --git a/tsconfig.json b/tsconfig.json index f4cea3b..4b80629 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,7 +31,7 @@ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - "types": ["@cloudflare/workers-types/2023-07-01", "bun"] /* Specify type package names to be included without being referenced in a source file. */, + "types": ["@cloudflare/workers-types/2023-07-01"] /* Specify type package names to be included without being referenced in a source file. */, // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ "resolveJsonModule": true /* Enable importing .json files */, // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ diff --git a/tsup.config.js b/tsup.config.js new file mode 100644 index 0000000..fef5aa4 --- /dev/null +++ b/tsup.config.js @@ -0,0 +1,9 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/sdk/index.ts"], + splitting: false, + sourcemap: true, + clean: true, + dts: true, +});