@twin.org/api-service 0.0.3-next.2 → 0.0.3-next.20

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,94 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { HttpUrlHelper } from "@twin.org/api-models";
4
+ import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
5
+ import { ComponentFactory, Guards, Is } from "@twin.org/core";
6
+ /**
7
+ * The hosting service for the server.
8
+ */
9
+ export class HostingService {
10
+ /**
11
+ * Runtime name for the class.
12
+ */
13
+ static CLASS_NAME = "HostingService";
14
+ /**
15
+ * The tenant admin component.
16
+ * @internal
17
+ */
18
+ _tenantAdminComponent;
19
+ /**
20
+ * The local origin URL e.g. "http://localhost:3000".
21
+ * @internal
22
+ */
23
+ _localOrigin;
24
+ /**
25
+ * The APIs public base URL e.g. "https://api.example.com:1234".
26
+ * @internal
27
+ */
28
+ _publicOrigin;
29
+ /**
30
+ * Create a new instance of HostingService.
31
+ * @param options The options to create the service.
32
+ */
33
+ constructor(options) {
34
+ Guards.object(HostingService.CLASS_NAME, "options", options);
35
+ Guards.object(HostingService.CLASS_NAME, "options.config", options.config);
36
+ Guards.stringValue(HostingService.CLASS_NAME, "options.config.localOrigin", options.config.localOrigin);
37
+ this._tenantAdminComponent = ComponentFactory.getIfExists(options?.tenantAdminComponentType ?? "tenant-admin");
38
+ this._localOrigin = options.config.localOrigin;
39
+ this._publicOrigin = options.config.publicOrigin;
40
+ }
41
+ /**
42
+ * Returns the class name of the component.
43
+ * @returns The class name of the component.
44
+ */
45
+ className() {
46
+ return HostingService.CLASS_NAME;
47
+ }
48
+ /**
49
+ * Get the public origin for the hosting.
50
+ * @param serverRequestUrl The url of the current server request if there is one.
51
+ * @returns The public origin.
52
+ */
53
+ async getPublicOrigin(serverRequestUrl) {
54
+ // If there is a tenant admin component, and the context has a tenant id set.
55
+ // set if the tenant has a specific public origin.
56
+ const contextIds = await ContextIdStore.getContextIds();
57
+ const tenantId = contextIds?.[ContextIdKeys.Tenant];
58
+ let tenantPublicOrigin;
59
+ if (Is.stringValue(tenantId)) {
60
+ tenantPublicOrigin = await this.getTenantOrigin(tenantId);
61
+ }
62
+ const serverRequestOrigin = Is.stringValue(serverRequestUrl)
63
+ ? HttpUrlHelper.extractOrigin(serverRequestUrl)
64
+ : undefined;
65
+ // If there is a tenant public origin, return it.
66
+ // If not and the config has a public origin, return it.
67
+ // Otherwise, use the server request URL if provided
68
+ // else fallback to local origin.
69
+ return tenantPublicOrigin ?? this._publicOrigin ?? serverRequestOrigin ?? this._localOrigin;
70
+ }
71
+ /**
72
+ * Get the public origin for the tenant if one exists.
73
+ * @param tenantId The tenant identifier.
74
+ * @returns The public origin for the tenant.
75
+ */
76
+ async getTenantOrigin(tenantId) {
77
+ Guards.stringHexLength(HostingService.CLASS_NAME, "tenantId", tenantId, 32);
78
+ if (Is.empty(this._tenantAdminComponent)) {
79
+ return undefined;
80
+ }
81
+ const tenant = await this._tenantAdminComponent.get(tenantId);
82
+ return tenant?.publicOrigin;
83
+ }
84
+ /**
85
+ * Build a public url based on the public origin and the url provided.
86
+ * @param url The url to build upon the public origin.
87
+ * @returns The full url based on the public origin.
88
+ */
89
+ async buildPublicUrl(url) {
90
+ const publicOrigin = await this.getPublicOrigin(url);
91
+ return HttpUrlHelper.replaceOrigin(url, publicOrigin);
92
+ }
93
+ }
94
+ //# sourceMappingURL=hostingService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostingService.js","sourceRoot":"","sources":["../../src/hostingService.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EACN,aAAa,EAGb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAI9D;;GAEG;AACH,MAAM,OAAO,cAAc;IAC1B;;OAEG;IACI,MAAM,CAAU,UAAU,oBAAoC;IAErE;;;OAGG;IACc,qBAAqB,CAAyB;IAE/D;;;OAGG;IACc,YAAY,CAAS;IAEtC;;;OAGG;IACc,aAAa,CAAU;IAExC;;;OAGG;IACH,YAAY,OAA0C;QACrD,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,oBAA0B,OAAO,CAAC,MAAM,CAAC,CAAC;QACjF,MAAM,CAAC,WAAW,CACjB,cAAc,CAAC,UAAU,gCAEzB,OAAO,CAAC,MAAM,CAAC,WAAW,CAC1B,CAAC;QACF,IAAI,CAAC,qBAAqB,GAAG,gBAAgB,CAAC,WAAW,CACxD,OAAO,EAAE,wBAAwB,IAAI,cAAc,CACnD,CAAC;QACF,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;QAC/C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;IAClD,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,cAAc,CAAC,UAAU,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,eAAe,CAAC,gBAAyB;QACrD,6EAA6E;QAC7E,kDAAkD;QAClD,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,kBAAkB,CAAC;QACvB,IAAI,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,kBAAkB,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,mBAAmB,GAAG,EAAE,CAAC,WAAW,CAAC,gBAAgB,CAAC;YAC3D,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC;YAC/C,CAAC,CAAC,SAAS,CAAC;QAEb,iDAAiD;QACjD,wDAAwD;QACxD,oDAAoD;QACpD,iCAAiC;QACjC,OAAO,kBAAkB,IAAI,IAAI,CAAC,aAAa,IAAI,mBAAmB,IAAI,IAAI,CAAC,YAAY,CAAC;IAC7F,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,eAAe,CAAC,QAAgB;QAC5C,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,UAAU,cAAoB,QAAQ,EAAE,EAAE,CAAC,CAAC;QAElF,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAC1C,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9D,OAAO,MAAM,EAAE,YAAY,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,cAAc,CAAC,GAAW;QACtC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACrD,OAAO,aAAa,CAAC,aAAa,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACvD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpUrlHelper,\n\ttype IHostingComponent,\n\ttype ITenantAdminComponent\n} from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport { ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IHostingServiceConstructorOptions } from \"./models/IHostingServiceConstructorOptions.js\";\n\n/**\n * The hosting service for the server.\n */\nexport class HostingService implements IHostingComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<HostingService>();\n\n\t/**\n\t * The tenant admin component.\n\t * @internal\n\t */\n\tprivate readonly _tenantAdminComponent?: ITenantAdminComponent;\n\n\t/**\n\t * The local origin URL e.g. \"http://localhost:3000\".\n\t * @internal\n\t */\n\tprivate readonly _localOrigin: string;\n\n\t/**\n\t * The APIs public base URL e.g. \"https://api.example.com:1234\".\n\t * @internal\n\t */\n\tprivate readonly _publicOrigin?: string;\n\n\t/**\n\t * Create a new instance of HostingService.\n\t * @param options The options to create the service.\n\t */\n\tconstructor(options: IHostingServiceConstructorOptions) {\n\t\tGuards.object(HostingService.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(HostingService.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.stringValue(\n\t\t\tHostingService.CLASS_NAME,\n\t\t\tnameof(options.config.localOrigin),\n\t\t\toptions.config.localOrigin\n\t\t);\n\t\tthis._tenantAdminComponent = ComponentFactory.getIfExists(\n\t\t\toptions?.tenantAdminComponentType ?? \"tenant-admin\"\n\t\t);\n\t\tthis._localOrigin = options.config.localOrigin;\n\t\tthis._publicOrigin = options.config.publicOrigin;\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 HostingService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Get the public origin for the hosting.\n\t * @param serverRequestUrl The url of the current server request if there is one.\n\t * @returns The public origin.\n\t */\n\tpublic async getPublicOrigin(serverRequestUrl?: string): Promise<string> {\n\t\t// If there is a tenant admin component, and the context has a tenant id set.\n\t\t// set if the tenant has a specific public origin.\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst tenantId = contextIds?.[ContextIdKeys.Tenant];\n\t\tlet tenantPublicOrigin;\n\t\tif (Is.stringValue(tenantId)) {\n\t\t\ttenantPublicOrigin = await this.getTenantOrigin(tenantId);\n\t\t}\n\n\t\tconst serverRequestOrigin = Is.stringValue(serverRequestUrl)\n\t\t\t? HttpUrlHelper.extractOrigin(serverRequestUrl)\n\t\t\t: undefined;\n\n\t\t// If there is a tenant public origin, return it.\n\t\t// If not and the config has a public origin, return it.\n\t\t// Otherwise, use the server request URL if provided\n\t\t// else fallback to local origin.\n\t\treturn tenantPublicOrigin ?? this._publicOrigin ?? serverRequestOrigin ?? this._localOrigin;\n\t}\n\n\t/**\n\t * Get the public origin for the tenant if one exists.\n\t * @param tenantId The tenant identifier.\n\t * @returns The public origin for the tenant.\n\t */\n\tpublic async getTenantOrigin(tenantId: string): Promise<string | undefined> {\n\t\tGuards.stringHexLength(HostingService.CLASS_NAME, nameof(tenantId), tenantId, 32);\n\n\t\tif (Is.empty(this._tenantAdminComponent)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst tenant = await this._tenantAdminComponent.get(tenantId);\n\t\treturn tenant?.publicOrigin;\n\t}\n\n\t/**\n\t * Build a public url based on the public origin and the url provided.\n\t * @param url The url to build upon the public origin.\n\t * @returns The full url based on the public origin.\n\t */\n\tpublic async buildPublicUrl(url: string): Promise<string> {\n\t\tconst publicOrigin = await this.getPublicOrigin(url);\n\t\treturn HttpUrlHelper.replaceOrigin(url, publicOrigin);\n\t}\n}\n"]}
package/dist/es/index.js CHANGED
@@ -1,7 +1,10 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
+ export * from "./hostingService.js";
3
4
  export * from "./informationRoutes.js";
