weifuwu 0.2.2 → 0.2.4

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 (45) hide show
  1. package/README.md +90 -5
  2. package/dist/compress.d.ts +6 -0
  3. package/dist/cookie.d.ts +12 -0
  4. package/dist/index.d.ts +21 -0
  5. package/dist/index.js +1486 -0
  6. package/dist/middleware.d.ts +21 -0
  7. package/dist/rate-limit.d.ts +8 -0
  8. package/dist/router.d.ts +55 -0
  9. package/dist/serve.d.ts +19 -0
  10. package/dist/static.d.ts +7 -0
  11. package/dist/tsx.d.ts +17 -0
  12. package/dist/types.d.ts +9 -0
  13. package/dist/upload.d.ts +14 -0
  14. package/dist/validate.d.ts +9 -0
  15. package/package.json +14 -2
  16. package/AGENTS.md +0 -105
  17. package/compress.ts +0 -69
  18. package/cookie.ts +0 -58
  19. package/index.ts +0 -21
  20. package/middleware.ts +0 -178
  21. package/rate-limit.ts +0 -68
  22. package/router.ts +0 -701
  23. package/serve.ts +0 -126
  24. package/static.ts +0 -113
  25. package/test/compress.test.ts +0 -106
  26. package/test/cookie.test.ts +0 -79
  27. package/test/fixtures/pages/about/page.tsx +0 -3
  28. package/test/fixtures/pages/blog/[slug]/load.ts +0 -3
  29. package/test/fixtures/pages/blog/[slug]/page.tsx +0 -3
  30. package/test/fixtures/pages/blog/[slug]/route.ts +0 -7
  31. package/test/fixtures/pages/blog/layout.tsx +0 -3
  32. package/test/fixtures/pages/layout.tsx +0 -12
  33. package/test/fixtures/pages/page.tsx +0 -3
  34. package/test/middleware.test.ts +0 -407
  35. package/test/rate-limit.test.ts +0 -94
  36. package/test/static.test.ts +0 -93
  37. package/test/tsx.test.ts +0 -285
  38. package/test/unode.test.ts +0 -401
  39. package/test/upload.test.ts +0 -130
  40. package/test/validate.test.ts +0 -133
  41. package/tsconfig.json +0 -13
  42. package/tsx.ts +0 -374
  43. package/types.ts +0 -23
  44. package/upload.ts +0 -101
  45. package/validate.ts +0 -88
