geonix 1.23.6 → 1.30.2

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.
package/src/Util.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createHash, randomBytes } from "crypto";
2
- import { URL, fileURLToPath } from "url";
3
- import { readFile } from "fs/promises";
2
+ import { URL } from "url";
3
+ import { createRequire } from "module";
4
4
  import { join } from "path";
5
5
  import { Transform } from "node:stream";
6
6
  import { networkInterfaces } from "os";
@@ -24,7 +24,7 @@ export const sleep = delay => new Promise(resolve => setTimeout(resolve, delay))
24
24
  *
25
25
  * @returns
26
26
  */
27
- export const nextTick = () => sleep(0);
27
+ export const yieldToEventLoop = () => new Promise(resolve => setImmediate(resolve));
28
28
 
29
29
  /**
30
30
  * Parse nats:// URL
@@ -47,12 +47,29 @@ export function parseURL(url) {
47
47
  };
48
48
  }
49
49
 
50
+ const BASE62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
51
+ const LOG256_LOG62 = Math.log(256) / Math.log(62); // ≈ 1.3437
52
+
53
+ export function encodeBase62(buffer) {
54
+ if (buffer.length === 0) { return ""; }
55
+ const len = Math.ceil(buffer.length * LOG256_LOG62);
56
+ let n = BigInt("0x" + buffer.toString("hex"));
57
+ const chars = new Array(len);
58
+ for (let i = len - 1; i >= 0; i--) {
59
+ chars[i] = BASE62[Number(n % 62n)];
60
+ n /= 62n;
61
+ }
62
+ return chars.join("");
63
+ }
64
+
50
65
  /**
51
- * Generate small random Base64 encoded ID
52
- * @param {number} size
53
- * @returns
66
+ * Generates a cryptographically random Base62-encoded ID string.
67
+ * Exported as `randomID` in the public API.
68
+ *
69
+ * @param {number} [size=16] - Number of random bytes to encode (more bytes → longer, more unique ID).
70
+ * @returns {string} URL-safe, Base62-encoded random string.
54
71
  */
55
- export const picoid = (size = 12) => randomBytes(size).toString("base64");
72
+ export const picoid = (size = 16) => encodeBase62(randomBytes(size));
56
73
 
57
74
  /**
58
75
  * Get SHA256 hash of a string or a buffer
@@ -81,39 +98,24 @@ export const createServerAtPort = (port, pkg, handler) =>
81
98
  });
82
99
 
83
100
  /**
84
- * Create TCP or HTTP server at first free port in rage
85
- * @param {Object} pkg
86
- * @param {Function} handler
87
- * @param {number} start
88
- * @param {number} poolSize
89
- * @returns
101
+ * Create TCP or HTTP server at an OS-assigned free port
102
+ * @param {Object} pkg
103
+ * @param {Function} handler
104
+ * @returns
90
105
  */
91
- export const createServerAtFreePort = async (pkg, handler, start = 30000, poolSize = 20000) => {
92
- for (let port = start; port < start + poolSize; port++) {
93
- try {
94
- const result = await createServerAtPort(port, pkg, handler);
95
- if (result) {
96
- return result;
97
- }
98
- } catch {
99
- // silenty ignore errors
100
- }
101
- }
102
- };
103
-
104
- /**
105
- * Create TCP server at random port
106
- * @param {Function} handler
107
- * @returns
108
- */
109
- export const createTCPServer = (handler, start = 30000, poolSize = 20000) => createServerAtFreePort(net, handler, start, poolSize);
106
+ export const createServerAtFreePort = (pkg, handler) =>
107
+ new Promise((resolve, reject) => {
108
+ const server = pkg.createServer(handler);
109
+ server.on("error", reject);
110
+ server.listen(0, () => resolve({ server, port: server.address().port }));
111
+ });
110
112
 
111
113
  /**
112
- * Create HTTP server at random port
113
- * @param {Function} handler
114
- * @returns
114
+ * Create TCP server at an OS-assigned free port
115
+ * @param {Function} handler
116
+ * @returns
115
117
  */
116
- export const createHTTPServer = (handler, start = 30000, poolSize = 20000) => createServerAtFreePort(net, handler, start, poolSize);
118
+ export const createTCPServer = (handler) => createServerAtFreePort(net, handler);
117
119
 
