A CLI for publishing standard.site documents to ATProto sequoia.pub
standard site lexicon cli publishing
55
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add cors support

Co-Authored-By: @stevedylan.dev

authored by

Heath Stewart and committed by tangled.org 9f834bf6 4cbb91ee

+64 -3
+10
docs/src/index.ts
··· 1 1 import { Hono } from "hono"; 2 + import { cors } from "hono/cors"; 2 3 import auth from "./routes/auth"; 3 4 import subscribe from "./routes/subscribe"; 5 + import "./lib/path-redirect"; 4 6 5 7 type Bindings = { 6 8 ASSETS: Fetcher; ··· 12 14 13 15 app.route("/oauth", auth); 14 16 app.route("/subscribe", subscribe); 17 + app.use("/subscribe", cors({ 18 + origin: (origin) => origin, 19 + credentials: true, 20 + })); 21 + app.use("/subscribe/*", cors({ 22 + origin: (origin) => origin, 23 + credentials: true, 24 + })); 15 25 16 26 app.get("/api/health", (c) => { 17 27 return c.json({ status: "ok" });
+1 -1
docs/src/lib/oauth-client.ts
··· 19 19 redirect_uris: [redirectUri], 20 20 grant_types: ["authorization_code", "refresh_token"], 21 21 response_types: ["code"], 22 - scope: "atproto transition:generic", 22 + scope: "atproto site.standard.graph.subscription", 23 23 token_endpoint_auth_method: "none", 24 24 application_type: "web", 25 25 dpop_bound_access_tokens: true,
+51
docs/src/lib/path-redirect.ts
··· 1 + // Cloudflare Workers compatibility patches for @atproto libraries. 2 + // 3 + // 1. Workers don't support `redirect: 'error'` — simulate it with 'manual'. 4 + // 2. Workers don't support the standard `cache` option in Request — strip it. 5 + 6 + function sanitizeInit(init?: RequestInit): RequestInit | undefined { 7 + if (!init) return init; 8 + const { cache, redirect, ...rest } = init; 9 + return { 10 + ...rest, 11 + // Workers only support 'follow' and 'manual' 12 + redirect: redirect === "error" ? "manual" : redirect, 13 + // Workers don't support standard cache modes — omit entirely 14 + ...(cache ? {} : {}), 15 + }; 16 + } 17 + 18 + const errorRedirectRequests = new WeakSet<Request>(); 19 + const OriginalRequest = globalThis.Request; 20 + 21 + globalThis.Request = class extends OriginalRequest { 22 + constructor( 23 + input: RequestInfo | URL, 24 + init?: RequestInit, 25 + ) { 26 + super(input, sanitizeInit(init)); 27 + if (init?.redirect === "error") { 28 + errorRedirectRequests.add(this); 29 + } 30 + } 31 + } as typeof Request; 32 + 33 + const originalFetch = globalThis.fetch; 34 + globalThis.fetch = (async ( 35 + input: RequestInfo | URL, 36 + init?: RequestInit, 37 + ): Promise<Response> => { 38 + const cleanInit = sanitizeInit(init); 39 + const response = await originalFetch(input, cleanInit); 40 + 41 + // Simulate redirect: 'error' — throw on 3xx 42 + const wantsRedirectError = 43 + init?.redirect === "error" || 44 + (input instanceof Request && errorRedirectRequests.has(input)); 45 + 46 + if (wantsRedirectError && response.status >= 300 && response.status < 400) { 47 + throw new TypeError("unexpected redirect"); 48 + } 49 + 50 + return response; 51 + }) as typeof fetch;
+2 -2
docs/src/routes/auth.ts
··· 27 27 redirect_uris: [redirectUri], 28 28 grant_types: ["authorization_code", "refresh_token"], 29 29 response_types: ["code"], 30 - scope: "atproto transition:generic", 30 + scope: "atproto site.standard.graph.subscription", 31 31 token_endpoint_auth_method: "none", 32 32 application_type: "web", 33 33 dpop_bound_access_tokens: true, ··· 44 44 45 45 const client = createOAuthClient(c.env.SEQUOIA_SESSIONS, c.env.CLIENT_URL); 46 46 const authUrl = await client.authorize(handle, { 47 - scope: "atproto transition:generic", 47 + scope: "atproto site.standard.graph.subscription", 48 48 }); 49 49 50 50 return c.redirect(authUrl.toString());