4
5
  export * from "./informationService.js";
6
+ export * from "./models/IHostingServiceConfig.js";
7
+ export * from "./models/IHostingServiceConstructorOptions.js";
5
8
  export * from "./models/IInformationServiceConfig.js";
6
9
  export * from "./models/IInformationServiceConstructorOptions.js";
7
10
  export * from "./restEntryPoints.js";
@@ -1 +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
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mCAAmC,CAAC;AAClD,cAAc,+CAA+C,CAAC;AAC9D,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 \"./hostingService.js\";\nexport * from \"./informationRoutes.js\";\nexport * from \"./informationService.js\";\nexport * from \"./models/IHostingServiceConfig.js\";\nexport * from \"./models/IHostingServiceConstructorOptions.js\";\nexport * from \"./models/IInformationServiceConfig.js\";\nexport * from \"./models/IInformationServiceConstructorOptions.js\";\nexport * from \"./restEntryPoints.js\";\n"]}
@@ -32,13 +32,17 @@ export function generateRestRoutesInformation(baseRouteName, componentName) {
32
32
  id: "serverRootResponse",
33
33
  description: "The response for the root request.",
34
34
  response: {
35
+ headers: {
36
+ [HeaderTypes.ContentType]: MimeTypes.PlainText
37
+ },
35
38
  body: "API Server - 1.0.0"
36
39
  }
37
40
  }
38
41
  ]
39
42
  }
40
43
  ],
41
- skipAuth: true
44
+ skipAuth: true,
45
+ skipTenant: true
42
46
  };
43
47
  const informationRoute = {
44
48
  operationId: "serverInformation",
@@ -79,7 +83,46 @@ export function generateRestRoutesInformation(baseRouteName, componentName) {
79
83
  mimeType: "image/x-icon"
80
84
  }
81
85
  ],
