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,281 +0,0 @@
1
- # ExpressRPCAppBuilder
2
-
3
- Express.js integration for `ts-procedures` that creates type-safe RPC endpoints as POST routes.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install ts-procedures express
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ```typescript
14
- import express from 'express'
15
- import { Procedures } from 'ts-procedures'
16
- import { ExpressRPCAppBuilder, RPCConfig } from 'ts-procedures/express-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 Express app
42
- const builder = new ExpressRPCAppBuilder({ pathPrefix: '/rpc' })
43
- .register(RPC, (req) => ({
44
- userId: req.headers['x-user-id'] as string
45
- }))
46
-
47
- const app = builder.build()
48
- app.listen(3000)
49
-
50
- // POST /rpc/users/profile/get-user/1 → { id: "123", name: "John Doe" }
51
- ```
52
-
53
- ## Configuration
54
-
55
- ```typescript
56
- type ExpressRPCAppBuilderConfig = {
57
- app?: express.Express // Existing Express app (optional)
58
- pathPrefix?: string // Prefix for all routes (e.g., '/rpc/v1')
59
- onRequestStart?: (req: express.Request) => void
60
- onRequestEnd?: (req: express.Request, res: express.Response) => void
61
- onSuccess?: (procedure: TProcedureRegistration, req: express.Request, res: express.Response) => void
62
- error?: (procedure: TProcedureRegistration, req: express.Request, res: express.Response, error: Error) => void
63
- }
64
- ```
65
-
66
- | Option | Type | Description |
67
- |--------|------|------------------------------------------------------|
68
- | `app` | `express.Express` | Use existing Express app instead of creating new one |
69
- | `pathPrefix` | `string` | Prefix all routes (e.g., `/rpc/v1`) |
70
- | `onRequestStart` | `(req) => void` | Called at start of each request |
71
- | `onRequestEnd` | `(req, res) => void` | Called after response finishes |
72
- | `onSuccess` | `(proc, req, res) => void` | Called on successful handler execution |
73
- | `error` | `(proc, req, res, err) => void` | Custom error handler |
74
-
75
- ## Context Resolution
76
-
77
- The context resolver receives the Express `Request` object:
78
-
79
- ```typescript
80
- builder.register(RPC, (req: express.Request) => ({
81
- userId: req.headers['x-user-id'] as string,
82
- sessionId: req.cookies?.sessionId,
83
- ip: req.ip
84
- }))
85
-
86
- // Async context resolution
87
- builder.register(RPC, async (req) => {
88
- const token = req.headers.authorization?.replace('Bearer ', '')
89
- const user = await verifyToken(token)
90
- return { userId: user.id, roles: user.roles }
91
- })
92
- ```
93
-
94
- ## Abort Signal
95
-
96
- `ExpressRPCAppBuilder` provides a lazy `AbortSignal` on `ctx.signal`. The underlying `AbortController` and `req.on('close')` listener are only created when `ctx.signal` is first accessed, so handlers that don't use it pay no overhead.
97
-
98
- The signal aborts when the client disconnects before the response finishes (premature close). Normal response completion does not trigger an abort.
99
-
100
- ```typescript
101
- RPC.Create(
102
- 'SlowQuery',
103
- { scope: 'data', version: 1 },
104
- async (ctx, params) => {
105
- // Automatically cancelled if client disconnects
106
- const response = await fetch('https://slow-api.example.com/data', {
107
- signal: ctx.signal,
108
- })
109
- return response.json()
110
- }
111
- )
112
- ```
113
-
114
- To use `ctx.signal` with type safety, include `signal: AbortSignal` in your context type:
115
-
116
- ```typescript
117
- type AppContext = { userId: string; signal: AbortSignal }
118
- const RPC = Procedures<AppContext, RPCConfig>()
119
- ```
120
-
121
- ## Error Handling
122
-
123
- Custom error handler receives the procedure, request, response, and error:
124
-
125
- ```typescript
126
- const builder = new ExpressRPCAppBuilder({
127
- onError: (procedure, req, res, error) => {
128
- console.error(`Error in ${procedure.name}:`, error)
129
-
130
- if (error instanceof ValidationError) {
131
- res.status(400).json({ error: error.message, code: 'VALIDATION_ERROR' })
132
- return
133
- }
134
-
135
- if (error instanceof AuthError) {
136
- res.status(401).json({ error: 'Unauthorized', code: 'AUTH_ERROR' })
137
- return
138
- }
139
-
140
- res.status(500).json({ error: 'Internal server error' })
141
- }
142
- })
143
- ```
144
-
145
- **Default error handling:** Returns `{ error: message }` with status 500.
146
-
147
- ## Using Existing Express App
148
-
149
- When providing an existing Express app, **you must set up JSON parsing middleware**:
150
-
151
- ```typescript
152
- const app = express()
153
- app.use(express.json()) // Required!
154
- app.use(cors())
155
- app.use(helmet())
156
-
157
- const builder = new ExpressRPCAppBuilder({ app })
158
- .register(RPC, contextResolver)
159
-
160
- builder.build() // Adds RPC routes to existing app
161
- ```
162
-
163
- When no `app` is provided, `express.json()` middleware is added automatically.
164
-
165
- ## API Reference
166
-
167
- ### Constructor
168
-
169
- ```typescript
170
- new ExpressRPCAppBuilder(config?: ExpressRPCAppBuilderConfig)
171
- ```
172
-
173
- ### Methods
174
-
175
- | Method | Signature | Description |
176
- |--------|-----------|-------------|
177
- | `register` | `register<T>(factory, context): this` | Register procedure factory with context |
178
- | `build` | `build(): express.Application` | Build routes and return app |
179
- | `makeRPCHttpRoutePath` | `makeRPCHttpRoutePath(config: RPCConfig): string` | Generate route path |
180
-
181
- ### Static Methods
182
-
183
- | Method | Signature | Description |
184
- |--------|-----------|-------------|
185
- | `makeRPCHttpRoutePath` | `static makeRPCHttpRoutePath({ config, prefix }): string` | Generate route path with custom prefix |
186
-
187
- ### Properties
188
-
189
- | Property | Type | Description |
190
- |----------|------|-------------|
191
- | `app` | `express.Express` | The Express application instance |
192
- | `docs` | `RPCHttpRouteDoc[]` | Route documentation (after `build()`) |
193
- | `config` | `ExpressRPCAppBuilderConfig` | The configuration object |
194
-
195
- ## TypeScript Types
196
-
197
- ```typescript
198
- import {
199
- ExpressRPCAppBuilder,
200
- ExpressRPCAppBuilderConfig,
201
- RPCConfig,
202
- RPCHttpRouteDoc
203
- } from 'ts-procedures/express-rpc'
204
- ```
205
-
206
- ## Full Example
207
-
208
- ```typescript
209
- import express from 'express'
210
- import { Procedures } from 'ts-procedures'
211
- import { ExpressRPCAppBuilder, RPCConfig } from 'ts-procedures/express-rpc'
212
- import { v } from 'suretype'
213
-
214
- // Context types
215
- type PublicContext = { source: 'public' }
216
- type AuthContext = { source: 'auth'; userId: string }
217
-
218
- // Create factories
219
- const PublicRPC = Procedures<PublicContext, RPCConfig>()
220
- const AuthRPC = Procedures<AuthContext, RPCConfig>()
221
-
222
- // Public procedures
223
- PublicRPC.Create('HealthCheck', { scope: 'health', version: 1 }, async () => ({
224
- status: 'ok'
225
- }))
226
-
227
- PublicRPC.Create('GetVersion', { scope: ['system', 'version'], version: 1 }, async () => ({
228
- version: '1.0.0'
229
- }))
230
-
231
- // Authenticated procedures
232
- AuthRPC.Create(
233
- 'GetProfile',
234
- {
235
- scope: ['users', 'profile'],
236
- version: 1,
237
- schema: { returnType: v.object({ userId: v.string() }) }
238
- },
239
- async (ctx) => ({ userId: ctx.userId })
240
- )
241
-
242
- AuthRPC.Create(
243
- 'UpdateProfile',
244
- {
245
- scope: ['users', 'profile'],
246
- version: 2,
247
- schema: { params: v.object({ name: v.string() }) }
248
- },
249
- async (ctx, params) => ({ userId: ctx.userId, name: params.name })
250
- )
251
-
252
- // Build app
253
- const builder = new ExpressRPCAppBuilder({
254
- pathPrefix: '/rpc',
255
- onRequestStart: (req) => console.log(`→ ${req.method} ${req.path}`),
256
- onRequestEnd: (req, res) => console.log(`← ${res.statusCode}`),
257
- onSuccess: (proc) => console.log(`✓ ${proc.name}`),
258
- onError: (proc, req, res, err) => {
259
- console.error(`✗ ${proc.name}:`, err.message)
260
- res.status(500).json({ error: err.message })
261
- }
262
- })
263
-
264
- builder
265
- .register(PublicRPC, () => ({ source: 'public' as const }))
266
- .register(AuthRPC, (req) => ({
267
- source: 'auth' as const,
268
- userId: req.headers['x-user-id'] as string || 'anonymous'
269
- }))
270
-
271
- const app = builder.build()
272
-
273
- // Generated routes:
274
- // POST /rpc/health/1
275
- // POST /rpc/system/version/get-version/1
276
- // POST /rpc/users/profile/get-user/1
277
- // POST /rpc/users/profile/get-user/2
278
-
279
- console.log('Routes:', builder.docs.map(d => d.path))
280
- app.listen(3000)
281
- ```