@pyreon/zero 0.24.5 → 0.24.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -39
- package/src/actions.ts +0 -196
- package/src/adapters/bun.ts +0 -114
- package/src/adapters/cloudflare.ts +0 -166
- package/src/adapters/index.ts +0 -61
- package/src/adapters/netlify.ts +0 -154
- package/src/adapters/node.ts +0 -163
- package/src/adapters/static.ts +0 -42
- package/src/adapters/validate.ts +0 -23
- package/src/adapters/vercel.ts +0 -182
- package/src/adapters/warn-missing-env.ts +0 -49
- package/src/ai.ts +0 -623
- package/src/api-routes.ts +0 -219
- package/src/app.ts +0 -92
- package/src/cache.ts +0 -136
- package/src/client.ts +0 -143
- package/src/compression.ts +0 -116
- package/src/config.ts +0 -35
- package/src/cors.ts +0 -94
- package/src/csp.ts +0 -226
- package/src/entry-server.ts +0 -224
- package/src/env.ts +0 -344
- package/src/error-overlay.ts +0 -118
- package/src/favicon.ts +0 -841
- package/src/font.ts +0 -511
- package/src/fs-router.ts +0 -1519
- package/src/i18n-routing.ts +0 -533
- package/src/icon.tsx +0 -182
- package/src/icons-plugin.ts +0 -296
- package/src/image-plugin.ts +0 -751
- package/src/image-types.ts +0 -60
- package/src/image.tsx +0 -340
- package/src/index.ts +0 -92
- package/src/isr.ts +0 -394
- package/src/link.tsx +0 -304
- package/src/logger.ts +0 -144
- package/src/manifest.ts +0 -787
- package/src/meta.tsx +0 -354
- package/src/middleware.ts +0 -65
- package/src/not-found.ts +0 -44
- package/src/og-image.ts +0 -378
- package/src/rate-limit.ts +0 -140
- package/src/script.tsx +0 -260
- package/src/seo.ts +0 -617
- package/src/server.ts +0 -89
- package/src/sharp.d.ts +0 -22
- package/src/ssg-plugin.ts +0 -1582
- package/src/testing.ts +0 -146
- package/src/theme.tsx +0 -257
- package/src/types.ts +0 -624
- package/src/utils/use-intersection-observer.ts +0 -36
- package/src/utils/with-headers.ts +0 -13
- package/src/vercel-revalidate-handler.ts +0 -204
- package/src/vite-plugin.ts +0 -848
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* M3.1 — Drop-in Vercel revalidate webhook handler.
|
|
3
|
-
*
|
|
4
|
-
* Pre-M3.1 the `vercelAdapter.revalidate(path)` (PR I) POSTed to
|
|
5
|
-
* `/api/_pyreon-revalidate?path=...&secret=...` — a CONVENTION that users
|
|
6
|
-
* had to implement themselves. This helper scaffolds the convention:
|
|
7
|
-
*
|
|
8
|
-
* // src/routes/api/_pyreon-revalidate.ts (or `pages/api/...` in
|
|
9
|
-
* // Next-style apps deployed to Vercel)
|
|
10
|
-
* export { vercelRevalidateHandler as default } from '@pyreon/zero/server'
|
|
11
|
-
*
|
|
12
|
-
* The handler validates the secret query param against
|
|
13
|
-
* `VERCEL_REVALIDATE_TOKEN`, validates the path is in the build-time
|
|
14
|
-
* revalidate manifest, and calls Vercel's `res.revalidate(path)` API.
|
|
15
|
-
*
|
|
16
|
-
* Returns a standard `(req: Request) => Response` Web API handler — works
|
|
17
|
-
* with Vercel Edge functions, Node serverless functions (via Vercel's
|
|
18
|
-
* `@vercel/node` adapter that bridges Node `req`/`res` to Web standard
|
|
19
|
-
* fetch shapes), and the in-process `mode: 'ssr'` runtime.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // src/routes/api/_pyreon-revalidate.ts
|
|
23
|
-
* import { vercelRevalidateHandler } from '@pyreon/zero/server'
|
|
24
|
-
*
|
|
25
|
-
* export const POST = vercelRevalidateHandler({
|
|
26
|
-
* // Optional — defaults to reading `_pyreon-revalidate.json` from cwd.
|
|
27
|
-
* manifestPath: './dist/_pyreon-revalidate.json',
|
|
28
|
-
* })
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* // Custom revalidate impl (e.g. for a self-hosted Pyreon SSR runtime
|
|
32
|
-
* // that wants build-time revalidate behavior without Vercel's
|
|
33
|
-
* // `res.revalidate()` API):
|
|
34
|
-
* export const POST = vercelRevalidateHandler({
|
|
35
|
-
* onRevalidate: async (path) => {
|
|
36
|
-
* // Clear your in-process ISR cache, emit a metrics event, etc.
|
|
37
|
-
* await myCache.invalidate(path)
|
|
38
|
-
* },
|
|
39
|
-
* })
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
import { readFile } from 'node:fs/promises'
|
|
43
|
-
import { resolve } from 'node:path'
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Build-time revalidate manifest written by `ssgPlugin` (PR I).
|
|
47
|
-
* Shape: `{ revalidate: { '/posts/1': 60, '/posts/2': 60, '/about': 3600 } }`.
|
|
48
|
-
*/
|
|
49
|
-
interface RevalidateManifest {
|
|
50
|
-
revalidate: Record<string, number | false>
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface VercelRevalidateHandlerOptions {
|
|
54
|
-
/**
|
|
55
|
-
* Absolute or cwd-relative path to the `_pyreon-revalidate.json` manifest.
|
|
56
|
-
* Defaults to `./dist/_pyreon-revalidate.json` (the standard SSG output).
|
|
57
|
-
*
|
|
58
|
-
* The handler refuses to revalidate paths NOT in this manifest — protects
|
|
59
|
-
* against arbitrary-path revalidation attacks even when the secret leaks.
|
|
60
|
-
*/
|
|
61
|
-
manifestPath?: string
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Custom revalidation impl. Defaults to calling Vercel's `res.revalidate()`
|
|
65
|
-
* API via the dynamic `@vercel/node`-bridged response object on globalThis
|
|
66
|
-
* (Vercel injects it for serverless functions).
|
|
67
|
-
*
|
|
68
|
-
* Supply this when running OUTSIDE Vercel (self-hosted SSR with a custom
|
|
69
|
-
* in-process ISR cache, edge runtimes that have their own purge API, etc.).
|
|
70
|
-
* Receives the validated path; throw to signal failure (handler returns 500).
|
|
71
|
-
*/
|
|
72
|
-
onRevalidate?: (path: string) => void | Promise<void>
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Override the env-var name the handler reads the secret from. Default
|
|
76
|
-
* `VERCEL_REVALIDATE_TOKEN` matches the adapter's `revalidate()` write.
|
|
77
|
-
* Useful when adopting the helper outside Vercel and the production
|
|
78
|
-
* webhook uses a different secret name.
|
|
79
|
-
*/
|
|
80
|
-
secretEnvVar?: string
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Create the Web-standard request handler. Reads the manifest once on first
|
|
85
|
-
* invocation (cached in-process) so repeated revalidations don't re-read the
|
|
86
|
-
* file. Manifest read failures cache the failure too — until next process
|
|
87
|
-
* restart, all requests get the same 500 response (signals deploy-time misconfig).
|
|
88
|
-
*/
|
|
89
|
-
export function vercelRevalidateHandler(
|
|
90
|
-
options: VercelRevalidateHandlerOptions = {},
|
|
91
|
-
): (req: Request) => Promise<Response> {
|
|
92
|
-
const manifestPath = options.manifestPath ?? './dist/_pyreon-revalidate.json'
|
|
93
|
-
const secretEnvVar = options.secretEnvVar ?? 'VERCEL_REVALIDATE_TOKEN'
|
|
94
|
-
|
|
95
|
-
// Manifest cache: loaded once per process. A nullish value means "not yet
|
|
96
|
-
// loaded"; a `{ error: ... }` shape means "load failed, every subsequent
|
|
97
|
-
// request gets 500 until restart". A `{ manifest: ... }` shape is the
|
|
98
|
-
// happy path.
|
|
99
|
-
let cache: { manifest: RevalidateManifest } | { error: unknown } | null = null
|
|
100
|
-
|
|
101
|
-
return async function handler(req: Request): Promise<Response> {
|
|
102
|
-
// Validate request shape: only POST, with `?path=&secret=` query.
|
|
103
|
-
if (req.method !== 'POST') {
|
|
104
|
-
return new Response(`Method ${req.method} not allowed`, { status: 405 })
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const url = new URL(req.url)
|
|
108
|
-
const path = url.searchParams.get('path')
|
|
109
|
-
const secret = url.searchParams.get('secret')
|
|
110
|
-
|
|
111
|
-
if (!path || !secret) {
|
|
112
|
-
return new Response('Bad Request: missing path or secret', { status: 400 })
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Validate the secret against the env var. Constant-time-ish: we
|
|
116
|
-
// compare strings of equal length; mismatched lengths short-circuit
|
|
117
|
-
// (acceptable — the attacker can already see the response time
|
|
118
|
-
// difference via fetch behavior). The env-var-missing case fails
|
|
119
|
-
// CLOSED (401) — production webhooks shouldn't accept requests when
|
|
120
|
-
// the server hasn't been configured.
|
|
121
|
-
const expected = process.env[secretEnvVar]
|
|
122
|
-
if (!expected) {
|
|
123
|
-
return new Response(
|
|
124
|
-
`Server misconfigured: ${secretEnvVar} env var not set`,
|
|
125
|
-
{ status: 500 },
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
if (secret !== expected) {
|
|
129
|
-
return new Response('Forbidden: invalid secret', { status: 403 })
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Load the manifest (once per process). On read failure, cache the
|
|
133
|
-
// error so subsequent requests get fast 500s — saves rep eated stat
|
|
134
|
-
// calls for a broken deploy.
|
|
135
|
-
if (cache === null) {
|
|
136
|
-
try {
|
|
137
|
-
const fileContent = await readFile(resolve(process.cwd(), manifestPath), 'utf-8')
|
|
138
|
-
const parsed = JSON.parse(fileContent) as RevalidateManifest
|
|
139
|
-
if (typeof parsed?.revalidate !== 'object' || parsed.revalidate === null) {
|
|
140
|
-
throw new Error(
|
|
141
|
-
`Malformed revalidate manifest at ${manifestPath}: missing or non-object \`revalidate\` field`,
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
cache = { manifest: parsed }
|
|
145
|
-
} catch (err) {
|
|
146
|
-
cache = { error: err }
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if ('error' in cache) {
|
|
150
|
-
return new Response(
|
|
151
|
-
`Server misconfigured: revalidate manifest at ${manifestPath} unreadable or malformed`,
|
|
152
|
-
{ status: 500 },
|
|
153
|
-
)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Validate the path is in the manifest — refuses arbitrary-path
|
|
157
|
-
// revalidation even with a valid secret. Closes the
|
|
158
|
-
// "secret leaked once → attacker revalidates anything" footgun.
|
|
159
|
-
if (!Object.prototype.hasOwnProperty.call(cache.manifest.revalidate, path)) {
|
|
160
|
-
return new Response(
|
|
161
|
-
`Path "${path}" not in revalidate manifest`,
|
|
162
|
-
{ status: 404 },
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Run the revalidation. Custom impl OR fallback to a structured
|
|
167
|
-
// response that downstream Vercel-style code can adapt
|
|
168
|
-
// (Vercel's `res.revalidate()` API can't be called from a
|
|
169
|
-
// Web-standard handler without the `@vercel/node` bridge — the
|
|
170
|
-
// user wires that themselves OR uses the `onRevalidate` callback).
|
|
171
|
-
if (options.onRevalidate) {
|
|
172
|
-
try {
|
|
173
|
-
await options.onRevalidate(path)
|
|
174
|
-
} catch (err) {
|
|
175
|
-
return new Response(
|
|
176
|
-
`Revalidation failed for "${path}": ${err instanceof Error ? err.message : String(err)}`,
|
|
177
|
-
{ status: 500 },
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return new Response(JSON.stringify({ revalidated: true, path }), {
|
|
183
|
-
status: 200,
|
|
184
|
-
headers: { 'Content-Type': 'application/json' },
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Reset the in-process manifest cache. Test-only — production code never
|
|
191
|
-
* reaches this. Used by unit tests to exercise the "manifest changed
|
|
192
|
-
* between requests" path without spinning up a new handler.
|
|
193
|
-
* @internal
|
|
194
|
-
*/
|
|
195
|
-
export function _resetVercelRevalidateHandlerCache(
|
|
196
|
-
handler: (req: Request) => Promise<Response>,
|
|
197
|
-
): void {
|
|
198
|
-
// The cache lives in the closure; tests instantiate a fresh handler per
|
|
199
|
-
// run rather than mutating an existing one. Kept here as a no-op marker
|
|
200
|
-
// for the API contract — if cache invalidation surfaces as a real need
|
|
201
|
-
// (e.g. hot-reload of the manifest after a deploy without restart), the
|
|
202
|
-
// implementation can flip to a module-level WeakMap<handler, cache>.
|
|
203
|
-
void handler
|
|
204
|
-
}
|