@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.
Files changed (54) hide show
  1. package/package.json +10 -39
  2. package/src/actions.ts +0 -196
  3. package/src/adapters/bun.ts +0 -114
  4. package/src/adapters/cloudflare.ts +0 -166
  5. package/src/adapters/index.ts +0 -61
  6. package/src/adapters/netlify.ts +0 -154
  7. package/src/adapters/node.ts +0 -163
  8. package/src/adapters/static.ts +0 -42
  9. package/src/adapters/validate.ts +0 -23
  10. package/src/adapters/vercel.ts +0 -182
  11. package/src/adapters/warn-missing-env.ts +0 -49
  12. package/src/ai.ts +0 -623
  13. package/src/api-routes.ts +0 -219
  14. package/src/app.ts +0 -92
  15. package/src/cache.ts +0 -136
  16. package/src/client.ts +0 -143
  17. package/src/compression.ts +0 -116
  18. package/src/config.ts +0 -35
  19. package/src/cors.ts +0 -94
  20. package/src/csp.ts +0 -226
  21. package/src/entry-server.ts +0 -224
  22. package/src/env.ts +0 -344
  23. package/src/error-overlay.ts +0 -118
  24. package/src/favicon.ts +0 -841
  25. package/src/font.ts +0 -511
  26. package/src/fs-router.ts +0 -1519
  27. package/src/i18n-routing.ts +0 -533
  28. package/src/icon.tsx +0 -182
  29. package/src/icons-plugin.ts +0 -296
  30. package/src/image-plugin.ts +0 -751
  31. package/src/image-types.ts +0 -60
  32. package/src/image.tsx +0 -340
  33. package/src/index.ts +0 -92
  34. package/src/isr.ts +0 -394
  35. package/src/link.tsx +0 -304
  36. package/src/logger.ts +0 -144
  37. package/src/manifest.ts +0 -787
  38. package/src/meta.tsx +0 -354
  39. package/src/middleware.ts +0 -65
  40. package/src/not-found.ts +0 -44
  41. package/src/og-image.ts +0 -378
  42. package/src/rate-limit.ts +0 -140
  43. package/src/script.tsx +0 -260
  44. package/src/seo.ts +0 -617
  45. package/src/server.ts +0 -89
  46. package/src/sharp.d.ts +0 -22
  47. package/src/ssg-plugin.ts +0 -1582
  48. package/src/testing.ts +0 -146
  49. package/src/theme.tsx +0 -257
  50. package/src/types.ts +0 -624
  51. package/src/utils/use-intersection-observer.ts +0 -36
  52. package/src/utils/with-headers.ts +0 -13
  53. package/src/vercel-revalidate-handler.ts +0 -204
  54. 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
- }
@@ -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, '&amp;')
100
- .replace(/</g, '&lt;')
101
- .replace(/>/g, '&gt;')
102
- .replace(/"/g, '&quot;')
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
- }