glasswork 0.6.2 → 0.7.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/README.md +37 -9
- package/dist/core/bootstrap.d.ts.map +1 -1
- package/dist/core/bootstrap.js +36 -6
- package/dist/core/bootstrap.js.map +1 -1
- package/dist/core/types.d.ts +103 -23
- package/dist/core/types.d.ts.map +1 -1
- package/dist/http/route-helpers.d.ts +102 -44
- package/dist/http/route-helpers.d.ts.map +1 -1
- package/dist/http/route-helpers.js +127 -126
- package/dist/http/route-helpers.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +47 -13
- package/dist/middleware/rate-limit.js.map +1 -1
- package/dist/openapi/defaults.d.ts.map +1 -1
- package/dist/openapi/defaults.js +5 -3
- package/dist/openapi/defaults.js.map +1 -1
- package/dist/openapi/openapi-processors.d.ts +51 -0
- package/dist/openapi/openapi-processors.d.ts.map +1 -0
- package/dist/openapi/openapi-processors.js +166 -0
- package/dist/openapi/openapi-processors.js.map +1 -0
- package/dist/openapi/openapi.d.ts +36 -3
- package/dist/openapi/openapi.d.ts.map +1 -1
- package/dist/openapi/openapi.js +27 -39
- package/dist/openapi/openapi.js.map +1 -1
- package/dist/utils/serialize-prisma-types.d.ts +63 -30
- package/dist/utils/serialize-prisma-types.d.ts.map +1 -1
- package/dist/utils/serialize-prisma-types.js +7 -2
- package/dist/utils/serialize-prisma-types.js.map +1 -1
- package/package.json +10 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/openapi/defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/openapi/defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAU/C;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,CAAC,gBA8FhD,CAAC"}
|
package/dist/openapi/defaults.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { toJsonSchema } from '@valibot/to-json-schema';
|
|
2
2
|
import { ErrorResponseDto, ValidationErrorResponseDto, ValidationIssueDto, } from '../http/error-response.dto.js';
|
|
3
|
+
/** Maximum value for a signed 32-bit integer */
|
|
4
|
+
const INT32_MAX = 2_147_483_647;
|
|
3
5
|
/**
|
|
4
6
|
* Default OpenAPI components provided by Glasswork.
|
|
5
7
|
* These include headers for features implemented by the framework:
|
|
@@ -69,7 +71,7 @@ export const defaultOpenAPIComponents = {
|
|
|
69
71
|
type: 'integer',
|
|
70
72
|
format: 'int32',
|
|
71
73
|
minimum: 0,
|
|
72
|
-
maximum:
|
|
74
|
+
maximum: INT32_MAX
|
|
73
75
|
},
|
|
74
76
|
},
|
|
75
77
|
'X-Total-Pages': {
|
|
@@ -78,7 +80,7 @@ export const defaultOpenAPIComponents = {
|
|
|
78
80
|
type: 'integer',
|
|
79
81
|
format: 'int32',
|
|
80
82
|
minimum: 0,
|
|
81
|
-
maximum:
|
|
83
|
+
maximum: INT32_MAX
|
|
82
84
|
},
|
|
83
85
|
},
|
|
84
86
|
'X-Current-Page': {
|
|
@@ -87,7 +89,7 @@ export const defaultOpenAPIComponents = {
|
|
|
87
89
|
type: 'integer',
|
|
88
90
|
format: 'int32',
|
|
89
91
|
minimum: 1,
|
|
90
|
-
maximum:
|
|
92
|
+
maximum: INT32_MAX
|
|
91
93
|
},
|
|
92
94
|
},
|
|
93
95
|
'X-Page-Size': {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/openapi/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAEvC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA+B;IAClE,OAAO,EAAE;QACP,uDAAuD;QACvD,aAAa,EAAE,YAAY,CAAC,gBAAgB,CAA2B;QACvE,uBAAuB,EAAE,YAAY,CAAC,0BAA0B,CAA2B;QAC3F,eAAe,EAAE,YAAY,CAAC,kBAAkB,CAA2B;KAC5E;IACD,OAAO,EAAE;QACP,eAAe;QACf,6BAA6B,EAAE;YAC7B,WAAW,EAAE,kCAAkC;YAC/C,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,qCAAqC;aAC/C;SACF;QAED,6CAA6C;QAC7C,iBAAiB,EAAE;YACjB,WAAW,EAAE,mDAAmD;YAChE,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,KAAK;aACf;SACF;QACD,qBAAqB,EAAE;YACrB,WAAW,EAAE,0CAA0C;YACvD,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,KAAK;aACf;SACF;QACD,iBAAiB,EAAE;YACjB,WAAW,EAAE,4CAA4C;YACzD,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,IAAI;aACd;SACF;QACD,aAAa,EAAE;YACb,WAAW,EAAE,4DAA4D;YACzE,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,IAAI;aACd;SACF;QAED,uCAAuC;QACvC,eAAe,EAAE;YACf,WAAW,EAAE,iCAAiC;YAC9C,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/openapi/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAEvC,gDAAgD;AAChD,MAAM,SAAS,GAAG,aAAa,CAAC;AAEhC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA+B;IAClE,OAAO,EAAE;QACP,uDAAuD;QACvD,aAAa,EAAE,YAAY,CAAC,gBAAgB,CAA2B;QACvE,uBAAuB,EAAE,YAAY,CAAC,0BAA0B,CAA2B;QAC3F,eAAe,EAAE,YAAY,CAAC,kBAAkB,CAA2B;KAC5E;IACD,OAAO,EAAE;QACP,eAAe;QACf,6BAA6B,EAAE;YAC7B,WAAW,EAAE,kCAAkC;YAC/C,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,qCAAqC;aAC/C;SACF;QAED,6CAA6C;QAC7C,iBAAiB,EAAE;YACjB,WAAW,EAAE,mDAAmD;YAChE,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,KAAK;aACf;SACF;QACD,qBAAqB,EAAE;YACrB,WAAW,EAAE,0CAA0C;YACvD,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,KAAK;aACf;SACF;QACD,iBAAiB,EAAE;YACjB,WAAW,EAAE,4CAA4C;YACzD,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,IAAI;aACd;SACF;QACD,aAAa,EAAE;YACb,WAAW,EAAE,4DAA4D;YACzE,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,IAAI;aACd;SACF;QAED,uCAAuC;QACvC,eAAe,EAAE;YACf,WAAW,EAAE,iCAAiC;YAC9C,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,SAAS;aACnB;SACF;QACD,eAAe,EAAE;YACf,WAAW,EAAE,uBAAuB;YACpC,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,SAAS;aACnB;SACF;QACD,gBAAgB,EAAE;YAChB,WAAW,EAAE,qBAAqB;YAClC,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,SAAS;aACnB;SACF;QACD,aAAa,EAAE;YACb,WAAW,EAAE,0BAA0B;YACvC,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,GAAG;aACb;SACF;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { OpenAPIProcessorContext, OpenAPIResponseObject, OpenAPIResponseProcessor } from '../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a processor that adds CORS headers to responses.
|
|
4
|
+
* Auto-enabled when middleware.cors is configured.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createCorsHeadersProcessor(enabled: boolean): OpenAPIResponseProcessor;
|
|
7
|
+
/**
|
|
8
|
+
* Create a processor that adds rate limiting headers to responses.
|
|
9
|
+
* Auto-enabled when rateLimit.enabled is true.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createRateLimitHeadersProcessor(enabled: boolean): OpenAPIResponseProcessor;
|
|
12
|
+
/**
|
|
13
|
+
* Processor that adds pagination headers to 200 responses.
|
|
14
|
+
* Auto-enabled when the route query schema includes pagination fields (page, pageSize).
|
|
15
|
+
*/
|
|
16
|
+
export declare const paginationHeadersProcessor: OpenAPIResponseProcessor;
|
|
17
|
+
/**
|
|
18
|
+
* Processor that adds response headers defined in route config.
|
|
19
|
+
* Supports both simple array format and flexible status-code-specific format.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // Simple: applies to all responses
|
|
24
|
+
* openapi: { responseHeaders: ['Api-Version', 'Server-Timing'] }
|
|
25
|
+
*
|
|
26
|
+
* // Flexible: status-code specific
|
|
27
|
+
* openapi: {
|
|
28
|
+
* responseHeaders: {
|
|
29
|
+
* '200': ['X-Total-Count'],
|
|
30
|
+
* 'default': ['Api-Version']
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare const responseHeadersProcessor: OpenAPIResponseProcessor;
|
|
36
|
+
/**
|
|
37
|
+
* Create built-in processors based on bootstrap configuration.
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export declare function createBuiltinProcessors(config: {
|
|
42
|
+
corsEnabled: boolean;
|
|
43
|
+
rateLimitEnabled: boolean;
|
|
44
|
+
}): OpenAPIResponseProcessor[];
|
|
45
|
+
/**
|
|
46
|
+
* Apply all processors to a response object.
|
|
47
|
+
*
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
export declare function applyProcessors(response: OpenAPIResponseObject, context: OpenAPIProcessorContext, processors: OpenAPIResponseProcessor[]): OpenAPIResponseObject;
|
|
51
|
+
//# sourceMappingURL=openapi-processors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-processors.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi-processors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,kBAAkB,CAAC;AAE1B;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,wBAAwB,CAcrF;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,OAAO,GAAG,wBAAwB,CAgC1F;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,EAAE,wBA0BxC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,wBAAwB,EAAE,wBA4CtC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,GAAG,wBAAwB,EAAE,CAO7B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,qBAAqB,EAC/B,OAAO,EAAE,uBAAuB,EAChC,UAAU,EAAE,wBAAwB,EAAE,GACrC,qBAAqB,CAMvB"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a processor that adds CORS headers to responses.
|
|
3
|
+
* Auto-enabled when middleware.cors is configured.
|
|
4
|
+
*/
|
|
5
|
+
export function createCorsHeadersProcessor(enabled) {
|
|
6
|
+
return (response, _context) => {
|
|
7
|
+
if (!enabled)
|
|
8
|
+
return response;
|
|
9
|
+
return {
|
|
10
|
+
...response,
|
|
11
|
+
headers: {
|
|
12
|
+
...response.headers,
|
|
13
|
+
'Access-Control-Allow-Origin': {
|
|
14
|
+
$ref: '#/components/headers/Access-Control-Allow-Origin',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a processor that adds rate limiting headers to responses.
|
|
22
|
+
* Auto-enabled when rateLimit.enabled is true.
|
|
23
|
+
*/
|
|
24
|
+
export function createRateLimitHeadersProcessor(enabled) {
|
|
25
|
+
return (response, context) => {
|
|
26
|
+
if (!enabled)
|
|
27
|
+
return response;
|
|
28
|
+
const { statusCode } = context;
|
|
29
|
+
const headers = {
|
|
30
|
+
'RateLimit-Limit': {
|
|
31
|
+
$ref: '#/components/headers/RateLimit-Limit',
|
|
32
|
+
},
|
|
33
|
+
'RateLimit-Remaining': {
|
|
34
|
+
$ref: '#/components/headers/RateLimit-Remaining',
|
|
35
|
+
},
|
|
36
|
+
'RateLimit-Reset': {
|
|
37
|
+
$ref: '#/components/headers/RateLimit-Reset',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
// Add Retry-After header for 429 responses
|
|
41
|
+
if (statusCode === '429') {
|
|
42
|
+
headers['Retry-After'] = {
|
|
43
|
+
$ref: '#/components/headers/Retry-After',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
...response,
|
|
48
|
+
headers: {
|
|
49
|
+
...response.headers,
|
|
50
|
+
...headers,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Processor that adds pagination headers to 200 responses.
|
|
57
|
+
* Auto-enabled when the route query schema includes pagination fields (page, pageSize).
|
|
58
|
+
*/
|
|
59
|
+
export const paginationHeadersProcessor = (response, context) => {
|
|
60
|
+
const { statusCode, hasPagination } = context;
|
|
61
|
+
// Only add pagination headers to 200 responses with pagination
|
|
62
|
+
if (statusCode !== '200' || !hasPagination) {
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
...response,
|
|
67
|
+
headers: {
|
|
68
|
+
...response.headers,
|
|
69
|
+
'X-Total-Count': {
|
|
70
|
+
$ref: '#/components/headers/X-Total-Count',
|
|
71
|
+
},
|
|
72
|
+
'X-Total-Pages': {
|
|
73
|
+
$ref: '#/components/headers/X-Total-Pages',
|
|
74
|
+
},
|
|
75
|
+
'X-Current-Page': {
|
|
76
|
+
$ref: '#/components/headers/X-Current-Page',
|
|
77
|
+
},
|
|
78
|
+
'X-Page-Size': {
|
|
79
|
+
$ref: '#/components/headers/X-Page-Size',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Processor that adds response headers defined in route config.
|
|
86
|
+
* Supports both simple array format and flexible status-code-specific format.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Simple: applies to all responses
|
|
91
|
+
* openapi: { responseHeaders: ['Api-Version', 'Server-Timing'] }
|
|
92
|
+
*
|
|
93
|
+
* // Flexible: status-code specific
|
|
94
|
+
* openapi: {
|
|
95
|
+
* responseHeaders: {
|
|
96
|
+
* '200': ['X-Total-Count'],
|
|
97
|
+
* 'default': ['Api-Version']
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export const responseHeadersProcessor = (response, context) => {
|
|
103
|
+
const { statusCode, routeConfig } = context;
|
|
104
|
+
const responseHeaders = routeConfig.openapi?.responseHeaders;
|
|
105
|
+
if (!responseHeaders) {
|
|
106
|
+
return response;
|
|
107
|
+
}
|
|
108
|
+
let headersToAdd = [];
|
|
109
|
+
if (Array.isArray(responseHeaders)) {
|
|
110
|
+
// Simple format: applies to all responses
|
|
111
|
+
headersToAdd = responseHeaders;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// Flexible format: status-code specific
|
|
115
|
+
const statusSpecific = responseHeaders[statusCode];
|
|
116
|
+
const defaultHeaders = responseHeaders['default'];
|
|
117
|
+
if (statusSpecific) {
|
|
118
|
+
headersToAdd = [...headersToAdd, ...statusSpecific];
|
|
119
|
+
}
|
|
120
|
+
if (defaultHeaders) {
|
|
121
|
+
headersToAdd = [...headersToAdd, ...defaultHeaders];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (headersToAdd.length === 0) {
|
|
125
|
+
return response;
|
|
126
|
+
}
|
|
127
|
+
const newHeaders = {};
|
|
128
|
+
for (const header of headersToAdd) {
|
|
129
|
+
newHeaders[header] = {
|
|
130
|
+
$ref: `#/components/headers/${header}`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
...response,
|
|
135
|
+
headers: {
|
|
136
|
+
...response.headers,
|
|
137
|
+
...newHeaders,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Create built-in processors based on bootstrap configuration.
|
|
143
|
+
*
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
146
|
+
export function createBuiltinProcessors(config) {
|
|
147
|
+
return [
|
|
148
|
+
createCorsHeadersProcessor(config.corsEnabled),
|
|
149
|
+
createRateLimitHeadersProcessor(config.rateLimitEnabled),
|
|
150
|
+
paginationHeadersProcessor,
|
|
151
|
+
responseHeadersProcessor,
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Apply all processors to a response object.
|
|
156
|
+
*
|
|
157
|
+
* @internal
|
|
158
|
+
*/
|
|
159
|
+
export function applyProcessors(response, context, processors) {
|
|
160
|
+
let result = response;
|
|
161
|
+
for (const processor of processors) {
|
|
162
|
+
result = processor(result, context);
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=openapi-processors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-processors.js","sourceRoot":"","sources":["../../src/openapi/openapi-processors.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,27 @@ export interface ConfigureOpenAPIOptions {
|
|
|
7
7
|
rateLimit?: RateLimitOptions;
|
|
8
8
|
middleware?: MiddlewareOptions;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Result from configuring OpenAPI, includes optional write function.
|
|
12
|
+
*/
|
|
13
|
+
export interface ConfigureOpenAPIResult {
|
|
14
|
+
/**
|
|
15
|
+
* Write the OpenAPI spec to a file.
|
|
16
|
+
* Call this after all routes have been registered.
|
|
17
|
+
*
|
|
18
|
+
* @returns Promise that resolves when the file has been written
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const app = await bootstrap(config);
|
|
23
|
+
* const { writeSpec } = configureOpenAPI({ app, ... });
|
|
24
|
+
*
|
|
25
|
+
* // After all routes are registered:
|
|
26
|
+
* await writeSpec?.();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
writeSpec?: () => Promise<void>;
|
|
30
|
+
}
|
|
10
31
|
/**
|
|
11
32
|
* Configure OpenAPI documentation for the application.
|
|
12
33
|
*
|
|
@@ -15,10 +36,22 @@ export interface ConfigureOpenAPIOptions {
|
|
|
15
36
|
* - production: No serving by default
|
|
16
37
|
* - test: Disabled by default
|
|
17
38
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
39
|
+
* Note: Response processors are now configured in bootstrap.ts and stored
|
|
40
|
+
* per-app instance via setOpenAPIContext. This eliminates global state.
|
|
20
41
|
*
|
|
21
42
|
* @param options - Configuration options
|
|
43
|
+
* @returns Object with optional `writeSpec` function for writing the spec to file
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Basic usage (auto-write with delay)
|
|
48
|
+
* configureOpenAPI({ app, environment, openapi });
|
|
49
|
+
*
|
|
50
|
+
* // Explicit write after routes are registered (recommended)
|
|
51
|
+
* const { writeSpec } = configureOpenAPI({ app, environment, openapi });
|
|
52
|
+
* // ... register all routes ...
|
|
53
|
+
* await writeSpec?.();
|
|
54
|
+
* ```
|
|
22
55
|
*/
|
|
23
|
-
export declare function configureOpenAPI(options: ConfigureOpenAPIOptions):
|
|
56
|
+
export declare function configureOpenAPI(options: ConfigureOpenAPIOptions): ConfigureOpenAPIResult;
|
|
24
57
|
//# sourceMappingURL=openapi.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAGjC,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAGjC,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAQ1B,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,IAAI,CAAC;IACV,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,sBAAsB,CA2EzF"}
|
package/dist/openapi/openapi.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { writeFile } from 'node:fs/promises';
|
|
2
2
|
import { swaggerUI } from '@hono/swagger-ui';
|
|
3
3
|
import { openAPIRouteHandler } from 'hono-openapi';
|
|
4
|
-
import { setGlobalResponseHooks, setGlobalSecuritySchemes } from '../http/route-helpers.js';
|
|
5
4
|
import { deepMerge } from '../utils/deep-merge.js';
|
|
6
5
|
import { createLogger } from '../utils/logger.js';
|
|
7
6
|
import { defaultOpenAPIComponents } from './defaults.js';
|
|
8
|
-
|
|
7
|
+
/** Default delay before writing OpenAPI spec (ms). Allows routes to register. */
|
|
8
|
+
const DEFAULT_WRITE_DELAY_MS = 1000;
|
|
9
9
|
/**
|
|
10
10
|
* Configure OpenAPI documentation for the application.
|
|
11
11
|
*
|
|
@@ -14,30 +14,31 @@ import { createCorsHeadersHook, createPaginationHeadersHook, createRateLimitHead
|
|
|
14
14
|
* - production: No serving by default
|
|
15
15
|
* - test: Disabled by default
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Note: Response processors are now configured in bootstrap.ts and stored
|
|
18
|
+
* per-app instance via setOpenAPIContext. This eliminates global state.
|
|
19
19
|
*
|
|
20
20
|
* @param options - Configuration options
|
|
21
|
+
* @returns Object with optional `writeSpec` function for writing the spec to file
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Basic usage (auto-write with delay)
|
|
26
|
+
* configureOpenAPI({ app, environment, openapi });
|
|
27
|
+
*
|
|
28
|
+
* // Explicit write after routes are registered (recommended)
|
|
29
|
+
* const { writeSpec } = configureOpenAPI({ app, environment, openapi });
|
|
30
|
+
* // ... register all routes ...
|
|
31
|
+
* await writeSpec?.();
|
|
32
|
+
* ```
|
|
21
33
|
*/
|
|
22
34
|
export function configureOpenAPI(options) {
|
|
23
35
|
const { app, environment, openapi, rateLimit, middleware } = options;
|
|
24
36
|
if (!openapi.enabled) {
|
|
25
|
-
return;
|
|
37
|
+
return {};
|
|
26
38
|
}
|
|
27
39
|
const logger = createLogger('Glasswork:OpenAPI', true);
|
|
28
40
|
const shouldServeSpecs = openapi.serveSpecs ?? environment === 'development';
|
|
29
41
|
const shouldServeUI = openapi.serveUI ?? environment === 'development';
|
|
30
|
-
// Build response hooks based on enabled features
|
|
31
|
-
const frameworkHooks = [
|
|
32
|
-
createCorsHeadersHook(!!middleware?.cors),
|
|
33
|
-
createRateLimitHeadersHook(rateLimit?.enabled ?? false),
|
|
34
|
-
createPaginationHeadersHook(),
|
|
35
|
-
];
|
|
36
|
-
// Merge with application-provided hooks
|
|
37
|
-
const allHooks = [...frameworkHooks, ...(openapi.responseHooks || [])];
|
|
38
|
-
// Make hooks available to route definitions globally
|
|
39
|
-
// This must be done synchronously before routes are mounted
|
|
40
|
-
setGlobalResponseHooks(allHooks);
|
|
41
42
|
// Filter components based on enabled features (only include headers that are actually used)
|
|
42
43
|
const filteredComponents = filterComponentsByFeatures(defaultOpenAPIComponents, {
|
|
43
44
|
rateLimitEnabled: rateLimit?.enabled ?? false,
|
|
@@ -45,13 +46,6 @@ export function configureOpenAPI(options) {
|
|
|
45
46
|
});
|
|
46
47
|
// Merge application documentation with filtered Glasswork defaults
|
|
47
48
|
const mergedDocumentation = deepMerge({ components: filteredComponents }, openapi.documentation || {});
|
|
48
|
-
// Extract security scheme names from the merged documentation and make them available globally
|
|
49
|
-
// This allows route definitions to reference the actual security schemes defined in the app
|
|
50
|
-
const securitySchemes = extractSecuritySchemeNames(mergedDocumentation);
|
|
51
|
-
setGlobalSecuritySchemes(securitySchemes);
|
|
52
|
-
if (securitySchemes.length > 0) {
|
|
53
|
-
logger.info(`Security schemes: ${securitySchemes.join(', ')}`);
|
|
54
|
-
}
|
|
55
49
|
// Serve OpenAPI spec endpoint (development only by default)
|
|
56
50
|
if (shouldServeSpecs) {
|
|
57
51
|
app.get('/api/openapi.json', openAPIRouteHandler(app, { documentation: mergedDocumentation }));
|
|
@@ -60,12 +54,11 @@ export function configureOpenAPI(options) {
|
|
|
60
54
|
if (shouldServeUI) {
|
|
61
55
|
app.get('/api', swaggerUI({ url: '/api/openapi.json' }));
|
|
62
56
|
}
|
|
63
|
-
//
|
|
57
|
+
// Create writeSpec function if file writing is configured
|
|
58
|
+
let writeSpec;
|
|
64
59
|
if (openapi.writeToFile) {
|
|
65
60
|
const filePath = openapi.writeToFile;
|
|
66
|
-
|
|
67
|
-
// This happens after routes are registered
|
|
68
|
-
setTimeout(async () => {
|
|
61
|
+
writeSpec = async () => {
|
|
69
62
|
try {
|
|
70
63
|
let specContent;
|
|
71
64
|
if (shouldServeSpecs) {
|
|
@@ -87,19 +80,14 @@ export function configureOpenAPI(options) {
|
|
|
87
80
|
catch (error) {
|
|
88
81
|
logger.error('Failed to write OpenAPI spec:', error);
|
|
89
82
|
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
*/
|
|
97
|
-
function extractSecuritySchemeNames(documentation) {
|
|
98
|
-
const securitySchemes = documentation.components?.securitySchemes;
|
|
99
|
-
if (!securitySchemes || typeof securitySchemes !== 'object') {
|
|
100
|
-
return [];
|
|
83
|
+
};
|
|
84
|
+
// Auto-write with delay for backward compatibility
|
|
85
|
+
// NOTE: For more reliable writes, call writeSpec() explicitly after registering routes
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
writeSpec?.();
|
|
88
|
+
}, DEFAULT_WRITE_DELAY_MS);
|
|
101
89
|
}
|
|
102
|
-
return
|
|
90
|
+
return { writeSpec };
|
|
103
91
|
}
|
|
104
92
|
/**
|
|
105
93
|
* Filter component headers based on enabled features.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAQnD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -110,31 +110,57 @@ export declare function serializePrismaTypes<T>(data: T, config?: SerializationC
|
|
|
110
110
|
* Prisma's Decimal has constructor.name as string (not literal 'Decimal'),
|
|
111
111
|
* so we match on the toNumber() method which is the key interface we need.
|
|
112
112
|
*/
|
|
113
|
-
type PrismaDecimalLike = {
|
|
113
|
+
export type PrismaDecimalLike = {
|
|
114
114
|
toNumber(): number;
|
|
115
115
|
constructor: {
|
|
116
116
|
name: string;
|
|
117
117
|
};
|
|
118
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* Common date field naming patterns used in databases.
|
|
121
|
+
* Fields matching these patterns will accept Date objects in AcceptPrismaTypes.
|
|
122
|
+
*
|
|
123
|
+
* Patterns:
|
|
124
|
+
* - `${string}At` - createdAt, updatedAt, deletedAt, expiresAt, etc.
|
|
125
|
+
* - `${string}Date` - birthDate, startDate, endDate, effectiveDate, etc.
|
|
126
|
+
* - `${string}Time` - startTime, endTime (when stored as Date/DateTime)
|
|
127
|
+
* - `${string}Timestamp` - loginTimestamp, etc.
|
|
128
|
+
* - Common standalone names: 'date', 'timestamp', 'datetime'
|
|
129
|
+
*/
|
|
130
|
+
type DateFieldPattern = `${string}At` | `${string}Date` | `${string}Time` | `${string}Timestamp` | 'date' | 'timestamp' | 'datetime';
|
|
119
131
|
/**
|
|
120
132
|
* Reverse type helper that accepts both serialized types AND their Prisma equivalents.
|
|
121
133
|
* This allows handlers to return Prisma objects directly while TypeScript knows they'll be serialized.
|
|
122
134
|
*
|
|
123
135
|
* **How it works:**
|
|
124
|
-
* -
|
|
125
|
-
*
|
|
126
|
-
* -
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
136
|
+
* - For Date fields: Only string fields with conventional date names (ending in `At`, `Date`, `Time`, etc.)
|
|
137
|
+
* accept Date objects. Other string fields only accept strings.
|
|
138
|
+
* - For Decimal fields: All number fields accept Decimal-like objects (see rationale below).
|
|
139
|
+
* - Recursively applies to objects and arrays.
|
|
140
|
+
*
|
|
141
|
+
* **Date Field Detection:**
|
|
142
|
+
* Date fields are detected by naming convention. Fields ending in `At`, `Date`, `Time`, `Timestamp`,
|
|
143
|
+
* or named `date`/`timestamp`/`datetime` will accept Date objects.
|
|
144
|
+
*
|
|
145
|
+
* If you have unconventionally named date fields, you have two options:
|
|
146
|
+
* 1. Rename them to follow the convention (recommended)
|
|
147
|
+
* 2. Use `strictTypes: true` in your route and handle types manually
|
|
148
|
+
* 3. Provide additional patterns via the `AdditionalDatePatterns` type parameter
|
|
149
|
+
*
|
|
150
|
+
* **Why Decimal is still permissive:**
|
|
151
|
+
* Unlike date fields which follow predictable naming conventions (`createdAt`, `updatedAt`, etc.),
|
|
152
|
+
* Decimal fields have domain-specific names with no reliable pattern:
|
|
153
|
+
* - `price`, `amount`, `balance` (finance)
|
|
154
|
+
* - `density`, `thickness`, `weight` (physics/materials)
|
|
155
|
+
* - `discountPercentage`, `taxRate` (percentages)
|
|
156
|
+
* - `latitude`, `longitude` (coordinates)
|
|
157
|
+
*
|
|
158
|
+
* Since there's no reliable naming convention for Decimal fields, we keep them permissive.
|
|
159
|
+
* In practice, developers rarely pass Decimal objects to non-decimal fields accidentally,
|
|
160
|
+
* and runtime schema validation will catch any actual errors.
|
|
161
|
+
*
|
|
162
|
+
* @template T - The serialized type to transform
|
|
163
|
+
* @template AdditionalDatePatterns - Optional additional patterns to match for date fields
|
|
138
164
|
*
|
|
139
165
|
* @example
|
|
140
166
|
* ```typescript
|
|
@@ -143,44 +169,51 @@ type PrismaDecimalLike = {
|
|
|
143
169
|
* id: v.string(),
|
|
144
170
|
* name: v.string(),
|
|
145
171
|
* createdAt: v.string(), // Expects ISO string
|
|
146
|
-
* balance: v.number(),
|
|
172
|
+
* balance: v.number(), // Expects number
|
|
147
173
|
* });
|
|
148
174
|
*
|
|
149
175
|
* // ✅ Intended usage: Prisma types are automatically handled
|
|
150
176
|
* handler: async () => {
|
|
151
177
|
* const user = await prisma.user.findUnique({ where: { id } });
|
|
152
|
-
* // user.createdAt is Date, user.balance is Decimal
|
|
178
|
+
* // user.createdAt is Date (matches 'At' pattern), user.balance is Decimal
|
|
153
179
|
* return user; // Works! Automatically serialized to match schema
|
|
154
180
|
* }
|
|
155
181
|
*
|
|
156
|
-
* // ✅
|
|
182
|
+
* // ✅ Date fields with conventional names accept Date objects
|
|
157
183
|
* handler: async () => {
|
|
158
184
|
* return {
|
|
159
185
|
* id: '123',
|
|
160
186
|
* name: 'John',
|
|
161
|
-
* createdAt: new Date(), //
|
|
162
|
-
*
|
|
187
|
+
* createdAt: new Date(), // ✅ 'createdAt' matches 'At' pattern
|
|
188
|
+
* birthDate: new Date(), // ✅ 'birthDate' matches 'Date' pattern
|
|
189
|
+
* balance: new Decimal('100.50'), // ✅ Decimal accepted for any number field
|
|
163
190
|
* };
|
|
164
191
|
* }
|
|
165
192
|
*
|
|
166
|
-
* //
|
|
193
|
+
* // ❌ Non-date string fields no longer accept Date (compile-time error)
|
|
167
194
|
* handler: async () => {
|
|
168
195
|
* return {
|
|
169
|
-
*
|
|
170
|
-
* name: new Date(), // Type allows, but weird - who would do this?
|
|
196
|
+
* name: new Date(), // ❌ Type error! 'name' doesn't match date patterns
|
|
171
197
|
* createdAt: '2025-01-01',
|
|
172
|
-
* balance: 100,
|
|
173
198
|
* };
|
|
174
199
|
* }
|
|
175
|
-
* // Note: Even if you did, schema validation might catch format issues
|
|
176
200
|
*
|
|
177
|
-
* //
|
|
178
|
-
* type
|
|
179
|
-
* // Result: { createdAt: string | Date; amount: number | Decimal }
|
|
201
|
+
* // Using additional date patterns for unconventional field names
|
|
202
|
+
* type MyResponse = AcceptPrismaTypes<ResponseType, 'lastLogin' | 'nextRenewal'>;
|
|
180
203
|
* ```
|
|
181
204
|
*/
|
|
182
|
-
|
|
183
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Helper type to add Date support to string types (including nullable strings).
|
|
207
|
+
* Preserves null and undefined in unions.
|
|
208
|
+
*/
|
|
209
|
+
type WithDateSupport<T> = T extends string | infer Rest ? string | Date | Rest : T;
|
|
210
|
+
/**
|
|
211
|
+
* Helper type to add Decimal support to number types (including nullable numbers).
|
|
212
|
+
* Preserves null and undefined in unions.
|
|
213
|
+
*/
|
|
214
|
+
type WithDecimalSupport<T> = T extends number | infer Rest ? number | PrismaDecimalLike | Rest : T;
|
|
215
|
+
export type AcceptPrismaTypes<T, AdditionalDatePatterns extends string = never> = T extends object ? T extends Array<infer U> ? Array<AcceptPrismaTypes<U, AdditionalDatePatterns>> : {
|
|
216
|
+
[K in keyof T]: K extends string ? K extends DateFieldPattern | AdditionalDatePatterns ? T[K] extends string | null | undefined ? WithDateSupport<T[K]> : AcceptPrismaTypes<T[K], AdditionalDatePatterns> : T[K] extends number | null | undefined ? WithDecimalSupport<T[K]> : AcceptPrismaTypes<T[K], AdditionalDatePatterns> : AcceptPrismaTypes<T[K], AdditionalDatePatterns>;
|
|
184
217
|
} : T;
|
|
185
218
|
export {};
|
|
186
219
|
//# sourceMappingURL=serialize-prisma-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize-prisma-types.d.ts","sourceRoot":"","sources":["../../src/utils/serialize-prisma-types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,GAC3C,MAAM,GACN,CAAC,SAAS;IAAE,WAAW,EAAE;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,CAAA;CAAE,GAC5C,MAAM,GACN,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GACtB,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GACzB,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACzC,CAAC,CAAC;AAEZ;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,YAAY,EAAE,eAAe,EAAE,CAAC;CACjC;
|
|
1
|
+
{"version":3,"file":"serialize-prisma-types.d.ts","sourceRoot":"","sources":["../../src/utils/serialize-prisma-types.ts"],"names":[],"mappings":"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"}
|