weifuwu 0.15.0 → 0.16.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.
package/README.md CHANGED
@@ -24,10 +24,12 @@ Everything follows the same `(req, ctx) => Response` contract. The Router handle
24
24
  - **PostgreSQL** — schema builder with type-safe DDL, CRUD (`read`/`readMany`, `insertMany`, `update`/`updateMany`, `delete`/`deleteMany`), WHERE helpers (`eq`, `gte`, `contains`, `and`, `or`), transactions, vector search
25
25
  - **Auth** — password + JWT + OAuth2 Server (authorization code / PKCE / client_credentials)
26
26
  - **Real-time** — WebSocket, messaging channels with agent routing
27
- - **AI** — streaming endpoint, DAG workflow tool, AI agents with RAG and tool-use
27
+ - **AI** — streaming endpoint, DAG workflow tool, AI agents with RAG and tool-use — re-exports `streamText`, `tool`, `openai` and more from AI SDK
28
28
  - **Data** — Redis client, job queue with cron scheduling
29
29
  - **Multi-tenant BaaS** — dynamic tables, auto REST + GraphQL, row-level isolation
30
30
  - **Deploy** — self-hosted PaaS: multi-app proxy, zero-downtime updates, auto SSL
31
+ - **Security** — `helmet()` security headers, request ID tracing, rate limiting, CORS, auth
32
+ - **SEO** — `robots.txt`, `sitemap.xml`, `X-Robots-Tag` middleware, `seoTags()` for meta / OG / Twitter Card
31
33
  - **i18n** — locale detection, JSON translations, `ctx.t()`
32
34
  - **Email** — SMTP or custom transport
33
35
  - **Health check** — configurable `/health` endpoint
@@ -47,8 +49,7 @@ serve((req, ctx) => new Response('Hello, World!'), { port: 3000 })
47
49
  ### Full app
48
50
 
