glasswork 0.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.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -0
  3. package/dist/bootstrap.d.ts +55 -0
  4. package/dist/bootstrap.d.ts.map +1 -0
  5. package/dist/bootstrap.js +359 -0
  6. package/dist/bootstrap.js.map +1 -0
  7. package/dist/core/bootstrap.d.ts +55 -0
  8. package/dist/core/bootstrap.d.ts.map +1 -0
  9. package/dist/core/bootstrap.js +329 -0
  10. package/dist/core/bootstrap.js.map +1 -0
  11. package/dist/core/module.d.ts +21 -0
  12. package/dist/core/module.d.ts.map +1 -0
  13. package/dist/core/module.js +85 -0
  14. package/dist/core/module.js.map +1 -0
  15. package/dist/core/types.d.ts +273 -0
  16. package/dist/core/types.d.ts.map +1 -0
  17. package/dist/core/types.js +2 -0
  18. package/dist/core/types.js.map +1 -0
  19. package/dist/environment.d.ts +21 -0
  20. package/dist/environment.d.ts.map +1 -0
  21. package/dist/environment.js +29 -0
  22. package/dist/environment.js.map +1 -0
  23. package/dist/error-handler.d.ts +33 -0
  24. package/dist/error-handler.d.ts.map +1 -0
  25. package/dist/error-handler.js +53 -0
  26. package/dist/error-handler.js.map +1 -0
  27. package/dist/errors.d.ts +36 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +65 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/get-client-ip.d.ts +24 -0
  32. package/dist/get-client-ip.d.ts.map +1 -0
  33. package/dist/get-client-ip.js +49 -0
  34. package/dist/get-client-ip.js.map +1 -0
  35. package/dist/http/error-handler.d.ts +33 -0
  36. package/dist/http/error-handler.d.ts.map +1 -0
  37. package/dist/http/error-handler.js +55 -0
  38. package/dist/http/error-handler.js.map +1 -0
  39. package/dist/http/errors.d.ts +36 -0
  40. package/dist/http/errors.d.ts.map +1 -0
  41. package/dist/http/errors.js +65 -0
  42. package/dist/http/errors.js.map +1 -0
  43. package/dist/http/route-helpers.d.ts +172 -0
  44. package/dist/http/route-helpers.d.ts.map +1 -0
  45. package/dist/http/route-helpers.js +249 -0
  46. package/dist/http/route-helpers.js.map +1 -0
  47. package/dist/index.d.ts +33 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +29 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/logger.d.ts +37 -0
  52. package/dist/logger.d.ts.map +1 -0
  53. package/dist/logger.js +52 -0
  54. package/dist/logger.js.map +1 -0
  55. package/dist/middleware/rate-limit.d.ts +7 -0
  56. package/dist/middleware/rate-limit.d.ts.map +1 -0
  57. package/dist/middleware/rate-limit.js +182 -0
  58. package/dist/middleware/rate-limit.js.map +1 -0
  59. package/dist/module.d.ts +21 -0
  60. package/dist/module.d.ts.map +1 -0
  61. package/dist/module.js +85 -0
  62. package/dist/module.js.map +1 -0
  63. package/dist/omit.d.ts +20 -0
  64. package/dist/omit.d.ts.map +1 -0
  65. package/dist/omit.js +28 -0
  66. package/dist/omit.js.map +1 -0
  67. package/dist/openapi/defaults.d.ts +11 -0
  68. package/dist/openapi/defaults.d.ts.map +1 -0
  69. package/dist/openapi/defaults.js +127 -0
  70. package/dist/openapi/defaults.js.map +1 -0
  71. package/dist/openapi/hooks.d.ts +14 -0
  72. package/dist/openapi/hooks.d.ts.map +1 -0
  73. package/dist/openapi/hooks.js +80 -0
  74. package/dist/openapi/hooks.js.map +1 -0
  75. package/dist/openapi/openapi.d.ts +24 -0
  76. package/dist/openapi/openapi.d.ts.map +1 -0
  77. package/dist/openapi/openapi.js +108 -0
  78. package/dist/openapi/openapi.js.map +1 -0
  79. package/dist/openapi-defaults.d.ts +11 -0
  80. package/dist/openapi-defaults.d.ts.map +1 -0
  81. package/dist/openapi-defaults.js +127 -0
  82. package/dist/openapi-defaults.js.map +1 -0
  83. package/dist/openapi-hooks.d.ts +14 -0
  84. package/dist/openapi-hooks.d.ts.map +1 -0
  85. package/dist/openapi-hooks.js +80 -0
  86. package/dist/openapi-hooks.js.map +1 -0
  87. package/dist/openapi.d.ts +24 -0
  88. package/dist/openapi.d.ts.map +1 -0
  89. package/dist/openapi.js +106 -0
  90. package/dist/openapi.js.map +1 -0
  91. package/dist/pagination.d.ts +48 -0
  92. package/dist/pagination.d.ts.map +1 -0
  93. package/dist/pagination.js +43 -0
  94. package/dist/pagination.js.map +1 -0
  95. package/dist/pick.d.ts +20 -0
  96. package/dist/pick.d.ts.map +1 -0
  97. package/dist/pick.js +28 -0
  98. package/dist/pick.js.map +1 -0
  99. package/dist/rate-limit.d.ts +7 -0
  100. package/dist/rate-limit.d.ts.map +1 -0
  101. package/dist/rate-limit.js +180 -0
  102. package/dist/rate-limit.js.map +1 -0
  103. package/dist/route-helpers.d.ts +172 -0
  104. package/dist/route-helpers.d.ts.map +1 -0
  105. package/dist/route-helpers.js +249 -0
  106. package/dist/route-helpers.js.map +1 -0
  107. package/dist/serialize-dates.d.ts +27 -0
  108. package/dist/serialize-dates.d.ts.map +1 -0
  109. package/dist/serialize-dates.js +39 -0
  110. package/dist/serialize-dates.js.map +1 -0
  111. package/dist/testing.d.ts +87 -0
  112. package/dist/testing.d.ts.map +1 -0
  113. package/dist/testing.js +94 -0
  114. package/dist/testing.js.map +1 -0
  115. package/dist/types.d.ts +273 -0
  116. package/dist/types.d.ts.map +1 -0
  117. package/dist/types.js +2 -0
  118. package/dist/types.js.map +1 -0
  119. package/dist/utils/deep-merge.d.ts +5 -0
  120. package/dist/utils/deep-merge.d.ts.map +1 -0
  121. package/dist/utils/deep-merge.js +21 -0
  122. package/dist/utils/deep-merge.js.map +1 -0
  123. package/dist/utils/environment.d.ts +21 -0
  124. package/dist/utils/environment.d.ts.map +1 -0
  125. package/dist/utils/environment.js +29 -0
  126. package/dist/utils/environment.js.map +1 -0
  127. package/dist/utils/get-client-ip.d.ts +24 -0
  128. package/dist/utils/get-client-ip.d.ts.map +1 -0
  129. package/dist/utils/get-client-ip.js +49 -0
  130. package/dist/utils/get-client-ip.js.map +1 -0
  131. package/dist/utils/logger.d.ts +37 -0
  132. package/dist/utils/logger.d.ts.map +1 -0
  133. package/dist/utils/logger.js +52 -0
  134. package/dist/utils/logger.js.map +1 -0
  135. package/dist/utils/omit.d.ts +20 -0
  136. package/dist/utils/omit.d.ts.map +1 -0
  137. package/dist/utils/omit.js +28 -0
  138. package/dist/utils/omit.js.map +1 -0
  139. package/dist/utils/pagination.d.ts +48 -0
  140. package/dist/utils/pagination.d.ts.map +1 -0
  141. package/dist/utils/pagination.js +42 -0
  142. package/dist/utils/pagination.js.map +1 -0
  143. package/dist/utils/pick.d.ts +20 -0
  144. package/dist/utils/pick.d.ts.map +1 -0
  145. package/dist/utils/pick.js +28 -0
  146. package/dist/utils/pick.js.map +1 -0
  147. package/dist/utils/serialize-dates.d.ts +27 -0
  148. package/dist/utils/serialize-dates.d.ts.map +1 -0
  149. package/dist/utils/serialize-dates.js +39 -0
  150. package/dist/utils/serialize-dates.js.map +1 -0
  151. package/dist/utils.d.ts +9 -0
  152. package/dist/utils.d.ts.map +1 -0
  153. package/dist/utils.js +31 -0
  154. package/dist/utils.js.map +1 -0
  155. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/openapi/defaults.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { OpenAPIResponseHook } from '../core/types.js';