118
120
  /**
119
121
  * Return number of seconds passed from the start of the day (0-86399)
@@ -124,39 +126,6 @@ export const getSecondsSinceMidnight = () => {
124
126
  return Math.floor((date.getTime() - date.setHours(0, 0, 0, 0)) / 1000);
125
127
  };
126
128
 
127
- /**
128
- * Parse function body and return array of param names
129
- * @param {*} fn
130
- * @returns string[]
131
- */
132
- export const getFunctionParams = (fn) => {
133
- const code = fn.toString();
134
- const endParenthesisPosition = code.indexOf(")");
135
- let params;
136
-
137
- if (endParenthesisPosition != -1) {
138
- params = code.substring(code.indexOf("(") + 1, endParenthesisPosition);
139
- } else {
140
- params = code.substring(0, code.indexOf("=>"));
141
- }
142
-
143
- params = params
144
- // cleanup spaces
145
- .replaceAll(" ", "")
146
- // split into array
147
- .split(",");
148
-
149
- // remove potential default values
150
- for (let index = 0; index < params.length; index++) {
151
- const defaultValueAssignmentPosition = params[index].indexOf("=");
152
- if (defaultValueAssignmentPosition != -1) {
153
- params[index] = params[index].substring(0, defaultValueAssignmentPosition - 1);
154
- }
155
- }
156
-
157
- return params;
158
- };
159
-
160
129
  export const proxyHttp = (target, req, res) =>
