snap-on-openapi 1.0.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 (131) hide show
  1. package/README.md +643 -0
  2. package/dist/OpenApi.d.ts +58 -0
  3. package/dist/OpenApi.js +272 -0
  4. package/dist/README.md +643 -0
  5. package/dist/assets/stoplight.html +27 -0
  6. package/dist/assets/swagger.html +892 -0
  7. package/dist/enums/ErrorCode.d.ts +5 -0
  8. package/dist/enums/ErrorCode.js +6 -0
  9. package/dist/enums/Methods.d.ts +7 -0
  10. package/dist/enums/Methods.js +8 -0
  11. package/dist/enums/SampleRouteType.d.ts +3 -0
  12. package/dist/enums/SampleRouteType.js +4 -0
  13. package/dist/enums/ValidationLocations.d.ts +6 -0
  14. package/dist/enums/ValidationLocations.js +7 -0
  15. package/dist/index.d.ts +39 -0
  16. package/dist/index.js +23 -0
  17. package/dist/services/ClientGenerator/ClientGenerator.d.ts +9 -0
  18. package/dist/services/ClientGenerator/ClientGenerator.js +48 -0
  19. package/dist/services/ConfigBuilder/ConfigBuilder.d.ts +31 -0
  20. package/dist/services/ConfigBuilder/ConfigBuilder.js +65 -0
  21. package/dist/services/ConfigBuilder/types/DefaultConfig.d.ts +22 -0
  22. package/dist/services/ConfigBuilder/types/DefaultConfig.js +47 -0
  23. package/dist/services/ConfigBuilder/types/DefaultErrorMap.d.ts +113 -0
  24. package/dist/services/ConfigBuilder/types/DefaultErrorMap.js +21 -0
  25. package/dist/services/ConfigBuilder/types/DefaultRouteContextMap.d.ts +6 -0
  26. package/dist/services/ConfigBuilder/types/DefaultRouteContextMap.js +3 -0
  27. package/dist/services/ConfigBuilder/types/DefaultRouteMap.d.ts +12 -0
  28. package/dist/services/ConfigBuilder/types/DefaultRouteMap.js +9 -0
  29. package/dist/services/ConfigBuilder/types/DefaultRouteParamsMap.d.ts +5 -0
  30. package/dist/services/ConfigBuilder/types/DefaultRouteParamsMap.js +3 -0
  31. package/dist/services/ConfigBuilder/types/OpenApiConstructor.d.ts +2 -0
  32. package/dist/services/ConfigBuilder/types/OpenApiConstructor.js +1 -0
  33. package/dist/services/DescriptionChecker/DescriptionChecker.d.ts +16 -0
  34. package/dist/services/DescriptionChecker/DescriptionChecker.js +47 -0
  35. package/dist/services/DevelopmentUtils/DevelopmentUtils.d.ts +4 -0
  36. package/dist/services/DevelopmentUtils/DevelopmentUtils.js +15 -0
  37. package/dist/services/ExpressWrapper/ExpressWrapper.d.ts +15 -0
  38. package/dist/services/ExpressWrapper/ExpressWrapper.js +68 -0
  39. package/dist/services/ExpressWrapper/types/ExpressApp.d.ts +8 -0
  40. package/dist/services/ExpressWrapper/types/ExpressApp.js +1 -0
  41. package/dist/services/ExpressWrapper/types/ExpressHandler.d.ts +3 -0
  42. package/dist/services/ExpressWrapper/types/ExpressHandler.js +1 -0
  43. package/dist/services/ExpressWrapper/types/ExpressRequest.d.ts +8 -0
  44. package/dist/services/ExpressWrapper/types/ExpressRequest.js +1 -0
  45. package/dist/services/ExpressWrapper/types/ExpressResponse.d.ts +6 -0
  46. package/dist/services/ExpressWrapper/types/ExpressResponse.js +1 -0
  47. package/dist/services/Logger/Logger.d.ts +15 -0
  48. package/dist/services/Logger/Logger.js +100 -0
  49. package/dist/services/Logger/types/LogLevel.d.ts +6 -0
  50. package/dist/services/Logger/types/LogLevel.js +7 -0
  51. package/dist/services/RoutingFactory/RoutingFactory.d.ts +10 -0
  52. package/dist/services/RoutingFactory/RoutingFactory.js +23 -0
  53. package/dist/services/SchemaGenerator/SchemaGenerator.d.ts +19 -0
  54. package/dist/services/SchemaGenerator/SchemaGenerator.js +131 -0
  55. package/dist/services/TanstackStartWrapper/TanstackStartWrapper.d.ts +35 -0
  56. package/dist/services/TanstackStartWrapper/TanstackStartWrapper.js +67 -0
  57. package/dist/services/TestUtils/TestUtils.d.ts +13 -0
  58. package/dist/services/TestUtils/TestUtils.js +48 -0
  59. package/dist/services/ValidationUtils/ValidationUtils.d.ts +56 -0
  60. package/dist/services/ValidationUtils/ValidationUtils.js +38 -0
  61. package/dist/services/ValidationUtils/transformers/stringBooleanTransformer.d.ts +3 -0
  62. package/dist/services/ValidationUtils/transformers/stringBooleanTransformer.js +5 -0
  63. package/dist/services/ValidationUtils/transformers/stringDateTransformer.d.ts +3 -0
  64. package/dist/services/ValidationUtils/transformers/stringDateTransformer.js +16 -0
  65. package/dist/services/ValidationUtils/transformers/stringNumberTransfromer.d.ts +3 -0
  66. package/dist/services/ValidationUtils/transformers/stringNumberTransfromer.js +24 -0
  67. package/dist/services/ValidationUtils/types/PaginatedResponse.d.ts +8 -0
  68. package/dist/services/ValidationUtils/types/PaginatedResponse.js +1 -0
  69. package/dist/types/AnyRoute.d.ts +3 -0
  70. package/dist/types/AnyRoute.js +1 -0
  71. package/dist/types/InitialBuilder.d.ts +9 -0
  72. package/dist/types/InitialBuilder.js +1 -0
  73. package/dist/types/NonEmptyArray.d.ts +1 -0
  74. package/dist/types/NonEmptyArray.js +1 -0
  75. package/dist/types/Route.d.ts +22 -0
  76. package/dist/types/Route.js +1 -0
  77. package/dist/types/RouteMap.d.ts +3 -0
  78. package/dist/types/RouteMap.js +1 -0
  79. package/dist/types/RoutePath.d.ts +1 -0
  80. package/dist/types/RoutePath.js +1 -0
  81. package/dist/types/Wrappers.d.ts +7 -0
  82. package/dist/types/Wrappers.js +1 -0
  83. package/dist/types/config/AnyConfig.d.ts +4 -0
  84. package/dist/types/config/AnyConfig.js +1 -0
  85. package/dist/types/config/AnyRouteConfigMap.d.ts +4 -0
  86. package/dist/types/config/AnyRouteConfigMap.js +1 -0
  87. package/dist/types/config/Config.d.ts +19 -0
  88. package/dist/types/config/Config.js +1 -0
  89. package/dist/types/config/ContextParams.d.ts +12 -0
  90. package/dist/types/config/ContextParams.js +1 -0
  91. package/dist/types/config/ErrorConfig.d.ts +6 -0
  92. package/dist/types/config/ErrorConfig.js +1 -0
  93. package/dist/types/config/ErrorConfigMap.d.ts +5 -0
  94. package/dist/types/config/ErrorConfigMap.js +1 -0
  95. package/dist/types/config/ErrorResponse.d.ts +8 -0
  96. package/dist/types/config/ErrorResponse.js +1 -0
  97. package/dist/types/config/ISpecificationExtension.d.ts +6 -0
  98. package/dist/types/config/ISpecificationExtension.js +1 -0
  99. package/dist/types/config/Info.d.ts +7 -0
  100. package/dist/types/config/Info.js +1 -0
  101. package/dist/types/config/OmitMappedField.d.ts +3 -0
  102. package/dist/types/config/OmitMappedField.js +1 -0
  103. package/dist/types/config/RouteConfig.d.ts +10 -0
  104. package/dist/types/config/RouteConfig.js +1 -0
  105. package/dist/types/config/RouteConfigMap.d.ts +7 -0
  106. package/dist/types/config/RouteConfigMap.js +1 -0
  107. package/dist/types/config/RouteContextMap.d.ts +6 -0
  108. package/dist/types/config/RouteContextMap.js +1 -0
  109. package/dist/types/config/RouteExtraProps.d.ts +2 -0
  110. package/dist/types/config/RouteExtraProps.js +1 -0
  111. package/dist/types/config/RouteExtraPropsMap.d.ts +4 -0
  112. package/dist/types/config/RouteExtraPropsMap.js +1 -0
  113. package/dist/types/config/Server.d.ts +5 -0
  114. package/dist/types/config/Server.js +1 -0
  115. package/dist/types/errors/ApiError.d.ts +5 -0
  116. package/dist/types/errors/ApiError.js +10 -0
  117. package/dist/types/errors/BuiltInError.d.ts +4 -0
  118. package/dist/types/errors/BuiltInError.js +3 -0
  119. package/dist/types/errors/FieldError.d.ts +33 -0
  120. package/dist/types/errors/FieldError.js +9 -0
  121. package/dist/types/errors/ValidationError.d.ts +10 -0
  122. package/dist/types/errors/ValidationError.js +17 -0
  123. package/dist/types/errors/responses/NotFoundErrorResponse.d.ts +12 -0
  124. package/dist/types/errors/responses/NotFoundErrorResponse.js +6 -0
  125. package/dist/types/errors/responses/UnknownErrorResponse.d.ts +12 -0
  126. package/dist/types/errors/responses/UnknownErrorResponse.js +6 -0
  127. package/dist/types/errors/responses/ValidationErrorResponse.d.ts +89 -0
  128. package/dist/types/errors/responses/ValidationErrorResponse.js +12 -0
  129. package/dist/vitest.config.d.ts +2 -0
  130. package/dist/vitest.config.js +25 -0
  131. package/package.json +53 -0
