@pyreon/zero 0.24.4 → 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
package/src/env.ts
DELETED
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Environment variable validation.
|
|
3
|
-
*
|
|
4
|
-
* Infers types from default values — no verbose validator imports needed.
|
|
5
|
-
* Explicit validators (`url()`, `oneOf()`) available for special cases.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { validateEnv, url, oneOf } from "@pyreon/zero/env"
|
|
10
|
-
*
|
|
11
|
-
* const env = validateEnv({
|
|
12
|
-
* PORT: 3000, // number, default 3000
|
|
13
|
-
* DEBUG: false, // boolean, default false
|
|
14
|
-
* HOST: "localhost", // string, default "localhost"
|
|
15
|
-
* DATABASE_URL: url(), // validated URL, required
|
|
16
|
-
* NODE_ENV: oneOf(["development", "production", "test"]),
|
|
17
|
-
* API_KEY: String, // required string, no default
|
|
18
|
-
* MAX_RETRIES: Number, // required number, no default
|
|
19
|
-
* })
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
export interface EnvValidatorOptions<T = string> {
|
|
24
|
-
/** Whether this variable is required. Default: true */
|
|
25
|
-
required?: boolean
|
|
26
|
-
/** Default value when not set. Makes the variable optional. */
|
|
27
|
-
default?: T
|
|
28
|
-
/** Human-readable description for error messages. */
|
|
29
|
-
description?: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface EnvValidator<T> {
|
|
33
|
-
__type: 'env-validator'
|
|
34
|
-
parse: (raw: string | undefined, key: string) => T
|
|
35
|
-
required: boolean
|
|
36
|
-
defaultValue?: T | undefined
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ─── Explicit validators (for special cases) ────────────────────────────────
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* String validator — accepts any non-empty string.
|
|
43
|
-
*/
|
|
44
|
-
export function str(options?: EnvValidatorOptions<string>): EnvValidator<string> {
|
|
45
|
-
const required = options?.default === undefined && options?.required !== false
|
|
46
|
-
return {
|
|
47
|
-
__type: 'env-validator',
|
|
48
|
-
required,
|
|
49
|
-
defaultValue: options?.default,
|
|
50
|
-
parse(raw, key) {
|
|
51
|
-
if (raw === undefined || raw === '') {
|
|
52
|
-
if (options?.default !== undefined) return options.default
|
|
53
|
-
throw new EnvError(key, 'is required but not set', options?.description)
|
|
54
|
-
}
|
|
55
|
-
return raw
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Number validator — parses to a number, rejects NaN.
|
|
62
|
-
*/
|
|
63
|
-
export function num(options?: EnvValidatorOptions<number>): EnvValidator<number> {
|
|
64
|
-
const required = options?.default === undefined && options?.required !== false
|
|
65
|
-
return {
|
|
66
|
-
__type: 'env-validator',
|
|
67
|
-
required,
|
|
68
|
-
defaultValue: options?.default,
|
|
69
|
-
parse(raw, key) {
|
|
70
|
-
if (raw === undefined || raw === '') {
|
|
71
|
-
if (options?.default !== undefined) return options.default
|
|
72
|
-
throw new EnvError(key, 'is required but not set', options?.description)
|
|
73
|
-
}
|
|
74
|
-
const n = Number(raw)
|
|
75
|
-
if (Number.isNaN(n)) {
|
|
76
|
-
throw new EnvError(key, `must be a number, got "${raw}"`, options?.description)
|
|
77
|
-
}
|
|
78
|
-
return n
|
|
79
|
-
},
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Boolean validator — accepts "true"/"1" as true, "false"/"0" as false.
|
|
85
|
-
*/
|
|
86
|
-
export function bool(options?: EnvValidatorOptions<boolean>): EnvValidator<boolean> {
|
|
87
|
-
const required = options?.default === undefined && options?.required !== false
|
|
88
|
-
return {
|
|
89
|
-
__type: 'env-validator',
|
|
90
|
-
required,
|
|
91
|
-
defaultValue: options?.default,
|
|
92
|
-
parse(raw, key) {
|
|
93
|
-
if (raw === undefined || raw === '') {
|
|
94
|
-
if (options?.default !== undefined) return options.default
|
|
95
|
-
throw new EnvError(key, 'is required but not set', options?.description)
|
|
96
|
-
}
|
|
97
|
-
const lower = raw.toLowerCase()
|
|
98
|
-
if (lower === 'true' || lower === '1') return true
|
|
99
|
-
if (lower === 'false' || lower === '0') return false
|
|
100
|
-
throw new EnvError(key, `must be "true" or "false", got "${raw}"`, options?.description)
|
|
101
|
-
},
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* URL validator — validates that the value is a valid URL.
|
|
107
|
-
*/
|
|
108
|
-
export function url(options?: EnvValidatorOptions<string>): EnvValidator<string> {
|
|
109
|
-
const required = options?.default === undefined && options?.required !== false
|
|
110
|
-
return {
|
|
111
|
-
__type: 'env-validator',
|
|
112
|
-
required,
|
|
113
|
-
defaultValue: options?.default,
|
|
114
|
-
parse(raw, key) {
|
|
115
|
-
if (raw === undefined || raw === '') {
|
|
116
|
-
if (options?.default !== undefined) return options.default
|
|
117
|
-
throw new EnvError(key, 'is required but not set', options?.description)
|
|
118
|
-
}
|
|
119
|
-
try {
|
|
120
|
-
new URL(raw)
|
|
121
|
-
return raw
|
|
122
|
-
} catch {
|
|
123
|
-
throw new EnvError(key, `must be a valid URL, got "${raw}"`, options?.description)
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Enum validator — value must be one of the allowed values.
|
|
131
|
-
*/
|
|
132
|
-
export function oneOf<T extends string>(
|
|
133
|
-
values: readonly T[],
|
|
134
|
-
options?: EnvValidatorOptions<T>,
|
|
135
|
-
): EnvValidator<T> {
|
|
136
|
-
const required = options?.default === undefined && options?.required !== false
|
|
137
|
-
return {
|
|
138
|
-
__type: 'env-validator',
|
|
139
|
-
required,
|
|
140
|
-
defaultValue: options?.default,
|
|
141
|
-
parse(raw, key) {
|
|
142
|
-
if (raw === undefined || raw === '') {
|
|
143
|
-
if (options?.default !== undefined) return options.default
|
|
144
|
-
throw new EnvError(key, 'is required but not set', options?.description)
|
|
145
|
-
}
|
|
146
|
-
if (!values.includes(raw as T)) {
|
|
147
|
-
throw new EnvError(
|
|
148
|
-
key,
|
|
149
|
-
`must be one of [${values.join(', ')}], got "${raw}"`,
|
|
150
|
-
options?.description,
|
|
151
|
-
)
|
|
152
|
-
}
|
|
153
|
-
return raw as T
|
|
154
|
-
},
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ─── Internal helpers ───────────────────────────────────────────────────────
|
|
159
|
-
|
|
160
|
-
class EnvError extends Error {
|
|
161
|
-
constructor(key: string, message: string, description?: string) {
|
|
162
|
-
const desc = description ? ` (${description})` : ''
|
|
163
|
-
super(`[Pyreon] ${key}${desc}: ${message}`)
|
|
164
|
-
this.name = 'EnvError'
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function isEnvValidator(v: unknown): v is EnvValidator<unknown> {
|
|
169
|
-
return typeof v === 'object' && v !== null && (v as any).__type === 'env-validator'
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Convert a plain schema value to an EnvValidator.
|
|
174
|
-
*
|
|
175
|
-
* - `3000` → num({ default: 3000 })
|
|
176
|
-
* - `false` → bool({ default: false })
|
|
177
|
-
* - `"localhost"` → str({ default: "localhost" })
|
|
178
|
-
* - `String` → str() (required)
|
|
179
|
-
* - `Number` → num() (required)
|
|
180
|
-
* - `Boolean` → bool() (required)
|
|
181
|
-
* - EnvValidator → pass through
|
|
182
|
-
*/
|
|
183
|
-
function toValidator(value: unknown): EnvValidator<unknown> {
|
|
184
|
-
if (isEnvValidator(value)) return value
|
|
185
|
-
|
|
186
|
-
// Constructor markers → required, no default
|
|
187
|
-
if (value === String) return str()
|
|
188
|
-
if (value === Number) return num()
|
|
189
|
-
if (value === Boolean) return bool()
|
|
190
|
-
|
|
191
|
-
// Plain values → infer type + use as default
|
|
192
|
-
if (typeof value === 'number') return num({ default: value })
|
|
193
|
-
if (typeof value === 'boolean') return bool({ default: value })
|
|
194
|
-
if (typeof value === 'string') return str({ default: value })
|
|
195
|
-
|
|
196
|
-
throw new Error(`[Pyreon] Invalid schema value: ${String(value)}. Use a default value, String/Number/Boolean, or a validator like url().`)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// ─── Type inference ─────────────────────────────────────────────────────────
|
|
200
|
-
|
|
201
|
-
/** Schema entry: plain value, constructor, or explicit validator. */
|
|
202
|
-
type SchemaEntry =
|
|
203
|
-
| string | number | boolean
|
|
204
|
-
| StringConstructor | NumberConstructor | BooleanConstructor
|
|
205
|
-
| EnvValidator<any>
|
|
206
|
-
|
|
207
|
-
/** Infer the output type from a schema entry. */
|
|
208
|
-
type InferEntry<T> =
|
|
209
|
-
T extends EnvValidator<infer V> ? V :
|
|
210
|
-
T extends StringConstructor ? string :
|
|
211
|
-
T extends NumberConstructor ? number :
|
|
212
|
-
T extends BooleanConstructor ? boolean :
|
|
213
|
-
T extends string ? string :
|
|
214
|
-
T extends number ? number :
|
|
215
|
-
T extends boolean ? boolean :
|
|
216
|
-
never
|
|
217
|
-
|
|
218
|
-
type InferEnvSchema<T> = {
|
|
219
|
-
[K in keyof T]: InferEntry<T[K]>
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// ─── Main API ───────────────────────────────────────────────────────────────
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Validate environment variables.
|
|
226
|
-
*
|
|
227
|
-
* Schema values can be:
|
|
228
|
-
* - **Default values**: `3000`, `false`, `"localhost"` → type inferred, used as default
|
|
229
|
-
* - **Constructors**: `String`, `Number`, `Boolean` → required, no default
|
|
230
|
-
* - **Validators**: `url()`, `oneOf([...])`, `str()`, `num()`, `bool()` → explicit validation
|
|
231
|
-
* - **Custom**: `schema(raw => z.coerce.number().parse(raw))` — bridge to any schema library
|
|
232
|
-
*
|
|
233
|
-
* @example
|
|
234
|
-
* ```ts
|
|
235
|
-
* import { validateEnv, url, oneOf } from "@pyreon/zero/env"
|
|
236
|
-
*
|
|
237
|
-
* const env = validateEnv({
|
|
238
|
-
* PORT: 3000, // optional, default 3000
|
|
239
|
-
* DATABASE_URL: url(), // required, validated URL
|
|
240
|
-
* NODE_ENV: oneOf(["dev", "prod", "test"]), // required, must be one of
|
|
241
|
-
* API_KEY: String, // required string
|
|
242
|
-
* DEBUG: false, // optional, default false
|
|
243
|
-
* })
|
|
244
|
-
* ```
|
|
245
|
-
*/
|
|
246
|
-
export function validateEnv<T extends Record<string, SchemaEntry>>(
|
|
247
|
-
envSchema: T,
|
|
248
|
-
source?: Record<string, string | undefined>,
|
|
249
|
-
): InferEnvSchema<T> {
|
|
250
|
-
const env = source ?? (typeof process !== 'undefined' ? process.env : {})
|
|
251
|
-
const result: Record<string, unknown> = {}
|
|
252
|
-
const errors: string[] = []
|
|
253
|
-
|
|
254
|
-
for (const [key, entry] of Object.entries(envSchema)) {
|
|
255
|
-
const validator = toValidator(entry)
|
|
256
|
-
try {
|
|
257
|
-
result[key] = validator.parse(env[key], key)
|
|
258
|
-
} catch (e) {
|
|
259
|
-
errors.push((e as Error).message)
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (errors.length > 0) {
|
|
264
|
-
const header = `\n[Pyreon] Environment validation failed (${errors.length} error${errors.length > 1 ? 's' : ''}):\n`
|
|
265
|
-
const body = errors.map((e) => ` ✗ ${e.replace('[Pyreon] ', '')}`).join('\n')
|
|
266
|
-
throw new Error(header + body + '\n')
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return result as InferEnvSchema<T>
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// ─── Public env (client-safe) ────────────────────────────────────────────────
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Extract public environment variables (prefixed with `ZERO_PUBLIC_`).
|
|
276
|
-
*
|
|
277
|
-
* @example
|
|
278
|
-
* ```ts
|
|
279
|
-
* const pub = publicEnv()
|
|
280
|
-
* // → { API_URL: "https://...", APP_NAME: "MyApp" }
|
|
281
|
-
*
|
|
282
|
-
* const pub = publicEnv({ API_URL: url(), APP_NAME: "Default" })
|
|
283
|
-
* // → validated against ZERO_PUBLIC_API_URL, ZERO_PUBLIC_APP_NAME
|
|
284
|
-
* ```
|
|
285
|
-
*/
|
|
286
|
-
export function publicEnv(): Record<string, string>
|
|
287
|
-
export function publicEnv<T extends Record<string, SchemaEntry>>(envSchema: T): InferEnvSchema<T>
|
|
288
|
-
export function publicEnv(envSchema?: Record<string, SchemaEntry>): Record<string, unknown> {
|
|
289
|
-
const prefix = 'ZERO_PUBLIC_'
|
|
290
|
-
const env = typeof process !== 'undefined' ? process.env : {}
|
|
291
|
-
|
|
292
|
-
if (!envSchema) {
|
|
293
|
-
const result: Record<string, string> = {}
|
|
294
|
-
for (const [key, value] of Object.entries(env)) {
|
|
295
|
-
if (key.startsWith(prefix) && value !== undefined) {
|
|
296
|
-
result[key.slice(prefix.length)] = value
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return result
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const prefixedSource: Record<string, string | undefined> = {}
|
|
303
|
-
for (const key of Object.keys(envSchema)) {
|
|
304
|
-
prefixedSource[key] = env[`${prefix}${key}`]
|
|
305
|
-
}
|
|
306
|
-
return validateEnv(envSchema, prefixedSource)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// ─── Custom validator escape hatch ──────────────────────────────────────────
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Create an env validator from a custom parse function.
|
|
313
|
-
* Use this to integrate any schema library (Zod, Valibot, ArkType, etc.).
|
|
314
|
-
*
|
|
315
|
-
* @example
|
|
316
|
-
* ```ts
|
|
317
|
-
* import { z } from "zod"
|
|
318
|
-
* import { validateEnv, schema } from "@pyreon/zero/env"
|
|
319
|
-
*
|
|
320
|
-
* const env = validateEnv({
|
|
321
|
-
* PORT: schema(raw => z.coerce.number().parse(raw)),
|
|
322
|
-
* DATABASE_URL: schema(raw => z.string().url().parse(raw)),
|
|
323
|
-
* HOST: "localhost", // plain defaults still work alongside
|
|
324
|
-
* })
|
|
325
|
-
* ```
|
|
326
|
-
*/
|
|
327
|
-
export function schema<T>(parse: (raw: string) => T): EnvValidator<T> {
|
|
328
|
-
return {
|
|
329
|
-
__type: 'env-validator',
|
|
330
|
-
required: true,
|
|
331
|
-
defaultValue: undefined,
|
|
332
|
-
parse(raw: string | undefined, key: string) {
|
|
333
|
-
if (raw === undefined || raw === '') {
|
|
334
|
-
throw new Error(`[Pyreon] ${key}: is required but not set`)
|
|
335
|
-
}
|
|
336
|
-
try {
|
|
337
|
-
return parse(raw)
|
|
338
|
-
} catch (e) {
|
|
339
|
-
const msg = e instanceof Error ? e.message : String(e)
|
|
340
|
-
throw new Error(`[Pyreon] ${key}: ${msg}`)
|
|
341
|
-
}
|
|
342
|
-
},
|
|
343
|
-
}
|
|
344
|
-
}
|
package/src/error-overlay.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dev-only error overlay for SSR/loader errors.
|
|
3
|
-
* Renders a styled HTML page with the error stack trace.
|
|
4
|
-
*/
|
|
5
|
-
export function renderErrorOverlay(error: Error): string {
|
|
6
|
-
const title = escapeHtml(error.message || 'Unknown error')
|
|
7
|
-
const stack = escapeHtml(error.stack || '')
|
|
8
|
-
|
|
9
|
-
return `<!DOCTYPE html>
|
|
10
|
-
<html lang="en">
|
|
11
|
-
<head>
|
|
12
|
-
<meta charset="UTF-8">
|
|
13
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
14
|
-
<title>SSR Error — Pyreon Zero</title>
|
|
15
|
-
<style>
|
|
16
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
17
|
-
body {
|
|
18
|
-
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace;
|
|
19
|
-
background: #1a1a2e;
|
|
20
|
-
color: #e0e0e0;
|
|
21
|
-
min-height: 100vh;
|
|
22
|
-
padding: 2rem;
|
|
23
|
-
}
|
|
24
|
-
.overlay {
|
|
25
|
-
max-width: 900px;
|
|
26
|
-
margin: 0 auto;
|
|
27
|
-
}
|
|
28
|
-
.header {
|
|
29
|
-
display: flex;
|
|
30
|
-
align-items: center;
|
|
31
|
-
gap: 0.75rem;
|
|
32
|
-
margin-bottom: 1.5rem;
|
|
33
|
-
}
|
|
34
|
-
.badge {
|
|
35
|
-
background: #e74c3c;
|
|
36
|
-
color: white;
|
|
37
|
-
padding: 0.25rem 0.75rem;
|
|
38
|
-
border-radius: 4px;
|
|
39
|
-
font-size: 0.75rem;
|
|
40
|
-
font-weight: 600;
|
|
41
|
-
text-transform: uppercase;
|
|
42
|
-
letter-spacing: 0.05em;
|
|
43
|
-
}
|
|
44
|
-
.label {
|
|
45
|
-
color: #888;
|
|
46
|
-
font-size: 0.85rem;
|
|
47
|
-
}
|
|
48
|
-
.message {
|
|
49
|
-
font-size: 1.25rem;
|
|
50
|
-
color: #ff6b6b;
|
|
51
|
-
margin-bottom: 1.5rem;
|
|
52
|
-
line-height: 1.5;
|
|
53
|
-
word-break: break-word;
|
|
54
|
-
}
|
|
55
|
-
.stack {
|
|
56
|
-
background: #16213e;
|
|
57
|
-
border: 1px solid #2a2a4a;
|
|
58
|
-
border-radius: 8px;
|
|
59
|
-
padding: 1.25rem;
|
|
60
|
-
overflow-x: auto;
|
|
61
|
-
font-size: 0.8rem;
|
|
62
|
-
line-height: 1.7;
|
|
63
|
-
white-space: pre-wrap;
|
|
64
|
-
word-break: break-all;
|
|
65
|
-
}
|
|
66
|
-
.stack .at { color: #888; }
|
|
67
|
-
.stack .file { color: #4ecdc4; }
|
|
68
|
-
.hint {
|
|
69
|
-
margin-top: 1.5rem;
|
|
70
|
-
padding: 1rem;
|
|
71
|
-
background: #1e2a45;
|
|
72
|
-
border-radius: 6px;
|
|
73
|
-
border-left: 3px solid #3498db;
|
|
74
|
-
font-size: 0.8rem;
|
|
75
|
-
color: #aaa;
|
|
76
|
-
line-height: 1.5;
|
|
77
|
-
}
|
|
78
|
-
</style>
|
|
79
|
-
</head>
|
|
80
|
-
<body>
|
|
81
|
-
<div class="overlay">
|
|
82
|
-
<div class="header">
|
|
83
|
-
<span class="badge">SSR Error</span>
|
|
84
|
-
<span class="label">Pyreon Zero — Dev Mode</span>
|
|
85
|
-
</div>
|
|
86
|
-
<div class="message">${title}</div>
|
|
87
|
-
<pre class="stack">${formatStack(stack)}</pre>
|
|
88
|
-
<div class="hint">
|
|
89
|
-
This error occurred during server-side rendering. Check the terminal for
|
|
90
|
-
the full stack trace. This overlay is only shown in development.
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
</body>
|
|
94
|
-
</html>`
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function escapeHtml(str: string): string {
|
|
98
|
-
return str
|
|
99
|
-
.replace(/&/g, '&')
|
|
100
|
-
.replace(/</g, '<')
|
|
101
|
-
.replace(/>/g, '>')
|
|
102
|
-
.replace(/"/g, '"')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function formatStack(stack: string): string {
|
|
106
|
-
return stack
|
|
107
|
-
.split('\n')
|
|
108
|
-
.map((line) => {
|
|
109
|
-
if (line.includes('at ')) {
|
|
110
|
-
const fileMatch = line.match(/\(([^)]+)\)/)
|
|
111
|
-
if (fileMatch) {
|
|
112
|
-
return line.replace(fileMatch[0], `(<span class="file">${fileMatch[1]}</span>)`)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return line
|
|
116
|
-
})
|
|
117
|
-
.join('\n')
|
|
118
|
-
}
|