161
130
  new Promise((resolve, reject) => {
162
131
  const remoteTarget = `${target}${req.originalUrl}`;
@@ -188,37 +157,40 @@ export const proxyHttp = (target, req, res) =>
188
157
  */
189
158
  export const OverlayObject = (object, overlay) => new Proxy(object, { get: (t, p) => overlay[p] !== undefined ? overlay[p] : t[p] });
190
159
 
191
- let _geonix_version = "N/A";
192
- try {
193
- const __dirname = fileURLToPath(new URL("..", import.meta.url));
194
- const local = JSON.parse(await readFile(join(__dirname, "package.json")));
195
- _geonix_version = local.version;
196
- } catch {
197
- // ignore errors
198
- }
199
-
200
- export const GeonixVersion = _geonix_version;
201
-
202
- export const StreamChunker = (chunkSize = 65536) => {
203
- let chunker = new Transform();
160
+ /**
161
+ * The version string of the currently installed Geonix package, read from `package.json` at
162
+ * module load time. Equals `"N/A"` when the package metadata cannot be found.
163
+ *
164
+ * @type {string}
165
+ */
166
+ export const GeonixVersion = (() => {
167
+ try {
168
+ return createRequire(import.meta.url)("../package.json").version ?? "N/A";
169
+ } catch {
170
+ return "N/A";
171
+ }
172
+ })();
204
173
 
205
- chunker._transform = function (chunk, encoding, done) {
174
+ /**
175
+ * Chunk a stream into smaller chunks
176
+ *
177
+ * @param {*} chunkSize
178
+ * @returns
179
+ */
180
+ export const StreamChunker = (chunkSize = 65536) => new Transform({
181
+ transform(chunk, _encoding, done) {
206
182
  let offset = 0;
207
183
  while (offset < chunk.length) {
208
184
  const sliceSize = Math.min(chunkSize, chunk.length - offset);
209
185
  this.push(chunk.slice(offset, offset + sliceSize));
210
186
  offset += sliceSize;
211
187
  }
212
-
213
188
  done();
214
- };
215
-
216
- chunker._flush = function (done) {
189
+ },
190
+ flush(done) {
217
191
  done();
218
- };
219
-
220
- return chunker;
221
- };
192
+ }
193
+ });
222
194
 
223
195
  export async function getFirstItemFromAsyncIterable(asyncIterable) {
224
196
  const iterator = asyncIterable[Symbol.asyncIterator]();
@@ -235,6 +207,10 @@ export function getNetworkAddresses() {
235
207
  if (addressObject.family === "IPv4") {
236
208
  list.push(addressObject.address);
237
209
  }
210
+
211
+ if (addressObject.family === "IPv6") {
212
+ list.push(`[${addressObject.address}]`);
213
+ }
238
214
  }
239
215
  }
240
216
 
@@ -246,15 +222,24 @@ export function isIterable(obj) {
246
222
  }
247
223
 
248
224
  /**
249
- *
250
- *
251
- * @param {Request} req
252
- * @param {parseMultipartOptions} options
253
- * @returns ParsedMultiPart[]
225
+ * Parses a `multipart/form-data` request body into an array of part objects. Each part
226
+ * exposes parsed headers (e.g. `content-disposition`), a `name`, an optional `filename`,
227
+ * and a `body` {@link import('stream').Readable}.
228
+ *
229
+ * By default parts are streamed through temporary files on disk; set `options.useMemory` to
230
+ * `true` to buffer them in memory instead.
231
+ *
232
+ * @param {import('http').IncomingMessage} req - Incoming HTTP request with a `multipart/form-data` content-type.
233
+ * @param {object} [_options] - Parsing options.
234
+ * @param {boolean} [_options.useMemory=false] - Buffer parts in memory instead of temp files.
235
+ * @param {number} [_options.maxFileSize] - Maximum allowed size in bytes for a single part.
236
+ * @param {number} [_options.maxFiles] - Maximum number of parts allowed.
237
+ * @returns {Promise<Array<{ name: string|null, filename: string|null, headers: object, body: import('stream').Readable, size: number }>>}
238
+ * @throws {Error} If the content-type is not `multipart/form-data` or a size/count limit is exceeded.
254
239
  */
255
240
  export async function parseMultipart(req, _options) {
256
241
  if (!req.headers["content-type"]?.startsWith("multipart/form-data")) {
257
- throw new Error("Invalid content type (multipart/form-data expected)");
242
+ throw Error("Invalid content type (multipart/form-data expected)");
258
243
  }
259
244
 
260
245
  const BUFFER_SIZE = 1024 * 1024;
@@ -272,15 +257,41 @@ export async function parseMultipart(req, _options) {
272
257
  options.useMemory = true;
273
258
  }
274
259
 
275
- const boundary = Buffer.from("\r\n--" + req.headers["content-type"].split("boundary=")[1]);
260
+ const boundaryValue = req.headers["content-type"].match(/boundary=([^;,\s]+)/)?.[1];
261
+ if (!boundaryValue) {
262
+ throw Error("parseMultipart: missing boundary in content-type");
263
+ }
264
+ const boundary = Buffer.from("\r\n--" + boundaryValue);
276
265
 
277
- await nextTick();
266
+ await yieldToEventLoop();
278
267
 
279
268
  let lastChunk = Buffer.from("\r\n");
280
269
  let activePart;
281
- let done = false;
270
+
271
+ const cleanup = async () => {
272
+ for (const part of parts) {
273
+ if (part.bodyFile) {
274
+ try {
275
+ part.body.destroy();
276
+ } catch {
277
+ // ignore errors
278
+ }
279
+ try {
280
+ await unlink(part.bodyFile);
281
+ } catch {
282
+ // ignore errors
283
+ }
284
+ }
285
+ }
286
+ };
282
287
 
283
288
  const write = (chunk) => {
289
+ if (options.maxFileSize !== undefined) {
290
+ activePart.size += chunk.length;
291
+ if (activePart.size > options.maxFileSize) {
292
+ throw Error(`parseMultipart: part exceeds maxFileSize of ${options.maxFileSize} bytes`);
293
+ }
294
+ }
284
295
  if (options.useMemory) {
285
296
  activePart.body.push(chunk);
286
297
  } else {
@@ -289,75 +300,95 @@ export async function parseMultipart(req, _options) {
289
300
  };
290
301
 
291
302
  const newPart = () => {
292
- // create new part
303
+ if (options.maxFiles !== undefined && parts.length >= options.maxFiles) {
304
+ throw Error(`parseMultipart: exceeded maxFiles limit of ${options.maxFiles}`);
305
+ }
293
306
  const bodyFile = tempFilename();
294
307
  activePart = {
295
308
  headers: {},
296
309
  bodyFile: options.useMemory ? undefined : bodyFile,
297
- body: options.useMemory ? [] : createWriteStream(bodyFile, { flags: "wx" })
310
+ body: options.useMemory ? [] : createWriteStream(bodyFile, { flags: "wx" }),
311
+ size: 0
298
312
  };
299
313
  parts.push(activePart);
300
314
  };
301
315
 
302
- while (stream.readable) {
303
- // next next chunk
304
- let chunk = stream.read(BUFFER_SIZE);
316
+ try {
317
+ while (stream.readable) {
318
+ // next next chunk
319
+ let chunk = stream.read(BUFFER_SIZE);
305
320
 
306
- if (!chunk) {
307
- await nextTick();
308
- continue;
309
- }
321
+ if (!chunk) {
322
+ await yieldToEventLoop();
323
+ continue;
324
+ }
310
325
 
311
- let combined = Buffer.concat([lastChunk, chunk]);
326
+ let combined = Buffer.concat([lastChunk, chunk]);
327
+ let lookbehindSet = false;
328
+
329
+ while (combined.length >= boundary.length + 2) {
330
+ const boundaryIndex = combined.indexOf(boundary);
331
+
332
+ if (boundaryIndex === -1) {
333
+ // Keep only the last boundary.length-1 bytes as lookbehind so a
334
+ // boundary that straddles a read boundary is not split across chunks.
335
+ const safeLength = combined.length - (boundary.length - 1);
336
+ if (activePart && safeLength > 0) {
337
+ write(combined.subarray(0, safeLength));
338
+ }
339
+ lastChunk = combined.subarray(safeLength);
340
+ lookbehindSet = true;
341
+ break;
342
+ }
312
343
 
313
- while (combined.length >= boundary.length + 2) {
314
- const boundaryIndex = combined.indexOf(boundary);
315
- const isLastBoundary = combined[boundaryIndex + boundary.length] === 45 && combined[boundaryIndex + boundary.length + 1] === 45;
344
+ const isLastBoundary = combined[boundaryIndex + boundary.length] === 45 && combined[boundaryIndex + boundary.length + 1] === 45;
316
345
 
317
- if (boundaryIndex === -1) {
318
- lastChunk = combined;
319
- break;
320
- }
346
+ if (boundaryIndex > 0) {
347
+ write(combined.subarray(0, boundaryIndex));
348
+ }
321
349
 
322
- if (boundaryIndex > 0) {
323
- write(combined.subarray(0, boundaryIndex));
324
- }
350
+ if (isLastBoundary) {
351
+ break;
352
+ }
325
353
 
326
- if (isLastBoundary) {
327
- combined = combined.subarray(boundaryIndex + boundary.length + 2);
328
- done = true;
329
- break;
330
- }
354
+ newPart();
331
355
 
332
- newPart();
356
+ const endOfHeaders = combined.indexOf(END_OF_HEADERS, boundaryIndex);
333
357
 
334
- const endOfHeaders = combined.indexOf(END_OF_HEADERS, boundaryIndex);
358
+ if (endOfHeaders === -1) {
359
+ throw Error("parseMultipart: malformed part — missing header terminator");
360
+ }
335
361
 
336
- activePart.headers = combined
337
- .subarray(boundaryIndex + boundary.length + 2, endOfHeaders).toString()
338
- .split("\r\n")
339
- .reduce((acc, val) => {
340
- const [header, value] = val.split(": ");
341
- acc[header.toLowerCase()] = value;
342
- return acc;
343
- }, {});
362
+ activePart.headers = combined
363
+ .subarray(boundaryIndex + boundary.length + 2, endOfHeaders).toString()
364
+ .split("\r\n")
365
+ .reduce((acc, val) => {
366
+ const [header, value] = val.split(": ");
367
+ acc[header.toLowerCase()] = value;
368
+ return acc;
369
+ }, Object.create(null));
344
370
 
345
- combined = combined.subarray(endOfHeaders + END_OF_HEADERS.length);
371
+ combined = combined.subarray(endOfHeaders + END_OF_HEADERS.length);
346
372
 
347
- lastChunk = combined;
348
- }
373
+ lastChunk = combined;
374
+ }
349
375
 
350
- // there's no boundary in the chunk, add it to active part
351
- if (activePart && lastChunk.length > 0 && !done) {
352
- write(lastChunk);
353
- lastChunk = Buffer.alloc(0);
376
+ // Carry the unprocessed remainder into the next read so it gets
377
+ // prepended to the next chunk. Skip when the lookbehind was already
378
+ // set inside the boundaryIndex === -1 branch above.
379
+ if (!lookbehindSet) {
380
+ lastChunk = combined;
381
+ }
354
382
  }
383
+ } catch (e) {
384
+ await cleanup();
385
+ throw e;
355
386
  }
356
387
 
357
388
  for (const part of parts) {
358
389
  // extract name and filename from content-disposition header
359
390
  if (part.headers["content-disposition"]) {
360
- const [, name] = part.headers["content-disposition"].match(/name="([^"]+)"/);
391
+ const [, name] = part.headers["content-disposition"].match(/name="([^"]+)"/) || [];
361
392
  const [, filename] = part.headers["content-disposition"].match(/filename="([^"]+)"/) || [];
362
393
  part.name = name ?? null;
363
394
  part.filename = filename ?? null;
@@ -383,24 +414,13 @@ export async function parseMultipart(req, _options) {
383
414
  }
384
415
 
385
416
  export function tempFilename() {
386
- return join(tmpdir(), `${randomSafeId(12)}.gxtmp`);
387
- }
388
-
389
- export function randomSafeId(size = 12) {
390
- const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
391
- let result = "";
392
-
393
- for (let i = 0; i < size; i++) {
394
- result += charset.charAt(Math.floor(Math.random() * charset.length));
395
- }
396
-
397
- return result;
417
+ return join(tmpdir(), `${picoid(12)}.gxtmp`);
398
418
  }
399
419
 
400
420
  export function deepMerge(target, ...source) {
401
421
  for (const src of source) {
402
- for (const key in src) {
403
- if (src[key] instanceof Object) {
422
+ for (const key of Object.keys(src)) {
423
+ if (src[key] instanceof Object && !Array.isArray(src[key])) {
404
424
  if (!target[key]) {
405
425
  target[key] = {};
406
426
  }
@@ -423,4 +443,22 @@ export function cleanupWebsocketUrl(url) {
423
443
  } catch {
424
444
  return url;
425
445
  }
426
- }
446
+ }
447
+
448
+ export async function fetchWithTimeout(url, options = {}, timeout = 500) {
449
+ const ac = new AbortController();
450
+ const timer = setTimeout(() => ac.abort(), timeout);
451
+ try {
452
+ return await fetch(url, { ...options, signal: ac.signal });
453
+ } finally {
454
+ clearTimeout(timer);
455
+ }
456
+ }
457
+
458
+ export function withTimeout(promise, timeout) {
459
+ let timeoutId;
460
+ const timer = new Promise((_, reject) => {
461
+ timeoutId = setTimeout(() => reject(Error("Timeout")), timeout);
462
+ });
463
+ return Promise.race([promise, timer]).finally(() => clearTimeout(timeoutId));
464
+ }
package/src/WebServer.js CHANGED
@@ -3,13 +3,21 @@ import express, { Router } from "express";
3
3
  import expressWs from "express-ws";
4
4
  import { createServerAtFreePort, createServerAtPort, sleep } from "./Util.js";
5
5
  import * as http from "http";
6
- import { Service } from "./Service.js";
7
6
  import * as path from "path";
8
7
  import { logger } from "./Logger.js";
9
8
  import { activeStreams } from "./Stream.js";
10
9
 
11
- export const HEALTH_CHECK_ENDPOINT = "/pA4vY7fT9oG5aI8cA4yV3qW5fP9qR1vI";
12
-
10
+ /**
11
+ * Creates an Express router that serves static files from `root`. Supports an optional URL
12
+ * prefix strip via `options.root` and falls back to `index.html` for unmatched paths when
13
+ * `options.indexOn404` is `true` (useful for single-page applications).
14
+ *
15
+ * @param {string} root - Filesystem path to the directory containing static assets.
16
+ * @param {object} [options] - Options forwarded to `express.static`, plus:
17
+ * @param {string} [options.root] - URL prefix to strip before serving.
18
+ * @param {boolean} [options.indexOn404] - If `true`, respond with `index.html` for all 404s.
19
+ * @returns {import('express').Router}
20
+ */
13
21
  export const ServeStatic = (root, options = {}) => {
14
22
  const router = Router();
15
23
  const absoluteRoot = path.resolve(root);
@@ -34,7 +42,6 @@ export const ServeStatic = (root, options = {}) => {
34
42
 
35
43
  if (options.indexOn404) {
36
44
  router.get("*", (req, res) => {
37
- logger.info(path.join(absoluteRoot, "index.html"));
38
45
  res.sendFile(path.join(absoluteRoot, "index.html"));
39
46
  });
40
47
  }
@@ -58,14 +65,15 @@ class WebServer {
58
65
  this.#started = true;
59
66
 
60
67
  let srv;
61
- if (process.env.LOCAL_PORT) {
62
- srv = await createServerAtPort(process.env.LOCAL_PORT, http, this.#app);
68
+ const localPort = process.env.GX_LOCAL_PORT || process.env.LOCAL_PORT;
69
+ if (localPort) {
70
+ srv = await createServerAtPort(localPort, http, this.#app);
63
71
  } else {
64
72
  srv = await createServerAtFreePort(http, this.#app);
65
73
  }
66
74
 
67
75
  if (!srv) {
68
- throw new Error("gx.webserver.start: unable to start");
76
+ throw Error("gx.webserver.start: unable to start");
69
77
  }
70
78
 
71
79
  this.#server = srv.server;
@@ -74,7 +82,7 @@ class WebServer {
74
82
  expressWs(this.#app, srv.server);
75
83
 
76
84
  // stream endpoint
77
- this.#app.get("/!!_____stream/:id", (req, res) => {
85
+ this.#app.get("/!!_gx/stream/:id", (req, res) => {
78
86
  const id = req.params.id;
79
87
 
80
88
  if (activeStreams[id]) {
@@ -86,8 +94,8 @@ class WebServer {
86
94
  }
87
95
  });
88
96
 
89
- this.#app.get(HEALTH_CHECK_ENDPOINT, (req, res) => {
90
- res.send({ status: "healthy", services: Service.serviceClasses });
97
+ this.#app.get("/!!_gx/health", (req, res) => {
98
+ res.send({ status: "healthy" });
91
99
  });
92
100
 
93
101
  // middleware to handle dynamic routers
package/test/context.js DELETED
@@ -1,35 +0,0 @@
1
- import { Remote, Service } from "../exports.js";
2
- import { sleep } from "../src/Util.js";
3
-
4
- class TimeService extends Service {
5
-
6
- #timestamp() {
7
- return new Date().toISOString();
8
- }
9
-
10
- getCurrentTime() {
11
- const [prefix] = this.context;
12
-
13
- return `${prefix} ${this.#timestamp()}`;
14
- }
15
-
16
- }
17
-
18
- class ApplicationService extends Service {
19
-
20
- #timeService = Remote("TimeService", "prefix");
21
-
22
- async onStart() {
23
- while (true) {
24
- const time = await this.#timeService.getCurrentTime();
25
-
26
- console.log("TIME =", time);
27
-
28
- await sleep(1000);
29
- }
30
- }
31
-
32
- }
33
-
34
- TimeService.start();
35
- ApplicationService.start();
@@ -1,24 +0,0 @@
1
- import { Gateway, Service, streamToBuffer } from "../exports.js";
2
- import { parseMultipart, sleep } from "../src/Util.js";
3
-
4
- class TestService extends Service {
5
-
6
- "GET /test/"(req, res) {
7
- res.send("Hello World");
8
- }
9
-
10
- }
11
-
12
- class Application extends Service {
13
-
14
- "GET /"(req, res) {
15
- res.send("app");
16
- }
17
-
18
- }
19
-
20
- Application.start();
21
-
22
- await sleep(3000);
23
-
24
- TestService.start();
package/test/gateway.js DELETED
@@ -1,34 +0,0 @@
1
- import { Gateway, Service, streamToBuffer } from "../exports.js";
2
- import { parseMultipart } from "../src/Util.js";
3
-
4
- // class TestService extends Service {
5
-
6
- // "GET /"(req, res) {
7
- // res.send("Hello World");
8
- // }
9
-
10
- // async "POST /upload"(req, res) {
11
- // const parts = await parseMultipart(req, { useMemory: false });
12
-
13
- // for (const part of parts) {
14
- // console.log(part.body);
15
- // }
16
-
17
- // res.send("OK");
18
- // }
19
-
20
- // }
21
-
22
- // TestService.start({
23
- // middleware: {
24
- // raw: true,
25
- // json: false,
26
- // cookies: false,
27
- // }
28
- // });
29
- // Gateway.start({
30
- // beforeRequest: (req, res) => {
31
- // res.set("X-Test", "Test");
32
- // }
33
- // });
34
- Gateway.start();
@@ -1,24 +0,0 @@
1
- import { Router } from "express";
2
- import { ServeStatic, Service } from "../exports.js";
3
-
4
- function special(req, res) {
5
- console.log("THIS:", this);
6
-
7
- res.send(`Hello ${this.value}`);
8
- };
9
-
10
- class ApplicationService extends Service {
11
-
12
- value = "World!";
13
-
14
- async onStart() {
15
- }
16
-
17
- "GET *" = [special, ServeStatic("test/static", { indexOn404: true })];
18
- // "GET *" = [(req, res) => {
19
- // res.send(`Hello ${this.value}`);
20
- // }];
21
-
22
- }
23
-
24
- ApplicationService.start();