@webtypen/webframez-core 0.3.109 → 0.3.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,6 +23,24 @@ This README reflects the current API in this repository and focuses on:
23
23
  npm i @webtypen/webframez-core
24
24
  ```
25
25
 
26
+ ## Optional server extensions
27
+
28
+ `WebApplication.boot({ extensions: [...] })` accepts `WebServerExtension` instances.
29
+ Their synchronous `attach({ server, basename, kernel, modulesLoader })` method runs
30
+ after routes are registered and before the HTTP server listens. This allows optional
31
+ transports such as `@webtypen/webframez-sockets` to share the application's port
32
+ without adding their dependencies to Core.
33
+
34
+ Call `await application.close()` to release extensions in reverse order before
35
+ closing HTTP. Cleanup continues if an extension fails, and the returned promise
36
+ reports the failure. Partially attached extensions are also closed after startup
37
+ failure. Application instances boot once; create a new instance after shutdown.
38
+ `port: 0` selects an ephemeral port, and `onBoot(port)` receives the actual port.
39
+
40
+ Socket integrations can report `socket.event.start`, `socket.event.end` and
41
+ `socket.event.error` through `WebframezHooks` using the same operation context as
42
+ HTTP and queue execution.
43
+
26
44
  ## Authentication
27
45
 
28
46
  New integrations can use `SessionAuth` for hashed, revocable device sessions,
@@ -1,4 +1,4 @@
1
- export type WebframezHookOperation = "http.request" | "route.handler" | "console.command" | "queue.job" | "queue.worker" | "app.boot" | "lambda.invoke";
1
+ export type WebframezHookOperation = "http.request" | "route.handler" | "console.command" | "queue.job" | "queue.worker" | "app.boot" | "lambda.invoke" | "socket.event";
2
2
  export type WebframezHookPhase = "start" | "end" | "error";
3
3
  export type WebframezHookEventName = `${WebframezHookOperation}.${WebframezHookPhase}`;
4
4
  export type WebframezHookAttributeValue = string | number | boolean | null;
@@ -0,0 +1,14 @@
1
+ /// <reference types="node" />
2
+ import type { Server } from "http";
3
+ import type { ModulesLoader } from "../Modules/ModulesLoader";
4
+ export type WebServerExtensionContext = {
5
+ server: Server;
6
+ basename: string | null;
7
+ kernel: any;
8
+ modulesLoader: ModulesLoader;
9
+ };
10
+ /** Optional transports attach before listen; close must release their connections. */
11
+ export interface WebServerExtension {
12
+ attach(context: WebServerExtensionContext): void;
13
+ close(): void | Promise<void>;
14
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -4,6 +4,10 @@ import { ModulesLoader } from "./Modules/ModulesLoader";
4
4
  export declare class WebApplication {
5
5
  private server;
6
6
  modulesLoader: ModulesLoader | null;
7
+ private serverExtensions;
8
+ private closing;
9
+ /** Close optional transports before waiting for the HTTP server to stop. */
10
+ close(): Promise<void>;
7
11
  /**
8
12
  * Init the routes and start the http-server
9
13
  */
@@ -30,6 +30,35 @@ const NotificationService_1 = require("./Notifications/NotificationService");
30
30
  class WebApplication {
31
31
  constructor() {
32
32
  this.modulesLoader = null;
33
+ this.serverExtensions = [];
34
+ this.closing = null;
35
+ }
36
+ /** Close optional transports before waiting for the HTTP server to stop. */
37
+ close() {
38
+ if (this.closing)
39
+ return this.closing;
40
+ this.closing = (() => __awaiter(this, void 0, void 0, function* () {
41
+ let failure;
42
+ for (const extension of this.serverExtensions.splice(0).reverse()) {
43
+ try {
44
+ yield extension.close();
45
+ }
46
+ catch (error) {
47
+ failure = failure || error;
48
+ }
49
+ }
50
+ if (this.server) {
51
+ yield new Promise((resolve, reject) => this.server.close((error) => {
52
+ if (error && error.code !== "ERR_SERVER_NOT_RUNNING")
53
+ reject(error);
54
+ else
55
+ resolve();
56
+ }));
57
+ }
58
+ if (failure)
59
+ throw failure;
60
+ }))();
61
+ return this.closing;
33
62
  }
34
63
  /**
35
64
  * Init the routes and start the http-server
@@ -46,6 +75,12 @@ class WebApplication {
46
75
  });
47
76
  }
48
77
  bootScoped(options) {
78
+ if (this.server || this.closing)
79
+ throw new Error("WebApplication instances can only boot once.");
80
+ const extensions = (options === null || options === void 0 ? void 0 : options.extensions) || [];
81
+ if (!Array.isArray(extensions) || extensions.some(extension => !extension || typeof extension.attach !== "function" || typeof extension.close !== "function")) {
82
+ throw new Error("WebApplication extensions must implement attach(context) and close().");
83
+ }
49
84
  const bootOperationId = WebframezHooks_1.WebframezHooks.createOperationId("app");
50
85
  void WebframezHooks_1.WebframezHooks.emit("app.boot.start", {
51
86
  operationId: bootOperationId,
@@ -99,7 +134,7 @@ class WebApplication {
99
134
  if (options.jobs && options.jobs.length > 0) {
100
135
  QueueJobsRegisty_1.QueueJobsRegisty.registerJob(options.jobs);
101
136
  }
102
- const port = options && options.port ? options.port : 3000;
137
+ const port = (options === null || options === void 0 ? void 0 : options.port) !== undefined ? options.port : 3000;
103
138
  this.server = http_1.default.createServer((req, res) => ErrorHandler_1.ErrorHandler.withContext({ applicationType: "WebApplication", scope: "controller", operationId: WebframezHooks_1.WebframezHooks.createOperationId("http") }, () => __awaiter(this, void 0, void 0, function* () {
104
139
  try {
105
140
  const handled = yield HttpKernelHandlerRegistry_1.HttpKernelHandlerRegistry.handle({
@@ -131,6 +166,7 @@ class WebApplication {
131
166
  }
132
167
  })));
133
168
  this.server.on("error", (error) => {
169
+ void this.close().catch(closeError => ErrorHandler_1.ErrorHandler.report(closeError, { source: "http.server.close" }));
134
170
  void ErrorHandler_1.ErrorHandler.report(error, { applicationType: "WebApplication", scope: "boot", source: "http.server" });
135
171
  void WebframezHooks_1.WebframezHooks.emit("app.boot.error", {
136
172
  operationId: bootOperationId,
@@ -149,26 +185,45 @@ class WebApplication {
149
185
  // this.server.on("close", () => {
150
186
  // void SigNozTelemetry.shutdown();
151
187
  // });
152
- this.server.listen(port, () => {
153
- void WebframezHooks_1.WebframezHooks.emit("app.boot.end", {
154
- operationId: bootOperationId,
155
- name: "web",
156
- status: "ok",
157
- attributes: {
158
- "server.port": port,
159
- "webframez.mode": options && options.mode ? options.mode : "web",
160
- },
161
- });
162
- const runtimeConsole = typeof globalThis !== "undefined" ? globalThis.console : undefined;
163
- if (runtimeConsole && typeof runtimeConsole.log === "function") {
164
- runtimeConsole.log("Server started and listening on port " +
165
- port +
166
- (Router_1.Router.basename ? " (Basename: " + Router_1.Router.basename + ")" : ""));
167
- }
168
- if (options && options.onBoot) {
169
- options.onBoot(port);
188
+ try {
189
+ for (const extension of extensions) {
190
+ this.serverExtensions.push(extension);
191
+ extension.attach({ server: this.server, basename: Router_1.Router.basename,
192
+ kernel: (options === null || options === void 0 ? void 0 : options.kernel) || null, modulesLoader: this.modulesLoader });
170
193
  }
171
- });
194
+ }
195
+ catch (error) {
196
+ void this.close().catch(closeError => ErrorHandler_1.ErrorHandler.report(closeError, { source: "http.server.close" }));
197
+ throw error;
198
+ }
199
+ try {
200
+ this.server.listen(port, () => {
201
+ const address = this.server.address();
202
+ const listeningPort = address && typeof address === "object" ? address.port : port;
203
+ void WebframezHooks_1.WebframezHooks.emit("app.boot.end", {
204
+ operationId: bootOperationId,
205
+ name: "web",
206
+ status: "ok",
207
+ attributes: {
208
+ "server.port": listeningPort,
209
+ "webframez.mode": options && options.mode ? options.mode : "web",
210
+ },
211
+ });
212
+ const runtimeConsole = typeof globalThis !== "undefined" ? globalThis.console : undefined;
213
+ if (runtimeConsole && typeof runtimeConsole.log === "function") {
214
+ runtimeConsole.log("Server started and listening on port " +
215
+ listeningPort +
216
+ (Router_1.Router.basename ? " (Basename: " + Router_1.Router.basename + ")" : ""));
217
+ }
218
+ if (options && options.onBoot) {
219
+ options.onBoot(listeningPort);
220
+ }
221
+ });
222
+ }
223
+ catch (error) {
224
+ void this.close().catch(closeError => ErrorHandler_1.ErrorHandler.report(closeError, { source: "http.server.close" }));
225
+ throw error;
226
+ }
172
227
  return this.server;
173
228
  }
174
229
  }
package/dist/index.d.ts CHANGED
@@ -61,6 +61,7 @@ export * from "./Api/ApiScopeRegistry";
61
61
  export * from "./Ai/AiTypes";
62
62
  export * from "./Mcp/McpTypes";
63
63
  export * from "./Http/HttpKernelHandlerRegistry";
64
+ export * from "./Http/WebServerExtension";
64
65
  export * from "./Auth";
65
66
  export * from "./Notifications/Notification";
66
67
  export * from "./Notifications/BaseNotificationsController";
package/dist/index.js CHANGED
@@ -78,6 +78,7 @@ __exportStar(require("./Api/ApiScopeRegistry"), exports);
78
78
  __exportStar(require("./Ai/AiTypes"), exports);
79
79
  __exportStar(require("./Mcp/McpTypes"), exports);
80
80
  __exportStar(require("./Http/HttpKernelHandlerRegistry"), exports);
81
+ __exportStar(require("./Http/WebServerExtension"), exports);
81
82
  __exportStar(require("./Auth"), exports);
82
83
  __exportStar(require("./Notifications/Notification"), exports);
83
84
  __exportStar(require("./Notifications/BaseNotificationsController"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-core",
3
- "version": "0.3.109",
3
+ "version": "0.3.110",
4
4
  "description": "The core package of the typescript based webframez (web-framework) by webtypen.de",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "license": "MIT",