@twin.org/api-service 0.0.2-next.9 → 0.0.3-next.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.
@@ -0,0 +1,8 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export * from "./informationRoutes.js";
4
+ export * from "./informationService.js";
5
+ export * from "./models/IInformationServiceConfig.js";
6
+ export * from "./models/IInformationServiceConstructorOptions.js";
7
+ export * from "./restEntryPoints.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uCAAuC,CAAC;AACtD,cAAc,mDAAmD,CAAC;AAClE,cAAc,sBAAsB,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./informationRoutes.js\";\nexport * from \"./informationService.js\";\nexport * from \"./models/IInformationServiceConfig.js\";\nexport * from \"./models/IInformationServiceConstructorOptions.js\";\nexport * from \"./restEntryPoints.js\";\n"]}
@@ -1,11 +1,9 @@
1
- import { ComponentFactory, Is, Guards } from '@twin.org/core';
2
- import { MimeTypes, HeaderTypes, HttpStatusCode } from '@twin.org/web';
3
- import { readFile } from 'node:fs/promises';
4
-
1
+ import { ComponentFactory, Is } from "@twin.org/core";
2
+ import { HeaderTypes, HttpStatusCode, MimeTypes } from "@twin.org/web";
5
3
  /**
6
4
  * The tag to associate with the routes.
7
5
  */
