ts-procedures 5.3.0 → 5.4.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 (60) hide show
  1. package/README.md +90 -0
  2. package/agent_config/claude-code/agents/ts-procedures-architect.md +15 -0
  3. package/agent_config/claude-code/skills/guide/anti-patterns.md +106 -0
  4. package/agent_config/claude-code/skills/guide/api-reference.md +150 -4
  5. package/agent_config/claude-code/skills/guide/patterns.md +155 -0
  6. package/agent_config/claude-code/skills/review/checklist.md +22 -0
  7. package/agent_config/claude-code/skills/scaffold/SKILL.md +3 -1
  8. package/agent_config/claude-code/skills/scaffold/templates/hono-api.md +169 -0
  9. package/agent_config/copilot/copilot-instructions.md +35 -0
  10. package/agent_config/cursor/cursorrules +35 -0
  11. package/build/implementations/http/hono-api/index.d.ts +102 -0
  12. package/build/implementations/http/hono-api/index.js +339 -0
  13. package/build/implementations/http/hono-api/index.js.map +1 -0
  14. package/build/implementations/http/hono-api/index.test.d.ts +1 -0
  15. package/build/implementations/http/hono-api/index.test.js +983 -0
  16. package/build/implementations/http/hono-api/index.test.js.map +1 -0
  17. package/build/implementations/http/hono-api/types.d.ts +13 -0
  18. package/build/implementations/http/hono-api/types.js +2 -0
  19. package/build/implementations/http/hono-api/types.js.map +1 -0
  20. package/build/implementations/types.d.ts +44 -0
  21. package/build/index.d.ts +28 -6
  22. package/build/index.js +28 -0
  23. package/build/index.js.map +1 -1
  24. package/build/schema/compute-schema.d.ts +5 -0
  25. package/build/schema/compute-schema.js +8 -1
  26. package/build/schema/compute-schema.js.map +1 -1
  27. package/build/schema/parser.d.ts +6 -5
  28. package/build/schema/parser.js +54 -0
  29. package/build/schema/parser.js.map +1 -1
  30. package/package.json +8 -4
  31. package/src/errors.test.ts +0 -163
  32. package/src/errors.ts +0 -107
  33. package/src/exports.ts +0 -7
  34. package/src/implementations/http/README.md +0 -217
  35. package/src/implementations/http/express-rpc/README.md +0 -281
  36. package/src/implementations/http/express-rpc/index.test.ts +0 -957
  37. package/src/implementations/http/express-rpc/index.ts +0 -265
  38. package/src/implementations/http/express-rpc/types.ts +0 -16
  39. package/src/implementations/http/hono-rpc/README.md +0 -358
  40. package/src/implementations/http/hono-rpc/index.test.ts +0 -1075
  41. package/src/implementations/http/hono-rpc/index.ts +0 -237
  42. package/src/implementations/http/hono-rpc/types.ts +0 -16
  43. package/src/implementations/http/hono-stream/README.md +0 -526
  44. package/src/implementations/http/hono-stream/index.test.ts +0 -1676
  45. package/src/implementations/http/hono-stream/index.ts +0 -435
  46. package/src/implementations/http/hono-stream/types.ts +0 -29
  47. package/src/implementations/types.ts +0 -75
  48. package/src/index.test.ts +0 -1194
  49. package/src/index.ts +0 -435
  50. package/src/schema/compute-schema.test.ts +0 -128
  51. package/src/schema/compute-schema.ts +0 -67
  52. package/src/schema/extract-json-schema.test.ts +0 -25
  53. package/src/schema/extract-json-schema.ts +0 -15
  54. package/src/schema/parser.test.ts +0 -182
  55. package/src/schema/parser.ts +0 -148
  56. package/src/schema/resolve-schema-lib.test.ts +0 -19
  57. package/src/schema/resolve-schema-lib.ts +0 -29
  58. package/src/schema/types.ts +0 -20
  59. package/src/stack-utils.test.ts +0 -94
  60. package/src/stack-utils.ts +0 -129