49
51
  ```ts
50
- import { serve, Router, postgres, user, aiStream, graphql } from 'weifuwu'
51
- import { openai } from '@ai-sdk/openai'
52
+ import { serve, Router, postgres, user, aiStream, graphql, openai } from 'weifuwu'
52
53
 
53
54
  const app = new Router()
54
55
  const pg = postgres()
@@ -106,6 +107,7 @@ All use the same pattern — `const m = module(options)` → `app.use('/path', m
106
107
  | `graphql(handler)` | GraphQL endpoint | — |
107
108
  | `logdb(options)` | Structured event logging | `log()`, `migrate()`, `clean()`, `close()` |
108
109
  | `health(options?)` | Health check | — |
110
+ | `seo(options?)` | `robots.txt`, `sitemap.xml`, indexing control | `seoMiddleware()`, `seoTags()` |
109
111
  | `iii(options?)` | Worker/Function/Trigger service paradigm | `migrate()`, `trigger()`, `addWorker()`, `listWorkers()`, `listFunctions()`, `listTriggers()`, `shutdown()` |
110
112
  | `registerWorker(url)` | Pure WebSocket SDK (browser/Node) | `registerFunction()`, `registerTrigger()`, `trigger()`, `shutdown()` |
111
113
 
@@ -121,6 +123,9 @@ All use the same pattern — `const m = module(options)` → `app.use('/path', m
121
123
  | `validate(schemas)` | Zod validation (body, query, params) |
122
124
  | `upload(options?)` | Multipart file upload |
123
125
  | `i18n(options)` | Internationalization — `ctx.t()`, locale detection |
126
+ | `seoMiddleware(options?)` | `X-Robots-Tag` header — string or path-based function |
127
+ | `helmet(options?)` | Security headers — CSP, HSTS, X-Frame-Options, etc. |
128
+ | `requestId(options?)` | `X-Request-ID` header + `ctx.requestId` |
124
129
 
125
130
  ## Utility functions
126
131
 
@@ -131,12 +136,21 @@ All use the same pattern — `const m = module(options)` → `app.use('/path', m
131
136
  | `getCookies(req)` / `setCookie(res, ...)` / `deleteCookie(res, ...)` | Cookie helpers |
132
137
  | `mailer(options)` | Email sender (SMTP or custom) |
133
138
  | `createTestServer(handler)` | Start test server → `{ server, url }` |
139
+ | `seoTags(config)` | Generate `<title>`, `<meta>`, Open Graph, Twitter Card, canonical tags |
140
+ | `createSSEStream(iterable, opts?)` | SSE response from `AsyncIterable` |
141
+ | `formatSSE(event, data)` | Format SSE event string |
142
+ | `formatSSEData(data)` | Format SSE data string |
134
143
  | `runWorkflow(options)` | DAG execution engine as AI SDK `Tool` |
135
144
  | `pgTable(name, columns)` | Type-safe table schema builder |
136
145
  | `pg.table(name, columns)` | Pre-bound table (no `sql` param needed) |
137
146
  | `serial()`, `uuid()`, `text()`, ... | Column type builders |
138
147
  | `eq()`, `gte()`, `contains()`, `and()` ... | WHERE clause helpers — same API as Drizzle |
139
148
  | `PgModule` | Base class for DB-backed modules |
149
+ | `streamText()` / `generateText()` / `streamObject()` / `generateObject()` | AI SDK — text/structured generation |
150
+ | `tool()` | AI SDK — tool definition |
151
+ | `embed()` / `embedMany()` | AI SDK — text embeddings |
152
+ | `smoothStream()` | AI SDK — smooth streaming middleware |
153
+ | `openai` / `createOpenAI()` | OpenAI provider for AI SDK |
140
154
 
141
155
  ---
142
156
 
@@ -145,7 +159,7 @@ All use the same pattern — `const m = module(options)` → `app.use('/path', m
145
159
  Optional module that organizes service logic as **Worker + Function + Trigger**, plus a pure WebSocket SDK for connecting remote workers. Built-in `stream::*` functions for hierarchical real-time data.
146
160
 
147
161
  ```ts
148
- import { serve, Router, iii, createWorker as Worker, registerWorker } from 'weifuwu'
162
+ import { serve, Router, iii, createWorker, registerWorker } from 'weifuwu'
149
163
 
150
164
  // Engine
151
165
  const engine = iii({ pg, redis })
@@ -154,7 +168,7 @@ app.use('/iii', engine.router())
154
168
  serve(app.handler(), { port: 3000, websocket: app.websocketHandler() })
155
169
 
156
170
  // Local worker
157
- const w = new Worker('orders')
171
+ const w = createWorker('orders')
158
172
  w.registerFunction('orders::create', async (payload) => {
159
173
  return db.query('INSERT INTO orders ...', [payload.items])
160
174
  })
@@ -1086,8 +1100,7 @@ export default function NotFound() {
1086
1100
  Server-sent event streaming via the Vercel AI SDK:
1087
1101
 
1088
1102
  ```ts
1089
- import { serve, Router, aiStream } from 'weifuwu'
1090
- import { openai } from '@ai-sdk/openai'
1103
+ import { serve, Router, aiStream, openai } from 'weifuwu'
1091
1104
 
1092
1105
  const app = new Router()
1093
1106
  const chat = await aiStream(async (req, ctx) => {
@@ -1104,8 +1117,7 @@ serve(app.handler(), { port: 3000 })
1104
1117
  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.
1105
1118
 
1106
1119
  ```ts
1107
- import { tool, streamText } from 'ai'
1108
- import { runWorkflow } from 'weifuwu'
1120
+ import { tool, streamText, runWorkflow } from 'weifuwu'
1109
1121
  import { z } from 'zod'
1110
1122
 
1111
1123
  const tools = {
@@ -1485,6 +1497,176 @@ const oc = await opencode({
1485
1497
 
1486
1498
  ---
1487
1499
 
1500
+ # SEO
1501
+
1502
+ Built-in SEO module — `robots.txt`, `sitemap.xml`, indexing headers, and meta tag utilities.
1503
+
1504
+ ```ts
1505
+ import { seo, seoMiddleware, seoTags } from 'weifuwu'
1506
+
1507
+ const app = new Router()
1508
+
1509
+ // robots.txt + sitemap.xml
1510
+ app.use(seo({
1511
+ baseUrl: 'https://example.com',
1512
+ robots: [
1513
+ { userAgent: '*', allow: '/', disallow: ['/admin', '/api'] },
1514
+ ],
1515
+ sitemap: {
1516
+ urls: [
1517
+ { loc: '/', changefreq: 'daily', priority: 1.0 },
1518
+ { loc: '/about', changefreq: 'monthly', priority: 0.8 },
1519
+ ],
1520
+ // Dynamic URLs from database
1521
+ async resolve() {
1522
+ const articles = await db.query('SELECT slug, updated_at FROM articles')
1523
+ return articles.map(a => ({
1524
+ loc: `/blog/${a.slug}`,
1525
+ lastmod: a.updated_at,
1526
+ }))
1527
+ },
1528
+ cacheTTL: 3_600_000, // re-generate every hour (default)
1529
+ },
1530
+ }))
1531
+ ```
1532
+
1533
+ ### Endpoints
1534
+
1535
+ | Path | Description |
1536
+ |------|-------------|
1537
+ | `GET /robots.txt` | Generated robots.txt with optional Sitemap reference |
1538
+ | `GET /sitemap.xml` | Generated XML sitemap with caching |
1539
+
1540
+ ### seoMiddleware — Indexing control
1541
+
1542
+ ```ts
1543
+ // Global — block all paths
1544
+ app.use(seoMiddleware({ headers: { 'X-Robots-Tag': 'noindex' } }))
1545
+
1546
+ // Per-path via function
1547
+ app.use(seoMiddleware({
1548
+ headers: {
1549
+ 'X-Robots-Tag': (path) => path.startsWith('/admin') ? 'noindex' : undefined,
1550
+ },
1551
+ }))
1552
+ ```
1553
+
1554
+ ### seoTags — Meta / OG / Twitter Card
1555
+
1556
+ Generate SEO meta tags for SSR pages:
1557
+
1558
+ ```ts
1559
+ const tags = seoTags({
1560
+ title: 'My Page',
1561
+ description: 'A great page about things',
1562
+ ogImage: 'https://example.com/og.png',
1563
+ twitterCard: 'summary_large_image',
1564
+ canonical: 'https://example.com/page',
1565
+ })
1566
+ // → <title>My Page</title>
1567
+ // → <meta property="og:title" content="My Page">
1568
+ // → <meta name="twitter:card" content="summary_large_image">
1569
+ // → <link rel="canonical" href="https://example.com/page">
1570
+ // ...
1571
+ ```
1572
+
1573
+ Use in `layout.tsx` or `page.tsx` with `tsx()`:
1574
+
1575
+ ```tsx
1576
+ export default function RootLayout({ children }) {
1577
+ return (
1578
+ <html>
1579
+ <head>{seoTags({ title: 'My App' })}</head>
1580
+ <body>{children}</body>
1581
+ </html>
1582
+ )
1583
+ }
1584
+ ```
1585
+
1586
+ # Security
1587
+
1588
+ ## Helmet — Security headers
1589
+
1590
+ ```ts
1591
+ import { helmet } from 'weifuwu'
1592
+
1593
+ // Apply all security headers with safe defaults
1594
+ app.use(helmet())
1595
+
1596
+ // Customize individual headers (any can be set to false to remove)
1597
+ app.use(helmet({
1598
+ contentSecurityPolicy: "default-src 'self'",
1599
+ xFrameOptions: 'DENY',
1600
+ strictTransportSecurity: 'max-age=63072000; includeSubDomains; preload',
1601
+ }))
1602
+
1603
+ // Middleware-order: set after helmet to override
1604
+ app.use(helmet({ xFrameOptions: false })) // remove a header
1605
+ ```
1606
+
1607
+ 13 security headers set by default:
1608
+
1609
+ | Header | Default |
1610
+ |--------|---------|
1611
+ | `Content-Security-Policy` | `default-src 'self'; ...` |
1612
+ | `X-Content-Type-Options` | `nosniff` |
1613
+ | `X-Frame-Options` | `SAMEORIGIN` |
1614
+ | `Strict-Transport-Security` | `max-age=15552000; includeSubDomains` |
1615
+ | `X-XSS-Protection` | `0` |
1616
+ | `Referrer-Policy` | `no-referrer` |
1617
+ | `Permissions-Policy` | `camera=(),geolocation=(),...` |
1618
+ | `Cross-Origin-Embedder-Policy` | `require-corp` |
1619
+ | `Cross-Origin-Opener-Policy` | `same-origin` |
1620
+ | `Cross-Origin-Resource-Policy` | `same-origin` |
1621
+ | `Origin-Agent-Cluster` | `?1` |
1622
+ | `X-DNS-Prefetch-Control` | `off` |
1623
+ | `X-Download-Options` | `noopen` |
1624
+ | `X-Permitted-Cross-Domain-Policies` | `none` |
1625
+
1626
+ Does not override response headers already set by the application — your explicit headers take precedence.
1627
+
1628
+ ## Request ID
1629
+
1630
+ ```ts
1631
+ import { requestId } from 'weifuwu'
1632
+
1633
+ // Every response gets X-Request-ID
1634
+ app.use(requestId())
1635
+
1636
+ // Custom header name
1637
+ app.use(requestId({ header: 'X-Trace-Id' }))
1638
+
1639
+ // Custom ID generator
1640
+ app.use(requestId({ generator: () => crypto.randomUUID() }))
1641
+
1642
+ // Access the ID in handlers via ctx.requestId
1643
+ app.get('/log', (req, ctx) => {
1644
+ console.log(`Handling request ${ctx.requestId}`)
1645
+ return Response.json({ id: ctx.requestId })
1646
+ })
1647
+ ```
1648
+
1649
+ Preserves incoming `X-Request-ID` for distributed tracing — if the upstream service already set it, the value is reused and propagated.
1650
+
1651
+ ## Server-Sent Events
1652
+
1653
+ ```ts
1654
+ import { createSSEStream, formatSSE } from 'weifuwu'
1655
+
1656
+ async function* eventStream() {
1657
+ yield { type: 'ping', data: { time: Date.now() } }
1658
+ yield { type: 'message', data: { text: 'hello' } }
1659
+ }
1660
+
1661
+ app.get('/events', () => createSSEStream(eventStream()))
1662
+ ```
1663
+
1664
+ | Function | Description |
1665
+ |----------|-------------|
1666
+ | `createSSEStream(iterable, opts?)` | Returns a `Response` with `Content-Type: text/event-stream` |
1667
+ | `formatSSE(event, data)` | Formats an SSE event string (`event: ...\ndata: ...\n\n`) |
1668
+ | `formatSSEData(data)` | Formats SSE data-only string (`data: ...\n\n`) |
1669
+
1488
1670
  # Health, i18n, Email & Testing
1489
1671
 
1490
1672
  ## Health check
package/cli.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { mkdir, writeFile, copyFile, readdir } from 'node:fs/promises'
2
+ import { mkdir, writeFile, copyFile, readFile } from 'node:fs/promises'
3
+ import { existsSync } from 'node:fs'
3
4
  import { homedir } from 'node:os'
4
5
  import { join, dirname, resolve } from 'node:path'
5
6
  import { fileURLToPath } from 'node:url'
@@ -7,26 +8,20 @@ import { fileURLToPath } from 'node:url'
7
8
  const __filename = fileURLToPath(import.meta.url)
8
9
  const __dirname = dirname(__filename)
9
10
 
10
- const pkgRoot = resolve(__dirname, '..')
11
+ const pkgRoot = existsSync(join(__dirname, 'package.json')) ? __dirname : resolve(__dirname, '..')
11
12
 
12
13
  async function cmdSkill() {
13
14
  const targetDir = join(homedir(), '.agents', 'skills', 'weifuwu')
14
- const docsTarget = join(targetDir, 'docs')
15
-
16
- await mkdir(docsTarget, { recursive: true })
15
+ await mkdir(targetDir, { recursive: true })
17
16
  await copyFile(join(pkgRoot, 'README.md'), join(targetDir, 'SKILL.md'))
18
-
19
- const docDir = join(pkgRoot, 'docs')
20
- const entries = await readdir(docDir)
21
- for (const entry of entries) {
22
- if (entry.endsWith('.md')) {
23
- await copyFile(join(docDir, entry), join(docsTarget, entry))
24
- }
25
- }
26
-
27
17
  console.log('✅ Installed weifuwu skill to ~/.agents/skills/weifuwu/')
28
18
  }
29
19
 
20
+ async function cmdVersion() {
21
+ const pkg = JSON.parse(await readFile(join(pkgRoot, 'package.json'), 'utf-8'))
22
+ console.log(pkg.version)
23
+ }
24
+
30
25
  async function cmdInit(name: string) {
31
26
  const targetDir = resolve(process.cwd(), name)
32
27
  await mkdir(targetDir, { recursive: true })
@@ -78,7 +73,9 @@ async function cmdInit(name: string) {
78
73
 
79
74
  const cmd = process.argv[2]
80
75
 
81
- if (cmd === 'skill') {
76
+ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
77
+ cmdVersion().catch(console.error)
78
+ } else if (cmd === 'skill') {
82
79
  cmdSkill().catch(console.error)
83
80
  } else if (cmd === 'init') {
84
81
  const name = process.argv[3]
@@ -88,5 +85,5 @@ if (cmd === 'skill') {
88
85
  }
89
86
  cmdInit(name).catch(console.error)
90
87
  } else {
91
- console.log('\nUsage:\n npx weifuwu init <name> Create a new weifuwu project\n npx weifuwu skill Install weifuwu skill to ~/.agents/skills/\n')
88
+ console.log('\nUsage:\n npx weifuwu version Print version\n npx weifuwu init <name> Create a new weifuwu project\n npx weifuwu skill Install weifuwu skill to ~/.agents/skills/\n')
92
89
  }
@@ -0,0 +1,2 @@
1
+ export { streamText, generateText, generateObject, streamObject, tool, embed, embedMany, smoothStream, } from 'ai';
2
+ export { openai, createOpenAI, } from '@ai-sdk/openai';
package/dist/cli.js CHANGED
@@ -1,27 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // cli.ts
4
- import { mkdir, writeFile, copyFile, readdir } from "node:fs/promises";
4
+ import { mkdir, writeFile, copyFile, readFile } from "node:fs/promises";
5
+ import { existsSync } from "node:fs";
5
6
  import { homedir } from "node:os";
6
7
  import { join, dirname, resolve } from "node:path";
7
8
  import { fileURLToPath } from "node:url";
8
9
  var __filename = fileURLToPath(import.meta.url);
9
10
  var __dirname = dirname(__filename);
10
- var pkgRoot = resolve(__dirname, "..");
11
+ var pkgRoot = existsSync(join(__dirname, "package.json")) ? __dirname : resolve(__dirname, "..");
11
12
  async function cmdSkill() {
12
13
  const targetDir = join(homedir(), ".agents", "skills", "weifuwu");
13
- const docsTarget = join(targetDir, "docs");
14
- await mkdir(docsTarget, { recursive: true });
14
+ await mkdir(targetDir, { recursive: true });
15
15
  await copyFile(join(pkgRoot, "README.md"), join(targetDir, "SKILL.md"));
16
- const docDir = join(pkgRoot, "docs");
17
- const entries = await readdir(docDir);
18
- for (const entry of entries) {
19
- if (entry.endsWith(".md")) {
20
- await copyFile(join(docDir, entry), join(docsTarget, entry));
21
- }
22
- }
23
16
  console.log("\u2705 Installed weifuwu skill to ~/.agents/skills/weifuwu/");
24
17
  }
18
+ async function cmdVersion() {
19
+ const pkg = JSON.parse(await readFile(join(pkgRoot, "package.json"), "utf-8"));
20
+ console.log(pkg.version);
21
+ }
25
22
  async function cmdInit(name) {
26
23
  const targetDir = resolve(process.cwd(), name);
27
24
  await mkdir(targetDir, { recursive: true });
@@ -65,7 +62,9 @@ async function cmdInit(name) {
65
62
  console.log(`\u2705 Created ${name}/ \u2014 cd ${name} && npm install && npm run dev`);
66
63
  }
67
64
  var cmd = process.argv[2];
68
- if (cmd === "skill") {
65
+ if (cmd === "version" || cmd === "-v" || cmd === "--version") {
66
+ cmdVersion().catch(console.error);
67
+ } else if (cmd === "skill") {
69
68
  cmdSkill().catch(console.error);
70
69
  } else if (cmd === "init") {
71
70
  const name = process.argv[3];
@@ -75,5 +74,5 @@ if (cmd === "skill") {
75
74
  }
76
75
  cmdInit(name).catch(console.error);
77
76
  } else {
78
- console.log("\nUsage:\n npx weifuwu init <name> Create a new weifuwu project\n npx weifuwu skill Install weifuwu skill to ~/.agents/skills/\n");
77
+ console.log("\nUsage:\n npx weifuwu version Print version\n npx weifuwu init <name> Create a new weifuwu project\n npx weifuwu skill Install weifuwu skill to ~/.agents/skills/\n");
79
78
  }
@@ -0,0 +1,18 @@
1
+ import type { Middleware } from './types.ts';
2
+ export interface HelmetOptions {
3
+ contentSecurityPolicy?: string | false;
4
+ crossOriginEmbedderPolicy?: string | false;
5
+ crossOriginOpenerPolicy?: string | false;
6
+ crossOriginResourcePolicy?: string | false;
7
+ originAgentCluster?: string | false;
8
+ referrerPolicy?: string | false;
9
+ strictTransportSecurity?: string | false;
10
+ xContentTypeOptions?: string | false;
11
+ xDnsPrefetchControl?: string | false;
12
+ xDownloadOptions?: string | false;
13
+ xFrameOptions?: string | false;
14
+ xPermittedCrossDomainPolicies?: string | false;
15
+ xXssProtection?: string | false;
16
+ permissionsPolicy?: string | false;
17
+ }
18
+ export declare function helmet(options?: HelmetOptions): Middleware;
@@ -4,6 +4,7 @@ import type { StreamUpdateOp, StreamSubscription } from './types.ts';
4
4
  export declare function createStream(opts?: {
5
5
  pg?: PostgresClient;
6
6
  redis?: Redis;
7
+ streamTTL?: number;
7
8
  }): {
8
9
  subscribe(ws: WebSocket, sub: StreamSubscription): void;
9
10
  unsubscribe(ws: WebSocket): void;
@@ -17,6 +17,8 @@ export interface TriggerInput {
17
17
  export interface IIIOptions {
18
18
  pg?: PostgresClient;
19
19
  redis?: Redis;
20
+ /** TTL in seconds for Redis stream keys. Default: 3600 (1 hour). Set to 0 for no expiration. */
21
+ streamTTL?: number;
20
22
  }
21
23
  export interface TriggerRequest {
22
24
  function_id: string;
package/dist/index.d.ts CHANGED
@@ -20,36 +20,45 @@ export { rateLimit } from './rate-limit.ts';
20
20
  export type { RateLimitOptions } from './rate-limit.ts';
21
21
  export { compress } from './compress.ts';
22
22
  export type { CompressOptions } from './compress.ts';
23
+ export { helmet } from './helmet.ts';
24
+ export type { HelmetOptions } from './helmet.ts';
25
+ export { requestId } from './request-id.ts';
26
+ export type { RequestIdOptions } from './request-id.ts';
27
+ export { createSSEStream, formatSSE, formatSSEData } from './sse.ts';
28
+ export type { SSEEvent } from './sse.ts';
23
29
  export { graphql } from './graphql.ts';
24
30
  export type { GraphQLOptions, GraphQLHandler } from './graphql.ts';
25
31
  export { aiStream } from './ai.ts';
26
32
  export type { AIHandler } from './ai.ts';
27
33
  export { runWorkflow } from './ai/workflow.ts';
34
+ export { streamText, generateText, generateObject, streamObject, tool, embed, embedMany, smoothStream, openai, createOpenAI, } from './ai-sdk.ts';
28
35
  export { postgres } from './postgres/index.ts';
29
36
  export type { PostgresOptions, PostgresClient } from './postgres/types.ts';
30
37
  export { user } from './user/index.ts';
31
- export type { UserOptions, UserData, UserModule, OAuth2Client } from './user/types.ts';
38
+ export type { UserOptions, UserData, UserModule, OAuth2Client, } from './user/types.ts';
32
39
  export { redis } from './redis/index.ts';
33
40
  export type { RedisOptions, RedisClient } from './redis/types.ts';
34
41
  export { queue } from './queue/index.ts';
35
42
  export type { QueueOptions, QueueJob, Queue } from './queue/types.ts';
36
43
  export { tenant } from './tenant/index.ts';
37
- export type { TenantOptions, TenantModule, TenantContext, FieldDef, FieldType, RelationDef, UserTableRow } from './tenant/types.ts';
44
+ export type { TenantOptions, TenantModule, TenantContext, FieldDef, FieldType, RelationDef, UserTableRow, } from './tenant/types.ts';
38
45
  export { agent } from './agent/index.ts';
39
- export type { AgentOptions, AgentModule, AgentConfig, RunParams, RunResult, KnowledgeDoc } from './agent/types.ts';
46
+ export type { AgentOptions, AgentModule, AgentConfig, RunParams, RunResult, KnowledgeDoc, } from './agent/types.ts';
40
47
  export { messager } from './messager/index.ts';
41
- export type { MessagerOptions, MessagerModule, Channel, ChannelMember, Message } from './messager/types.ts';
48
+ export type { MessagerOptions, MessagerModule, Channel, ChannelMember, Message, } from './messager/types.ts';
42
49
  export { deploy, defineConfig } from './deploy/index.ts';
43
- export type { DeployConfig, AppConfig, DeployServer, AppStatus } from './deploy/types.ts';
50
+ export type { DeployConfig, AppConfig, DeployServer, AppStatus, } from './deploy/types.ts';
44
51
  export { opencode } from './opencode/index.ts';
45
- export type { OpencodeOptions, OpencodeModule, SkillDef, OpencodePermissions, Session as OpencodeSession } from './opencode/types.ts';
52
+ export type { OpencodeOptions, OpencodeModule, SkillDef, OpencodePermissions, Session as OpencodeSession, } from './opencode/types.ts';
46
53
  export { health } from './health.ts';
47
54
  export type { HealthOptions } from './health.ts';
48
55
  export { i18n } from './i18n.ts';
49
56
  export type { I18nOptions } from './i18n.ts';
57
+ export { seo, seoMiddleware, seoTags } from './seo.ts';
58
+ export type { SeoOptions, RobotsRule, SitemapUrl, SitemapConfig, SeoHeadersConfig, SeoTagsConfig, } from './seo.ts';
50
59
  export { mailer } from './mailer.ts';
51
60
  export type { MailerOptions, MailOptions, Mailer } from './mailer.ts';
52
61
  export { logdb } from './logdb/index.ts';
53
- export type { LogdbOptions, LogdbModule, LogEntry, LogEntryInput } from './logdb/types.ts';
54
- export { iii, createWorker as Worker, registerWorker } from './iii/index.ts';
55
- export type { IIIModule, IIIOptions, WorkerInfo, FunctionInfo, TriggerInfo, FunctionHandler, FunctionContext, TriggerInput, RemoteWorker, TriggerRequest } from './iii/types.ts';
62
+ export type { LogdbOptions, LogdbModule, LogEntry, LogEntryInput, } from './logdb/types.ts';
63
+ export { iii, createWorker, registerWorker } from './iii/index.ts';
64
+ export type { IIIModule, IIIOptions, WorkerInfo, FunctionInfo, TriggerInfo, FunctionHandler, FunctionContext, TriggerInput, RemoteWorker, TriggerRequest, } from './iii/types.ts';