@dokploy/trpc-openapi 0.0.2 → 0.0.4

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 (68) hide show
  1. package/dist/generator/paths.d.ts.map +1 -1
  2. package/dist/generator/paths.js +2 -2
  3. package/dist/generator/paths.js.map +1 -1
  4. package/dist/generator/schema.d.ts.map +1 -1
  5. package/dist/generator/schema.js +3 -13
  6. package/dist/generator/schema.js.map +1 -1
  7. package/dist/types.d.ts +12 -10
  8. package/dist/types.d.ts.map +1 -1
  9. package/dist/utils/procedure.d.ts +4 -3
  10. package/dist/utils/procedure.d.ts.map +1 -1
  11. package/dist/utils/procedure.js +30 -7
  12. package/dist/utils/procedure.js.map +1 -1
  13. package/package.json +14 -9
  14. package/assets/trpc-openapi-graph.png +0 -0
  15. package/assets/trpc-openapi-readme.png +0 -0
  16. package/assets/trpc-openapi.svg +0 -4
  17. package/examples/with-express/README.md +0 -11
  18. package/examples/with-express/package.json +0 -28
  19. package/examples/with-express/src/database.ts +0 -67
  20. package/examples/with-express/src/index.ts +0 -27
  21. package/examples/with-express/src/openapi.ts +0 -13
  22. package/examples/with-express/src/router.ts +0 -424
  23. package/examples/with-express/tsconfig.json +0 -102
  24. package/examples/with-interop/README.md +0 -10
  25. package/examples/with-interop/package.json +0 -13
  26. package/examples/with-interop/src/index.ts +0 -17
  27. package/examples/with-interop/tsconfig.json +0 -103
  28. package/examples/with-nextjs/.eslintrc.json +0 -3
  29. package/examples/with-nextjs/README.md +0 -12
  30. package/examples/with-nextjs/next-env.d.ts +0 -5
  31. package/examples/with-nextjs/next.config.js +0 -6
  32. package/examples/with-nextjs/package.json +0 -33
  33. package/examples/with-nextjs/public/favicon.ico +0 -0
  34. package/examples/with-nextjs/src/pages/_app.tsx +0 -7
  35. package/examples/with-nextjs/src/pages/api/[...trpc].ts +0 -18
  36. package/examples/with-nextjs/src/pages/api/openapi.json.ts +0 -10
  37. package/examples/with-nextjs/src/pages/api/trpc/[...trpc].ts +0 -9
  38. package/examples/with-nextjs/src/pages/index.tsx +0 -12
  39. package/examples/with-nextjs/src/server/database.ts +0 -67
  40. package/examples/with-nextjs/src/server/openapi.ts +0 -13
  41. package/examples/with-nextjs/src/server/router.ts +0 -425
  42. package/examples/with-nextjs/tsconfig.json +0 -24
  43. package/jest.config.ts +0 -12
  44. package/pnpm-workspace.yaml +0 -7
  45. package/src/adapters/express.ts +0 -20
  46. package/src/adapters/index.ts +0 -3
  47. package/src/adapters/next.ts +0 -64
  48. package/src/adapters/node-http/core.ts +0 -203
  49. package/src/adapters/node-http/errors.ts +0 -45
  50. package/src/adapters/node-http/input.ts +0 -76
  51. package/src/adapters/node-http/procedures.ts +0 -64
  52. package/src/adapters/standalone.ts +0 -19
  53. package/src/generator/index.ts +0 -51
  54. package/src/generator/paths.ts +0 -127
  55. package/src/generator/schema.ts +0 -238
  56. package/src/index.ts +0 -42
  57. package/src/types.ts +0 -79
  58. package/src/utils/method.ts +0 -8
  59. package/src/utils/path.ts +0 -12
  60. package/src/utils/procedure.ts +0 -45
  61. package/src/utils/zod.ts +0 -115
  62. package/test/adapters/express.test.ts +0 -150
  63. package/test/adapters/next.test.ts +0 -162
  64. package/test/adapters/standalone.test.ts +0 -1335
  65. package/test/generator.test.ts +0 -2897
  66. package/tsconfig.build.json +0 -4
  67. package/tsconfig.eslint.json +0 -5
  68. package/tsconfig.json +0 -19
