@adaas/a-server 0.0.20 → 0.0.22

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.
@@ -100,7 +100,14 @@ import { A_Config, A_ConfigLoader, A_Polyfill, ConfigReader, ENVConfigReader, Fi
100
100
  '/assets/.*': 'https://test.com',
101
101
  '/style.css': 'https://test.com'
102
102
  }),
103
- new A_StaticConfig(['docs'])
103
+ new A_StaticConfig(
104
+ ['./public', './assets'], // Simple directories
105
+ [ // Custom aliases
106
+ { path: '/api-docs', directory: './docs', alias: '/documentation' },
107
+ { path: '/assets', directory: './static/assets' },
108
+ { path: '/uploads', directory: './user-uploads' }
109
+ ]
110
+ )
104
111
  ],
105
112
  entities: []
106
113
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaas/a-server",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "description": "SDK to create a server with ease. Build your backend server with modular structure and use it within or outside the ADAAS ecosystem.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -49,7 +49,7 @@
49
49
  "homepage": "https://github.com/ADAAS-org/adaas-a-server#readme",
50
50
  "dependencies": {
51
51
  "@adaas/a-concept": "^0.1.24",
52
- "@adaas/a-utils": "^0.1.10"
52
+ "@adaas/a-utils": "^0.1.11"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/chai": "^4.3.14",
@@ -4,9 +4,7 @@ import { A_ProxyConfig } from "@adaas/a-server/context/A-ProxyConfig/A_ProxyConf
4
4
  import { A_Request } from "@adaas/a-server/entities/A-Request/A-Request.entity";
5
5
  import { A_Response } from "@adaas/a-server/entities/A-Response/A-Response.entity";
6
6
  import { A_Route } from "@adaas/a-server/entities/A-Route/A-Route.entity";
7
- import { A_Logger } from "@adaas/a-utils";
8
- import http from "http";
9
- import https from "https";
7
+ import { A_Logger, A_Polyfill } from "@adaas/a-utils";
10
8
 
11
9
 
12
10
  export class A_ServerProxy extends A_Component {
@@ -25,7 +23,7 @@ export class A_ServerProxy extends A_Component {
25
23
  }
26
24
 
27
25
 
28
-
26
+
29
27
  @A_Feature.Extend({
30
28
  name: A_SERVER_TYPES__ServerFeature.onRequest,
31
29
  })
@@ -33,9 +31,10 @@ export class A_ServerProxy extends A_Component {
33
31
  @A_Inject(A_Request) req: A_Request,
34
32
  @A_Inject(A_Response) res: A_Response,
35
33
  @A_Inject(A_ProxyConfig) proxyConfig: A_ProxyConfig,
36
- @A_Inject(A_Logger) logger: A_Logger
34
+ @A_Inject(A_Logger) logger: A_Logger,
35
+ @A_Inject(A_Polyfill) polyfill: A_Polyfill
37
36
  ) {
38
- return new Promise<void>((resolve, reject) => {
37
+ return new Promise<void>(async (resolve, reject) => {
39
38
  const { method, url } = req;
40
39
 
41
40
  const route = new A_Route(url, method);
@@ -51,7 +50,9 @@ export class A_ServerProxy extends A_Component {
51
50
  config
52
51
  );
53
52
 
54
- const client = config.protocol === "https:" ? https : http;
53
+ const client = await (config.protocol === "https:"
54
+ ? polyfill.https()
55
+ : polyfill.http());
55
56
 
56
57
  const proxyReq = client.request(
57
58
  {
@@ -1,27 +1,32 @@
1
1
  import { A_Component, A_Concept, A_Feature, A_Inject } from "@adaas/a-concept"
2
- import fs from "fs";
3
- import path from "path";
4
- import { URL } from "url";
5
2
  import { A_Request } from "@adaas/a-server/entities/A-Request/A-Request.entity";
6
3
  import { A_Response } from "@adaas/a-server/entities/A-Response/A-Response.entity";
7
4
  import { A_SERVER_TYPES__ServerFeature } from "@adaas/a-server/containers/A-Service/A-Service.container.types";
8
5
  import { A_Route } from "@adaas/a-server/entities/A-Route/A-Route.entity";
9
- import { A_StaticConfig } from "@adaas/a-server/context/A-StaticConfig/A-StaticConfig.context";
10
- import { A_Logger } from "@adaas/a-utils";
11
-
6
+ import { A_StaticConfig, A_StaticAlias } from "@adaas/a-server/context/A-StaticConfig/A-StaticConfig.context";
7
+ import { A_Logger, A_Polyfill } from "@adaas/a-utils";
12
8
 
13
9
  export class A_StaticLoader extends A_Component {
14
10
 
11
+ private _fsPolyfill: any;
12
+ private _pathPolyfill: any;
15
13
 
16
14
  @A_Concept.Load()
17
15
  async load(
18
16
  @A_Inject(A_Logger) logger: A_Logger,
19
- @A_Inject(A_StaticConfig) config: A_StaticConfig
17
+ @A_Inject(A_StaticConfig) config: A_StaticConfig,
18
+ @A_Inject(A_Polyfill) polyfill: A_Polyfill
20
19
  ) {
20
+ // Initialize polyfills
21
+ this._fsPolyfill = await polyfill.fs();
22
+ this._pathPolyfill = await polyfill.path();
23
+
24
+ // Log configured aliases
25
+ const aliases = config.getEnabledAliases();
21
26
  logger.log(
22
27
  'pink',
23
- `Static directories configured:`,
24
- config.directories.join('\n')
28
+ `Static aliases configured:`,
29
+ aliases.map(alias => `${alias.alias} -> ${alias.directory}`).join('\n')
25
30
  );
26
31
  }
27
32
 
@@ -37,7 +42,8 @@ export class A_StaticLoader extends A_Component {
37
42
  @A_Inject(A_Request) req: A_Request,
38
43
  @A_Inject(A_Response) res: A_Response,
39
44
  @A_Inject(A_Logger) logger: A_Logger,
40
- @A_Inject(A_StaticConfig) config: A_StaticConfig
45
+ @A_Inject(A_StaticConfig) config: A_StaticConfig,
46
+ @A_Inject(A_Polyfill) polyfill: A_Polyfill
41
47
  ) {
42
48
 
43
49
  if (req.method !== 'GET' && req.method !== 'HEAD') {
@@ -47,87 +53,252 @@ export class A_StaticLoader extends A_Component {
47
53
  const { method, url } = req;
48
54
  const route = new A_Route(url, method);
49
55
 
50
- const staticDirConfig = config.has(route.path)
51
-
52
- if (!staticDirConfig) {
56
+ // Check if this request matches any of our configured aliases
57
+ const alias = config.findMatchingAlias(route.path);
58
+ if (!alias) {
53
59
  return; // No static config for this path
54
60
  }
55
61
 
56
- const staticDir = path.resolve(process.cwd(), staticDirConfig);
62
+ try {
63
+ // Ensure polyfills are available
64
+ const fs = this._fsPolyfill || await polyfill.fs();
65
+ const path = this._pathPolyfill || await polyfill.path();
66
+
67
+ const staticDir = path.resolve(process.cwd(), alias.directory);
68
+
69
+ // Validate static directory exists
70
+ if (!fs.existsSync(staticDir)) {
71
+ logger.log("red", `Static directory ${staticDir} does not exist.`);
72
+ return;
73
+ }
74
+
75
+ // Get the file path relative to the alias
76
+ const relativePath = route.path.replace(alias.path, '');
77
+ const safePath = this.safeFilePath(staticDir, relativePath, req.headers?.host, path, fs);
78
+
79
+ await this.serveFile(safePath, res, logger, fs, path);
80
+ } catch (error: any) {
81
+ logger.error(`Static file serving error: ${error.message}`);
82
+ res.writeHead(404, { "Content-Type": "text/plain" });
83
+ res.send("File not found");
84
+ }
85
+ }
57
86
 
58
- if (!fs.existsSync(staticDir) || !fs.statSync(staticDir).isDirectory()) {
59
- logger.log("red", `Static directory ${staticDir} does not exist or is not a directory.`);
60
- return;
87
+ /**
88
+ * Add a custom static file alias through the config
89
+ * @param alias - The URL path alias (e.g., '/assets')
90
+ * @param directory - The local directory path
91
+ * @param path - Optional custom path (defaults to alias)
92
+ * @param config - Static config instance
93
+ * @param logger - Logger instance for logging
94
+ */
95
+ public addAlias(
96
+ alias: string,
97
+ directory: string,
98
+ config: A_StaticConfig,
99
+ logger?: A_Logger,
100
+ path?: string
101
+ ): void {
102
+ config.addAlias(alias, directory, path);
103
+
104
+ if (logger) {
105
+ logger.log('cyan', `Static alias added: ${alias} -> ${directory}`);
61
106
  }
107
+ }
62
108
 
63
- await this.serveFile(route.path.startsWith('/') ? route.path.slice(1) : route.path, res);
109
+ /**
110
+ * Remove a static file alias through the config
111
+ * @param aliasPath - The path of the alias to remove
112
+ * @param config - Static config instance
113
+ * @param logger - Logger instance for logging
114
+ */
115
+ public removeAlias(aliasPath: string, config: A_StaticConfig, logger?: A_Logger): boolean {
116
+ const removed = config.removeAlias(aliasPath);
117
+
118
+ if (removed && logger) {
119
+ logger.log('yellow', `Static alias removed: ${aliasPath}`);
120
+ }
121
+
122
+ return removed;
64
123
  }
65
124
 
125
+ /**
126
+ * Get all configured aliases from config
127
+ * @param config - Static config instance
128
+ */
129
+ public getAliases(config: A_StaticConfig): A_StaticAlias[] {
130
+ return config.getAliases();
131
+ }
66
132
 
133
+ /**
134
+ * Enable or disable an alias
135
+ * @param aliasPath - The path of the alias
136
+ * @param enabled - Whether to enable or disable
137
+ * @param config - Static config instance
138
+ * @param logger - Logger instance for logging
139
+ */
140
+ public setAliasEnabled(
141
+ aliasPath: string,
142
+ enabled: boolean,
143
+ config: A_StaticConfig,
144
+ logger?: A_Logger
145
+ ): boolean {
146
+ const result = config.setAliasEnabled(aliasPath, enabled);
147
+
148
+ if (result && logger) {
149
+ logger.log('blue', `Static alias ${enabled ? 'enabled' : 'disabled'}: ${aliasPath}`);
150
+ }
151
+
152
+ return result;
153
+ }
67
154
 
68
155
  protected getMimeType(ext: string): string {
69
156
  const mimeTypes: Record<string, string> = {
157
+ // Text
70
158
  ".html": "text/html",
71
- ".js": "application/javascript",
159
+ ".htm": "text/html",
72
160
  ".css": "text/css",
161
+ ".txt": "text/plain",
162
+ ".md": "text/markdown",
163
+ ".xml": "application/xml",
164
+
165
+ // JavaScript
166
+ ".js": "application/javascript",
167
+ ".mjs": "application/javascript",
168
+ ".jsx": "application/javascript",
169
+ ".ts": "application/typescript",
170
+ ".tsx": "application/typescript",
171
+
172
+ // JSON
73
173
  ".json": "application/json",
174
+ ".jsonld": "application/ld+json",
175
+
176
+ // Images
74
177
  ".png": "image/png",
75
178
  ".jpg": "image/jpeg",
76
179
  ".jpeg": "image/jpeg",
77
180
  ".gif": "image/gif",
78
181
  ".svg": "image/svg+xml",
79
182
  ".ico": "image/x-icon",
80
- ".txt": "text/plain",
183
+ ".webp": "image/webp",
184
+ ".bmp": "image/bmp",
185
+ ".tiff": "image/tiff",
186
+
187
+ // Fonts
188
+ ".woff": "font/woff",
189
+ ".woff2": "font/woff2",
190
+ ".ttf": "font/ttf",
191
+ ".otf": "font/otf",
192
+ ".eot": "application/vnd.ms-fontobject",
193
+
194
+ // Audio/Video
195
+ ".mp3": "audio/mpeg",
196
+ ".wav": "audio/wav",
197
+ ".mp4": "video/mp4",
198
+ ".webm": "video/webm",
199
+ ".ogg": "application/ogg",
200
+
201
+ // Archives
202
+ ".zip": "application/zip",
203
+ ".tar": "application/x-tar",
204
+ ".gz": "application/gzip",
205
+
206
+ // Documents
207
+ ".pdf": "application/pdf",
208
+ ".doc": "application/msword",
209
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
210
+ ".xls": "application/vnd.ms-excel",
211
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
81
212
  };
82
213
 
83
214
  return mimeTypes[ext.toLowerCase()] || "application/octet-stream";
84
215
  }
85
216
 
86
217
 
87
- protected safeFilePath(staticDir: string, reqUrl: string, host?: string): string {
88
- const parsedUrl = new URL(reqUrl || "/", `http://${host || "localhost"}`);
218
+ protected safeFilePath(staticDir: string, reqUrl: string, host: string = 'localhost', pathPolyfill: any, fsPolyfill: any): string {
219
+ const parsedUrl = new URL(reqUrl || "/", `http://${host}`);
89
220
  let pathname = decodeURIComponent(parsedUrl.pathname);
90
221
 
91
222
  // Prevent path traversal attacks
92
223
  pathname = pathname.replace(/\.\.[\/\\]/g, "");
93
224
 
94
- let filePath = path.join(staticDir, pathname);
95
- if (!fs.existsSync(filePath))
225
+ let filePath = pathPolyfill.join(staticDir, pathname);
226
+
227
+ if (!fsPolyfill.existsSync(filePath)) {
96
228
  throw new Error(`File not found: ${filePath}`);
229
+ }
97
230
 
98
231
  return filePath;
99
232
  }
100
233
 
101
-
102
- protected serveFile(filePath: string, res: A_Response): Promise<void> {
103
-
104
-
234
+ protected serveFile(
235
+ filePath: string,
236
+ res: A_Response,
237
+ logger: A_Logger,
238
+ fsPolyfill: any,
239
+ pathPolyfill: any
240
+ ): Promise<void> {
105
241
  return new Promise<void>((resolve, reject) => {
106
-
107
- if (fs.existsSync(filePath)) {
108
- const ext = path.extname(filePath);
109
- const contentType = this.getMimeType(ext);
110
-
111
- res.writeHead(200, { "Content-Type": contentType });
112
- const stream = fs.createReadStream(filePath);
113
- stream.pipe(res.original);
114
-
115
- stream.on('end', () => {
116
- resolve();
117
- });
118
-
119
- stream.on("error", (err) => {
120
- reject(new Error(`File stream error: ${err.message}`));
121
- });
122
- } else {
123
- reject(new Error(`File not found: ${filePath}`));
242
+ try {
243
+ if (fsPolyfill.existsSync(filePath)) {
244
+ const ext = pathPolyfill.extname(filePath);
245
+ const contentType = this.getMimeType(ext);
246
+
247
+ // Set appropriate headers
248
+ const headers: Record<string, string> = {
249
+ "Content-Type": contentType,
250
+ "Cache-Control": this.getCacheControl(ext),
251
+ "X-Content-Type-Options": "nosniff"
252
+ };
253
+
254
+ res.writeHead(200, headers);
255
+ const stream = fsPolyfill.createReadStream(filePath);
256
+
257
+ if (stream && res.original) {
258
+ stream.pipe(res.original);
259
+
260
+ stream.on('end', () => {
261
+ logger.log('green', `Successfully served: ${filePath}`);
262
+ resolve();
263
+ });
264
+
265
+ stream.on("error", (err: any) => {
266
+ logger.error(`File stream error: ${err.message}`);
267
+ reject(new Error(`File stream error: ${err.message}`));
268
+ });
269
+ } else {
270
+ res.writeHead(500, { "Content-Type": "text/plain" });
271
+ res.send("Internal server error");
272
+ reject(new Error("Failed to create file stream"));
273
+ }
274
+ } else {
275
+ res.writeHead(404, { "Content-Type": "text/plain" });
276
+ res.send("File not found");
277
+ reject(new Error(`File not found: ${filePath}`));
278
+ }
279
+ } catch (error: any) {
280
+ logger.error(`Error serving file: ${error.message}`);
281
+ res.writeHead(500, { "Content-Type": "text/plain" });
282
+ res.send("Internal server error");
283
+ reject(error);
124
284
  }
125
-
126
-
127
- resolve();
128
285
  });
129
286
  }
130
287
 
288
+ protected getCacheControl(ext: string): string {
289
+ // Different cache strategies for different file types
290
+ const staticAssets = ['.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf', '.otf'];
291
+ const dynamicContent = ['.html', '.htm'];
292
+
293
+ if (staticAssets.includes(ext.toLowerCase())) {
294
+ return "public, max-age=31536000"; // 1 year for static assets
295
+ } else if (dynamicContent.includes(ext.toLowerCase())) {
296
+ return "public, max-age=3600"; // 1 hour for HTML
297
+ } else {
298
+ return "public, max-age=86400"; // 1 day for other files
299
+ }
300
+ }
301
+
131
302
  }
132
303
 
133
304
 
@@ -1,110 +1,8 @@
1
- import { A_Component, A_Feature, A_Inject } from "@adaas/a-concept"
2
- import fs from "fs";
3
- import path from "path";
4
- import { URL } from "url";
5
- import { A_Request } from "@adaas/a-server/entities/A-Request/A-Request.entity";
6
- import { A_Response } from "@adaas/a-server/entities/A-Response/A-Response.entity";
7
- import { A_SERVER_TYPES__ServerFeature } from "@adaas/a-server/containers/A-Service/A-Service.container.types";
8
- import { A_Logger } from "@adaas/a-utils";
9
1
 
10
2
 
11
- export class A_StaticLoader extends A_Component {
12
-
13
- // =======================================================
14
- // ================ Method Definition=====================
15
- // =======================================================
16
-
17
- @A_Feature.Extend({
18
- name: A_SERVER_TYPES__ServerFeature.onRequest,
19
- })
20
- async onRequest(
21
- @A_Inject(A_Request) req: A_Request,
22
- @A_Inject(A_Response) res: A_Response,
23
- @A_Inject(A_Logger) logger: A_Logger
24
- ) {
25
-
26
- if (req.method !== 'GET' && req.method !== 'HEAD') {
27
- return; // Only handle GET and HEAD requests
28
- }
29
-
30
- const staticDir = path.resolve(process.cwd(), 'public');
31
-
32
- if (!fs.existsSync(staticDir) || !fs.statSync(staticDir).isDirectory()) {
33
- logger.log("red", `Static directory ${staticDir} does not exist or is not a directory.`);
34
- return;
35
- }
36
-
37
- const filePath = this.safeFilePath(staticDir, req.url || "/", req.headers.host);
38
-
39
- await this.serveFile(filePath, res);
40
- }
41
-
42
-
43
-
44
- protected getMimeType(ext: string): string {
45
- const mimeTypes: Record<string, string> = {
46
- ".html": "text/html",
47
- ".js": "application/javascript",
48
- ".css": "text/css",
49
- ".json": "application/json",
50
- ".png": "image/png",
51
- ".jpg": "image/jpeg",
52
- ".jpeg": "image/jpeg",
53
- ".gif": "image/gif",
54
- ".svg": "image/svg+xml",
55
- ".ico": "image/x-icon",
56
- ".txt": "text/plain",
57
- };
58
-
59
- return mimeTypes[ext.toLowerCase()] || "application/octet-stream";
60
- }
61
-
62
-
63
- protected safeFilePath(staticDir: string, reqUrl: string, host?: string): string {
64
- const parsedUrl = new URL(reqUrl || "/", `http://${host || "localhost"}`);
65
- let pathname = decodeURIComponent(parsedUrl.pathname);
66
-
67
- // Prevent path traversal attacks
68
- pathname = pathname.replace(/\.\.[\/\\]/g, "");
69
-
70
- let filePath = path.join(staticDir, pathname);
71
-
72
- if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
73
- filePath = path.join(filePath, "index.html");
74
- }
75
-
76
- return filePath;
77
- }
78
-
79
-
80
- protected serveFile(filePath: string, res: A_Response): Promise<void> {
81
-
82
- return new Promise<void>((resolve, reject) => {
83
-
84
- if (fs.existsSync(filePath)) {
85
- const ext = path.extname(filePath);
86
- const contentType = this.getMimeType(ext);
87
-
88
- res.writeHead(200, { "Content-Type": contentType });
89
- const stream = fs.createReadStream(filePath);
90
- stream.pipe(res.original);
91
-
92
- stream.on('end', () => {
93
- resolve();
94
- });
95
-
96
- stream.on("error", (err) => {
97
- reject(new Error(`File stream error: ${err.message}`));
98
- });
99
- } else {
100
- reject(new Error(`File not found: ${filePath}`));
101
- }
102
-
103
-
104
- resolve();
105
- });
106
- }
107
-
3
+ export type A_SERVER_TYPES__StaticLoader_Init = {
4
+ /**
5
+ * Path to the static files directory
6
+ */
7
+ staticFilesPath: string,
108
8
  }
109
-
110
-
@@ -1,12 +1,12 @@
1
- import { createServer, IncomingMessage, Server, ServerResponse } from "http";
2
- import { A_SERVER_TYPES__ServerFeature, A_SERVER_TYPES__ServerFeatures } from "./A-Service.container.types";
1
+ import type { IncomingMessage, Server, ServerResponse } from "http";
2
+ import { A_SERVER_TYPES__ServerFeature } from "./A-Service.container.types";
3
3
  import { A_Server } from "@adaas/a-server/context/A-Server/A_Server.context";
4
4
  import { A_Request } from "@adaas/a-server/entities/A-Request/A-Request.entity";
5
5
  import { A_Response } from "@adaas/a-server/entities/A-Response/A-Response.entity";
6
- import crypto from 'crypto';
7
6
  import { A_SERVER_CONSTANTS__DEFAULT_ENV_VARIABLES_ARRAY, A_TYPES__ServerENVVariables } from "@adaas/a-server/constants/env.constants";
8
- import { A_Concept, A_Container, A_Feature, A_IdentityHelper, A_Scope } from "@adaas/a-concept";
9
- import { A_Config, A_Logger } from "@adaas/a-utils";
7
+ import { A_Concept, A_Container, A_Error, A_Feature, A_IdentityHelper, A_Inject, A_Scope } from "@adaas/a-concept";
8
+ import { A_Config, A_Logger, A_Polyfill, IcryptoInterface } from "@adaas/a-utils";
9
+ import { A_ServerLogger } from "@adaas/a-server/components/A-ServerLogger/A_ServerLogger.component";
10
10
 
11
11
 
12
12
 
@@ -22,11 +22,24 @@ export class A_Service extends A_Container {
22
22
  port!: number;
23
23
 
24
24
  @A_Concept.Load()
25
- async load() {
25
+ async load(
26
+ ) {
27
+ // Initialize Logger
28
+ let logger: A_ServerLogger;
29
+ if (!this.scope.has(A_ServerLogger))
30
+ this.scope.register(A_ServerLogger);
26
31
 
27
- let config: A_Config<A_TYPES__ServerENVVariables>;
28
- let aServer: A_Server;
32
+ logger = this.scope.resolve(A_ServerLogger);
33
+
34
+ // Initialize Polyfill
35
+ let polyfill: A_Polyfill;
36
+ if (!this.scope.has(A_Polyfill))
37
+ this.scope.register(A_Polyfill);
29
38
 
39
+ polyfill = this.scope.resolve(A_Polyfill);
40
+
41
+ // Initialize Config
42
+ let config: A_Config<A_TYPES__ServerENVVariables>;
30
43
  if (!this.scope.has(A_Config<A_TYPES__ServerENVVariables>)) {
31
44
  const config = new A_Config<A_TYPES__ServerENVVariables>({
32
45
  variables: [...Array.from(A_SERVER_CONSTANTS__DEFAULT_ENV_VARIABLES_ARRAY)],
@@ -37,9 +50,10 @@ export class A_Service extends A_Container {
37
50
 
38
51
  this.scope.register(config);
39
52
  }
40
-
41
53
  config = this.scope.resolve(A_Config) as A_Config<A_TYPES__ServerENVVariables>;
42
54
 
55
+ let aServer: A_Server;
56
+
43
57
 
44
58
  if (!this.scope.has(A_Server)) {
45
59
  aServer = new A_Server({
@@ -52,8 +66,10 @@ export class A_Service extends A_Container {
52
66
  // Set the server to listen on port 3000
53
67
  this.port = config.get('A_SERVER_PORT');
54
68
 
69
+ const http = await polyfill.http();
70
+
55
71
  // Create the HTTP server
56
- this.server = createServer(this.onRequest.bind(this));
72
+ this.server = http.createServer(this.onRequest.bind(this));
57
73
 
58
74
  }
59
75
 
@@ -147,7 +163,7 @@ export class A_Service extends A_Container {
147
163
  await res.status(200).send();
148
164
 
149
165
  } catch (error) {
150
-
166
+
151
167
  const logger = this.scope.resolve(A_Logger);
152
168
 
153
169
  logger.error(error);
@@ -164,9 +180,11 @@ export class A_Service extends A_Container {
164
180
  ): Promise<{ req: A_Request, res: A_Response }> {
165
181
 
166
182
  if (!request.method || !request.url)
167
- throw new Error('Request method or url is missing');
183
+ throw new A_Error('Request method or url is missing');
184
+
168
185
 
169
- const id = this.generateRequestId(request.method, request.url);
186
+
187
+ const id = await this.generateRequestId(request.method, request.url);
170
188
 
171
189
  const req = new A_Request({ id, request, scope: this.scope.name });
172
190
  const res = new A_Response({ id, response, scope: this.scope.name });
@@ -177,17 +195,19 @@ export class A_Service extends A_Container {
177
195
  return { req, res };
178
196
  }
179
197
 
180
- protected generateRequestId(
198
+ protected async generateRequestId(
181
199
  method: string,
182
200
  url: string
183
- ): string {
201
+ ): Promise<string> {
202
+ const crypto = await this.scope.resolve(A_Polyfill).crypto();
203
+
184
204
  // Use the current time, request URL, and a few other details to create a unique ID
185
- const hash = crypto.createHash('sha256');
186
205
  const timeId = A_IdentityHelper.generateTimeId();
187
206
  const randomValue = Math.random().toString(); // Adds extra randomness
188
207
 
189
- hash.update(`${timeId}-${method}-${url}-${randomValue}`);
190
- return `${timeId}-${hash.digest('hex')}`;
208
+ const hash = await crypto.createTextHash(`${timeId}-${method}-${url}-${randomValue}`, 'sha256');
209
+
210
+ return `${timeId}-${hash}`;
191
211
  }
192
212
 
193
213
  @A_Feature.Define({ invoke: true })