@scalar/mock-server 0.9.8 → 0.9.10
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/CHANGELOG.md +17 -0
- package/dist/create-mock-server.js +79 -68
- package/dist/index.js +1 -5
- package/dist/libs/store.js +66 -65
- package/dist/routes/mock-any-response.js +70 -63
- package/dist/routes/mock-handler-response.js +140 -100
- package/dist/routes/respond-with-authorize-page.js +89 -80
- package/dist/routes/respond-with-openapi-document.js +32 -35
- package/dist/routes/respond-with-token.js +40 -45
- package/dist/types.js +2 -5
- package/dist/utils/build-handler-context.js +83 -70
- package/dist/utils/build-seed-context.js +59 -52
- package/dist/utils/create-openapi-definition.js +7 -10
- package/dist/utils/execute-handler.js +16 -18
- package/dist/utils/execute-seed.js +22 -21
- package/dist/utils/find-preferred-response-key.js +9 -9
- package/dist/utils/get-open-auth-token-urls.js +45 -35
- package/dist/utils/get-operation.js +12 -12
- package/dist/utils/handle-authentication.js +106 -101
- package/dist/utils/hono-route-from-path.js +6 -6
- package/dist/utils/is-authentication-required.js +17 -15
- package/dist/utils/log-authentication-instructions.js +105 -110
- package/dist/utils/process-openapi-document.js +71 -58
- package/dist/utils/set-up-authentication-routes.js +80 -77
- package/dist/utils/store-wrapper.js +40 -39
- package/package.json +10 -14
- package/dist/create-mock-server.js.map +0 -7
- package/dist/index.js.map +0 -7
- package/dist/libs/store.js.map +0 -7
- package/dist/routes/mock-any-response.js.map +0 -7
- package/dist/routes/mock-handler-response.js.map +0 -7
- package/dist/routes/respond-with-authorize-page.js.map +0 -7
- package/dist/routes/respond-with-openapi-document.js.map +0 -7
- package/dist/routes/respond-with-token.js.map +0 -7
- package/dist/types.js.map +0 -7
- package/dist/utils/build-handler-context.js.map +0 -7
- package/dist/utils/build-seed-context.js.map +0 -7
- package/dist/utils/create-openapi-definition.js.map +0 -7
- package/dist/utils/execute-handler.js.map +0 -7
- package/dist/utils/execute-seed.js.map +0 -7
- package/dist/utils/find-preferred-response-key.js.map +0 -7
- package/dist/utils/get-open-auth-token-urls.js.map +0 -7
- package/dist/utils/get-operation.js.map +0 -7
- package/dist/utils/handle-authentication.js.map +0 -7
- package/dist/utils/hono-route-from-path.js.map +0 -7
- package/dist/utils/is-authentication-required.js.map +0 -7
- package/dist/utils/log-authentication-instructions.js.map +0 -7
- package/dist/utils/process-openapi-document.js.map +0 -7
- package/dist/utils/set-up-authentication-routes.js.map +0 -7
- package/dist/utils/store-wrapper.js.map +0 -7
|
@@ -1,110 +1,150 @@
|
|
|
1
|
-
import { getExampleFromSchema } from
|
|
2
|
-
import { accepts } from
|
|
3
|
-
import { buildHandlerContext } from
|
|
4
|
-
import { executeHandler } from
|
|
1
|
+
import { getExampleFromSchema } from '@scalar/oas-utils/spec-getters';
|
|
2
|
+
import { accepts } from 'hono/accepts';
|
|
3
|
+
import { buildHandlerContext } from '../utils/build-handler-context.js';
|
|
4
|
+
import { executeHandler } from '../utils/execute-handler.js';
|
|
5
|
+
/**
|
|
6
|
+
* Get example response from OpenAPI spec for a given status code.
|
|
7
|
+
* Returns the example value if found, or null if not available.
|
|
8
|
+
*/
|
|
5
9
|
function getExampleFromResponse(c, statusCode, responses) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
if (!responses) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const statusCodeStr = statusCode.toString();
|
|
14
|
+
const response = responses[statusCodeStr] || responses.default;
|
|
15
|
+
if (!response) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const supportedContentTypes = Object.keys(response.content ?? {});
|
|
19
|
+
// If no content types are defined, return null
|
|
20
|
+
if (supportedContentTypes.length === 0) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// Content-Type negotiation
|
|
24
|
+
const acceptedContentType = accepts(c, {
|
|
25
|
+
header: 'Accept',
|
|
26
|
+
supports: supportedContentTypes,
|
|
27
|
+
default: supportedContentTypes.includes('application/json')
|
|
28
|
+
? 'application/json'
|
|
29
|
+
: (supportedContentTypes[0] ?? 'text/plain;charset=UTF-8'),
|
|
30
|
+
});
|
|
31
|
+
const acceptedResponse = response.content?.[acceptedContentType];
|
|
32
|
+
if (!acceptedResponse) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// Extract example from example property or generate from schema
|
|
36
|
+
return acceptedResponse.example !== undefined
|
|
37
|
+
? acceptedResponse.example
|
|
38
|
+
: acceptedResponse.schema
|
|
39
|
+
? getExampleFromSchema(acceptedResponse.schema, {
|
|
40
|
+
emptyString: 'string',
|
|
41
|
+
variables: c.req.param(),
|
|
42
|
+
mode: 'read',
|
|
43
|
+
})
|
|
44
|
+
: null;
|
|
32
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Determine HTTP status code based on store operation tracking.
|
|
48
|
+
* Prioritizes operations based on semantic meaning:
|
|
49
|
+
* - get > update > delete > create > list
|
|
50
|
+
* This ensures that if a handler performs multiple operations (e.g., get followed by create for logging),
|
|
51
|
+
* the status code reflects the most semantically meaningful operation.
|
|
52
|
+
*/
|
|
33
53
|
function determineStatusCode(tracking) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const getOperation = operations.find((op) => op.operation === "get");
|
|
39
|
-
if (getOperation) {
|
|
40
|
-
if (getOperation.result === void 0 || getOperation.result === null) {
|
|
41
|
-
return 404;
|
|
54
|
+
const { operations } = tracking;
|
|
55
|
+
// If no operations were performed, default to 200
|
|
56
|
+
if (operations.length === 0) {
|
|
57
|
+
return 200;
|
|
42
58
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
// Priority order: get > update > delete > create > list
|
|
60
|
+
// Check for get operations first (highest priority)
|
|
61
|
+
const getOperation = operations.find((op) => op.operation === 'get');
|
|
62
|
+
if (getOperation) {
|
|
63
|
+
// Return 404 if get() returned undefined or null
|
|
64
|
+
if (getOperation.result === undefined || getOperation.result === null) {
|
|
65
|
+
return 404;
|
|
66
|
+
}
|
|
67
|
+
return 200;
|
|
49
68
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
// Check for update operations
|
|
70
|
+
const updateOperation = operations.find((op) => op.operation === 'update');
|
|
71
|
+
if (updateOperation) {
|
|
72
|
+
// Return 404 if update() returned null (item not found)
|
|
73
|
+
if (updateOperation.result === null || updateOperation.result === undefined) {
|
|
74
|
+
return 404;
|
|
75
|
+
}
|
|
76
|
+
return 200;
|
|
56
77
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
// Check for delete operations
|
|
79
|
+
const deleteOperation = operations.find((op) => op.operation === 'delete');
|
|
80
|
+
if (deleteOperation) {
|
|
81
|
+
// Return 404 if delete() returned null (item not found)
|
|
82
|
+
if (deleteOperation.result === null || deleteOperation.result === undefined) {
|
|
83
|
+
return 404;
|
|
84
|
+
}
|
|
85
|
+
return 204;
|
|
86
|
+
}
|
|
87
|
+
// Check for create operations
|
|
88
|
+
const createOperation = operations.find((op) => op.operation === 'create');
|
|
89
|
+
if (createOperation) {
|
|
90
|
+
return 201;
|
|
91
|
+
}
|
|
92
|
+
// Default to 200 for list or any other operation
|
|
93
|
+
return 200;
|
|
64
94
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
const { context, tracking } = await buildHandlerContext(c, operation);
|
|
79
|
-
const { result } = await executeHandler(handlerCode, context);
|
|
80
|
-
const statusCode = determineStatusCode(tracking);
|
|
81
|
-
c.status(statusCode);
|
|
82
|
-
if (statusCode === 204) {
|
|
83
|
-
return c.body(null);
|
|
95
|
+
/**
|
|
96
|
+
* Mock response using x-handler code.
|
|
97
|
+
* Executes the handler and returns its result as the response.
|
|
98
|
+
*/
|
|
99
|
+
export async function mockHandlerResponse(c, operation, options) {
|
|
100
|
+
// Call onRequest callback
|
|
101
|
+
if (options?.onRequest) {
|
|
102
|
+
options.onRequest({
|
|
103
|
+
context: c,
|
|
104
|
+
operation,
|
|
105
|
+
});
|
|
84
106
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
c
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
// Get x-handler code from operation
|
|
108
|
+
const handlerCode = operation?.['x-handler'];
|
|
109
|
+
if (!handlerCode) {
|
|
110
|
+
c.status(500);
|
|
111
|
+
return c.json({ error: 'x-handler code not found in operation' });
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
// Build handler context with tracking
|
|
115
|
+
const { context, tracking } = await buildHandlerContext(c, operation);
|
|
116
|
+
// Execute handler
|
|
117
|
+
const { result } = await executeHandler(handlerCode, context);
|
|
118
|
+
// Determine status code based on all store operations, prioritizing semantically meaningful ones
|
|
119
|
+
const statusCode = determineStatusCode(tracking);
|
|
120
|
+
// Set status code
|
|
121
|
+
c.status(statusCode);
|
|
122
|
+
// For 204 No Content, return null body without Content-Type header
|
|
123
|
+
if (statusCode === 204) {
|
|
124
|
+
return c.body(null);
|
|
125
|
+
}
|
|
126
|
+
// Set Content-Type header for other responses
|
|
127
|
+
c.header('Content-Type', 'application/json');
|
|
128
|
+
// Return the handler result as JSON
|
|
129
|
+
// Handle undefined/null results gracefully
|
|
130
|
+
if (result === undefined || result === null) {
|
|
131
|
+
// Try to pick up example response from OpenAPI spec if available
|
|
132
|
+
const exampleResponse = getExampleFromResponse(c, statusCode, operation.responses);
|
|
133
|
+
if (exampleResponse !== null) {
|
|
134
|
+
return c.json(exampleResponse);
|
|
135
|
+
}
|
|
136
|
+
return c.json(null);
|
|
137
|
+
}
|
|
138
|
+
return c.json(result);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
// Log error to console
|
|
142
|
+
console.error('x-handler execution error:', error);
|
|
143
|
+
// Return 500 error
|
|
144
|
+
c.status(500);
|
|
145
|
+
return c.json({
|
|
146
|
+
error: 'Handler execution failed',
|
|
147
|
+
message: error instanceof Error ? error.message : String(error),
|
|
148
|
+
});
|
|
96
149
|
}
|
|
97
|
-
return c.json(result);
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error("x-handler execution error:", error);
|
|
100
|
-
c.status(500);
|
|
101
|
-
return c.json({
|
|
102
|
-
error: "Handler execution failed",
|
|
103
|
-
message: error instanceof Error ? error.message : String(error)
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
150
|
}
|
|
107
|
-
export {
|
|
108
|
-
mockHandlerResponse
|
|
109
|
-
};
|
|
110
|
-
//# sourceMappingURL=mock-handler-response.js.map
|
|
@@ -1,92 +1,105 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
1
|
+
/** Always responds with this code */
|
|
2
|
+
const EXAMPLE_AUTHORIZATION_CODE = 'super-secret-token';
|
|
3
|
+
/** Always responds with this token for implicit flow */
|
|
4
|
+
const EXAMPLE_ACCESS_TOKEN = 'super-secret-access-token';
|
|
5
|
+
/**
|
|
6
|
+
* Escapes HTML special characters to prevent XSS when rendering user-controlled scope values.
|
|
7
|
+
*/
|
|
3
8
|
function escapeHtml(s) {
|
|
4
|
-
|
|
9
|
+
return s
|
|
10
|
+
.replace(/&/g, '&')
|
|
11
|
+
.replace(/</g, '<')
|
|
12
|
+
.replace(/>/g, '>')
|
|
13
|
+
.replace(/"/g, '"')
|
|
14
|
+
.replace(/'/g, ''');
|
|
5
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Parses the OAuth 2.0 scope query parameter (space- or +-separated) into an array of scope strings.
|
|
18
|
+
*/
|
|
6
19
|
function parseScopeParam(scopeQuery) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
if (!scopeQuery || scopeQuery.trim() === '') {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return scopeQuery
|
|
24
|
+
.split(/[\s+]+/)
|
|
25
|
+
.map((s) => s.trim())
|
|
26
|
+
.filter(Boolean);
|
|
11
27
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Responds with an HTML page that simulates an OAuth 2.0 authorization page.
|
|
30
|
+
*/
|
|
31
|
+
export function respondWithAuthorizePage(c, title = '') {
|
|
32
|
+
const redirectUri = c.req.query('redirect_uri');
|
|
33
|
+
const responseType = c.req.query('response_type');
|
|
34
|
+
const scope = c.req.query('scope');
|
|
35
|
+
const state = c.req.query('state');
|
|
36
|
+
if (!redirectUri) {
|
|
37
|
+
return c.html(generateErrorHtml('Missing redirect_uri parameter', 'This parameter is required for the OAuth 2.0 authorization flow to function correctly. Please provide a valid redirect URI in your request.'), 400);
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
// Validate redirect URI against allowed domains
|
|
41
|
+
const redirectUrl = new URL(redirectUri);
|
|
42
|
+
const isImplicitFlow = responseType === 'token';
|
|
43
|
+
if (isImplicitFlow) {
|
|
44
|
+
const fragmentParams = new URLSearchParams();
|
|
45
|
+
fragmentParams.set('access_token', EXAMPLE_ACCESS_TOKEN);
|
|
46
|
+
fragmentParams.set('token_type', 'Bearer');
|
|
47
|
+
fragmentParams.set('expires_in', '3600');
|
|
48
|
+
if (scope) {
|
|
49
|
+
fragmentParams.set('scope', scope);
|
|
50
|
+
}
|
|
51
|
+
if (state) {
|
|
52
|
+
fragmentParams.set('state', state);
|
|
53
|
+
}
|
|
54
|
+
redirectUrl.hash = fragmentParams.toString();
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
redirectUrl.searchParams.set('code', EXAMPLE_AUTHORIZATION_CODE);
|
|
58
|
+
if (state) {
|
|
59
|
+
redirectUrl.searchParams.set('state', state);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const deniedUrl = new URL(redirectUri);
|
|
63
|
+
if (isImplicitFlow) {
|
|
64
|
+
const deniedFragmentParams = new URLSearchParams();
|
|
65
|
+
deniedFragmentParams.set('error', 'access_denied');
|
|
66
|
+
deniedFragmentParams.set('error_description', 'User has denied the authorization request');
|
|
67
|
+
if (state) {
|
|
68
|
+
deniedFragmentParams.set('state', state);
|
|
69
|
+
}
|
|
70
|
+
deniedUrl.hash = deniedFragmentParams.toString();
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
if (state) {
|
|
74
|
+
deniedUrl.searchParams.set('state', state);
|
|
75
|
+
}
|
|
76
|
+
deniedUrl.searchParams.set('error', 'access_denied');
|
|
77
|
+
deniedUrl.searchParams.set('error_description', 'User has denied the authorization request');
|
|
78
|
+
}
|
|
79
|
+
const scopes = parseScopeParam(c.req.query('scope'));
|
|
80
|
+
const htmlContent = generateAuthorizationHtml(redirectUrl.toString(), deniedUrl.toString(), title, scopes);
|
|
81
|
+
return c.html(htmlContent);
|
|
46
82
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const deniedFragmentParams = new URLSearchParams();
|
|
50
|
-
deniedFragmentParams.set("error", "access_denied");
|
|
51
|
-
deniedFragmentParams.set("error_description", "User has denied the authorization request");
|
|
52
|
-
if (state) {
|
|
53
|
-
deniedFragmentParams.set("state", state);
|
|
54
|
-
}
|
|
55
|
-
deniedUrl.hash = deniedFragmentParams.toString();
|
|
56
|
-
} else {
|
|
57
|
-
if (state) {
|
|
58
|
-
deniedUrl.searchParams.set("state", state);
|
|
59
|
-
}
|
|
60
|
-
deniedUrl.searchParams.set("error", "access_denied");
|
|
61
|
-
deniedUrl.searchParams.set("error_description", "User has denied the authorization request");
|
|
83
|
+
catch {
|
|
84
|
+
return c.html(generateErrorHtml('Invalid redirect_uri format', 'Please provide a valid URL. The redirect_uri parameter must be a properly formatted URL that includes the protocol (e.g., https://) and a valid domain. This is essential for the OAuth 2.0 flow to securely redirect after authorization.'), 400);
|
|
62
85
|
}
|
|
63
|
-
const scopes = parseScopeParam(c.req.query("scope"));
|
|
64
|
-
const htmlContent = generateAuthorizationHtml(redirectUrl.toString(), deniedUrl.toString(), title, scopes);
|
|
65
|
-
return c.html(htmlContent);
|
|
66
|
-
} catch {
|
|
67
|
-
return c.html(
|
|
68
|
-
generateErrorHtml(
|
|
69
|
-
"Invalid redirect_uri format",
|
|
70
|
-
"Please provide a valid URL. The redirect_uri parameter must be a properly formatted URL that includes the protocol (e.g., https://) and a valid domain. This is essential for the OAuth 2.0 flow to securely redirect after authorization."
|
|
71
|
-
),
|
|
72
|
-
400
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
86
|
}
|
|
76
|
-
function generateAuthorizationHtml(redirectUrl, deniedUrl, title =
|
|
77
|
-
|
|
87
|
+
function generateAuthorizationHtml(redirectUrl, deniedUrl, title = '', scopes = []) {
|
|
88
|
+
const scopesSection = scopes.length > 0
|
|
89
|
+
? `
|
|
78
90
|
<p class="font-medium text-gray-700">Requested Scopes</p>
|
|
79
91
|
<ul class="list-disc list-inside space-y-1">
|
|
80
|
-
${scopes.map((scope) => `<li><code class="bg-gray-100 py-1 px-2 rounded text-sm">${escapeHtml(scope)}</code></li>`).join(
|
|
81
|
-
</ul>`
|
|
82
|
-
|
|
92
|
+
${scopes.map((scope) => `<li><code class="bg-gray-100 py-1 px-2 rounded text-sm">${escapeHtml(scope)}</code></li>`).join('\n ')}
|
|
93
|
+
</ul>`
|
|
94
|
+
: '';
|
|
95
|
+
return `
|
|
83
96
|
<!DOCTYPE html>
|
|
84
97
|
<html lang="en">
|
|
85
98
|
<head>
|
|
86
99
|
<meta charset="UTF-8">
|
|
87
100
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
88
101
|
<title>OAuth 2.0 Authorization</title>
|
|
89
|
-
<script src="https://cdn.tailwindcss.com"
|
|
102
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
90
103
|
</head>
|
|
91
104
|
<body class="flex justify-center items-center h-screen bg-gray-100">
|
|
92
105
|
|
|
@@ -134,13 +147,13 @@ function generateAuthorizationHtml(redirectUrl, deniedUrl, title = "", scopes =
|
|
|
134
147
|
`;
|
|
135
148
|
}
|
|
136
149
|
function generateErrorHtml(title, message) {
|
|
137
|
-
|
|
150
|
+
return `<html>
|
|
138
151
|
<html lang="en">
|
|
139
152
|
<head>
|
|
140
153
|
<meta charset="UTF-8">
|
|
141
154
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
142
155
|
<title>OAuth 2.0 Authorization</title>
|
|
143
|
-
<script src="https://cdn.tailwindcss.com"
|
|
156
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
144
157
|
</head>
|
|
145
158
|
<body>
|
|
146
159
|
<div class="p-4 m-8 flex flex-col gap-4 text-lg">
|
|
@@ -157,7 +170,3 @@ function generateErrorHtml(title, message) {
|
|
|
157
170
|
</body>
|
|
158
171
|
</html>`;
|
|
159
172
|
}
|
|
160
|
-
export {
|
|
161
|
-
respondWithAuthorizePage
|
|
162
|
-
};
|
|
163
|
-
//# sourceMappingURL=respond-with-authorize-page.js.map
|
|
@@ -1,39 +1,36 @@
|
|
|
1
|
-
import { normalize, toYaml } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (format === "json") {
|
|
9
|
-
return c.json(document);
|
|
1
|
+
import { normalize, toYaml } from '@scalar/openapi-parser';
|
|
2
|
+
/**
|
|
3
|
+
* OpenAPI endpoints
|
|
4
|
+
*/
|
|
5
|
+
export function respondWithOpenApiDocument(c, input, format = 'json') {
|
|
6
|
+
if (!input) {
|
|
7
|
+
return c.text('Not found', 404);
|
|
10
8
|
}
|
|
11
9
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
const document = normalize(input);
|
|
11
|
+
// JSON
|
|
12
|
+
if (format === 'json') {
|
|
13
|
+
return c.json(document);
|
|
14
|
+
}
|
|
15
|
+
// YAML
|
|
16
|
+
try {
|
|
17
|
+
const yamlDocument = toYaml(normalize(document));
|
|
18
|
+
c.header('Content-Type', 'text/yaml');
|
|
19
|
+
return c.text(yamlDocument, 200, {
|
|
20
|
+
'Content-Type': 'application/yaml; charset=UTF-8',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return c.json({
|
|
25
|
+
error: 'Failed to convert document to YAML',
|
|
26
|
+
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
27
|
+
}, 500);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
return c.json({
|
|
32
|
+
error: 'Failed to parse OpenAPI document',
|
|
33
|
+
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
34
|
+
}, 400);
|
|
25
35
|
}
|
|
26
|
-
} catch (error) {
|
|
27
|
-
return c.json(
|
|
28
|
-
{
|
|
29
|
-
error: "Failed to parse OpenAPI document",
|
|
30
|
-
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
31
|
-
},
|
|
32
|
-
400
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
36
|
}
|
|
36
|
-
export {
|
|
37
|
-
respondWithOpenApiDocument
|
|
38
|
-
};
|
|
39
|
-
//# sourceMappingURL=respond-with-openapi-document.js.map
|
|
@@ -1,46 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return c.json(tokenResponse);
|
|
1
|
+
/** Always responds with this token */
|
|
2
|
+
const EXAMPLE_ACCESS_TOKEN = 'super-secret-access-token';
|
|
3
|
+
/**
|
|
4
|
+
* Responds with a JSON object simulating an OAuth 2.0 token response.
|
|
5
|
+
*/
|
|
6
|
+
export function respondWithToken(c) {
|
|
7
|
+
const grantType = c.req.query('grant_type');
|
|
8
|
+
if (!grantType) {
|
|
9
|
+
return c.json({
|
|
10
|
+
error: 'invalid_request',
|
|
11
|
+
error_description: 'Missing grant_type parameter',
|
|
12
|
+
}, 400);
|
|
13
|
+
}
|
|
14
|
+
// Validate supported grant types
|
|
15
|
+
const supportedGrantTypes = ['authorization_code', 'client_credentials', 'refresh_token'];
|
|
16
|
+
if (!supportedGrantTypes.includes(grantType)) {
|
|
17
|
+
return c.json({
|
|
18
|
+
error: 'unsupported_grant_type',
|
|
19
|
+
error_description: `Grant type must be one of: ${supportedGrantTypes.join(', ')}`,
|
|
20
|
+
}, 400);
|
|
21
|
+
}
|
|
22
|
+
// Validate required parameters for each grant type
|
|
23
|
+
if (grantType === 'authorization_code' && !c.req.query('code')) {
|
|
24
|
+
return c.json({
|
|
25
|
+
error: 'invalid_request',
|
|
26
|
+
error_description: 'Missing code parameter',
|
|
27
|
+
}, 400);
|
|
28
|
+
}
|
|
29
|
+
// Simulate token generation
|
|
30
|
+
const tokenResponse = {
|
|
31
|
+
access_token: EXAMPLE_ACCESS_TOKEN,
|
|
32
|
+
token_type: 'Bearer',
|
|
33
|
+
expires_in: 3600,
|
|
34
|
+
refresh_token: 'example-refresh-token',
|
|
35
|
+
scope: c.req.query('scope') ?? 'read write',
|
|
36
|
+
};
|
|
37
|
+
// Security headers
|
|
38
|
+
c.header('Cache-Control', 'no-store');
|
|
39
|
+
c.header('Pragma', 'no-cache');
|
|
40
|
+
return c.json(tokenResponse);
|
|
42
41
|
}
|
|
43
|
-
export {
|
|
44
|
-
respondWithToken
|
|
45
|
-
};
|
|
46
|
-
//# sourceMappingURL=respond-with-token.js.map
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
httpMethods
|
|
4
|
-
};
|
|
5
|
-
//# sourceMappingURL=types.js.map
|
|
1
|
+
/** Available HTTP methods for Hono routes */
|
|
2
|
+
export const httpMethods = ['get', 'put', 'post', 'delete', 'options', 'patch'];
|