82
- skipAuth: true
86
+ skipAuth: true,
87
+ skipTenant: true
88
+ };
89
+ const livezRoute = {
90
+ operationId: "serverLivez",
91
+ summary: "Get the livez status for the server",
92
+ tag: tagsInformation[0].name,
93
+ method: "GET",
94
+ path: `${baseRouteName}/livez`,
95
+ handler: async (httpRequestContext, request) => serverLivez(httpRequestContext, componentName, request),
96
+ responseType: [
97
+ {
98
+ type: "IServerLivezResponse",
99
+ mimeType: MimeTypes.PlainText,
100
+ examples: [
101
+ {
102
+ id: "livezResponseOK",
103
+ description: "The response for the liveness request.",
104
+ response: {
105
+ headers: {
106
+ [HeaderTypes.ContentType]: MimeTypes.PlainText
107
+ },
108
+ body: "ok"
109
+ }
110
+ },
111
+ {
112
+ id: "livezResponseFailure",
113
+ description: "The response for the liveness request with errors.",
114
+ response: {
115
+ headers: {
116
+ [HeaderTypes.ContentType]: MimeTypes.PlainText
117
+ },
118
+ body: "failed"
119
+ }
120
+ }
121
+ ]
122
+ }
123
+ ],
124
+ skipAuth: true,
125
+ skipTenant: true
83
126
  };
84
127
  const healthRoute = {
85
128
  operationId: "serverHealth",
@@ -183,7 +226,7 @@ export function generateRestRoutesInformation(baseRouteName, componentName) {
183
226
  ],
184
227
  skipAuth: true
185
228
  };
186
- return [rootRoute, favIconRoute, informationRoute, healthRoute, specRoute];
229
+ return [rootRoute, favIconRoute, informationRoute, livezRoute, healthRoute, specRoute];
187
230
  }
188
231
  /**
189
232
  * Get the root for the server.
@@ -195,6 +238,9 @@ export function generateRestRoutesInformation(baseRouteName, componentName) {
195
238
  export async function serverRoot(httpRequestContext, componentName, request) {
196
239
  const component = ComponentFactory.get(componentName);
197
240
  return {
241
+ headers: {
242
+ [HeaderTypes.ContentType]: MimeTypes.PlainText
243
+ },
198
244
  body: await component.root()
199
245
  };
200
246
  }
@@ -211,6 +257,22 @@ export async function serverInfo(httpRequestContext, componentName, request) {
211
257
  body: await component.info()
212
258
  };
213
259
  }
260
+ /**
261
+ * Get the livez for the server.
262
+ * @param httpRequestContext The request context for the API.
263
+ * @param componentName The name of the component to use in the routes.
264
+ * @param request The request.
265
+ * @returns The response object with additional http response properties.
266
+ */
267
+ export async function serverLivez(httpRequestContext, componentName, request) {
268
+ const component = ComponentFactory.get(componentName);
269
+ return {
270
+ headers: {
271
+ [HeaderTypes.ContentType]: MimeTypes.PlainText
272
+ },
273
+ body: (await component.livez()) ? "ok" : "failed"
274
+ };
275
+ }
214
276
  /**
215
277
  * Get the health for the server.
216
278
  * @param httpRequestContext The request context for the API.
@@ -1 +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"]}
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\tIServerLivezResponse,\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\theaders: {\n\t\t\t\t\t\t\t\t[HeaderTypes.ContentType]: MimeTypes.PlainText\n\t\t\t\t\t\t\t},\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\tskipTenant: 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\tskipTenant: true\n\t};\n\n\tconst livezRoute: IRestRoute<INoContentRequest, IServerLivezResponse> = {\n\t\toperationId: \"serverLivez\",\n\t\tsummary: \"Get the livez status for the server\",\n\t\ttag: tagsInformation[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/livez`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tserverLivez(httpRequestContext, componentName, request),\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IServerLivezResponse>(),\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: \"livezResponseOK\",\n\t\t\t\t\t\tdescription: \"The response for the liveness request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t[HeaderTypes.ContentType]: MimeTypes.PlainText\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tbody: \"ok\"\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: \"livezResponseFailure\",\n\t\t\t\t\t\tdescription: \"The response for the liveness request with errors.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t[HeaderTypes.ContentType]: MimeTypes.PlainText\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tbody: \"failed\"\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\tskipTenant: 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, livezRoute, 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\theaders: {\n\t\t\t[HeaderTypes.ContentType]: MimeTypes.PlainText\n\t\t},\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 livez 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 serverLivez(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: INoContentRequest\n): Promise<IServerLivezResponse> {\n\tconst component = ComponentFactory.get<IInformationComponent>(componentName);\n\treturn {\n\t\theaders: {\n\t\t\t[HeaderTypes.ContentType]: MimeTypes.PlainText\n\t\t},\n\t\tbody: (await component.livez()) ? \"ok\" : \"failed\"\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"]}
@@ -1,6 +1,7 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  import { readFile } from "node:fs/promises";
4
+ import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
4
5
  import { Guards, Is } from "@twin.org/core";
5
6
  /**
6
7
  * The information service for the server.
@@ -105,6 +106,17 @@ export class InformationService {
105
106
  async spec() {
106
107
  return this._openApiSpec;
107
108
  }
109
+ /**
110
+ * Is the server live.
111
+ * @returns True if the server is live.
112
+ */
113
+ async livez() {
114
+ let errorCount = 0;
115
+ if (Is.arrayValue(this._healthInfo.components)) {
116
+ errorCount = this._healthInfo.components.filter(c => c.status === "error").length;
117
+ }
118
+ return errorCount === 0;
119
+ }
108
120
  /**
109
121
  * Get the server health.
110
122
  * @returns The service health.
@@ -112,9 +124,13 @@ export class InformationService {
112
124
  async health() {
113
125
  let errorCount = 0;
114
126
  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;
127
+ const contextIds = await ContextIdStore.getContextIds();
128
+ const tenantId = contextIds?.[ContextIdKeys.Tenant];
129
+ // Filter so we only get components that are not tenant specific or match the tenant id
130
+ const components = this._healthInfo.components?.filter(c => Is.empty(c.tenantId) || c.tenantId === tenantId);
131
+ if (Is.arrayValue(components)) {
132
+ errorCount = components.filter(c => c.status === "error").length;
133
+ warningCount = components.filter(c => c.status === "warning").length;
118
134
  }
119
135
  if (errorCount > 0) {
120
136
  this._healthInfo.status = "error";
@@ -125,23 +141,34 @@ export class InformationService {
125
141
  else {
126
142
  this._healthInfo.status = "ok";
127
143
  }
128
- return this._healthInfo;
144
+ return {
145
+ status: this._healthInfo.status,
146
+ components: components?.map(c => ({
147
+ name: c.name,
148
+ status: c.status,
149
+ details: c.details
150
+ }))
151
+ };
129
152
  }
130
153
  /**
131
154
  * Set the status of a component.
132
155
  * @param name The component name.
133
156
  * @param status The status of the component.
134
157
  * @param details The details for the status.
158
+ * @param tenantId The tenant id, optional if the health status is not tenant specific.
135
159
  * @returns Nothing.
136
160
  */
