mock-config-server 2.3.0 → 2.4.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.
Files changed (29) hide show
  1. package/README.md +1 -1
  2. package/dist/src/core/graphql/createGraphQLRoutes/createGraphQLRoutes.js +9 -5
  3. package/dist/src/core/middlewares/notFoundMiddleware/helpers/getGraphqlUrlSuggestions/getGraphqlUrlSuggestions.d.ts +7 -2
  4. package/dist/src/core/middlewares/notFoundMiddleware/helpers/getGraphqlUrlSuggestions/getGraphqlUrlSuggestions.js +8 -7
  5. package/dist/src/core/middlewares/notFoundMiddleware/helpers/getRestUrlSuggestions/getRestUrlSuggestions.d.ts +7 -2
  6. package/dist/src/core/middlewares/notFoundMiddleware/helpers/getRestUrlSuggestions/getRestUrlSuggestions.js +4 -4
  7. package/dist/src/core/middlewares/notFoundMiddleware/notFoundMiddleware.js +36 -30
  8. package/dist/src/server/createMockServer/createMockServer.js +2 -1
  9. package/dist/src/static/views/assets/icons/scheme-dark.svg +3 -0
  10. package/dist/src/static/views/assets/icons/scheme-light.svg +3 -0
  11. package/dist/src/static/views/assets/images/404.png +0 -0
  12. package/dist/src/static/views/assets/images/logo.png +0 -0
  13. package/dist/src/static/views/assets/images/success.png +0 -0
  14. package/dist/src/static/views/assets/styles/global.css +88 -0
  15. package/dist/src/static/views/components/header/index.css +55 -0
  16. package/dist/src/static/views/components/header/index.ejs +40 -0
  17. package/dist/src/static/views/components/header/index.js +1 -0
  18. package/dist/src/static/views/features/scheme/dark.css +12 -0
  19. package/dist/src/static/views/features/scheme/index.ejs +3 -0
  20. package/dist/src/static/views/features/scheme/index.js +31 -0
  21. package/dist/src/static/views/features/scheme/light.css +12 -0
  22. package/dist/src/static/views/features/tab/index.css +30 -0
  23. package/dist/src/static/views/features/tab/index.ejs +2 -0
  24. package/dist/src/static/views/features/tab/index.js +12 -0
  25. package/dist/src/static/views/pages/404/index.css +10 -0
  26. package/dist/src/static/views/pages/404/index.ejs +85 -0
  27. package/dist/src/utils/types/rest.d.ts +2 -1
  28. package/package.json +9 -2
  29. package/dist/src/static/views/notFound.ejs +0 -42
package/README.md CHANGED
@@ -215,7 +215,7 @@ Entity for connecting statics to the server, like HTML, JSON, PNG, etc.
215
215
  Object with settings for [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). You can flexibly configure the required origin, methods, headers, credentials, maxAge for the entire server. If you do not specify `CORS` settings, then it will be disabled.
216
216
 
217
217
  - `origin` {string | RegExp | Array<string | RegExp> | Function | Promise } available origins from which requests can be made
218
- - `methods?` {Array<GET | POST | DELETE | PUT | PATCH>} available methods (default: `*`)
218
+ - `methods?` {Array<GET | POST | DELETE | PUT | PATCH>} available methods (default: `GET,OPTIONS,PUT,PATCH,POST,DELETE`)
219
219
  - `allowedHeaders?` {Array<string>} allowed headers (default: `*`)
220
220
  - `exposedHeaders?` {Array<string>} exposed headers (default: `*`)
221
221
  - `credentials?` {boolean} param tells browsers whether to expose the response to the frontend JavaScript code (default: `true`)
