yxorp 0.1.4 → 0.2.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 (63) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +369 -81
  3. package/bin/yxorp.js +13 -22
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +59 -0
  6. package/dist/middleware/bootstrap.middleware.d.ts +15 -0
  7. package/dist/middleware/bootstrap.middleware.js +63 -0
  8. package/dist/middleware/mock.middleware.d.ts +9 -0
  9. package/dist/middleware/mock.middleware.js +54 -0
  10. package/dist/middleware/proxy.middleware.d.ts +15 -0
  11. package/dist/middleware/proxy.middleware.js +26 -0
  12. package/dist/middleware/proxyRes.middleware.d.ts +9 -0
  13. package/dist/middleware/proxyRes.middleware.js +28 -0
  14. package/dist/middleware/rawBody.middleware.d.ts +6 -0
  15. package/dist/middleware/rawBody.middleware.js +24 -0
  16. package/dist/middleware/rewrite.middleware.d.ts +9 -0
  17. package/dist/middleware/rewrite.middleware.js +54 -0
  18. package/dist/middleware/static.middleware.d.ts +12 -0
  19. package/dist/middleware/static.middleware.js +85 -0
  20. package/dist/services/config-resolver.d.ts +6 -0
  21. package/dist/services/config-resolver.js +45 -0
  22. package/dist/services/config.service.d.ts +6 -0
  23. package/dist/services/config.service.js +12 -0
  24. package/dist/services/http-proxy.service.d.ts +14 -0
  25. package/dist/services/http-proxy.service.js +19 -0
  26. package/dist/services/http-server.service.d.ts +15 -0
  27. package/dist/services/http-server.service.js +23 -0
  28. package/dist/services/logger.service.d.ts +7 -0
  29. package/dist/services/logger.service.js +17 -0
  30. package/dist/services/pipeline.service.d.ts +8 -0
  31. package/dist/services/pipeline.service.js +29 -0
  32. package/dist/services/rules-matchers/mock-rules-matcher.service.d.ts +8 -0
  33. package/dist/services/rules-matchers/mock-rules-matcher.service.js +35 -0
  34. package/dist/services/rules-matchers/remote-rules-matcher.service.d.ts +8 -0
  35. package/dist/services/rules-matchers/remote-rules-matcher.service.js +51 -0
  36. package/dist/services/rules-matchers/rewrite-rules-matcher.service.d.ts +8 -0
  37. package/dist/services/rules-matchers/rewrite-rules-matcher.service.js +35 -0
  38. package/dist/services/yxorp-server.service.d.ts +10 -0
  39. package/dist/services/yxorp-server.service.js +49 -0
  40. package/dist/types/yxorp-config.d.ts +54 -0
  41. package/dist/types/yxorp-config.js +2 -0
  42. package/package.json +45 -38
  43. package/.editorconfig +0 -16
  44. package/src/index.ts +0 -48
  45. package/src/middleware/bootstrap.middleware.ts +0 -87
  46. package/src/middleware/mock.middleware.ts +0 -61
  47. package/src/middleware/proxy.middleware.ts +0 -42
  48. package/src/middleware/proxyRes.middleware.ts +0 -34
  49. package/src/middleware/rawBody.middleware.ts +0 -22
  50. package/src/middleware/rewrite.middleware.ts +0 -64
  51. package/src/middleware/static.middleware.ts +0 -113
  52. package/src/services/config.service.ts +0 -26
  53. package/src/services/http-proxy.service.ts +0 -25
  54. package/src/services/http-server.service.ts +0 -24
  55. package/src/services/logger.service.ts +0 -21
  56. package/src/services/pipeline.service.ts +0 -44
  57. package/src/services/rules-matchers/mock-rules-matcher.service.ts +0 -48
  58. package/src/services/rules-matchers/remote-rules-matcher.service.ts +0 -49
  59. package/src/services/rules-matchers/rewrite-rules-matcher.service.ts +0 -48
  60. package/src/services/yxorp-server.service.ts +0 -73
  61. package/src/types/http.d.ts +0 -10
  62. package/src/types/yxorp-config.ts +0 -67
  63. package/tsconfig.json +0 -21
