glasswork 0.6.2 → 0.7.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.
- package/README.md +37 -9
- package/dist/core/bootstrap.d.ts.map +1 -1
- package/dist/core/bootstrap.js +43 -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 +137 -129
- 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/list-query/casl.d.ts +9 -8
- package/dist/list-query/casl.d.ts.map +1 -1
- package/dist/list-query/casl.js +29 -11
- package/dist/list-query/casl.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 +8 -4
- package/dist/utils/serialize-prisma-types.js.map +1 -1
- package/package.json +11 -3
package/dist/list-query/casl.js
CHANGED
|
@@ -1,23 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Dynamically import @casl/prisma to keep it as a truly optional dependency.
|
|
3
|
+
* Only loaded when CASL scope functions are actually used.
|
|
4
|
+
*/
|
|
5
|
+
async function getAccessibleBy() {
|
|
6
|
+
try {
|
|
7
|
+
const { accessibleBy } = await import('@casl/prisma');
|
|
8
|
+
return accessibleBy;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
throw new Error('@casl/prisma package not found. Install with: npm install @casl/ability @casl/prisma\n' +
|
|
12
|
+
'Note: @casl/ability is required as a peer dependency of @casl/prisma.');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
2
15
|
/**
|
|
3
16
|
* Create a CASL scope function that can be applied to a ListQueryBuilder
|
|
4
17
|
*
|
|
5
|
-
* @param ability - The CASL ability instance
|
|
18
|
+
* @param ability - The CASL ability instance (any object that works with @casl/prisma's accessibleBy)
|
|
6
19
|
* @param subject - The Prisma model name (e.g., 'User', 'Organization')
|
|
7
20
|
* @returns A function that applies the CASL conditions to a builder
|
|
8
21
|
*
|
|
9
22
|
* @example
|
|
10
23
|
* ```typescript
|
|
11
|
-
*
|
|
24
|
+
* import { PureAbility } from '@casl/ability';
|
|
25
|
+
* const scope = await createCaslScope(ability, 'User');
|
|
12
26
|
* const params = createListQuery({ filter: UserFilterSchema })
|
|
13
27
|
* .parse(query)
|
|
14
28
|
* .apply(scope)
|
|
15
29
|
* .build();
|
|
16
30
|
* ```
|
|
17
31
|
*/
|
|
18
|
-
// biome-ignore lint/suspicious/noExplicitAny:
|
|
19
|
-
export function createCaslScope(ability, subject) {
|
|
20
|
-
|
|
32
|
+
// biome-ignore lint/suspicious/noExplicitAny: Ability type is generic to work with any CASL ability instance. The actual type checking happens at runtime when accessibleBy is called.
|
|
33
|
+
export async function createCaslScope(ability, subject) {
|
|
34
|
+
const accessibleBy = await getAccessibleBy();
|
|
35
|
+
// biome-ignore lint/suspicious/noExplicitAny: CASL's accessibleBy function has type constraints that don't perfectly align with generic ability types. This cast is necessary for runtime compatibility.
|
|
21
36
|
const conditions = accessibleBy(ability)[subject];
|
|
22
37
|
// biome-ignore lint/suspicious/noExplicitAny: Return type must be generic to work with any ListQueryBuilder regardless of its schema types. This maintains maximum flexibility for consumers.
|
|
23
38
|
return (builder) => builder.scope(conditions);
|
|
@@ -26,22 +41,25 @@ export function createCaslScope(ability, subject) {
|
|
|
26
41
|
* Create a CASL scope helper that extracts ability from context
|
|
27
42
|
*
|
|
28
43
|
* @param subject - The Prisma model name (e.g., 'User', 'Organization')
|
|
29
|
-
* @returns
|
|
44
|
+
* @returns An async function that takes a builder and ability, and applies the CASL conditions
|
|
30
45
|
*
|
|
31
46
|
* @example
|
|
32
47
|
* ```typescript
|
|
48
|
+
* import { PureAbility } from '@casl/ability';
|
|
33
49
|
* const scopeUsers = withCaslScope('User');
|
|
34
|
-
* const params = createListQuery({ filter: UserFilterSchema })
|
|
50
|
+
* const params = await createListQuery({ filter: UserFilterSchema })
|
|
35
51
|
* .parse(query)
|
|
36
|
-
* .
|
|
52
|
+
* .applyAsync(scopeUsers, ability)
|
|
37
53
|
* .build();
|
|
38
54
|
* ```
|
|
39
55
|
*/
|
|
40
56
|
export function withCaslScope(subject) {
|
|
41
|
-
|
|
57
|
+
// biome-ignore lint/suspicious/noExplicitAny: Ability type is generic to work with any CASL ability instance. The actual type checking happens at runtime when accessibleBy is called.
|
|
58
|
+
return async (
|
|
42
59
|
// biome-ignore lint/suspicious/noExplicitAny: ListQueryBuilder must be generic to work with any schema types. This maintains maximum flexibility for consumers.
|
|
43
60
|
builder, ability) => {
|
|
44
|
-
|
|
61
|
+
const accessibleBy = await getAccessibleBy();
|
|
62
|
+
// biome-ignore lint/suspicious/noExplicitAny: CASL's accessibleBy function has type constraints that don't perfectly align with generic ability types. This cast is necessary for runtime compatibility.
|
|
45
63
|
return builder.scope(accessibleBy(ability)[subject]);
|
|
46
64
|
};
|
|
47
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"casl.js","sourceRoot":"","sources":["../../src/list-query/casl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"casl.js","sourceRoot":"","sources":["../../src/list-query/casl.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,KAAK,UAAU,eAAe;IAC5B,IAAI,CAAC;QACH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QACtD,OAAO,YAAY,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,wFAAwF;YACtF,uEAAuE,CAC1E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,uLAAuL;AACvL,MAAM,CAAC,KAAK,UAAU,eAAe,CAAiB,OAAiB,EAAE,OAAe;IACtF,MAAM,YAAY,GAAG,MAAM,eAAe,EAAE,CAAC;IAC7C,yMAAyM;IACzM,MAAM,UAAU,GAAG,YAAY,CAAC,OAAc,CAAC,CAAC,OAAO,CAAC,CAAC;IACzD,8LAA8L;IAC9L,OAAO,CAAC,OAAmC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAC5E,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,uLAAuL;IACvL,OAAO,KAAK;IACV,gKAAgK;IAChK,OAAmC,EACnC,OAAiB,EACjB,EAAE;QACF,MAAM,YAAY,GAAG,MAAM,eAAe,EAAE,CAAC;QAC7C,yMAAyM;QACzM,OAAO,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,OAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAwLzD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,CAuDtF"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { getClientIp } from '../utils/get-client-ip.js';
|
|
2
2
|
import { createLogger } from '../utils/logger.js';
|
|
3
3
|
const logger = createLogger('Glasswork:RateLimit', true);
|
|
4
|
+
/** Default cleanup interval for memory store (1 minute) */
|
|
5
|
+
const DEFAULT_CLEANUP_INTERVAL_MS = 60_000;
|
|
4
6
|
/**
|
|
5
|
-
* In-memory rate limiter storage
|
|
7
|
+
* In-memory rate limiter storage.
|
|
8
|
+
*
|
|
9
|
+
* Includes automatic cleanup of expired entries to prevent memory leaks.
|
|
10
|
+
* Call `stopCleanup()` when shutting down to clear the interval timer.
|
|
6
11
|
*/
|
|
7
12
|
class MemoryStore {
|
|
8
13
|
store = new Map();
|
|
14
|
+
cleanupTimer = null;
|
|
9
15
|
async get(key) {
|
|
10
16
|
const item = this.store.get(key);
|
|
11
17
|
if (!item) {
|
|
@@ -26,9 +32,14 @@ class MemoryStore {
|
|
|
26
32
|
item.count += 1;
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Start periodic cleanup of expired entries.
|
|
37
|
+
* @param intervalMs - Cleanup interval in milliseconds (default: 60000)
|
|
38
|
+
*/
|
|
39
|
+
startCleanup(intervalMs = DEFAULT_CLEANUP_INTERVAL_MS) {
|
|
40
|
+
// Clear any existing timer to prevent duplicates
|
|
41
|
+
this.stopCleanup();
|
|
42
|
+
this.cleanupTimer = setInterval(() => {
|
|
32
43
|
const now = Date.now();
|
|
33
44
|
for (const [key, item] of this.store.entries()) {
|
|
34
45
|
if (item.windowEnd < now) {
|
|
@@ -37,27 +48,48 @@ class MemoryStore {
|
|
|
37
48
|
}
|
|
38
49
|
}, intervalMs);
|
|
39
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Stop the cleanup timer.
|
|
53
|
+
* Should be called when shutting down to prevent memory leaks in serverless environments.
|
|
54
|
+
*/
|
|
55
|
+
stopCleanup() {
|
|
56
|
+
if (this.cleanupTimer) {
|
|
57
|
+
clearInterval(this.cleanupTimer);
|
|
58
|
+
this.cleanupTimer = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
40
61
|
}
|
|
41
62
|
/**
|
|
42
|
-
* DynamoDB rate limiter storage
|
|
63
|
+
* DynamoDB rate limiter storage.
|
|
64
|
+
*
|
|
65
|
+
* NOTE: This store "fails open" on errors - if DynamoDB is unavailable,
|
|
66
|
+
* requests will be allowed through without rate limiting. This is intentional
|
|
67
|
+
* to prevent DynamoDB issues from blocking all traffic, but means rate limiting
|
|
68
|
+
* is not guaranteed during outages. Monitor DynamoDB errors in your logs.
|
|
43
69
|
*/
|
|
44
70
|
class DynamoDBStore {
|
|
45
|
-
|
|
71
|
+
clientPromise;
|
|
46
72
|
tableName;
|
|
47
73
|
constructor(tableName, region) {
|
|
48
74
|
this.tableName = tableName;
|
|
49
|
-
//
|
|
50
|
-
this.initClient(region);
|
|
75
|
+
// Initialize client lazily - Promise is stored and awaited on first use
|
|
76
|
+
this.clientPromise = this.initClient(region);
|
|
51
77
|
}
|
|
52
78
|
async initClient(region) {
|
|
53
79
|
const { DynamoDBClient } = await import('@aws-sdk/client-dynamodb');
|
|
54
80
|
const { DynamoDBDocumentClient } = await import('@aws-sdk/lib-dynamodb');
|
|
55
81
|
const dynamoClient = new DynamoDBClient(region ? { region } : {});
|
|
56
|
-
|
|
82
|
+
return DynamoDBDocumentClient.from(dynamoClient);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get the initialized DynamoDB client, waiting for initialization if needed.
|
|
86
|
+
*/
|
|
87
|
+
async getClient() {
|
|
88
|
+
return this.clientPromise;
|
|
57
89
|
}
|
|
58
90
|
async get(key) {
|
|
59
91
|
const { GetCommand } = await import('@aws-sdk/lib-dynamodb');
|
|
60
|
-
const client = this.
|
|
92
|
+
const client = await this.getClient();
|
|
61
93
|
try {
|
|
62
94
|
// @ts-expect-error - client type is complex
|
|
63
95
|
const { Item } = await client.send(new GetCommand({
|
|
@@ -72,13 +104,15 @@ class DynamoDBStore {
|
|
|
72
104
|
windowEnd: Item.windowEnd,
|
|
73
105
|
};
|
|
74
106
|
}
|
|
75
|
-
catch {
|
|
107
|
+
catch (error) {
|
|
108
|
+
// Log error but fail open to prevent DynamoDB issues from blocking traffic
|
|
109
|
+
logger.error('DynamoDB rate limit get failed:', error);
|
|
76
110
|
return null;
|
|
77
111
|
}
|
|
78
112
|
}
|
|
79
113
|
async set(key, value) {
|
|
80
114
|
const { PutCommand } = await import('@aws-sdk/lib-dynamodb');
|
|
81
|
-
const client = this.
|
|
115
|
+
const client = await this.getClient();
|
|
82
116
|
const expiresAt = Math.floor(value.windowEnd / 1000);
|
|
83
117
|
try {
|
|
84
118
|
// @ts-expect-error - client type is complex
|
|
@@ -98,7 +132,7 @@ class DynamoDBStore {
|
|
|
98
132
|
}
|
|
99
133
|
async increment(key) {
|
|
100
134
|
const { UpdateCommand } = await import('@aws-sdk/lib-dynamodb');
|
|
101
|
-
const client = this.
|
|
135
|
+
const client = await this.getClient();
|
|
102
136
|
try {
|
|
103
137
|
// @ts-expect-error - client type is complex
|
|
104
138
|
await client.send(new UpdateCommand({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;AAEzD
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"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"}
|
|
@@ -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"}
|