8
- const tagsInformation = [
6
+ export const tagsInformation = [
9
7
  {
10
8
  name: "Info",
11
9
  description: "Information endpoints for the REST server."
@@ -17,14 +15,14 @@ const tagsInformation = [
17
15
  * @param componentName The name of the component to use in the routes stored in the ComponentFactory.
18
16
  * @returns The generated routes.
19
17
  */
20
- function generateRestRoutesInformation(baseRouteName, componentName) {
18
+ export function generateRestRoutesInformation(baseRouteName, componentName) {
21
19
  const rootRoute = {
22
20
  operationId: "serverRoot",
23
21
  summary: "Get the root text page",
24
22
  tag: tagsInformation[0].name,
25
23
  method: "GET",
26
24
  path: `${baseRouteName}/`,
27
- handler: async (httpRequestContext, request) => serverRoot(httpRequestContext, componentName),
25
+ handler: async (httpRequestContext, request) => serverRoot(httpRequestContext, componentName, request),
28
26
  responseType: [
29
27
  {
30
28
  type: "IServerRootResponse",
@@ -48,7 +46,7 @@ function generateRestRoutesInformation(baseRouteName, componentName) {
48
46
  tag: tagsInformation[0].name,
49
47
  method: "GET",
50
48
  path: `${baseRouteName}/info`,
51
- handler: async (httpRequestContext, request) => serverInfo(httpRequestContext, componentName),
49
+ handler: async (httpRequestContext, request) => serverInfo(httpRequestContext, componentName, request),
52
50
  responseType: [
53
51
  {
54
52
  type: "IServerInfoResponse",
@@ -74,7 +72,7 @@ function generateRestRoutesInformation(baseRouteName, componentName) {
74
72
  tag: tagsInformation[0].name,
75
73
  method: "GET",
76
74
  path: `${baseRouteName}/favicon.ico`,
77
- handler: async (httpRequestContext, request) => serverFavIcon(httpRequestContext, componentName),
75
+ handler: async (httpRequestContext, request) => serverFavIcon(httpRequestContext, componentName, request),
78
76
  responseType: [
79
77
  {
80
78
  type: "IServerFavIconResponse",
@@ -89,7 +87,7 @@ function generateRestRoutesInformation(baseRouteName, componentName) {
89
87
  tag: tagsInformation[0].name,
90
88
  method: "GET",
91
89
  path: `${baseRouteName}/health`,
92
- handler: async (httpRequestContext, request) => serverHealth(httpRequestContext, componentName),
90
+ handler: async (httpRequestContext, request) => serverHealth(httpRequestContext, componentName, request),
93
91
  responseType: [
94
92
  {
95
93
  type: "IServerHealthResponse",
@@ -164,7 +162,7 @@ function generateRestRoutesInformation(baseRouteName, componentName) {
164
162
  tag: tagsInformation[0].name,
165
163
  method: "GET",
166
164
  path: `${baseRouteName}/spec`,
167
- handler: async (httpRequestContext, request) => serverSpec(httpRequestContext, componentName),
165
+ handler: async (httpRequestContext, request) => serverSpec(httpRequestContext, componentName, request),
168
166
  responseType: [
169
167
  {
170
168
  type: "IServerSpecResponse",
@@ -194,7 +192,7 @@ function generateRestRoutesInformation(baseRouteName, componentName) {
194
192
  * @param request The request.
195
193
  * @returns The response object with additional http response properties.
196
194
  */
197
- async function serverRoot(httpRequestContext, componentName, request) {
195
+ export async function serverRoot(httpRequestContext, componentName, request) {
198
196
  const component = ComponentFactory.get(componentName);
199
197
  return {
200
198
  body: await component.root()
@@ -207,7 +205,7 @@ async function serverRoot(httpRequestContext, componentName, request) {
207
205
  * @param request The request.
208
206
  * @returns The response object with additional http response properties.
209
207
  */
210
- async function serverInfo(httpRequestContext, componentName, request) {
208
+ export async function serverInfo(httpRequestContext, componentName, request) {
211
209
  const component = ComponentFactory.get(componentName);
212
210
  return {
213
211
  body: await component.info()
@@ -220,7 +218,7 @@ async function serverInfo(httpRequestContext, componentName, request) {
220
218
  * @param request The request.
221
219
  * @returns The response object with additional http response properties.
222
220
  */
223
- async function serverHealth(httpRequestContext, componentName, request) {
221
+ export async function serverHealth(httpRequestContext, componentName, request) {
224
222
  const component = ComponentFactory.get(componentName);
225
223
  return {
226
224
  body: await component.health()
@@ -233,7 +231,7 @@ async function serverHealth(httpRequestContext, componentName, request) {
233
231
  * @param request The request.
234
232
  * @returns The response object with additional http response properties.
235
233
  */
236
- async function serverFavIcon(httpRequestContext, componentName, request) {
234
+ export async function serverFavIcon(httpRequestContext, componentName, request) {
237
235
  const component = ComponentFactory.get(componentName);
238
236
  const favIcon = await component.favicon();
239
237
  if (Is.uint8Array(favIcon)) {
@@ -255,7 +253,7 @@ async function serverFavIcon(httpRequestContext, componentName, request) {
255
253
  * @param request The request.
256
254
  * @returns The response object with additional http response properties.
257
255
  */
258
- async function serverSpec(httpRequestContext, componentName, request) {
256
+ export async function serverSpec(httpRequestContext, componentName, request) {
259
257
  const component = ComponentFactory.get(componentName);
260
258
  const spec = await component.spec();
261
259
  if (Is.objectValue(spec)) {
@@ -267,171 +265,4 @@ async function serverSpec(httpRequestContext, componentName, request) {
267
265
  statusCode: HttpStatusCode.notFound
268
266
  };
269
267
  }
270
-
271
- // Copyright 2024 IOTA Stiftung.
272
- // SPDX-License-Identifier: Apache-2.0.
273
- /**
274
- * The information service for the server.
275
- */
276
- class InformationService {
277
- /**
278
- * Runtime name for the class.
279
- */
280
- CLASS_NAME = "InformationService";
281
- /**
282
- * The server information.
283
- * @internal
284
- */
285
- _serverInfo;
286
- /**
287
- * The server health.
288
- * @internal
289
- */
290
- _healthInfo;
291
- /**
292
- * The path to the favicon Spec.
293
- * @internal
294
- */
295
- _faviconPath;
296
- /**
297
- * The favicon.
298
- * @internal
299
- */
300
- _favicon;
301
- /**
302
- * The path to the OpenAPI Spec.
303
- * @internal
304
- */
305
- _openApiSpecPath;
306
- /**
307
- * The OpenAPI spec.
308
- * @internal
309
- */
310
- _openApiSpec;
311
- /**
312
- * Create a new instance of InformationService.
313
- * @param options The options to create the service.
314
- */
315
- constructor(options) {
316
- Guards.object(this.CLASS_NAME, "options", options);
317
- Guards.object(this.CLASS_NAME, "options.config", options.config);
318
- Guards.object(this.CLASS_NAME, "options.config.serverInfo", options.config.serverInfo);
319
- this._serverInfo = options.config.serverInfo;
320
- this._healthInfo = {
321
- status: "ok"
322
- };
323
- this._faviconPath = options.config.favIconPath;
324
- this._openApiSpecPath = options.config.openApiSpecPath;
325
- }
326
- /**
327
- * The service needs to be started when the application is initialized.
328
- * @returns Nothing.
329
- */
330
- async start() {
331
- const openApiPath = this._openApiSpecPath;
332
- if (Is.stringValue(openApiPath)) {
333
- const contentBuffer = await readFile(openApiPath, "utf8");
334
- this._openApiSpec = JSON.parse(contentBuffer);
335
- }
336
- const favIconPath = this._faviconPath;
337
- if (Is.stringValue(favIconPath)) {
338
- this._favicon = await readFile(favIconPath);
339
- }
340
- }
341
- /**
342
- * Get the root information.
343
- * @returns The root information.
344
- */
345
- async root() {
346
- return `${this._serverInfo.name} - ${this._serverInfo.version}`;
347
- }
348
- /**
349
- * Get the server information.
350
- * @returns The service information.
351
- */
352
- async info() {
353
- return this._serverInfo;
354
- }
355
- /**
356
- * Get the favicon.
357
- * @returns The favicon.
358
- */
359
- async favicon() {
360
- return this._favicon;
361
- }
362
- /**
363
- * Get the OpenAPI spec.
364
- * @returns The OpenAPI spec.
365
- */
366
- async spec() {
367
- return this._openApiSpec;
368
- }
369
- /**
370
- * Get the server health.
371
- * @returns The service health.
372
- */
373
- async health() {
374
- let errorCount = 0;
375
- let warningCount = 0;
376
- if (Is.arrayValue(this._healthInfo.components)) {
377
- errorCount = this._healthInfo.components.filter(c => c.status === "error").length;
378
- warningCount = this._healthInfo.components.filter(c => c.status === "warning").length;
379
- }
380
- if (errorCount > 0) {
381
- this._healthInfo.status = "error";
382
- }
383
- else if (warningCount > 0) {
384
- this._healthInfo.status = "warning";
385
- }
386
- else {
387
- this._healthInfo.status = "ok";
388
- }
389
- return this._healthInfo;
390
- }
391
- /**
392
- * Set the status of a component.
393
- * @param name The component name.
394
- * @param status The status of the component.
395
- * @param details The details for the status.
396
- * @returns Nothing.
397
- */
398
- async setComponentHealth(name, status, details) {
399
- const component = this._healthInfo.components?.find(c => c.name === name);
400
- if (Is.undefined(component)) {
401
- this._healthInfo.components ??= [];
402
- this._healthInfo.components.push({
403
- name,
404
- status,
405
- details
406
- });
407
- }
408
- else {
409
- component.status = status;
410
- component.details = details;
411
- }
412
- }
413
- /**
414
- * Remove the status of a component.
415
- * @param name The component name.
416
- * @returns Nothing.
417
- */
418
- async removeComponentHealth(name) {
419
- if (Is.arrayValue(this._healthInfo.components)) {
420
- const componentIndex = this._healthInfo.components.findIndex(c => c.name === name);
421
- if (componentIndex !== -1) {
422
- this._healthInfo.components.splice(componentIndex, 1);
423
- }
424
- }
425
- }
426
- }
427
-
428
- const restEntryPoints = [
429
- {
430
- name: "information",
431
- defaultBaseRoute: "",
432
- tags: tagsInformation,
433
- generateRoutes: generateRestRoutesInformation
434
- }
435
- ];
436
-
437
- export { InformationService, generateRestRoutesInformation, restEntryPoints, serverFavIcon, serverHealth, serverInfo, serverRoot, serverSpec, tagsInformation };
268
+ //# sourceMappingURL=informationRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"informationRoutes.js","sourceRoot":"","sources":["../../src/informationRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIHttpRequestContext,\n\tIInformationComponent,\n\tINoContentRequest,\n\tIRestRoute,\n\tIServerFavIconResponse,\n\tIServerHealthResponse,\n\tIServerInfoResponse,\n\tIServerRootResponse,\n\tIServerSpecResponse,\n\tITag\n} from \"@twin.org/api-models\";\nimport { ComponentFactory, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HeaderTypes, HttpStatusCode, MimeTypes } from \"@twin.org/web\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsInformation: ITag[] = [\n\t{\n\t\tname: \"Info\",\n\t\tdescription: \"Information endpoints for the REST server.\"\n\t}\n];\n\n/**\n * The REST routes for server information.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesInformation(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst rootRoute: IRestRoute = {\n\t\toperationId: \"serverRoot\",\n\t\tsummary: \"Get the root text page\",\n\t\ttag: tagsInformation[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tserverRoot(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IServerRootResponse>(),\n\t\t\t\tmimeType: MimeTypes.PlainText,\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"serverRootResponse\",\n\t\t\t\t\t\tdescription: \"The response for the root request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: \"API Server - 1.0.0\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst informationRoute: IRestRoute<INoContentRequest, IServerInfoResponse> = {\n\t\toperationId: \"serverInformation\",\n\t\tsummary: \"Get the information for the server\",\n\t\ttag: tagsInformation[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/info`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tserverInfo(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IServerInfoResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"informationResponse\",\n\t\t\t\t\t\tdescription: \"The response for the information request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\tname: \"API Server\",\n\t\t\t\t\t\t\t\tversion: \"1.0.0\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst favIconRoute: IRestRoute<INoContentRequest, IServerFavIconResponse> = {\n\t\toperationId: \"serverFavIcon\",\n\t\tsummary: \"Get the favicon for the server\",\n\t\ttag: tagsInformation[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/favicon.ico`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tserverFavIcon(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IServerFavIconResponse>(),\n\t\t\t\tmimeType: \"image/x-icon\"\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst healthRoute: IRestRoute<INoContentRequest, IServerHealthResponse> = {\n\t\toperationId: \"serverHealth\",\n\t\tsummary: \"Get the health for the server\",\n\t\ttag: tagsInformation[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/health`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tserverHealth(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IServerHealthResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"healthResponseOK\",\n\t\t\t\t\t\tdescription: \"The response for the health request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\tstatus: \"ok\",\n\t\t\t\t\t\t\t\tcomponents: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: \"Database\",\n\t\t\t\t\t\t\t\t\t\tstatus: \"ok\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: \"Storage\",\n\t\t\t\t\t\t\t\t\t\tstatus: \"ok\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"healthResponseWarning\",\n\t\t\t\t\t\tdescription: \"The response for the health request with warnings.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\tstatus: \"warning\",\n\t\t\t\t\t\t\t\tcomponents: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: \"Database\",\n\t\t\t\t\t\t\t\t\t\tstatus: \"warning\",\n\t\t\t\t\t\t\t\t\t\tdetails: \"The database is running slow.\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: \"Storage\",\n\t\t\t\t\t\t\t\t\t\tstatus: \"ok\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"healthResponseError\",\n\t\t\t\t\t\tdescription: \"The response for the health request with errors.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\t\t\tcomponents: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: \"Database\",\n\t\t\t\t\t\t\t\t\t\tstatus: \"ok\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: \"Storage\",\n\t\t\t\t\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\t\t\t\t\tdetails: \"The storage is full.\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst specRoute: IRestRoute<INoContentRequest, IServerSpecResponse> = {\n\t\toperationId: \"serverSpec\",\n\t\tsummary: \"Get the OpenAPI specification for the endpoints\",\n\t\ttag: tagsInformation[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/spec`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tserverSpec(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IServerSpecResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"specResponse\",\n\t\t\t\t\t\tdescription: \"The response for the spec request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\topenapi: \"3.1.0\",\n\t\t\t\t\t\t\t\tinfo: {},\n\t\t\t\t\t\t\t\tpaths: {}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\treturn [rootRoute, favIconRoute, informationRoute, healthRoute, specRoute];\n}\n\n/**\n * Get the root for the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function serverRoot(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: INoContentRequest\n): Promise<IServerRootResponse> {\n\tconst component = ComponentFactory.get<IInformationComponent>(componentName);\n\treturn {\n\t\tbody: await component.root()\n\t};\n}\n\n/**\n * Get the information for the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function serverInfo(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: INoContentRequest\n): Promise<IServerInfoResponse> {\n\tconst component = ComponentFactory.get<IInformationComponent>(componentName);\n\treturn {\n\t\tbody: await component.info()\n\t};\n}\n\n/**\n * Get the health for the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function serverHealth(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: INoContentRequest\n): Promise<IServerHealthResponse> {\n\tconst component = ComponentFactory.get<IInformationComponent>(componentName);\n\treturn {\n\t\tbody: await component.health()\n\t};\n}\n\n/**\n * Get the favicon for the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function serverFavIcon(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: INoContentRequest\n): Promise<IServerFavIconResponse> {\n\tconst component = ComponentFactory.get<IInformationComponent>(componentName);\n\tconst favIcon = await component.favicon();\n\n\tif (Is.uint8Array(favIcon)) {\n\t\treturn {\n\t\t\theaders: {\n\t\t\t\t[HeaderTypes.ContentType]: \"image/x-icon\"\n\t\t\t},\n\t\t\tbody: favIcon\n\t\t};\n\t}\n\treturn {\n\t\tstatusCode: HttpStatusCode.notFound\n\t};\n}\n\n/**\n * Get the spec for the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function serverSpec(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: INoContentRequest\n): Promise<IServerSpecResponse> {\n\tconst component = ComponentFactory.get<IInformationComponent>(componentName);\n\tconst spec = await component.spec();\n\n\tif (Is.objectValue(spec)) {\n\t\treturn {\n\t\t\tbody: spec\n\t\t};\n\t}\n\treturn {\n\t\tstatusCode: HttpStatusCode.notFound\n\t};\n}\n"]}
@@ -0,0 +1,166 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { readFile } from "node:fs/promises";
4
+ import { Guards, Is } from "@twin.org/core";
5
+ /**
6
+ * The information service for the server.
7
+ */
8
+ export class InformationService {
9
+ /**
10
+ * Runtime name for the class.
11
+ */
12
+ static CLASS_NAME = "InformationService";
13
+ /**
14
+ * The server information.
15
+ * @internal
16
+ */
17
+ _serverInfo;
18
+ /**
19
+ * The server health.
20
+ * @internal
21
+ */
22
+ _healthInfo;
23
+ /**
24
+ * The path to the favicon Spec.
25
+ * @internal
26
+ */
27
+ _faviconPath;
28
+ /**
29
+ * The favicon.
30
+ * @internal
31
+ */
32
+ _favicon;
33
+ /**
34
+ * The path to the OpenAPI Spec.
35
+ * @internal
36
+ */
37
+ _openApiSpecPath;
38
+ /**
39
+ * The OpenAPI spec.
40
+ * @internal
41
+ */
42
+ _openApiSpec;
43
+ /**
44
+ * Create a new instance of InformationService.
45
+ * @param options The options to create the service.
46
+ */
47
+ constructor(options) {
48
+ Guards.object(InformationService.CLASS_NAME, "options", options);
49
+ Guards.object(InformationService.CLASS_NAME, "options.config", options.config);
50
+ Guards.object(InformationService.CLASS_NAME, "options.config.serverInfo", options.config.serverInfo);
51
+ this._serverInfo = options.config.serverInfo;
52
+ this._healthInfo = {
53
+ status: "ok"
54
+ };
55
+ this._faviconPath = options.config.favIconPath;
56
+ this._openApiSpecPath = options.config.openApiSpecPath;
57
+ }
58
+ /**
59
+ * Returns the class name of the component.
60
+ * @returns The class name of the component.
61
+ */
62
+ className() {
63
+ return InformationService.CLASS_NAME;
64
+ }
65
+ /**
66
+ * The service needs to be started when the application is initialized.
67
+ * @returns Nothing.
68
+ */
69
+ async start() {
70
+ const openApiPath = this._openApiSpecPath;
71
+ if (Is.stringValue(openApiPath)) {
72
+ const contentBuffer = await readFile(openApiPath, "utf8");
73
+ this._openApiSpec = JSON.parse(contentBuffer);
74
+ }
75
+ const favIconPath = this._faviconPath;
76
+ if (Is.stringValue(favIconPath)) {
77
+ this._favicon = await readFile(favIconPath);
78
+ }
79
+ }
80
+ /**
81
+ * Get the root information.
82
+ * @returns The root information.
83
+ */
84
+ async root() {
85
+ return `${this._serverInfo.name} - ${this._serverInfo.version}`;
86
+ }
87
+ /**
88
+ * Get the server information.
89
+ * @returns The service information.
90
+ */
91
+ async info() {
92
+ return this._serverInfo;
93
+ }
94
+ /**
95
+ * Get the favicon.
96
+ * @returns The favicon.
97
+ */
98
+ async favicon() {
99
+ return this._favicon;
100
+ }
101
+ /**
102
+ * Get the OpenAPI spec.
103
+ * @returns The OpenAPI spec.
104
+ */
105
+ async spec() {
106
+ return this._openApiSpec;
107
+ }
108
+ /**
109
+ * Get the server health.
110
+ * @returns The service health.
111
+ */
112
+ async health() {
113
+ let errorCount = 0;
114
+ let warningCount = 0;
115
+ if (Is.arrayValue(this._healthInfo.components)) {
116
+ errorCount = this._healthInfo.components.filter(c => c.status === "error").length;
117
+ warningCount = this._healthInfo.components.filter(c => c.status === "warning").length;
118
+ }
119
+ if (errorCount > 0) {
120
+ this._healthInfo.status = "error";
121
+ }
122
+ else if (warningCount > 0) {
123
+ this._healthInfo.status = "warning";
124
+ }
125
+ else {
126
+ this._healthInfo.status = "ok";
127
+ }
128
+ return this._healthInfo;
129
+ }
130
+ /**
131
+ * Set the status of a component.
132
+ * @param name The component name.
133
+ * @param status The status of the component.
134
+ * @param details The details for the status.
135
+ * @returns Nothing.
136
+ */
137
+ async setComponentHealth(name, status, details) {
138
+ const component = this._healthInfo.components?.find(c => c.name === name);
139
+ if (Is.undefined(component)) {
140
+ this._healthInfo.components ??= [];
141
+ this._healthInfo.components.push({
142
+ name,
143
+ status,
144
+ details
145
+ });
146
+ }
147
+ else {
148
+ component.status = status;
149
+ component.details = details;
150
+ }
151
+ }
152
+ /**
153
+ * Remove the status of a component.
154
+ * @param name The component name.
155
+ * @returns Nothing.
156
+ */
157
+ async removeComponentHealth(name) {
158
+ if (Is.arrayValue(this._healthInfo.components)) {
159
+ const componentIndex = this._healthInfo.components.findIndex(c => c.name === name);
160
+ if (componentIndex !== -1) {
161
+ this._healthInfo.components.splice(componentIndex, 1);
162
+ }
163
+ }
164
+ }
165
+ }
166
+ //# sourceMappingURL=informationService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"informationService.js","sourceRoot":"","sources":["../../src/informationService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { readFile } from \"node:fs/promises\";\nimport type {\n\tHealthStatus,\n\tIHealthInfo,\n\tIInformationComponent,\n\tIServerInfo\n} from \"@twin.org/api-models\";\nimport { Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IInformationServiceConstructorOptions } from \"./models/IInformationServiceConstructorOptions.js\";\n\n/**\n * The information service for the server.\n */\nexport class InformationService implements IInformationComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<InformationService>();\n\n\t/**\n\t * The server information.\n\t * @internal\n\t */\n\tprivate readonly _serverInfo: IServerInfo;\n\n\t/**\n\t * The server health.\n\t * @internal\n\t */\n\tprivate readonly _healthInfo: IHealthInfo;\n\n\t/**\n\t * The path to the favicon Spec.\n\t * @internal\n\t */\n\tprivate readonly _faviconPath?: string;\n\n\t/**\n\t * The favicon.\n\t * @internal\n\t */\n\tprivate _favicon?: Uint8Array;\n\n\t/**\n\t * The path to the OpenAPI Spec.\n\t * @internal\n\t */\n\tprivate readonly _openApiSpecPath?: string;\n\n\t/**\n\t * The OpenAPI spec.\n\t * @internal\n\t */\n\tprivate _openApiSpec?: string;\n\n\t/**\n\t * Create a new instance of InformationService.\n\t * @param options The options to create the service.\n\t */\n\tconstructor(options: IInformationServiceConstructorOptions) {\n\t\tGuards.object(InformationService.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(InformationService.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.object(\n\t\t\tInformationService.CLASS_NAME,\n\t\t\tnameof(options.config.serverInfo),\n\t\t\toptions.config.serverInfo\n\t\t);\n\n\t\tthis._serverInfo = options.config.serverInfo;\n\t\tthis._healthInfo = {\n\t\t\tstatus: \"ok\"\n\t\t};\n\t\tthis._faviconPath = options.config.favIconPath;\n\t\tthis._openApiSpecPath = options.config.openApiSpecPath;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn InformationService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @returns Nothing.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tconst openApiPath = this._openApiSpecPath;\n\t\tif (Is.stringValue(openApiPath)) {\n\t\t\tconst contentBuffer = await readFile(openApiPath, \"utf8\");\n\t\t\tthis._openApiSpec = JSON.parse(contentBuffer);\n\t\t}\n\n\t\tconst favIconPath = this._faviconPath;\n\t\tif (Is.stringValue(favIconPath)) {\n\t\t\tthis._favicon = await readFile(favIconPath);\n\t\t}\n\t}\n\n\t/**\n\t * Get the root information.\n\t * @returns The root information.\n\t */\n\tpublic async root(): Promise<string> {\n\t\treturn `${this._serverInfo.name} - ${this._serverInfo.version}`;\n\t}\n\n\t/**\n\t * Get the server information.\n\t * @returns The service information.\n\t */\n\tpublic async info(): Promise<IServerInfo> {\n\t\treturn this._serverInfo;\n\t}\n\n\t/**\n\t * Get the favicon.\n\t * @returns The favicon.\n\t */\n\tpublic async favicon(): Promise<Uint8Array | undefined> {\n\t\treturn this._favicon;\n\t}\n\n\t/**\n\t * Get the OpenAPI spec.\n\t * @returns The OpenAPI spec.\n\t */\n\tpublic async spec(): Promise<unknown> {\n\t\treturn this._openApiSpec;\n\t}\n\n\t/**\n\t * Get the server health.\n\t * @returns The service health.\n\t */\n\tpublic async health(): Promise<IHealthInfo> {\n\t\tlet errorCount = 0;\n\t\tlet warningCount = 0;\n\n\t\tif (Is.arrayValue(this._healthInfo.components)) {\n\t\t\terrorCount = this._healthInfo.components.filter(c => c.status === \"error\").length;\n\t\t\twarningCount = this._healthInfo.components.filter(c => c.status === \"warning\").length;\n\t\t}\n\n\t\tif (errorCount > 0) {\n\t\t\tthis._healthInfo.status = \"error\";\n\t\t} else if (warningCount > 0) {\n\t\t\tthis._healthInfo.status = \"warning\";\n\t\t} else {\n\t\t\tthis._healthInfo.status = \"ok\";\n\t\t}\n\n\t\treturn this._healthInfo;\n\t}\n\n\t/**\n\t * Set the status of a component.\n\t * @param name The component name.\n\t * @param status The status of the component.\n\t * @param details The details for the status.\n\t * @returns Nothing.\n\t */\n\tpublic async setComponentHealth(\n\t\tname: string,\n\t\tstatus: HealthStatus,\n\t\tdetails?: string\n\t): Promise<void> {\n\t\tconst component = this._healthInfo.components?.find(c => c.name === name);\n\n\t\tif (Is.undefined(component)) {\n\t\t\tthis._healthInfo.components ??= [];\n\t\t\tthis._healthInfo.components.push({\n\t\t\t\tname,\n\t\t\t\tstatus,\n\t\t\t\tdetails\n\t\t\t});\n\t\t} else {\n\t\t\tcomponent.status = status;\n\t\t\tcomponent.details = details;\n\t\t}\n\t}\n\n\t/**\n\t * Remove the status of a component.\n\t * @param name The component name.\n\t * @returns Nothing.\n\t */\n\tpublic async removeComponentHealth(name: string): Promise<void> {\n\t\tif (Is.arrayValue(this._healthInfo.components)) {\n\t\t\tconst componentIndex = this._healthInfo.components.findIndex(c => c.name === name);\n\t\t\tif (componentIndex !== -1) {\n\t\t\t\tthis._healthInfo.components.splice(componentIndex, 1);\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IInformationServiceConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IInformationServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IInformationServiceConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IServerInfo } from \"@twin.org/api-models\";\n\n/**\n * Configuration for the information service.\n */\nexport interface IInformationServiceConfig {\n\t/**\n\t * The server information.\n\t */\n\tserverInfo: IServerInfo;\n\n\t/**\n\t * The path to the OpenAPI Spec.\n\t */\n\topenApiSpecPath?: string;\n\n\t/**\n\t * The path to the favicon.\n\t */\n\tfavIconPath?: string;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IInformationServiceConstructorOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IInformationServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IInformationServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IInformationServiceConfig } from \"./IInformationServiceConfig.js\";\n\n/**\n * Options for the InformationService constructor.\n */\nexport interface IInformationServiceConstructorOptions {\n\t/**\n\t * The configuration for the service.\n\t */\n\tconfig: IInformationServiceConfig;\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { generateRestRoutesInformation, tagsInformation } from "./informationRoutes.js";
2
+ export const restEntryPoints = [
3
+ {
4
+ name: "information",
5
+ defaultBaseRoute: "",
6
+ tags: tagsInformation,
7
+ generateRoutes: generateRestRoutesInformation
8
+ }
9
+ ];
10
+ //# sourceMappingURL=restEntryPoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restEntryPoints.js","sourceRoot":"","sources":["../../src/restEntryPoints.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAExF,MAAM,CAAC,MAAM,eAAe,GAA2B;IACtD;QACC,IAAI,EAAE,aAAa;QACnB,gBAAgB,EAAE,EAAE;QACpB,IAAI,EAAE,eAAe;QACrB,cAAc,EAAE,6BAA6B;KAC7C;CACD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IRestRouteEntryPoint } from \"@twin.org/api-models\";\nimport { generateRestRoutesInformation, tagsInformation } from \"./informationRoutes.js\";\n\nexport const restEntryPoints: IRestRouteEntryPoint[] = [\n\t{\n\t\tname: \"information\",\n\t\tdefaultBaseRoute: \"\",\n\t\ttags: tagsInformation,\n\t\tgenerateRoutes: generateRestRoutesInformation\n\t}\n];\n"]}
@@ -1,5 +1,5 @@
1
- export * from "./informationRoutes";
2
- export * from "./informationService";
3
- export * from "./models/IInformationServiceConfig";
4
- export * from "./models/IInformationServiceConstructorOptions";
5
- export * from "./restEntryPoints";
1
+ export * from "./informationRoutes.js";
2
+ export * from "./informationService.js";
3
+ export * from "./models/IInformationServiceConfig.js";
4
+ export * from "./models/IInformationServiceConstructorOptions.js";
5
+ export * from "./restEntryPoints.js";
@@ -1,5 +1,5 @@
1
1
  import type { HealthStatus, IHealthInfo, IInformationComponent, IServerInfo } from "@twin.org/api-models";
2
- import type { IInformationServiceConstructorOptions } from "./models/IInformationServiceConstructorOptions";
2
+ import type { IInformationServiceConstructorOptions } from "./models/IInformationServiceConstructorOptions.js";
3
3
  /**
4
4
  * The information service for the server.
5
5
  */
@@ -7,12 +7,17 @@ export declare class InformationService implements IInformationComponent {
7
7
  /**
8
8
  * Runtime name for the class.
9
9
  */
10
- readonly CLASS_NAME: string;
10
+ static readonly CLASS_NAME: string;
11
11
  /**
12
12
  * Create a new instance of InformationService.
13
13
  * @param options The options to create the service.
14
14
  */
15
15
  constructor(options: IInformationServiceConstructorOptions);
16
+ /**
17
+ * Returns the class name of the component.
18
+ * @returns The class name of the component.
19
+ */
20
+ className(): string;
16
21
  /**
17
22
  * The service needs to be started when the application is initialized.
18
23
  * @returns Nothing.
@@ -1,4 +1,4 @@
1
- import type { IInformationServiceConfig } from "./IInformationServiceConfig";
1
+ import type { IInformationServiceConfig } from "./IInformationServiceConfig.js";
2
2
  /**
3
3
  * Options for the InformationService constructor.
4
4
  */
package/docs/changelog.md CHANGED
@@ -1,5 +1,97 @@
1
1
  # @twin.org/api-service - Changelog
2
2
 
3
+ ## [0.0.3-next.2](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.1...api-service-v0.0.3-next.2) (2025-11-12)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **api-service:** Synchronize repo versions
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/api-models bumped from 0.0.3-next.1 to 0.0.3-next.2
16
+
17
+ ## [0.0.3-next.1](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.0...api-service-v0.0.3-next.1) (2025-11-10)
18
+
19
+
20
+ ### Features
21
+
22
+ * add context id features ([#42](https://github.com/twinfoundation/api/issues/42)) ([0186055](https://github.com/twinfoundation/api/commit/0186055c48afde842a4254b4df9ac9249c40fe40))
23
+ * add root, favicon routes ([71da1c3](https://github.com/twinfoundation/api/commit/71da1c3a93c349588aff7084d1d8d6a29a277da8))
24
+ * add validate-locales ([cdba610](https://github.com/twinfoundation/api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e))
25
+ * eslint migration to flat config ([0dd5820](https://github.com/twinfoundation/api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805))
26
+ * remove unused namespace ([08478f2](https://github.com/twinfoundation/api/commit/08478f27efda9beb0271fdb22f6972e918361965))
27
+ * update dependencies ([1171dc4](https://github.com/twinfoundation/api/commit/1171dc416a9481737f6a640e3cf30145768f37e9))
28
+ * update framework core ([d8eebf2](https://github.com/twinfoundation/api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa))
29
+ * update IComponent signatures ([915ce37](https://github.com/twinfoundation/api/commit/915ce37712326ab4aa6869c350eabaa4622e8430))
30
+ * use shared store mechanism ([#19](https://github.com/twinfoundation/api/issues/19)) ([32116df](https://github.com/twinfoundation/api/commit/32116df3b4380a30137f5056f242a5c99afa2df9))
31
+
32
+
33
+ ### Dependencies
34
+
35
+ * The following workspace dependencies were updated
36
+ * dependencies
37
+ * @twin.org/api-models bumped from 0.0.3-next.0 to 0.0.3-next.1
38
+
39
+ ## [0.0.2-next.13](https://github.com/twinfoundation/api/compare/api-service-v0.0.2-next.12...api-service-v0.0.2-next.13) (2025-10-09)
40
+
41
+
42
+ ### Miscellaneous Chores
43
+
44
+ * **api-service:** Synchronize repo versions
45
+
46
+
47
+ ### Dependencies
48
+
49
+ * The following workspace dependencies were updated
50
+ * dependencies
51
+ * @twin.org/api-models bumped from 0.0.2-next.12 to 0.0.2-next.13
52
+
53
+ ## [0.0.2-next.12](https://github.com/twinfoundation/api/compare/api-service-v0.0.2-next.11...api-service-v0.0.2-next.12) (2025-10-09)
54
+
55
+
56
+ ### Features
57
+
58
+ * add validate-locales ([cdba610](https://github.com/twinfoundation/api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e))
59
+
60
+
61
+ ### Dependencies
62
+
63
+ * The following workspace dependencies were updated
64
+ * dependencies
65
+ * @twin.org/api-models bumped from 0.0.2-next.11 to 0.0.2-next.12
66
+
67
+ ## [0.0.2-next.11](https://github.com/twinfoundation/api/compare/api-service-v0.0.2-next.10...api-service-v0.0.2-next.11) (2025-09-29)
68
+
69
+
70
+ ### Features
71
+
72
+ * update IComponent signatures ([915ce37](https://github.com/twinfoundation/api/commit/915ce37712326ab4aa6869c350eabaa4622e8430))
73
+
74
+
75
+ ### Dependencies
76
+
77
+ * The following workspace dependencies were updated
78
+ * dependencies
79
+ * @twin.org/api-models bumped from 0.0.2-next.10 to 0.0.2-next.11
80
+
81
+ ## [0.0.2-next.10](https://github.com/twinfoundation/api/compare/api-service-v0.0.2-next.9...api-service-v0.0.2-next.10) (2025-09-23)
82
+
83
+
84
+ ### Miscellaneous Chores
85
+
86
+ * **api-service:** Synchronize repo versions
87
+
88
+
89
+ ### Dependencies
90
+
91
+ * The following workspace dependencies were updated
92
+ * dependencies
93
+ * @twin.org/api-models bumped from 0.0.2-next.9 to 0.0.2-next.10
94
+
3
95
  ## [0.0.2-next.9](https://github.com/twinfoundation/api/compare/api-service-v0.0.2-next.8...api-service-v0.0.2-next.9) (2025-08-29)
4
96
 
5
97
 
@@ -30,15 +30,29 @@ The options to create the service.
30
30
 
31
31
  ### CLASS\_NAME
32
32
 
33
- > `readonly` **CLASS\_NAME**: `string`
33
+ > `readonly` `static` **CLASS\_NAME**: `string`
34
34
 
35
35
  Runtime name for the class.
36
36
 
37
+ ## Methods
38
+
39
+ ### className()
40
+
41
+ > **className**(): `string`
42
+
43
+ Returns the class name of the component.
44
+
45
+ #### Returns
46
+
47
+ `string`
48
+
49
+ The class name of the component.
50
+
37
51
  #### Implementation of
38
52
 
39
- `IInformationComponent.CLASS_NAME`
53
+ `IInformationComponent.className`
40
54
 
41
- ## Methods
55
+ ***
42
56
 
43
57
  ### start()
44
58
 
@@ -96,13 +110,13 @@ The service information.
96
110
 
97
111
  ### favicon()
98
112
 
99
- > **favicon**(): `Promise`\<`undefined` \| `Uint8Array`\<`ArrayBufferLike`\>\>
113
+ > **favicon**(): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\> \| `undefined`\>
100
114
 
101
115
  Get the favicon.
102
116
 
103
117
  #### Returns
104
118
 
105
- `Promise`\<`undefined` \| `Uint8Array`\<`ArrayBufferLike`\>\>
119
+ `Promise`\<`Uint8Array`\<`ArrayBufferLike`\> \| `undefined`\>
106
120
 
107
121
  The favicon.
108
122
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/api-service",
3
- "version": "0.0.2-next.9",
3
+ "version": "0.0.3-next.2",
4
4
  "description": "Information contract implementation and REST endpoint definitions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,26 +14,39 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/api-models": "0.0.2-next.9",
17
+ "@twin.org/api-models": "0.0.3-next.2",
18
18
  "@twin.org/core": "next",
19
19
  "@twin.org/nameof": "next",
20
20
  "@twin.org/web": "next"
21
21
  },
22
- "main": "./dist/cjs/index.cjs",
23
- "module": "./dist/esm/index.mjs",
22
+ "main": "./dist/es/index.js",
24
23
  "types": "./dist/types/index.d.ts",
25
24
  "exports": {
26
25
  ".": {
27
26
  "types": "./dist/types/index.d.ts",
28
- "require": "./dist/cjs/index.cjs",
29
- "import": "./dist/esm/index.mjs"
27
+ "import": "./dist/es/index.js",
28
+ "default": "./dist/es/index.js"
30
29
  }
31
30
  },
32
31
  "files": [
33
- "dist/cjs",
34
- "dist/esm",
32
+ "dist/es",
35
33
  "dist/types",
36
34
  "locales",
37
35
  "docs"
38
- ]
36
+ ],
37
+ "keywords": [
38
+ "twin",
39
+ "trade",
40
+ "iota",
41
+ "framework",
42
+ "blockchain",
43
+ "api",
44
+ "service",
45
+ "microservice",
46
+ "business-logic"
47
+ ],
48
+ "bugs": {
49
+ "url": "git+https://github.com/twinfoundation/api/issues"
50
+ },
51
+ "homepage": "https://twindev.org"
39
52
  }
@@ -1,447 +0,0 @@
1
- 'use strict';
2
-
3
- var core = require('@twin.org/core');
4
- var web = require('@twin.org/web');
5
- var promises = require('node:fs/promises');
6
-
7
- /**
8
- * The tag to associate with the routes.
9
- */
10
- const tagsInformation = [
11
- {
12
- name: "Info",
13
- description: "Information endpoints for the REST server."
14
- }
15
- ];
16
- /**
17
- * The REST routes for server information.
18
- * @param baseRouteName Prefix to prepend to the paths.
19
- * @param componentName The name of the component to use in the routes stored in the ComponentFactory.
20
- * @returns The generated routes.
21
- */
22
- function generateRestRoutesInformation(baseRouteName, componentName) {
23
- const rootRoute = {
24
- operationId: "serverRoot",
25
- summary: "Get the root text page",
26
- tag: tagsInformation[0].name,
27
- method: "GET",
28
- path: `${baseRouteName}/`,
29
- handler: async (httpRequestContext, request) => serverRoot(httpRequestContext, componentName),
30
- responseType: [
31
- {
32
- type: "IServerRootResponse",
33
- mimeType: web.MimeTypes.PlainText,
34
- examples: [
35
- {
36
- id: "serverRootResponse",
37
- description: "The response for the root request.",
38
- response: {
39
- body: "API Server - 1.0.0"
40
- }
41
- }
42
- ]
43
- }
44
- ],
45
- skipAuth: true
46
- };
47
- const informationRoute = {
48
- operationId: "serverInformation",
49
- summary: "Get the information for the server",
50
- tag: tagsInformation[0].name,
51
- method: "GET",
52
- path: `${baseRouteName}/info`,
53
- handler: async (httpRequestContext, request) => serverInfo(httpRequestContext, componentName),
54
- responseType: [
55
- {
56
- type: "IServerInfoResponse",
57
- examples: [
58
- {
59
- id: "informationResponse",
60
- description: "The response for the information request.",
61
- response: {
62
- body: {
63
- name: "API Server",
64
- version: "1.0.0"
65
- }
66
- }
67
- }
68
- ]
69
- }
70
- ],
71
- skipAuth: true
72
- };
73
- const favIconRoute = {
74
- operationId: "serverFavIcon",
75
- summary: "Get the favicon for the server",
76
- tag: tagsInformation[0].name,
77
- method: "GET",
78
- path: `${baseRouteName}/favicon.ico`,
79
- handler: async (httpRequestContext, request) => serverFavIcon(httpRequestContext, componentName),
80
- responseType: [
81
- {
82
- type: "IServerFavIconResponse",
83
- mimeType: "image/x-icon"
84
- }
85
- ],
86
- skipAuth: true
87
- };
88
- const healthRoute = {
89
- operationId: "serverHealth",
90
- summary: "Get the health for the server",
91
- tag: tagsInformation[0].name,
92
- method: "GET",
93
- path: `${baseRouteName}/health`,
94
- handler: async (httpRequestContext, request) => serverHealth(httpRequestContext, componentName),
95
- responseType: [
96
- {
97
- type: "IServerHealthResponse",
98
- examples: [
99
- {
100
- id: "healthResponseOK",
101
- description: "The response for the health request.",
102
- response: {
103
- body: {
104
- status: "ok",
105
- components: [
106
- {
107
- name: "Database",
108
- status: "ok"
109
- },
110
- {
111
- name: "Storage",
112
- status: "ok"
113
- }
114
- ]
115
- }
116
- }
117
- },
118
- {
119
- id: "healthResponseWarning",
120
- description: "The response for the health request with warnings.",
121
- response: {
122
- body: {
123
- status: "warning",
124
- components: [
125
- {
126
- name: "Database",
127
- status: "warning",
128
- details: "The database is running slow."
129
- },
130
- {
131
- name: "Storage",
132
- status: "ok"
133
- }
134
- ]
135
- }
136
- }
137
- },
138
- {
139
- id: "healthResponseError",
140
- description: "The response for the health request with errors.",
141
- response: {
142
- body: {
143
- status: "error",
144
- components: [
145
- {
146
- name: "Database",
147
- status: "ok"
148
- },
149
- {
150
- name: "Storage",
151
- status: "error",
152
- details: "The storage is full."
153
- }
154
- ]
155
- }
156
- }
157
- }
158
- ]
159
- }
160
- ],
161
- skipAuth: true
162
- };
163
- const specRoute = {
164
- operationId: "serverSpec",
165
- summary: "Get the OpenAPI specification for the endpoints",
166
- tag: tagsInformation[0].name,
167
- method: "GET",
168
- path: `${baseRouteName}/spec`,
169
- handler: async (httpRequestContext, request) => serverSpec(httpRequestContext, componentName),
170
- responseType: [
171
- {
172
- type: "IServerSpecResponse",
173
- examples: [
174
- {
175
- id: "specResponse",
176
- description: "The response for the spec request.",
177
- response: {
178
- body: {
179
- openapi: "3.1.0",
180
- info: {},
181
- paths: {}
182
- }
183
- }
184
- }
185
- ]
186
- }
187
- ],
188
- skipAuth: true
189
- };
190
- return [rootRoute, favIconRoute, informationRoute, healthRoute, specRoute];
191
- }
192
- /**
193
- * Get the root for the server.
194
- * @param httpRequestContext The request context for the API.
195
- * @param componentName The name of the component to use in the routes.
196
- * @param request The request.
197
- * @returns The response object with additional http response properties.
198
- */
199
- async function serverRoot(httpRequestContext, componentName, request) {
200
- const component = core.ComponentFactory.get(componentName);
201
- return {
202
- body: await component.root()
203
- };
204
- }
205
- /**
206
- * Get the information for the server.
207
- * @param httpRequestContext The request context for the API.
208
- * @param componentName The name of the component to use in the routes.
209
- * @param request The request.
210
- * @returns The response object with additional http response properties.
211
- */
212
- async function serverInfo(httpRequestContext, componentName, request) {
213
- const component = core.ComponentFactory.get(componentName);
214
- return {
215
- body: await component.info()
216
- };
217
- }
218
- /**
219
- * Get the health for the server.
220
- * @param httpRequestContext The request context for the API.
221
- * @param componentName The name of the component to use in the routes.
222
- * @param request The request.
223
- * @returns The response object with additional http response properties.
224
- */
225
- async function serverHealth(httpRequestContext, componentName, request) {
226
- const component = core.ComponentFactory.get(componentName);
227
- return {
228
- body: await component.health()
229
- };
230
- }
231
- /**
232
- * Get the favicon for the server.
233
- * @param httpRequestContext The request context for the API.
234
- * @param componentName The name of the component to use in the routes.
235
- * @param request The request.
236
- * @returns The response object with additional http response properties.
237
- */
238
- async function serverFavIcon(httpRequestContext, componentName, request) {
239
- const component = core.ComponentFactory.get(componentName);
240
- const favIcon = await component.favicon();
241
- if (core.Is.uint8Array(favIcon)) {
242
- return {
243
- headers: {
244
- [web.HeaderTypes.ContentType]: "image/x-icon"
245
- },
246
- body: favIcon
247
- };
248
- }
249
- return {
250
- statusCode: web.HttpStatusCode.notFound
251
- };
252
- }
253
- /**
254
- * Get the spec for the server.
255
- * @param httpRequestContext The request context for the API.
256
- * @param componentName The name of the component to use in the routes.
257
- * @param request The request.
258
- * @returns The response object with additional http response properties.
259
- */
260
- async function serverSpec(httpRequestContext, componentName, request) {
261
- const component = core.ComponentFactory.get(componentName);
262
- const spec = await component.spec();
263
- if (core.Is.objectValue(spec)) {
264
- return {
265
- body: spec
266
- };
267
- }
268
- return {
269
- statusCode: web.HttpStatusCode.notFound
270
- };
271
- }
272
-
273
- // Copyright 2024 IOTA Stiftung.
274
- // SPDX-License-Identifier: Apache-2.0.
275
- /**
276
- * The information service for the server.
277
- */
278
- class InformationService {
279
- /**
280
- * Runtime name for the class.
281
- */
282
- CLASS_NAME = "InformationService";
283
- /**
284
- * The server information.
285
- * @internal
286
- */
287
- _serverInfo;
288
- /**
289
- * The server health.
290
- * @internal
291
- */
292
- _healthInfo;
293
- /**
294
- * The path to the favicon Spec.
295
- * @internal
296
- */
297
- _faviconPath;
298
- /**
299
- * The favicon.
300
- * @internal
301
- */
302
- _favicon;
303
- /**
304
- * The path to the OpenAPI Spec.
305
- * @internal
306
- */
307
- _openApiSpecPath;
308
- /**
309
- * The OpenAPI spec.
310
- * @internal
311
- */
312
- _openApiSpec;
313
- /**
314
- * Create a new instance of InformationService.
315
- * @param options The options to create the service.
316
- */
317
- constructor(options) {
318
- core.Guards.object(this.CLASS_NAME, "options", options);
319
- core.Guards.object(this.CLASS_NAME, "options.config", options.config);
320
- core.Guards.object(this.CLASS_NAME, "options.config.serverInfo", options.config.serverInfo);
321
- this._serverInfo = options.config.serverInfo;
322
- this._healthInfo = {
323
- status: "ok"
324
- };
325
- this._faviconPath = options.config.favIconPath;
326
- this._openApiSpecPath = options.config.openApiSpecPath;
327
- }
328
- /**
329
- * The service needs to be started when the application is initialized.
330
- * @returns Nothing.
331
- */
332
- async start() {
333
- const openApiPath = this._openApiSpecPath;
334
- if (core.Is.stringValue(openApiPath)) {
335
- const contentBuffer = await promises.readFile(openApiPath, "utf8");
336
- this._openApiSpec = JSON.parse(contentBuffer);
337
- }
338
- const favIconPath = this._faviconPath;
339
- if (core.Is.stringValue(favIconPath)) {
340
- this._favicon = await promises.readFile(favIconPath);
341
- }
342
- }
343
- /**
344
- * Get the root information.
345
- * @returns The root information.
346
- */
347
- async root() {
348
- return `${this._serverInfo.name} - ${this._serverInfo.version}`;
349
- }
350
- /**
351
- * Get the server information.
352
- * @returns The service information.
353
- */
354
- async info() {
355
- return this._serverInfo;
356
- }
357
- /**
358
- * Get the favicon.
359
- * @returns The favicon.
360
- */
361
- async favicon() {
362
- return this._favicon;
363
- }
364
- /**
365
- * Get the OpenAPI spec.
366
- * @returns The OpenAPI spec.
367
- */
368
- async spec() {
369
- return this._openApiSpec;
370
- }
371
- /**
372
- * Get the server health.
373
- * @returns The service health.
374
- */
375
- async health() {
376
- let errorCount = 0;
377
- let warningCount = 0;
378
- if (core.Is.arrayValue(this._healthInfo.components)) {
379
- errorCount = this._healthInfo.components.filter(c => c.status === "error").length;
380
- warningCount = this._healthInfo.components.filter(c => c.status === "warning").length;
381
- }
382
- if (errorCount > 0) {
383
- this._healthInfo.status = "error";
384
- }
385
- else if (warningCount > 0) {
386
- this._healthInfo.status = "warning";
387
- }
388
- else {
389
- this._healthInfo.status = "ok";
390
- }
391
- return this._healthInfo;
392
- }
393
- /**
394
- * Set the status of a component.
395
- * @param name The component name.
396
- * @param status The status of the component.
397
- * @param details The details for the status.
398
- * @returns Nothing.
399
- */
400
- async setComponentHealth(name, status, details) {
401
- const component = this._healthInfo.components?.find(c => c.name === name);
402
- if (core.Is.undefined(component)) {
403
- this._healthInfo.components ??= [];
404
- this._healthInfo.components.push({
405
- name,
406
- status,
407
- details
408
- });
409
- }
410
- else {
411
- component.status = status;
412
- component.details = details;
413
- }
414
- }
415
- /**
416
- * Remove the status of a component.
417
- * @param name The component name.
418
- * @returns Nothing.
419
- */
420
- async removeComponentHealth(name) {
421
- if (core.Is.arrayValue(this._healthInfo.components)) {
422
- const componentIndex = this._healthInfo.components.findIndex(c => c.name === name);
423
- if (componentIndex !== -1) {
424
- this._healthInfo.components.splice(componentIndex, 1);
425
- }
426
- }
427
- }
428
- }
429
-
430
- const restEntryPoints = [
431
- {
432
- name: "information",
433
- defaultBaseRoute: "",
434
- tags: tagsInformation,
435
- generateRoutes: generateRestRoutesInformation
436
- }
437
- ];
438
-
439
- exports.InformationService = InformationService;
440
- exports.generateRestRoutesInformation = generateRestRoutesInformation;
441
- exports.restEntryPoints = restEntryPoints;
442
- exports.serverFavIcon = serverFavIcon;
443
- exports.serverHealth = serverHealth;
444
- exports.serverInfo = serverInfo;
445
- exports.serverRoot = serverRoot;
446
- exports.serverSpec = serverSpec;
447
- exports.tagsInformation = tagsInformation;