@@ -1,265 +0,0 @@
1
- import express from 'express'
2
- import { kebabCase } from 'es-toolkit/string'
3
- import { Procedures, TProcedureRegistration } from '../../../index.js'
4
- import {
5
- ExtractConfig,
6
- ExtractContext,
7
- ProceduresFactory,
8
- RPCConfig,
9
- RPCHttpRouteDoc,
10
- } from '../../types.js'
11
- import { castArray } from 'es-toolkit/compat'
12
- import { ExpressFactoryItem } from './types.js'
13
-
14
- export type { RPCConfig, RPCHttpRouteDoc }
15
-
16
- export type ExpressRPCAppBuilderConfig = {
17
- /**
18
- * An existing Express application instance to use.
19
- * When provided, ensure to set up necessary middleware (e.g., json/body parser) beforehand.
20
- * If not provided, a new instance will be created.
21
- */
22
- app?: express.Express
23
- /** Optional path prefix for all RPC routes. */
24
- pathPrefix?: string
25
- onRequestStart?: (req: express.Request) => void
26
- onRequestEnd?: (req: express.Request, res: express.Response) => void
27
- onSuccess?: (
28
- procedure: TProcedureRegistration,
29
- req: express.Request,
30
- res: express.Response
31
- ) => void
32
- /**
33
- * Error handler called when a procedure throws an error.
34
- * @param procedure
35
- * @param req
36
- * @param res
37
- * @param error
38
- */
39
- onError?: (
40
- procedure: TProcedureRegistration,
41
- req: express.Request,
42
- res: express.Response,
43
- error: Error
44
- ) => void
45
- }
46
-
47
- /**
48
- * Builder class for creating an Express application with RPC routes.
49
- *
50
- * Usage:
51
- * const PublicRPC = Procedures<PublicRPCContext, RPCConfig>()
52
- * const ProtectedRPC = Procedures<ProtectedRPCContext, RPCConfig>()
53
- *
54
- * const rpcApp = new ExpressRPCAppBuilder()
55
- * .register(PublicRPC, (req): Promise<PublicRPCContext> => { /* context resolution logic * / })
56
- * .register(ProtectedRPC, (req): Promise<ProtectedRPCContext> => { /* context resolution logic * / })
57
- * .build();
58
- *
59
- * const app = rpcApp.app; // Express application
60
- * const docs = rpcApp.docs; // RPC route documentation
61
- */
62
- export class ExpressRPCAppBuilder {
63
- /**
64
- * Constructor for ExpressRPCAppBuilder.
65
- *
66
- * @param config
67
- */
68
- constructor(readonly config?: ExpressRPCAppBuilderConfig) {
69
- if (config?.app) {
70
- this._app = config.app
71
- } else {
72
- // Default middleware if no app is provided
73
- this._app.use(express.json())
74
- }
75
-
76
- if (config?.onRequestStart) {
77
- this._app.use((req, res, next) => {
78
- config.onRequestStart!(req)
79
- next()
80
- })
81
- }
82
-
83
- if (config?.onRequestEnd) {
84
- this._app.use((req, res, next) => {
85
- res.on('finish', () => {
86
- config.onRequestEnd!(req, res)
87
- })
88
- next()
89
- })
90
- }
91
- }
92
-
93
- /**
94
- * Generates the RPC route path based on the RPC configuration.
95
- * The RPCConfig name can be a string or an array of strings to form nested paths.
96
- *
97
- * Example
98
- * name: ['string', 'string-string', 'string']
99
- * path: /string/string-string/string/version
100
- * @param config
101
- */
102
- static makeRPCHttpRoutePath({
103
- name,
104
- config,
105
- prefix,
106
- }: {
107
- name: string
108
- prefix?: string
109
- config: RPCConfig
110
- }) {
111
- const normalizedPrefix = prefix ? (prefix.startsWith('/') ? prefix : `/${prefix}`) : ''
112
-
113
- return `${normalizedPrefix}/${castArray(config.scope).map(kebabCase).join('/')}/${kebabCase(name)}/${String(config.version).trim()}`
114
- }
115
-
116
- /**
117
- * Instance method wrapper for makeRPCHttpRoutePath that uses the builder's pathPrefix.
118
- * @param config - The RPC configuration
119
- */
120
- makeRPCHttpRoutePath(name: string, config: RPCConfig): string {
121
- return ExpressRPCAppBuilder.makeRPCHttpRoutePath({
122
- name,
123
- config,
124
- prefix: this.config?.pathPrefix,
125
- })
126
- }
127
-
128
- private factories: ExpressFactoryItem<any>[] = []
129
-
130
- private _app: express.Express = express()
131
- private _docs: (RPCHttpRouteDoc & object)[] = []
132
-
133
- get app(): express.Express {
134
- return this._app
135
- }
136
-
137
- get docs(): RPCHttpRouteDoc[] {
138
- return this._docs
139
- }
140
-
141
- /**
142
- * Registers a procedure factory with its context.
143
- * @param factory - The procedure factory created by Procedures<Context, RPCConfig>()
144
- * @param factoryContext - The context for procedure handlers. Can be a direct value,
145
- * a sync function (req) => Context, or an async function (req) => Promise<Context>
146
- * @param extendProcedureDoc - A custom function to extend the generated RPC route documentation for each procedure.
147
- */
148
- register<TFactory extends ProceduresFactory>(
149
- factory: TFactory,
150
- factoryContext:
151
- | ExtractContext<TFactory>
152
- | ((req: express.Request) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>),
153
- extendProcedureDoc?: (params: {
154
- /* RPC App builder base http route doc */
155
- base: RPCHttpRouteDoc
156
- /* Procedure registration */
157
- procedure: TProcedureRegistration<any, ExtractConfig<TFactory>>
158
- }) => Record<string, any>
159
- ): this {
160
- this.factories.push({ factory, factoryContext, extendProcedureDoc } as ExpressFactoryItem<any>)
161
- return this
162
- }
163
-
164
- /**
165
- * Builds and returns the Express application with registered RPC routes.
166
- * @return express.Application
167
- */
168
- build(): express.Application {
169
- this.factories.forEach(({ factory, factoryContext, extendProcedureDoc }) => {
170
- factory.getProcedures().map((procedure: TProcedureRegistration<any, RPCConfig>) => {
171
- const route = this.buildRpcHttpRouteDoc(procedure, extendProcedureDoc)
172
-
173
- this._docs.push(route)
174
-
175
- this._app[route.method](route.path, async (req, res) => {
176
- try {
177
- const context =
178
- typeof factoryContext === 'function'
179
- ? await factoryContext(req)
180
- : (factoryContext as ExtractContext<typeof factory>)
181
-
182
- let ac: AbortController | undefined
183
- const ctxWithSignal = Object.defineProperty({ ...context }, 'signal', {
184
- get() {
185
- if (!ac) {
186
- ac = new AbortController()
187
- req.on('close', () => { if (!res.writableFinished) ac!.abort() })
188
- }
189
- return ac.signal
190
- },
191
- enumerable: true,
192
- })
193
-
194
- res.json(await procedure.handler(ctxWithSignal, req.body))
195
- if (this.config?.onSuccess) {
196
- this.config.onSuccess(procedure, req, res)
197
- }
198
- // if status not set, set to 200
199
- if (!res.status) {
200
- res.status(200)
201
- }
202
- } catch (error) {
203
- if (this.config?.onError) {
204
- this.config.onError(procedure, req, res, error as Error)
205
- return
206
- }
207
- if (!res.status) {
208
- res.status(500)
209
- }
210
- // if no res.json set, set default error message
211
- if (!res.headersSent) {
212
- res.json({ error: (error as Error).message })
213
- }
214
- }
215
- })
216
- })
217
- })
218
-
219
- return this._app
220
- }
221
-
222
- /**
223
- * Generates the RPC HTTP route for the given procedure.
224
- * @param procedure
225
- */
226
- private buildRpcHttpRouteDoc(
227
- procedure: TProcedureRegistration<any, RPCConfig>,
228
- extendProcedureDoc: ExpressFactoryItem['extendProcedureDoc']
229
- ): RPCHttpRouteDoc {
230
- const { config } = procedure
231
- const path = ExpressRPCAppBuilder.makeRPCHttpRoutePath({
232
- name: procedure.name,
233
- config,
234
- prefix: this.config?.pathPrefix,
235
- })
236
- const method = 'post' as const // RPCs use POST method
237
- const jsonSchema: { body?: Record<string, unknown>; response?: Record<string, unknown> } = {}
238
-
239
- if (config.schema?.params) {
240
- jsonSchema.body = config.schema.params
241
- }
242
- if (config.schema?.returnType) {
243
- jsonSchema.response = config.schema.returnType
244
- }
245
-
246
- const base = {
247
- name: procedure.name,
248
- version: config.version,
249
- scope: config.scope,
250
- path,
251
- method,
252
- jsonSchema,
253
- }
254
- let extendedDoc: object = {}
255
-
256
- if (extendProcedureDoc) {
257
- extendedDoc = extendProcedureDoc({ base, procedure })
258
- }
259
-
260
- return {
261
- ...extendedDoc,
262
- ...base,
263
- }
264
- }
265
- }
@@ -1,16 +0,0 @@
1
- import { ExtractConfig, ExtractContext, RPCConfig, RPCHttpRouteDoc } from '../../types.js'
2
- import { Procedures, TProcedureRegistration } from '../../../index.js'
3
- import express from 'express'
4
-
5
- export type ExpressFactoryItem<TFactory = ReturnType<typeof Procedures<any, RPCConfig>>> = {
6
- factory: TFactory
7
- factoryContext:
8
- | ExtractContext<TFactory>
9
- | ((req: express.Request) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>)
10
- extendProcedureDoc?: (params: {
11
- /* RPC App builder base http route doc */
12
- base: RPCHttpRouteDoc
13
- /* Procedure registration */
14
- procedure: TProcedureRegistration<any, ExtractConfig<TFactory>>
15
- }) => Record<string, any>
16
- }
@@ -1,358 +0,0 @@
1
- # HonoRPCAppBuilder
2
-
3
- Hono integration for `ts-procedures` that creates type-safe RPC endpoints as POST routes. Works with Bun, Deno, Cloudflare Workers, and Node.js.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install ts-procedures hono
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ```typescript
14
- import { Hono } from 'hono'
15
- import { Procedures } from 'ts-procedures'
16
- import { HonoRPCAppBuilder, RPCConfig } from 'ts-procedures/hono-rpc'
17
- import { v } from 'suretype'
18
-
19
- // Define your context type
20
- type AppContext = { userId: string }
21
-
22
- // Create a procedure factory
23
- const RPC = Procedures<AppContext, RPCConfig>()
24
-
25
- // Define procedures
26
- RPC.Create(
27
- 'GetUser',
28
- {
29
- scope: ['users', 'profile'],
30
- version: 1,
31
- schema: {
32
- params: v.object({ id: v.string() }),
33
- returnType: v.object({ id: v.string(), name: v.string() }),
34
- },
35
- },
36
- async (ctx, params) => {
37
- return { id: params.id, name: 'John Doe' }
38
- }
39
- )
40
-
41
- // Build the Hono app
42
- const builder = new HonoRPCAppBuilder({ pathPrefix: '/rpc' }).register(RPC, (c) => ({
43
- userId: c.req.header('x-user-id') || 'anonymous',
44
- }))
45
-
46
- const app = builder.build()
47
-
48
- // Bun
49
- export default app
50
-
51
- // Node.js
52
- // import { serve } from '@hono/node-server'
53
- // serve(app)
54
-
55
- // POST /rpc/users/profile/get-user/1 → { id: "123", name: "John Doe" }
56
- ```
57
-
58
- ## Configuration
59
-
60
- ```typescript
61
- type HonoRPCAppBuilderConfig = {
62
- app?: Hono // Existing Hono app (optional)
63
- pathPrefix?: string // Prefix for all routes (e.g., '/rpc/v1')
64
- onRequestStart?: (c: Context) => void
65
- onRequestEnd?: (c: Context) => void
66
- onSuccess?: (procedure: TProcedureRegistration, c: Context) => void
67
- error?: (
68
- procedure: TProcedureRegistration,
69
- c: Context,
70
- error: Error
71
- ) => Response | Promise<Response>
72
- }
73
- ```
74
-
75
- | Option | Type | Description |
76
- | ---------------- | ---------------------------- | ------------------------------------------------- |
77
- | `app` | `Hono` | Use existing Hono app instead of creating new one |
78
- | `pathPrefix` | `string` | Prefix all routes (e.g., `/rpc/v1`) |
79
- | `onRequestStart` | `(c) => void` | Called at start of each request |
80
- | `onRequestEnd` | `(c) => void` | Called after handler completes |
81
- | `onSuccess` | `(proc, c) => void` | Called on successful handler execution |
82
- | `error` | `(proc, c, err) => Response` | Custom error handler (must return Response) |
83
-
84
- ## Context Resolution
85
-
86
- The context resolver receives the Hono `Context` object:
87
-
88
- ```typescript
89
- builder.register(RPC, (c: Context) => ({
90
- userId: c.req.header('x-user-id') || 'anonymous',
91
- userAgent: c.req.header('user-agent'),
92
- ip: c.req.raw.headers.get('cf-connecting-ip'), // Cloudflare
93
- }))
94
-
95
- // Async context resolution
96
- builder.register(RPC, async (c) => {
97
- const token = c.req.header('authorization')?.replace('Bearer ', '')
98
- const user = await verifyToken(token)
99
- return { userId: user.id, roles: user.roles }
100
- })
101
- ```
102
-
103
- ## Extending Procedure Documentation
104
-
105
- The `register` method accepts an optional third parameter `extendProcedureDoc` that allows you to add custom fields to each procedure's documentation. This is useful for adding metadata like descriptions, tags, or custom fields for API documentation generators.
106
-
107
- ```typescript
108
- // Example of a factory extending the procedure config:
109
- type ExtendedRPCConfig = {
110
- description: string
111
- tags: string[]
112
- }
113
-
114
- builder.register(RPC, (c) => ({ userId: c.req.header('x-user-id') || 'anonymous' }), {
115
- extendProcedureDoc: ({ base, procedure }, { base: RPCHttpRouteDoc, procedure }) =>
116
- ({
117
- description: `Procedure: ${procedure.name}`,
118
- tags: Array.isArray(procedure.config.scope)
119
- ? procedure.config.scope
120
- : [procedure.config.scope],
121
- }),
122
- })
123
-
124
- // Access extended docs after build()
125
- const app = builder.build()
126
- console.log(builder.docs) // Each doc now includes description and tags
127
- ```
128
-
129
- The `extendProcedureDoc` callback receives:
130
-
131
- | Parameter | Type | Description |
132
- | ----------- | ------------------------ | ------------------------------------------------------------------------------------------ |
133
- | `base` | `RPCHttpRouteDoc` | The base documentation with `name`, `path`, `method`, `scope`, `version`, and `jsonSchema` |
134
- | `procedure` | `TProcedureRegistration` | The full procedure registration including `name`, `config`, and `handler` |
135
-
136
- This allows you to derive documentation fields from procedure config or add static metadata per factory registration.
137
-
138
- ## Abort Signal
139
-
140
- `HonoRPCAppBuilder` automatically injects the HTTP request's `AbortSignal` (`c.req.raw.signal`) into the handler context. When a client disconnects mid-request, `ctx.signal` aborts, cancelling any signal-aware operations:
141
-
142
- ```typescript
143
- RPC.Create(
144
- 'SlowQuery',
145
- { scope: 'data', version: 1 },
146
- async (ctx, params) => {
147
- // Automatically cancelled if client disconnects
148
- const response = await fetch('https://slow-api.example.com/data', {
149
- signal: ctx.signal,
150
- })
151
- return response.json()
152
- }
153
- )
154
- ```
155
-
156
- To use `ctx.signal` with type safety, include `signal: AbortSignal` in your context type:
157
-
158
- ```typescript
159
- type AppContext = { userId: string; signal: AbortSignal }
160
- const RPC = Procedures<AppContext, RPCConfig>()
161
- ```
162
-
163
- ## Error Handling
164
-
165
- Custom error handler receives the procedure, context, and error. **Must return a Response:**
166
-
167
- ```typescript
168
- const builder = new HonoRPCAppBuilder({
169
- onError: (procedure, c, error) => {
170
- console.error(`Error in ${procedure.name}:`, error)
171
-
172
- if (error instanceof ValidationError) {
173
- return c.json({ error: error.message, code: 'VALIDATION_ERROR' }, 400)
174
- }
175
-
176
- if (error instanceof AuthError) {
177
- return c.json({ error: 'Unauthorized', code: 'AUTH_ERROR' }, 401)
178
- }
179
-
180
- return c.json({ error: 'Internal server error' }, 500)
181
- },
182
- })
183
- ```
184
-
185
- **Default error handling:** Returns `{ error: message }` with status 500.
186
-
187
- ## Using Existing Hono App
188
-
189
- You can add RPC routes to an existing Hono application:
190
-
191
- ```typescript
192
- const app = new Hono()
193
-
194
- // Add custom middleware and routes
195
- app.use('*', cors())
196
- app.get('/custom', (c) => c.json({ custom: true }))
197
-
198
- const builder = new HonoRPCAppBuilder({ app }).register(RPC, contextResolver)
199
-
200
- builder.build() // Adds RPC routes to existing app
201
- ```
202
-
203
- ## Runtime Compatibility
204
-
205
- HonoRPCAppBuilder works across all Hono-supported runtimes:
206
-
207
- ### Bun
208
-
209
- ```typescript
210
- const app = builder.build()
211
- export default app
212
- ```
213
-
214
- ### Node.js
215
-
216
- ```typescript
217
- import { serve } from '@hono/node-server'
218
-
219
- const app = builder.build()
220
- serve(app)
221
- ```
222
-
223
- ### Deno
224
-
225
- ```typescript
226
- import { serve } from 'https://deno.land/std/http/server.ts'
227
-
228
- const app = builder.build()
229
- serve(app.fetch)
230
- ```
231
-
232
- ### Cloudflare Workers
233
-
234
- ```typescript
235
- const app = builder.build()
236
- export default app
237
- ```
238
-
239
- ## API Reference
240
-
241
- ### Constructor
242
-
243
- ```typescript
244
- new HonoRPCAppBuilder(config?: HonoRPCAppBuilderConfig)
245
- ```
246
-
247
- ### Methods
248
-
249
- | Method | Signature | Description |
250
- | ---------------------- | ------------------------------------------------- | ------------------------------------------------------------------ |
251
- | `register` | `register<T>(factory, context, options?): this` | Register procedure factory with context and optional doc extension |
252
- | `build` | `build(): Hono` | Build routes and return app |
253
- | `makeRPCHttpRoutePath` | `makeRPCHttpRoutePath(config: RPCConfig): string` | Generate route path |
254
-
255
- ### Static Methods
256
-
257
- | Method | Signature | Description |
258
- | ---------------------- | --------------------------------------------------------- | -------------------------------------- |
259
- | `makeRPCHttpRoutePath` | `static makeRPCHttpRoutePath({ config, prefix }): string` | Generate route path with custom prefix |
260
-
261
- ### Properties
262
-
263
- | Property | Type | Description |
264
- | -------- | ------------------------- | ------------------------------------- |
265
- | `app` | `Hono` | The Hono application instance |
266
- | `docs` | `RPCHttpRouteDoc[]` | Route documentation (after `build()`) |
267
- | `config` | `HonoRPCAppBuilderConfig` | The configuration object |
268
-
269
- ## TypeScript Types
270
-
271
- ```typescript
272
- import {
273
- HonoRPCAppBuilder,
274
- HonoRPCAppBuilderConfig,
275
- RPCConfig,
276
- RPCHttpRouteDoc,
277
- } from 'ts-procedures/hono-rpc'
278
- ```
279
-
280
- ## Full Example
281
-
282
- ```typescript
283
- import { Hono, Context } from 'hono'
284
- import { Procedures } from 'ts-procedures'
285
- import { HonoRPCAppBuilder, RPCConfig } from 'ts-procedures/hono-rpc'
286
- import { v } from 'suretype'
287
-
288
- // Context types
289
- type PublicContext = { source: 'public' }
290
- type AuthContext = { source: 'auth'; userId: string }
291
-
292
- // Create factories
293
- const PublicRPC = Procedures<PublicContext, RPCConfig>()
294
- const AuthRPC = Procedures<AuthContext, RPCConfig>()
295
-
296
- // Public procedures
297
- PublicRPC.Create('HealthCheck', { scope: 'health', version: 1 }, async () => ({
298
- status: 'ok',
299
- }))
300
-
301
- PublicRPC.Create('GetVersion', { scope: ['system', 'version'], version: 1 }, async () => ({
302
- version: '1.0.0',
303
- }))
304
-
305
- // Authenticated procedures
306
- AuthRPC.Create(
307
- 'GetProfile',
308
- {
309
- scope: ['users', 'profile'],
310
- version: 1,
311
- schema: { returnType: v.object({ userId: v.string() }) },
312
- },
313
- async (ctx) => ({ userId: ctx.userId })
314
- )
315
-
316
- AuthRPC.Create(
317
- 'UpdateProfile',
318
- {
319
- scope: ['users', 'profile'],
320
- version: 2,
321
- schema: { params: v.object({ name: v.string() }) },
322
- },
323
- async (ctx, params) => ({ userId: ctx.userId, name: params.name })
324
- )
325
-
326
- // Build app
327
- const builder = new HonoRPCAppBuilder({
328
- pathPrefix: '/rpc',
329
- onRequestStart: (c) => console.log(`→ ${c.req.method} ${c.req.path}`),
330
- onRequestEnd: (c) => console.log(`← completed`),
331
- onSuccess: (proc) => console.log(`✓ ${proc.name}`),
332
- onError: (proc, c, err) => {
333
- console.error(`✗ ${proc.name}:`, err.message)
334
- return c.json({ error: err.message }, 500)
335
- },
336
- })
337
-
338
- builder
339
- .register(PublicRPC, () => ({ source: 'public' as const }))
340
- .register(AuthRPC, (c) => ({
341
- source: 'auth' as const,
342
- userId: c.req.header('x-user-id') || 'anonymous',
343
- }))
344
-
345
- const app = builder.build()
346
-
347
- // Generated routes:
348
- // POST /rpc/health/1
349
- // POST /rpc/system/version/get-version/1
350
- // POST /rpc/users/profile/get-user/1
351
- // POST /rpc/users/profile/get-user/2
352
-
353
- console.log(
354
- 'Routes:',
355
- builder.docs.map((d) => d.path)
356
- )
357
- export default app
358
- ```