Skip to content

Commit 216fd64

Browse files
committed
Add prettier and wrangler build pre-commit hooks
Add prettier hook for JS/TS/CSS/HTML files (excludes Go-generated wasm_exec.js). Add wrangler build hook to validate Cloudflare Pages Functions TypeScript on commit. Format existing web files.
1 parent 413c408 commit 216fd64

6 files changed

Lines changed: 840 additions & 669 deletions

File tree

.pre-commit-config.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ repos:
2929
types: [go]
3030
pass_filenames: false
3131

32+
- repo: https://github.com/pre-commit/mirrors-prettier
33+
rev: v4.0.0-alpha.8
34+
hooks:
35+
- id: prettier
36+
types_or: [javascript, ts, css, html]
37+
exclude: wasm_exec\.js$
38+
39+
- repo: local
40+
hooks:
41+
- id: wrangler-build
42+
name: wrangler build
43+
entry: bash -c 'cd web && wrangler pages functions build --outdir /tmp/certkit-fn-check'
44+
language: system
45+
files: ^web/functions/
46+
types: [ts]
47+
pass_filenames: false
48+
3249
- repo: https://github.com/igorshubovych/markdownlint-cli
3350
rev: v0.43.0
3451
hooks:

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ pre-commit install
381381
pre-commit run --all-files # Manual run against all files
382382
```
383383

384-
Configured hooks: `goimports`, `go vet`, `go build`, `go test`, `markdownlint`.
384+
Configured hooks: `goimports`, `go vet`, `go build`, `go test`, `prettier`, `wrangler build`, `markdownlint`.
385385

386386
### Tooling gates
387387

web/functions/api/fetch.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@ const MAX_RESPONSE_SIZE = 256 * 1024; // 256KB — certs are small
1010
// Allowed origins for CORS. The proxy only serves requests from these origins.
1111
const ALLOWED_ORIGINS: string[] = [
1212
"https://certkit.pages.dev",
13-
"http://localhost:8080", // local dev (make wasm-serve)
14-
"http://localhost:8788", // wrangler pages dev
13+
"http://localhost:8080", // local dev (make wasm-serve)
14+
"http://localhost:8788", // wrangler pages dev
1515
];
1616

1717
function corsHeaders(origin: string | null): Record<string, string> {
18-
const allowed = origin && ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
18+
const allowed =
19+
origin && ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
1920
return {
2021
"Access-Control-Allow-Origin": allowed,
2122
"Access-Control-Allow-Methods": "GET, OPTIONS",
2223
"Access-Control-Allow-Headers": "Content-Type",
23-
"Vary": "Origin",
24+
Vary: "Origin",
2425
};
2526
}
2627

@@ -144,7 +145,7 @@ const ALLOWED_DOMAINS: string[] = [
144145
function isAllowedDomain(hostname: string): boolean {
145146
const lower = hostname.toLowerCase();
146147
return ALLOWED_DOMAINS.some(
147-
(domain) => lower === domain || lower.endsWith("." + domain)
148+
(domain) => lower === domain || lower.endsWith("." + domain),
148149
);
149150
}
150151

@@ -238,15 +239,19 @@ export const onRequestGet: PagesFunction = async ({ request }) => {
238239

239240
// Block query strings and fragments — AIA URLs are static file paths
240241
if (parsed.search || parsed.hash) {
241-
return errorResponse(400, "Query strings and fragments are not allowed", origin);
242+
return errorResponse(
243+
400,
244+
"Query strings and fragments are not allowed",
245+
origin,
246+
);
242247
}
243248

244249
if (!isAllowedDomain(parsed.hostname)) {
245250
return errorResponse(
246251
403,
247252
`Domain '${parsed.hostname}' is not in the allow list. ` +
248253
"This proxy only fetches from known CA AIA endpoints.",
249-
origin
254+
origin,
250255
);
251256
}
252257

@@ -262,7 +267,11 @@ export const onRequestGet: PagesFunction = async ({ request }) => {
262267
!path.endsWith("/") &&
263268
!path.endsWith(".crl")
264269
) {
265-
return errorResponse(403, "URL path does not look like a certificate file", origin);
270+
return errorResponse(
271+
403,
272+
"URL path does not look like a certificate file",
273+
origin,
274+
);
266275
}
267276

268277
// Reconstruct from validated components — never forward the raw input URL.
@@ -309,7 +318,10 @@ export const onRequestGet: PagesFunction = async ({ request }) => {
309318
responseHeaders.set("Content-Length", body.byteLength.toString());
310319
responseHeaders.set("X-Content-Type-Options", "nosniff");
311320
// Cache forever — AIA certificates are immutable (same URL = same cert)
312-
responseHeaders.set("Cache-Control", "public, max-age=31536000, immutable");
321+
responseHeaders.set(
322+
"Cache-Control",
323+
"public, max-age=31536000, immutable",
324+
);
313325

314326
return new Response(body, { status: 200, headers: responseHeaders });
315327
} catch {
@@ -321,7 +333,11 @@ export const onRequestGet: PagesFunction = async ({ request }) => {
321333
return errorResponse(lastStatus, lastMessage, origin);
322334
};
323335

324-
function errorResponse(status: number, message: string, origin: string | null): Response {
336+
function errorResponse(
337+
status: number,
338+
message: string,
339+
origin: string | null,
340+
): Response {
325341
return new Response(JSON.stringify({ error: message }), {
326342
status,
327343
headers: { ...corsHeaders(origin), "Content-Type": "application/json" },

0 commit comments

Comments
 (0)