@@ -1,425 +0,0 @@
1
- import { TRPCError, initTRPC } from "@trpc/server";
2
- import { CreateNextContextOptions } from "@trpc/server/adapters/next";
3
- import jwt from "jsonwebtoken";
4
- import { OpenApiMeta } from "trpc-openapi";
5
- import { v4 as uuid } from "uuid";
6
- import { z } from "zod";
7
-
8
- import { Post, User, database } from "./database";
9
-
10
- const jwtSecret = uuid();
11
-
12
- export type Context = {
13
- user: User | null;
14
- requestId: string;
15
- };
16
-
17
- const t = initTRPC
18
- .context<Context>()
19
- .meta<OpenApiMeta>()
20
- .create({
21
- errorFormatter: ({ error, shape }) => {
22
- if (
23
- error.code === "INTERNAL_SERVER_ERROR" &&
24
- process.env.NODE_ENV === "production"
25
- ) {
26
- return { ...shape, message: "Internal server error" };
27
- }
28
- return shape;
29
- },
30
- });
31
-
32
- export const createContext = async ({
33
- req,
34
- res,
35
- }: CreateNextContextOptions): Promise<Context> => {
36
- const requestId = uuid();
37
- res.setHeader("x-request-id", requestId);
38
-
39
- let user: User | null = null;
40
-
41
- try {
42
- if (req.headers.authorization) {
43
- const token = req.headers.authorization.split(" ")[1];
44
- const userId = jwt.verify(token, jwtSecret) as string;
45
- if (userId) {
46
- user = database.users.find((_user) => _user.id === userId) ?? null;
47
- }
48
- }
49
- } catch (cause) {
50
- console.error(cause);
51
- }
52
-
53
- return { user, requestId };
54
- };
55
-
56
- const publicProcedure = t.procedure;
57
- const protectedProcedure = t.procedure.use(({ ctx, next }) => {
58
- if (!ctx.user) {
59
- throw new TRPCError({
60
- message: "User not found",
61
- code: "UNAUTHORIZED",
62
- });
63
- }
64
- return next({ ctx: { ...ctx, user: ctx.user } });
65
- });
66
-
67
- const authRouter = t.router({
68
- register: publicProcedure
69
- .meta({
70
- openapi: {
71
- method: "POST",
72
- path: "/auth/register",
73
- tags: ["auth"],
74
- summary: "Register as a new user",
75
- },
76
- })
77
- .input(
78
- z.object({
79
- email: z.string().email(),
80
- passcode: z.preprocess(
81
- (arg) => (typeof arg === "string" ? parseInt(arg) : arg),
82
- z.number().min(1000).max(9999),
83
- ),
84
- name: z.string().min(3),
85
- }),
86
- )
87
- .output(
88
- z.object({
89
- user: z.object({
90
- id: z.string().uuid(),
91
- email: z.string().email(),
92
- name: z.string().min(3),
93
- }),
94
- }),
95
- )
96
- .mutation(({ input }) => {
97
- let user = database.users.find((_user) => _user.email === input.email);
98
-
99
- if (user) {
100
- throw new TRPCError({
101
- message: "User with email already exists",
102
- code: "UNAUTHORIZED",
103
- });
104
- }
105
-
106
- user = {
107
- id: uuid(),
108
- email: input.email,
109
- passcode: input.passcode,
110
- name: input.name,
111
- };
112
-
113
- database.users.push(user);
114
-
115
- return { user: { id: user.id, email: user.email, name: user.name } };
116
- }),
117
- login: publicProcedure
118
- .meta({
119
- openapi: {
120
- method: "POST",
121
- path: "/auth/login",
122
- tags: ["auth"],
123
- summary: "Login as an existing user",
124
- },
125
- })
126
- .input(
127
- z.object({
128
- email: z.string().email(),
129
- passcode: z.preprocess(
130
- (arg) => (typeof arg === "string" ? parseInt(arg) : arg),
131
- z.number().min(1000).max(9999),
132
- ),
133
- }),
134
- )
135
- .output(
136
- z.object({
137
- token: z.string(),
138
- }),
139
- )
140
- .mutation(({ input }) => {
141
- const user = database.users.find((_user) => _user.email === input.email);
142
-
143
- if (!user) {
144
- throw new TRPCError({
145
- message: "User with email not found",
146
- code: "UNAUTHORIZED",
147
- });
148
- }
149
- if (user.passcode !== input.passcode) {
150
- throw new TRPCError({
151
- message: "Passcode was incorrect",
152
- code: "UNAUTHORIZED",
153
- });
154
- }
155
-
156
- return {
157
- token: jwt.sign(user.id, jwtSecret),
158
- };
159
- }),
160
- });
161
-
162
- const usersRouter = t.router({
163
- getUsers: publicProcedure
164
- .meta({
165
- openapi: {
166
- method: "GET",
167
- path: "/users",
168
- tags: ["users"],
169
- summary: "Read all users",
170
- },
171
- })
172
- // .input(z.void())
173
- // .output(
174
- // z.object({
175
- // users: z.array(
176
- // z.object({
177
- // id: z.string().uuid(),
178
- // email: z.string().email(),
179
- // name: z.string(),
180
- // }),
181
- // ),
182
- // }),
183
- // )
184
- .query(() => {
185
- const users = database.users.map((user) => ({
186
- id: user.id,
187
- email: user.email,
188
- name: user.name,
189
- }));
190
-
191
- return { users };
192
- }),
193
- getUserById: publicProcedure
194
- .meta({
195
- openapi: {
196
- method: "GET",
197
- path: "/users/{id}",
198
- tags: ["users"],
199
- summary: "Read a user by id",
200
- },
201
- })
202
- .input(
203
- z.object({
204
- id: z.string().uuid(),
205
- }),
206
- )
207
- .output(
208
- z.object({
209
- user: z.object({
210
- id: z.string().uuid(),
211
- email: z.string().email(),
212
- name: z.string(),
213
- }),
214
- }),
215
- )
216
- .query(({ input }) => {
217
- const user = database.users.find((_user) => _user.id === input.id);
218
-
219
- if (!user) {
220
- throw new TRPCError({
221
- message: "User not found",
222
- code: "NOT_FOUND",
223
- });
224
- }
225
-
226
- return { user };
227
- }),
228
- });
229
-
230
- const postsRouter = t.router({
231
- getPosts: publicProcedure
232
- .meta({
233
- openapi: {
234
- method: "GET",
235
- path: "/posts",
236
- tags: ["posts"],
237
- summary: "Read all posts",
238
- },
239
- })
240
- .input(
241
- z.object({
242
- userId: z.string().uuid().optional(),
243
- }),
244
- )
245
- .output(
246
- z.object({
247
- posts: z.array(
248
- z.object({
249
- id: z.string().uuid(),
250
- content: z.string(),
251
- userId: z.string().uuid(),
252
- }),
253
- ),
254
- }),
255
- )
256
- .query(({ input }) => {
257
- let posts: Post[] = database.posts;
258
-
259
- if (input.userId) {
260
- posts = posts.filter((post) => {
261
- return post.userId === input.userId;
262
- });
263
- }
264
-
265
- return { posts };
266
- }),
267
- getPostById: publicProcedure
268
- .meta({
269
- openapi: {
270
- method: "GET",
271
- path: "/posts/{id}",
272
- tags: ["posts"],
273
- summary: "Read a post by id",
274
- },
275
- })
276
- .input(
277
- z.object({
278
- id: z.string().uuid(),
279
- }),
280
- )
281
- .output(
282
- z.object({
283
- post: z.object({
284
- id: z.string().uuid(),
285
- content: z.string(),
286
- userId: z.string().uuid(),
287
- }),
288
- }),
289
- )
290
- .query(({ input }) => {
291
- const post = database.posts.find((_post) => _post.id === input.id);
292
-
293
- if (!post) {
294
- throw new TRPCError({
295
- message: "Post not found",
296
- code: "NOT_FOUND",
297
- });
298
- }
299
-
300
- return { post };
301
- }),
302
- createPost: protectedProcedure
303
- .meta({
304
- openapi: {
305
- method: "POST",
306
- path: "/posts",
307
- tags: ["posts"],
308
- protect: true,
309
- summary: "Create a new post",
310
- },
311
- })
312
- .input(
313
- z.object({
314
- content: z.string().min(1).max(140),
315
- }),
316
- )
317
- .output(
318
- z.object({
319
- post: z.object({
320
- id: z.string().uuid(),
321
- content: z.string(),
322
- userId: z.string().uuid(),
323
- }),
324
- }),
325
- )
326
- .mutation(({ input, ctx }) => {
327
- const post: Post = {
328
- id: uuid(),
329
- content: input.content,
330
- userId: ctx.user.id,
331
- };
332
-
333
- database.posts.push(post);
334
-
335
- return { post };
336
- }),
337
- updatePostById: protectedProcedure
338
- .meta({
339
- openapi: {
340
- method: "PUT",
341
- path: "/posts/{id}",
342
- tags: ["posts"],
343
- protect: true,
344
- summary: "Update an existing post",
345
- },
346
- })
347
- .input(
348
- z.object({
349
- id: z.string().uuid(),
350
- content: z.string().min(1),
351
- }),
352
- )
353
- .output(
354
- z.object({
355
- post: z.object({
356
- id: z.string().uuid(),
357
- content: z.string(),
358
- userId: z.string().uuid(),
359
- }),
360
- }),
361
- )
362
- .mutation(({ input, ctx }) => {
363
- const post = database.posts.find((_post) => _post.id === input.id);
364
-
365
- if (!post) {
366
- throw new TRPCError({
367
- message: "Post not found",
368
- code: "NOT_FOUND",
369
- });
370
- }
371
- if (post.userId !== ctx.user.id) {
372
- throw new TRPCError({
373
- message: "Cannot edit post owned by other user",
374
- code: "FORBIDDEN",
375
- });
376
- }
377
-
378
- post.content = input.content;
379
-
380
- return { post };
381
- }),
382
- deletePostById: protectedProcedure
383
- .meta({
384
- openapi: {
385
- method: "DELETE",
386
- path: "/posts/{id}",
387
- tags: ["posts"],
388
- protect: true,
389
- summary: "Delete a post",
390
- },
391
- })
392
- .input(
393
- z.object({
394
- id: z.string().uuid(),
395
- }),
396
- )
397
- .output(z.null())
398
- .mutation(({ input, ctx }) => {
399
- const post = database.posts.find((_post) => _post.id === input.id);
400
- if (!post) {
401
- throw new TRPCError({
402
- message: "Post not found",
403
- code: "NOT_FOUND",
404
- });
405
- }
406
- if (post.userId !== ctx.user.id) {
407
- throw new TRPCError({
408
- message: "Cannot delete post owned by other user",
409
- code: "FORBIDDEN",
410
- });
411
- }
412
-
413
- database.posts = database.posts.filter((_post) => _post !== post);
414
-
415
- return null;
416
- }),
417
- });
418
-
419
- export const appRouter = t.router({
420
- auth: authRouter,
421
- users: usersRouter,
422
- posts: postsRouter,
423
- });
424
-
425
- export type AppRouter = typeof appRouter;
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es5",
4
- "lib": ["dom", "dom.iterable", "esnext"],
5
- "allowJs": true,
6
- "skipLibCheck": true,
7
- "strict": true,
8
- "forceConsistentCasingInFileNames": true,
9
- "noEmit": true,
10
- "esModuleInterop": true,
11
- "module": "esnext",
12
- "moduleResolution": "node",
13
- "resolveJsonModule": true,
14
- "isolatedModules": true,
15
- "jsx": "preserve",
16
- "incremental": true,
17
- "baseUrl": ".",
18
- "paths": {
19
- "trpc-openapi": ["../../dist"]
20
- }
21
- },
22
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
23
- "exclude": ["node_modules"]
24
- }
package/jest.config.ts DELETED
@@ -1,12 +0,0 @@
1
- // @ts-check
2
-
3
- /** @type {import('ts-jest').JestConfigWithTsJest} */
4
- module.exports = {
5
- preset: 'ts-jest',
6
- testEnvironment: 'node',
7
- rootDir: './test',
8
- snapshotFormat: {
9
- escapeString: true,
10
- printBasicPrototype: true,
11
- },
12
- };
@@ -1,7 +0,0 @@
1
- packages:
2
- - 'examples/with-nextjs'
3
- - 'examples/with-express'
4
- - 'examples/with-interop'
5
- - 'examples/with-serverless'
6
- - 'examples/with-fastify'
7
- - 'examples/with-nuxtjs'
@@ -1,20 +0,0 @@
1
- import { Request, Response } from 'express';
2
-
3
- import { OpenApiRouter } from '../types';
4
- import {
5
- CreateOpenApiNodeHttpHandlerOptions,
6
- createOpenApiNodeHttpHandler,
7
- } from './node-http/core';
8
-
9
- export type CreateOpenApiExpressMiddlewareOptions<TRouter extends OpenApiRouter> =
10
- CreateOpenApiNodeHttpHandlerOptions<TRouter, Request, Response>;
11
-
12
- export const createOpenApiExpressMiddleware = <TRouter extends OpenApiRouter>(
13
- opts: CreateOpenApiExpressMiddlewareOptions<TRouter>,
14
- ) => {
15
- const openApiHttpHandler = createOpenApiNodeHttpHandler(opts);
16
-
17
- return async (req: Request, res: Response) => {
18
- await openApiHttpHandler(req, res);
19
- };
20
- };
@@ -1,3 +0,0 @@
1
- export * from "./standalone";
2
- export * from "./express";
3
- export * from "./next";
@@ -1,64 +0,0 @@
1
- import { TRPCError } from "@trpc/server";
2
- import type { NextApiRequest, NextApiResponse } from "next";
3
-
4
- import type { OpenApiErrorResponse, OpenApiRouter } from "../types";
5
- import { normalizePath } from "../utils/path";
6
- import {
7
- type CreateOpenApiNodeHttpHandlerOptions,
8
- createOpenApiNodeHttpHandler,
9
- } from "./node-http/core";
10
-
11
- export type CreateOpenApiNextHandlerOptions<TRouter extends OpenApiRouter> =
12
- Omit<
13
- CreateOpenApiNodeHttpHandlerOptions<
14
- TRouter,
15
- NextApiRequest,
16
- NextApiResponse
17
- >,
18
- "maxBodySize"
19
- >;
20
-
21
- export const createOpenApiNextHandler = <TRouter extends OpenApiRouter>(
22
- opts: CreateOpenApiNextHandlerOptions<TRouter>,
23
- ) => {
24
- const openApiHttpHandler = createOpenApiNodeHttpHandler(opts);
25
-
26
- return async (req: NextApiRequest, res: NextApiResponse) => {
27
- let pathname: string | null = null;
28
- if (typeof req.query.trpc === "string") {
29
- pathname = req.query.trpc;
30
- } else if (Array.isArray(req.query.trpc)) {
31
- pathname = req.query.trpc.join("/");
32
- }
33
-
34
- if (pathname === null) {
35
- const error = new TRPCError({
36
- message:
37
- 'Query "trpc" not found - is the `trpc-openapi` file named `[...trpc].ts`?',
38
- code: "INTERNAL_SERVER_ERROR",
39
- });
40
-
41
- opts.onError?.({
42
- error,
43
- type: "unknown",
44
- path: undefined,
45
- input: undefined,
46
- ctx: undefined,
47
- req,
48
- });
49
-
50
- res.statusCode = 500;
51
- res.setHeader("Content-Type", "application/json");
52
- const body: OpenApiErrorResponse = {
53
- message: error.message,
54
- code: error.code,
55
- };
56
- res.end(JSON.stringify(body));
57
-
58
- return;
59
- }
60
-
61
- req.url = normalizePath(pathname);
62
- await openApiHttpHandler(req, res);
63
- };
64
- };