@@ -1,113 +0,0 @@
1
- import { Service } from 'typedi';
2
- import { ServerResponse, IncomingMessage } from 'http';
3
- import path from 'path';
4
- import fs from 'fs/promises';
5
- import mime from 'mime';
6
- import { Dirent } from 'fs';
7
- import { Config } from '../services/config.service';
8
- import { Middleware } from '../services/pipeline.service';
9
- import { LoggerService } from '../services/logger.service';
10
-
11
-
12
- @Service({
13
- global: true
14
- })
15
- export class StaticMiddleware implements Middleware<[req: IncomingMessage, res: ServerResponse]> {
16
- constructor(
17
- private config: Config,
18
- private logger: LoggerService,
19
- ) {
20
- }
21
-
22
- public async use(req: IncomingMessage, res: ServerResponse, next: () => void): Promise<void> {
23
- try {
24
- const staticRules = this.config.get().staticRules || [];
25
-
26
- const url = new URL(req.url || '', 'http://fake.com');
27
- const urlPath = url.pathname;
28
-
29
- if (!staticRules) {
30
- next();
31
- return;
32
- }
33
-
34
- const currentStaticRule = staticRules.filter((staticRule) => {
35
- return urlPath.startsWith(staticRule.path);
36
- })[0];
37
-
38
- if (!currentStaticRule) {
39
- next();
40
- return;
41
- }
42
-
43
- const pathToDirectory = path.resolve(currentStaticRule.directory);
44
-
45
- const dirents = await this.readdir(pathToDirectory);
46
-
47
- const filePath = dirents
48
- .filter(dirent => !dirent.isDirectory())
49
- .map(dirent => ({
50
- urlPath: path.join(
51
- currentStaticRule.path,
52
- path.join(dirent.path, dirent.name).replace(pathToDirectory, '')
53
- ).replace(/\\/g, '/'),
54
- path: path.join(dirent.path, dirent.name).replace(/\\/g, '/'),
55
- }
56
- ))
57
- .filter(dirent => {
58
- const pathname = path.extname(urlPath)
59
- ? urlPath
60
- : path.join(urlPath, currentStaticRule.directoryIndex || '').replace(/\\/g, '/');
61
-
62
- if (currentStaticRule.caseInsensitive) {
63
- return dirent.urlPath.toLocaleLowerCase() === pathname.toLowerCase();
64
- } else {
65
- return dirent.urlPath === pathname;
66
- }
67
- })
68
- .map(dirent => dirent.path)[0];
69
-
70
-
71
- if (!filePath) {
72
- next();
73
- return;
74
- }
75
-
76
- const file = await fs.readFile(path.resolve(filePath));
77
- const mimeType = mime.getType(path.resolve(filePath));
78
-
79
- if (mimeType) {
80
- res.setHeader('content-type', mimeType);
81
- }
82
-
83
- res.setHeader('content-length', file.length);
84
- res.end(file);
85
-
86
- this.logger.info(`[STATIC] ${req.url}`);
87
- } catch (e) {
88
- this.logger.error(e);
89
- next();
90
- }
91
- }
92
-
93
- private async readdir(pathname: string, subpathname?: string): Promise<Dirent[]> {
94
- const files: Dirent[] = [];
95
- const fullpath = path.join(pathname, subpathname || '');
96
-
97
- const dirents = await fs.readdir(fullpath, {
98
- withFileTypes: true,
99
- });
100
-
101
- for (let dirent of dirents) {
102
- dirent.path = fullpath;
103
- files.push(dirent);
104
-
105
- if (dirent.isDirectory()) {
106
- files.push(...await this.readdir(fullpath, dirent.name));
107
- }
108
- }
109
-
110
- return files;
111
- }
112
-
113
- }
@@ -1,26 +0,0 @@
1
- import { Service, Token, Inject } from 'typedi';
2
- import { YxorpConfig } from "../types/yxorp-config";
3
-
4
-
5
- export const ProxyConfigToken = new Token<YxorpConfig>('ProxyConfigToken');
6
-
7
- @Service({
8
- global: true
9
- })
10
- export class Config {
11
- private config!: YxorpConfig;
12
-
13
- constructor(
14
- @Inject(ProxyConfigToken) private proxyConfig: YxorpConfig,
15
- ) {
16
- this.config = this.proxyConfig;
17
- }
18
-
19
- public set(config: YxorpConfig): void {
20
- this.config = config;
21
- }
22
-
23
- public get(): YxorpConfig {
24
- return this.config;
25
- }
26
- }
@@ -1,25 +0,0 @@
1
- import { Service } from 'typedi';
2
- import { IncomingMessage, ServerResponse} from 'http';
3
- import httpProxy from 'http-proxy';
4
- import { Pipeline } from "./pipeline.service";
5
-
6
- @Service({
7
- global: true
8
- })
9
- export class HttpProxy {
10
- private httpProxy = httpProxy.createProxyServer({});
11
-
12
- constructor(
13
- private pipeline: Pipeline<[proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse]>,
14
- ) {
15
- }
16
-
17
- public use = this.pipeline.use.bind(this.pipeline);
18
- public execute = this.pipeline.execute.bind(this.pipeline);
19
- public on = this.httpProxy.on.bind(this.httpProxy);
20
- public addListener = this.httpProxy.addListener.bind(this.httpProxy);
21
- public removeListener = this.httpProxy.removeListener.bind(this.httpProxy);
22
- public web = this.httpProxy.web.bind(this.httpProxy);
23
- public ws = this.httpProxy.ws.bind(this.httpProxy);
24
-
25
- }
@@ -1,24 +0,0 @@
1
- import { Service } from 'typedi';
2
- import http, { IncomingMessage, Server, ServerResponse} from 'http';
3
- import { Pipeline } from './pipeline.service';
4
-
5
- @Service({
6
- global: true
7
- })
8
- export class HttpServer {
9
- private server: Server = http.createServer((req: IncomingMessage, res: ServerResponse) => {
10
- this.pipeline.execute(req, res);
11
- });
12
-
13
- constructor(
14
- private pipeline: Pipeline<[req: IncomingMessage, res: ServerResponse]>,
15
- ) {
16
- }
17
-
18
- public use = this.pipeline.use.bind(this.pipeline);
19
- public on = this.server.on.bind(this.server);
20
- public addListener = this.server.addListener.bind(this.server);
21
- public removeListener = this.server.removeListener.bind(this.server);
22
- public listen = this.server.listen.bind(this.server);
23
-
24
- }
@@ -1,21 +0,0 @@
1
- import { Service } from 'typedi';
2
- import { createLogger, format, transports } from 'winston';
3
-
4
-
5
- @Service({
6
- global: true
7
- })
8
- export class LoggerService {
9
- private logger = createLogger({
10
- format: format.combine(
11
- format.colorize(),
12
- format.simple(),
13
- ),
14
- transports: [new transports.Console],
15
- });
16
-
17
- public info = this.logger.info.bind(this.logger);
18
- public error = this.logger.error.bind(this.logger);
19
- public warn = this.logger.warn.bind(this.logger);
20
- public debug = this.logger.debug.bind(this.logger);
21
- }
@@ -1,44 +0,0 @@
1
- import { Container, Service } from 'typedi';
2
-
3
-
4
- export interface Middleware<T extends any[]> {
5
- use(...args: [...T, () => void]): void;
6
- [k: string]: any;
7
- }
8
-
9
- export interface MiddlewareCtor<T extends any[]> {
10
- new(...args: any): Middleware<T>;
11
- }
12
- export type Middlewares<T extends any[]> = MiddlewareCtor<T>[];
13
-
14
- @Service({
15
- transient: true,
16
- })
17
- export class Pipeline<T extends any[]> {
18
- private stack: Middlewares<T> = [];
19
-
20
- public use(...middlewares: Middlewares<T>) {
21
- this.stack.push(...middlewares);
22
- }
23
-
24
- public async execute(...args: [...T]) {
25
- let prevIndex = -1;
26
-
27
- const runner = async (index: number) => {
28
- if (index === prevIndex) {
29
- throw new Error('next() called multiple times');
30
- }
31
-
32
- prevIndex = index;
33
-
34
- const middleware = Container.get(this.stack[index]);
35
-
36
- await middleware.use(...args, () => {
37
- return runner(index + 1);
38
- });
39
- }
40
-
41
- await runner(0);
42
- }
43
-
44
- }
@@ -1,48 +0,0 @@
1
- import { Service } from 'typedi';
2
- import { Config } from '../config.service';
3
- import { match } from 'path-to-regexp';
4
- import { MockRule } from '../../types/yxorp-config';
5
-
6
-
7
- @Service({
8
- global: true
9
- })
10
- export class MockRulesMatcher {
11
- constructor(
12
- private config: Config
13
- ) {
14
- }
15
-
16
- public match(url: string, method: string): MockRule | undefined {
17
- const mockRules = this.config.get().mockRules || [];
18
-
19
- for (let mockRule of mockRules) {
20
- if (mockRule.disable) {
21
- continue;
22
- }
23
-
24
- if (mockRule.method.toLowerCase() !== method.toLowerCase()) {
25
- continue;
26
- }
27
-
28
- const matchResult = match<Record<string, string>>(mockRule.path, {
29
- decode: decodeURIComponent,
30
- })(url);
31
-
32
- if (matchResult) {
33
- return mockRule;
34
- }
35
- }
36
- }
37
-
38
- public params(url: string, mockRule: MockRule): Object | undefined {
39
- const matchResult = match(mockRule.path, {
40
- decode: decodeURIComponent,
41
- })(url);
42
-
43
- if (matchResult) {
44
- return matchResult.params;
45
- }
46
- }
47
-
48
- }
@@ -1,49 +0,0 @@
1
- import { Service } from 'typedi';
2
- import { Config } from '../config.service';
3
- import { match, compile } from 'path-to-regexp';
4
- import { RemoteRule } from '../../types/yxorp-config';
5
-
6
-
7
- @Service({
8
- global: true
9
- })
10
- export class RemoteRulesMatcher {
11
- constructor(
12
- private config: Config
13
- ) {
14
- }
15
-
16
- public match(url: string, ws: boolean = false): RemoteRule | undefined {
17
- const rules = this.config.get().remoteRules || [];
18
-
19
- for (let rule of rules) {
20
- if (rule.disable) {
21
- continue;
22
- }
23
-
24
- if (!!rule.ws !== ws) {
25
- continue;
26
- }
27
-
28
- const matchResult = match(rule.path, {
29
- decode: decodeURIComponent
30
- })(url);
31
-
32
- if (matchResult) {
33
- return rule;
34
- }
35
- }
36
- }
37
-
38
- public toPath(url: string, remoteRule: RemoteRule): string | undefined {
39
- const matchResult = match(remoteRule.path, {
40
- decode: decodeURIComponent
41
- })(url);
42
-
43
- if (matchResult) {
44
- const toPath = compile(remoteRule.target, { validate: false });
45
- return toPath(matchResult.params);
46
- }
47
- }
48
-
49
- }
@@ -1,48 +0,0 @@
1
- import { Service } from 'typedi';
2
- import { Config } from '../config.service';
3
- import { match } from 'path-to-regexp';
4
- import { RewriteRule } from '../../types/yxorp-config';
5
-
6
-
7
- @Service({
8
- global: true
9
- })
10
- export class RewriteRulesMatcher {
11
- constructor(
12
- private config: Config
13
- ) {
14
- }
15
-
16
- public match(url: string, method: string): RewriteRule | undefined {
17
- const rewriteRules = this.config.get().rewriteRules || [];
18
-
19
- for (let rewriteRule of rewriteRules) {
20
- if (rewriteRule.disable) {
21
- continue;
22
- }
23
-
24
- if (rewriteRule.method.toLowerCase() !== method.toLowerCase()) {
25
- continue;
26
- }
27
-
28
- const matchResult = match<Record<string, string>>(rewriteRule.path, {
29
- decode: decodeURIComponent,
30
- })(url);
31
-
32
- if (matchResult) {
33
- return rewriteRule;
34
- }
35
- }
36
- }
37
-
38
- public params(url: string, rewriteRule: RewriteRule): Object | undefined {
39
- const matchResult = match(rewriteRule.path, {
40
- decode: decodeURIComponent,
41
- })(url);
42
-
43
- if (matchResult) {
44
- return matchResult.params;
45
- }
46
- }
47
-
48
- }
@@ -1,73 +0,0 @@
1
- import { Service } from 'typedi';
2
- import { IncomingMessage, ServerResponse } from 'http';
3
- import { Duplex } from 'stream';
4
- import { BootstrapMiddleware } from '../middleware/bootstrap.middleware';
5
- import { ProxyMiddleware } from '../middleware/proxy.middleware';
6
- import { RawBodyMiddleware } from '../middleware/rawBody.middleware';
7
- import { ProxyResMiddleware } from '../middleware/proxyRes.middleware';
8
- import { RewriteMiddleware } from '../middleware/rewrite.middleware';
9
- import { MockMiddleware } from '../middleware/mock.middleware';
10
- import { StaticMiddleware } from '../middleware/static.middleware';
11
- import { HttpServer } from './http-server.service';
12
- import { HttpProxy } from './http-proxy.service';
13
- import { RemoteRulesMatcher } from './rules-matchers/remote-rules-matcher.service';
14
- import { Config } from './config.service';
15
- import { LoggerService } from './logger.service';
16
-
17
-
18
- @Service({
19
- global: true
20
- })
21
- export class YxorpServer {
22
- constructor(
23
- private httpServer: HttpServer,
24
- private httpProxy: HttpProxy,
25
- private remoteRulesMatcher: RemoteRulesMatcher,
26
- private config: Config,
27
- private logger: LoggerService,
28
- ) {
29
- this.httpServer.use(
30
- StaticMiddleware,
31
- BootstrapMiddleware,
32
- MockMiddleware,
33
- ProxyMiddleware
34
- );
35
- this.httpProxy.use(
36
- BootstrapMiddleware,
37
- RawBodyMiddleware,
38
- RewriteMiddleware,
39
- ProxyResMiddleware,
40
- );
41
-
42
- this.httpProxy.on('proxyRes', this.onProxyRes);
43
- this.httpServer.addListener('upgrade', this.onServerUpgrade);
44
- }
45
-
46
- private onProxyRes = (proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse) => {
47
- this.httpProxy.execute(proxyRes, req, res);
48
- }
49
-
50
- private onServerUpgrade = (req: IncomingMessage, socket: Duplex, head: Buffer): void => {
51
- const url = req.url || '';
52
-
53
- const proxyOptions = this.config.get().proxyOptions;
54
- const remoteRule = this.remoteRulesMatcher.match(url, true);
55
- const target = remoteRule
56
- ? this.remoteRulesMatcher.toPath(url, remoteRule)
57
- : undefined;
58
-
59
- const options = {
60
- ...proxyOptions,
61
- prependPath: !target,
62
- target: target || proxyOptions.target,
63
- }
64
-
65
- this.httpProxy.ws(
66
- req, socket, head, options, (error) => this.logger.info(error)
67
- );
68
- }
69
-
70
- public listen = this.httpServer.listen.bind(this.httpServer) as typeof this.httpServer.listen;
71
-
72
- }
73
-
@@ -1,10 +0,0 @@
1
- declare module 'http' {
2
- export interface IncomingMessage {
3
- rawBody?: Buffer;
4
- rewriteRule?: import("./yxorp-config").RewriteRule;
5
- rewriteRuleParams?: Object;
6
- mockRule?: import("./yxorp-config").MockRule;
7
- mockRuleParams?: Object;
8
- query?: Record<string, any>;
9
- }
10
- }
@@ -1,67 +0,0 @@
1
- import { ServerOptions } from 'http-proxy';
2
-
3
- export interface YxorpConfig extends ConfigFile {
4
- proxyOptions: ServerOptions;
5
- }
6
-
7
- export interface ConfigFile {
8
- target: string;
9
- proxyPort: string | number;
10
- scripts?: string[];
11
- remoteRules?: RemoteRule[];
12
- staticRules?: StaticRule[];
13
- mockRules?: MockRule[];
14
- rewriteRules?: RewriteRule[];
15
- }
16
-
17
- export interface RemoteRule {
18
- path: string;
19
- target: string;
20
- ws?: boolean;
21
- disable?: boolean;
22
- }
23
-
24
- export type StaticRule = {
25
- path: string;
26
- directory: string;
27
- caseInsensitive?: boolean;
28
- directoryIndex?: string;
29
- disable?: boolean;
30
- }
31
-
32
- export type MockFileRule = {
33
- method: string;
34
- path: string;
35
- file: string;
36
- statusCode?: number;
37
- disable?: boolean;
38
- }
39
-
40
- export type MockScriptRule = {
41
- method: string;
42
- path: string;
43
- script: string;
44
- disable?: boolean;
45
- }
46
-
47
- export type MockRule = MockFileRule | MockScriptRule;
48
-
49
-
50
- export type RewriteFileRule = {
51
- method: string;
52
- path: string;
53
- file: string;
54
- statusCode?: number;
55
- disable?: boolean;
56
- }
57
-
58
- export type RewriteScriptRule = {
59
- method: string;
60
- path: string;
61
- script: string;
62
- disable?: boolean;
63
- }
64
-
65
- export type RewriteRule = RewriteFileRule | RewriteScriptRule;
66
-
67
-
package/tsconfig.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- // This is an alias to @tsconfig/node16: https://github.com/tsconfig/bases
3
- "extends": "ts-node/node12/tsconfig.json",
4
- "ts-node": {
5
- // It is faster to skip typechecking.
6
- // Remove if you want ts-node to do typechecking.
7
- "transpileOnly": true,
8
- "files": true,
9
- "compilerOptions": {
10
- // compilerOptions specified here will override those declared below,
11
- // but *only* in ts-node. Useful if you want ts-node and tsc to use
12
- // different options with a single tsconfig.json.
13
- }
14
- },
15
- "compilerOptions": {
16
- // typescript options here
17
- "resolveJsonModule": true,
18
- "emitDecoratorMetadata": true,
19
- "experimentalDecorators": true
20
- },
21
- }