@sonatel-os/openapi-runtime 0.1.1 → 0.2.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.
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Authentication provider interface.
3
+ * @typedef {Object} AuthProvider
4
+ * @property {(entry: object, schemeName: string, scopes: string[], req: object) => Promise<void>} apply
5
+ */
6
+ /**
7
+ * API Key provider options.
8
+ * @typedef {Object} ApiKeyOptions
9
+ * @property {'header' | 'query'} in - Where to place the API key
10
+ * @property {string} name - Header name or query param name
11
+ * @property {string | (() => string) | (() => Promise<string>)} getValue - Key value or getter
12
+ */
13
+ /**
14
+ * Bearer token provider options.
15
+ * @typedef {Object} BearerOptions
16
+ * @property {string} [token] - Static token value
17
+ * @property {(() => string) | (() => Promise<string>)} [getToken] - Dynamic token getter
18
+ */
19
+ /**
20
+ * Basic auth provider options.
21
+ * @typedef {Object} BasicOptions
22
+ * @property {string} [username] - Static username
23
+ * @property {string} [password] - Static password
24
+ * @property {(() => { username: string, password: string }) | (() => Promise<{ username: string, password: string }>)} [getCredentials] - Dynamic credentials getter
25
+ */
26
+ /**
27
+ * OAuth2 Client Credentials provider options.
28
+ * @typedef {Object} ClientCredentialsOptions
29
+ * @property {string} tokenUrl - Token endpoint URL (must be HTTPS in production)
30
+ * @property {string} clientId - OAuth client ID
31
+ * @property {string} clientSecret - OAuth client secret
32
+ * @property {string} [scope] - OAuth scopes (space-separated)
33
+ * @property {string} [audience] - Token audience
34
+ * @property {Record<string, string>} [extraParams] - Additional token request params
35
+ * @property {boolean} [allowInsecure=false] - Allow HTTP for testing (DANGEROUS)
36
+ */
37
+ /**
38
+ * Creates an API Key authentication provider.
39
+ *
40
+ * @param {ApiKeyOptions} opts - Provider options
41
+ * @returns {AuthProvider} Configured provider
42
+ * @throws {ConfigurationError} If required options are missing
43
+ *
44
+ * @example
45
+ * apiKey({ in: 'header', name: 'X-API-Key', getValue: () => process.env.API_KEY })
46
+ */
47
+ export function apiKey({ in: placement, name, getValue }: ApiKeyOptions): AuthProvider;
48
+ /**
49
+ * Creates a Basic authentication provider.
50
+ *
51
+ * @param {BasicOptions} opts - Provider options
52
+ * @returns {AuthProvider} Configured provider
53
+ *
54
+ * @example
55
+ * basic({ username: 'user', password: 'pass' })
56
+ * basic({ getCredentials: async () => fetchCredentials() })
57
+ */
58
+ export function basic({ username, password, getCredentials }: BasicOptions): AuthProvider;
59
+ /**
60
+ * Creates a Bearer token authentication provider.
61
+ *
62
+ * @param {BearerOptions} opts - Provider options
63
+ * @returns {AuthProvider} Configured provider
64
+ *
65
+ * @example
66
+ * bearer({ token: 'static-token' })
67
+ * bearer({ getToken: async () => await refreshToken() })
68
+ */
69
+ export function bearer({ token, getToken }: BearerOptions): AuthProvider;
70
+ /**
71
+ * Creates an OAuth2 Client Credentials provider with token caching.
72
+ *
73
+ * @param {ClientCredentialsOptions} opts - Provider options
74
+ * @returns {AuthProvider} Configured provider
75
+ * @throws {ConfigurationError} If required options are missing
76
+ * @throws {SecurityError} If tokenUrl is not HTTPS (unless allowInsecure=true)
77
+ *
78
+ * @example
79
+ * clientCredentials({
80
+ * tokenUrl: 'https://auth.example.com/oauth/token',
81
+ * clientId: 'my-app',
82
+ * clientSecret: 'secret',
83
+ * scope: 'read write'
84
+ * })
85
+ */
86
+ export function clientCredentials({ tokenUrl, clientId, clientSecret, scope, audience, extraParams, allowInsecure }: ClientCredentialsOptions): AuthProvider;
87
+ /**
88
+ * Authentication provider interface.
89
+ */
90
+ export type AuthProvider = {
91
+ apply: (entry: object, schemeName: string, scopes: string[], req: object) => Promise<void>;
92
+ };
93
+ /**
94
+ * API Key provider options.
95
+ */
96
+ export type ApiKeyOptions = {
97
+ /**
98
+ * - Where to place the API key
99
+ */
100
+ in: "header" | "query";
101
+ /**
102
+ * - Header name or query param name
103
+ */
104
+ name: string;
105
+ /**
106
+ * - Key value or getter
107
+ */
108
+ getValue: string | (() => string) | (() => Promise<string>);
109
+ };
110
+ /**
111
+ * Bearer token provider options.
112
+ */
113
+ export type BearerOptions = {
114
+ /**
115
+ * - Static token value
116
+ */
117
+ token?: string | undefined;
118
+ /**
119
+ * - Dynamic token getter
120
+ */
121
+ getToken?: (() => string) | (() => Promise<string>) | undefined;
122
+ };
123
+ /**
124
+ * Basic auth provider options.
125
+ */
126
+ export type BasicOptions = {
127
+ /**
128
+ * - Static username
129
+ */
130
+ username?: string | undefined;
131
+ /**
132
+ * - Static password
133
+ */
134
+ password?: string | undefined;
135
+ /**
136
+ * - Dynamic credentials getter
137
+ */
138
+ getCredentials?: (() => {
139
+ username: string;
140
+ password: string;
141
+ }) | (() => Promise<{
142
+ username: string;
143
+ password: string;
144
+ }>) | undefined;
145
+ };
146
+ /**
147
+ * OAuth2 Client Credentials provider options.
148
+ */
149
+ export type ClientCredentialsOptions = {
150
+ /**
151
+ * - Token endpoint URL (must be HTTPS in production)
152
+ */
153
+ tokenUrl: string;
154
+ /**
155
+ * - OAuth client ID
156
+ */
157
+ clientId: string;
158
+ /**
159
+ * - OAuth client secret
160
+ */
161
+ clientSecret: string;
162
+ /**
163
+ * - OAuth scopes (space-separated)
164
+ */
165
+ scope?: string | undefined;
166
+ /**
167
+ * - Token audience
168
+ */
169
+ audience?: string | undefined;
170
+ /**
171
+ * - Additional token request params
172
+ */
173
+ extraParams?: Record<string, string> | undefined;
174
+ /**
175
+ * - Allow HTTP for testing (DANGEROUS)
176
+ */
177
+ allowInsecure?: boolean | undefined;
178
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @module constants
3
+ * @description Centralized constants - no magic values in codebase.
4
+ */
5
+ /** @type {readonly string[]} Valid HTTP methods for OpenAPI operations */
6
+ export const HTTP_METHODS: readonly string[];
7
+ /** @type {string} Default content type for API requests/responses */
8
+ export const DEFAULT_CONTENT_TYPE: string;
9
+ /** @type {string} Form URL encoded content type for OAuth */
10
+ export const FORM_URLENCODED_CONTENT_TYPE: string;
11
+ /** @type {number} Default OAuth token expiry in seconds */
12
+ export const DEFAULT_TOKEN_EXPIRY_SECONDS: number;
13
+ /** @type {number} Buffer before token expiry to trigger refresh (seconds) */
14
+ export const TOKEN_REFRESH_BUFFER_SECONDS: number;
15
+ /** @type {number} Minimum buffer to prevent negative expiry calculations */
16
+ export const MIN_TOKEN_BUFFER_SECONDS: number;
17
+ /** @type {string} Default auth configuration filename */
18
+ export const AUTH_CONFIG_FILENAME: string;
19
+ /** @type {string} Default directory for OpenAPI spec files */
20
+ export const SPECS_DIRECTORY: string;
21
+ /** @type {string} Environment variable prefix for auto-detection */
22
+ export const ENV_PREFIX: string;
23
+ /** @type {RegExp} Pattern for valid spec names (alphanumeric, dash, underscore) */
24
+ export const VALID_SPEC_NAME_PATTERN: RegExp;
25
+ /** @type {RegExp} Pattern for valid operation IDs */
26
+ export const VALID_OPERATION_ID_PATTERN: RegExp;
27
+ /** @type {string} Required protocol for OAuth token endpoints */
28
+ export const SECURE_PROTOCOL: string;
29
+ /** @type {boolean} Whether to enforce HTTPS for OAuth (disable only for testing) */
30
+ export const ENFORCE_HTTPS: boolean;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Options for building a client.
3
+ * @typedef {Object} BuildClientOptions
4
+ * @property {string} name - Name to register the spec under
5
+ * @property {string | object} spec - URL to spec or spec object
6
+ */
7
+ /**
8
+ * Options for executing an API request.
9
+ * @typedef {Object} ExecuteOptions
10
+ * @property {string} operationId - Operation to execute
11
+ * @property {Record<string, any>} [parameters] - Path/query parameters
12
+ * @property {any} [requestBody] - Request body
13
+ * @property {Record<string, string>} [headers] - Additional headers
14
+ * @property {Record<string, string>} [qs] - Additional query string params
15
+ */
16
+ /**
17
+ * Builds a Swagger client for an OpenAPI spec.
18
+ * Resolves $refs, validates the spec, and indexes operations.
19
+ *
20
+ * @param {BuildClientOptions} opts - Client options
21
+ * @returns {Promise<import('./registry.js').SpecEntry>} Fully initialized spec entry
22
+ *
23
+ * @example
24
+ * const entry = await buildClient({
25
+ * name: 'products',
26
+ * spec: './specs/products.openapi.json'
27
+ * })
28
+ */
29
+ export function buildClient({ name, spec }: BuildClientOptions): Promise<import("./registry.js").SpecEntry>;
30
+ /**
31
+ * Executes an API request by operationId.
32
+ * Applies authentication, interceptors, and merges headers.
33
+ *
34
+ * @param {import('./registry.js').SpecEntry} entry - Spec entry
35
+ * @param {ExecuteOptions} req - Request options
36
+ * @returns {Promise<object>} API response
37
+ * @throws {Error} If request fails and no error interceptor handles it
38
+ *
39
+ * @example
40
+ * const response = await executeByOperationId(entry, {
41
+ * operationId: 'getProducts',
42
+ * parameters: { category: 'electronics' }
43
+ * })
44
+ */
45
+ export function executeByOperationId(entry: import("./registry.js").SpecEntry, req: ExecuteOptions): Promise<object>;
46
+ /**
47
+ * Options for building a client.
48
+ */
49
+ export type BuildClientOptions = {
50
+ /**
51
+ * - Name to register the spec under
52
+ */
53
+ name: string;
54
+ /**
55
+ * - URL to spec or spec object
56
+ */
57
+ spec: string | object;
58
+ };
59
+ /**
60
+ * Options for executing an API request.
61
+ */
62
+ export type ExecuteOptions = {
63
+ /**
64
+ * - Operation to execute
65
+ */
66
+ operationId: string;
67
+ /**
68
+ * - Path/query parameters
69
+ */
70
+ parameters?: Record<string, any> | undefined;
71
+ /**
72
+ * - Request body
73
+ */
74
+ requestBody?: any;
75
+ /**
76
+ * - Additional headers
77
+ */
78
+ headers?: Record<string, string> | undefined;
79
+ /**
80
+ * - Additional query string params
81
+ */
82
+ qs?: Record<string, string> | undefined;
83
+ };
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Returns the global spec registry singleton.
3
+ *
4
+ * @returns {Registry} The global registry instance
5
+ *
6
+ * @example
7
+ * const registry = getRegistry()
8
+ * registry.set('users', entry)
9
+ */
10
+ export function getRegistry(): Registry;
11
+ /**
12
+ * Retrieves a spec entry from the registry, throwing if not found.
13
+ *
14
+ * @param {string} name - Registered spec name
15
+ * @returns {SpecEntry} The spec entry
16
+ * @throws {SpecNotFoundError} If spec is not registered
17
+ *
18
+ * @example
19
+ * const entry = ensureEntry('products')
20
+ */
21
+ export function ensureEntry(name: string): SpecEntry;
22
+ /**
23
+ * Clears all entries from the registry.
24
+ * Useful for testing or cleanup.
25
+ *
26
+ * @example
27
+ * clearRegistry()
28
+ */
29
+ export function clearRegistry(): void;
30
+ /**
31
+ * Indexes all operations in a spec by operationId.
32
+ * Creates both a Map for fast lookup and an array for iteration.
33
+ *
34
+ * @param {object} spec - Resolved OpenAPI specification
35
+ * @returns {{ opsById: Map<string, object>, operations: OperationInfo[] }}
36
+ *
37
+ * @example
38
+ * const { opsById, operations } = indexOperations(spec)
39
+ * const getUsers = opsById.get('getUsers')
40
+ */
41
+ export function indexOperations(spec: object): {
42
+ opsById: Map<string, object>;
43
+ operations: OperationInfo[];
44
+ };
45
+ /**
46
+ * Information about a single API operation.
47
+ */
48
+ export type OperationInfo = {
49
+ /**
50
+ * - Unique operation identifier
51
+ */
52
+ operationId: string;
53
+ /**
54
+ * - HTTP method (get, post, etc.)
55
+ */
56
+ method: string;
57
+ /**
58
+ * - URL path template
59
+ */
60
+ path: string;
61
+ /**
62
+ * - Short operation description
63
+ */
64
+ summary?: string | undefined;
65
+ /**
66
+ * - Grouping tags
67
+ */
68
+ tags?: string[] | undefined;
69
+ };
70
+ /**
71
+ * A registered spec entry in the registry.
72
+ */
73
+ export type SpecEntry = {
74
+ /**
75
+ * - Registered name for this spec
76
+ */
77
+ name: string;
78
+ /**
79
+ * - Swagger client instance
80
+ */
81
+ client: any;
82
+ /**
83
+ * - Resolved OpenAPI specification
84
+ */
85
+ spec: object;
86
+ /**
87
+ * - Operations indexed by operationId
88
+ */
89
+ opsById: Map<string, object>;
90
+ /**
91
+ * - List of all operations
92
+ */
93
+ operations: OperationInfo[];
94
+ /**
95
+ * - Default request options
96
+ */
97
+ defaults: {
98
+ headers?: Record<string, string>;
99
+ };
100
+ /**
101
+ * - Lifecycle hooks
102
+ */
103
+ interceptors: {
104
+ request?: Function;
105
+ response?: Function;
106
+ error?: Function;
107
+ };
108
+ };
109
+ /**
110
+ * Global registry with attached globals.
111
+ */
112
+ export type Registry = Map<string, SpecEntry> & {
113
+ __globals: {
114
+ headers: Record<string, string>;
115
+ };
116
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @module errors
3
+ * @description Custom error types for precise error handling and debugging.
4
+ */
5
+ /**
6
+ * Base error class for all OpenAPI Runtime errors.
7
+ * @extends Error
8
+ */
9
+ export class OpenAPIRuntimeError extends Error {
10
+ /**
11
+ * @param {string} message - Error message
12
+ * @param {string} code - Error code for programmatic handling
13
+ * @param {object} [details] - Additional error context
14
+ */
15
+ constructor(message: string, code: string, details?: object);
16
+ code: string;
17
+ details: object;
18
+ /**
19
+ * Convert error to JSON for logging/serialization.
20
+ * @returns {{ name: string, code: string, message: string, details: object }}
21
+ */
22
+ toJSON(): {
23
+ name: string;
24
+ code: string;
25
+ message: string;
26
+ details: object;
27
+ };
28
+ }
29
+ /**
30
+ * Thrown when a requested spec is not found in the registry.
31
+ * @extends OpenAPIRuntimeError
32
+ */
33
+ export class SpecNotFoundError extends OpenAPIRuntimeError {
34
+ /**
35
+ * @param {string} specName - Name of the spec that wasn't found
36
+ */
37
+ constructor(specName: string);
38
+ }
39
+ /**
40
+ * Thrown when a requested operation is not found in a spec.
41
+ * @extends OpenAPIRuntimeError
42
+ */
43
+ export class OperationNotFoundError extends OpenAPIRuntimeError {
44
+ /**
45
+ * @param {string} operationId - The operation ID that wasn't found
46
+ * @param {string} [specName] - Optional spec name for context
47
+ */
48
+ constructor(operationId: string, specName?: string);
49
+ }
50
+ /**
51
+ * Thrown when authentication fails or is misconfigured.
52
+ * @extends OpenAPIRuntimeError
53
+ */
54
+ export class AuthenticationError extends OpenAPIRuntimeError {
55
+ /**
56
+ * @param {string} message - Error description
57
+ * @param {Array<{ scheme: string, error: string }>} [failures] - List of auth failures
58
+ */
59
+ constructor(message: string, failures?: Array<{
60
+ scheme: string;
61
+ error: string;
62
+ }>);
63
+ failures: {
64
+ scheme: string;
65
+ error: string;
66
+ }[];
67
+ }
68
+ /**
69
+ * Thrown when security requirements are violated.
70
+ * @extends OpenAPIRuntimeError
71
+ */
72
+ export class SecurityError extends OpenAPIRuntimeError {
73
+ /**
74
+ * @param {string} message - Security violation description
75
+ * @param {string} [violation] - Type of security violation
76
+ */
77
+ constructor(message: string, violation?: string);
78
+ }
79
+ /**
80
+ * Thrown when validation fails (request or response).
81
+ * @extends OpenAPIRuntimeError
82
+ */
83
+ export class ValidationError extends OpenAPIRuntimeError {
84
+ /**
85
+ * @param {string} message - Validation error description
86
+ * @param {Array<object>} errors - AJV validation errors
87
+ * @param {'request' | 'response'} type - Whether request or response failed
88
+ */
89
+ constructor(message: string, errors?: Array<object>, type?: "request" | "response");
90
+ errors: object[];
91
+ validationType: "request" | "response";
92
+ }
93
+ /**
94
+ * Thrown when spec loading or parsing fails.
95
+ * @extends OpenAPIRuntimeError
96
+ */
97
+ export class SpecLoadError extends OpenAPIRuntimeError {
98
+ /**
99
+ * @param {string} specName - Name/path of the spec that failed to load
100
+ * @param {string} reason - Why loading failed
101
+ */
102
+ constructor(specName: string, reason: string);
103
+ }
104
+ /**
105
+ * Thrown when configuration is invalid.
106
+ * @extends OpenAPIRuntimeError
107
+ */
108
+ export class ConfigurationError extends OpenAPIRuntimeError {
109
+ /**
110
+ * @param {string} message - What's wrong with the configuration
111
+ * @param {string} [configKey] - Which config key is problematic
112
+ */
113
+ constructor(message: string, configKey?: string);
114
+ }
115
+ /**
116
+ * Thrown when input parameters are invalid.
117
+ * @extends OpenAPIRuntimeError
118
+ */
119
+ export class InvalidInputError extends OpenAPIRuntimeError {
120
+ /**
121
+ * @param {string} message - What input is invalid
122
+ * @param {string} [field] - Which field is invalid
123
+ * @param {*} [value] - The invalid value (sanitized)
124
+ */
125
+ constructor(message: string, field?: string, value?: any);
126
+ }