@@ -0,0 +1,58 @@
1
+ import 'zod-openapi/extend';
2
+ import { ErrorCode } from './enums/ErrorCode.js';
3
+ import { AnyRoute } from './types/AnyRoute.js';
4
+ import { RoutingFactory } from './services/RoutingFactory/RoutingFactory.js';
5
+ import { RouteMap } from './types/RouteMap.js';
6
+ import { SchemaGenerator } from './services/SchemaGenerator/SchemaGenerator.js';
7
+ import { AnyConfig } from './types/config/AnyConfig.js';
8
+ import { Logger } from './services/Logger/Logger.js';
9
+ import { DescriptionChecker } from './services/DescriptionChecker/DescriptionChecker.js';
10
+ import { DevelopmentUtils } from './services/DevelopmentUtils/DevelopmentUtils.js';
11
+ import { ClientGenerator } from './services/ClientGenerator/ClientGenerator.js';
12
+ import { ValidationUtils } from './services/ValidationUtils/ValidationUtils.js';
13
+ import { Wrappers } from './types/Wrappers.js';
14
+ import { Server } from './types/config/Server.js';
15
+ import { RoutePath } from './types/RoutePath.js';
16
+ import { SampleRouteType } from './enums/SampleRouteType.js';
17
+ import { ConfigBuilder } from './services/ConfigBuilder/ConfigBuilder.js';
18
+ import { DefaultConfig } from './services/ConfigBuilder/types/DefaultConfig.js';
19
+ import { DefaultErrorMap } from './services/ConfigBuilder/types/DefaultErrorMap.js';
20
+ import { DefaultRouteMap } from './services/ConfigBuilder/types/DefaultRouteMap.js';
21
+ import { InitialBuilder } from './types/InitialBuilder.js';
22
+ import { DefaultRouteContextMap } from './services/ConfigBuilder/types/DefaultRouteContextMap.js';
23
+ import { DefaultRouteParamsMap } from './services/ConfigBuilder/types/DefaultRouteParamsMap.js';
24
+ import z from 'zod';
25
+ export declare class OpenApi<TRouteTypes extends string, TErrorCodes extends string, TConfig extends AnyConfig<TRouteTypes, TErrorCodes>> {
26
+ static readonly builder: InitialBuilder;
27
+ readonly validators: ValidationUtils;
28
+ readonly factory: RoutingFactory<TRouteTypes, TErrorCodes, TConfig>;
29
+ readonly schemaGenerator: SchemaGenerator<TRouteTypes, TErrorCodes, TConfig>;
30
+ readonly clientGenerator: ClientGenerator<TRouteTypes, TErrorCodes, TConfig>;
31
+ readonly wrappers: Wrappers<TRouteTypes, TErrorCodes, TConfig>;
32
+ protected routes: AnyRoute<TRouteTypes>[];
33
+ protected logger: Logger;
34
+ protected basePath: RoutePath;
35
+ protected developmentUtils: DevelopmentUtils;
36
+ protected config: TConfig;
37
+ protected descriptionChecker: DescriptionChecker;
38
+ protected servers: Server[];
39
+ protected constructor(config: TConfig);
40
+ getConfig(): TConfig;
41
+ getBasePath(): RoutePath;
42
+ getServers(): Server[];
43
+ addRoute(route: AnyRoute<TRouteTypes>): void;
44
+ addRoutes(pathExtension: RoutePath, routes: AnyRoute<TRouteTypes>[]): void;
45
+ addRouteMap(routeMap: RouteMap<TRouteTypes>): void;
46
+ mergePaths(...paths: RoutePath[]): RoutePath;
47
+ addServer(url: string, description: string): void;
48
+ protected getRouteForPath(path: string, method: string): AnyRoute<TRouteTypes> | null;
49
+ processRootRoute(originalReq: Request): Promise<{
50
+ status: number;
51
+ body: unknown;
52
+ }>;
53
+ protected handleError(e: unknown): {
54
+ status: number;
55
+ body: z.TypeOf<import("./index.js").OpenApiErrorConfigMap<TErrorCodes>[TErrorCodes]["responseValidator"]>;
56
+ };
57
+ protected static getBuilder(): ConfigBuilder<SampleRouteType, ErrorCode, DefaultErrorMap, DefaultRouteParamsMap, DefaultRouteContextMap, DefaultRouteMap, DefaultConfig>;
58
+ }
@@ -0,0 +1,272 @@
1
+ import 'zod-openapi/extend';
2
+ import { ErrorCode } from './enums/ErrorCode.js';
3
+ import { ValidationLocation } from './enums/ValidationLocations.js';
4
+ import { ValidationError } from './types/errors/ValidationError.js';
5
+ import { RoutingFactory } from './services/RoutingFactory/RoutingFactory.js';
6
+ import { SchemaGenerator } from './services/SchemaGenerator/SchemaGenerator.js';
7
+ import { Method } from './enums/Methods.js';
8
+ import { BuiltInError } from './types/errors/BuiltInError.js';
9
+ import { Logger } from './services/Logger/Logger.js';
10
+ import { DescriptionChecker } from './services/DescriptionChecker/DescriptionChecker.js';
11
+ import { DevelopmentUtils } from './services/DevelopmentUtils/DevelopmentUtils.js';
12
+ import { ClientGenerator } from './services/ClientGenerator/ClientGenerator.js';
13
+ import { ValidationUtils } from './services/ValidationUtils/ValidationUtils.js';
14
+ import { TanstackStartWrapper } from './services/TanstackStartWrapper/TanstackStartWrapper.js';
15
+ import { ExpressWrapper } from './services/ExpressWrapper/ExpressWrapper.js';
16
+ import { ConfigBuilder } from './services/ConfigBuilder/ConfigBuilder.js';
17
+ import z from 'zod';
18
+ export class OpenApi {
19
+ static builder = OpenApi.getBuilder();
20
+ validators = new ValidationUtils();
21
+ factory;
22
+ schemaGenerator;
23
+ clientGenerator = new ClientGenerator(this);
24
+ wrappers;
25
+ routes = [];
26
+ logger;
27
+ basePath;
28
+ developmentUtils;
29
+ config;
30
+ descriptionChecker;
31
+ servers = [];
32
+ constructor(config) {
33
+ this.config = config;
34
+ this.logger = new Logger('OpenAPI');
35
+ if (config.logLevel) {
36
+ Logger.logLevel = config.logLevel;
37
+ }
38
+ this.descriptionChecker = new DescriptionChecker({
39
+ checkValidators: !config.skipDescriptionsCheck,
40
+ });
41
+ this.developmentUtils = new DevelopmentUtils();
42
+ this.factory = new RoutingFactory(config);
43
+ this.basePath = config.basePath;
44
+ const info = {
45
+ title: config.apiName ?? 'My API',
46
+ version: '3.1.0',
47
+ };
48
+ this.schemaGenerator = new SchemaGenerator(this.logger.getInvoker(), info, this.config, this.routes, this.servers);
49
+ this.wrappers = {
50
+ tanstackStart: new TanstackStartWrapper(this),
51
+ express: new ExpressWrapper(this),
52
+ };
53
+ this.servers.push({
54
+ description: 'Local',
55
+ url: this.basePath,
56
+ });
57
+ const servers = this.config.servers ?? [];
58
+ this.servers.push(...servers);
59
+ }
60
+ getConfig() {
61
+ return this.config;
62
+ }
63
+ getBasePath() {
64
+ return this.basePath;
65
+ }
66
+ getServers() {
67
+ return this.servers;
68
+ }
69
+ addRoute(route) {
70
+ if (!this.config.skipDescriptionsCheck) {
71
+ this.descriptionChecker.checkRoutes([route]);
72
+ }
73
+ this.routes.push(route);
74
+ }
75
+ addRoutes(pathExtension, routes) {
76
+ const newRoutes = routes.map((x) => ({
77
+ ...x,
78
+ path: this.mergePaths(pathExtension, x.path),
79
+ }));
80
+ if (!this.config.skipDescriptionsCheck) {
81
+ this.descriptionChecker.checkRoutes(newRoutes);
82
+ }
83
+ this.routes.push(...newRoutes);
84
+ }
85
+ addRouteMap(routeMap) {
86
+ for (const [path, routes] of Object.entries(routeMap)) {
87
+ this.addRoutes(path, routes);
88
+ }
89
+ }
90
+ mergePaths(...paths) {
91
+ let final = '/';
92
+ for (const path of paths) {
93
+ if (path === '/') {
94
+ continue;
95
+ }
96
+ if (final === '/') {
97
+ final = path;
98
+ continue;
99
+ }
100
+ final = `${final}${path}`;
101
+ }
102
+ return final;
103
+ }
104
+ addServer(url, description) {
105
+ this.servers.push({ description, url });
106
+ }
107
+ getRouteForPath(path, method) {
108
+ const fittingRoutes = [];
109
+ outer: for (const route of this.routes) {
110
+ if (route.method === method) {
111
+ fittingRoutes.push(route);
112
+ const routeParts = route.path.split('/').filter((x) => x !== '');
113
+ const pathParts = path.split('/').filter((x) => x !== '');
114
+ if (routeParts.length !== pathParts.length) {
115
+ continue;
116
+ }
117
+ for (const [i, chunk] of routeParts.entries()) {
118
+ if (chunk.includes('{')) {
119
+ continue;
120
+ }
121
+ if (chunk !== pathParts[i]) {
122
+ continue outer;
123
+ }
124
+ }
125
+ return route;
126
+ }
127
+ }
128
+ return null;
129
+ }
130
+ async processRootRoute(originalReq) {
131
+ try {
132
+ const url = new URL(originalReq.url);
133
+ const urlPath = url.pathname.replace(this.getBasePath(), '');
134
+ const route = this.getRouteForPath(urlPath, originalReq.method);
135
+ if (!route) {
136
+ this.logger.info(`Route for ${originalReq.method}:${urlPath} not found`);
137
+ throw new BuiltInError(ErrorCode.NotFound);
138
+ }
139
+ const pathParams = {};
140
+ const routeParts = route.path.split('/');
141
+ const pathParts = urlPath.split('/');
142
+ for (const [i, chunk] of routeParts.entries()) {
143
+ if (chunk.startsWith('{') && chunk.endsWith('}')) {
144
+ const name = chunk.slice(1, chunk.length - 1);
145
+ if (!pathParts[i]) {
146
+ throw new Error(`Can't find '${name}' param in path`);
147
+ }
148
+ pathParams[name] = pathParts[i];
149
+ continue;
150
+ }
151
+ }
152
+ let body = {};
153
+ try {
154
+ body = await originalReq.json();
155
+ }
156
+ catch {
157
+ }
158
+ const reqQuery = {};
159
+ const queryEntries = url.searchParams.entries();
160
+ for (const [key, val] of queryEntries) {
161
+ if (reqQuery[key] !== undefined) {
162
+ if (Array.isArray(reqQuery[key])) {
163
+ reqQuery[key].push(val);
164
+ continue;
165
+ }
166
+ reqQuery[key] = [reqQuery[key], val];
167
+ continue;
168
+ }
169
+ reqQuery[key] = val;
170
+ }
171
+ const req = {
172
+ path: urlPath,
173
+ method: originalReq.method,
174
+ params: pathParams,
175
+ query: reqQuery,
176
+ body: body,
177
+ };
178
+ this.logger.info(`Calling route ${route.path}`);
179
+ this.logger.info(`${req.method}: ${req.path}`, {
180
+ params: req.params,
181
+ query: req.query,
182
+ body: req.body,
183
+ });
184
+ const queryValidator = route.validators.query?.strict() ?? z.object({});
185
+ const query = queryValidator.safeParse(req.query);
186
+ if (!query.success) {
187
+ throw new ValidationError(query.error, ValidationLocation.Query);
188
+ }
189
+ const pathvalidator = route.validators.path?.strict() ?? z.object({});
190
+ const path = pathvalidator.safeParse(req.params);
191
+ if (!path.success) {
192
+ throw new ValidationError(path.error, ValidationLocation.Path);
193
+ }
194
+ let response;
195
+ const containsBody = route.method !== Method.GET;
196
+ if (containsBody && route.validators.body) {
197
+ const body = route.validators.body.strict().safeParse(req.body);
198
+ if (!body.success) {
199
+ throw new ValidationError(body.error, ValidationLocation.Body);
200
+ }
201
+ const context = await this.config.routes[route.type].contextFactory({
202
+ route: route,
203
+ request: originalReq,
204
+ params: {
205
+ query: query.data,
206
+ path: path.data,
207
+ body: body.data,
208
+ },
209
+ });
210
+ response = await route.handler({
211
+ ...context,
212
+ params: {
213
+ query: query.data,
214
+ path: path.data,
215
+ body: body.data,
216
+ },
217
+ });
218
+ }
219
+ else {
220
+ const context = await this.config.routes[route.type].contextFactory({
221
+ route: route,
222
+ request: originalReq,
223
+ params: {
224
+ query: query.data,
225
+ path: path.data,
226
+ body: {},
227
+ },
228
+ });
229
+ response = await route.handler({
230
+ ...context,
231
+ params: {
232
+ query: query.data,
233
+ path: path.data,
234
+ body: {},
235
+ }
236
+ });
237
+ }
238
+ const validated = route.validators.response.safeParse(response);
239
+ if (!validated.success) {
240
+ throw new ValidationError(validated.error, ValidationLocation.Response);
241
+ }
242
+ this.logger.info('Response: 200', validated.data);
243
+ return { status: 200, body: validated.data };
244
+ }
245
+ catch (e) {
246
+ return this.handleError(e);
247
+ }
248
+ }
249
+ handleError(e) {
250
+ this.logger.error('Error during request openAPI route handling', e);
251
+ try {
252
+ const response = this.config.handleError ? this.config.handleError(e) : this.config.defaultError;
253
+ const status = this.config.errors[response.code].status;
254
+ const valid = this.config.errors[response.code].responseValidator.safeParse(response.body);
255
+ if (!valid.success) {
256
+ throw new Error("Error response haven't passed validation");
257
+ }
258
+ this.logger.info(`Response: '${status}'`, response.body);
259
+ return { status: Number(status), body: response.body };
260
+ }
261
+ catch (e) {
262
+ this.logger.error('Error during error handling', e);
263
+ const status = this.config.errors[this.config.defaultError.code].status;
264
+ return { status: Number(status), body: this.config.defaultError.body };
265
+ }
266
+ }
267
+ static getBuilder() {
268
+ return new ConfigBuilder((conf) => {
269
+ return new OpenApi(conf);
270
+ });
271
+ }
272
+ }