@@ -8,16 +8,20 @@ const createGraphQLRoutes = (router, graphqlConfig, serverResponseInterceptors)
8
8
  const graphqlMiddleware = async (request, response, next) => {
9
9
  const graphQLInput = (0, helpers_1.getGraphQLInput)(request);
10
10
  if (!graphQLInput || !graphQLInput.query) {
11
- return response.status(400).json('Query is missing, you must pass a valid GraphQL query');
11
+ return response
12
+ .status(400)
13
+ .json({ message: 'Query is missing, you must pass a valid GraphQL query' });
12
14
  }
13
15
  const query = (0, helpers_1.parseQuery)(graphQLInput.query);
14
16
  if (!query) {
15
- return response.status(400).json('Query is invalid, you must use a valid GraphQL query');
16
- }
17
- if (!query.operationName || !query.operationType) {
18
17
  return response
19
18
  .status(400)
20
- .json(`You should to specify operationName and operationType for ${request.method}:${request.baseUrl}${request.path}`);
19
+ .json({ message: 'Query is invalid, you must use a valid GraphQL query' });
20
+ }
21
+ if (!query.operationName || !query.operationType) {
22
+ return response.status(400).json({
23
+ message: `You should to specify operationName and operationType for ${request.method}:${request.baseUrl}${request.path}`
24
+ });
21
25
  }
22
26
  const matchedRequestConfig = preparedGraphQLRequestConfig.find((requestConfig) => {
23
27
  if (requestConfig.operationName instanceof RegExp) {
@@ -1,6 +1,11 @@
1
+ import type { GraphQLOperationType } from '../../../../../utils/types';
2
+ export type GraphqlRequestSuggestionConfigs = {
3
+ operationType: GraphQLOperationType;
4
+ operationName: string;
5
+ }[];
1
6
  interface GetGraphqlUrlSuggestionsParams {
2
7
  url: URL;
3
- graphqlPatternUrlMeaningfulStrings: string[];
8
+ requestConfigs: GraphqlRequestSuggestionConfigs;
4
9
  }
5
- export declare const getGraphqlUrlSuggestions: ({ url, graphqlPatternUrlMeaningfulStrings }: GetGraphqlUrlSuggestionsParams) => string[];
10
+ export declare const getGraphqlUrlSuggestions: ({ url, requestConfigs }: GetGraphqlUrlSuggestionsParams) => GraphqlRequestSuggestionConfigs;
6
11
  export {};
@@ -2,15 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getGraphqlUrlSuggestions = void 0;
4
4
  const getLevenshteinDistance_1 = require("../getLevenshteinDistance/getLevenshteinDistance");
5
- const getGraphqlUrlSuggestions = ({ url, graphqlPatternUrlMeaningfulStrings }) => {
5
+ const getGraphqlUrlSuggestions = ({ url, requestConfigs }) => {
6
6
  // ✅ important: operationName is always second word in 'query' query param
7
- const operationName = url.searchParams.get('query')?.split(' ')[1];
8
- const actualUrlMeaningfulString = `${url.pathname}/${operationName}`;
9
- const graphqlUrlSuggestions = graphqlPatternUrlMeaningfulStrings.reduce((acc, patternUrlMeaningfulString) => {
10
- const distance = (0, getLevenshteinDistance_1.getLevenshteinDistance)(actualUrlMeaningfulString, patternUrlMeaningfulString);
11
- const tolerance = Math.floor(patternUrlMeaningfulString.length / 2);
7
+ const actualOperationName = url.searchParams.get('query')?.split(' ')[1];
8
+ const actualUrlMeaningful = `${url.pathname}/${actualOperationName}`;
9
+ const graphqlUrlSuggestions = requestConfigs.reduce((acc, requestConfig) => {
10
+ const { operationName } = requestConfig;
11
+ const distance = (0, getLevenshteinDistance_1.getLevenshteinDistance)(actualUrlMeaningful, operationName);
12
+ const tolerance = Math.floor(operationName.length / 2);
12
13
  if (distance <= tolerance)
13
- acc.push(patternUrlMeaningfulString);
14
+ acc.push(requestConfig);
14
15
  return acc;
15
16
  }, []);
16
17
  return graphqlUrlSuggestions;
@@ -1,6 +1,11 @@
1
+ import type { RestMethod, RestPathString } from '../../../../../utils/types';
2
+ export type RestRequestSuggestionConfigs = {
3
+ method: RestMethod;
4
+ path: RestPathString;
5
+ }[];
1
6
  interface GetRestUrlSuggestionsParams {
2
7
  url: URL;
3
- patternUrls: string[];
8
+ requestConfigs: RestRequestSuggestionConfigs;
4
9
  }
5
- export declare const getRestUrlSuggestions: ({ url, patternUrls }: GetRestUrlSuggestionsParams) => string[];
10
+ export declare const getRestUrlSuggestions: ({ url, requestConfigs }: GetRestUrlSuggestionsParams) => RestRequestSuggestionConfigs;
6
11
  export {};
@@ -4,10 +4,10 @@ exports.getRestUrlSuggestions = void 0;
4
4
  const helpers_1 = require("../../../../../utils/helpers");
5
5
  const getLevenshteinDistance_1 = require("../getLevenshteinDistance/getLevenshteinDistance");
6
6
  const helpers_2 = require("./helpers");
7
- const getRestUrlSuggestions = ({ url, patternUrls }) => {
7
+ const getRestUrlSuggestions = ({ url, requestConfigs }) => {
8
8
  const actualUrlParts = (0, helpers_1.getUrlParts)(url.pathname);
9
- const restUrlSuggestions = patternUrls.reduce((acc, patternUrl) => {
10
- const patternUrlParts = (0, helpers_1.getUrlParts)(patternUrl);
9
+ const restUrlSuggestions = requestConfigs.reduce((acc, requestConfig) => {
10
+ const patternUrlParts = (0, helpers_1.getUrlParts)(requestConfig.path);
11
11
  // ✅ important: ignore patterns with different amount of parts
12
12
  if (patternUrlParts.length !== actualUrlParts.length)
13
13
  return acc;
@@ -25,7 +25,7 @@ const getRestUrlSuggestions = ({ url, patternUrls }) => {
25
25
  })
26
26
  .join('/');
27
27
  const suggestionWithQueryParams = `/${urlSuggestion}${url.search}`;
28
- acc.push(suggestionWithQueryParams);
28
+ acc.push({ ...requestConfig, path: suggestionWithQueryParams });
29
29
  }
30
30
  return acc;
31
31
  }, []);
@@ -5,42 +5,48 @@ const helpers_1 = require("../../../utils/helpers");
5
5
  const helpers_2 = require("./helpers");
6
6
  const notFoundMiddleware = ({ server, mockServerConfig }) => {
7
7
  const { baseUrl: serverBaseUrl, rest, graphql } = mockServerConfig;
8
- const operationNames = graphql?.configs.map(({ operationName }) => operationName) ?? [];
9
- const graphqlPatternUrlMeaningfulStrings = Array.from(operationNames.reduce((acc, operationName) => {
10
- if (typeof operationName === 'string')
11
- acc.add(`${serverBaseUrl ?? ''}${graphql?.baseUrl ?? ''}/${operationName}`);
12
- return acc;
13
- }, new Set()));
14
- const restPaths = rest?.configs.map(({ path }) => path) ?? [];
15
- const patternUrls = Array.from(restPaths.reduce((acc, patternPath) => {
16
- if (typeof patternPath === 'string')
17
- acc.add(`${serverBaseUrl ?? ''}${rest?.baseUrl ?? ''}${patternPath}`);
18
- return acc;
19
- }, new Set()));
8
+ const restRequestConfigs = rest?.configs
9
+ .filter(({ path }) => !(path instanceof RegExp))
10
+ .map((request) => ({
11
+ method: request.method,
12
+ path: `${serverBaseUrl ?? ''}${rest?.baseUrl ?? ''}${request.path}`
13
+ })) ?? [];
14
+ const graphqlRequestConfigs = graphql?.configs
15
+ .filter(({ operationName }) => !(operationName instanceof RegExp))
16
+ .map((request) => ({
17
+ operationType: request.operationType,
18
+ operationName: `${serverBaseUrl ?? ''}${graphql?.baseUrl ?? ''} ${request.operationName}`
19
+ })) ?? [];
20
20
  server.use((request, response) => {
21
21
  const url = new URL(`${request.protocol}://${request.get('host')}${request.originalUrl}`);
22
- let graphqlUrlSuggestions = [];
23
- if (graphql) {
24
- const graphqlQuery = (0, helpers_1.parseGraphQLRequest)(request);
25
- if (graphqlQuery) {
26
- graphqlUrlSuggestions = (0, helpers_2.getGraphqlUrlSuggestions)({
27
- url,
28
- graphqlPatternUrlMeaningfulStrings
29
- });
30
- }
31
- }
32
- let restUrlSuggestions = [];
22
+ let restRequestSuggestions = [];
33
23
  if (rest) {
34
- restUrlSuggestions = (0, helpers_2.getRestUrlSuggestions)({
24
+ restRequestSuggestions = (0, helpers_2.getRestUrlSuggestions)({
25
+ url,
26
+ requestConfigs: restRequestConfigs
27
+ });
28
+ }
29
+ let graphqlRequestSuggestions = [];
30
+ if (graphql && (0, helpers_1.parseGraphQLRequest)(request)) {
31
+ graphqlRequestSuggestions = (0, helpers_2.getGraphqlUrlSuggestions)({
35
32
  url,
36
- patternUrls
33
+ requestConfigs: graphqlRequestConfigs
34
+ });
35
+ }
36
+ const isRequestSupportHtml = request.headers.accept?.includes('text/html') || request.headers.accept?.includes('*/*');
37
+ if (isRequestSupportHtml) {
38
+ response.status(404).render('pages/404', {
39
+ restRequestSuggestions,
40
+ graphqlRequestSuggestions
37
41
  });
42
+ return;
38
43
  }
39
- response.status(404).render('notFound', {
40
- requestMethod: request.method,
41
- url: `${url.pathname}${url.search}`,
42
- restUrlSuggestions,
43
- graphqlUrlSuggestions
44
+ response.status(404).json({
45
+ message: 'Request or page not found. Similar requests in data',
46
+ data: {
47
+ restRequestSuggestions,
48
+ graphqlRequestSuggestions
49
+ }
44
50
  });
45
51
  });
46
52
  };
@@ -13,8 +13,9 @@ const helpers_1 = require("../../utils/helpers");
13
13
  const createMockServer = (mockServerConfig) => {
14
14
  const { cors, staticPath, rest, graphql, interceptors } = mockServerConfig;
15
15
  const server = (0, express_1.default)();
16
- server.set('views', (0, helpers_1.urlJoin)(__dirname, '../../static/views'));
17
16
  server.set('view engine', 'ejs');
17
+ server.set('views', (0, helpers_1.urlJoin)(__dirname, '../../static/views'));
18
+ server.use(express_1.default.static((0, helpers_1.urlJoin)(__dirname, '../../static/views')));
18
19
  server.use(body_parser_1.default.urlencoded({ extended: false }));
19
20
  server.use(body_parser_1.default.json({ limit: '10mb' }));
20
21
  (0, middlewares_1.cookieParseMiddleware)(server);
@@ -0,0 +1,3 @@
1
+ <svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M11 8C12.65 8 14 9.35 14 11C14 12.65 12.65 14 11 14C9.35 14 8 12.65 8 11C8 9.35 9.35 8 11 8ZM11 6C8.24 6 6 8.24 6 11C6 13.76 8.24 16 11 16C13.76 16 16 13.76 16 11C16 8.24 13.76 6 11 6ZM1 12H3C3.55 12 4 11.55 4 11C4 10.45 3.55 10 3 10H1C0.45 10 0 10.45 0 11C0 11.55 0.45 12 1 12ZM19 12H21C21.55 12 22 11.55 22 11C22 10.45 21.55 10 21 10H19C18.45 10 18 10.45 18 11C18 11.55 18.45 12 19 12ZM10 1V3C10 3.55 10.45 4 11 4C11.55 4 12 3.55 12 3V1C12 0.45 11.55 0 11 0C10.45 0 10 0.45 10 1ZM10 19V21C10 21.55 10.45 22 11 22C11.55 22 12 21.55 12 21V19C12 18.45 11.55 18 11 18C10.45 18 10 18.45 10 19ZM4.99 3.58C4.6 3.19 3.96 3.19 3.58 3.58C3.19 3.97 3.19 4.61 3.58 4.99L4.64 6.05C5.03 6.44 5.67 6.44 6.05 6.05C6.43 5.66 6.44 5.02 6.05 4.64L4.99 3.58ZM17.36 15.95C16.97 15.56 16.33 15.56 15.95 15.95C15.56 16.34 15.56 16.98 15.95 17.36L17.01 18.42C17.4 18.81 18.04 18.81 18.42 18.42C18.81 18.03 18.81 17.39 18.42 17.01L17.36 15.95ZM18.42 4.99C18.81 4.6 18.81 3.96 18.42 3.58C18.03 3.19 17.39 3.19 17.01 3.58L15.95 4.64C15.56 5.03 15.56 5.67 15.95 6.05C16.34 6.43 16.98 6.44 17.36 6.05L18.42 4.99ZM6.05 17.36C6.44 16.97 6.44 16.33 6.05 15.95C5.66 15.56 5.02 15.56 4.64 15.95L3.58 17.01C3.19 17.4 3.19 18.04 3.58 18.42C3.97 18.8 4.61 18.81 4.99 18.42L6.05 17.36Z" fill="white"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="18" height="18" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill="#343434" fill-opacity="0.9" d="M6.36997 2.51C6.18997 3.15 6.09997 3.82 6.09997 4.5C6.09997 8.58 9.41997 11.9 13.5 11.9C14.18 11.9 14.85 11.81 15.49 11.63C14.45 14.19 11.93 16 8.99997 16C5.13997 16 1.99997 12.86 1.99997 9C1.99997 6.07 3.80997 3.55 6.36997 2.51ZM8.99997 0C4.02997 0 -3.05176e-05 4.03 -3.05176e-05 9C-3.05176e-05 13.97 4.02997 18 8.99997 18C13.97 18 18 13.97 18 9C18 8.54 17.96 8.08 17.9 7.64C16.92 9.01 15.32 9.9 13.5 9.9C10.52 9.9 8.09997 7.48 8.09997 4.5C8.09997 2.69 8.98997 1.08 10.36 0.0999999C9.91997 0.0399999 9.45997 0 8.99997 0Z"/>
3
+ </svg>
@@ -0,0 +1,88 @@
1
+ :root {
2
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
3
+ 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
4
+ -webkit-font-smoothing: antialiased;
5
+ -moz-osx-font-smoothing: grayscale;
6
+ }
7
+
8
+ body {
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: center;
12
+ margin: 0;
13
+ background-color: var(--background-body);
14
+ color: var(--primary);
15
+ }
16
+
17
+ svg {
18
+ color: var(--primary-glass);
19
+ }
20
+
21
+ a {
22
+ color: var(--primary);
23
+ text-decoration-line: none;
24
+ }
25
+
26
+ .link {
27
+ color: var(--primary);
28
+ font-size: 14px;
29
+ font-weight: 500;
30
+ line-height: 17px;
31
+ text-decoration-line: underline;
32
+ }
33
+
34
+ .title {
35
+ display: flex;
36
+ height: 50px;
37
+ flex-direction: row;
38
+ align-items: center;
39
+ font-size: 20px;
40
+ font-weight: 500;
41
+ gap: 10px;
42
+ line-height: 24px;
43
+ }
44
+
45
+ .title img {
46
+ height: 50px;
47
+ }
48
+
49
+ .description {
50
+ display: flex;
51
+ flex-direction: column;
52
+ align-items: flex-start;
53
+ font-size: 14px;
54
+ font-weight: 200;
55
+ gap: 10px;
56
+ line-height: 17px;
57
+ }
58
+
59
+ .margin-container {
60
+ display: flex;
61
+ width: 100%;
62
+ justify-content: center;
63
+ }
64
+
65
+ .hero {
66
+ display: flex;
67
+ width: 800px;
68
+ flex-direction: column;
69
+ margin-top: 100px;
70
+ margin-bottom: 100px;
71
+ gap: 10px;
72
+ padding-inline: 75px;
73
+ }
74
+
75
+ .content {
76
+ display: flex;
77
+ width: 800px;
78
+ flex-direction: column;
79
+ gap: 20px;
80
+ padding-inline: 75px;
81
+ }
82
+
83
+ .content-head {
84
+ display: flex;
85
+ flex-direction: column;
86
+ align-items: center;
87
+ gap: 10px;
88
+ }
@@ -0,0 +1,55 @@
1
+ .header {
2
+ display: flex;
3
+ width: 100%;
4
+ justify-content: center;
5
+ backdrop-filter: blur(20px);
6
+ background-color: var(--background-container);
7
+ }
8
+
9
+ .header-content {
10
+ display: flex;
11
+ width: 800px;
12
+ height: 60px;
13
+ flex-direction: row;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+ gap: 25px;
17
+ padding-inline: 75px;
18
+ }
19
+
20
+ .header-menu {
21
+ display: flex;
22
+ flex-direction: row;
23
+ gap: 25px;
24
+ }
25
+
26
+ .header-menu-item {
27
+ display: flex;
28
+ height: 25px;
29
+ flex-direction: row;
30
+ align-items: center;
31
+ justify-content: center;
32
+ cursor: pointer;
33
+ font-size: 14px;
34
+ font-weight: 400;
35
+ gap: 10px;
36
+ line-height: 17px;
37
+ }
38
+
39
+ .logo img {
40
+ height: 30px;
41
+ margin-right: 5px;
42
+ }
43
+
44
+ li {
45
+ list-style-type: none;
46
+ }
47
+
48
+ .scheme-switcher {
49
+ width: 22px;
50
+ height: 22px;
51
+ background-image: var(--image-scheme);
52
+ background-position: center;
53
+ background-repeat: no-repeat;
54
+ cursor: pointer;
55
+ }
@@ -0,0 +1,40 @@
1
+ <link rel="stylesheet" href="/components/header/index.css" />
2
+ <script defer src="/components/header/index.js"></script>
3
+
4
+ <header class="header">
5
+ <div class="header-content">
6
+ <a href="/">
7
+ <div class="header-menu-item logo">
8
+ <img src="/assets/images/logo.png" />
9
+ <svg width="41" height="9" fill="none" xmlns="http://www.w3.org/2000/svg">
10
+ <path d="M12.04 8.5C11.9067 8.5 11.7867 8.45333 11.68 8.36C11.5867 8.25333 11.54 8.13333 11.54 8V3.8C11.54 3.01333 11.38 2.46 11.06 2.14C10.7533 1.82 10.3533 1.66 9.86 1.66C9.36667 1.66 8.88667 1.8 8.42 2.08C7.95333 2.36 7.52667 2.71333 7.14 3.14V8C7.14 8.13333 7.09333 8.25333 7 8.36C6.90667 8.45333 6.78667 8.5 6.64 8.5C6.50667 8.5 6.38667 8.45333 6.28 8.36C6.18667 8.25333 6.14 8.13333 6.14 8V3.8C6.14 3.01333 5.98 2.46 5.66 2.14C5.35333 1.82 4.95333 1.66 4.46 1.66C3.96667 1.66 3.48 1.81333 3 2.12C2.52 2.41333 2.08667 2.77333 1.7 3.2V8C1.7 8.13333 1.65333 8.25333 1.56 8.36C1.46667 8.45333 1.34667 8.5 1.2 8.5C1.06667 8.5 0.946667 8.45333 0.84 8.36C0.746667 8.25333 0.7 8.13333 0.7 8V1.16C0.7 1.01333 0.746667 0.893333 0.84 0.8C0.946667 0.706666 1.06667 0.66 1.2 0.66C1.34667 0.66 1.46667 0.706666 1.56 0.8C1.65333 0.893333 1.7 1.01333 1.7 1.16V1.84C2.12667 1.49333 2.57333 1.21333 3.04 0.999999C3.50667 0.773333 3.98 0.66 4.46 0.66C4.98 0.66 5.45333 0.753333 5.88 0.94C6.30667 1.12667 6.63333 1.49333 6.86 2.04C7.31333 1.62667 7.79333 1.29333 8.3 1.04C8.82 0.786666 9.34 0.66 9.86 0.66C10.3533 0.66 10.8 0.746666 11.2 0.92C11.6 1.09333 11.92 1.40667 12.16 1.86C12.4133 2.3 12.54 2.94667 12.54 3.8V8C12.54 8.13333 12.4933 8.25333 12.4 8.36C12.3067 8.45333 12.1867 8.5 12.04 8.5ZM18.9791 0.579999C19.7924 0.579999 20.4924 0.766666 21.0791 1.14C21.6791 1.5 22.1391 1.99333 22.4591 2.62C22.7924 3.23333 22.9591 3.92 22.9591 4.68C22.9591 5.41333 22.7924 6.07333 22.4591 6.66C22.1391 7.24667 21.6791 7.71333 21.0791 8.06C20.4924 8.40667 19.7924 8.58 18.9791 8.58C18.1791 8.58 17.4791 8.40667 16.8791 8.06C16.2791 7.71333 15.8124 7.24 15.4791 6.64C15.1591 6.04 14.9991 5.37333 14.9991 4.64C14.9991 3.89333 15.1591 3.21333 15.4791 2.6C15.7991 1.98667 16.2591 1.5 16.8591 1.14C17.4591 0.766666 18.1657 0.579999 18.9791 0.579999ZM18.9791 1.58C18.3657 1.58 17.8324 1.72 17.3791 2C16.9391 2.28 16.5991 2.65333 16.3591 3.12C16.1191 3.57333 15.9991 4.08 15.9991 4.64C15.9991 5.46667 16.2657 6.16667 16.7991 6.74C17.3457 7.3 18.0724 7.58 18.9791 7.58C19.8857 7.58 20.6057 7.3 21.1391 6.74C21.6857 6.18 21.9591 5.49333 21.9591 4.68C21.9591 4.10667 21.8391 3.58667 21.5991 3.12C21.3591 2.65333 21.0124 2.28 20.5591 2C20.1191 1.72 19.5924 1.58 18.9791 1.58ZM30.6391 1.88C30.1591 1.73333 29.6924 1.66 29.2391 1.66C28.2257 1.66 27.4324 1.95333 26.8591 2.54C26.2991 3.11333 26.0191 3.85333 26.0191 4.76C26.0191 5.54667 26.2991 6.2 26.8591 6.72C27.4191 7.24 28.2057 7.5 29.2191 7.5C29.5924 7.5 29.9191 7.47333 30.1991 7.42C30.4791 7.35333 30.6991 7.29333 30.8591 7.24C31.0057 7.18667 31.0991 7.15333 31.1391 7.14C31.1924 7.12667 31.2324 7.12 31.2591 7.12C31.3924 7.12 31.5057 7.17333 31.5991 7.28C31.7057 7.37333 31.7591 7.48667 31.7591 7.62C31.7591 7.84667 31.6524 8 31.4391 8.08C31.1457 8.2 30.8124 8.3 30.4391 8.38C30.0791 8.46 29.6724 8.5 29.2191 8.5C28.3124 8.5 27.5457 8.33333 26.9191 8C26.2924 7.65333 25.8191 7.2 25.4991 6.64C25.1791 6.06667 25.0191 5.44 25.0191 4.76C25.0191 3.97333 25.1857 3.27333 25.5191 2.66C25.8524 2.03333 26.3324 1.54667 26.9591 1.2C27.5857 0.84 28.3457 0.66 29.2391 0.66C29.9191 0.66 30.5657 0.766666 31.1791 0.98C31.4057 1.06 31.5191 1.22667 31.5191 1.48C31.5191 1.6 31.4657 1.71333 31.3591 1.82C31.2524 1.91333 31.1324 1.96 30.9991 1.96C30.9591 1.96 30.8391 1.93333 30.6391 1.88ZM39.05 1.82C38.9167 1.78 38.6833 1.73333 38.35 1.68C38.03 1.62667 37.6767 1.6 37.29 1.6C36.6367 1.6 36.13 1.71333 35.77 1.94C35.41 2.16667 35.23 2.44667 35.23 2.78C35.23 3.07333 35.3767 3.31333 35.67 3.5C35.9767 3.67333 36.4767 3.79333 37.17 3.86L37.97 3.94C38.65 4.00667 39.1833 4.16 39.57 4.4C39.9567 4.64 40.23 4.92667 40.39 5.26C40.55 5.58 40.63 5.92 40.63 6.28C40.63 6.64 40.5367 7 40.35 7.36C40.1633 7.70667 39.8367 8 39.37 8.24C38.9167 8.46667 38.2633 8.58 37.41 8.58C36.9167 8.58 36.41 8.53333 35.89 8.44C35.37 8.33333 34.8767 8.18 34.41 7.98C34.21 7.88667 34.11 7.72667 34.11 7.5C34.11 7.38 34.1633 7.27333 34.27 7.18C34.3767 7.07333 34.4967 7.02 34.63 7.02C34.6567 7.02 34.7033 7.03333 34.77 7.06C34.8367 7.08667 34.9567 7.13333 35.13 7.2C35.3833 7.29333 35.7167 7.38 36.13 7.46C36.5433 7.54 36.97 7.58 37.41 7.58C38.13 7.58 38.6767 7.46 39.05 7.22C39.4367 6.96667 39.63 6.65333 39.63 6.28C39.63 5.93333 39.4967 5.64 39.23 5.4C38.9633 5.14667 38.5167 4.99333 37.89 4.94L37.09 4.86C36.3033 4.78 35.7033 4.63333 35.29 4.42C34.8767 4.19333 34.5967 3.93333 34.45 3.64C34.3033 3.34667 34.23 3.05333 34.23 2.76C34.23 2.42667 34.3167 2.1 34.49 1.78C34.6767 1.44667 34.99 1.16667 35.43 0.94C35.8833 0.713333 36.5033 0.6 37.29 0.6C37.7433 0.6 38.1633 0.633333 38.55 0.7C38.95 0.753333 39.3033 0.826667 39.61 0.92C39.7167 0.933333 39.8033 0.993333 39.87 1.1C39.9367 1.19333 39.97 1.29333 39.97 1.4C39.97 1.54667 39.9167 1.66667 39.81 1.76C39.7167 1.85333 39.6033 1.9 39.47 1.9C39.39 1.9 39.25 1.87333 39.05 1.82Z" fill="currentColor" />
11
+ </svg>
12
+ </div>
13
+ </a>
14
+ <ul class="header-menu">
15
+ <li>
16
+ <a href="https://github.com/siberiacancode/mock-config-server" rel="noopener noreferrer" target="_blank">
17
+ <div class="header-menu-item">
18
+ <svg width="21" height="20" fill="none" xmlns="http://www.w3.org/2000/svg">
19
+ <path d="M10.5613 0C5.03628 0 0.561279 4.59118 0.561279 10.2539C0.561279 14.7853 3.42628 18.6279 7.39878 19.9823C7.89878 20.0788 8.08211 19.7618 8.08211 19.4892C8.08211 19.2457 8.07378 18.6006 8.06961 17.7461C5.28795 18.3647 4.70128 16.3703 4.70128 16.3703C4.24628 15.1869 3.58878 14.8707 3.58878 14.8707C2.68295 14.235 3.65878 14.2478 3.65878 14.2478C4.66295 14.3196 5.19045 15.3039 5.19045 15.3039C6.08211 16.8719 7.53128 16.4191 8.10295 16.1567C8.19295 15.4936 8.45045 15.0416 8.73628 14.7853C6.51545 14.5289 4.18128 13.6471 4.18128 9.71813C4.18128 8.59875 4.56878 7.68444 5.21045 6.96667C5.09795 6.70776 4.76045 5.66528 5.29795 4.2528C5.29795 4.2528 6.13545 3.97766 8.04795 5.30383C8.84795 5.07568 9.69795 4.96289 10.5479 4.95776C11.3979 4.96289 12.2479 5.07568 13.0479 5.30383C14.9479 3.97766 15.7854 4.2528 15.7854 4.2528C16.3229 5.66528 15.9854 6.70776 15.8854 6.96667C16.5229 7.68444 16.9104 8.59875 16.9104 9.71813C16.9104 13.6573 14.5729 14.5246 12.3479 14.7767C12.6979 15.0843 13.0229 15.7132 13.0229 16.6737C13.0229 18.046 13.0104 19.1483 13.0104 19.4816C13.0104 19.7507 13.1854 20.0711 13.6979 19.9686C17.6988 18.6236 20.5613 14.7784 20.5613 10.2539C20.5613 4.59118 16.0838 0 10.5613 0Z" fill="currentColor" />
20
+ </svg>
21
+ github
22
+ </div>
23
+ </a>
24
+ </li>
25
+ <li>
26
+ <a href="https://github.com/siberiacancode/mock-config-server/issues" rel="noopener noreferrer" target="_blank">
27
+ <div class="header-menu-item">
28
+ <svg width="17" height="17" fill="none" xmlns="http://www.w3.org/2000/svg">
29
+ <path d="M14.9613 0.5H2.16128C1.28128 0.5 0.569279 1.22 0.569279 2.1L0.561279 16.5L3.76128 13.3H14.9613C15.8413 13.3 16.5613 12.58 16.5613 11.7V2.1C16.5613 1.22 15.8413 0.5 14.9613 0.5ZM6.16128 7.7H4.56128V6.1H6.16128V7.7ZM9.36128 7.7H7.76128V6.1H9.36128V7.7ZM12.5613 7.7H10.9613V6.1H12.5613V7.7Z" fill="currentColor" />
30
+ </svg>
31
+ issues
32
+ </div>
33
+ </a>
34
+ </li>
35
+ <li>
36
+ <div id="scheme-switcher" class="scheme-switcher"></div>
37
+ </li>
38
+ </ul>
39
+ </div>
40
+ </header>
@@ -0,0 +1 @@
1
+ document.getElementById('scheme-switcher').addEventListener('click', () => window.switchScheme());
@@ -0,0 +1,12 @@
1
+ :root {
2
+ color-scheme: dark;
3
+ --accent: #6972ff;
4
+ --accent-glass: rgb(105 114 255 / 50%);
5
+ --primary: #fff;
6
+ --primary-glass: rgba(255 255 255 / 70%);
7
+ --shadow: 0px 4px 10px rgba(0 0 0 / 25%);
8
+ --background-body: #363942;
9
+ --background-container: rgba(0 0 0 / 15%);
10
+ --background-card: rgba(255 255 255 / 5%);
11
+ --image-scheme: url('/assets/icons/scheme-dark.svg');
12
+ }
@@ -0,0 +1,3 @@
1
+ <link rel="stylesheet" href="/features/scheme/light.css" media="(prefers-color-scheme: light)" />
2
+ <link rel="stylesheet" href="/features/scheme/dark.css" media="(prefers-color-scheme: dark)" />
3
+ <script defer src="/features/scheme/index.js"></script>
@@ -0,0 +1,31 @@
1
+ const lightStyles = document.querySelector(
2
+ 'link[rel=stylesheet][media*=prefers-color-scheme][media*=light]'
3
+ );
4
+ const darkStyles = document.querySelector(
5
+ 'link[rel=stylesheet][media*=prefers-color-scheme][media*=dark]'
6
+ );
7
+
8
+ function getSavedScheme() {
9
+ return localStorage.getItem('color-scheme');
10
+ }
11
+
12
+ function setScheme(scheme) {
13
+ const lightMedia = scheme === 'light' ? 'all' : 'not all';
14
+ const darkMedia = scheme === 'dark' ? 'all' : 'not all';
15
+
16
+ lightStyles.media = lightMedia;
17
+ darkStyles.media = darkMedia;
18
+
19
+ localStorage.setItem('color-scheme', scheme);
20
+ }
21
+
22
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
+ function switchScheme() {
24
+ setScheme(getSavedScheme() === 'light' ? 'dark' : 'light');
25
+ }
26
+
27
+ function initScheme() {
28
+ setScheme(getSavedScheme() ?? 'dark');
29
+ }
30
+
31
+ initScheme();
@@ -0,0 +1,12 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --accent: rgb(149 155 255);
4
+ --accent-glass: rgb(149 155 255 / 50%);
5
+ --primary: #343434;
6
+ --primary-glass: rgba(52 52 52 / 70%);
7
+ --shadow: 0px 4px 10px rgba(0 0 0 / 15%);
8
+ --background-body: #e4e5ef;
9
+ --background-container: rgba(255 255 255 / 75%);
10
+ --background-card: rgba(255 255 255 / 75%);
11
+ --image-scheme: url('/assets/icons/scheme-light.svg');
12
+ }
@@ -0,0 +1,30 @@
1
+ .tab-items {
2
+ display: flex;
3
+ flex-direction: row;
4
+ align-items: center;
5
+ padding: 3px;
6
+ border-radius: 20px;
7
+ background: var(--background-container);
8
+ font-size: 14px;
9
+ font-weight: 300;
10
+ gap: 4px;
11
+ line-height: 17px;
12
+ }
13
+
14
+ .tab-item {
15
+ display: flex;
16
+ width: 120px;
17
+ height: 30px;
18
+ align-items: center;
19
+ justify-content: center;
20
+ border-radius: 20px;
21
+ cursor: pointer;
22
+ }
23
+
24
+ .tab-item:not(.tab-item-active):hover {
25
+ background-color: var(--accent-glass);
26
+ }
27
+
28
+ .tab-content {
29
+ display: none;
30
+ }
@@ -0,0 +1,2 @@
1
+ <link rel="stylesheet" href="/features/tab/index.css" />
2
+ <script defer src="/features/tab/index.js"></script>
@@ -0,0 +1,12 @@
1
+ function switchTab(activeTabId) {
2
+ document.querySelector('body').className = activeTabId;
3
+ }
4
+
5
+ function initTab() {
6
+ const tabItems = document.getElementsByClassName('tab-item');
7
+ for (let i = 0; i < tabItems.length; i += 1) {
8
+ tabItems[i].addEventListener('click', () => switchTab(tabItems[i].id));
9
+ }
10
+ }
11
+
12
+ initTab();
@@ -0,0 +1,10 @@
1
+ /* stylelint-disable */
2
+ .tab-item-REST #tab-item-REST,
3
+ .tab-item-GraphQL #tab-item-GraphQL {
4
+ background: var(--accent);
5
+ }
6
+
7
+ .tab-item-REST #tab-content-REST,
8
+ .tab-item-GraphQL #tab-content-GraphQL {
9
+ display: unset;
10
+ }
@@ -0,0 +1,85 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+
8
+ <title>404 - 🎉 Mock Config Server</title>
9
+
10
+ <link rel="stylesheet" href="/assets/styles/global.css" />
11
+ <link rel="stylesheet" href="/pages/404/index.css" />
12
+
13
+ <% const api = { rest: "REST", graphql: "GraphQL" } %>
14
+
15
+ <% const rootPath = (path) => `../../${path}` %>
16
+ <%- include(rootPath('features/scheme/index')) -%>
17
+ <%- include(rootPath('features/tab/index')) -%>
18
+ </head>
19
+
20
+ <body class="tab-item-<%= graphqlRequestSuggestions.length ? api.graphql : api.rest %>">
21
+ <%- include(rootPath('components/header/index')) -%>
22
+ <div class="margin-container">
23
+ <div class="hero">
24
+ <div class="title">
25
+ <img src="/assets/images/404.png" />
26
+ 404
27
+ </div>
28
+ <div class="description">
29
+ Such request or page not found ☹️
30
+ <a class="link" href="/">
31
+ Return on main page
32
+ </a>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <div class="margin-container">
37
+ <div class="content">
38
+ <div class="content-head">
39
+ <div class="tab-items">
40
+ <% Object.values(api).forEach((variant)=> { %>
41
+ <div id="tab-item-<%=variant%>" class="tab-item">
42
+ <%=variant%>
43
+ </div>
44
+ <% }) %>
45
+ </div>
46
+ </div>
47
+ <div id="tab-content-<%=api.rest%>" class="tab-content">
48
+ <div class="description">
49
+ <% if (restRequestSuggestions.length) { %>
50
+ <span>We searched a bit, maybe this will help you:</span>
51
+ <% restRequestSuggestions.forEach((requestSuggestion) => { %>
52
+ <span><%= requestSuggestion.method.toUpperCase() %> <%=
53
+ requestSuggestion.path %></span>
54
+ <% })} else { %>
55
+ <span>We searched, but found nothing.</span>
56
+ <span>Maybe you don't have <%=api.rest%> configs? 👀</span>
57
+ <a class="link" href="https://github.com/siberiacancode/mock-config-server#configs" rel="noopener noreferrer" target="_blank">
58
+ Read documentation 📘 for more information
59
+ </a>
60
+ <% } %>
61
+ </div>
62
+ </div>
63
+
64
+ <div id="tab-content-<%=api.graphql%>" class="tab-content">
65
+ <div class="description">
66
+ <% if (graphqlRequestSuggestions.length) { %>
67
+ <span>We searched a bit, maybe this will help you:</span>
68
+ <% graphqlRequestSuggestions.forEach((requestSuggestion) => { %>
69
+ <span><%= requestSuggestion.operationType %> <%=
70
+ requestSuggestion.operationName %></span>
71
+ <% })} else { %>
72
+ <span>We searched, but found nothing.</span>
73
+ <span>Maybe you don't have <%=api.graphql%> configs? 👀</span>
74
+ <a class="link" href="https://github.com/siberiacancode/mock-config-server#configs" rel="noopener noreferrer" target="_blank">
75
+ Read documentation 📘 for more information
76
+ </a>
77
+ <% } %>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </body>
84
+
85
+ </html>
@@ -23,8 +23,9 @@ export interface RestRouteConfig<Method extends RestMethod, Entities extends Res
23
23
  interceptors?: Pick<Interceptors, 'response'>;
24
24
  }
25
25
  export type RestMethod = 'get' | 'post' | 'delete' | 'put' | 'patch' | 'options';
26
+ export type RestPathString = `/${string}`;
26
27
  export interface BaseRestRequestConfig<Method extends RestMethod> {
27
- path: `/${string}` | RegExp;
28
+ path: RestPathString | RegExp;
28
29
  method: Method;
29
30
  routes: RestRouteConfig<Method>[];
30
31
  interceptors?: Interceptors;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mock-config-server",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Tool that easily and quickly imitates server operation, create full fake api in few steps",
5
5
  "author": {
6
6
  "name": "SIBERIA CAN CODE 🧊",
@@ -41,10 +41,12 @@
41
41
  "test": "jest",
42
42
  "type": "tsc --noEmit",
43
43
  "lint": "eslint . --ext ts --no-error-on-unmatched-pattern",
44
+ "stylelint": "stylelint \"src/static/**/*.css\"",
44
45
  "format": "prettier --write {src,bin}/**/*.ts",
45
46
  "pretty": "yarn type && yarn format && yarn lint --fix"
46
47
  },
47
48
  "lint-staged": {
49
+ "*.css": "yarn stylelint",
48
50
  "*.js": "yarn format",
49
51
  "*.ts": "yarn pretty"
50
52
  },
@@ -55,7 +57,7 @@
55
57
  "@types/yargs": "^17.0.24",
56
58
  "ansi-colors": "^4.1.3",
57
59
  "body-parser": "^1.20.0",
58
- "ejs": "^3.1.8",
60
+ "ejs": "^3.1.9",
59
61
  "esbuild": "^0.17.8",
60
62
  "express": "^4.18.1",
61
63
  "flat": "^5.0.2",
@@ -76,6 +78,11 @@
76
78
  "eslint-import-resolver-typescript": "^3.4.1",
77
79
  "eslint-plugin-import": "^2.27.5",
78
80
  "eslint-plugin-prettier": "^4.2.1",
81
+ "style-loader": "^3.3.2",
82
+ "stylelint": "^15.6.1",
83
+ "stylelint-config-prettier": "^9.0.5",
84
+ "stylelint-config-standard": "^32.0.0",
85
+ "stylelint-order": "^6.0.3",
79
86
  "husky": "^8.0.1",
80
87
  "jest": "^29.4.2",
81
88
  "lint-staged": "^13.1.1",
@@ -1,42 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <body class="container">
4
- <h1>404</h1>
5
- <div>
6
- Seems to be your config does not have data for <%= requestMethod %>
7
- <%= decodeURIComponent(url) %> request, or you have typo in it.
8
- </div>
9
-
10
- <% if (restUrlSuggestions.length || graphqlUrlSuggestions.length) { %>
11
- <h2>
12
- Maybe you are looking for one of these paths?
13
- </h2>
14
- <% } %>
15
-
16
- <% if (restUrlSuggestions.length) { %>
17
- <div>
18
- <h3>REST</h3>
19
- <ul>
20
- <% restUrlSuggestions.forEach((restUrlSuggestion) => { %>
21
- <li>
22
- <a href=<%= restUrlSuggestion %>><%= decodeURIComponent(restUrlSuggestion) %></a>
23
- </li>
24
- <% }) %>
25
- </ul>
26
- </div>
27
- <% } %>
28
-
29
- <% if (graphqlUrlSuggestions.length) { %>
30
- <div>
31
- <h3>GraphQL</h3>
32
- <ul>
33
- <% graphqlUrlSuggestions.forEach((graphqlUrlSuggestion) => { %>
34
- <li>
35
- <%= decodeURIComponent(graphqlUrlSuggestion) %>
36
- </li>
37
- <% }) %>
38
- </ul>
39
- </div>
40
- <% } %>
41
- </body>
42
- </html>