137
- async setComponentHealth(name, status, details) {
138
- const component = this._healthInfo.components?.find(c => c.name === name);
161
+ async setComponentHealth(name, status, details, tenantId) {
162
+ const component = Is.empty(tenantId)
163
+ ? this._healthInfo.components?.find(c => c.name === name && Is.empty(c.tenantId))
164
+ : this._healthInfo.components?.find(c => c.name === name && c.tenantId === tenantId);
139
165
  if (Is.undefined(component)) {
140
166
  this._healthInfo.components ??= [];
141
167
  this._healthInfo.components.push({
142
168
  name,
143
169
  status,
144
- details
170
+ details,
171
+ tenantId
145
172
  });
146
173
  }
147
174
  else {
@@ -152,11 +179,14 @@ export class InformationService {
152
179
  /**
153
180
  * Remove the status of a component.
154
181
  * @param name The component name.
182
+ * @param tenantId The tenant id, optional if the health status is not tenant specific.
155
183
  * @returns Nothing.
156
184
  */
157
- async removeComponentHealth(name) {
185
+ async removeComponentHealth(name, tenantId) {
158
186
  if (Is.arrayValue(this._healthInfo.components)) {
159
- const componentIndex = this._healthInfo.components.findIndex(c => c.name === name);
187
+ const componentIndex = Is.empty(tenantId)
188
+ ? this._healthInfo.components?.findIndex(c => c.name === name && Is.empty(c.tenantId))
189
+ : this._healthInfo.components?.findIndex(c => c.name === name && c.tenantId === tenantId);
160
190
  if (componentIndex !== -1) {
161
191
  this._healthInfo.components.splice(componentIndex, 1);
162
192
  }
@@ -1 +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"]}
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\tIHealthComponentInfo,\n\tIHealthInfo,\n\tIInformationComponent,\n\tIServerInfo\n} from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\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: {\n\t\tstatus: HealthStatus;\n\t\tcomponents?: (IHealthComponentInfo & { tenantId?: string })[];\n\t};\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 * Is the server live.\n\t * @returns True if the server is live.\n\t */\n\tpublic async livez(): Promise<boolean> {\n\t\tlet errorCount = 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}\n\n\t\treturn errorCount === 0;\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\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst tenantId = contextIds?.[ContextIdKeys.Tenant];\n\n\t\t// Filter so we only get components that are not tenant specific or match the tenant id\n\t\tconst components = this._healthInfo.components?.filter(\n\t\t\tc => Is.empty(c.tenantId) || c.tenantId === tenantId\n\t\t);\n\n\t\tif (Is.arrayValue(components)) {\n\t\t\terrorCount = components.filter(c => c.status === \"error\").length;\n\t\t\twarningCount = 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 {\n\t\t\tstatus: this._healthInfo.status,\n\t\t\tcomponents: components?.map(c => ({\n\t\t\t\tname: c.name,\n\t\t\t\tstatus: c.status,\n\t\t\t\tdetails: c.details\n\t\t\t}))\n\t\t};\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 * @param tenantId The tenant id, optional if the health status is not tenant specific.\n\t * @returns Nothing.\n\t */\n\tpublic async setComponentHealth(\n\t\tname: string,\n\t\tstatus: HealthStatus,\n\t\tdetails?: string,\n\t\ttenantId?: string\n\t): Promise<void> {\n\t\tconst component = Is.empty(tenantId)\n\t\t\t? this._healthInfo.components?.find(c => c.name === name && Is.empty(c.tenantId))\n\t\t\t: this._healthInfo.components?.find(c => c.name === name && c.tenantId === tenantId);\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\ttenantId\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 * @param tenantId The tenant id, optional if the health status is not tenant specific.\n\t * @returns Nothing.\n\t */\n\tpublic async removeComponentHealth(name: string, tenantId?: string): Promise<void> {\n\t\tif (Is.arrayValue(this._healthInfo.components)) {\n\t\t\tconst componentIndex = Is.empty(tenantId)\n\t\t\t\t? this._healthInfo.components?.findIndex(c => c.name === name && Is.empty(c.tenantId))\n\t\t\t\t: this._healthInfo.components?.findIndex(c => c.name === name && c.tenantId === tenantId);\n\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,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IHostingServiceConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IHostingServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IHostingServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the hosting service.\n */\nexport interface IHostingServiceConfig {\n\t/**\n\t * The local origin, must be provided as a fallback e.g. http://localhost:1234.\n\t */\n\tlocalOrigin: string;\n\n\t/**\n\t * The APIs public base URL e.g. \"https://api.example.com:1234\".\n\t */\n\tpublicOrigin?: string;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IHostingServiceConstructorOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IHostingServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IHostingServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IHostingServiceConfig } from \"./IHostingServiceConfig.js\";\n\n/**\n * Options for the IHostingService constructor.\n */\nexport interface IHostingServiceConstructorOptions {\n\t/**\n\t * The tenant admin component type.\n\t * @default tenant-admin\n\t */\n\ttenantAdminComponentType?: string;\n\n\t/**\n\t * The configuration for the service.\n\t */\n\tconfig: IHostingServiceConfig;\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import { type IHostingComponent } from "@twin.org/api-models";
2
+ import type { IHostingServiceConstructorOptions } from "./models/IHostingServiceConstructorOptions.js";
3
+ /**
4
+ * The hosting service for the server.
5
+ */
6
+ export declare class HostingService implements IHostingComponent {
7
+ /**
8
+ * Runtime name for the class.
9
+ */
10
+ static readonly CLASS_NAME: string;
11
+ /**
12
+ * Create a new instance of HostingService.
13
+ * @param options The options to create the service.
14
+ */
15
+ constructor(options: IHostingServiceConstructorOptions);
16
+ /**
17
+ * Returns the class name of the component.
18
+ * @returns The class name of the component.
19
+ */
20
+ className(): string;
21
+ /**
22
+ * Get the public origin for the hosting.
23
+ * @param serverRequestUrl The url of the current server request if there is one.
24
+ * @returns The public origin.
25
+ */
26
+ getPublicOrigin(serverRequestUrl?: string): Promise<string>;
27
+ /**
28
+ * Get the public origin for the tenant if one exists.
29
+ * @param tenantId The tenant identifier.
30
+ * @returns The public origin for the tenant.
31
+ */
32
+ getTenantOrigin(tenantId: string): Promise<string | undefined>;
33
+ /**
34
+ * Build a public url based on the public origin and the url provided.
35
+ * @param url The url to build upon the public origin.
36
+ * @returns The full url based on the public origin.
37
+ */
38
+ buildPublicUrl(url: string): Promise<string>;
39
+ }
@@ -1,5 +1,8 @@
1
+ export * from "./hostingService.js";
1
2
  export * from "./informationRoutes.js";
2
3
  export * from "./informationService.js";
4
+ export * from "./models/IHostingServiceConfig.js";
5
+ export * from "./models/IHostingServiceConstructorOptions.js";
3
6
  export * from "./models/IInformationServiceConfig.js";
4
7
  export * from "./models/IInformationServiceConstructorOptions.js";
5
8
  export * from "./restEntryPoints.js";
@@ -1,4 +1,4 @@
1
- import type { IHttpRequestContext, INoContentRequest, IRestRoute, IServerFavIconResponse, IServerHealthResponse, IServerInfoResponse, IServerRootResponse, IServerSpecResponse, ITag } from "@twin.org/api-models";
1
+ import type { IHttpRequestContext, INoContentRequest, IRestRoute, IServerFavIconResponse, IServerHealthResponse, IServerInfoResponse, IServerLivezResponse, IServerRootResponse, IServerSpecResponse, ITag } from "@twin.org/api-models";
2
2
  /**
3
3
  * The tag to associate with the routes.
4
4
  */
@@ -26,6 +26,14 @@ export declare function serverRoot(httpRequestContext: IHttpRequestContext, comp
26
26
  * @returns The response object with additional http response properties.
27
27
  */
28
28
  export declare function serverInfo(httpRequestContext: IHttpRequestContext, componentName: string, request: INoContentRequest): Promise<IServerInfoResponse>;
29
+ /**
30
+ * Get the livez for the server.
31
+ * @param httpRequestContext The request context for the API.
32
+ * @param componentName The name of the component to use in the routes.
33
+ * @param request The request.
34
+ * @returns The response object with additional http response properties.
35
+ */
36
+ export declare function serverLivez(httpRequestContext: IHttpRequestContext, componentName: string, request: INoContentRequest): Promise<IServerLivezResponse>;
29
37
  /**
30
38
  * Get the health for the server.
31
39
  * @param httpRequestContext The request context for the API.
@@ -43,6 +43,11 @@ export declare class InformationService implements IInformationComponent {
43
43
  * @returns The OpenAPI spec.
44
44
  */
45
45
  spec(): Promise<unknown>;
46
+ /**
47
+ * Is the server live.
48
+ * @returns True if the server is live.
49
+ */
50
+ livez(): Promise<boolean>;
46
51
  /**
47
52
  * Get the server health.
48
53
  * @returns The service health.
@@ -53,13 +58,15 @@ export declare class InformationService implements IInformationComponent {
53
58
  * @param name The component name.
54
59
  * @param status The status of the component.
55
60
  * @param details The details for the status.
61
+ * @param tenantId The tenant id, optional if the health status is not tenant specific.
56
62
  * @returns Nothing.
57
63
  */
58
- setComponentHealth(name: string, status: HealthStatus, details?: string): Promise<void>;
64
+ setComponentHealth(name: string, status: HealthStatus, details?: string, tenantId?: string): Promise<void>;
59
65
  /**
60
66
  * Remove the status of a component.
61
67
  * @param name The component name.
68
+ * @param tenantId The tenant id, optional if the health status is not tenant specific.
62
69
  * @returns Nothing.
63
70
  */
64
- removeComponentHealth(name: string): Promise<void>;
71
+ removeComponentHealth(name: string, tenantId?: string): Promise<void>;
65
72
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Configuration for the hosting service.
3
+ */
4
+ export interface IHostingServiceConfig {
5
+ /**
6
+ * The local origin, must be provided as a fallback e.g. http://localhost:1234.
7
+ */
8
+ localOrigin: string;
9
+ /**
10
+ * The APIs public base URL e.g. "https://api.example.com:1234".
11
+ */
12
+ publicOrigin?: string;
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { IHostingServiceConfig } from "./IHostingServiceConfig.js";
2
+ /**
3
+ * Options for the IHostingService constructor.
4
+ */
5
+ export interface IHostingServiceConstructorOptions {
6
+ /**
7
+ * The tenant admin component type.
8
+ * @default tenant-admin
9
+ */
10
+ tenantAdminComponentType?: string;
11
+ /**
12
+ * The configuration for the service.
13
+ */
14
+ config: IHostingServiceConfig;
15
+ }
package/docs/changelog.md CHANGED
@@ -1,5 +1,274 @@
1
1
  # @twin.org/api-service - Changelog
2
2
 
3
+ ## [0.0.3-next.20](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.19...api-service-v0.0.3-next.20) (2026-02-09)
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.19 to 0.0.3-next.20
16
+
17
+ ## [0.0.3-next.19](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.18...api-service-v0.0.3-next.19) (2026-02-06)
18
+
19
+
20
+ ### Miscellaneous Chores
21
+
22
+ * **api-service:** Synchronize repo versions
23
+
24
+
25
+ ### Dependencies
26
+
27
+ * The following workspace dependencies were updated
28
+ * dependencies
29
+ * @twin.org/api-models bumped from 0.0.3-next.18 to 0.0.3-next.19
30
+
31
+ ## [0.0.3-next.18](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.17...api-service-v0.0.3-next.18) (2026-02-04)
32
+
33
+
34
+ ### Miscellaneous Chores
35
+
36
+ * **api-service:** Synchronize repo versions
37
+
38
+
39
+ ### Dependencies
40
+
41
+ * The following workspace dependencies were updated
42
+ * dependencies
43
+ * @twin.org/api-models bumped from 0.0.3-next.17 to 0.0.3-next.18
44
+
45
+ ## [0.0.3-next.17](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.16...api-service-v0.0.3-next.17) (2026-01-26)
46
+
47
+
48
+ ### Features
49
+
50
+ * update public origin building ([6c8e042](https://github.com/twinfoundation/api/commit/6c8e0422d9ddbed42a843e1c23498c99977b2fc7))
51
+
52
+
53
+ ### Dependencies
54
+
55
+ * The following workspace dependencies were updated
56
+ * dependencies
57
+ * @twin.org/api-models bumped from 0.0.3-next.16 to 0.0.3-next.17
58
+
59
+ ## [0.0.3-next.16](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.15...api-service-v0.0.3-next.16) (2026-01-26)
60
+
61
+
62
+ ### Features
63
+
64
+ * public base url ([#70](https://github.com/twinfoundation/api/issues/70)) ([5b958cd](https://github.com/twinfoundation/api/commit/5b958cd91e8a38cdae2835ff5f2356c7e48d37c3))
65
+
66
+
67
+ ### Dependencies
68
+
69
+ * The following workspace dependencies were updated
70
+ * dependencies
71
+ * @twin.org/api-models bumped from 0.0.3-next.15 to 0.0.3-next.16
72
+
73
+ ## [0.0.3-next.15](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.14...api-service-v0.0.3-next.15) (2026-01-22)
74
+
75
+
76
+ ### Miscellaneous Chores
77
+
78
+ * **api-service:** Synchronize repo versions
79
+
80
+
81
+ ### Dependencies
82
+
83
+ * The following workspace dependencies were updated
84
+ * dependencies
85
+ * @twin.org/api-models bumped from 0.0.3-next.14 to 0.0.3-next.15
86
+
87
+ ## [0.0.3-next.14](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.13...api-service-v0.0.3-next.14) (2026-01-20)
88
+
89
+
90
+ ### Miscellaneous Chores
91
+
92
+ * **api-service:** Synchronize repo versions
93
+
94
+
95
+ ### Dependencies
96
+
97
+ * The following workspace dependencies were updated
98
+ * dependencies
99
+ * @twin.org/api-models bumped from 0.0.3-next.13 to 0.0.3-next.14
100
+
101
+ ## [0.0.3-next.13](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.12...api-service-v0.0.3-next.13) (2026-01-19)
102
+
103
+
104
+ ### Miscellaneous Chores
105
+
106
+ * **api-service:** Synchronize repo versions
107
+
108
+
109
+ ### Dependencies
110
+
111
+ * The following workspace dependencies were updated
112
+ * dependencies
113
+ * @twin.org/api-models bumped from 0.0.3-next.12 to 0.0.3-next.13
114
+
115
+ ## [0.0.3-next.12](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.11...api-service-v0.0.3-next.12) (2026-01-12)
116
+
117
+
118
+ ### Miscellaneous Chores
119
+
120
+ * **api-service:** Synchronize repo versions
121
+
122
+
123
+ ### Dependencies
124
+
125
+ * The following workspace dependencies were updated
126
+ * dependencies
127
+ * @twin.org/api-models bumped from 0.0.3-next.11 to 0.0.3-next.12
128
+
129
+ ## [0.0.3-next.11](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.10...api-service-v0.0.3-next.11) (2026-01-08)
130
+
131
+
132
+ ### Miscellaneous Chores
133
+
134
+ * **api-service:** Synchronize repo versions
135
+
136
+
137
+ ### Dependencies
138
+
139
+ * The following workspace dependencies were updated
140
+ * dependencies
141
+ * @twin.org/api-models bumped from 0.0.3-next.10 to 0.0.3-next.11
142
+
143
+ ## [0.0.3-next.10](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.9...api-service-v0.0.3-next.10) (2026-01-05)
144
+
145
+
146
+ ### Miscellaneous Chores
147
+
148
+ * **api-service:** Synchronize repo versions
149
+
150
+
151
+ ### Dependencies
152
+
153
+ * The following workspace dependencies were updated
154
+ * dependencies
155
+ * @twin.org/api-models bumped from 0.0.3-next.9 to 0.0.3-next.10
156
+
157
+ ## [0.0.3-next.9](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.8...api-service-v0.0.3-next.9) (2026-01-05)
158
+
159
+
160
+ ### Features
161
+
162
+ * add context id features ([#42](https://github.com/twinfoundation/api/issues/42)) ([0186055](https://github.com/twinfoundation/api/commit/0186055c48afde842a4254b4df9ac9249c40fe40))
163
+ * add livez endpoint ([#57](https://github.com/twinfoundation/api/issues/57)) ([ef007db](https://github.com/twinfoundation/api/commit/ef007db8201736dd3053211f849ffd03baaa485e))
164
+ * add root, favicon routes ([71da1c3](https://github.com/twinfoundation/api/commit/71da1c3a93c349588aff7084d1d8d6a29a277da8))
165
+ * add validate-locales ([cdba610](https://github.com/twinfoundation/api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e))
166
+ * eslint migration to flat config ([0dd5820](https://github.com/twinfoundation/api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805))
167
+ * remove unused namespace ([08478f2](https://github.com/twinfoundation/api/commit/08478f27efda9beb0271fdb22f6972e918361965))
168
+ * update dependencies ([1171dc4](https://github.com/twinfoundation/api/commit/1171dc416a9481737f6a640e3cf30145768f37e9))
169
+ * update framework core ([d8eebf2](https://github.com/twinfoundation/api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa))
170
+ * update IComponent signatures ([915ce37](https://github.com/twinfoundation/api/commit/915ce37712326ab4aa6869c350eabaa4622e8430))
171
+ * use shared store mechanism ([#19](https://github.com/twinfoundation/api/issues/19)) ([32116df](https://github.com/twinfoundation/api/commit/32116df3b4380a30137f5056f242a5c99afa2df9))
172
+
173
+
174
+ ### Dependencies
175
+
176
+ * The following workspace dependencies were updated
177
+ * dependencies
178
+ * @twin.org/api-models bumped from 0.0.3-next.8 to 0.0.3-next.9
179
+
180
+ ## [0.0.3-next.8](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.7...api-service-v0.0.3-next.8) (2025-12-17)
181
+
182
+
183
+ ### Miscellaneous Chores
184
+
185
+ * **api-service:** Synchronize repo versions
186
+
187
+
188
+ ### Dependencies
189
+
190
+ * The following workspace dependencies were updated
191
+ * dependencies
192
+ * @twin.org/api-models bumped from 0.0.3-next.7 to 0.0.3-next.8
193
+
194
+ ## [0.0.3-next.7](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.6...api-service-v0.0.3-next.7) (2025-11-26)
195
+
196
+
197
+ ### Miscellaneous Chores
198
+
199
+ * **api-service:** Synchronize repo versions
200
+
201
+
202
+ ### Dependencies
203
+
204
+ * The following workspace dependencies were updated
205
+ * dependencies
206
+ * @twin.org/api-models bumped from 0.0.3-next.6 to 0.0.3-next.7
207
+
208
+ ## [0.0.3-next.6](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.5...api-service-v0.0.3-next.6) (2025-11-20)
209
+
210
+
211
+ ### Miscellaneous Chores
212
+
213
+ * **api-service:** Synchronize repo versions
214
+
215
+
216
+ ### Dependencies
217
+
218
+ * The following workspace dependencies were updated
219
+ * dependencies
220
+ * @twin.org/api-models bumped from 0.0.3-next.5 to 0.0.3-next.6
221
+
222
+ ## [0.0.3-next.5](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.4...api-service-v0.0.3-next.5) (2025-11-14)
223
+
224
+
225
+ ### Miscellaneous Chores
226
+
227
+ * **api-service:** Synchronize repo versions
228
+
229
+
230
+ ### Dependencies
231
+
232
+ * The following workspace dependencies were updated
233
+ * dependencies
234
+ * @twin.org/api-models bumped from 0.0.3-next.4 to 0.0.3-next.5
235
+
236
+ ## [0.0.3-next.4](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.3...api-service-v0.0.3-next.4) (2025-11-14)
237
+
238
+
239
+ ### Features
240
+
241
+ * add context id features ([#42](https://github.com/twinfoundation/api/issues/42)) ([0186055](https://github.com/twinfoundation/api/commit/0186055c48afde842a4254b4df9ac9249c40fe40))
242
+ * add root, favicon routes ([71da1c3](https://github.com/twinfoundation/api/commit/71da1c3a93c349588aff7084d1d8d6a29a277da8))
243
+ * add validate-locales ([cdba610](https://github.com/twinfoundation/api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e))
244
+ * eslint migration to flat config ([0dd5820](https://github.com/twinfoundation/api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805))
245
+ * remove unused namespace ([08478f2](https://github.com/twinfoundation/api/commit/08478f27efda9beb0271fdb22f6972e918361965))
246
+ * update dependencies ([1171dc4](https://github.com/twinfoundation/api/commit/1171dc416a9481737f6a640e3cf30145768f37e9))
247
+ * update framework core ([d8eebf2](https://github.com/twinfoundation/api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa))
248
+ * update IComponent signatures ([915ce37](https://github.com/twinfoundation/api/commit/915ce37712326ab4aa6869c350eabaa4622e8430))
249
+ * use shared store mechanism ([#19](https://github.com/twinfoundation/api/issues/19)) ([32116df](https://github.com/twinfoundation/api/commit/32116df3b4380a30137f5056f242a5c99afa2df9))
250
+
251
+
252
+ ### Dependencies
253
+
254
+ * The following workspace dependencies were updated
255
+ * dependencies
256
+ * @twin.org/api-models bumped from 0.0.3-next.3 to 0.0.3-next.4
257
+
258
+ ## [0.0.3-next.3](https://github.com/twinfoundation/api/compare/api-service-v0.0.3-next.2...api-service-v0.0.3-next.3) (2025-11-14)
259
+
260
+
261
+ ### Miscellaneous Chores
262
+
263
+ * **api-service:** Synchronize repo versions
264
+
265
+
266
+ ### Dependencies
267
+
268
+ * The following workspace dependencies were updated
269
+ * dependencies
270
+ * @twin.org/api-models bumped from 0.0.3-next.2 to 0.0.3-next.3
271
+
3
272
  ## [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
273
 
5
274
 
@@ -0,0 +1,131 @@
1
+ # Class: HostingService
2
+
3
+ The hosting service for the server.
4
+
5
+ ## Implements
6
+
7
+ - `IHostingComponent`
8
+
9
+ ## Constructors
10
+
11
+ ### Constructor
12
+
13
+ > **new HostingService**(`options`): `HostingService`
14
+
15
+ Create a new instance of HostingService.
16
+
17
+ #### Parameters
18
+
19
+ ##### options
20
+
21
+ [`IHostingServiceConstructorOptions`](../interfaces/IHostingServiceConstructorOptions.md)
22
+
23
+ The options to create the service.
24
+
25
+ #### Returns
26
+
27
+ `HostingService`
28
+
29
+ ## Properties
30
+
31
+ ### CLASS\_NAME
32
+
33
+ > `readonly` `static` **CLASS\_NAME**: `string`
34
+
35
+ Runtime name for the class.
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
+
51
+ #### Implementation of
52
+
53
+ `IHostingComponent.className`
54
+
55
+ ***
56
+
57
+ ### getPublicOrigin()
58
+
59
+ > **getPublicOrigin**(`serverRequestUrl?`): `Promise`\<`string`\>
60
+
61
+ Get the public origin for the hosting.
62
+
63
+ #### Parameters
64
+
65
+ ##### serverRequestUrl?
66
+
67
+ `string`
68
+
69
+ The url of the current server request if there is one.
70
+
71
+ #### Returns
72
+
73
+ `Promise`\<`string`\>
74
+
75
+ The public origin.
76
+
77
+ #### Implementation of
78
+
79
+ `IHostingComponent.getPublicOrigin`
80
+
81
+ ***
82
+
83
+ ### getTenantOrigin()
84
+
85
+ > **getTenantOrigin**(`tenantId`): `Promise`\<`string` \| `undefined`\>
86
+
87
+ Get the public origin for the tenant if one exists.
88
+
89
+ #### Parameters
90
+
91
+ ##### tenantId
92
+
93
+ `string`
94
+
95
+ The tenant identifier.
96
+
97
+ #### Returns
98
+
99
+ `Promise`\<`string` \| `undefined`\>
100
+
101
+ The public origin for the tenant.
102
+
103
+ #### Implementation of
104
+
105
+ `IHostingComponent.getTenantOrigin`
106
+
107
+ ***
108
+
109
+ ### buildPublicUrl()
110
+
111
+ > **buildPublicUrl**(`url`): `Promise`\<`string`\>
112
+
113
+ Build a public url based on the public origin and the url provided.
114
+
115
+ #### Parameters
116
+
117
+ ##### url
118
+
119
+ `string`
120
+
121
+ The url to build upon the public origin.
122
+
123
+ #### Returns
124
+
125
+ `Promise`\<`string`\>
126
+
127
+ The full url based on the public origin.
128
+
129
+ #### Implementation of
130
+
131
+ `IHostingComponent.buildPublicUrl`
@@ -144,6 +144,24 @@ The OpenAPI spec.
144
144
 
145
145
  ***
146
146
 
147
+ ### livez()
148
+
149
+ > **livez**(): `Promise`\<`boolean`\>
150
+
151
+ Is the server live.
152
+
153
+ #### Returns
154
+
155
+ `Promise`\<`boolean`\>
156
+
157
+ True if the server is live.
158
+
159
+ #### Implementation of
160
+
161
+ `IInformationComponent.livez`
162
+
163
+ ***
164
+
147
165
  ### health()
148
166
 
149
167
  > **health**(): `Promise`\<`IHealthInfo`\>
@@ -164,7 +182,7 @@ The service health.
164
182
 
165
183
  ### setComponentHealth()
166
184
 
167
- > **setComponentHealth**(`name`, `status`, `details?`): `Promise`\<`void`\>
185
+ > **setComponentHealth**(`name`, `status`, `details?`, `tenantId?`): `Promise`\<`void`\>
168
186
 
169
187
  Set the status of a component.
170
188
 
@@ -188,6 +206,12 @@ The status of the component.
188
206
 
189
207
  The details for the status.
190
208
 
209
+ ##### tenantId?
210
+
211
+ `string`
212
+
213
+ The tenant id, optional if the health status is not tenant specific.
214
+
191
215
  #### Returns
192
216
 
193
217
  `Promise`\<`void`\>
@@ -202,7 +226,7 @@ Nothing.
202
226
 
203
227
  ### removeComponentHealth()
204
228
 
205
- > **removeComponentHealth**(`name`): `Promise`\<`void`\>
229
+ > **removeComponentHealth**(`name`, `tenantId?`): `Promise`\<`void`\>
206
230
 
207
231
  Remove the status of a component.
208
232
 
@@ -214,6 +238,12 @@ Remove the status of a component.
214
238
 
215
239
  The component name.
216
240
 
241
+ ##### tenantId?
242
+
243
+ `string`
244
+
245
+ The tenant id, optional if the health status is not tenant specific.
246
+
217
247
  #### Returns
218
248
 
219
249
  `Promise`\<`void`\>
@@ -0,0 +1,31 @@
1
+ # Function: serverLivez()
2
+
3
+ > **serverLivez**(`httpRequestContext`, `componentName`, `request`): `Promise`\<`IServerLivezResponse`\>
4
+
5
+ Get the livez for the server.
6
+
7
+ ## Parameters
8
+
9
+ ### httpRequestContext
10
+
11
+ `IHttpRequestContext`
12
+
13
+ The request context for the API.
14
+
15
+ ### componentName
16
+
17
+ `string`
18
+
19
+ The name of the component to use in the routes.
20
+
21
+ ### request
22
+
23
+ `INoContentRequest`
24
+
25
+ The request.
26
+
27
+ ## Returns
28
+
29
+ `Promise`\<`IServerLivezResponse`\>
30
+
31
+ The response object with additional http response properties.
@@ -2,10 +2,13 @@
2
2
 
3
3
  ## Classes
4
4
 
5
+ - [HostingService](classes/HostingService.md)
5
6
  - [InformationService](classes/InformationService.md)
6
7
 
7
8
  ## Interfaces
8
9
 
10
+ - [IHostingServiceConfig](interfaces/IHostingServiceConfig.md)
11
+ - [IHostingServiceConstructorOptions](interfaces/IHostingServiceConstructorOptions.md)
9
12
  - [IInformationServiceConfig](interfaces/IInformationServiceConfig.md)
10
13
  - [IInformationServiceConstructorOptions](interfaces/IInformationServiceConstructorOptions.md)
11
14
 
@@ -19,6 +22,7 @@
19
22
  - [generateRestRoutesInformation](functions/generateRestRoutesInformation.md)
20
23
  - [serverRoot](functions/serverRoot.md)
21
24
  - [serverInfo](functions/serverInfo.md)
25
+ - [serverLivez](functions/serverLivez.md)
22
26
  - [serverHealth](functions/serverHealth.md)
23
27
  - [serverFavIcon](functions/serverFavIcon.md)
24
28
  - [serverSpec](functions/serverSpec.md)
@@ -0,0 +1,19 @@
1
+ # Interface: IHostingServiceConfig
2
+
3
+ Configuration for the hosting service.
4
+
5
+ ## Properties
6
+
7
+ ### localOrigin
8
+
9
+ > **localOrigin**: `string`
10
+
11
+ The local origin, must be provided as a fallback e.g. http://localhost:1234.
12
+
13
+ ***
14
+
15
+ ### publicOrigin?
16
+
17
+ > `optional` **publicOrigin**: `string`
18
+
19
+ The APIs public base URL e.g. "https://api.example.com:1234".
@@ -0,0 +1,25 @@
1
+ # Interface: IHostingServiceConstructorOptions
2
+
3
+ Options for the IHostingService constructor.
4
+
5
+ ## Properties
6
+
7
+ ### tenantAdminComponentType?
8
+
9
+ > `optional` **tenantAdminComponentType**: `string`
10
+
11
+ The tenant admin component type.
12
+
13
+ #### Default
14
+
15
+ ```ts
16
+ tenant-admin
17
+ ```
18
+
19
+ ***
20
+
21
+ ### config
22
+
23
+ > **config**: [`IHostingServiceConfig`](IHostingServiceConfig.md)
24
+
25
+ The configuration for the service.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/api-service",
3
- "version": "0.0.3-next.2",
3
+ "version": "0.0.3-next.20",
4
4
  "description": "Information contract implementation and REST endpoint definitions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,8 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/api-models": "0.0.3-next.2",
17
+ "@twin.org/api-models": "0.0.3-next.20",
18
+ "@twin.org/context": "next",
18
19
  "@twin.org/core": "next",
19
20
  "@twin.org/nameof": "next",
20
21
  "@twin.org/web": "next"