2
+ /**
3
+ * Create a hook that adds CORS headers to responses
4
+ */
5
+ export declare function createCorsHeadersHook(enabled: boolean): OpenAPIResponseHook;
6
+ /**
7
+ * Create a hook that adds rate limiting headers to responses
8
+ */
9
+ export declare function createRateLimitHeadersHook(enabled: boolean): OpenAPIResponseHook;
10
+ /**
11
+ * Create a hook that adds pagination headers to 200 responses
12
+ */
13
+ export declare function createPaginationHeadersHook(): OpenAPIResponseHook;
14
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/openapi/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAc3E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAgChF;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,mBAAmB,CAyBjE"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Create a hook that adds CORS headers to responses
3
+ */
4
+ export function createCorsHeadersHook(enabled) {
5
+ return (response, _context) => {
6
+ if (!enabled)
7
+ return response;
8
+ return {
9
+ ...response,
10
+ headers: {
11
+ ...response.headers,
12
+ 'Access-Control-Allow-Origin': {
13
+ $ref: '#/components/headers/Access-Control-Allow-Origin',
14
+ },
15
+ },
16
+ };
17
+ };
18
+ }
19
+ /**
20
+ * Create a hook that adds rate limiting headers to responses
21
+ */
22
+ export function createRateLimitHeadersHook(enabled) {
23
+ return (response, context) => {
24
+ if (!enabled)
25
+ return response;
26
+ const { statusCode } = context;
27
+ const headers = {
28
+ 'RateLimit-Limit': {
29
+ $ref: '#/components/headers/RateLimit-Limit',
30
+ },
31
+ 'RateLimit-Remaining': {
32
+ $ref: '#/components/headers/RateLimit-Remaining',
33
+ },
34
+ 'RateLimit-Reset': {
35
+ $ref: '#/components/headers/RateLimit-Reset',
36
+ },
37
+ };
38
+ // Add Retry-After header for 429 responses
39
+ if (statusCode === '429') {
40
+ headers['Retry-After'] = {
41
+ $ref: '#/components/headers/Retry-After',
42
+ };
43
+ }
44
+ return {
45
+ ...response,
46
+ headers: {
47
+ ...response.headers,
48
+ ...headers,
49
+ },
50
+ };
51
+ };
52
+ }
53
+ /**
54
+ * Create a hook that adds pagination headers to 200 responses
55
+ */
56
+ export function createPaginationHeadersHook() {
57
+ return (response, context) => {
58
+ const { statusCode, routeConfig } = context;
59
+ // Only add pagination headers to 200 responses with paginate flag
60
+ if (statusCode !== '200' || !routeConfig.paginate) {
61
+ return response;
62
+ }
63
+ return {
64
+ ...response,
65
+ headers: {
66
+ ...response.headers,
67
+ 'X-Total-Count': {
68
+ $ref: '#/components/headers/X-Total-Count',
69
+ },
70
+ 'X-Page': {
71
+ $ref: '#/components/headers/X-Page',
72
+ },
73
+ 'X-Limit': {
74
+ $ref: '#/components/headers/X-Limit',
75
+ },
76
+ },
77
+ };
78
+ };
79
+ }
80
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/openapi/hooks.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QAE9B,OAAO;YACL,GAAG,QAAQ;YACX,OAAO,EAAE;gBACP,GAAG,QAAQ,CAAC,OAAO;gBACnB,6BAA6B,EAAE;oBAC7B,IAAI,EAAE,kDAAkD;iBACzD;aACF;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAAgB;IACzD,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC3B,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QAC/B,MAAM,OAAO,GAAqC;YAChD,iBAAiB,EAAE;gBACjB,IAAI,EAAE,sCAAsC;aAC7C;YACD,qBAAqB,EAAE;gBACrB,IAAI,EAAE,0CAA0C;aACjD;YACD,iBAAiB,EAAE;gBACjB,IAAI,EAAE,sCAAsC;aAC7C;SACF,CAAC;QAEF,2CAA2C;QAC3C,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,aAAa,CAAC,GAAG;gBACvB,IAAI,EAAE,kCAAkC;aACzC,CAAC;QACJ,CAAC;QAED,OAAO;YACL,GAAG,QAAQ;YACX,OAAO,EAAE;gBACP,GAAG,QAAQ,CAAC,OAAO;gBACnB,GAAG,OAAO;aACX;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC3B,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;QAE5C,kEAAkE;QAClE,IAAI,UAAU,KAAK,KAAK,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;YAClD,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,OAAO;YACL,GAAG,QAAQ;YACX,OAAO,EAAE;gBACP,GAAG,QAAQ,CAAC,OAAO;gBACnB,eAAe,EAAE;oBACf,IAAI,EAAE,oCAAoC;iBAC3C;gBACD,QAAQ,EAAE;oBACR,IAAI,EAAE,6BAA6B;iBACpC;gBACD,SAAS,EAAE;oBACT,IAAI,EAAE,8BAA8B;iBACrC;aACF;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { Hono } from 'hono';
2
+ import type { Environment, MiddlewareOptions, OpenAPIOptions, RateLimitOptions } from '../core/types.js';
3
+ export interface ConfigureOpenAPIOptions {
4
+ app: Hono;
5
+ environment: Environment;
6
+ openapi: OpenAPIOptions;
7
+ rateLimit?: RateLimitOptions;
8
+ middleware?: MiddlewareOptions;
9
+ }
10
+ /**
11
+ * Configure OpenAPI documentation for the application.
12
+ *
13
+ * Behavior by environment:
14
+ * - development: Serve specs and UI at /api and /api/openapi.json
15
+ * - production: Write spec to disk (openapi.json), no serving
16
+ * - test: Disabled by default
17
+ *
18
+ * This function automatically sets up response hooks based on enabled features
19
+ * and merges them with any application-provided hooks.
20
+ *
21
+ * @param options - Configuration options
22
+ */
23
+ export declare function configureOpenAPI(options: ConfigureOpenAPIOptions): void;
24
+ //# sourceMappingURL=openapi.d.ts.map
@@ -0,0 +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;AAW1B,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;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAkEvE"}
@@ -0,0 +1,108 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { swaggerUI } from '@hono/swagger-ui';
3
+ import { openAPISpecs } from 'hono-openapi';
4
+ import { setGlobalResponseHooks } from '../http/route-helpers.js';
5
+ import { deepMerge } from '../utils/deep-merge.js';
6
+ import { createLogger } from '../utils/logger.js';
7
+ import { defaultOpenAPIComponents } from './defaults.js';
8
+ import { createCorsHeadersHook, createPaginationHeadersHook, createRateLimitHeadersHook, } from './hooks.js';
9
+ /**
10
+ * Configure OpenAPI documentation for the application.
11
+ *
12
+ * Behavior by environment:
13
+ * - development: Serve specs and UI at /api and /api/openapi.json
14
+ * - production: Write spec to disk (openapi.json), no serving
15
+ * - test: Disabled by default
16
+ *
17
+ * This function automatically sets up response hooks based on enabled features
18
+ * and merges them with any application-provided hooks.
19
+ *
20
+ * @param options - Configuration options
21
+ */
22
+ export function configureOpenAPI(options) {
23
+ const { app, environment, openapi, rateLimit, middleware } = options;
24
+ if (!openapi.enabled) {
25
+ return;
26
+ }
27
+ const logger = createLogger('Glasswork:OpenAPI', true);
28
+ const shouldServeSpecs = openapi.serveSpecs ?? environment === 'development';
29
+ 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
+ // Filter components based on enabled features (only include headers that are actually used)
42
+ const filteredComponents = filterComponentsByFeatures(defaultOpenAPIComponents, {
43
+ rateLimitEnabled: rateLimit?.enabled ?? false,
44
+ corsEnabled: !!middleware?.cors,
45
+ });
46
+ // Merge application documentation with filtered Glasswork defaults
47
+ const mergedDocumentation = deepMerge({ components: filteredComponents }, openapi.documentation || {});
48
+ // Serve OpenAPI spec endpoint (development only by default)
49
+ if (shouldServeSpecs) {
50
+ app.get('/api/openapi.json', openAPISpecs(app, { documentation: mergedDocumentation }));
51
+ }
52
+ // Serve Swagger UI (development only by default)
53
+ if (shouldServeUI) {
54
+ app.get('/api', swaggerUI({ url: '/api/openapi.json' }));
55
+ }
56
+ // Write OpenAPI spec to file (production/Lambda)
57
+ if (openapi.writeToFile) {
58
+ const filePath = openapi.writeToFile;
59
+ // Generate spec and write to file
60
+ // This happens after routes are registered
61
+ setTimeout(async () => {
62
+ try {
63
+ // Use Hono's built-in request method to simulate the request
64
+ const response = await app.request('/api/openapi.json');
65
+ const specContent = await response.text();
66
+ await writeFile(filePath, specContent, 'utf-8');
67
+ logger.info(`OpenAPI spec written to ${filePath}`);
68
+ }
69
+ catch (error) {
70
+ logger.error('Failed to write OpenAPI spec:', error);
71
+ }
72
+ }, 1000); // Wait for routes to be registered
73
+ }
74
+ }
75
+ /**
76
+ * Filter component headers based on enabled features.
77
+ * This removes header definitions from the components schema that won't be used.
78
+ */
79
+ function filterComponentsByFeatures(components, features) {
80
+ if (!components.headers) {
81
+ return components;
82
+ }
83
+ const filteredHeaders = Object.fromEntries(Object.entries(components.headers).filter(([name]) => shouldIncludeHeader(name, features)));
84
+ return {
85
+ ...components,
86
+ headers: filteredHeaders,
87
+ };
88
+ }
89
+ /**
90
+ * Determine if a header should be included based on feature configuration
91
+ */
92
+ function shouldIncludeHeader(headerName, { rateLimitEnabled, corsEnabled }) {
93
+ // Always include pagination headers (they're route-specific, not feature-gated)
94
+ if (headerName.startsWith('X-')) {
95
+ return true;
96
+ }
97
+ // Include CORS headers only if CORS is enabled
98
+ if (headerName === 'Access-Control-Allow-Origin') {
99
+ return corsEnabled;
100
+ }
101
+ // Include rate limit headers only if rate limiting is enabled
102
+ if (headerName.startsWith('RateLimit-') || headerName === 'Retry-After') {
103
+ return rateLimitEnabled;
104
+ }
105
+ // Include other headers by default
106
+ return true;
107
+ }
108
+ //# sourceMappingURL=openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { OpenAPIV3 } from 'openapi-types';
2
+ /**
3
+ * Default OpenAPI components provided by Glasswork.
4
+ * These include headers for features implemented by the framework:
5
+ * - Rate limiting headers
6
+ * - Pagination headers
7
+ * - CORS headers
8
+ * - Error response schemas
9
+ */
10
+ export declare const defaultOpenAPIComponents: OpenAPIV3.ComponentsObject;
11
+ //# sourceMappingURL=openapi-defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-defaults.d.ts","sourceRoot":"","sources":["../src/openapi-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,CAAC,gBAuHhD,CAAC"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Default OpenAPI components provided by Glasswork.
3
+ * These include headers for features implemented by the framework:
4
+ * - Rate limiting headers
5
+ * - Pagination headers
6
+ * - CORS headers
7
+ * - Error response schemas
8
+ */
9
+ export const defaultOpenAPIComponents = {
10
+ schemas: {
11
+ // Error response schemas
12
+ ErrorResponse: {
13
+ type: 'object',
14
+ required: ['error'],
15
+ properties: {
16
+ error: {
17
+ type: 'string',
18
+ maxLength: 1000,
19
+ pattern: '^[\\s\\S]*$', // Any characters including whitespace
20
+ description: 'Error message',
21
+ },
22
+ issues: {
23
+ type: 'array',
24
+ maxItems: 100,
25
+ items: {
26
+ type: 'object',
27
+ required: ['message'],
28
+ properties: {
29
+ message: {
30
+ type: 'string',
31
+ maxLength: 500,
32
+ },
33
+ path: {
34
+ type: 'array',
35
+ maxItems: 20,
36
+ items: {
37
+ oneOf: [
38
+ { type: 'string', maxLength: 200 },
39
+ { type: 'number', format: 'int32' },
40
+ ],
41
+ },
42
+ },
43
+ },
44
+ },
45
+ description: 'Validation error details',
46
+ },
47
+ },
48
+ },
49
+ },
50
+ headers: {
51
+ // CORS headers
52
+ 'Access-Control-Allow-Origin': {
53
+ description: 'Allowed origin for CORS requests',
54
+ schema: {
55
+ type: 'string',
56
+ maxLength: 2048,
57
+ pattern: '^https?://[a-zA-Z0-9.-]+(:[0-9]+)?$',
58
+ },
59
+ },
60
+ // Rate limiting headers (from rate-limit.ts)
61
+ 'RateLimit-Limit': {
62
+ description: 'Total requests allowed in the current time window',
63
+ schema: {
64
+ type: 'integer',
65
+ format: 'int32',
66
+ minimum: 0,
67
+ maximum: 10000,
68
+ },
69
+ },
70
+ 'RateLimit-Remaining': {
71
+ description: 'Remaining requests in the current window',
72
+ schema: {
73
+ type: 'integer',
74
+ format: 'int32',
75
+ minimum: 0,
76
+ maximum: 10000,
77
+ },
78
+ },
79
+ 'RateLimit-Reset': {
80
+ description: 'Seconds until the rate limit window resets',
81
+ schema: {
82
+ type: 'integer',
83
+ format: 'int32',
84
+ minimum: 0,
85
+ maximum: 3600,
86
+ },
87
+ },
88
+ 'Retry-After': {
89
+ description: 'Seconds to wait before retrying the request (429 response)',
90
+ schema: {
91
+ type: 'integer',
92
+ format: 'int32',
93
+ minimum: 1,
94
+ maximum: 3600,
95
+ },
96
+ },
97
+ // Pagination headers (from pagination.ts)
98
+ 'X-Total-Count': {
99
+ description: 'Total number of items available',
100
+ schema: {
101
+ type: 'integer',
102
+ format: 'int32',
103
+ minimum: 0,
104
+ maximum: 2147483647, // Max int32
105
+ },
106
+ },
107
+ 'X-Page': {
108
+ description: 'Current page number',
109
+ schema: {
110
+ type: 'integer',
111
+ format: 'int32',
112
+ minimum: 1,
113
+ maximum: 2147483647, // Max int32
114
+ },
115
+ },
116
+ 'X-Limit': {
117
+ description: 'Number of items per page',
118
+ schema: {
119
+ type: 'integer',
120
+ format: 'int32',
121
+ minimum: 1,
122
+ maximum: 100,
123
+ },
124
+ },
125
+ },
126
+ };
127
+ //# sourceMappingURL=openapi-defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-defaults.js","sourceRoot":"","sources":["../src/openapi-defaults.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { OpenAPIResponseHook } from './types.js';
2
+ /**
3
+ * Create a hook that adds CORS headers to responses
4
+ */
5
+ export declare function createCorsHeadersHook(enabled: boolean): OpenAPIResponseHook;
6
+ /**
7
+ * Create a hook that adds rate limiting headers to responses
8
+ */
9
+ export declare function createRateLimitHeadersHook(enabled: boolean): OpenAPIResponseHook;
10
+ /**
11
+ * Create a hook that adds pagination headers to 200 responses
12
+ */
13
+ export declare function createPaginationHeadersHook(): OpenAPIResponseHook;
14
+ //# sourceMappingURL=openapi-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-hooks.d.ts","sourceRoot":"","sources":["../src/openapi-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAc3E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAgChF;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,mBAAmB,CAyBjE"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Create a hook that adds CORS headers to responses
3
+ */
4
+ export function createCorsHeadersHook(enabled) {
5
+ return (response, _context) => {
6
+ if (!enabled)
7
+ return response;
8
+ return {
9
+ ...response,
10
+ headers: {
11
+ ...response.headers,
12
+ 'Access-Control-Allow-Origin': {
13
+ $ref: '#/components/headers/Access-Control-Allow-Origin',
14
+ },
15
+ },
16
+ };
17
+ };
18
+ }
19
+ /**
20
+ * Create a hook that adds rate limiting headers to responses
21
+ */
22
+ export function createRateLimitHeadersHook(enabled) {
23
+ return (response, context) => {
24
+ if (!enabled)
25
+ return response;
26
+ const { statusCode } = context;
27
+ const headers = {
28
+ 'RateLimit-Limit': {
29
+ $ref: '#/components/headers/RateLimit-Limit',
30
+ },
31
+ 'RateLimit-Remaining': {
32
+ $ref: '#/components/headers/RateLimit-Remaining',
33
+ },
34
+ 'RateLimit-Reset': {
35
+ $ref: '#/components/headers/RateLimit-Reset',
36
+ },
37
+ };
38
+ // Add Retry-After header for 429 responses
39
+ if (statusCode === '429') {
40
+ headers['Retry-After'] = {
41
+ $ref: '#/components/headers/Retry-After',
42
+ };
43
+ }
44
+ return {
45
+ ...response,
46
+ headers: {
47
+ ...response.headers,
48
+ ...headers,
49
+ },
50
+ };
51
+ };
52
+ }
53
+ /**
54
+ * Create a hook that adds pagination headers to 200 responses
55
+ */
56
+ export function createPaginationHeadersHook() {
57
+ return (response, context) => {
58
+ const { statusCode, routeConfig } = context;
59
+ // Only add pagination headers to 200 responses with paginate flag
60
+ if (statusCode !== '200' || !routeConfig.paginate) {
61
+ return response;
62
+ }
63
+ return {
64
+ ...response,
65
+ headers: {
66
+ ...response.headers,
67
+ 'X-Total-Count': {
68
+ $ref: '#/components/headers/X-Total-Count',
69
+ },
70
+ 'X-Page': {
71
+ $ref: '#/components/headers/X-Page',
72
+ },
73
+ 'X-Limit': {
74
+ $ref: '#/components/headers/X-Limit',
75
+ },
76
+ },
77
+ };
78
+ };
79
+ }
80
+ //# sourceMappingURL=openapi-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-hooks.js","sourceRoot":"","sources":["../src/openapi-hooks.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QAE9B,OAAO;YACL,GAAG,QAAQ;YACX,OAAO,EAAE;gBACP,GAAG,QAAQ,CAAC,OAAO;gBACnB,6BAA6B,EAAE;oBAC7B,IAAI,EAAE,kDAAkD;iBACzD;aACF;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAAgB;IACzD,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC3B,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QAC/B,MAAM,OAAO,GAAqC;YAChD,iBAAiB,EAAE;gBACjB,IAAI,EAAE,sCAAsC;aAC7C;YACD,qBAAqB,EAAE;gBACrB,IAAI,EAAE,0CAA0C;aACjD;YACD,iBAAiB,EAAE;gBACjB,IAAI,EAAE,sCAAsC;aAC7C;SACF,CAAC;QAEF,2CAA2C;QAC3C,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,aAAa,CAAC,GAAG;gBACvB,IAAI,EAAE,kCAAkC;aACzC,CAAC;QACJ,CAAC;QAED,OAAO;YACL,GAAG,QAAQ;YACX,OAAO,EAAE;gBACP,GAAG,QAAQ,CAAC,OAAO;gBACnB,GAAG,OAAO;aACX;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC3B,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;QAE5C,kEAAkE;QAClE,IAAI,UAAU,KAAK,KAAK,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;YAClD,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,OAAO;YACL,GAAG,QAAQ;YACX,OAAO,EAAE;gBACP,GAAG,QAAQ,CAAC,OAAO;gBACnB,eAAe,EAAE;oBACf,IAAI,EAAE,oCAAoC;iBAC3C;gBACD,QAAQ,EAAE;oBACR,IAAI,EAAE,6BAA6B;iBACpC;gBACD,SAAS,EAAE;oBACT,IAAI,EAAE,8BAA8B;iBACrC;aACF;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { Hono } from 'hono';
2
+ import type { Environment, MiddlewareOptions, OpenAPIOptions, RateLimitOptions } from './types.js';
3
+ export interface ConfigureOpenAPIOptions {
4
+ app: Hono;
5
+ environment: Environment;
6
+ openapi: OpenAPIOptions;
7
+ rateLimit?: RateLimitOptions;
8
+ middleware?: MiddlewareOptions;
9
+ }
10
+ /**
11
+ * Configure OpenAPI documentation for the application.
12
+ *
13
+ * Behavior by environment:
14
+ * - development: Serve specs and UI at /api and /api/openapi.json
15
+ * - production: Write spec to disk (openapi.json), no serving
16
+ * - test: Disabled by default
17
+ *
18
+ * This function automatically sets up response hooks based on enabled features
19
+ * and merges them with any application-provided hooks.
20
+ *
21
+ * @param options - Configuration options
22
+ */
23
+ export declare function configureOpenAPI(options: ConfigureOpenAPIOptions): void;
24
+ //# sourceMappingURL=openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAUjC,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnG,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;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAgEvE"}
@@ -0,0 +1,106 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { swaggerUI } from '@hono/swagger-ui';
3
+ import { openAPISpecs } from 'hono-openapi';
4
+ import { defaultOpenAPIComponents } from './openapi-defaults.js';
5
+ import { createCorsHeadersHook, createPaginationHeadersHook, createRateLimitHeadersHook, } from './openapi-hooks.js';
6
+ import { setGlobalResponseHooks } from './route-helpers.js';
7
+ import { deepMerge } from './utils.js';
8
+ /**
9
+ * Configure OpenAPI documentation for the application.
10
+ *
11
+ * Behavior by environment:
12
+ * - development: Serve specs and UI at /api and /api/openapi.json
13
+ * - production: Write spec to disk (openapi.json), no serving
14
+ * - test: Disabled by default
15
+ *
16
+ * This function automatically sets up response hooks based on enabled features
17
+ * and merges them with any application-provided hooks.
18
+ *
19
+ * @param options - Configuration options
20
+ */
21
+ export function configureOpenAPI(options) {
22
+ const { app, environment, openapi, rateLimit, middleware } = options;
23
+ if (!openapi.enabled) {
24
+ return;
25
+ }
26
+ const shouldServeSpecs = openapi.serveSpecs ?? environment === 'development';
27
+ const shouldServeUI = openapi.serveUI ?? environment === 'development';
28
+ // Build response hooks based on enabled features
29
+ const frameworkHooks = [
30
+ createCorsHeadersHook(!!middleware?.cors),
31
+ createRateLimitHeadersHook(rateLimit?.enabled ?? false),
32
+ createPaginationHeadersHook(),
33
+ ];
34
+ // Merge with application-provided hooks
35
+ const allHooks = [...frameworkHooks, ...(openapi.responseHooks || [])];
36
+ // Make hooks available to route definitions globally
37
+ // This must be done synchronously before routes are mounted
38
+ setGlobalResponseHooks(allHooks);
39
+ // Filter components based on enabled features (only include headers that are actually used)
40
+ const filteredComponents = filterComponentsByFeatures(defaultOpenAPIComponents, {
41
+ rateLimitEnabled: rateLimit?.enabled ?? false,
42
+ corsEnabled: !!middleware?.cors,
43
+ });
44
+ // Merge application documentation with filtered Glasswork defaults
45
+ const mergedDocumentation = deepMerge({ components: filteredComponents }, openapi.documentation || {});
46
+ // Serve OpenAPI spec endpoint (development only by default)
47
+ if (shouldServeSpecs) {
48
+ app.get('/api/openapi.json', openAPISpecs(app, { documentation: mergedDocumentation }));
49
+ }
50
+ // Serve Swagger UI (development only by default)
51
+ if (shouldServeUI) {
52
+ app.get('/api', swaggerUI({ url: '/api/openapi.json' }));
53
+ }
54
+ // Write OpenAPI spec to file (production/Lambda)
55
+ if (openapi.writeToFile) {
56
+ const filePath = openapi.writeToFile;
57
+ // Generate spec and write to file
58
+ // This happens after routes are registered
59
+ setTimeout(async () => {
60
+ try {
61
+ // Use Hono's built-in request method to simulate the request
62
+ const response = await app.request('/api/openapi.json');
63
+ const specContent = await response.text();
64
+ await writeFile(filePath, specContent, 'utf-8');
65
+ console.log(`[Glasswork] OpenAPI spec written to ${filePath}`);
66
+ }
67
+ catch (error) {
68
+ console.error('[Glasswork] Failed to write OpenAPI spec:', error);
69
+ }
70
+ }, 1000); // Wait for routes to be registered
71
+ }
72
+ }
73
+ /**
74
+ * Filter component headers based on enabled features.
75
+ * This removes header definitions from the components schema that won't be used.
76
+ */
77
+ function filterComponentsByFeatures(components, features) {
78
+ if (!components.headers) {
79
+ return components;
80
+ }
81
+ const filteredHeaders = Object.fromEntries(Object.entries(components.headers).filter(([name]) => shouldIncludeHeader(name, features)));
82
+ return {
83
+ ...components,
84
+ headers: filteredHeaders,
85
+ };
86
+ }
87
+ /**
88
+ * Determine if a header should be included based on feature configuration
89
+ */
90
+ function shouldIncludeHeader(headerName, { rateLimitEnabled, corsEnabled }) {
91
+ // Always include pagination headers (they're route-specific, not feature-gated)
92
+ if (headerName.startsWith('X-')) {
93
+ return true;
94
+ }
95
+ // Include CORS headers only if CORS is enabled
96
+ if (headerName === 'AccessControlAllowOrigin') {
97
+ return corsEnabled;
98
+ }
99
+ // Include rate limit headers only if rate limiting is enabled
100
+ if (headerName.startsWith('RateLimit') || headerName === 'RetryAfter') {
101
+ return rateLimitEnabled;
102
+ }
103
+ // Include other headers by default
104
+ return true;
105
+ }
106
+ //# sourceMappingURL=openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}