geonix 1.20.1 → 1.20.3

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/Service.js CHANGED
@@ -1,11 +1,13 @@
1
- import { codec, connection } from "./Connection.js";
2
- import { picoid, sleep, hash, getSecondsSinceMidnight, OverlayObject, GeonixVersion, getFirstItemFromAsyncIterable } from "./Util.js";
1
+ import { connection } from "./Connection.js";
2
+ import { picoid, sleep, hash, getSecondsSinceMidnight, OverlayObject, GeonixVersion, getFirstItemFromAsyncIterable, getNetworkAddresses } from "./Util.js";
3
3
  import { webserver } from "./WebServer.js";
4
4
  import { createConnection } from "net";
5
5
  import { EOL } from "os";
6
6
  import cookieParser from "cookie-parser";
7
7
  import express from "express";
8
8
  import { isStream, streamToString } from "./Stream.js";
9
+ import { logger } from "./Logger.js";
10
+ import { decode } from "./Codec.js";
9
11
 
10
12
  const protectedMethodNames = ["constructor", "onStart"];
11
13
  const endpointMatcher = /^((?<options>.+)\|)?(?<verb>WS|SUB|GET|POST|PATCH|PUT|DELETE|HEAD|OPTIONS|ALL)\s(?<url>.*)/;
@@ -15,14 +17,33 @@ const ERROR_END_DELIMITER = "-".repeat(40);
15
17
 
16
18
  const json = express.json({ limit: "100mb" });
17
19
  const raw = express.raw({ type: "*/*", limit: "100mb" });
20
+ const cookies = cookieParser();
21
+
22
+ /**
23
+ * @typedef {Object} ServiceOptions
24
+ * @property {Object} middleware
25
+ * @property {boolean} middleware.json Enable JSON middleware
26
+ * @property {boolean} middleware.raw Enable RAW middleware
27
+ * @property {boolean} middleware.cookies Enable cookies middleware
28
+ * @property {boolean} fullBeacon Enable full beacon
29
+ */
30
+ const defaultServiceOptions = {
31
+ middleware: {
32
+ json: true,
33
+ raw: true,
34
+ cookies: true
35
+ },
36
+ fullBeacon: true
37
+ };
18
38
 
