weifuwu 0.10.0 → 0.12.0

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/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  weifuwu doesn't invent its own request/response abstraction. `Request` and `Response` are the same objects you use in `fetch()` — what you learn in the browser applies directly on the server. `ctx` is the only framework object, and it only carries what the router parsed for you (`params`, `query`).
8
8
 
9
- Everything follows the same `(req, ctx) => Response` contract. The Router handles HTTP routing and WebSocket. All other features — auth, validation, database, GraphQL, AI, workflow — are standalone modules you import and mount with `app.use()`.
9
+ Everything follows the same `(req, ctx) => Response` contract. The Router handles HTTP routing and WebSocket. All other features — auth, validation, database, GraphQL, AI — are standalone modules you import and mount with `app.use()`.
10
10
 
11
11
  ## Features
12
12
 
@@ -17,9 +17,9 @@ Everything follows the same `(req, ctx) => Response` contract. The Router handle
17
17
  - **React SSR + Hydration** — `tsx({ dir })` — page.tsx / load.ts / layout.tsx / route.ts / not-found.tsx
18
18
  - **WebSocket** — `router.ws()` with upgrade middleware (auth before connect)
19
19
  - **GraphQL** — `graphql(handler)` sub-Router with GraphiQL IDE
20
- - **AI streaming** — `ai(handler)` sub-Router via Vercel AI SDK
21
- - **AI workflows** — `workflow(handler)` sub-Router intent-to-execution pipelines with `tool()` + SSE
22
- - **AI Agent** — `agent()` — server-side AI agents with chat/workflow/knowledge types, OpenAI-compatible, Ollama-ready
20
+ - **AI streaming** — `aiStream(handler)` sub-Router via Vercel AI SDK
21
+ - **DAG workflow tool** — `runWorkflow()` — multi-step execution engine as a single AI SDK `Tool`
22
+ - **AI Agent** — `agent()` — server-side AI agents with chat/tool-use/knowledge types, OpenAI-compatible, Ollama-ready
23
23
  - **Messaging** — `messager()` — real-time chat with channels, WebSocket, agent routing, webhook support
24
24
  - **Tenant BaaS** — `tenant()` — multi-tenant dynamic tables, auto REST + GraphQL, row-level isolation, pgvector/HNSW
25
25
  - **Redis** — `redis()` — ioredis client, `ctx.redis`, middleware
