weifuwu 0.25.2 → 0.27.1

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 (208) hide show
  1. package/README.md +291 -2489
  2. package/ai/provider.ts +129 -0
  3. package/ai/stream.ts +63 -0
  4. package/{dist/cli.d.ts → cli.js} +1 -1
  5. package/cli.ts +55 -257
  6. package/core/cookie.ts +114 -0
  7. package/core/env.ts +142 -0
  8. package/core/logger.ts +72 -0
  9. package/core/router.ts +795 -0
  10. package/core/serve.ts +294 -0
  11. package/core/sse.ts +85 -0
  12. package/core/trace.ts +146 -0
  13. package/graphql.ts +267 -0
  14. package/hub.ts +133 -0
  15. package/index.ts +71 -0
  16. package/mailer.ts +81 -0
  17. package/middleware/compress.ts +103 -0
  18. package/middleware/cors.ts +81 -0
  19. package/middleware/csrf.ts +112 -0
  20. package/middleware/flash.ts +144 -0
  21. package/middleware/health.ts +44 -0
  22. package/middleware/helmet.ts +98 -0
  23. package/middleware/i18n.ts +175 -0
  24. package/middleware/rate-limit.ts +167 -0
  25. package/middleware/request-id.ts +60 -0
  26. package/middleware/static.ts +149 -0
  27. package/middleware/theme.ts +84 -0
  28. package/middleware/upload.ts +168 -0
  29. package/middleware/validate.ts +186 -0
  30. package/package.json +15 -36
  31. package/postgres/client.ts +132 -0
  32. package/postgres/index.ts +4 -0
  33. package/postgres/module.ts +37 -0
  34. package/postgres/schema/columns.ts +186 -0
  35. package/postgres/schema/index.ts +36 -0
  36. package/postgres/schema/sql.ts +39 -0
  37. package/postgres/schema/table.ts +548 -0
  38. package/postgres/schema/where.ts +99 -0
  39. package/postgres/types.ts +48 -0
  40. package/queue/cron.ts +90 -0
  41. package/queue/index.ts +654 -0
  42. package/queue/types.ts +60 -0
  43. package/redis/client.ts +24 -0
  44. package/{dist/redis/index.d.ts → redis/index.ts} +2 -2
  45. package/redis/types.ts +28 -0
  46. package/types.ts +78 -0
  47. package/cli/template/app.ts +0 -22
  48. package/cli/template/index.ts +0 -10
  49. package/cli/template/locales/en.json +0 -13
  50. package/cli/template/locales/zh-CN.json +0 -13
  51. package/cli/template/locales/zh-TW.json +0 -13
  52. package/cli/template/locales/zh.json +0 -13
  53. package/cli/template/ui/app/globals.css +0 -2
  54. package/cli/template/ui/app/layout.tsx +0 -15
  55. package/cli/template/ui/app/page.tsx +0 -124
  56. package/cli/template/ui/components/Greeting.tsx +0 -3
  57. package/dist/agent/client.d.ts +0 -2
  58. package/dist/agent/index.d.ts +0 -2
  59. package/dist/agent/rest.d.ts +0 -14
  60. package/dist/agent/run.d.ts +0 -19
  61. package/dist/agent/types.d.ts +0 -55
  62. package/dist/ai/provider.d.ts +0 -45
  63. package/dist/ai/utils.d.ts +0 -5
  64. package/dist/ai/workflow.d.ts +0 -17
  65. package/dist/ai-sdk.d.ts +0 -2
  66. package/dist/ai.d.ts +0 -13
  67. package/dist/analytics.d.ts +0 -45
  68. package/dist/auth.d.ts +0 -22
  69. package/dist/cache.d.ts +0 -74
  70. package/dist/cli.js +0 -302
  71. package/dist/client-locale.d.ts +0 -25
  72. package/dist/client-pref.d.ts +0 -3
  73. package/dist/client-router.d.ts +0 -300
  74. package/dist/client-state.d.ts +0 -22
  75. package/dist/client-theme.d.ts +0 -36
  76. package/dist/compile.d.ts +0 -15
  77. package/dist/compress.d.ts +0 -20
  78. package/dist/cookie.d.ts +0 -36
  79. package/dist/cors.d.ts +0 -25
  80. package/dist/cron-utils.d.ts +0 -73
  81. package/dist/csrf.d.ts +0 -47
  82. package/dist/deploy/config.d.ts +0 -2
  83. package/dist/deploy/gateway.d.ts +0 -2
  84. package/dist/deploy/index.d.ts +0 -4
  85. package/dist/deploy/manager.d.ts +0 -16
  86. package/dist/deploy/process.d.ts +0 -14
  87. package/dist/deploy/types.d.ts +0 -53
  88. package/dist/env.d.ts +0 -69
  89. package/dist/error-boundary.d.ts +0 -2
  90. package/dist/flash.d.ts +0 -90
  91. package/dist/fts.d.ts +0 -36
  92. package/dist/graphql.d.ts +0 -16
  93. package/dist/head.d.ts +0 -6
  94. package/dist/health.d.ts +0 -24
  95. package/dist/helmet.d.ts +0 -33
  96. package/dist/html-shell.d.ts +0 -1
  97. package/dist/hub.d.ts +0 -37
  98. package/dist/i18n.d.ts +0 -39
  99. package/dist/iii/client.d.ts +0 -2
  100. package/dist/iii/index.d.ts +0 -4
  101. package/dist/iii/register-worker.d.ts +0 -9
  102. package/dist/iii/rest.d.ts +0 -3
  103. package/dist/iii/stream.d.ts +0 -82
  104. package/dist/iii/types.d.ts +0 -121
  105. package/dist/iii/worker.d.ts +0 -2
  106. package/dist/iii/ws.d.ts +0 -22
  107. package/dist/index.d.ts +0 -101
  108. package/dist/index.js +0 -12752
  109. package/dist/kb/index.d.ts +0 -3
  110. package/dist/kb/types.d.ts +0 -72
  111. package/dist/layout.d.ts +0 -2
  112. package/dist/live.d.ts +0 -7
  113. package/dist/logdb/client.d.ts +0 -2
  114. package/dist/logdb/index.d.ts +0 -2
  115. package/dist/logdb/rest.d.ts +0 -5
  116. package/dist/logdb/types.d.ts +0 -27
  117. package/dist/logger.d.ts +0 -16
  118. package/dist/mailer.d.ts +0 -51
  119. package/dist/mcp.d.ts +0 -34
  120. package/dist/messager/agent.d.ts +0 -11
  121. package/dist/messager/client.d.ts +0 -2
  122. package/dist/messager/index.d.ts +0 -2
  123. package/dist/messager/rest.d.ts +0 -15
  124. package/dist/messager/types.d.ts +0 -57
  125. package/dist/messager/ws.d.ts +0 -14
  126. package/dist/module-server.d.ts +0 -9
  127. package/dist/not-found.d.ts +0 -2
  128. package/dist/notifier/client.d.ts +0 -2
  129. package/dist/notifier/index.d.ts +0 -2
  130. package/dist/notifier/types.d.ts +0 -105
  131. package/dist/opencode/client.d.ts +0 -2
  132. package/dist/opencode/index.d.ts +0 -2
  133. package/dist/opencode/permissions.d.ts +0 -5
  134. package/dist/opencode/prompt.d.ts +0 -8
  135. package/dist/opencode/rest.d.ts +0 -16
  136. package/dist/opencode/run.d.ts +0 -13
  137. package/dist/opencode/session.d.ts +0 -26
  138. package/dist/opencode/skills.d.ts +0 -4
  139. package/dist/opencode/tools/bash.d.ts +0 -6
  140. package/dist/opencode/tools/edit.d.ts +0 -19
  141. package/dist/opencode/tools/glob.d.ts +0 -9
  142. package/dist/opencode/tools/grep.d.ts +0 -17
  143. package/dist/opencode/tools/index.d.ts +0 -12
  144. package/dist/opencode/tools/question.d.ts +0 -5
  145. package/dist/opencode/tools/read.d.ts +0 -16
  146. package/dist/opencode/tools/skill.d.ts +0 -18
  147. package/dist/opencode/tools/web.d.ts +0 -18
  148. package/dist/opencode/tools/write.d.ts +0 -13
  149. package/dist/opencode/types.d.ts +0 -90
  150. package/dist/opencode/ws.d.ts +0 -21
  151. package/dist/permissions.d.ts +0 -51
  152. package/dist/postgres/client.d.ts +0 -4
  153. package/dist/postgres/index.d.ts +0 -4
  154. package/dist/postgres/module.d.ts +0 -17
  155. package/dist/postgres/schema/columns.d.ts +0 -99
  156. package/dist/postgres/schema/index.d.ts +0 -6
  157. package/dist/postgres/schema/sql.d.ts +0 -22
  158. package/dist/postgres/schema/table.d.ts +0 -141
  159. package/dist/postgres/schema/where.d.ts +0 -29
  160. package/dist/postgres/types.d.ts +0 -50
  161. package/dist/queue/index.d.ts +0 -2
  162. package/dist/queue/types.d.ts +0 -62
  163. package/dist/rate-limit.d.ts +0 -45
  164. package/dist/react.d.ts +0 -14
  165. package/dist/react.js +0 -751
  166. package/dist/redis/client.d.ts +0 -2
  167. package/dist/redis/types.d.ts +0 -18
  168. package/dist/request-id.d.ts +0 -40
  169. package/dist/router.d.ts +0 -73
  170. package/dist/s3.d.ts +0 -68
  171. package/dist/seo.d.ts +0 -104
  172. package/dist/serve.d.ts +0 -38
  173. package/dist/server-registry.d.ts +0 -10
  174. package/dist/session.d.ts +0 -117
  175. package/dist/sse.d.ts +0 -47
  176. package/dist/ssr-entries.d.ts +0 -4
  177. package/dist/ssr.d.ts +0 -11
  178. package/dist/static.d.ts +0 -23
  179. package/dist/stream.d.ts +0 -24
  180. package/dist/tailwind.d.ts +0 -15
  181. package/dist/tenant/client.d.ts +0 -2
  182. package/dist/tenant/graphql.d.ts +0 -3
  183. package/dist/tenant/index.d.ts +0 -2
  184. package/dist/tenant/rest.d.ts +0 -3
  185. package/dist/tenant/schema.d.ts +0 -5
  186. package/dist/tenant/types.d.ts +0 -48
  187. package/dist/tenant/utils.d.ts +0 -9
  188. package/dist/test-utils.d.ts +0 -194
  189. package/dist/theme.d.ts +0 -31
  190. package/dist/trace.d.ts +0 -95
  191. package/dist/tsx-context.d.ts +0 -32
  192. package/dist/types.d.ts +0 -47
  193. package/dist/upload.d.ts +0 -55
  194. package/dist/use-action.d.ts +0 -42
  195. package/dist/use-agent-stream.d.ts +0 -49
  196. package/dist/use-flash-message.d.ts +0 -17
  197. package/dist/use-websocket.d.ts +0 -42
  198. package/dist/user/client.d.ts +0 -30
  199. package/dist/user/index.d.ts +0 -2
  200. package/dist/user/oauth-login.d.ts +0 -21
  201. package/dist/user/oauth2.d.ts +0 -31
  202. package/dist/user/types.d.ts +0 -178
  203. package/dist/validate.d.ts +0 -32
  204. package/dist/vendor.d.ts +0 -7
  205. package/dist/webhook.d.ts +0 -79
  206. package/opencode/ui/app/globals.css +0 -1
  207. package/opencode/ui/app/layout.tsx +0 -13
  208. package/opencode/ui/app/page.tsx +0 -523