19
39
  export class Service {
20
40
 
21
41
  static serviceClasses = [];
22
42
 
23
43
  static start(options = {}) {
24
- if (!this.serviceClasses.includes(this.prototype.constructor.name))
44
+ if (!this.serviceClasses.includes(this.prototype.constructor.name)) {
25
45
  this.serviceClasses.push(this.prototype.constructor.name);
46
+ }
26
47
 
27
48
  const instance = new this();
28
49
  instance.#start(options);
@@ -31,21 +52,28 @@ export class Service {
31
52
  // ---------------------------------------------------------------------------------------------
32
53
 
33
54
  #me = {};
34
- #options = {};
55
+ #options = defaultServiceOptions;
35
56
 
36
57
  async #start(options = {}) {
37
- this.#options = options;
58
+ this.#options = { ...this.#options, ...options };
38
59
 
39
60
  await webserver.waitUntilReady();
40
61
  await connection.waitUntilReady();
41
62
 
42
- const fields = Object.getOwnPropertyNames(this).filter(isEndpointFilter).concat(Object.getOwnPropertyNames(this.constructor.prototype));
63
+ const fields = Object.getOwnPropertyNames(this)
64
+ .filter(isEndpointFilter)
65
+ .concat(Object.getOwnPropertyNames(this.constructor.prototype));
43
66
 
44
67
  // preserve order of endpoints as defined in the source
45
68
  const serviceSource = this.constructor.toString().split("\n");
46
69
  fields.sort((a, b, ia = -1, ib = -1) => {
47
- for (let line = 0; line < serviceSource.length; line++) ia = serviceSource[line].includes(a) ? line : ia;
48
- for (let line = 0; line < serviceSource.length; line++) ib = serviceSource[line].includes(b) ? line : ib;
70
+ for (let line = 0; line < serviceSource.length; line++) {
71
+ ia = serviceSource[line].includes(a) ? line : ia;
72
+ }
73
+
74
+ for (let line = 0; line < serviceSource.length; line++) {
75
+ ib = serviceSource[line].includes(b) ? line : ib;
76
+ }
49
77
  return ia - ib;
50
78
  });
51
79
 
@@ -65,7 +93,7 @@ export class Service {
65
93
  // geonix version
66
94
  gx: GeonixVersion,
67
95
  // IP addresses
68
- a: webserver.getAddresses().map(address => `${address}:${webserver.getPort()}`)
96
+ a: getNetworkAddresses().map(address => `${address}:${webserver.getPort()}`)
69
97
  };
70
98
 
71
99
  // check if method takes context as first argument
@@ -79,7 +107,7 @@ export class Service {
79
107
  this.#directListener();
80
108
  this.#webserver();
81
109
 
82
- console.log("gx.service.start", this.#me.n, this.#me.v);
110
+ logger.info("gx.service.start", this.#me.n, this.#me.v);
83
111
 
84
112
  // execute onStart method, if present
85
113
  if (this.onStart) {
@@ -93,7 +121,11 @@ export class Service {
93
121
  */
94
122
  async #beacon() {
95
123
  while (true) {
96
- connection.publish("gx2.beacon", { i: this.#me.i });
124
+ if (this.#options.fullBeacon) {
125
+ connection.publish("gx2.beacon", this.#me);
126
+ } else {
127
+ connection.publish("gx2.beacon", { i: this.#me.i });
128
+ }
97
129
  await sleep(1000);
98
130
  }
99
131
  }
@@ -106,13 +138,11 @@ export class Service {
106
138
  const subscription = await connection.subscribe(`gx2.service.${hash(identifier)}`, { queue: identifier });
107
139
 
108
140
  for await (let event of subscription) {
109
- let call = codec.decode(event.data);
141
+ let call = decode(event.data);
110
142
 
111
- if (isStream(call))
112
- call = JSON.parse(await streamToString(call));
143
+ if (isStream(call)) { call = JSON.parse(await streamToString(call)); }
113
144
 
114
- if (call.$r && call.p)
115
- this.#onCall(call.p, (json) => connection.publish(call.$r, json));
145
+ if (call.$r && call.p) { this.#onCall(call.p, (json) => connection.publish(call.$r, json)); }
116
146
  }
117
147
  }
118
148
 
@@ -124,13 +154,15 @@ export class Service {
124
154
  const subscription = await connection.subscribe(`gx2.service.${hash(identifier)}`, { queue: identifier });
125
155
 
126
156
  for await (let event of subscription) {
127
- let call = codec.decode(event.data);
157
+ let call = decode(event.data);
128
158
 
129
- if (isStream(call))
159
+ if (isStream(call)) {
130
160
  call = JSON.parse(await streamToString(call));
161
+ }
131
162
 
132
- if (call.$r && call.p)
163
+ if (call.$r && call.p) {
133
164
  this.#onCall(call.p, (json) => connection.publish(call.$r, json));
165
+ }
134
166
  }
135
167
  }
136
168
 
@@ -142,11 +174,24 @@ export class Service {
142
174
  const endpoints = this.#me.m
143
175
  .filter(isEndpointFilter);
144
176
 
145
- if (!endpoints || endpoints.length === 0)
177
+ if (!endpoints || endpoints.length === 0) {
146
178
  return;
179
+ }
147
180
 
148
181
  const router = webserver.router();
149
- router.use(json, raw, cookieParser());
182
+
183
+ // setup defualt middlewares
184
+ if (this.#options.middleware.json) {
185
+ router.use(json);
186
+ }
187
+ if (this.#options.middleware.raw) {
188
+ router.use(raw);
189
+ }
190
+ if (this.#options.middleware.cookies) {
191
+ router.use(cookies);
192
+ }
193
+
194
+ // register endpoints
150
195
  for (let endpoint of endpoints) {
151
196
  let { verb, url: uri } = endpointMatcher.exec(endpoint)?.groups || {};
152
197
  verb = verb.toLowerCase();
@@ -158,8 +203,9 @@ export class Service {
158
203
 
159
204
  handlers = [...handlersBefore, ...handlers, ...handlersAfter];
160
205
 
161
- for (let n = 0; n < handlers.length; n++)
206
+ for (let n = 0; n < handlers.length; n++) {
162
207
  handlers[n] = handlers[n].bind(this);
208
+ }
163
209
 
164
210
  switch (verb) {
165
211
  case "ws":
@@ -180,8 +226,9 @@ export class Service {
180
226
  async #sub(subject, handler) {
181
227
  const subscription = await connection.subscribe(`gx.sub.${subject}`);
182
228
  const processor = async () => {
183
- for await (const event of subscription)
229
+ for await (const event of subscription) {
184
230
  handler(event.data);
231
+ }
185
232
  };
186
233
  processor();
187
234
  }
@@ -198,13 +245,15 @@ export class Service {
198
245
  const method = this[methodName];
199
246
  let _args = args;
200
247
 
201
- if (!method)
248
+ if (!method) {
202
249
  return respond({ e: `unknown method (${this.#me.n}.${methodName})` });
250
+ }
203
251
 
204
252
  try {
205
253
  // inject context as first argument
206
- if (method.takesContext)
254
+ if (method.takesContext) {
207
255
  _args = [OverlayObject(context, { caller, me: this.#me }), ..._args];
256
+ }
208
257
 
209
258
  respond({ r: await method.apply(this, _args) });
210
259
  } catch (e) {
package/src/Stream.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import { Readable } from "stream";
2
2
  import { connection } from "./Connection.js";
3
- import { picoid, StreamChunker } from "./Util.js";
3
+ import { getFirstItemFromAsyncIterable, getNetworkAddresses, picoid, StreamChunker } from "./Util.js";
4
+ import { logger } from "./Logger.js";
5
+ import { webserver } from "./WebServer.js";
4
6
 
5
7
  const CHUNK_SIZE = 1024 * 128;
6
8
 
7
9
  export const stats = {};
10
+ export const activeStreams = {};
8
11
 
9
12
  /**
10
13
  * Converts data to stream
@@ -14,15 +17,17 @@ export const stats = {};
14
17
  * @returns
15
18
  */
16
19
  export function Stream(data, tag = "_") {
17
- if (isStream(data))
20
+ if (isStream(data)) {
18
21
  return data;
22
+ }
19
23
 
20
24
  const id = picoid();
21
25
  let readable = data;
22
26
 
23
27
  // convert Buffer or string to a Readable
24
- if (!(readable.pipe && readable.readable))
28
+ if (!(readable.pipe && readable.readable)) {
25
29
  readable = Readable.from(Buffer.from(data));
30
+ }
26
31
 
27
32
  // split the stream is smaller chunks
28
33
  const transform = StreamChunker(Math.min(connection.getMaxPayloadSize(), CHUNK_SIZE));
@@ -30,23 +35,38 @@ export function Stream(data, tag = "_") {
30
35
  readable = transform;
31
36
 
32
37
  stats[tag] = stats[tag] !== undefined ? stats[tag] + 1 : 1;
38
+ activeStreams[id] = readable;
33
39
 
34
- const controlHandler = async () => {
40
+ // NATS handler
41
+ (async () => {
35
42
  const control = await connection.subscribe(`gx2.stream.${id}.a`, { max: 1 });
36
43
 
37
- for await (const event of control)
38
- if (event.data.length === 0) {
39
- readable.on("data", chunk => connection.publishRaw(`gx2.stream.${id}.b`, chunk));
40
- readable.on("close", () => {
41
- connection.publishRaw(`gx2.stream.${id}.b`);
42
- stats[tag]--;
43
- });
44
- }
44
+ const event = await getFirstItemFromAsyncIterable(control);
45
+ if (activeStreams[id] !== undefined && event.data.length === 0) {
46
+ // remove stream from the list
47
+ delete activeStreams[id];
48
+
49
+ // kickstart the stream
50
+ readable.on("data", chunk => connection.publishRaw(`gx2.stream.${id}.b`, chunk));
51
+ readable.on("close", () => {
52
+ connection.publishRaw(`gx2.stream.${id}.b`);
53
+ stats[tag]--;
54
+ });
55
+ }
56
+ })();
57
+
58
+ const result = {
59
+ $: "stream",
60
+ id
45
61
  };
46
62
 
47
- controlHandler();
63
+ // get the port and addresses of the webserver
64
+ const addresses = webserver.getPort() ? getNetworkAddresses().map(address => `${address}:${webserver.getPort()}`) : undefined;
65
+ if (addresses) {
66
+ result.a = addresses;
67
+ }
48
68
 
49
- return { $: "stream", id };
69
+ return result;
50
70
  }
51
71
 
52
72
  export function isStream(object) {
@@ -54,23 +74,43 @@ export function isStream(object) {
54
74
  }
55
75
 
56
76
  export async function getReadable(object) {
57
- if (!isStream(object))
77
+ if (!isStream(object)) {
58
78
  return object;
79
+ }
80
+
81
+ // get stream via HTTP
82
+ if (object.a.length > 0) {
83
+ for (const address of object.a) {
84
+ try {
85
+ const uri = `http://${address}/!!_____stream/${object.id}`;
86
+ const response = await fetch(uri);
87
+
88
+ if (response.status === 200) {
89
+ return Readable.fromWeb(response.body);
90
+ }
91
+ } catch {
92
+ // ignore errors
93
+ }
94
+ }
95
+ }
59
96
 
97
+ // get stream via NATS
60
98
  const readable = new Readable({ read: () => null });
61
99
  const subscription = await connection.subscribe(`gx2.stream.${object.id}.b`);
62
100
 
63
101
  const dataHandler = async () => {
64
- for await (const event of subscription)
102
+ for await (const event of subscription) {
65
103
  try {
66
104
  if (event.data.length === 0) {
67
105
  readable.push(null);
68
106
  subscription.drain();
69
- } else
107
+ } else {
70
108
  readable.push(event.data);
109
+ }
71
110
  } catch (e) {
72
- console.error("Stream.getReadable.dataHandler.error:", e);
111
+ logger.error("Stream.getReadable.dataHandler.error:", e);
73
112
  }
113
+ }
74
114
  };
75
115
  dataHandler();
76
116
 
@@ -82,17 +122,17 @@ export async function getReadable(object) {
82
122
 
83
123
  export async function streamToBuffer(object) {
84
124
  let readable = object;
85
- if (isStream(readable))
125
+ if (isStream(readable)) {
86
126
  readable = await getReadable(readable);
127
+ }
87
128
 
88
129
  return Buffer.concat(await readable.toArray());
89
130
  }
90
131
 
91
- export async function streamToString(object) {
92
- let readable = object;
93
- if (isStream(readable))
94
- readable = await getReadable(readable);
95
-
96
- return Buffer.concat(await readable.toArray()).toString();
132
+ export async function streamToString(object, encoding) {
133
+ return streamToBuffer(object).toString(encoding);
97
134
  }
98
135
 
136
+ export async function streamToJSON(object) {
137
+ return JSON.parse(await streamToBuffer(object));
138
+ }
package/src/Util.js CHANGED
@@ -3,6 +3,7 @@ import { URL, fileURLToPath } from "url";
3
3
  import { readFile } from "fs/promises";
4
4
  import { join } from "path";
5
5
  import { Transform } from "node:stream";
6
+ import { networkInterfaces } from "os";
6
7
  import * as http from "http";
7
8
  import * as https from "https";
8
9
  import * as net from "net";
@@ -60,7 +61,6 @@ export const createServerAtPort = (port, pkg, handler) =>
60
61
  new Promise((resolve) => {
61
62
  const server = pkg.createServer(handler);
62
63
  server.on("error", (_error) => {
63
- // console.log('error', error.message)
64
64
  resolve(null);
65
65
  });
66
66
  server.listen(port, () => {
@@ -78,14 +78,16 @@ export const createServerAtPort = (port, pkg, handler) =>
78
78
  * @returns
79
79
  */
80
80
  export const createServerAtFreePort = async (pkg, handler, start = 30000, poolSize = 20000) => {
81
- for (let port = start; port < start + poolSize; port++)
81
+ for (let port = start; port < start + poolSize; port++) {
82
82
  try {
83
83
  const result = await createServerAtPort(port, pkg, handler);
84
- // console.log(port, '=', result)
85
- if (result) return result;
84
+ if (result) {
85
+ return result;
86
+ }
86
87
  } catch {
87
88
  // silenty ignore errors
88
89
  }
90
+ }
89
91
  };
90
92
 
91
93
  /**
@@ -121,10 +123,11 @@ export const getFunctionParams = (fn) => {
121
123
  const endParenthesisPosition = code.indexOf(")");
122
124
  let params;
123
125
 
124
- if (endParenthesisPosition != -1)
126
+ if (endParenthesisPosition != -1) {
125
127
  params = code.substring(code.indexOf("(") + 1, endParenthesisPosition);
126
- else
128
+ } else {
127
129
  params = code.substring(0, code.indexOf("=>"));
130
+ }
128
131
 
129
132
  params = params
130
133
  // cleanup spaces
@@ -135,8 +138,9 @@ export const getFunctionParams = (fn) => {
135
138
  // remove potential default values
136
139
  for (let index = 0; index < params.length; index++) {
137
140
  const defaultValueAssignmentPosition = params[index].indexOf("=");
138
- if (defaultValueAssignmentPosition != -1)
141
+ if (defaultValueAssignmentPosition != -1) {
139
142
  params[index] = params[index].substring(0, defaultValueAssignmentPosition - 1);
143
+ }
140
144
  }
141
145
 
142
146
  return params;
@@ -153,8 +157,10 @@ export const proxyHttp = (target, req, res) =>
153
157
  const protocol = req.protocol === "https" ? https : http;
154
158
  const proxyReq = protocol.request(remoteTarget, options, (proxyRes) => {
155
159
  res.status(proxyRes.statusCode);
156
- for (const header in proxyRes.headers)
160
+ for (const header in proxyRes.headers) {
157
161
  res.set(header, proxyRes.headers[header]);
162
+ }
163
+
158
164
  proxyRes.pipe(res);
159
165
  });
160
166
  proxyReq.on("error", (error) => reject(error));
@@ -207,4 +213,19 @@ export async function getFirstItemFromAsyncIterable(asyncIterable) {
207
213
  const iterator = asyncIterable[Symbol.asyncIterator]();
208
214
  const result = await iterator.next();
209
215
  return result.value;
216
+ }
217
+
218
+ export function getNetworkAddresses() {
219
+ const list = [];
220
+ const interfaces = networkInterfaces();
221
+
222
+ for (let interfaceAddresses of Object.values(interfaces)) {
223
+ for (let addressObject of interfaceAddresses) {
224
+ if (addressObject.family === "IPv4") {
225
+ list.push(addressObject.address);
226
+ }
227
+ }
228
+ }
229
+
230
+ return list;
210
231
  }
package/src/WebServer.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import "express-async-errors";
2
2
  import express, { Router } from "express";
3
3
  import expressWs from "express-ws";
4
- import { networkInterfaces } from "os";
5
4
  import { createServerAtFreePort, createServerAtPort, sleep } from "./Util.js";
6
5
  import * as http from "http";
7
6
  import { Service } from "./Service.js";
8
7
  import * as path from "path";
8
+ import { logger } from "./Logger.js";
9
+ import { activeStreams } from "./Stream.js";
9
10
 
10
11
  export const HEALTH_CHECK_ENDPOINT = "/pA4vY7fT9oG5aI8cA4yV3qW5fP9qR1vI";
11
12
 
@@ -16,12 +17,14 @@ export const ServeStatic = (root, options = {}) => {
16
17
  router.use((req, res, next) => {
17
18
  if (options.root) {
18
19
  // remove trailing slash
19
- if (options.root.endsWith("/"))
20
+ if (options.root.endsWith("/")) {
20
21
  options.root = options.root.slice(0, -1);
22
+ }
21
23
 
22
24
  // replace root prefix
23
- if (req.url.startsWith(options.root))
25
+ if (req.url.startsWith(options.root)) {
24
26
  req.url = req.url.replace(options.root, "");
27
+ }
25
28
  }
26
29
 
27
30
  next();
@@ -29,11 +32,12 @@ export const ServeStatic = (root, options = {}) => {
29
32
 
30
33
  router.use(express.static(root, options));
31
34
 
32
- if (options.indexOn404)
35
+ if (options.indexOn404) {
33
36
  router.get("*", (req, res) => {
34
- console.log(path.join(absoluteRoot, "index.html"));
37
+ logger.info(path.join(absoluteRoot, "index.html"));
35
38
  res.sendFile(path.join(absoluteRoot, "index.html"));
36
39
  });
40
+ }
37
41
 
38
42
  return router;
39
43
  };
@@ -47,31 +51,43 @@ class WebServer {
47
51
  #started = false;
48
52
 
49
53
  async start() {
50
- if (this.#started)
51
- return;
54
+ if (this.#started) { return; }
52
55
 
53
56
  this.#started = true;
54
57
 
55
- let port, server;
58
+ let srv;
56
59
  if (process.env.LOCAL_PORT) {
57
- ({ server, port } = await createServerAtPort(process.env.LOCAL_PORT, http, this.#app));
60
+ srv = await createServerAtPort(process.env.LOCAL_PORT, http, this.#app);
58
61
  } else {
59
- ({ server, port } = await createServerAtFreePort(http, this.#app));
62
+ srv = await createServerAtFreePort(http, this.#app);
63
+ }
64
+
65
+ if (!srv) {
66
+ throw new Error("gx.webserver.start: unable to start");
60
67
  }
61
68
 
62
- this.#server = server;
63
- this.#port = port;
69
+ this.#server = srv.server;
70
+ this.#port = srv.port;
64
71
 
65
- expressWs(this.#app, server);
72
+ expressWs(this.#app, srv.server);
73
+
74
+ // stream endpoint
75
+ this.#app.get("/!!_____stream/:id", (req, res) => {
76
+ const id = req.params.id;
77
+
78
+ if (activeStreams[id]) {
79
+ res.status(200);
80
+ activeStreams[id].pipe(res);
81
+ delete activeStreams[id];
82
+ } else {
83
+ res.status(404).send({ error: 404 });
84
+ }
85
+ });
66
86
 
67
87
  this.#app.get(HEALTH_CHECK_ENDPOINT, (req, res) => {
68
88
  res.send({ status: "healthy", services: Service.serviceClasses });
69
89
  });
70
90
 
71
- // this.#app.use((req, res, next) => {
72
- // next()
73
- // })
74
-
75
91
  // wait for 2 seconds and then set fall-through handler
76
92
  // this should provide more than enough time to start all the services
77
93
  setTimeout(() => {
@@ -88,23 +104,11 @@ class WebServer {
88
104
  this.#app.disable("x-powered-by");
89
105
  this.#app.disable("etag");
90
106
 
91
- console.log(`gx.webserver.start: listening on http://127.0.0.1:${this.#port}`);
107
+ logger.info(`gx.webserver.start: listening on http://127.0.0.1:${this.#port}`);
92
108
 
93
109
  this.#ready = true;
94
110
  }
95
111
 
96
- getAddresses() {
97
- const list = [];
98
- const interfaces = networkInterfaces();
99
-
100
- for (let interfaceAddresses of Object.values(interfaces))
101
- for (let addressObject of interfaceAddresses)
102
- if (addressObject.family === "IPv4")
103
- list.push(addressObject.address);
104
-
105
- return list;
106
- }
107
-
108
112
  getPort() {
109
113
  return this.#port;
110
114
  }
@@ -118,14 +122,15 @@ class WebServer {
118
122
  async waitUntilReady() {
119
123
  await this.start();
120
124
 
121
- while (!this.#ready)
125
+ while (!this.#ready) {
122
126
  await sleep(100);
127
+ }
123
128
  }
124
129
 
125
130
  stop() {
126
131
  if (this.#server) {
127
132
  this.#server.close();
128
- console.log("gx.webserver.stop");
133
+ logger.info("gx.webserver.stop");
129
134
  }
130
135
  }
131
136
 
@@ -0,0 +1,35 @@
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();
package/test/gateway.js CHANGED
@@ -2,14 +2,14 @@ import { Gateway, Service } from "../exports.js";
2
2
 
3
3
  class TestService extends Service {
4
4
 
5
- 'GET /'(req, res) {
6
- res.send('Hello World')
5
+ "GET /"(req, res) {
6
+ res.send("Hello World");
7
7
  }
8
8
  }
9
9
 
10
- TestService.start()
10
+ TestService.start();
11
11
  Gateway.start({
12
12
  beforeRequest: (req, res) => {
13
- res.set('X-Test', 'Test')
13
+ res.set("X-Test", "Test");
14
14
  }
15
- })
15
+ });
package/test/simple.js ADDED
@@ -0,0 +1,29 @@
1
+ import { Remote, Service } from "../exports.js";
2
+ import { sleep } from "../src/Util.js";
3
+
4
+ class TimeService extends Service {
5
+
6
+ getCurrentTime() {
7
+ return new Date().toISOString();
8
+ }
9
+
10
+ }
11
+
12
+ class ApplicationService extends Service {
13
+
14
+ #timeService = Remote("TimeService");
15
+
16
+ async onStart() {
17
+ while (true) {
18
+ const time = await this.#timeService.getCurrentTime();
19
+
20
+ console.log("TIME =", time);
21
+
22
+ await sleep(1000);
23
+ }
24
+ }
25
+
26
+ }
27
+
28
+ TimeService.start();
29
+ ApplicationService.start();