ts-procedures 3.0.1 → 3.1.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/build/implementations/http/express-rpc/index.d.ts +14 -4
- package/build/implementations/http/express-rpc/index.js +20 -8
- package/build/implementations/http/express-rpc/index.js.map +1 -1
- package/build/implementations/http/express-rpc/index.test.js +162 -3
- package/build/implementations/http/express-rpc/index.test.js.map +1 -1
- package/build/implementations/http/express-rpc/types.d.ts +6 -23
- package/build/implementations/http/hono-rpc/index.d.ts +13 -4
- package/build/implementations/http/hono-rpc/index.js +20 -8
- package/build/implementations/http/hono-rpc/index.js.map +1 -1
- package/build/implementations/http/hono-rpc/index.test.js +158 -2
- package/build/implementations/http/hono-rpc/index.test.js.map +1 -1
- package/build/implementations/http/hono-rpc/types.d.ts +6 -23
- package/build/implementations/types.d.ts +32 -1
- package/package.json +1 -1
- package/src/implementations/http/express-rpc/README.md +2 -2
- package/src/implementations/http/express-rpc/index.test.ts +234 -3
- package/src/implementations/http/express-rpc/index.ts +49 -13
- package/src/implementations/http/express-rpc/types.ts +8 -25
- package/src/implementations/http/hono-rpc/README.md +84 -44
- package/src/implementations/http/hono-rpc/index.test.ts +230 -2
- package/src/implementations/http/hono-rpc/index.ts +49 -14
- package/src/implementations/http/hono-rpc/types.ts +8 -25
- package/src/implementations/types.ts +39 -1
|
@@ -251,7 +251,7 @@ describe('ExpressRPCAppBuilder', () => {
|
|
|
251
251
|
res.status(400).json({ customError: error.message })
|
|
252
252
|
})
|
|
253
253
|
|
|
254
|
-
const builder = new ExpressRPCAppBuilder({
|
|
254
|
+
const builder = new ExpressRPCAppBuilder({ onError: errorHandler })
|
|
255
255
|
const RPC = Procedures<{}, RPCConfig>()
|
|
256
256
|
|
|
257
257
|
RPC.Create('Test', { scope: 'test', version: 1 }, async () => {
|
|
@@ -407,7 +407,10 @@ describe('ExpressRPCAppBuilder', () => {
|
|
|
407
407
|
builder.register(RPC, factoryContext)
|
|
408
408
|
const app = builder.build()
|
|
409
409
|
|
|
410
|
-
await request(app)
|
|
410
|
+
await request(app)
|
|
411
|
+
.post('/get-auth/get-auth/1')
|
|
412
|
+
.set('Authorization', 'Bearer token123')
|
|
413
|
+
.send({})
|
|
411
414
|
|
|
412
415
|
expect(factoryContext).toHaveBeenCalledTimes(1)
|
|
413
416
|
expect(factoryContext.mock.calls[0]![0]).toHaveProperty('headers')
|
|
@@ -515,7 +518,10 @@ describe('ExpressRPCAppBuilder', () => {
|
|
|
515
518
|
})
|
|
516
519
|
|
|
517
520
|
test("array scope with procedure name: ['users', 'profile'] + 'GetById' → /users/profile/get-by-id/1", () => {
|
|
518
|
-
const path = builder.makeRPCHttpRoutePath('GetById', {
|
|
521
|
+
const path = builder.makeRPCHttpRoutePath('GetById', {
|
|
522
|
+
scope: ['users', 'profile'],
|
|
523
|
+
version: 1,
|
|
524
|
+
})
|
|
519
525
|
expect(path).toBe('/users/profile/get-by-id/1')
|
|
520
526
|
})
|
|
521
527
|
|
|
@@ -620,6 +626,231 @@ describe('ExpressRPCAppBuilder', () => {
|
|
|
620
626
|
})
|
|
621
627
|
})
|
|
622
628
|
|
|
629
|
+
// --------------------------------------------------------------------------
|
|
630
|
+
// extendProcedureDoc Tests
|
|
631
|
+
// --------------------------------------------------------------------------
|
|
632
|
+
describe('extendProcedureDoc', () => {
|
|
633
|
+
test('adds custom properties to generated documentation', () => {
|
|
634
|
+
const builder = new ExpressRPCAppBuilder()
|
|
635
|
+
const RPC = Procedures<{}, RPCConfig>()
|
|
636
|
+
|
|
637
|
+
RPC.Create('GetUser', { scope: 'users', version: 1 }, async () => ({ name: 'test' }))
|
|
638
|
+
|
|
639
|
+
builder.register(
|
|
640
|
+
RPC,
|
|
641
|
+
() => ({}),
|
|
642
|
+
({ base, procedure }) => ({
|
|
643
|
+
summary: `Get user endpoint`,
|
|
644
|
+
tags: ['users'],
|
|
645
|
+
operationId: procedure.name,
|
|
646
|
+
})
|
|
647
|
+
)
|
|
648
|
+
builder.build()
|
|
649
|
+
|
|
650
|
+
const doc = builder.docs[0]!
|
|
651
|
+
expect(doc).toHaveProperty('summary', 'Get user endpoint')
|
|
652
|
+
expect(doc).toHaveProperty('tags', ['users'])
|
|
653
|
+
expect(doc).toHaveProperty('operationId', 'GetUser')
|
|
654
|
+
})
|
|
655
|
+
|
|
656
|
+
test('receives correct base and procedure parameters', () => {
|
|
657
|
+
const extendFn = vi.fn(() => ({}))
|
|
658
|
+
const builder = new ExpressRPCAppBuilder()
|
|
659
|
+
const RPC = Procedures<{}, RPCConfig>()
|
|
660
|
+
|
|
661
|
+
const paramsSchema = v.object({ id: v.string() })
|
|
662
|
+
RPC.Create(
|
|
663
|
+
'GetItem',
|
|
664
|
+
{ scope: 'items', version: 2, schema: { params: paramsSchema } },
|
|
665
|
+
async () => ({})
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
builder.register(RPC, () => ({}), extendFn)
|
|
669
|
+
builder.build()
|
|
670
|
+
|
|
671
|
+
expect(extendFn).toHaveBeenCalledTimes(1)
|
|
672
|
+
const callArg = extendFn.mock.calls[0]![0 as any] as any
|
|
673
|
+
|
|
674
|
+
// Verify base properties
|
|
675
|
+
expect(callArg.base).toHaveProperty('name', 'GetItem')
|
|
676
|
+
expect(callArg.base).toHaveProperty('version', 2)
|
|
677
|
+
expect(callArg.base).toHaveProperty('scope', 'items')
|
|
678
|
+
expect(callArg.base).toHaveProperty('path', '/items/get-item/2')
|
|
679
|
+
expect(callArg.base).toHaveProperty('method', 'post')
|
|
680
|
+
expect(callArg.base.jsonSchema).toHaveProperty('body')
|
|
681
|
+
|
|
682
|
+
// Verify procedure properties
|
|
683
|
+
expect(callArg.procedure).toHaveProperty('name', 'GetItem')
|
|
684
|
+
expect(callArg.procedure).toHaveProperty('handler')
|
|
685
|
+
expect(callArg.procedure.config).toHaveProperty('scope', 'items')
|
|
686
|
+
expect(callArg.procedure.config).toHaveProperty('version', 2)
|
|
687
|
+
})
|
|
688
|
+
|
|
689
|
+
test('base properties take precedence over extended properties', () => {
|
|
690
|
+
const builder = new ExpressRPCAppBuilder()
|
|
691
|
+
const RPC = Procedures<{}, RPCConfig>()
|
|
692
|
+
|
|
693
|
+
RPC.Create('Test', { scope: 'test', version: 1 }, async () => ({}))
|
|
694
|
+
|
|
695
|
+
builder.register(
|
|
696
|
+
RPC,
|
|
697
|
+
() => ({}),
|
|
698
|
+
() => ({
|
|
699
|
+
name: 'OverriddenName',
|
|
700
|
+
path: '/overridden/path',
|
|
701
|
+
method: 'get',
|
|
702
|
+
customField: 'custom-value',
|
|
703
|
+
})
|
|
704
|
+
)
|
|
705
|
+
builder.build()
|
|
706
|
+
|
|
707
|
+
const doc = builder.docs[0]!
|
|
708
|
+
// Base properties should NOT be overridden
|
|
709
|
+
expect(doc.name).toBe('Test')
|
|
710
|
+
expect(doc.path).toBe('/test/test/1')
|
|
711
|
+
expect(doc.method).toBe('post')
|
|
712
|
+
// Custom field should be present
|
|
713
|
+
expect(doc).toHaveProperty('customField', 'custom-value')
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
test('different factories can have different extendProcedureDoc functions', () => {
|
|
717
|
+
const builder = new ExpressRPCAppBuilder()
|
|
718
|
+
|
|
719
|
+
const PublicRPC = Procedures<{}, RPCConfig>()
|
|
720
|
+
const AdminRPC = Procedures<{}, RPCConfig>()
|
|
721
|
+
|
|
722
|
+
PublicRPC.Create('GetPublic', { scope: 'public', version: 1 }, async () => ({}))
|
|
723
|
+
AdminRPC.Create('GetAdmin', { scope: 'admin', version: 1 }, async () => ({}))
|
|
724
|
+
|
|
725
|
+
builder
|
|
726
|
+
.register(
|
|
727
|
+
PublicRPC,
|
|
728
|
+
() => ({}),
|
|
729
|
+
() => ({
|
|
730
|
+
security: [],
|
|
731
|
+
tags: ['public'],
|
|
732
|
+
})
|
|
733
|
+
)
|
|
734
|
+
.register(
|
|
735
|
+
AdminRPC,
|
|
736
|
+
() => ({}),
|
|
737
|
+
() => ({
|
|
738
|
+
security: [{ bearerAuth: [] }],
|
|
739
|
+
tags: ['admin'],
|
|
740
|
+
})
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
builder.build()
|
|
744
|
+
|
|
745
|
+
const publicDoc = builder.docs.find((d) => d.name === 'GetPublic')!
|
|
746
|
+
const adminDoc = builder.docs.find((d) => d.name === 'GetAdmin')!
|
|
747
|
+
|
|
748
|
+
expect(publicDoc).toHaveProperty('security', [])
|
|
749
|
+
expect(publicDoc).toHaveProperty('tags', ['public'])
|
|
750
|
+
expect(adminDoc).toHaveProperty('security', [{ bearerAuth: [] }])
|
|
751
|
+
expect(adminDoc).toHaveProperty('tags', ['admin'])
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
test('extendProcedureDoc is called for each procedure in factory', () => {
|
|
755
|
+
const extendFn = vi.fn(() => ({ extended: true }))
|
|
756
|
+
const builder = new ExpressRPCAppBuilder()
|
|
757
|
+
const RPC = Procedures<{}, RPCConfig>()
|
|
758
|
+
|
|
759
|
+
RPC.Create('Method1', { scope: 'm1', version: 1 }, async () => ({}))
|
|
760
|
+
RPC.Create('Method2', { scope: 'm2', version: 1 }, async () => ({}))
|
|
761
|
+
RPC.Create('Method3', { scope: 'm3', version: 1 }, async () => ({}))
|
|
762
|
+
|
|
763
|
+
builder.register(RPC, () => ({}), extendFn)
|
|
764
|
+
builder.build()
|
|
765
|
+
|
|
766
|
+
expect(extendFn).toHaveBeenCalledTimes(3)
|
|
767
|
+
|
|
768
|
+
const procedureNames = extendFn.mock.calls.map((call: any) => call[0].procedure.name)
|
|
769
|
+
expect(procedureNames).toContain('Method1')
|
|
770
|
+
expect(procedureNames).toContain('Method2')
|
|
771
|
+
expect(procedureNames).toContain('Method3')
|
|
772
|
+
})
|
|
773
|
+
|
|
774
|
+
test('not providing extendProcedureDoc results in base documentation only', () => {
|
|
775
|
+
const builder = new ExpressRPCAppBuilder()
|
|
776
|
+
const RPC = Procedures<{}, RPCConfig>()
|
|
777
|
+
|
|
778
|
+
RPC.Create('Test', { scope: 'test', version: 1 }, async () => ({}))
|
|
779
|
+
|
|
780
|
+
builder.register(RPC, () => ({})) // No extendProcedureDoc
|
|
781
|
+
builder.build()
|
|
782
|
+
|
|
783
|
+
const doc = builder.docs[0]!
|
|
784
|
+
expect(doc.name).toBe('Test')
|
|
785
|
+
expect(doc.path).toBe('/test/test/1')
|
|
786
|
+
expect(doc.method).toBe('post')
|
|
787
|
+
// Should not have any extra properties
|
|
788
|
+
expect(Object.keys(doc)).toEqual(['name', 'version', 'scope', 'path', 'method', 'jsonSchema'])
|
|
789
|
+
})
|
|
790
|
+
|
|
791
|
+
test('extendProcedureDoc can access procedure config for conditional logic', () => {
|
|
792
|
+
const builder = new ExpressRPCAppBuilder()
|
|
793
|
+
const RPC = Procedures<{}, RPCConfig>()
|
|
794
|
+
|
|
795
|
+
RPC.Create('PublicEndpoint', { scope: 'public', version: 1 }, async () => ({}))
|
|
796
|
+
RPC.Create('PrivateEndpoint', { scope: 'private', version: 1 }, async () => ({}))
|
|
797
|
+
|
|
798
|
+
builder.register(
|
|
799
|
+
RPC,
|
|
800
|
+
() => ({}),
|
|
801
|
+
({ procedure }) => ({
|
|
802
|
+
isPublic: procedure.config.scope === 'public',
|
|
803
|
+
description: `This is a ${procedure.config.scope} endpoint`,
|
|
804
|
+
})
|
|
805
|
+
)
|
|
806
|
+
builder.build()
|
|
807
|
+
|
|
808
|
+
const publicDoc = builder.docs.find((d) => d.name === 'PublicEndpoint')!
|
|
809
|
+
const privateDoc = builder.docs.find((d) => d.name === 'PrivateEndpoint')!
|
|
810
|
+
|
|
811
|
+
expect(publicDoc).toHaveProperty('isPublic', true)
|
|
812
|
+
expect(publicDoc).toHaveProperty('description', 'This is a public endpoint')
|
|
813
|
+
expect(privateDoc).toHaveProperty('isPublic', false)
|
|
814
|
+
expect(privateDoc).toHaveProperty('description', 'This is a private endpoint')
|
|
815
|
+
})
|
|
816
|
+
|
|
817
|
+
test('extendProcedureDoc can use base jsonSchema for OpenAPI-style docs', () => {
|
|
818
|
+
const builder = new ExpressRPCAppBuilder()
|
|
819
|
+
const RPC = Procedures<{}, RPCConfig>()
|
|
820
|
+
|
|
821
|
+
const paramsSchema = v.object({ userId: v.string() })
|
|
822
|
+
const returnSchema = v.object({ name: v.string(), email: v.string() })
|
|
823
|
+
|
|
824
|
+
RPC.Create(
|
|
825
|
+
'GetUser',
|
|
826
|
+
{ scope: 'users', version: 1, schema: { params: paramsSchema, returnType: returnSchema } },
|
|
827
|
+
async () => ({ name: 'test', email: 'test@example.com' })
|
|
828
|
+
)
|
|
829
|
+
|
|
830
|
+
builder.register(
|
|
831
|
+
RPC,
|
|
832
|
+
() => ({}),
|
|
833
|
+
({ base }) => ({
|
|
834
|
+
requestBody: base.jsonSchema.body
|
|
835
|
+
? { content: { 'application/json': { schema: base.jsonSchema.body } } }
|
|
836
|
+
: undefined,
|
|
837
|
+
responses: {
|
|
838
|
+
200: base.jsonSchema.response
|
|
839
|
+
? { content: { 'application/json': { schema: base.jsonSchema.response } } }
|
|
840
|
+
: { description: 'Success' },
|
|
841
|
+
},
|
|
842
|
+
})
|
|
843
|
+
)
|
|
844
|
+
builder.build()
|
|
845
|
+
|
|
846
|
+
const doc = builder.docs[0] as any
|
|
847
|
+
expect(doc).toHaveProperty('requestBody')
|
|
848
|
+
expect(doc.requestBody).toHaveProperty('content')
|
|
849
|
+
expect(doc).toHaveProperty('responses')
|
|
850
|
+
expect(doc.responses).toHaveProperty('200')
|
|
851
|
+
})
|
|
852
|
+
})
|
|
853
|
+
|
|
623
854
|
// --------------------------------------------------------------------------
|
|
624
855
|
// Integration Test
|
|
625
856
|
// --------------------------------------------------------------------------
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import express from 'express'
|
|
2
2
|
import { kebabCase } from 'es-toolkit/string'
|
|
3
3
|
import { Procedures, TProcedureRegistration } from '../../../index.js'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ExtractConfig,
|
|
6
|
+
ExtractContext,
|
|
7
|
+
ProceduresFactory,
|
|
8
|
+
RPCConfig,
|
|
9
|
+
RPCHttpRouteDoc,
|
|
10
|
+
} from '../../types.js'
|
|
5
11
|
import { castArray } from 'es-toolkit/compat'
|
|
6
|
-
import { ExpressFactoryItem
|
|
12
|
+
import { ExpressFactoryItem } from './types.js'
|
|
7
13
|
|
|
8
14
|
export type { RPCConfig, RPCHttpRouteDoc }
|
|
9
15
|
|
|
@@ -23,7 +29,14 @@ export type ExpressRPCAppBuilderConfig = {
|
|
|
23
29
|
req: express.Request,
|
|
24
30
|
res: express.Response
|
|
25
31
|
) => void
|
|
26
|
-
|
|
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?: (
|
|
27
40
|
procedure: TProcedureRegistration,
|
|
28
41
|
req: express.Request,
|
|
29
42
|
res: express.Response,
|
|
@@ -115,7 +128,7 @@ export class ExpressRPCAppBuilder {
|
|
|
115
128
|
private factories: ExpressFactoryItem<any>[] = []
|
|
116
129
|
|
|
117
130
|
private _app: express.Express = express()
|
|
118
|
-
private _docs: RPCHttpRouteDoc[] = []
|
|
131
|
+
private _docs: (RPCHttpRouteDoc & object)[] = []
|
|
119
132
|
|
|
120
133
|
get app(): express.Express {
|
|
121
134
|
return this._app
|
|
@@ -130,14 +143,21 @@ export class ExpressRPCAppBuilder {
|
|
|
130
143
|
* @param factory - The procedure factory created by Procedures<Context, RPCConfig>()
|
|
131
144
|
* @param factoryContext - The context for procedure handlers. Can be a direct value,
|
|
132
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.
|
|
133
147
|
*/
|
|
134
148
|
register<TFactory extends ProceduresFactory>(
|
|
135
149
|
factory: TFactory,
|
|
136
150
|
factoryContext:
|
|
137
151
|
| ExtractContext<TFactory>
|
|
138
|
-
| ((req: express.Request) => ExtractContext<TFactory> | Promise<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>
|
|
139
159
|
): this {
|
|
140
|
-
this.factories.push({ factory, factoryContext } as ExpressFactoryItem<any>)
|
|
160
|
+
this.factories.push({ factory, factoryContext, extendProcedureDoc } as ExpressFactoryItem<any>)
|
|
141
161
|
return this
|
|
142
162
|
}
|
|
143
163
|
|
|
@@ -146,9 +166,9 @@ export class ExpressRPCAppBuilder {
|
|
|
146
166
|
* @return express.Application
|
|
147
167
|
*/
|
|
148
168
|
build(): express.Application {
|
|
149
|
-
this.factories.forEach(({ factory, factoryContext }) => {
|
|
169
|
+
this.factories.forEach(({ factory, factoryContext, extendProcedureDoc }) => {
|
|
150
170
|
factory.getProcedures().map((procedure: TProcedureRegistration<any, RPCConfig>) => {
|
|
151
|
-
const route = this.buildRpcHttpRouteDoc(procedure)
|
|
171
|
+
const route = this.buildRpcHttpRouteDoc(procedure, extendProcedureDoc)
|
|
152
172
|
|
|
153
173
|
this._docs.push(route)
|
|
154
174
|
|
|
@@ -168,8 +188,8 @@ export class ExpressRPCAppBuilder {
|
|
|
168
188
|
res.status(200)
|
|
169
189
|
}
|
|
170
190
|
} catch (error) {
|
|
171
|
-
if (this.config?.
|
|
172
|
-
this.config.
|
|
191
|
+
if (this.config?.onError) {
|
|
192
|
+
this.config.onError(procedure, req, res, error as Error)
|
|
173
193
|
return
|
|
174
194
|
}
|
|
175
195
|
if (!res.status) {
|
|
@@ -191,14 +211,17 @@ export class ExpressRPCAppBuilder {
|
|
|
191
211
|
* Generates the RPC HTTP route for the given procedure.
|
|
192
212
|
* @param procedure
|
|
193
213
|
*/
|
|
194
|
-
private buildRpcHttpRouteDoc(
|
|
214
|
+
private buildRpcHttpRouteDoc(
|
|
215
|
+
procedure: TProcedureRegistration<any, RPCConfig>,
|
|
216
|
+
extendProcedureDoc: ExpressFactoryItem['extendProcedureDoc']
|
|
217
|
+
): RPCHttpRouteDoc {
|
|
195
218
|
const { config } = procedure
|
|
196
219
|
const path = ExpressRPCAppBuilder.makeRPCHttpRoutePath({
|
|
197
220
|
name: procedure.name,
|
|
198
221
|
config,
|
|
199
222
|
prefix: this.config?.pathPrefix,
|
|
200
223
|
})
|
|
201
|
-
const method = 'post' // RPCs use POST method
|
|
224
|
+
const method = 'post' as const // RPCs use POST method
|
|
202
225
|
const jsonSchema: { body?: object; response?: object } = {}
|
|
203
226
|
|
|
204
227
|
if (config.schema?.params) {
|
|
@@ -208,10 +231,23 @@ export class ExpressRPCAppBuilder {
|
|
|
208
231
|
jsonSchema.response = config.schema.returnType
|
|
209
232
|
}
|
|
210
233
|
|
|
211
|
-
|
|
234
|
+
const base = {
|
|
235
|
+
name: procedure.name,
|
|
236
|
+
version: config.version,
|
|
237
|
+
scope: config.scope,
|
|
212
238
|
path,
|
|
213
239
|
method,
|
|
214
240
|
jsonSchema,
|
|
215
241
|
}
|
|
242
|
+
let extendedDoc: object = {}
|
|
243
|
+
|
|
244
|
+
if (extendProcedureDoc) {
|
|
245
|
+
extendedDoc = extendProcedureDoc({ base, procedure })
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
...extendedDoc,
|
|
250
|
+
...base,
|
|
251
|
+
}
|
|
216
252
|
}
|
|
217
253
|
}
|
|
@@ -1,33 +1,16 @@
|
|
|
1
|
-
import { RPCConfig } from '../../types.js'
|
|
2
|
-
import { Procedures } from '../../../index.js'
|
|
1
|
+
import { ExtractConfig, ExtractContext, RPCConfig, RPCHttpRouteDoc } from '../../types.js'
|
|
2
|
+
import { Procedures, TProcedureRegistration } from '../../../index.js'
|
|
3
3
|
import express from 'express'
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Extracts the TContext type from a Procedures factory return type.
|
|
7
|
-
* Uses the first parameter of the handler function to infer the context type.
|
|
8
|
-
*/
|
|
9
|
-
export type ExtractContext<TFactory> = TFactory extends {
|
|
10
|
-
getProcedures: () => Array<{ handler: (ctx: infer TContext, ...args: any[]) => any }>
|
|
11
|
-
}
|
|
12
|
-
? TContext
|
|
13
|
-
: never
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Minimal structural type for a Procedures factory.
|
|
17
|
-
* Uses explicit `any` types to avoid variance issues with generic constraints.
|
|
18
|
-
*/
|
|
19
|
-
export type ProceduresFactory = {
|
|
20
|
-
getProcedures: () => Array<{
|
|
21
|
-
name: string
|
|
22
|
-
config: any
|
|
23
|
-
handler: (ctx: any, params?: any) => Promise<any>
|
|
24
|
-
}>
|
|
25
|
-
Create: (...args: any[]) => any
|
|
26
|
-
}
|
|
27
|
-
|
|
28
5
|
export type ExpressFactoryItem<TFactory = ReturnType<typeof Procedures<any, RPCConfig>>> = {
|
|
29
6
|
factory: TFactory
|
|
30
7
|
factoryContext:
|
|
31
8
|
| ExtractContext<TFactory>
|
|
32
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>
|
|
33
16
|
}
|