package/upload.ts DELETED
@@ -1,101 +0,0 @@
1
- import { writeFile, mkdir } from 'node:fs/promises'
2
- import { randomUUID } from 'node:crypto'
3
- import { join } from 'node:path'
4
- import type { Middleware } from './types.ts'
5
-
6
- export interface UploadedFile {
7
- name: string
8
- type: string
9
- size: number
10
- path?: string
11
- buffer?: Buffer
12
- }
13
-
14
- export interface UploadOptions {
15
- dir?: string
16
- maxFileSize?: number
17
- allowedTypes?: string[]
18
- }
19
-
20
- export function upload(options?: UploadOptions): Middleware {
21
- const saveDir = options?.dir
22
-
23
- return async (req, ctx, next) => {
24
- const ct = req.headers.get('content-type') ?? ''
25
- if (!ct.includes('multipart/form-data')) {
26
- return next(req, ctx)
27
- }
28
-
29
- const match = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i)
30
- if (!match) {
31
- return Response.json({ error: 'Missing boundary' }, { status: 400 })
32
- }
33
-
34
- const boundary = match[1] ?? match[2]!
35
- const body = await req.text()
36
- const rawParts = body.split(`--${boundary}`).filter((p) => p && !p.startsWith('--') && !p.startsWith('\r\n--'))
37
-
38
- const files: Record<string, UploadedFile | UploadedFile[]> = {}
39
- const fields: Record<string, string> = {}
40
-
41
- for (const raw of rawParts) {
42
- const trimmed = raw.replace(/^\r?\n/, '')
43
- const lines = trimmed.split(/\r?\n/)
44
- let i = 0
45
- const headers: Record<string, string> = {}
46
- while (i < lines.length && lines[i]!.length > 0) {
47
- const sep = lines[i]!.indexOf(': ')
48
- if (sep !== -1) headers[lines[i]!.slice(0, sep).toLowerCase()] = lines[i]!.slice(sep + 2)
49
- i++
50
- }
51
- i++
52
- const bodyValue = lines.slice(i).join('\r\n')
53
-
54
- const disposition = headers['content-disposition'] ?? ''
55
- const nameMatch = disposition.match(/name="([^"]*)"/)
56
- if (!nameMatch) continue
57
- const name = nameMatch[1]!
58
- const filenameMatch = disposition.match(/filename="([^"]*)"/)
59
- const filename = filenameMatch?.[1]
60
-
61
- if (filename) {
62
- const buf = Buffer.from(bodyValue.replace(/\r?\n$/, ''), 'binary')
63
- if (options?.allowedTypes) {
64
- const mime = headers['content-type'] ?? 'application/octet-stream'
65
- if (!options.allowedTypes.includes(mime)) {
66
- return Response.json({ error: `File type not allowed: ${mime}` }, { status: 415 })
67
- }
68
- }
69
- if (options?.maxFileSize && buf.byteLength > options.maxFileSize) {
70
- return Response.json({ error: `File too large: ${filename}` }, { status: 413 })
71
- }
72
-
73
- const uf: UploadedFile = {
74
- name: filename,
75
- type: headers['content-type'] ?? 'application/octet-stream',
76
- size: buf.byteLength,
77
- buffer: saveDir ? undefined : buf,
78
- }
79
-
80
- if (saveDir) {
81
- const filePath = join(saveDir, `${randomUUID()}-${filename}`)
82
- await mkdir(saveDir, { recursive: true })
83
- await writeFile(filePath, buf)
84
- uf.path = filePath
85
- }
86
-
87
- if (files[name]) {
88
- const existing = files[name]
89
- files[name] = Array.isArray(existing) ? [...existing, uf] : [existing, uf]
90
- } else {
91
- files[name] = uf
92
- }
93
- } else {
94
- fields[name] = bodyValue.replace(/\r?\n$/, '')
95
- }
96
- }
97
-
98
- ctx.parsed = { ...ctx.parsed, files, fields }
99
- return next(req, ctx)
100
- }
101
- }
package/validate.ts DELETED
@@ -1,88 +0,0 @@
1
- import type { ZodSchema } from 'zod'
2
- import type { Middleware } from './types.ts'
3
-
4
- export interface ValidationSchemas {
5
- body?: ZodSchema
6
- query?: ZodSchema
7
- params?: ZodSchema
8
- headers?: ZodSchema
9
- }
10
-
11
- export function validate(schemas: ValidationSchemas): Middleware {
12
- return async (req, ctx, next) => {
13
- const parsed: Record<string, unknown> = {}
14
- const issues: { path: string[]; message: string }[] = []
15
-
16
- if (schemas.params) {
17
- const result = schemas.params.safeParse(ctx.params)
18
- if (result.success) {
19
- parsed.params = result.data
20
- } else {
21
- issues.push(...result.error.issues.map((i) => ({
22
- path: ['params', ...i.path.map(String)],
23
- message: i.message,
24
- })))
25
- }
26
- }
27
-
28
- if (schemas.query) {
29
- const result = schemas.query.safeParse(ctx.query)
30
- if (result.success) {
31
- parsed.query = result.data
32
- } else {
33
- issues.push(...result.error.issues.map((i) => ({
34
- path: ['query', ...i.path.map(String)],
35
- message: i.message,
36
- })))
37
- }
38
- }
39
-
40
- if (schemas.headers) {
41
- const rawHeaders: Record<string, string> = {}
42
- req.headers.forEach((v, k) => { rawHeaders[k] = v })
43
- const result = schemas.headers.safeParse(rawHeaders)
44
- if (result.success) {
45
- parsed.headers = result.data
46
- } else {
47
- issues.push(...result.error.issues.map((i) => ({
48
- path: ['headers', ...i.path.map(String)],
49
- message: i.message,
50
- })))
51
- }
52
- }
53
-
54
- if (schemas.body) {
55
- if (req.body === null) {
56
- issues.push({ path: ['body'], message: 'Request body is required' })
57
- } else {
58
- const bodyText = await req.text()
59
- if (!bodyText && req.method !== 'GET' && req.method !== 'HEAD') {
60
- issues.push({ path: ['body'], message: 'Request body is required' })
61
- } else {
62
- let bodyValue: unknown
63
- try {
64
- bodyValue = JSON.parse(bodyText)
65
- } catch {
66
- bodyValue = bodyText
67
- }
68
- const result = schemas.body.safeParse(bodyValue)
69
- if (result.success) {
70
- parsed.body = result.data
71
- } else {
72
- issues.push(...result.error.issues.map((i) => ({
73
- path: ['body', ...i.path.map(String)],
74
- message: i.message,
75
- })))
76
- }
77
- }
78
- }
79
- }
80
-
81
- if (issues.length > 0) {
82
- return Response.json({ error: 'Validation failed', issues }, { status: 400 })
83
- }
84
-
85
- ctx.parsed = parsed
86
- return next(req, ctx)
87
- }
88
- }