Browse Source

feat(ditsmod): upgrade npm packages and added bun integration. (#9213)

Костя Третяк 1 year ago
parent
commit
a3ff92743d

+ 132 - 0
frameworks/TypeScript/ditsmod/benchmark_config.json

@@ -130,6 +130,138 @@
         "display_name": "ditsmod [mysql & simplified use of di]",
         "display_name": "ditsmod [mysql & simplified use of di]",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "notes": "Simplified use of Dependency Injection (no request level injector).",
         "versus": "nodejs"
         "versus": "nodejs"
+      },
+      "bun": {
+        "dockerfile": "ditsmod-bun.dockerfile",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "None",
+        "framework": "Ditsmod",
+        "language": "TypeScript",
+        "flavor": "None",
+        "orm": "None",
+        "platform": "bun",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Ditsmod on bun",
+        "notes": "",
+        "versus": "bun"
+      },
+      "simplified-di-bun": {
+        "dockerfile": "ditsmod-bun.dockerfile",
+        "json_url": "/json2",
+        "plaintext_url": "/plaintext2",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "None",
+        "framework": "Ditsmod",
+        "language": "TypeScript",
+        "flavor": "None",
+        "orm": "None",
+        "platform": "bun",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "ditsmod on bun [simplified use of di]",
+        "notes": "Simplified use of Dependency Injection (no request level injector).",
+        "versus": "bun"
+      },
+      "postgres-bun": {
+        "dockerfile": "ditsmod-bun-postgres.dockerfile",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "cached_query_url": "/cached-queries?count=",
+        "fortune_url": "/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "Ditsmod",
+        "language": "TypeScript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "bun",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "ditsmod on bun [postgres]",
+        "notes": "",
+        "versus": "bun"
+      },
+      "mysql-bun": {
+        "dockerfile": "ditsmod-bun-mysql.dockerfile",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "cached_query_url": "/cached-queries?count=",
+        "fortune_url": "/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "Ditsmod",
+        "language": "TypeScript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "bun",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "ditsmod on bun [mysql]",
+        "notes": "",
+        "versus": "bun"
+      },
+      "postgres2-bun": {
+        "dockerfile": "ditsmod-bun-postgres.dockerfile",
+        "db_url": "/db2",
+        "query_url": "/queries2?queries=",
+        "update_url": "/updates2?queries=",
+        "cached_query_url": "/cached-queries2?count=",
+        "fortune_url": "/fortunes2",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "Ditsmod",
+        "language": "TypeScript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "bun",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "ditsmod on bun [postgres & simplified use of di]",
+        "notes": "Simplified use of Dependency Injection (no request level injector).",
+        "versus": "bun"
+      },
+      "mysql2-bun": {
+        "dockerfile": "ditsmod-bun-mysql.dockerfile",
+        "db_url": "/db2",
+        "query_url": "/queries2?queries=",
+        "update_url": "/updates2?queries=",
+        "cached_query_url": "/cached-queries2?count=",
+        "fortune_url": "/fortunes2",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "Ditsmod",
+        "language": "TypeScript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "bun",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "ditsmod on bun [mysql & simplified use of di]",
+        "notes": "Simplified use of Dependency Injection (no request level injector).",
+        "versus": "bun"
       }
       }
     }
     }
   ]
   ]

BIN
frameworks/TypeScript/ditsmod/bun.lockb


+ 17 - 0
frameworks/TypeScript/ditsmod/ditsmod-bun-mysql.dockerfile

@@ -0,0 +1,17 @@
+FROM oven/bun:1.1
+
+COPY ./ ./
+
+RUN bun install
+RUN bun run build
+
+ENV NODE_ENV production
+ENV IS_BUN true
+ENV DATABASE mysql
+ENV MYSQL_HOST tfb-database
+ENV MYSQL_USER benchmarkdbuser
+ENV MYSQL_PSWD benchmarkdbpass
+ENV MYSQL_DBNAME hello_world
+
+EXPOSE 8080
+CMD rm node_modules/@ditsmod/*/tsconfig.json && bun src/app/bun-integration/spawn.ts

+ 17 - 0
frameworks/TypeScript/ditsmod/ditsmod-bun-postgres.dockerfile

@@ -0,0 +1,17 @@
+FROM oven/bun:1.1
+
+COPY ./ ./
+
+RUN bun install
+RUN bun run build
+
+ENV NODE_ENV production
+ENV IS_BUN true
+ENV DATABASE postgres
+ENV PG_HOST tfb-database
+ENV PG_USER benchmarkdbuser
+ENV PG_PSWD benchmarkdbpass
+ENV PG_DBNAME hello_world
+
+EXPOSE 8080
+CMD rm node_modules/@ditsmod/*/tsconfig.json && bun src/app/bun-integration/spawn.ts

+ 12 - 0
frameworks/TypeScript/ditsmod/ditsmod-bun.dockerfile

