@frontmcp/auth 0.0.1 → 0.8.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 +11 -0
- package/authorization/authorization.types.d.ts +236 -0
- package/authorization/authorization.types.d.ts.map +1 -0
- package/authorization/index.d.ts +9 -0
- package/authorization/index.d.ts.map +1 -0
- package/cimd/cimd-redis.cache.d.ts +111 -0
- package/cimd/cimd-redis.cache.d.ts.map +1 -0
- package/cimd/cimd.cache.d.ts +200 -0
- package/cimd/cimd.cache.d.ts.map +1 -0
- package/cimd/cimd.errors.d.ts +124 -0
- package/cimd/cimd.errors.d.ts.map +1 -0
- package/cimd/cimd.logger.d.ts +39 -0
- package/cimd/cimd.logger.d.ts.map +1 -0
- package/cimd/cimd.service.d.ts +88 -0
- package/cimd/cimd.service.d.ts.map +1 -0
- package/cimd/cimd.types.d.ts +178 -0
- package/cimd/cimd.types.d.ts.map +1 -0
- package/cimd/cimd.validator.d.ts +49 -0
- package/cimd/cimd.validator.d.ts.map +1 -0
- package/cimd/index.d.ts +17 -0
- package/cimd/index.d.ts.map +1 -0
- package/esm/index.mjs +4001 -0
- package/esm/package.json +59 -0
- package/index.d.ts +44 -0
- package/index.d.ts.map +1 -0
- package/index.js +4131 -0
- package/jwks/dev-key-persistence.d.ts +70 -0
- package/jwks/dev-key-persistence.d.ts.map +1 -0
- package/jwks/index.d.ts +20 -0
- package/jwks/index.d.ts.map +1 -0
- package/jwks/jwks.service.d.ts +69 -0
- package/jwks/jwks.service.d.ts.map +1 -0
- package/jwks/jwks.types.d.ts +33 -0
- package/jwks/jwks.types.d.ts.map +1 -0
- package/jwks/jwks.utils.d.ts +5 -0
- package/jwks/jwks.utils.d.ts.map +1 -0
- package/package.json +2 -2
- package/session/authorization-vault.d.ts +667 -0
- package/session/authorization-vault.d.ts.map +1 -0
- package/session/authorization.store.d.ts +311 -0
- package/session/authorization.store.d.ts.map +1 -0
- package/session/index.d.ts +19 -0
- package/session/index.d.ts.map +1 -0
- package/session/storage/in-memory-authorization-vault.d.ts +53 -0
- package/session/storage/in-memory-authorization-vault.d.ts.map +1 -0
- package/session/storage/index.d.ts +17 -0
- package/session/storage/index.d.ts.map +1 -0
- package/session/storage/storage-authorization-vault.d.ts +107 -0
- package/session/storage/storage-authorization-vault.d.ts.map +1 -0
- package/session/storage/storage-token-store.d.ts +92 -0
- package/session/storage/storage-token-store.d.ts.map +1 -0
- package/session/token.store.d.ts +39 -0
- package/session/token.store.d.ts.map +1 -0
- package/session/token.vault.d.ts +33 -0
- package/session/token.vault.d.ts.map +1 -0
- package/session/utils/index.d.ts +5 -0
- package/session/utils/index.d.ts.map +1 -0
- package/session/utils/tiny-ttl-cache.d.ts +20 -0
- package/session/utils/tiny-ttl-cache.d.ts.map +1 -0
- package/session/vault-encryption.d.ts +190 -0
- package/session/vault-encryption.d.ts.map +1 -0
- package/ui/base-layout.d.ts +170 -0
- package/ui/base-layout.d.ts.map +1 -0
- package/ui/index.d.ts +10 -0
- package/ui/index.d.ts.map +1 -0
- package/ui/templates.d.ts +134 -0
- package/ui/templates.d.ts.map +1 -0
- package/utils/audience.validator.d.ts +130 -0
- package/utils/audience.validator.d.ts.map +1 -0
- package/utils/index.d.ts +8 -0
- package/utils/index.d.ts.map +1 -0
- package/utils/www-authenticate.utils.d.ts +98 -0
- package/utils/www-authenticate.utils.d.ts.map +1 -0
- package/vault/auth-providers.types.d.ts +262 -0
- package/vault/auth-providers.types.d.ts.map +1 -0
- package/vault/credential-cache.d.ts +98 -0
- package/vault/credential-cache.d.ts.map +1 -0
- package/vault/credential-helpers.d.ts +14 -0
- package/vault/credential-helpers.d.ts.map +1 -0
- package/vault/index.d.ts +10 -0
- package/vault/index.d.ts.map +1 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CIMD (Client ID Metadata Documents) Error Classes
|
|
3
|
+
*
|
|
4
|
+
* Standalone error classes for CIMD-specific failures.
|
|
5
|
+
* These do not depend on the SDK's error classes.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Base class for all CIMD-related errors.
|
|
9
|
+
*
|
|
10
|
+
* Provides a consistent API with code, statusCode, and getPublicMessage()
|
|
11
|
+
* that mirrors the SDK's PublicMcpError but without the dependency.
|
|
12
|
+
*/
|
|
13
|
+
export declare abstract class CimdError extends Error {
|
|
14
|
+
/**
|
|
15
|
+
* Error code for machine-readable identification.
|
|
16
|
+
*/
|
|
17
|
+
readonly code: string;
|
|
18
|
+
/**
|
|
19
|
+
* HTTP status code to return when this error occurs.
|
|
20
|
+
*/
|
|
21
|
+
readonly statusCode: number;
|
|
22
|
+
/**
|
|
23
|
+
* The client_id URL that caused the error.
|
|
24
|
+
*/
|
|
25
|
+
readonly clientIdUrl?: string;
|
|
26
|
+
constructor(message: string, code: string, statusCode: number, clientIdUrl?: string);
|
|
27
|
+
/**
|
|
28
|
+
* Get a message safe to return to clients.
|
|
29
|
+
* Override in subclasses to provide user-friendly messages.
|
|
30
|
+
*/
|
|
31
|
+
getPublicMessage(): string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Thrown when the client_id URL is invalid.
|
|
35
|
+
*
|
|
36
|
+
* Examples:
|
|
37
|
+
* - HTTP URL instead of HTTPS
|
|
38
|
+
* - Missing path component
|
|
39
|
+
* - Invalid URL format
|
|
40
|
+
*/
|
|
41
|
+
export declare class InvalidClientIdUrlError extends CimdError {
|
|
42
|
+
readonly reason: string;
|
|
43
|
+
constructor(clientIdUrl: string, reason: string);
|
|
44
|
+
getPublicMessage(): string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Thrown when fetching the CIMD document fails.
|
|
48
|
+
*
|
|
49
|
+
* Examples:
|
|
50
|
+
* - Network timeout
|
|
51
|
+
* - HTTP error (404, 500, etc.)
|
|
52
|
+
* - Connection refused
|
|
53
|
+
*/
|
|
54
|
+
export declare class CimdFetchError extends CimdError {
|
|
55
|
+
readonly httpStatus?: number;
|
|
56
|
+
readonly originalError?: Error;
|
|
57
|
+
constructor(clientIdUrl: string, message: string, options?: {
|
|
58
|
+
httpStatus?: number;
|
|
59
|
+
originalError?: Error;
|
|
60
|
+
});
|
|
61
|
+
getPublicMessage(): string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Thrown when the CIMD document fails validation.
|
|
65
|
+
*
|
|
66
|
+
* Examples:
|
|
67
|
+
* - Missing required fields
|
|
68
|
+
* - Invalid field values
|
|
69
|
+
* - client_id in document doesn't match URL
|
|
70
|
+
*/
|
|
71
|
+
export declare class CimdValidationError extends CimdError {
|
|
72
|
+
readonly validationErrors: string[];
|
|
73
|
+
constructor(clientIdUrl: string, errors: string[]);
|
|
74
|
+
getPublicMessage(): string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Thrown when the client_id in the document doesn't match the URL.
|
|
78
|
+
*
|
|
79
|
+
* Per CIMD spec, the client_id field in the document MUST exactly match
|
|
80
|
+
* the URL from which the document was fetched.
|
|
81
|
+
*/
|
|
82
|
+
export declare class CimdClientIdMismatchError extends CimdError {
|
|
83
|
+
readonly documentClientId: string;
|
|
84
|
+
constructor(urlClientId: string, documentClientId: string);
|
|
85
|
+
getPublicMessage(): string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Thrown when SSRF protection blocks a client_id URL.
|
|
89
|
+
*
|
|
90
|
+
* Examples:
|
|
91
|
+
* - Private IP address
|
|
92
|
+
* - Blocked domain
|
|
93
|
+
* - Link-local address
|
|
94
|
+
*/
|
|
95
|
+
export declare class CimdSecurityError extends CimdError {
|
|
96
|
+
readonly securityReason: string;
|
|
97
|
+
constructor(clientIdUrl: string, reason: string);
|
|
98
|
+
getPublicMessage(): string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Thrown when the redirect_uri doesn't match any in the CIMD document.
|
|
102
|
+
*/
|
|
103
|
+
export declare class RedirectUriMismatchError extends CimdError {
|
|
104
|
+
readonly requestedRedirectUri: string;
|
|
105
|
+
readonly allowedRedirectUris: string[];
|
|
106
|
+
constructor(clientIdUrl: string, requestedRedirectUri: string, allowedRedirectUris: string[]);
|
|
107
|
+
getPublicMessage(): string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Thrown when the CIMD response exceeds the maximum allowed size.
|
|
111
|
+
*/
|
|
112
|
+
export declare class CimdResponseTooLargeError extends CimdError {
|
|
113
|
+
readonly maxBytes: number;
|
|
114
|
+
readonly actualBytes?: number;
|
|
115
|
+
constructor(clientIdUrl: string, maxBytes: number, actualBytes?: number);
|
|
116
|
+
getPublicMessage(): string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Thrown when CIMD is disabled but a CIMD client_id is used.
|
|
120
|
+
*/
|
|
121
|
+
export declare class CimdDisabledError extends CimdError {
|
|
122
|
+
constructor();
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=cimd.errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.errors.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,8BAAsB,SAAU,SAAQ,KAAK;IAC3C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAanF;;;OAGG;IACH,gBAAgB,IAAI,MAAM;CAG3B;AAED;;;;;;;GAOG;AACH,qBAAa,uBAAwB,SAAQ,SAAS;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKtC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;;;GAOG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC;gBAEnB,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,KAAK,CAAA;KAAE;IAMjG,gBAAgB,IAAI,MAAM;CAMpC;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;gBAExB,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAKxC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACtD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;gBAEtB,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAUhD,gBAAgB,IAAI,MAAM;CAGpC;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;gBAEpB,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKtC,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,SAAS;IACrD,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC;gBAE3B,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE;IAWnF,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAa9D,gBAAgB,IAAI,MAAM;CAGpC;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;;CAI/C"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CIMD Logger Interface
|
|
3
|
+
*
|
|
4
|
+
* A generic logger interface that FrontMcpLogger already satisfies.
|
|
5
|
+
* This allows CIMD to be used independently of the SDK.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Logger interface for CIMD service.
|
|
9
|
+
*
|
|
10
|
+
* This interface is compatible with FrontMcpLogger and most common
|
|
11
|
+
* logging libraries (winston, pino, bunyan, etc.).
|
|
12
|
+
*/
|
|
13
|
+
export interface CimdLogger {
|
|
14
|
+
/**
|
|
15
|
+
* Create a child logger with a prefix.
|
|
16
|
+
*/
|
|
17
|
+
child(prefix: string): CimdLogger;
|
|
18
|
+
/**
|
|
19
|
+
* Log a debug message.
|
|
20
|
+
*/
|
|
21
|
+
debug(message: string, ...args: unknown[]): void;
|
|
22
|
+
/**
|
|
23
|
+
* Log an info message.
|
|
24
|
+
*/
|
|
25
|
+
info(message: string, ...args: unknown[]): void;
|
|
26
|
+
/**
|
|
27
|
+
* Log a warning message.
|
|
28
|
+
*/
|
|
29
|
+
warn(message: string, ...args: unknown[]): void;
|
|
30
|
+
/**
|
|
31
|
+
* Log an error message.
|
|
32
|
+
*/
|
|
33
|
+
error(message: string, ...args: unknown[]): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* No-op logger implementation for when logging is not needed.
|
|
37
|
+
*/
|
|
38
|
+
export declare const noopLogger: CimdLogger;
|
|
39
|
+
//# sourceMappingURL=cimd.logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.logger.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAElC;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAEjD;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAEhD;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAEhD;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAClD;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,UAMxB,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CIMD (Client ID Metadata Documents) Service
|
|
3
|
+
*
|
|
4
|
+
* Core service for resolving and validating OAuth Client ID Metadata Documents.
|
|
5
|
+
*
|
|
6
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00
|
|
7
|
+
*/
|
|
8
|
+
import type { CimdLogger } from './cimd.logger';
|
|
9
|
+
import { type ClientMetadataDocument, type CimdConfigInput, type CimdResolutionResult } from './cimd.types';
|
|
10
|
+
/**
|
|
11
|
+
* CIMD Service for resolving and validating client metadata documents.
|
|
12
|
+
*
|
|
13
|
+
* Following the JwksService pattern for HTTP fetching with caching.
|
|
14
|
+
*/
|
|
15
|
+
export declare class CimdService {
|
|
16
|
+
private readonly config;
|
|
17
|
+
private readonly cacheConfig;
|
|
18
|
+
private readonly securityConfig;
|
|
19
|
+
private readonly networkConfig;
|
|
20
|
+
private readonly cache;
|
|
21
|
+
private readonly logger;
|
|
22
|
+
/**
|
|
23
|
+
* Whether CIMD is enabled.
|
|
24
|
+
*/
|
|
25
|
+
get enabled(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Create a new CIMD service.
|
|
28
|
+
*
|
|
29
|
+
* @param logger - Optional logger. If not provided, logging is disabled.
|
|
30
|
+
* @param config - Optional configuration.
|
|
31
|
+
*/
|
|
32
|
+
constructor(logger?: CimdLogger, config?: Partial<CimdConfigInput>);
|
|
33
|
+
/**
|
|
34
|
+
* Check if a client_id is a CIMD URL.
|
|
35
|
+
*
|
|
36
|
+
* @param clientId - The client_id to check
|
|
37
|
+
* @returns true if this is a CIMD client_id (HTTPS URL with path, or HTTP for localhost when testing)
|
|
38
|
+
*/
|
|
39
|
+
isCimdClientId(clientId: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a client_id to its metadata document.
|
|
42
|
+
*
|
|
43
|
+
* If the client_id is a CIMD URL, this fetches and validates the metadata document.
|
|
44
|
+
* Non-CIMD client IDs return a result with isCimdClient: false.
|
|
45
|
+
*
|
|
46
|
+
* @param clientId - The client_id to resolve
|
|
47
|
+
* @returns Resolution result with metadata if available
|
|
48
|
+
*/
|
|
49
|
+
resolveClientMetadata(clientId: string): Promise<CimdResolutionResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Validate that a redirect_uri is registered for the client.
|
|
52
|
+
*
|
|
53
|
+
* @param redirectUri - The redirect_uri from the authorization request
|
|
54
|
+
* @param metadata - The client's metadata document
|
|
55
|
+
* @throws RedirectUriMismatchError if the redirect_uri is not registered
|
|
56
|
+
*/
|
|
57
|
+
validateRedirectUri(redirectUri: string, metadata: ClientMetadataDocument): void;
|
|
58
|
+
/**
|
|
59
|
+
* Clear the cache for a specific client or all clients.
|
|
60
|
+
*
|
|
61
|
+
* @param clientId - Optional client_id to clear; clears all if not provided
|
|
62
|
+
*/
|
|
63
|
+
clearCache(clientId?: string): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Get cache statistics.
|
|
66
|
+
*/
|
|
67
|
+
getCacheStats(): Promise<{
|
|
68
|
+
size: number;
|
|
69
|
+
}>;
|
|
70
|
+
/**
|
|
71
|
+
* Fetch a metadata document from a CIMD URL.
|
|
72
|
+
*
|
|
73
|
+
* Following the JwksService.fetchJson() pattern.
|
|
74
|
+
*/
|
|
75
|
+
private fetchMetadataDocument;
|
|
76
|
+
/**
|
|
77
|
+
* Read response body with size limit.
|
|
78
|
+
*/
|
|
79
|
+
private readResponseWithLimit;
|
|
80
|
+
/**
|
|
81
|
+
* Validate a fetched document against the schema.
|
|
82
|
+
*
|
|
83
|
+
* @param clientId - The URL from which the document was fetched
|
|
84
|
+
* @param document - The document to validate
|
|
85
|
+
*/
|
|
86
|
+
private validateDocument;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=cimd.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.service.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAUhD,OAAO,EAML,KAAK,sBAAsB,EAE3B,KAAK,eAAe,EACpB,KAAK,oBAAoB,EAI1B,MAAM,cAAc,CAAC;AAGtB;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IAEpC;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;IA4BlE;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;;;;OAQG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAoD5E;;;;;;OAMG;IACH,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAUhF;;;;OAIG;IACG,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlD;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAMhD;;;;OAIG;YACW,qBAAqB;IAmJnC;;OAEG;YACW,qBAAqB;IAyCnC;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;CAiBzB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CIMD (Client ID Metadata Documents) Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Implements OAuth Client ID Metadata Documents per
|
|
5
|
+
* draft-ietf-oauth-client-id-metadata-document-00
|
|
6
|
+
*
|
|
7
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
/**
|
|
11
|
+
* OAuth Client Metadata Document schema.
|
|
12
|
+
*
|
|
13
|
+
* When a client_id is an HTTPS URL, this document is fetched from that URL
|
|
14
|
+
* and contains the client's OAuth metadata.
|
|
15
|
+
*
|
|
16
|
+
* Per RFC 7591 (OAuth 2.0 Dynamic Client Registration) and CIMD spec.
|
|
17
|
+
*/
|
|
18
|
+
export declare const clientMetadataDocumentSchema: z.ZodObject<{
|
|
19
|
+
client_id: z.ZodString;
|
|
20
|
+
client_name: z.ZodString;
|
|
21
|
+
redirect_uris: z.ZodArray<z.ZodString>;
|
|
22
|
+
token_endpoint_auth_method: z.ZodDefault<z.ZodEnum<{
|
|
23
|
+
none: "none";
|
|
24
|
+
client_secret_basic: "client_secret_basic";
|
|
25
|
+
client_secret_post: "client_secret_post";
|
|
26
|
+
private_key_jwt: "private_key_jwt";
|
|
27
|
+
}>>;
|
|
28
|
+
grant_types: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
29
|
+
response_types: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
30
|
+
client_uri: z.ZodOptional<z.ZodString>;
|
|
31
|
+
logo_uri: z.ZodOptional<z.ZodString>;
|
|
32
|
+
jwks_uri: z.ZodOptional<z.ZodString>;
|
|
33
|
+
jwks: z.ZodOptional<z.ZodObject<{
|
|
34
|
+
keys: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
35
|
+
}, z.core.$strip>>;
|
|
36
|
+
tos_uri: z.ZodOptional<z.ZodString>;
|
|
37
|
+
policy_uri: z.ZodOptional<z.ZodString>;
|
|
38
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
39
|
+
contacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
40
|
+
software_statement: z.ZodOptional<z.ZodString>;
|
|
41
|
+
software_id: z.ZodOptional<z.ZodString>;
|
|
42
|
+
software_version: z.ZodOptional<z.ZodString>;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
export type ClientMetadataDocument = z.infer<typeof clientMetadataDocumentSchema>;
|
|
45
|
+
export type ClientMetadataDocumentInput = z.input<typeof clientMetadataDocumentSchema>;
|
|
46
|
+
/**
|
|
47
|
+
* Redis configuration for CIMD cache.
|
|
48
|
+
*/
|
|
49
|
+
export declare const cimdRedisCacheConfigSchema: z.ZodObject<{
|
|
50
|
+
url: z.ZodOptional<z.ZodString>;
|
|
51
|
+
host: z.ZodOptional<z.ZodString>;
|
|
52
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
53
|
+
password: z.ZodOptional<z.ZodString>;
|
|
54
|
+
db: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
tls: z.ZodOptional<z.ZodBoolean>;
|
|
56
|
+
keyPrefix: z.ZodDefault<z.ZodString>;
|
|
57
|
+
}, z.core.$strip>;
|
|
58
|
+
export type CimdRedisCacheConfig = z.infer<typeof cimdRedisCacheConfigSchema>;
|
|
59
|
+
/**
|
|
60
|
+
* Cache configuration for CIMD service.
|
|
61
|
+
*/
|
|
62
|
+
export declare const cimdCacheConfigSchema: z.ZodObject<{
|
|
63
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
64
|
+
memory: "memory";
|
|
65
|
+
redis: "redis";
|
|
66
|
+
}>>;
|
|
67
|
+
defaultTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
68
|
+
maxTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
69
|
+
minTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
70
|
+
redis: z.ZodOptional<z.ZodObject<{
|
|
71
|
+
url: z.ZodOptional<z.ZodString>;
|
|
72
|
+
host: z.ZodOptional<z.ZodString>;
|
|
73
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
74
|
+
password: z.ZodOptional<z.ZodString>;
|
|
75
|
+
db: z.ZodOptional<z.ZodNumber>;
|
|
76
|
+
tls: z.ZodOptional<z.ZodBoolean>;
|
|
77
|
+
keyPrefix: z.ZodDefault<z.ZodString>;
|
|
78
|
+
}, z.core.$strip>>;
|
|
79
|
+
}, z.core.$strip>;
|
|
80
|
+
export type CimdCacheConfig = z.infer<typeof cimdCacheConfigSchema>;
|
|
81
|
+
/**
|
|
82
|
+
* Security configuration for CIMD service.
|
|
83
|
+
*/
|
|
84
|
+
export declare const cimdSecurityConfigSchema: z.ZodObject<{
|
|
85
|
+
blockPrivateIPs: z.ZodDefault<z.ZodBoolean>;
|
|
86
|
+
allowedDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
87
|
+
blockedDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
88
|
+
warnOnLocalhostRedirects: z.ZodDefault<z.ZodBoolean>;
|
|
89
|
+
allowInsecureForTesting: z.ZodDefault<z.ZodBoolean>;
|
|
90
|
+
}, z.core.$strip>;
|
|
91
|
+
export type CimdSecurityConfig = z.infer<typeof cimdSecurityConfigSchema>;
|
|
92
|
+
/**
|
|
93
|
+
* Network configuration for CIMD service.
|
|
94
|
+
*/
|
|
95
|
+
export declare const cimdNetworkConfigSchema: z.ZodObject<{
|
|
96
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
97
|
+
maxResponseSizeBytes: z.ZodDefault<z.ZodNumber>;
|
|
98
|
+
redirectPolicy: z.ZodDefault<z.ZodEnum<{
|
|
99
|
+
deny: "deny";
|
|
100
|
+
"same-origin": "same-origin";
|
|
101
|
+
allow: "allow";
|
|
102
|
+
}>>;
|
|
103
|
+
maxRedirects: z.ZodDefault<z.ZodNumber>;
|
|
104
|
+
}, z.core.$strip>;
|
|
105
|
+
export type CimdNetworkConfig = z.infer<typeof cimdNetworkConfigSchema>;
|
|
106
|
+
/**
|
|
107
|
+
* Full CIMD service configuration schema.
|
|
108
|
+
*/
|
|
109
|
+
export declare const cimdConfigSchema: z.ZodObject<{
|
|
110
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
111
|
+
cache: z.ZodOptional<z.ZodObject<{
|
|
112
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
113
|
+
memory: "memory";
|
|
114
|
+
redis: "redis";
|
|
115
|
+
}>>;
|
|
116
|
+
defaultTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
117
|
+
maxTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
118
|
+
minTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
119
|
+
redis: z.ZodOptional<z.ZodObject<{
|
|
120
|
+
url: z.ZodOptional<z.ZodString>;
|
|
121
|
+
host: z.ZodOptional<z.ZodString>;
|
|
122
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
123
|
+
password: z.ZodOptional<z.ZodString>;
|
|
124
|
+
db: z.ZodOptional<z.ZodNumber>;
|
|
125
|
+
tls: z.ZodOptional<z.ZodBoolean>;
|
|
126
|
+
keyPrefix: z.ZodDefault<z.ZodString>;
|
|
127
|
+
}, z.core.$strip>>;
|
|
128
|
+
}, z.core.$strip>>;
|
|
129
|
+
security: z.ZodOptional<z.ZodObject<{
|
|
130
|
+
blockPrivateIPs: z.ZodDefault<z.ZodBoolean>;
|
|
131
|
+
allowedDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
132
|
+
blockedDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
133
|
+
warnOnLocalhostRedirects: z.ZodDefault<z.ZodBoolean>;
|
|
134
|
+
allowInsecureForTesting: z.ZodDefault<z.ZodBoolean>;
|
|
135
|
+
}, z.core.$strip>>;
|
|
136
|
+
network: z.ZodOptional<z.ZodObject<{
|
|
137
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
138
|
+
maxResponseSizeBytes: z.ZodDefault<z.ZodNumber>;
|
|
139
|
+
redirectPolicy: z.ZodDefault<z.ZodEnum<{
|
|
140
|
+
deny: "deny";
|
|
141
|
+
"same-origin": "same-origin";
|
|
142
|
+
allow: "allow";
|
|
143
|
+
}>>;
|
|
144
|
+
maxRedirects: z.ZodDefault<z.ZodNumber>;
|
|
145
|
+
}, z.core.$strip>>;
|
|
146
|
+
}, z.core.$strip>;
|
|
147
|
+
export type CimdConfig = z.infer<typeof cimdConfigSchema>;
|
|
148
|
+
export type CimdConfigInput = z.input<typeof cimdConfigSchema>;
|
|
149
|
+
/**
|
|
150
|
+
* Result of resolving a client_id to its metadata document.
|
|
151
|
+
*/
|
|
152
|
+
export interface CimdResolutionResult {
|
|
153
|
+
/**
|
|
154
|
+
* Whether this client_id is a CIMD URL.
|
|
155
|
+
*/
|
|
156
|
+
isCimdClient: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* The resolved metadata document (if isCimdClient is true).
|
|
159
|
+
*/
|
|
160
|
+
metadata?: ClientMetadataDocument;
|
|
161
|
+
/**
|
|
162
|
+
* Whether the result came from cache.
|
|
163
|
+
*/
|
|
164
|
+
fromCache: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* When the cached entry expires (if cached).
|
|
167
|
+
*/
|
|
168
|
+
expiresAt?: number;
|
|
169
|
+
/**
|
|
170
|
+
* HTTP ETag for conditional requests.
|
|
171
|
+
*/
|
|
172
|
+
etag?: string;
|
|
173
|
+
/**
|
|
174
|
+
* HTTP Last-Modified header value.
|
|
175
|
+
*/
|
|
176
|
+
lastModified?: string;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=cimd.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.types.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;iBAiGvC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAMvF;;GAEG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;iBAwCrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;iBA+BhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;;;iBAoCnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;iBA2BlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAM/D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,YAAY,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CimdSecurityConfig } from './cimd.types';
|
|
2
|
+
/**
|
|
3
|
+
* Check if a client_id is a CIMD URL (HTTPS URL with path component).
|
|
4
|
+
*
|
|
5
|
+
* Per CIMD spec, a CIMD client_id is an HTTPS URL that:
|
|
6
|
+
* - Uses the https:// scheme
|
|
7
|
+
* - Has a path component (not just the root)
|
|
8
|
+
*
|
|
9
|
+
* @param clientId - The client_id to check
|
|
10
|
+
* @param allowInsecure - Allow HTTP for localhost (testing only)
|
|
11
|
+
* @returns true if this is a CIMD client_id
|
|
12
|
+
*/
|
|
13
|
+
export declare function isCimdClientId(clientId: string, allowInsecure?: boolean): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Validate a client_id URL for CIMD usage.
|
|
16
|
+
*
|
|
17
|
+
* @param clientId - The client_id to validate
|
|
18
|
+
* @param securityConfig - Optional security configuration
|
|
19
|
+
* @returns The parsed URL object
|
|
20
|
+
* @throws InvalidClientIdUrlError if the URL is invalid
|
|
21
|
+
* @throws CimdSecurityError if the URL violates security policy
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateClientIdUrl(clientId: string, securityConfig?: Partial<CimdSecurityConfig>): URL;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a hostname should be blocked for SSRF protection.
|
|
26
|
+
*/
|
|
27
|
+
interface SsrfCheckResult {
|
|
28
|
+
allowed: boolean;
|
|
29
|
+
reason: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Perform SSRF protection checks on a hostname.
|
|
33
|
+
*
|
|
34
|
+
* @param hostname - The hostname to check
|
|
35
|
+
* @returns Result indicating if the hostname is allowed
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkSsrfProtection(hostname: string): SsrfCheckResult;
|
|
38
|
+
/**
|
|
39
|
+
* Check if any redirect URIs in the list are localhost-only.
|
|
40
|
+
*
|
|
41
|
+
* This is a warning indicator for development clients that might
|
|
42
|
+
* have accidentally been submitted for production use.
|
|
43
|
+
*
|
|
44
|
+
* @param redirectUris - Array of redirect URIs to check
|
|
45
|
+
* @returns true if all URIs are localhost
|
|
46
|
+
*/
|
|
47
|
+
export declare function hasOnlyLocalhostRedirectUris(redirectUris: string[]): boolean;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=cimd.validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.validator.d.ts","sourceRoot":"","sources":["../../src/cimd/cimd.validator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,UAAQ,GAAG,OAAO,CA4B/E;AAUD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,GAAG,CA6DvG;AAED;;GAEG;AACH,UAAU,eAAe;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAqBrE;AAqKD;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAgB5E"}
|
package/cimd/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CIMD (Client ID Metadata Documents) Module
|
|
3
|
+
*
|
|
4
|
+
* OAuth Client ID Metadata Documents support per
|
|
5
|
+
* draft-ietf-oauth-client-id-metadata-document-00
|
|
6
|
+
*
|
|
7
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00
|
|
8
|
+
*/
|
|
9
|
+
export { type CimdLogger, noopLogger } from './cimd.logger';
|
|
10
|
+
export { clientMetadataDocumentSchema, cimdCacheConfigSchema, cimdRedisCacheConfigSchema, cimdSecurityConfigSchema, cimdNetworkConfigSchema, cimdConfigSchema, type ClientMetadataDocument, type ClientMetadataDocumentInput, type CimdCacheConfig, type CimdRedisCacheConfig, type CimdSecurityConfig, type CimdNetworkConfig, type CimdConfig, type CimdConfigInput, type CimdResolutionResult, } from './cimd.types';
|
|
11
|
+
export { CimdError, InvalidClientIdUrlError, CimdFetchError, CimdValidationError, CimdClientIdMismatchError, CimdSecurityError, RedirectUriMismatchError, CimdResponseTooLargeError, CimdDisabledError, } from './cimd.errors';
|
|
12
|
+
export { isCimdClientId, validateClientIdUrl, checkSsrfProtection, hasOnlyLocalhostRedirectUris, } from './cimd.validator';
|
|
13
|
+
export { InMemoryCimdCache, CimdCache, // Backwards compatibility alias
|
|
14
|
+
createCimdCache, extractCacheHeaders, parseCacheHeaders, type CimdCacheBackend, type CimdCacheEntry, type CimdCacheTtlConfig, type CacheableHeaders, } from './cimd.cache';
|
|
15
|
+
export { RedisCimdCache } from './cimd-redis.cache';
|
|
16
|
+
export { CimdService } from './cimd.service';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cimd/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAK5D,OAAO,EAEL,4BAA4B,EAC5B,qBAAqB,EACrB,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,EACvB,gBAAgB,EAEhB,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC;AAKtB,OAAO,EACL,SAAS,EACT,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAKvB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EAEL,iBAAiB,EACjB,SAAS,EAAE,gCAAgC;AAE3C,eAAe,EAEf,mBAAmB,EACnB,iBAAiB,EAEjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKpD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|