@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.
Files changed (50) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/create-mock-server.js +79 -68
  3. package/dist/index.js +1 -5
  4. package/dist/libs/store.js +66 -65
  5. package/dist/routes/mock-any-response.js +70 -63
  6. package/dist/routes/mock-handler-response.js +140 -100
  7. package/dist/routes/respond-with-authorize-page.js +89 -80
  8. package/dist/routes/respond-with-openapi-document.js +32 -35
  9. package/dist/routes/respond-with-token.js +40 -45
  10. package/dist/types.js +2 -5
  11. package/dist/utils/build-handler-context.js +83 -70
  12. package/dist/utils/build-seed-context.js +59 -52
  13. package/dist/utils/create-openapi-definition.js +7 -10
  14. package/dist/utils/execute-handler.js +16 -18
  15. package/dist/utils/execute-seed.js +22 -21
  16. package/dist/utils/find-preferred-response-key.js +9 -9
  17. package/dist/utils/get-open-auth-token-urls.js +45 -35
  18. package/dist/utils/get-operation.js +12 -12
  19. package/dist/utils/handle-authentication.js +106 -101
  20. package/dist/utils/hono-route-from-path.js +6 -6
  21. package/dist/utils/is-authentication-required.js +17 -15
  22. package/dist/utils/log-authentication-instructions.js +105 -110
  23. package/dist/utils/process-openapi-document.js +71 -58
  24. package/dist/utils/set-up-authentication-routes.js +80 -77
  25. package/dist/utils/store-wrapper.js +40 -39
  26. package/package.json +10 -14
  27. package/dist/create-mock-server.js.map +0 -7
  28. package/dist/index.js.map +0 -7
  29. package/dist/libs/store.js.map +0 -7
  30. package/dist/routes/mock-any-response.js.map +0 -7
  31. package/dist/routes/mock-handler-response.js.map +0 -7
  32. package/dist/routes/respond-with-authorize-page.js.map +0 -7
  33. package/dist/routes/respond-with-openapi-document.js.map +0 -7
  34. package/dist/routes/respond-with-token.js.map +0 -7
  35. package/dist/types.js.map +0 -7
  36. package/dist/utils/build-handler-context.js.map +0 -7
  37. package/dist/utils/build-seed-context.js.map +0 -7
  38. package/dist/utils/create-openapi-definition.js.map +0 -7
  39. package/dist/utils/execute-handler.js.map +0 -7
  40. package/dist/utils/execute-seed.js.map +0 -7
  41. package/dist/utils/find-preferred-response-key.js.map +0 -7
  42. package/dist/utils/get-open-auth-token-urls.js.map +0 -7
  43. package/dist/utils/get-operation.js.map +0 -7
  44. package/dist/utils/handle-authentication.js.map +0 -7
  45. package/dist/utils/hono-route-from-path.js.map +0 -7
  46. package/dist/utils/is-authentication-required.js.map +0 -7
  47. package/dist/utils/log-authentication-instructions.js.map +0 -7
  48. package/dist/utils/process-openapi-document.js.map +0 -7
  49. package/dist/utils/set-up-authentication-routes.js.map +0 -7
  50. package/dist/utils/store-wrapper.js.map +0 -7
@@ -1,75 +1,88 @@
1
- import { faker } from "@faker-js/faker";
2
- import { getExampleFromSchema } from "@scalar/oas-utils/spec-getters";
3
- import { accepts } from "hono/accepts";
4
- import { store } from "../libs/store.js";
5
- import { createStoreWrapper } from "./store-wrapper.js";
1
+ import { faker } from '@faker-js/faker';
2
+ import { getExampleFromSchema } from '@scalar/oas-utils/spec-getters';
3
+ import { accepts } from 'hono/accepts';
4
+ import { store } from '../libs/store.js';
5
+ import { createStoreWrapper } from './store-wrapper.js';
6
+ /**
7
+ * Get example response from OpenAPI spec for a given status code.
8
+ * Returns the example value if found, or null if not available.
9
+ */
6
10
  function getExampleFromResponse(c, statusCode, responses) {
7
- if (!responses) {
8
- return null;
9
- }
10
- const response = responses[statusCode] || responses.default;
11
- if (!response) {
12
- return null;
13
- }
14
- const supportedContentTypes = Object.keys(response.content ?? {});
15
- if (supportedContentTypes.length === 0) {
16
- return null;
17
- }
18
- const acceptedContentType = accepts(c, {
19
- header: "Accept",
20
- supports: supportedContentTypes,
21
- default: supportedContentTypes.includes("application/json") ? "application/json" : supportedContentTypes[0] ?? "text/plain;charset=UTF-8"
22
- });
23
- const acceptedResponse = response.content?.[acceptedContentType];
24
- if (!acceptedResponse) {
25
- return null;
26
- }
27
- return acceptedResponse.example !== void 0 ? acceptedResponse.example : acceptedResponse.schema ? getExampleFromSchema(acceptedResponse.schema, {
28
- emptyString: "string",
29
- variables: c.req.param(),
30
- mode: "read"
31
- }) : null;
11
+ if (!responses) {
12
+ return null;
13
+ }
14
+ const response = responses[statusCode] || 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 - prefer application/json
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
  }