package/core/cookie.ts ADDED
@@ -0,0 +1,114 @@
1
+ /** Options for setting a cookie. All fields map to standard Set-Cookie attributes. */
2
+ export interface CookieOptions {
3
+ domain?: string
4
+ path?: string
5
+ maxAge?: number
6
+ expires?: Date
7
+ httpOnly?: boolean
8
+ secure?: boolean
9
+ sameSite?: 'strict' | 'lax' | 'none'
10
+ }
11
+
12
+ /** Parse cookies from a Request's `Cookie` header.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const cookies = getCookies(req)
17
+ * console.log(cookies.session_id) // value or undefined
18
+ * ``` */
19
+ export function getCookies(req: Request): Record<string, string> {
20
+ const header = req.headers.get('cookie')
21
+ if (!header) return {}
22
+
23
+ const cookies: Record<string, string> = {}
24
+ for (const pair of header.split(';')) {
25
+ const idx = pair.indexOf('=')
26
+ if (idx === -1) continue
27
+ let name = pair.slice(0, idx).trim()
28
+ let value = pair.slice(idx + 1).trim()
29
+ if (!name) continue
30
+ // Decode cookie name (consistent with value)
31
+ try {
32
+ name = decodeURIComponent(name)
33
+ } catch {}
34
+ // Strip surrounding quotes from value (RFC 6265)
35
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
36
+ value = value.slice(1, -1)
37
+ }
38
+ try {
39
+ cookies[name] = decodeURIComponent(value)
40
+ } catch {
41
+ cookies[name] = value
42
+ }
43
+ }
44
+ return cookies
45
+ }
46
+
47
+ function serializeCookie(name: string, value: string, options?: CookieOptions): string {
48
+ // Reject control characters and special chars per RFC 6265
49
+ // eslint-disable-next-line no-control-regex
50
+ if (/[\x00-\x1F\x7F-\x9F;,]/.test(name) || /[\x00-\x1F\x7F-\x9F;,]/.test(value)) {
51
+ throw new Error(`Invalid cookie name or value: contains control characters or special chars`)
52
+ }
53
+ const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`]
54
+ if (options?.maxAge != null) parts.push(`Max-Age=${options.maxAge}`)
55
+ if (options?.expires) parts.push(`Expires=${options.expires.toUTCString()}`)
56
+ if (options?.domain) parts.push(`Domain=${options.domain}`)
57
+ if (options?.path) parts.push(`Path=${options.path}`)
58
+ if (options?.httpOnly) parts.push('HttpOnly')
59
+ if (options?.secure) parts.push('Secure')
60
+ if (options?.sameSite) parts.push(`SameSite=${options.sameSite}`)
61
+ return parts.join('; ')
62
+ }
63
+
64
+ /** Set a cookie on a Response.
65
+ *
66
+ * Appends a `Set-Cookie` header to the existing response headers.
67
+ * Returns a new Response with the added header.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * const res = new Response('ok')
72
+ * return setCookie(res, 'session', token, { httpOnly: true, path: '/' })
73
+ * ``` */
74
+ export function setCookie(
75
+ res: Response,
76
+ name: string,
77
+ value: string,
78
+ options?: CookieOptions,
79
+ ): Response {
80
+ const headers = new Headers(res.headers)
81
+ headers.append('Set-Cookie', serializeCookie(name, value, options))
82
+ return new Response(res.body, {
83
+ status: res.status,
84
+ statusText: res.statusText,
85
+ headers,
86
+ })
87
+ }
88
+
89
+ /** Delete a cookie by setting `Max-Age=0`.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * return deleteCookie(res, 'session')
94
+ * ``` */
95
+ export function deleteCookie(
96
+ res: Response,
97
+ name: string,
98
+ options?: Omit<CookieOptions, 'maxAge'>,
99
+ ): Response {
100
+ const headers = new Headers(res.headers)
101
+ headers.append(
102
+ 'Set-Cookie',
103
+ serializeCookie(name, '', {
104
+ ...options,
105
+ maxAge: 0,
106
+ expires: new Date(0),
107
+ }),
108
+ )
109
+ return new Response(res.body, {
110
+ status: res.status,
111
+ statusText: res.statusText,
112
+ headers,
113
+ })
114
+ }
package/core/env.ts ADDED
@@ -0,0 +1,142 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import type { Context, Middleware } from '../types.ts'
4
+
5
+ /** Build-time injection from esbuild --define. `true` in dist/index.js, undefined in TS source. */
6
+ declare let __WFW_BUNDLED__: boolean | undefined
7
+
8
+ const PUBLIC_PREFIX = 'WEIFUWU_PUBLIC_'
9
+
10
+ /**
11
+ * Get all public environment variables (those prefixed with `WEIFUWU_PUBLIC_`),
12
+ * with the prefix stripped.
13
+ *
14
+ * ```ts
15
+ * const pub = getPublicEnv()
16
+ * // WEIFUWU_PUBLIC_API_URL=http://api.example.com → { API_URL: 'http://api.example.com' }
17
+ * ```
18
+ */
19
+ export function getPublicEnv(): Record<string, string> {
20
+ const result: Record<string, string> = {}
21
+ for (const [key, value] of Object.entries(process.env)) {
22
+ if (key.startsWith(PUBLIC_PREFIX) && value !== undefined) {
23
+ result[key.slice(PUBLIC_PREFIX.length)] = value
24
+ }
25
+ }
26
+ return result
27
+ }
28
+
29
+ // Augment Context with env property
30
+ declare module '../types.ts' {
31
+ interface Context {
32
+ env?: Record<string, string>
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Whether this code is running from the compiled `dist/index.js` bundle.
38
+ * `false` when running TypeScript source directly (dev workflow in weifuwu repo).
39
+ *
40
+ * Used by modules that need to resolve package-internal files differently
41
+ * depending on whether they are compiled (published npm package) or raw TS.
42
+ */
43
+ export function isBundled(): boolean {
44
+ return typeof __WFW_BUNDLED__ !== 'undefined' ? __WFW_BUNDLED__ : false
45
+ }
46
+
47
+ /**
48
+ * Whether `NODE_ENV` is explicitly set to `'development'`.
49
+ *
50
+ * Used for dev-only features: HMR, livereload, React `createRoot` (not hydrate).
51
+ * **Not** the opposite of {@link isProd} — when `NODE_ENV` is unset, both return `false`.
52
+ */
53
+ export function isDev(): boolean {
54
+ const env = process.env.NODE_ENV
55
+ return env !== 'production' && env !== 'test'
56
+ }
57
+
58
+ /**
59
+ * Whether `NODE_ENV` is explicitly set to `'production'`.
60
+ *
61
+ * Used for production-only behavior: plain-text 404, suppressed warnings, minified output.
62
+ */
63
+ export function isProd(): boolean {
64
+ return process.env.NODE_ENV === 'production'
65
+ }
66
+
67
+ /**
68
+ * Load environment variables from a `.env` file into `process.env`.
69
+ *
70
+ * Does **not** override existing `process.env` values.
71
+ * Supports quoted values and inline comments.
72
+ *
73
+ * @param path - Path to `.env` file (default: `'.env'` relative to cwd).
74
+ *
75
+ * ```ts
76
+ * import { loadEnv } from 'weifuwu'
77
+ * loadEnv()
78
+ * console.log(process.env.PORT)
79
+ * ```
80
+ */
81
+ export function loadEnv(path?: string): void {
82
+ const filePath = resolve(process.cwd(), path ?? '.env')
83
+
84
+ let content: string
85
+ try {
86
+ content = readFileSync(filePath, 'utf-8')
87
+ } catch {
88
+ return
89
+ }
90
+
91
+ for (const line of content.split('\n')) {
92
+ const trimmed = line.trim()
93
+ if (!trimmed || trimmed.startsWith('#')) continue
94
+
95
+ const eqIdx = trimmed.indexOf('=')
96
+ if (eqIdx === -1) continue
97
+
98
+ const key = trimmed.slice(0, eqIdx).trim()
99
+ if (!key) continue
100
+
101
+ if (process.env[key] !== undefined) continue
102
+
103
+ let value = trimmed.slice(eqIdx + 1).trim()
104
+
105
+ if (
106
+ (value.startsWith('"') && value.endsWith('"')) ||
107
+ (value.startsWith("'") && value.endsWith("'"))
108
+ ) {
109
+ value = value.slice(1, -1)
110
+ } else {
111
+ // Strip inline comments: space before #, or # at start of value
112
+ const commentIdx = value.search(/\s#/)
113
+ if (commentIdx !== -1) {
114
+ value = value.slice(0, commentIdx).trimEnd()
115
+ }
116
+ }
117
+
118
+ process.env[key] = value
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Public env middleware.
124
+ *
125
+ * Injects `ctx.env` with all environment variables prefixed with `WEIFUWU_PUBLIC_`,
126
+ * with the prefix stripped. Safe to expose to the client.
127
+ *
128
+ * ```ts
129
+ * import { env } from 'weifuwu'
130
+ * app.use(env())
131
+ *
132
+ * // .env: WEIFUWU_PUBLIC_API_URL=https://api.example.com
133
+ * // ctx: ctx.env.API_URL === 'https://api.example.com'
134
+ * ```
135
+ */
136
+ export function env(): Middleware<Context, Context & { env: Record<string, string> }> {
137
+ const entries = getPublicEnv()
138
+ return async (req, ctx, next) => {
139
+ ;(ctx as Context & { env: Record<string, string> }).env = entries
140
+ return next(req, ctx as Context & { env: Record<string, string> })
141
+ }
142
+ }
package/core/logger.ts ADDED
@@ -0,0 +1,72 @@
1
+ /* eslint-disable no-console */
2
+ import type { Middleware, Context } from '../types.ts'
3
+ import { currentTraceId } from './trace.ts'
4
+
5
+ export interface LoggerOptions {
6
+ /** 'short' = method + path + status + ms, 'combined' = short + query string, 'json' = structured stderr JSON */
7
+ format?: 'short' | 'combined' | 'json'
8
+ }
9
+
10
+ export interface LogEvent {
11
+ level: 'info' | 'warn' | 'error'
12
+ message: string
13
+ method?: string
14
+ path?: string
15
+ status?: number
16
+ elapsed_ms?: number
17
+ traceId?: string
18
+ timestamp?: string
19
+ }
20
+
21
+ function emit(event: LogEvent): void {
22
+ event.traceId = event.traceId ?? currentTraceId()
23
+ event.timestamp = new Date().toISOString()
24
+ process.stderr.write(JSON.stringify(event) + '\n')
25
+ }
26
+
27
+ export function logger(options?: LoggerOptions): Middleware<Context, Context> {
28
+ const format = options?.format ?? 'short'
29
+
30
+ return async (req, ctx, next) => {
31
+ const start = Date.now()
32
+ const url = new URL(req.url)
33
+
34
+ try {
35
+ const res = await next(req, ctx)
36
+ const ms = Date.now() - start
37
+ const pathAndQuery = format === 'combined' ? url.pathname + url.search : url.pathname
38
+
39
+ if (format === 'json') {
40
+ emit({
41
+ level: 'info',
42
+ message: 'request',
43
+ method: req.method,
44
+ path: pathAndQuery,
45
+ status: res.status,
46
+ elapsed_ms: ms,
47
+ })
48
+ } else {
49
+ console.log(`${req.method} ${pathAndQuery} ${res.status} ${ms}ms`)
50
+ }
51
+
52
+ return res
53
+ } catch (err) {
54
+ const ms = Date.now() - start
55
+ const pathAndQuery = format === 'combined' ? url.pathname + url.search : url.pathname
56
+
57
+ if (format === 'json') {
58
+ emit({
59
+ level: 'error',
60
+ message: err instanceof Error ? err.message : String(err),
61
+ method: req.method,
62
+ path: pathAndQuery,
63
+ status: 500,
64
+ elapsed_ms: ms,
65
+ })
66
+ } else {
67
+ console.log(`${req.method} ${pathAndQuery} 500 ${ms}ms`)
68
+ }
69
+ throw err
70
+ }
71
+ }
72
+ }