diff --git a/patches/sponsorkit@17.1.0.patch b/patches/sponsorkit@17.1.0.patch new file mode 100644 index 000000000..d2ca6aa56 --- /dev/null +++ b/patches/sponsorkit@17.1.0.patch @@ -0,0 +1,22 @@ +diff --git a/dist/shared/sponsorkit.D4Gzyh4z.mjs b/dist/shared/sponsorkit.D4Gzyh4z.mjs +index f7e74b63ff2ed0b206a184722aa1d61c86963377..e4f29889f5f1518023b6f4822195a53bb597e8c5 100644 +--- a/dist/shared/sponsorkit.D4Gzyh4z.mjs ++++ b/dist/shared/sponsorkit.D4Gzyh4z.mjs +@@ -1282,7 +1282,7 @@ async function fetchOpenCollectiveSponsors(key, id, slug, githubHandle, includeP + method: "POST", + body: { query }, + headers: { +- "Api-Key": `${key}`, ++ "Personal-Token": `${key}`, + "Content-Type": "application/json" + } + }); +@@ -1308,7 +1308,7 @@ async function fetchOpenCollectiveSponsors(key, id, slug, githubHandle, includeP + method: "POST", + body: { query }, + headers: { +- "Api-Key": `${key}`, ++ "Personal-Token": `${key}`, + "Content-Type": "application/json" + } + }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 393a13a94..a11a7bf08 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -428,6 +428,9 @@ patchedDependencies: pixi-live2d-display: hash: 122ac09349321d5bfe9d9817aa095cd1c9c4132af86345aa9e27ba8b63dadf2c path: patches/pixi-live2d-display.patch + sponsorkit@17.1.0: + hash: f5887da52a29ae85732e01644d05c0c274231fe76b95aebeafbfab6dc4444f88 + path: patches/sponsorkit@17.1.0.patch importers: @@ -507,7 +510,7 @@ importers: version: 2.13.1 sponsorkit: specifier: ^17.1.0 - version: 17.1.0 + version: 17.1.0(patch_hash=f5887da52a29ae85732e01644d05c0c274231fe76b95aebeafbfab6dc4444f88) sponsors-svg: specifier: ^0.3.0 version: 0.3.0 @@ -32096,7 +32099,7 @@ snapshots: dependencies: vue: 3.5.32(typescript@5.9.3) - sponsorkit@17.1.0: + sponsorkit@17.1.0(patch_hash=f5887da52a29ae85732e01644d05c0c274231fe76b95aebeafbfab6dc4444f88): dependencies: ansis: 4.2.0 cac: 6.7.14 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0fb180abe..639b5d232 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -27,6 +27,7 @@ patchedDependencies: '@xsai/stream-text@0.5.0-beta.2': patches/@xsai__stream-text@0.5.0-beta.2.patch mineflayer-pathfinder: patches/mineflayer-pathfinder.patch pixi-live2d-display: patches/pixi-live2d-display.patch + sponsorkit@17.1.0: patches/sponsorkit@17.1.0.patch catalog: '@ax-llm/ax': ^19.0.43 '@better-auth/oauth-provider': 1.5.6 diff --git a/scripts/sponsorkit/sponsorkitOpenCollectivePatch.test.js b/scripts/sponsorkit/sponsorkitOpenCollectivePatch.test.js new file mode 100644 index 000000000..615ed7225 --- /dev/null +++ b/scripts/sponsorkit/sponsorkitOpenCollectivePatch.test.js @@ -0,0 +1,83 @@ +import { ProvidersMap } from 'sponsorkit' +import { afterEach, describe, expect, it, vi } from 'vitest' + +/** + * Creates a minimal OpenCollective GraphQL response for SponsorKit's fetch flow. + * + * @param {'orders' | 'transactions'} nodeType - The OpenCollective connection requested by SponsorKit. + */ +function createGraphqlResponse(nodeType) { + return new Response(JSON.stringify({ + data: { + account: { + [nodeType]: { + nodes: [], + totalCount: 0, + }, + }, + }, + }), { + headers: { + 'Content-Type': 'application/json', + }, + }) +} + +/** + * Normalizes fetch init headers so the test can assert browser-style header names. + * + * @param {HeadersInit | undefined} headers - Headers passed by ofetch to the platform fetch API. + */ +function normalizeHeaders(headers) { + return new Headers(headers) +} + +/** + * @example SponsorKit sends OpenCollective personal tokens through the documented HTTP header. + */ +describe('sponsorKit OpenCollective pnpm patch', () => { + afterEach(() => { + vi.unstubAllGlobals() + }) + + /** + * @example A personal token is sent as Personal-Token, not as the legacy Api-Key header. + */ + it('uses the OpenCollective Personal-Token header for GraphQL requests', async () => { + const requests = [] + + vi.stubGlobal('fetch', vi.fn(async (url, init) => { + requests.push({ + url, + headers: normalizeHeaders(init?.headers), + }) + + return createGraphqlResponse(requests.length === 1 ? 'orders' : 'transactions') + })) + + await ProvidersMap.opencollective.fetchSponsors({ + includePastSponsors: true, + opencollective: { + key: 'personal-token-example', + slug: 'proj-airi', + }, + }) + + /** + * @example expect SponsorKit to request both OpenCollective orders and transactions. + */ + expect(requests).toHaveLength(2) + + for (const request of requests) { + /** + * @example expect every GraphQL request to use OpenCollective's Personal-Token auth header. + */ + expect(request.headers.get('Personal-Token')).toBe('personal-token-example') + + /** + * @example expect SponsorKit not to send the deprecated Api-Key header for personal tokens. + */ + expect(request.headers.has('Api-Key')).toBe(false) + } + }) +}) diff --git a/scripts/sponsorkit/vitest.config.ts b/scripts/sponsorkit/vitest.config.ts new file mode 100644 index 000000000..2b1c323fe --- /dev/null +++ b/scripts/sponsorkit/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'node', + }, +})