@@ -0,0 +1,12 @@
+FROM oven/bun:1.1
+
+COPY ./ ./
+
+RUN bun install
+RUN bun run build
+
+ENV NODE_ENV production
+ENV IS_BUN true
+
+EXPOSE 8080
+CMD rm node_modules/@ditsmod/*/tsconfig.json && bun src/app/bun-integration/spawn.ts

+ 1 - 1
frameworks/TypeScript/ditsmod/ditsmod-mysql.dockerfile

@@ -1,4 +1,4 @@
-FROM node:18.17.1-slim
+FROM node:20.16-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/TypeScript/ditsmod/ditsmod-postgres.dockerfile

@@ -1,4 +1,4 @@
-FROM node:18.17.1-slim
+FROM node:20.16-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 1 - 1
frameworks/TypeScript/ditsmod/ditsmod.dockerfile

@@ -1,4 +1,4 @@
-FROM node:18.17.1-slim
+FROM node:20.16-slim
 
 
 COPY ./ ./
 COPY ./ ./
 
 

+ 6 - 5
frameworks/TypeScript/ditsmod/package.json

@@ -19,18 +19,19 @@
   "author": "Костя Третяк",
   "author": "Костя Третяк",
   "license": "MIT",
   "license": "MIT",
   "dependencies": {
   "dependencies": {
-    "@ditsmod/core": "~2.51.1",
-    "@ditsmod/routing": "~2.1.0",
+    "@ditsmod/core": "~2.54.2",
+    "@ditsmod/routing": "~2.3.0",
     "handlebars": "^4.7.8",
     "handlebars": "^4.7.8",
-    "lru-cache": "^10.0.1",
-    "mariadb": "^3.2.1",
-    "postgres": "^3.3.5"
+    "lru-cache": "^11.0.0",
+    "mariadb": "^3.3.1",
+    "postgres": "^3.4.4"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@types/eslint": "^8.44.2",
     "@types/eslint": "^8.44.2",
     "@types/node": "^20.5.7",
     "@types/node": "^20.5.7",
     "@typescript-eslint/eslint-plugin": "^6.5.0",
     "@typescript-eslint/eslint-plugin": "^6.5.0",
     "@typescript-eslint/parser": "^6.5.0",
     "@typescript-eslint/parser": "^6.5.0",
+    "bun-types": "^1.1.22",
     "eslint": "^8.48.0",
     "eslint": "^8.48.0",
     "prettier": "^3.0.2",
     "prettier": "^3.0.2",
     "typescript": "^5.2.2"
     "typescript": "^5.2.2"

+ 7 - 2
frameworks/TypeScript/ditsmod/src/app/app.module.ts

@@ -1,8 +1,13 @@
-import { Providers, rootModule } from '@ditsmod/core';
+import { PreRouter, rootModule } from '@ditsmod/core';
+
 import { SimpleModule } from '#routed/simple/simple.module.js';
 import { SimpleModule } from '#routed/simple/simple.module.js';
+import { BunPreRouter } from './bun-integration/pre-router.js';
+import { BunProviders } from './bun-integration/bun-providers.js';
 
 
 @rootModule({
 @rootModule({
   appends: [SimpleModule],
   appends: [SimpleModule],
-  providersPerApp: [...new Providers().useLogConfig({ level: 'off' })],
+  providersPerApp: [
+    ...new BunProviders().useLogConfig({ level: 'off' }).if(process.env.IS_BUN).useClass(PreRouter, BunPreRouter),
+  ],
 })
 })
 export class AppModule {}
 export class AppModule {}

+ 17 - 0
frameworks/TypeScript/ditsmod/src/app/bun-integration/bun-application.ts

@@ -0,0 +1,17 @@
+import { AnyFn, AppInitializer, Application } from '@ditsmod/core';
+import { Serve, Server } from 'bun';
+
+export class BunApplication extends Application {
+  protected override createServer(requestListener: any): any {
+    const serveOptions = this.appOptions.serverOptions as Serve;
+    serveOptions.fetch ??= (req) => requestListener(req);
+    return Bun.serve(serveOptions);
+  }
+
+  protected override async createServerAndBindToListening(appInitializer: AppInitializer, resolve: AnyFn) {
+    this.flushLogs();
+    const server = (await this.createServer(appInitializer.requestListener)) as Server;
+    this.systemLogMediator.serverListen(this, server.hostname, server.port);
+    resolve({ server });
+  }
+}

+ 21 - 0
frameworks/TypeScript/ditsmod/src/app/bun-integration/bun-providers.ts

@@ -0,0 +1,21 @@
+import { Providers, Class } from '@ditsmod/core';
+
+export class BunProviders extends Providers {
+  protected setCondition?: boolean;
+  protected ifCondition?: boolean;
+
+  if(condition: any) {
+    this.setCondition = true;
+    this.ifCondition = condition;
+    return this;
+  }
+
+  override useClass<A extends Class, B extends A>(token: A, useClass: B, multi?: boolean): this {
+    if (!this.setCondition || this.ifCondition) {
+      this.pushProvider({ token, useClass }, multi);
+    }
+    this.setCondition = undefined;
+    this.ifCondition = undefined;
+    return this;
+  }
+}

+ 61 - 0
frameworks/TypeScript/ditsmod/src/app/bun-integration/node-res.ts

@@ -0,0 +1,61 @@
+import { Writable } from 'node:stream';
+
+export class NodeRes extends Writable {
+  #chunks: Buffer[] = [];
+  #resolve: (body: string) => void;
+  status: number = 200;
+  headers = {} as HeadersInit;
+  body = new Promise<any>((resolve) => (this.#resolve = resolve));
+  headersSent?: boolean;
+  statusText?: string;
+
+  set statusCode(statusCode: number) {
+    this.status = statusCode;
+  }
+
+  getHeader(name: string) {
+    return this.headers[name as keyof HeadersInit];
+  }
+
+  getHeaders() {
+    return this.headers;
+  }
+
+  setHeader(name: string, value: number | string | readonly string[]) {
+    this.headers = { ...this.headers, [name]: value } as HeadersInit;
+    return this;
+  }
+
+  writeHead(statusCode: number, headers?: HeadersInit): this;
+  writeHead(statusCode: number, statusMessage: string, headers?: HeadersInit): this;
+  writeHead(statusCode: number, statusMsgOrHeaders?: string | HeadersInit, headers?: HeadersInit): this {
+    this.status = statusCode;
+    if (typeof statusMsgOrHeaders == 'object') {
+      this.mergeHeaders(statusMsgOrHeaders);
+    } else {
+      this.statusText = statusMsgOrHeaders;
+      this.mergeHeaders(headers);
+    }
+    return this;
+  }
+
+  override _write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
+    this.#chunks.push(Buffer.from(chunk));
+    callback();
+  }
+
+  override _final(callback: (error?: Error | null) => void): void {
+    const finalData = Buffer.concat(this.#chunks);
+    this.headersSent = true;
+    this.#resolve(finalData.toString());
+    callback();
+  }
+
+  protected mergeHeaders(headers: HeadersInit = {}) {
+    if (Array.isArray(headers)) {
+      headers.forEach(([key, val]) => ((this.headers as any)[key] = val));
+    } else {
+      this.headers = { ...this.headers, ...headers };
+    }
+  }
+}

+ 26 - 0
frameworks/TypeScript/ditsmod/src/app/bun-integration/pre-router.ts

@@ -0,0 +1,26 @@
+import { PreRouter } from '@ditsmod/core';
+import { NodeRes } from './node-res.js';
+
+export class BunPreRouter extends PreRouter {
+  override requestListener: any = async (req: Request) => {
+    const nodeReq = req as any;
+    const nodeRes = new NodeRes();
+
+    const url = new URL(req.url);
+    const uri = url.pathname;
+    const queryString = url.search.slice(1);
+    const { handle, params } = this.router.find(req.method as any, uri);
+    if (!handle) {
+      this.sendNotImplemented(nodeRes as any);
+      const body = await nodeRes.body;
+      return new Response(body, nodeRes);
+    }
+
+    await handle(nodeReq, nodeRes as any, params!, queryString).catch((err) => {
+      this.sendInternalServerError(nodeRes as any, err);
+    });
+
+    const body = await nodeRes.body;
+    return new Response(body, nodeRes);
+  };
+}

+ 9 - 0
frameworks/TypeScript/ditsmod/src/app/bun-integration/spawn.ts

@@ -0,0 +1,9 @@
+import os from 'node:os';
+
+const numCPUs = os.cpus().length;
+for (let i = 0; i < numCPUs; i++) {
+  Bun.spawn(['bun', 'dist/main.bun.js'], {
+    stdio: ['inherit', 'inherit', 'inherit'],
+    env: { ...process.env },
+  });
+}

+ 12 - 0
frameworks/TypeScript/ditsmod/src/main.bun.ts

@@ -0,0 +1,12 @@
+import { Serve, Server } from 'bun';
+
+import { AppModule } from './app/app.module.js';
+import { BunApplication } from './app/bun-integration/bun-application.js';
+
+const reusePort = process.env.NODE_ENV == 'production';
+const serverOptions = { port: 8080, hostname: '0.0.0.0', reusePort } as Serve;
+const { server } = await new BunApplication().bootstrap(AppModule, {
+  serverOptions: serverOptions as any,
+});
+
+const bunServer = server as unknown as Server;

+ 9 - 6
frameworks/TypeScript/ditsmod/tsconfig.json

@@ -16,14 +16,17 @@
     "noImplicitAny": true,
     "noImplicitAny": true,
     "strictPropertyInitialization": false,
     "strictPropertyInitialization": false,
     "allowJs": false,
     "allowJs": false,
-    "paths": {
-      "#routed/*": ["./src/app/modules/routed/*"],
-      "#service/*": ["./src/app/modules/service/*"],
-      "#utils/*": ["./src/app/utils/*"],
-    }
+    "types": ["bun-types"],
+    // Bun works with bugs if this code is uncommented.
+    // "paths": {
+    //   "#routed/*": ["./src/app/modules/routed/*"],
+    //   "#service/*": ["./src/app/modules/service/*"],
+    //   "#utils/*": ["./src/app/utils/*"],
+    // }
   },
   },
   "include": [
   "include": [
     "src",
     "src",
-    "test"
+    "test",
+    "./spawn.ts"
   ]
   ]
 }
 }