@@ -278,6 +278,50 @@ await users.createIndex('embedding', { // pgvector HNSW
278
278
  await users.drop({ cascade: true })
279
279
  ```
280
280
 
281
+ ### Type-safe CRUD with BoundTable
282
+
283
+ Two usage paths — use `pg.table()` when you have a `pg` handle, or `pgTable()` with explicit `sql`:
284
+
285
+ ```ts
286
+ // pg.table() — auto-binds sql, no need to pass it
287
+ const users = pg.table('_users', {
288
+ id: serial('id').primaryKey(),
289
+ name: text('name').notNull(),
290
+ email: text('email').unique(),
291
+ active: boolean('active').default(true),
292
+ createdAt: timestamptz('created_at').default(sql`NOW()`),
293
+ })
294
+
295
+ // INSERT ... RETURNING * — auto-strips serial id
296
+ const user = await users.insert({ name: 'Alice', email: 'alice@test.com' })
297
+ // → { id: 1, name: 'Alice', email: 'alice@test.com', active: true, ... }
298
+
299
+ // SELECT ... WHERE id = ? LIMIT 1
300
+ const found = await users.findById(1)
301
+
302
+ // SELECT ... WHERE ... [ORDER BY ...] [LIMIT ...] [OFFSET ...]
303
+ const admins = await users.find({ role: 'admin' })
304
+ const sorted = await users.find({ active: true }, { orderBy: { name: 'asc' } })
305
+ const page = await users.find(undefined, { limit: 10, offset: 0 })
306
+ const filtered = await users.find({ role: 'admin' }, { orderBy: { name: 'desc' }, limit: 5 })
307
+
308
+ // UPDATE ... SET ... WHERE ... RETURNING *
309
+ const updated = await users.update({ id: 1 }, { name: 'Bob' })
310
+ // With SQL expressions:
311
+ await users.update({ id: 1 }, { name: 'Bob', updated_at: sql`NOW()` })
312
+
313
+ // DELETE ... WHERE ... RETURNING 1
314
+ const ok = await users.delete({ id: 1 })
315
+ ```
316
+
317
+ When using `pgTable()` directly (without `pg`), pass `sql` as the first argument:
318
+
319
+ ```ts
320
+ const t = pgTable('_users', { ... })
321
+ await t.insert(ctx.sql, { name: 'Alice' })
322
+ await t.find(ctx.sql, { role: 'admin' }, { orderBy: { name: 'asc' } })
323
+ ```
324
+
281
325
  ### Complex queries use raw SQL
282
326
 
283
327
  ```ts
@@ -690,7 +734,7 @@ await fetch('http://localhost/api/sys/tenants/invite', {
690
734
 
691
735
  ## AI Agent
692
736
 
693
- Server-side AI agents with OpenAI-compatible API. Built-in chat, workflow (tool-calling), and knowledge (RAG) types. Works out of the box with Ollama or any OpenAI-compatible provider.
737
+ Server-side AI agents with OpenAI-compatible API. Built-in chat, tool-use (tool-calling), and knowledge (RAG) types. Works out of the box with Ollama or any OpenAI-compatible provider.
694
738
 
695
739
  ```ts
696
740
  import { agent } from 'weifuwu'
@@ -704,7 +748,7 @@ app.use('/api', agents.router())
704
748
  | Type | Description | Execution |
705
749
  |------|-------------|-----------|
706
750
  | `chat` | Pure conversation | `streamText()` / `generateText()` |
707
- | `workflow` | Tool-calling agent | `streamText({ tools })` |
751
+ | `tool-use` | Tool-calling agent | `streamText({ tools })` |
708
752
 
709
753
  ### Knowledge (RAG)
710
754
 
@@ -807,7 +851,7 @@ GraphQL endpoint with GraphiQL IDE. Mount as a sub-Router:
807
851
  import { serve, Router, graphql } from 'weifuwu'
808
852
 
809
853
  const app = new Router()
810
- app.use('/graphql', graphql(() => ({
854
+ const gql = graphql(() => ({
811
855
  schema: `
812
856
  type Query { hello: String }
813
857
  type Mutation { setMessage(msg: String!): String }
@@ -817,7 +861,8 @@ app.use('/graphql', graphql(() => ({
817
861
  Mutation: { setMessage: (_, { msg }) => msg },
818
862
  },
819
863
  graphiql: true,
820
- })))
864
+ }))
865
+ app.use('/graphql', gql.router())
821
866
 
822
867
  serve(app.handler(), { port: 3000 })
823
868
  ```
@@ -829,30 +874,31 @@ The handler receives `(req, ctx)` so you can customize the schema based on the r
829
874
  Server-sent event streaming via the Vercel AI SDK:
830
875
 
831
876
  ```ts
832
- import { serve, Router, ai } from 'weifuwu'
877
+ import { serve, Router, aiStream } from 'weifuwu'
833
878
  import { openai } from '@ai-sdk/openai'
834
879
 
835
880
  const app = new Router()
836
- app.use('/chat', ai(async (req, ctx) => {
881
+ const chat = await aiStream(async (req, ctx) => {
837
882
  const { messages } = await req.json()
838
883
  return { model: openai('gpt-4o'), messages }
839
- }))
884
+ })
885
+ app.use('/chat', chat.router())
840
886
 
841
887
  serve(app.handler(), { port: 3000 })
842
888
  ```
843
889
 
844
- ## Workflow
890
+ ## runWorkflow
845
891
 
846
- Define business capabilities as **Tools** (`tool()`), then chain them into **workflows** for AI-driven multi-step execution. Works with or without an LLM hand-write the workflow JSON or let AI generate it from a goal.
892
+ Multi-step DAG execution engine packaged as a single AI SDK `Tool`. Use it with `streamText()` or `generateText()` when the LLM needs conditional logic, loops, or multi-step tool orchestration.
847
893
 
848
894
  ```ts
849
- import { Router, tool, workflow } from 'weifuwu'
895
+ import { tool, streamText } from 'ai'
896
+ import { runWorkflow } from 'weifuwu'
850
897
  import { z } from 'zod'
851
898
 