33
- async function buildHandlerContext(c, operation) {
34
- let body = void 0;
35
- try {
36
- const contentType = c.req.header("content-type") ?? "";
37
- if (contentType.includes("application/json")) {
38
- body = await c.req.json().catch(() => void 0);
39
- } else if (contentType.includes("application/x-www-form-urlencoded")) {
40
- body = await c.req.parseBody().catch(() => void 0);
41
- } else if (contentType.includes("text/")) {
42
- body = await c.req.text().catch(() => void 0);
46
+ /**
47
+ * Build the handler context from a Hono context.
48
+ */
49
+ export async function buildHandlerContext(c, operation) {
50
+ let body = undefined;
51
+ try {
52
+ const contentType = c.req.header('content-type') ?? '';
53
+ if (contentType.includes('application/json')) {
54
+ body = await c.req.json().catch(() => undefined);
55
+ }
56
+ else if (contentType.includes('application/x-www-form-urlencoded')) {
57
+ body = await c.req.parseBody().catch(() => undefined);
58
+ }
59
+ else if (contentType.includes('text/')) {
60
+ body = await c.req.text().catch(() => undefined);
61
+ }
62
+ }
63
+ catch {
64
+ // Ignore parsing errors, body remains undefined
43
65
  }
44
- } catch {
45
- }
46
- const { wrappedStore, tracking } = createStoreWrapper(store);
47
- const res = {};
48
- if (operation?.responses) {
49
- for (const statusCode of Object.keys(operation.responses)) {
50
- res[statusCode] = getExampleFromResponse(
51
- c,
52
- statusCode,
53
- operation.responses
54
- );
66
+ const { wrappedStore, tracking } = createStoreWrapper(store);
67
+ // Build res object with examples for all response status codes
68
+ const res = {};
69
+ if (operation?.responses) {
70
+ for (const statusCode of Object.keys(operation.responses)) {
71
+ res[statusCode] = getExampleFromResponse(c, statusCode, operation.responses);
72
+ }
55
73
  }
56
- }
57
- return {
58
- context: {
59
- store: wrappedStore,
60
- faker,
61
- req: {
62
- body,
63
- params: c.req.param(),
64
- query: Object.fromEntries(new URL(c.req.url).searchParams.entries()),
65
- headers: Object.fromEntries(Object.entries(c.req.header()).map(([key, value]) => [key, value ?? ""]))
66
- },
67
- res
68
- },
69
- tracking
70
- };
74
+ return {
75
+ context: {
76
+ store: wrappedStore,
77
+ faker,
78
+ req: {
79
+ body,
80
+ params: c.req.param(),
81
+ query: Object.fromEntries(new URL(c.req.url).searchParams.entries()),
82
+ headers: Object.fromEntries(Object.entries(c.req.header()).map(([key, value]) => [key, value ?? ''])),
83
+ },
84
+ res,
85
+ },
86
+ tracking,
87
+ };
71
88
  }
72
- export {
73
- buildHandlerContext
74
- };
75
- //# sourceMappingURL=build-handler-context.js.map
@@ -1,53 +1,60 @@
1
- import { faker } from "@faker-js/faker";
2
- import { store } from "../libs/store.js";
3
- import { createStoreWrapper } from "./store-wrapper.js";
4
- function buildSeedContext(schemaKey) {
5
- const { wrappedStore } = createStoreWrapper(store);
6
- const seedHelper = (arg1, arg2) => {
7
- if (typeof arg1 === "number" && typeof arg2 === "function") {
8
- const count = arg1;
9
- const factory = arg2;
10
- const items = [];
11
- for (let i = 0; i < count; i++) {
12
- const item = factory();
13
- const created = wrappedStore.create(schemaKey, item);
14
- items.push(created);
15
- }
16
- return items;
17
- }
18
- if (Array.isArray(arg1)) {
19
- const items = [];
20
- for (const item of arg1) {
21
- const created = wrappedStore.create(schemaKey, item);
22
- items.push(created);
23
- }
24
- return items;
25
- }
26
- if (typeof arg1 === "function") {
27
- const factory = arg1;
28
- const item = factory();
29
- const created = wrappedStore.create(schemaKey, item);
30
- return created;
31
- }
32
- throw new Error("Invalid seed() usage. Use seed.count(n, factory), seed(array), or seed(factory)");
33
- };
34
- seedHelper.count = (n, factory) => {
35
- const items = [];
36
- for (let i = 0; i < n; i++) {
37
- const item = factory();
38
- const created = wrappedStore.create(schemaKey, item);
39
- items.push(created);
40
- }
41
- return items;
42
- };
43
- return {
44
- store: wrappedStore,
45
- faker,
46
- seed: seedHelper,
47
- schema: schemaKey
48
- };
1
+ import { faker } from '@faker-js/faker';
2
+ import { store } from '../libs/store.js';
3
+ import { createStoreWrapper } from './store-wrapper.js';
4
+ /**
5
+ * Build the seed context with a seed helper function.
6
+ * The seed helper automatically uses the schema key as the collection name.
7
+ */
8
+ export function buildSeedContext(schemaKey) {
9
+ const { wrappedStore } = createStoreWrapper(store);
10
+ /**
11
+ * Seed helper function that provides a Laravel-inspired API.
12
+ */
13
+ const seedHelper = ((arg1, arg2) => {
14
+ // Case 1: seed.count(n, factory)
15
+ if (typeof arg1 === 'number' && typeof arg2 === 'function') {
16
+ const count = arg1;
17
+ const factory = arg2;
18
+ const items = [];
19
+ for (let i = 0; i < count; i++) {
20
+ const item = factory();
21
+ const created = wrappedStore.create(schemaKey, item);
22
+ items.push(created);
23
+ }
24
+ return items;
25
+ }
26
+ // Case 2: seed(array)
27
+ if (Array.isArray(arg1)) {
28
+ const items = [];
29
+ for (const item of arg1) {
30
+ const created = wrappedStore.create(schemaKey, item);
31
+ items.push(created);
32
+ }
33
+ return items;
34
+ }
35
+ // Case 3: seed(factory) - single item
36
+ if (typeof arg1 === 'function') {
37
+ const factory = arg1;
38
+ const item = factory();
39
+ const created = wrappedStore.create(schemaKey, item);
40
+ return created;
41
+ }
42
+ throw new Error('Invalid seed() usage. Use seed.count(n, factory), seed(array), or seed(factory)');
43
+ });
44
+ // Add count method to the function
45
+ seedHelper.count = (n, factory) => {
46
+ const items = [];
47
+ for (let i = 0; i < n; i++) {
48
+ const item = factory();
49
+ const created = wrappedStore.create(schemaKey, item);
50
+ items.push(created);
51
+ }
52
+ return items;
53
+ };
54
+ return {
55
+ store: wrappedStore,
56
+ faker,
57
+ seed: seedHelper,
58
+ schema: schemaKey,
59
+ };
49
60
  }
50
- export {
51
- buildSeedContext
52
- };
53
- //# sourceMappingURL=build-seed-context.js.map
@@ -1,11 +1,8 @@
1
- function createOpenApiDefinition(securitySchemes) {
2
- return {
3
- openapi: "3.1.1",
4
- info: { title: "Test API", version: "1.0.0" },
5
- components: { securitySchemes }
6
- };
1
+ /** Helper function create an OpenAPI document with security schemss */
2
+ export function createOpenApiDefinition(securitySchemes) {
3
+ return {
4
+ openapi: '3.1.1',
5
+ info: { title: 'Test API', version: '1.0.0' },
6
+ components: { securitySchemes },
7
+ };
7
8
  }
8
- export {
9
- createOpenApiDefinition
10
- };
11
- //# sourceMappingURL=create-openapi-definition.js.map
@@ -1,20 +1,18 @@
1
- async function executeHandler(code, context) {
2
- const handlerFunction = new Function(
3
- "store",
4
- "faker",
5
- "req",
6
- "res",
7
- `
1
+ /**
2
+ * Execute handler code in a sandboxed environment.
3
+ * The code has access only to the provided context (store, faker, req, res).
4
+ */
5
+ export async function executeHandler(code, context) {
6
+ // Create a function that executes the handler code with the context
7
+ // Using Function constructor to create a sandboxed environment
8
+ // The code is wrapped in a function body that returns the result
9
+ const handlerFunction = new Function('store', 'faker', 'req', 'res', `
8
10
  ${code}
9
- `
10
- );
11
- const result = handlerFunction(context.store, context.faker, context.req, context.res);
12
- if (result instanceof Promise) {
13
- return { result: await result };
14
- }
15
- return { result };
11
+ `);
12
+ const result = handlerFunction(context.store, context.faker, context.req, context.res);
13
+ // If the result is a Promise, await it
14
+ if (result instanceof Promise) {
15
+ return { result: await result };
16
+ }
17
+ return { result };
16
18
  }
17
- export {
18
- executeHandler
19
- };
20
- //# sourceMappingURL=execute-handler.js.map
@@ -1,24 +1,25 @@
1
- async function executeSeed(code, context) {
2
- const seedFunction = new Function(
3
- "store",
4
- "faker",
5
- "seed",
6
- "schema",
7
- `
1
+ /**
2
+ * Execute seed code in a sandboxed environment.
3
+ * The code has access only to the provided context (store, faker, seed, schema).
4
+ */
5
+ export async function executeSeed(code, context) {
6
+ // Create a function that executes the seed code with the context
7
+ // Using Function constructor to create a sandboxed environment
8
+ // The code is wrapped in a function body that returns the result
9
+ const seedFunction = new Function('store', 'faker', 'seed', 'schema', `
8
10
  ${code}
9
- `
10
- );
11
- try {
12
- const result = seedFunction(context.store, context.faker, context.seed, context.schema);
13
- if (result instanceof Promise) {
14
- return { result: await result };
11
+ `);
12
+ // Execute the seed function with the context
13
+ try {
14
+ const result = seedFunction(context.store, context.faker, context.seed, context.schema);
15
+ // If the result is a Promise, await it
16
+ if (result instanceof Promise) {
17
+ return { result: await result };
18
+ }
19
+ return { result };
20
+ }
21
+ catch (error) {
22
+ // Re-throw to be caught by the caller
23
+ throw error;
15
24
  }
16
- return { result };
17
- } catch (error) {
18
- throw error;
19
- }
20
25
  }
21
- export {
22
- executeSeed
23
- };
24
- //# sourceMappingURL=execute-seed.js.map
@@ -1,11 +1,11 @@
1
- function findPreferredResponseKey(responses) {
2
- return (
1
+ /**
2
+ * Find the preferred response key: default, 200, 201 …
3
+ */
4
+ export function findPreferredResponseKey(responses) {
5
+ return (
3
6
  // Regular status codes
4
- ["default", "200", "201", "204", "404", "500"].find((key) => responses?.includes(key) ?? false) ?? // Lowest status code
5
- responses?.sort()[0] ?? void 0
6
- );
7
+ ['default', '200', '201', '204', '404', '500'].find((key) => responses?.includes(key) ?? false) ??
8
+ // Lowest status code
9
+ responses?.sort()[0] ??
10
+ undefined);
7
11
  }
8
- export {
9
- findPreferredResponseKey
10
- };
11
- //# sourceMappingURL=find-preferred-response-key.js.map
@@ -1,42 +1,52 @@
1
- function getPathFromUrl(url) {
2
- try {
3
- const urlObject = url.startsWith("http") ? new URL(url) : new URL(url, "http://example.com");
4
- const path = urlObject.pathname;
5
- return path === "/" ? path : path.replace(/\/$/, "");
6
- } catch {
7
- return url;
8
- }
1
+ /**
2
+ * Extract path from URL
3
+ */
4
+ export function getPathFromUrl(url) {
5
+ try {
6
+ // Handle relative URLs by prepending a base
7
+ const urlObject = url.startsWith('http') ? new URL(url) : new URL(url, 'http://example.com');
8
+ // Normalize: remove trailing slash except for root path
9
+ const path = urlObject.pathname;
10
+ return path === '/' ? path : path.replace(/\/$/, '');
11
+ }
12
+ catch {
13
+ // If URL is invalid, return the original string
14
+ return url;
15
+ }
9
16
  }
17
+ /**
18
+ * Returns all token URLs mentioned in the securitySchemes, without the domain
19
+ */
20
+ // Type guard for OAuth2 security scheme
10
21
  function isOAuth2Scheme(scheme) {
11
- return scheme.type === "oauth2";
22
+ return scheme.type === 'oauth2';
12
23
  }
24
+ // Validate token URL
13
25
  function isValidTokenUrl(url) {
14
- return url.trim().length > 0;
26
+ return url.trim().length > 0;
15
27
  }
16
- function getOpenAuthTokenUrls(schema) {
17
- if (!schema?.components?.securitySchemes) {
18
- return [];
19
- }
20
- const securitySchemes = schema.components.securitySchemes;
21
- const tokenUrls = /* @__PURE__ */ new Set();
22
- for (const scheme of Object.values(securitySchemes)) {
23
- if (!isOAuth2Scheme(scheme)) {
24
- continue;
28
+ export function getOpenAuthTokenUrls(schema) {
29
+ if (!schema?.components?.securitySchemes) {
30
+ return [];
31
+ }
32
+ const securitySchemes = schema.components.securitySchemes;
33
+ // Use Set from the start for better memory efficiency
34
+ const tokenUrls = new Set();
35
+ // Iterate through all security schemes
36
+ for (const scheme of Object.values(securitySchemes)) {
37
+ if (!isOAuth2Scheme(scheme)) {
38
+ continue;
39
+ }
40
+ const flows = scheme.flows; // Type assertion no longer needed
41
+ // Helper to safely add valid token URLs
42
+ const addTokenUrl = (url) => {
43
+ if (url && isValidTokenUrl(url)) {
44
+ tokenUrls.add(getPathFromUrl(url));
45
+ }
46
+ };
47
+ addTokenUrl(flows?.password?.tokenUrl);
48
+ addTokenUrl(flows?.clientCredentials?.tokenUrl);
49
+ addTokenUrl(flows?.authorizationCode?.tokenUrl);
25
50
  }
26
- const flows = scheme.flows;
27
- const addTokenUrl = (url) => {
28
- if (url && isValidTokenUrl(url)) {
29
- tokenUrls.add(getPathFromUrl(url));
30
- }
31
- };
32
- addTokenUrl(flows?.password?.tokenUrl);
33
- addTokenUrl(flows?.clientCredentials?.tokenUrl);
34
- addTokenUrl(flows?.authorizationCode?.tokenUrl);
35
- }
36
- return Array.from(tokenUrls);
51
+ return Array.from(tokenUrls);
37
52
  }
38
- export {
39
- getOpenAuthTokenUrls,
40
- getPathFromUrl
41
- };
42
- //# sourceMappingURL=get-open-auth-token-urls.js.map
@@ -1,14 +1,14 @@
1
- import { httpMethods } from "../types.js";
2
- function getOperations(path) {
3
- const operations = {};
4
- for (const method of httpMethods) {
5
- if (path?.[method]) {
6
- operations[method] = path?.[method];
1
+ import { httpMethods } from '../types.js';
2
+ /**
3
+ * Takes a dereferenced OpenAPI document and returns all operations.
4
+ * Ignores other attributes, like summary, parameters, etc.
5
+ */
6
+ export function getOperations(path) {
7
+ const operations = {};
8
+ for (const method of httpMethods) {
9
+ if (path?.[method]) {
10
+ operations[method] = path?.[method];
11
+ }
7
12
  }
8
- }
9
- return operations;
13
+ return operations;
10
14
  }
11
- export {
12
- getOperations
13
- };
14
- //# sourceMappingURL=get-operation.js.map