Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions packages/app/api/share/[kind].ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { describe, it, expect, beforeEach, afterEach } from "vitest";

// §15.x test for /api/share/[kind] — the single function that fans out
// to the per-kind OG handlers in _lib (one function, two surfaces, to
// stay under the 12-function deploy cap; see #380). The handlers
// themselves are covered by share-proof/share-link suites; this pins the
// dispatch: kind=link -> link card, default -> proof card, resolved from
// either req.query.kind (Vercel) or the request path (fallback).
// §15.x test for the api/share/proof dispatcher — one static function
// that fans out to the per-kind OG handlers in _lib (one function, two
// surfaces, to stay under the 12-function deploy cap; see #380). The
// handlers themselves are covered by the share-proof/share-link suites;
// this pins the dispatch: ?kind=link -> link card, default -> proof card.

import handler from "./[kind].js";
import handler from "./proof.js";

interface MockRes {
headers: Record<string, string>;
Expand Down Expand Up @@ -36,29 +35,29 @@ afterEach(() => {
delete process.env.VERCEL_URL;
});

describe("share kind dispatch", () => {
it("kind=link via req.query routes to the link handler", async () => {
describe("share dispatch", () => {
it("?kind=link via the query string routes to the link handler", async () => {
const res = makeRes();
await handler(
{ url: "/api/share/link?type=conditional-invoice&chainId=421614&id=2", query: { kind: "link" }, headers: {} },
{ url: "/api/share/proof?kind=link&type=conditional-invoice&chainId=421614&id=2", headers: {} },
res,
);
expect(res.body).toContain("Conditional invoice on Blank");
});

it("kind=link inferred from the path when query.kind is absent", async () => {
it("?kind=link via req.query (Vercel-injected) routes to the link handler", async () => {
const res = makeRes();
await handler(
{ url: "/api/share/link?type=pay&id=alice", headers: {} },
{ url: "/api/share/proof?type=pay&id=alice", query: { kind: "link", type: "pay", id: "alice" }, headers: {} },
res,
);
expect(res.body).toContain("Get paid privately on Blank");
});

it("default (proof) handler when kind is not link", async () => {
it("no kind -> proof handler (the /v/:proofId default)", async () => {
const res = makeRes();
// No proof id -> generic proof copy, no chain read needed.
await handler({ url: "/api/share/proof", query: { kind: "proof" }, headers: {} }, res);
await handler({ url: "/api/share/proof", headers: {} }, res);
expect(res.body).toContain("Encrypted proof on Blank");
});
});
32 changes: 32 additions & 0 deletions packages/app/api/share/proof.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* /api/share/proof — single static serverless function that fans out to
* the per-kind OG handlers in _lib. One function, two surfaces, so the
* resource-link previews don't push the deploy over the 12-function cap
* (#380).
*
* Why a static route (not api/share/[kind].ts):
* A dynamic [kind] route is resolved AFTER vercel.json `rewrites`, so
* the catch-all `/(.*) -> /index.html` shadows it and the function is
* never reached (verified live: /api/share/<dynamic> returned the SPA
* shell). Static API files are matched in the filesystem phase, before
* rewrites — the same reason /api/health and the original proof route
* always worked. So both surfaces ride this one static path:
*
* /v/:proofId -> /api/share/proof (kind=proof)
* /pay /claim /shop -> /api/share/proof?kind=link&...(kind=link)
* /fund /conditional-invoice
*
* Vercel injects the destination query onto req.query; we read ?kind to
* pick the handler and fall back to the proof handler (the original
* behaviour) when it is absent.
*/

import proofHandler from "../_lib/share-proof.js";
import linkHandler from "../_lib/share-link.js";

export default async function handler(req: any, res: any) {
const url = new URL(req.url ?? "/", "http://x");
const kind = req.query?.kind ?? url.searchParams.get("kind");
if (kind === "link") return linkHandler(req, res);
return proofHandler(req, res);
}
10 changes: 5 additions & 5 deletions packages/app/vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
{ "source": "/v/:proofId", "destination": "/api/share/proof?id=:proofId" },
{ "source": "/api/relayer-health", "destination": "/api/health?kind=relayer" },
{ "source": "/api/reconcile-user", "destination": "/api/cron/reconcile-user" },
{ "source": "/conditional-invoice/:chainId/:escrowId", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/link?type=conditional-invoice&chainId=:chainId&id=:escrowId" },
{ "source": "/pay/:identifier", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/link?type=pay&id=:identifier" },
{ "source": "/claim/:chainId/:linkId", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/link?type=claim&chainId=:chainId&id=:linkId" },
{ "source": "/shop/:chainId/:listingId", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/link?type=shop&chainId=:chainId&id=:listingId" },
{ "source": "/fund/:chainId/:campaignId", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/link?type=fund&chainId=:chainId&id=:campaignId" },
{ "source": "/conditional-invoice/:chainId/:escrowId", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/proof?kind=link&type=conditional-invoice&chainId=:chainId&id=:escrowId" },
{ "source": "/pay/:identifier", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/proof?kind=link&type=pay&id=:identifier" },
{ "source": "/claim/:chainId/:linkId", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/proof?kind=link&type=claim&chainId=:chainId&id=:linkId" },
{ "source": "/shop/:chainId/:listingId", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/proof?kind=link&type=shop&chainId=:chainId&id=:listingId" },
{ "source": "/fund/:chainId/:campaignId", "missing": [{ "type": "query", "key": "app" }], "destination": "/api/share/proof?kind=link&type=fund&chainId=:chainId&id=:campaignId" },
{ "source": "/(.*)", "destination": "/index.html" }
],
"headers": [
Expand Down
Loading