852
- // 1. Define tools (business capabilities)
853
899
  const tools = {
854
900
  queryUser: tool({
855
- description: 'Query user info, returns email, name',
901
+ description: 'Query user info',
856
902
  inputSchema: z.object({ userId: z.string() }),
857
903
  execute: async ({ userId }) => ({ id: userId, email: 'user@test.com', name: 'Test' }),
858
904
  }),
@@ -861,139 +907,57 @@ const tools = {
861
907
  inputSchema: z.object({ to: z.string(), subject: z.string() }),
862
908
  execute: async ({ to, subject }) => ({ sent: true }),
863
909
  }),
910
+ runWF: runWorkflow({ tools: { queryUser, sendEmail } }),
864
911
  }
865
912
 
866
- // 2. Mount workflow sub-router
867
- const app = new Router()
868
- app.use('/agent', workflow(() => ({ tools })))
869
- // POST /agent { nodes: [...] } → 200 { workflow: {...}, result: ... }
870
-
871
- // With SSE streaming:
872
- app.use('/agent-stream', workflow(() => ({ tools, stream: true })))
873
- // POST /agent-stream { nodes: [...] }
874
- // → 200 { workflowId: "xxx", eventsUrl: "/xxx/events" }
875
- // GET /agent-stream/:workflowId/events
876
- // → SSE: workflow-start → node-start → node-end → complete
877
-
878
- // With LLM model (generates workflow from goal):
879
- app.use('/agent-llm', workflow(() => ({
913
+ // Use in any streamText call — the LLM can decide when to trigger a workflow
914
+ const result = await streamText({
915
+ model,
880
916
  tools,
881
- model: openai('gpt-4o'),
882
- })))
883
- // POST /agent-llm { goal: "给用户123发欢迎邮件" }
884
- // ← LLM generates → executes → returns result
885
- ```
886
-
887
- ### Tool
888
-
889
- ```ts
890
- import { tool } from 'weifuwu'
891
- import { z } from 'zod'
892
-
893
- const myTool = tool({
894
- description: '做什么的,返回什么',
895
- inputSchema: z.object({ key: z.string() }),
896
- execute: async (input, ctx) => {
897
- return { result: input.key }
898
- },
899
- })
900
- ```
901
-
902
- `ctx.onStream` 用于流式推送(如 LLM token 输出):
903
-
904
- ```ts
905
- const llmTool = tool({
906
- description: '生成文本',
907
- inputSchema: z.object({ prompt: z.string() }),
908
- execute: async (input, ctx) => {
909
- const stream = await openai.chat.completions.create({ ... })
910
- let full = ''
911
- for await (const chunk of stream) {
912
- full += chunk.choices[0]?.delta?.content || ''
913
- ctx.onStream?.({ type: 'llm-stream', chunk, accumulated: full })
914
- }
915
- return { text: full }
916
- },
917
+ messages: [{ role: 'user', content: '查询用户123,如果存在则发送欢迎邮件' }],
917
918
  })
918
919
  ```
919
920
 
920
- ### Core Nodes
921
+ ### Node types
921
922
 
922
- 7 built-in node types:
923
+ 7 built-in node types for defining the execution graph:
923
924
 
924
925
  | Node | Purpose | Input |
925
926
  |------|---------|-------|
926
- | `call` | Call a tool or sub-workflow | `{ tool: "name", args: {...} }` or `{ function: "name", args: {...} }` |
927
- | `set` | Declare or assign a variable | `{ name: "x", value: 42 }` |
927
+ | `call` | Call a registered AI SDK Tool | `{ tool: "name", args: {...} }` |
928
+ | `set` | Assign a variable | `{ name: "x", value: 42 }` |
928
929
  | `get` | Read a variable | `{ name: "x" }` |
929
930
  | `eval` | Evaluate an expression | `{ expression: "$var.x + 1" }` |
930
931
  | `if` | Conditional branch | `{ conditions: [{ test: ..., body: [nodes] }] }` |
931
932
  | `while` | Loop | `{ condition: "$var.i < 5" }, body: [nodes]` |
932
933
  | `http` | HTTP request | `{ url: "https://...", method: "GET" }` |
933
934
 
934
- ### Variable Reference Syntax
935
+ ### Reference syntax
935
936
 
936
937
  | Pattern | Meaning | Example |
937
938
  |---------|---------|---------|
938
939
  | `$var.x` | Variable `x` | `$var.counter` |
939
940
  | `$nodes.u.output` | Full output of node `u` | `$nodes.u.output` |
940
941
  | `$nodes.u.output.field` | Specific field | `$nodes.u.output.email` |
941
- | `$input.userId` | Workflow input param | `$input.userId` |
942
- | `42`, `true`, `"hello"` | Literal values | Passed as-is |
943
-
944
- ### Engine API
945
-
946
- For programmatic use outside of Router:
947
-
948
- ```ts
949
- import { createWorkflowEngine, createSSEManager } from 'weifuwu'
950
-
951
- const sse = createSSEManager()
952
- const engine = createWorkflowEngine({ tools, sseManager: sse })
953
-
954
- // Sync execution
955
- const result = await engine.execute({ nodes: [...] })
942
+ | `$input.userId` | Input param | `$input.userId` |
956
943
 
957
- // Async execution with SSE
958
- engine.runAsync('wf-1', { nodes: [...] })
959
- ```
944
+ ### LLM generation
960
945
 
961
- ### SSE Events
946
+ Pass a `model` to `runWorkflow` — the LLM generates the workflow JSON from a goal:
962
947
 
963
948
  ```ts
964
- const sse = createSSEManager()
965
- const stream = sse.createStream('wf-1')
949
+ const runWF = runWorkflow({
950
+ tools: { queryUser, sendEmail },
951
+ model: openai('gpt-4o'),
952
+ })
966
953
 
967
- const reader = stream.getReader()
968
- // event: workflow-start — { workflowId, goal }
969
- // event: node-start — { nodeId, tool, input }
970
- // event: node-end — { nodeId, output }
971
- // event: llm-stream — { nodeId, chunk, accumulated }
972
- // event: complete — { result, duration }
973
- // event: error — { error }
954
+ const result = await streamText({
955
+ model,
956
+ tools: { runWF },
957
+ })
974
958
  ```
975
959
 
976
- ### Sub-workflows
977
-
978
- Define reusable sub-workflows in the `functions` field:
979
-
980
- ```json
981
- {
982
- "functions": {
983
- "double": {
984
- "inputSchema": { "type": "object", "properties": { "x": { "type": "number" } } },
985
- "workflow": {
986
- "nodes": [
987
- { "id": "calc", "tool": "eval", "input": { "expression": "$input.x * 2" } }
988
- ]
989
- }
990
- }
991
- },
992
- "nodes": [
993
- { "id": "call_double", "tool": "call", "input": { "function": "double", "args": { "x": 21 } } }
994
- ]
995
- }
996
- ```
960
+ The LLM calls `runWF` with a goal, and `runWorkflow` internally calls `generateText` to produce the workflow nodes, then executes them.
997
961
 
998
962
  ## React pages with tsx()
999
963
 
@@ -1191,17 +1155,81 @@ export default function NotFound() {
1191
1155
  }
1192
1156
  ```
1193
1157
 
1158
+ ## Health check
1159
+
1160
+ ```ts
1161
+ import { serve, Router, health } from 'weifuwu'
1162
+
1163
+ const app = new Router()
1164
+ app.use(health()) // GET /health → 200
1165
+ app.use(health({ path: '/healthz' })) // custom path
1166
+ app.use(health({
1167
+ check: async () => { await db.sql`SELECT 1` }, // fail → 503
1168
+ }))
1169
+ serve(app.handler(), { port: 3000 })
1170
+ ```
1171
+
1172
+ Returns a `Router` — mount with `app.use()`.
1173
+
1174
+ ## Internationalization
1175
+
1176
+ ```ts
1177
+ import { i18n } from 'weifuwu'
1178
+
1179
+ app.use(i18n({ dir: './locales', defaultLocale: 'en' }))
1180
+
1181
+ // In any handler after i18n middleware:
1182
+ app.get('/hello', (req, ctx) => {
1183
+ const msg = ctx.t('greeting', { name: 'World' })
1184
+ return Response.json({ message: msg, locale: ctx.locale })
1185
+ })
1186
+ ```
1187
+
1188
+ Locale detection: `Cookie: locale=zh` → `Accept-Language: zh-CN` → `defaultLocale`.
1189
+
1190
+ ## Email
1191
+
1192
+ ```ts
1193
+ import { mailer } from 'weifuwu'
1194
+
1195
+ // SMTP transport
1196
+ const mail = mailer({
1197
+ transport: 'smtp://user:pass@smtp.example.com',
1198
+ from: 'noreply@example.com',
1199
+ })
1200
+ await mail.send({ to: 'user@example.com', subject: 'Welcome', html: '<h1>Hi!</h1>' })
1201
+ await mail.close()
1202
+
1203
+ // Custom transport (Resend, SES, SendGrid, etc.)
1204
+ const mail2 = mailer({
1205
+ send: async (msg) => { await resend.emails.send(msg) },
1206
+ })
1207
+ await mail2.send({ to: 'user@example.com', subject: 'Hi', text: 'Hello' })
1208
+ await mail2.close()
1209
+ ```
1210
+
1211
+ ## Test utilities
1212
+
1213
+ ```ts
1214
+ import { createTestServer } from 'weifuwu'
1215
+
1216
+ const { server, url } = await createTestServer(app.handler())
1217
+ const res = await fetch(`${url}/api/users`)
1218
+ assert.equal(res.status, 200)
1219
+ server.stop()
1220
+ ```
1221
+
1194
1222
  ## Usage within a full app
1195
1223
 
1196
1224
  ```ts
1197
- import { serve, Router, ai, graphql, workflow } from 'weifuwu'
1198
- import { tsx } from 'weifuwu/tsx'
1225
+ import { serve, Router, aiStream, graphql } from 'weifuwu'
1199
1226
 
1200
1227
  const app = new Router()
1201
1228
  app.use('/', await tsx({ dir: './pages/' }))
1202
- app.use('/chat', ai(async (req) => ({ model: openai('gpt-4o'), messages: (await req.json()).messages })))
1203
- app.use('/graphql', graphql(() => ({ schema: `type Query { hello: String }`, resolvers: { Query: { hello: () => 'world' } } })))
1204
- app.use('/agent', workflow(() => ({ tools: myTools, stream: true })))
1229
+ const chat = await aiStream(async (req) => ({ model: openai('gpt-4o'), messages: (await req.json()).messages }))
1230
+ app.use('/chat', chat.router())
1231
+ const gql = graphql(() => ({ schema: `type Query { hello: String }`, resolvers: { Query: { hello: () => 'world' } } }))
1232
+ app.use('/graphql', gql.router())
1205
1233
  app.ws('/chat', { message(ws, _, data) { ws.send(data) } })
1206
1234
 
1207
1235
  serve(app.handler(), { websocket: app.websocketHandler() })
@@ -1334,7 +1362,7 @@ Returns `TenantModule` — `{ migrate, middleware, router, graphql, close }`.
1334
1362
  | `model` | env `OPENAI_MODEL` → Ollama | `LanguageModel` from ai SDK |
1335
1363
  | `embeddingModel` | env `OPENAI_EMBEDDING_MODEL` → Ollama | `EmbeddingModel` for knowledge RAG |
1336
1364
  | `embeddingDimension` | `1024` | Vector dimension for pgvector |
1337
- | `tools` | — | Tools for workflow-type agents (ai SDK `Tool` objects) |
1365
+ | `tools` | — | Tools for tool-use agents (ai SDK `Tool` objects) |
1338
1366
 
1339
1367
  Returns `AgentModule` — `{ migrate, router, run, addKnowledge, close }`.
1340
1368
 
@@ -1418,12 +1446,12 @@ serve(app.handler(), { websocket: app.websocketHandler() })
1418
1446
  | `queue(options?)` | Redis-backed job queue — immediate, delayed, cron scheduling |
1419
1447
  | `user(options)` | Built-in authentication (password + OAuth2 Server + JWT, middleware) |
1420
1448
  | `tenant(options)` | Multi-tenant BaaS — dynamic tables, REST + GraphQL auto-generation, row-level isolation |
1421
- | `agent(options)` | AI Agent — chat/workflow/knowledge, Ollama-ready, programmatic API |
1449
+ | `agent(options)` | AI Agent — chat/tool-use/knowledge, Ollama-ready, programmatic API |
1422
1450
  | `messager(options)` | Real-time messaging — channels, WebSocket, agent routing, webhooks |
1423
1451
  | `opencode(options)` | AI programming assistant — chat agents with tools, skills, permissions, isolated workspaces |
1424
1452
  | `graphql(handler)` | GraphQL endpoint (GET/POST + GraphiQL) |
1425
- | `ai(handler)` | AI streaming endpoint (POST) |
1426
- | `workflow(handler)` | Workflow engine (POST + SSE) |
1453
+ | `aiStream(handler)` | AI streaming endpoint (POST) |
1454
+ | `runWorkflow(options)` | DAG execution engine as an AI SDK `Tool` — use with `streamText()` |
1427
1455
 
1428
1456
  ### Deploy
1429
1457
 
@@ -1441,13 +1469,14 @@ serve(app.handler(), { websocket: app.websocketHandler() })
1441
1469
  | `setCookie(res, name, value, options?)` | Set cookie (returns new Response) |
1442
1470
  | `deleteCookie(res, name)` | Delete cookie (returns new Response) |
1443
1471
  | `useTsx()` | Hook returning `{ params, query, user, parsed }` from `TsxContext` |
1444
- | `createWorkflowEngine(options)` | Programmatic workflow engine |
1445
- | `createSSEManager()` | SSE event manager for workflows |
1446
- | `tool(def)` | Define a workflow tool |
1447
- | `pgTable(name, columns)` | Type-safe table schema definition with DDL generation |
1472
+ | `runWorkflow(options)` | Create a DAG execution AI SDK `Tool` — `{ tools?, model?, maxSteps? }` |
1473
+ | `pgTable(name, columns)` | Type-safe table schema definition with DDL + CRUD |
1474
+ | `pg.table(name, columns)` | Pre-bound table (no `sql` parameter needed for CRUD) |
1448
1475
  | `serial()`, `uuid()`, `text()`, `integer()`, `boolean()`, `timestamptz()`, `jsonb()`, `textArray()`, `vector()` | Column type builders |
1449
- | `sql(strings, ...)` | SQL expression literal for table defaults (e.g. `sql\`NOW()\``) |
1476
+ | `sql(strings, ...)` | SQL expression literal for defaults and SET values (e.g. `sql\`NOW()\``) |
1450
1477
  | `PgModule` | Base class for database-backed modules (provides `sql`, `close()`) |
1478
+ | `BoundTable` | Table with pre-bound `sql` — returned by `pg.table()` |
1479
+ | `FindOptions` | Query options: `{ orderBy?, limit?, offset? }` for `find()` |
1451
1480
 
1452
1481
  Import `useTsx` and `TsxContext` from `'weifuwu'`.
1453
1482
 
@@ -1,4 +1,4 @@
1
- import type { LanguageModel, EmbeddingModel, Tool } from '../vendor.ts';
1
+ import { type LanguageModel, type EmbeddingModel, type Tool } from 'ai';
2
2
  import type { Sql } from '../vendor.ts';
3
3
  import type { RunParams, RunResult, KnowledgeDoc } from './types.ts';
4
4
  interface RunnerDeps {
@@ -1,10 +1,10 @@
1
- import type { LanguageModel, EmbeddingModel, Tool } from '../vendor.ts';
1
+ import type { LanguageModel, EmbeddingModel, Tool } from 'ai';
2
2
  export interface AgentConfig {
3
3
  id: number;
4
4
  tenant_id: string | null;
5
5
  name: string;
6
6
  description: string;
7
- type: 'chat' | 'workflow';
7
+ type: 'chat' | 'tool-use';
8
8
  model: string;
9
9
  system_prompt: string;
10
10
  owner_id: number;
@@ -0,0 +1,14 @@
1
+ import type { LanguageModel } from 'ai';
2
+ export declare function runWorkflow(opts?: {
3
+ tools?: Record<string, any>;
4
+ model?: LanguageModel;
5
+ maxSteps?: number;
6
+ }): import("ai").Tool<{
7
+ goal: string;
8
+ nodes?: any[];
9
+ }, {
10
+ result: unknown;
11
+ nodeOutputs: {
12
+ [k: string]: unknown;
13
+ };
14
+ }>;
package/dist/ai.d.ts CHANGED
@@ -1,19 +1,7 @@
1
1
  import type { Context } from './types.ts';
2
2
  import { Router } from './router.ts';
3
- type StreamTextParams = {
4
- model: unknown;
5
- prompt?: string;
6
- system?: string;
7
- messages?: unknown[];
8
- maxTokens?: number;
9
- temperature?: number;
10
- [key: string]: unknown;
11
- };
12
- export type AIHandler = (req: Request, ctx: Context) => StreamTextParams | Promise<StreamTextParams>;
13
- export declare const _ai: {
14
- streamText: (params: StreamTextParams) => {
15
- toTextStreamResponse: () => Response;
16
- };
17
- };
18
- export declare function ai(handler: AIHandler): Promise<Router>;
19
- export {};
3
+ export type AIHandler = (req: Request, ctx: Context) => Record<string, unknown> | Promise<Record<string, unknown>>;
4
+ export declare const _ai: Record<string, any>;
5
+ export declare function aiStream(handler: AIHandler): Promise<{
6
+ router(): Router;
7
+ }>;
@@ -37,7 +37,7 @@ export interface AppStatus {
37
37
  error?: string;
38
38
  }
39
39
  export interface DeployServer {
40
- stop(): Promise<void>;
40
+ close(): Promise<void>;
41
41
  ready: Promise<void>;
42
42
  url: string;
43
43
  apps: {
package/dist/graphql.d.ts CHANGED
@@ -9,4 +9,6 @@ export interface GraphQLOptions {
9
9
  graphiql?: boolean;
10
10
  }
11
11
  export type GraphQLHandler = (req: Request, ctx: Context) => GraphQLOptions | Promise<GraphQLOptions>;
12
- export declare function graphql(handler: GraphQLHandler): Router;
12
+ export declare function graphql(handler: GraphQLHandler): {
13
+ router(): Router;
14
+ };
@@ -0,0 +1,6 @@
1
+ import { Router } from './router.ts';
2
+ export interface HealthOptions {
3
+ path?: string;
4
+ check?: () => Promise<void>;
5
+ }
6
+ export declare function health(options?: HealthOptions): Router;
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { Middleware } from './types.ts';
2
+ export interface I18nOptions {
3
+ dir: string;
4
+ defaultLocale?: string;
5
+ }
6
+ export declare function i18n(options: I18nOptions): Middleware;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type { Context, Handler, Middleware, ErrorHandler } from './types.ts';
2
- export { serve } from './serve.ts';
2
+ export { serve, createTestServer } from './serve.ts';
3
3
  export type { ServeOptions, Server } from './serve.ts';
4
4
  export { Router } from './router.ts';
5
5
  export type { WebSocketHandler } from './router.ts';
@@ -21,10 +21,9 @@ export { compress } from './compress.ts';
21
21
  export type { CompressOptions } from './compress.ts';
22
22
  export { graphql } from './graphql.ts';
23
23
  export type { GraphQLOptions, GraphQLHandler } from './graphql.ts';
24
- export { ai } from './ai.ts';
24
+ export { aiStream } from './ai.ts';
25
25
  export type { AIHandler } from './ai.ts';
26
- export { tool, createWorkflowEngine, createSSEManager, generateWorkflow, workflow } from './workflow/index.ts';
27
- export type { Tool, Workflow, WorkflowEngine, WorkflowState, SSEEvent, WorkflowOptions, WorkflowHandler } from './workflow/index.ts';
26
+ export { runWorkflow } from './ai/workflow.ts';
28
27
  export { postgres } from './postgres/index.ts';
29
28
  export type { PostgresOptions, PostgresClient } from './postgres/types.ts';
30
29
  export { user } from './user/index.ts';
@@ -43,3 +42,9 @@ export { deploy, defineConfig } from './deploy/index.ts';
43
42
  export type { DeployConfig, AppConfig, DeployServer, AppStatus } from './deploy/types.ts';
44
43
  export { opencode } from './opencode/index.ts';
45
44
  export type { OpencodeOptions, OpencodeModule, SkillDef, OpencodePermissions, Session as OpencodeSession } from './opencode/types.ts';
45
+ export { health } from './health.ts';
46
+ export type { HealthOptions } from './health.ts';
47
+ export { i18n } from './i18n.ts';
48
+ export type { I18nOptions } from './i18n.ts';
49
+ export { mailer } from './mailer.ts';
50
+ export type { MailerOptions, MailOptions, Mailer } from './mailer.ts';