@flancer32/teq-web 0.4.0 → 0.6.0

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +236 -127
  3. package/ai/AGENTS.md +36 -0
  4. package/ai/abstractions.md +75 -0
  5. package/ai/examples/minimal-server.md +79 -0
  6. package/ai/overview.md +29 -0
  7. package/ai/rules.md +44 -0
  8. package/package.json +43 -37
  9. package/src/Back/Api/Handler.mjs +26 -0
  10. package/src/Back/Config/Runtime/Tls.mjs +89 -0
  11. package/src/Back/Config/Runtime.mjs +114 -0
  12. package/src/Back/Dto/Info.mjs +62 -0
  13. package/src/Back/Dto/RequestContext.mjs +33 -0
  14. package/src/Back/Dto/{Handler/Source.js → Source.mjs} +24 -25
  15. package/src/Back/Enum/Server/Type.mjs +13 -0
  16. package/src/Back/Enum/Stage.mjs +13 -0
  17. package/src/Back/Handler/Pre/Log.mjs +55 -0
  18. package/src/Back/Handler/Static/A/{Config.js → Config.mjs} +11 -4
  19. package/src/Back/Handler/Static/A/{Fallback.js → Fallback.mjs} +12 -4
  20. package/src/Back/Handler/Static/A/{FileService.js → FileService.mjs} +30 -17
  21. package/src/Back/Handler/Static/A/{Registry.js → Registry.mjs} +15 -7
  22. package/src/Back/Handler/Static/A/{Resolver.js → Resolver.mjs} +10 -3
  23. package/src/Back/Handler/Static.mjs +79 -0
  24. package/src/Back/Helper/Cast.mjs +116 -0
  25. package/src/Back/Helper/{Mime.js → Mime.mjs} +2 -0
  26. package/src/Back/Helper/Order/Kahn.mjs +69 -0
  27. package/src/Back/Helper/{Respond.js → Respond.mjs} +14 -7
  28. package/src/Back/Logger.mjs +57 -0
  29. package/src/Back/PipelineEngine.mjs +216 -0
  30. package/src/Back/{Server.js → Server.mjs} +38 -30
  31. package/types.d.ts +45 -22
  32. package/src/AGENTS.md +0 -108
  33. package/src/Back/Api/Handler.js +0 -26
  34. package/src/Back/Defaults.js +0 -6
  35. package/src/Back/Dispatcher.js +0 -115
  36. package/src/Back/Dto/Handler/Info.js +0 -68
  37. package/src/Back/Enum/Server/Type.js +0 -12
  38. package/src/Back/Enum/Stage.js +0 -10
  39. package/src/Back/Handler/Pre/Log.js +0 -45
  40. package/src/Back/Handler/Static.js +0 -63
  41. package/src/Back/Helper/Cast.js +0 -114
  42. package/src/Back/Helper/Order/Kahn.js +0 -66
  43. package/src/Back/Logger.js +0 -53
  44. package/src/Back/Server/Config/Tls.js +0 -55
  45. package/src/Back/Server/Config.js +0 -69
  46. package/teqfw.json +0 -8
@@ -0,0 +1,216 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Pipeline Engine is the single request-lifecycle coordination component.
5
+ * It executes handlers in three deterministic stages:
6
+ * `INIT -> PROCESS -> FINALIZE`.
7
+ */
8
+ export const __deps__ = Object.freeze({
9
+ dtoRequestContextFactory: 'Fl32_Web_Back_Dto_RequestContext__Factory$',
10
+ logger: 'Fl32_Web_Back_Logger$',
11
+ respond: 'Fl32_Web_Back_Helper_Respond$',
12
+ helpOrder: 'Fl32_Web_Back_Helper_Order_Kahn$',
13
+ STAGE: 'Fl32_Web_Back_Enum_Stage$',
14
+ });
15
+
16
+ const KEY_STAGE = Symbol('stage');
17
+
18
+ export default class Fl32_Web_Back_PipelineEngine {
19
+ /* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
20
+ /**
21
+ * @param {object} params
22
+ * @param {Fl32_Web_Back_Dto_RequestContext$Factory} params.dtoRequestContextFactory
23
+ * @param {Fl32_Web_Back_Logger} params.logger
24
+ * @param {Fl32_Web_Back_Helper_Respond} params.respond
25
+ * @param {Fl32_Web_Back_Helper_Order_Kahn} params.helpOrder
26
+ * @param {Fl32_Web_Back_Enum_Stage} params.STAGE
27
+ */
28
+ constructor(
29
+ {
30
+ dtoRequestContextFactory,
31
+ logger,
32
+ respond,
33
+ helpOrder,
34
+ STAGE,
35
+ }
36
+ ) {
37
+ /* eslint-enable jsdoc/require-param-description,jsdoc/check-param-names */
38
+ /** @type {Map<string, Fl32_Web_Back_Api_Handler>} */
39
+ const handlers = new Map();
40
+ /** @type {Fl32_Web_Back_Api_Handler[]} */
41
+ let initHandlers = [];
42
+ /** @type {Fl32_Web_Back_Api_Handler[]} */
43
+ let processHandlers = [];
44
+ /** @type {Fl32_Web_Back_Api_Handler[]} */
45
+ let finalizeHandlers = [];
46
+ let isLocked = false;
47
+
48
+ /**
49
+ * @param {Fl32_Web_Node_Http_IncomingMessage|Fl32_Web_Node_Http2_ServerRequest} request
50
+ * @param {Fl32_Web_Back_Response_Target} response
51
+ * @returns {Fl32_Web_Back_Dto_RequestContext}
52
+ */
53
+ function createRequestContext(request, response) {
54
+ let completed = false;
55
+ /** @type {Fl32_Web_Back_Dto_RequestContext & {[KEY_STAGE]: string|null}} */
56
+ const context = dtoRequestContextFactory.create();
57
+ context.request = request;
58
+ context.response = response;
59
+ context.data = {};
60
+ context.completed = false;
61
+ context.complete = () => {
62
+ context.completed = true;
63
+ };
64
+ context.isCompleted = () => completed;
65
+ context[KEY_STAGE] = null;
66
+
67
+ Object.defineProperty(context, 'completed', {
68
+ configurable: false,
69
+ enumerable: true,
70
+ get() {
71
+ return completed;
72
+ },
73
+ set(value) {
74
+ if (value !== true && value !== false) {
75
+ throw new Error('Request completion flag accepts only boolean values');
76
+ }
77
+ if (value === false && completed) {
78
+ throw new Error('Request completion flag cannot be reset');
79
+ }
80
+ if (value === true && !completed) {
81
+ if (context[KEY_STAGE] !== STAGE.PROCESS) {
82
+ throw new Error('Only PROCESS handlers may complete request processing');
83
+ }
84
+ completed = true;
85
+ }
86
+ },
87
+ });
88
+
89
+ return context;
90
+ }
91
+
92
+ /**
93
+ * @param {Fl32_Web_Back_Api_Handler} handler
94
+ * @param {string} stage
95
+ * @param {Fl32_Web_Back_Dto_RequestContext & {[KEY_STAGE]: string|null}} context
96
+ * @returns {Promise<void>}
97
+ */
98
+ async function runHandler(handler, stage, context) {
99
+ context[KEY_STAGE] = stage;
100
+ await handler.handle(context);
101
+ }
102
+
103
+ /**
104
+ * @returns {void}
105
+ */
106
+ this.orderHandlers = function () {
107
+ const init = [];
108
+ const process = [];
109
+ const finalize = [];
110
+
111
+ for (const handler of handlers.values()) {
112
+ const info = handler.getRegistrationInfo();
113
+ if (info.stage === STAGE.INIT) {
114
+ init.push(handler);
115
+ } else if (info.stage === STAGE.PROCESS) {
116
+ process.push(handler);
117
+ } else if (info.stage === STAGE.FINALIZE) {
118
+ finalize.push(handler);
119
+ } else {
120
+ throw new Error(`Unsupported handler stage '${String(info.stage)}'`);
121
+ }
122
+ }
123
+
124
+ initHandlers = helpOrder.sort(init);
125
+ processHandlers = helpOrder.sort(process);
126
+ finalizeHandlers = helpOrder.sort(finalize);
127
+ isLocked = true;
128
+ };
129
+
130
+ /**
131
+ * @returns {void}
132
+ */
133
+ this.lockHandlers = () => this.orderHandlers();
134
+
135
+ /**
136
+ * @param {Fl32_Web_Back_Api_Handler} handler
137
+ * @returns {void}
138
+ */
139
+ this.addHandler = function (handler) {
140
+ if (isLocked) {
141
+ throw new Error('Handler registration is locked after pipeline ordering');
142
+ }
143
+ const info = handler.getRegistrationInfo();
144
+ if (!info?.name) {
145
+ throw new Error('Handler registration requires a non-empty name');
146
+ }
147
+ handlers.set(info.name, handler);
148
+ };
149
+
150
+ /**
151
+ * @param {Fl32_Web_Back_Api_Handler} handler
152
+ * @returns {void}
153
+ */
154
+ this.registerHandler = (handler) => this.addHandler(handler);
155
+
156
+ /**
157
+ * @param {Fl32_Web_Node_Http_IncomingMessage|Fl32_Web_Node_Http2_ServerRequest} req
158
+ * @param {Fl32_Web_Back_Response_Target} res
159
+ * @returns {Promise<void>}
160
+ */
161
+ this.onEventRequest = async function (req, res) {
162
+ if (!isLocked) {
163
+ this.orderHandlers();
164
+ }
165
+ /** @type {Fl32_Web_Back_Dto_RequestContext & {[KEY_STAGE]: string|null}} */
166
+ const context = createRequestContext(req, res);
167
+
168
+ try {
169
+ for (const handler of initHandlers) {
170
+ try {
171
+ await runHandler(handler, STAGE.INIT, context);
172
+ } catch (error) {
173
+ logger.exception(error);
174
+ }
175
+ }
176
+
177
+ for (const handler of processHandlers) {
178
+ if (context.isCompleted()) {
179
+ break;
180
+ }
181
+ try {
182
+ await runHandler(handler, STAGE.PROCESS, context);
183
+ } catch (error) {
184
+ logger.exception(error);
185
+ if (respond.isWritable(res)) {
186
+ respond.code500_InternalServerError({res, body: 'Internal Server Error'});
187
+ }
188
+ context.completed = true;
189
+ break;
190
+ }
191
+ }
192
+
193
+ if (!context.isCompleted() && respond.isWritable(res)) {
194
+ logger.error(`404 Not Found: ${req.url}`);
195
+ respond.code404_NotFound({res});
196
+ }
197
+ } finally {
198
+ for (const handler of finalizeHandlers) {
199
+ try {
200
+ await runHandler(handler, STAGE.FINALIZE, context);
201
+ } catch (error) {
202
+ logger.exception(error);
203
+ }
204
+ }
205
+ context[KEY_STAGE] = null;
206
+ }
207
+ };
208
+
209
+ /**
210
+ * @param {Fl32_Web_Node_Http_IncomingMessage|Fl32_Web_Node_Http2_ServerRequest} req
211
+ * @param {Fl32_Web_Back_Response_Target} res
212
+ * @returns {Promise<void>}
213
+ */
214
+ this.handleRequest = async (req, res) => this.onEventRequest(req, res);
215
+ }
216
+ }
@@ -1,55 +1,63 @@
1
+ // @ts-check
2
+
1
3
  /**
2
4
  * Web server implementation supporting HTTP/1 and HTTP/2 protocols.
3
- * Handles incoming requests and delegates them to the dispatcher.
4
- *
5
- * @property {function(): module:http.Server} getInstance - Returns the server instance.
6
- * @property {function(Fl32_Web_Back_Server_Config.Dto): Promise<void>} start - Starts the server with given configuration.
7
- * @property {function(): Promise<void>} stop - Stops the server.
5
+ * Handles incoming requests and delegates them to the Pipeline Engine.
8
6
  */
7
+ export const __deps__ = Object.freeze({
8
+ http: 'node_http',
9
+ http2: 'node_http2',
10
+ config: 'Fl32_Web_Back_Config_Runtime$',
11
+ logger: 'Fl32_Web_Back_Logger$',
12
+ pipelineEngine: 'Fl32_Web_Back_PipelineEngine$',
13
+ SERVER_TYPE: 'Fl32_Web_Back_Enum_Server_Type$',
14
+ });
15
+
9
16
  export default class Fl32_Web_Back_Server {
10
17
  /* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
11
18
  /**
12
- * @param {typeof import('node:http')} http
13
- * @param {typeof import('node:http2')} http2
14
- * @param {Fl32_Web_Back_Defaults} DEF
15
- * @param {Fl32_Web_Back_Logger} logger
16
- * @param {Fl32_Web_Back_Dispatcher} dispatcher
17
- * @param {typeof Fl32_Web_Back_Enum_Server_Type} SERVER_TYPE
19
+ * @param {object} deps
20
+ * @param {Fl32_Web_Node_Http} deps.http
21
+ * @param {Fl32_Web_Node_Http2} deps.http2
22
+ * @param {Fl32_Web_Back_Config_Runtime} deps.config
23
+ * @param {Fl32_Web_Back_Logger} deps.logger
24
+ * @param {Fl32_Web_Back_PipelineEngine} deps.pipelineEngine
25
+ * @param {Fl32_Web_Back_Enum_Server_Type} deps.SERVER_TYPE
18
26
  */
19
27
  constructor(
20
28
  {
21
- 'node:http': http,
22
- 'node:http2': http2,
23
- Fl32_Web_Back_Defaults$: DEF,
24
- Fl32_Web_Back_Logger$: logger,
25
- Fl32_Web_Back_Dispatcher$: dispatcher,
26
- Fl32_Web_Back_Enum_Server_Type$: SERVER_TYPE,
29
+ http,
30
+ http2,
31
+ config,
32
+ logger,
33
+ pipelineEngine,
34
+ SERVER_TYPE,
27
35
  }
28
36
  ) {
29
37
  /* eslint-enable jsdoc/require-param-description,jsdoc/check-param-names */
30
38
  // VARS
31
- const {createServer} = http;
32
- const {createServer: createServerH2, createSecureServer} = http2;
33
- /** @type {module:http.Server} */
39
+ const { createServer } = http;
40
+ const { createServer: createServerH2, createSecureServer } = http2;
41
+ /** @type {Fl32_Web_Node_Http_Server} */
34
42
  let _instance;
35
43
 
36
44
  // MAIN
37
45
  /**
38
- * @returns {module:http.Server}
46
+ * @returns {Fl32_Web_Node_Http_Server}
39
47
  */
40
48
  this.getInstance = () => _instance;
41
49
 
42
50
  /**
43
51
  * Starts the server with optional configuration.
44
- * @param {Fl32_Web_Back_Server_Config.Dto} [cfg] - Server configuration
52
+ * @param {Fl32_Web_Back_Config_Runtime_Server} [cfg]
45
53
  * @returns {Promise<void>}
46
54
  */
47
55
  this.start = async function (cfg) {
48
- // order handlers in the dispatcher
49
- dispatcher.orderHandlers();
56
+ pipelineEngine.lockHandlers();
50
57
  // create server
51
- const port = cfg?.port ?? DEF.PORT;
52
- const type = cfg?.type ?? SERVER_TYPE.HTTP;
58
+ const port = cfg?.port ?? config.server.port;
59
+ const type = cfg?.type ?? config.server.type;
60
+ const tls = cfg?.tls ?? config.server.tls;
53
61
 
54
62
  if (type === SERVER_TYPE.HTTP2) {
55
63
  _instance = createServerH2();
@@ -58,18 +66,18 @@ export default class Fl32_Web_Back_Server {
58
66
  _instance = createServer({});
59
67
  logger.info(`Starting server in HTTP/1 mode on port ${port}...`);
60
68
  } else if (type === SERVER_TYPE.HTTPS) {
61
- if (!cfg.tls?.key || !cfg.tls?.cert) {
69
+ if (!tls?.key || !tls?.cert) {
62
70
  logger.error('HTTPS server requires TLS key and certificate');
63
71
  throw new Error('TLS key and certificate are required for HTTPS server');
64
72
  }
65
- _instance = createSecureServer(cfg.tls);
73
+ _instance = createSecureServer(tls);
66
74
  logger.info(`Starting server in HTTPS (HTTP/2 + TLS) mode on port ${port}...`);
67
75
  } else {
68
76
  logger.error(`Unsupported server type: ${type}`);
69
77
  throw new Error(`Server type '${type}' is not supported`);
70
78
  }
71
79
 
72
- _instance.on('request', dispatcher.onEventRequest);
80
+ _instance.on('request', pipelineEngine.handleRequest);
73
81
  _instance.listen(port);
74
82
  };
75
83
 
@@ -89,4 +97,4 @@ export default class Fl32_Web_Back_Server {
89
97
  }
90
98
  };
91
99
  }
92
- }
100
+ }
package/types.d.ts CHANGED
@@ -1,25 +1,48 @@
1
1
  declare global {
2
- type Fl32_Web_Back_Api_Handler = import("./src/Back/Api/Handler.js").default;
3
- type Fl32_Web_Back_Defaults = import("./src/Back/Defaults.js").default;
4
- type Fl32_Web_Back_Dispatcher = import("./src/Back/Dispatcher.js").default;
5
- type Fl32_Web_Back_Dto_Handler_Info = import("./src/Back/Dto/Handler/Info.js").default;
6
- type Fl32_Web_Back_Dto_Handler_Source = import("./src/Back/Dto/Handler/Source.js").default;
7
- type Fl32_Web_Back_Enum_Server_Type = import("./src/Back/Enum/Server/Type.js").default;
8
- type Fl32_Web_Back_Enum_Stage = import("./src/Back/Enum/Stage.js").default;
9
- type Fl32_Web_Back_Handler_Pre_Log = import("./src/Back/Handler/Pre/Log.js").default;
10
- type Fl32_Web_Back_Handler_Static = import("./src/Back/Handler/Static.js").default;
11
- type Fl32_Web_Back_Handler_Static_A_Config = import("./src/Back/Handler/Static/A/Config.js").default;
12
- type Fl32_Web_Back_Handler_Static_A_Fallback = import("./src/Back/Handler/Static/A/Fallback.js").default;
13
- type Fl32_Web_Back_Handler_Static_A_FileService = import("./src/Back/Handler/Static/A/FileService.js").default;
14
- type Fl32_Web_Back_Handler_Static_A_Registry = import("./src/Back/Handler/Static/A/Registry.js").default;
15
- type Fl32_Web_Back_Handler_Static_A_Resolver = import("./src/Back/Handler/Static/A/Resolver.js").default;
16
- type Fl32_Web_Back_Helper_Cast = import("./src/Back/Helper/Cast.js").default;
17
- type Fl32_Web_Back_Helper_Mime = import("./src/Back/Helper/Mime.js").default;
18
- type Fl32_Web_Back_Helper_Order_Kahn = import("./src/Back/Helper/Order/Kahn.js").default;
19
- type Fl32_Web_Back_Helper_Respond = import("./src/Back/Helper/Respond.js").default;
20
- type Fl32_Web_Back_Logger = import("./src/Back/Logger.js").default;
21
- type Fl32_Web_Back_Server = import("./src/Back/Server.js").default;
22
- type Fl32_Web_Back_Server_Config = import("./src/Back/Server/Config.js").default;
23
- type Fl32_Web_Back_Server_Config_Tls = import("./src/Back/Server/Config/Tls.js").default;
2
+ type Fl32_Web_Back_Api_Handler = import("./src/Back/Api/Handler.mjs").default;
3
+ type Fl32_Web_Back_Config_Runtime = import("./src/Back/Config/Runtime.mjs").Data;
4
+ type Fl32_Web_Back_Config_Runtime$Factory = import("./src/Back/Config/Runtime.mjs").Factory;
5
+ type Fl32_Web_Back_Config_Runtime_Server = import("./src/Back/Config/Runtime.mjs").Server;
6
+ type Fl32_Web_Back_Config_Runtime_Tls = import("./src/Back/Config/Runtime/Tls.mjs").Data;
7
+ type Fl32_Web_Back_Config_Runtime_Tls$Factory = import("./src/Back/Config/Runtime/Tls.mjs").Factory;
8
+ type Fl32_Web_Back_Config_Runtime_Params = {server?: {port?: unknown, type?: unknown, tls?: unknown}};
9
+ type Fl32_Web_Back_Config_Runtime_Tls_Params = {ca?: unknown, cert?: unknown, key?: unknown};
10
+ type Fl32_Web_Back_Dto_Info = import("./src/Back/Dto/Info.mjs").default;
11
+ type Fl32_Web_Back_Dto_Info$Factory = import("./src/Back/Dto/Info.mjs").Factory;
12
+ type Fl32_Web_Back_Dto_RequestContext = import("./src/Back/Dto/RequestContext.mjs").default;
13
+ type Fl32_Web_Back_Dto_RequestContext$Factory = import("./src/Back/Dto/RequestContext.mjs").Factory;
14
+ type Fl32_Web_Back_Dto_Source = import("./src/Back/Dto/Source.mjs").default;
15
+ type Fl32_Web_Back_Dto_Source$Factory = import("./src/Back/Dto/Source.mjs").Factory;
16
+ type Fl32_Web_Back_Enum_Server_Type = import("./src/Back/Enum/Server/Type.mjs").default;
17
+ type Fl32_Web_Back_Enum_Stage = import("./src/Back/Enum/Stage.mjs").default;
18
+ type Fl32_Web_Back_Handler_Pre_Log = import("./src/Back/Handler/Pre/Log.mjs").default;
19
+ type Fl32_Web_Back_Handler_Static = import("./src/Back/Handler/Static.mjs").default;
20
+ type Fl32_Web_Back_Handler_Static_A_Config = import("./src/Back/Handler/Static/A/Config.mjs").default;
21
+ type Fl32_Web_Back_Handler_Static_A_Config_Value = {root: string, prefix: string, allow?: Record<string, string[]>, defaults: string[]};
22
+ type Fl32_Web_Back_Handler_Static_A_Fallback = import("./src/Back/Handler/Static/A/Fallback.mjs").default;
23
+ type Fl32_Web_Back_Handler_Static_A_FileService = import("./src/Back/Handler/Static/A/FileService.mjs").default;
24
+ type Fl32_Web_Back_Handler_Static_A_Match = {config: Fl32_Web_Back_Handler_Static_A_Config_Value, rel: string};
25
+ type Fl32_Web_Back_Handler_Static_A_Registry = import("./src/Back/Handler/Static/A/Registry.mjs").default;
26
+ type Fl32_Web_Back_Handler_Static_A_Resolver = import("./src/Back/Handler/Static/A/Resolver.mjs").default;
27
+ type Fl32_Web_Back_Helper_Cast = import("./src/Back/Helper/Cast.mjs").default;
28
+ type Fl32_Web_Back_Helper_Mime = import("./src/Back/Helper/Mime.mjs").default;
29
+ type Fl32_Web_Back_Helper_Order_Kahn = import("./src/Back/Helper/Order/Kahn.mjs").default;
30
+ type Fl32_Web_Back_Helper_Respond = import("./src/Back/Helper/Respond.mjs").default;
31
+ type Fl32_Web_Back_Logger = import("./src/Back/Logger.mjs").default;
32
+ type Fl32_Web_Back_PipelineEngine = import("./src/Back/PipelineEngine.mjs").default;
33
+ type Fl32_Web_Back_Response_Body = string | object;
34
+ type Fl32_Web_Back_Response_Headers = {[key: string]: string};
35
+ type Fl32_Web_Back_Response_Target = Fl32_Web_Node_Http_ServerResponse | Fl32_Web_Node_Http2_ServerResponse;
36
+ type Fl32_Web_Back_Server = import("./src/Back/Server.mjs").default;
37
+ type Fl32_Web_Node_Fs = typeof import("node:fs");
38
+ type Fl32_Web_Node_Http = typeof import("node:http");
39
+ type Fl32_Web_Node_Http_Server = import("node:http").Server;
40
+ type Fl32_Web_Node_Http_IncomingMessage = import("node:http").IncomingMessage;
41
+ type Fl32_Web_Node_Http_ServerResponse = import("node:http").ServerResponse;
42
+ type Fl32_Web_Node_Http2 = typeof import("node:http2");
43
+ type Fl32_Web_Node_Http2_ServerRequest = import("node:http2").Http2ServerRequest;
44
+ type Fl32_Web_Node_Http2_ServerResponse = import("node:http2").Http2ServerResponse;
45
+ type Fl32_Web_Node_Path = typeof import("node:path");
24
46
  }
47
+
25
48
  export {};
package/src/AGENTS.md DELETED
@@ -1,108 +0,0 @@
1
- # AI Agents Configuration for Source Directory
2
-
3
- This document defines **long-term, project-agnostic** conventions that AI agents must follow when reading, creating, refactoring, or testing code under any `src/` directory. These guidelines are **permanent** and apply to **all** tasks in this area.
4
-
5
- ---
6
-
7
- ## 1. Directory & File Organization
8
-
9
- * **AZ-structuring**
10
-
11
- * **A-struct**: decompose a single “root” module into its private implementation parts under an `A/` subfolder.
12
-
13
- ```text
14
- src/Feature/Component.js
15
- src/Feature/Component/A/Part1.js
16
- src/Feature/Component/A/Part2.js
17
- ```
18
-
19
- Files under `A/` are private to their parent.
20
- * **Z-struct**: helper modules whose changes do not affect siblings live under a `Z/` subfolder.
21
-
22
- * **Visibility boundaries**: anything **outside** `A/` or `Z/` is public API.
23
-
24
- * **Role → Feature layering**
25
-
26
- * First level: by architectural layer (e.g. `Back/`, `Front/`, `Shared/`).
27
- * Second level: by business feature or component.
28
-
29
- * **Mirror in tests**
30
-
31
- * For every `src/.../X.js` there must be a matching `test/unit/.../X.test.mjs` with the same relative path and filename.
32
-
33
- ---
34
-
35
- ## 2. Class Naming (FQN)
36
-
37
- * Map file path to fully qualified class name (FQN). Example:
38
-
39
- ```text
40
- src/Back/Handler/Static/A/Config.js => class Back_Handler_Static_A_Config
41
- ```
42
-
43
- ---
44
-
45
- ## 3. Dependency Injection, `this` & Closures
46
-
47
- * **DI Container**
48
-
49
- * Inject all external services (file system, path, network, helpers, loggers, factories, enums) via a DI container.
50
- * **Private state via closure**
51
-
52
- * Do **not** assign injected dependencies to `this`.
53
- * Capture each in a `const` inside the constructor and expose only public methods.
54
-
55
- ```js
56
- class Example {
57
- constructor({helperService, logger}) {
58
- this.handle = (req, res) => {
59
- if (!helperService.isWritable(res)) return false;
60
- logger.info('Processing');
61
- return true;
62
- };
63
- }
64
- }
65
- ```
66
-
67
- ---
68
-
69
- ## 4. Comments & Annotations
70
-
71
- * **Language**: all comments and JSDoc must be in **English**.
72
- * **JSDoc**: use `@param {Type}` and `@returns {Type}` on every public method.
73
- * **Class annotation**: annotate all classes with a JSDoc `@class` tag and description.
74
-
75
- ---
76
-
77
- ## 5. Error Handling
78
-
79
- * **Module-level**
80
-
81
- * Do **not** catch all exceptions in each module.
82
- * Catch only **expected, domain-specific** errors when a meaningful fallback exists.
83
- * **Top-level handler**
84
-
85
- * Wrap the entire pipeline in a single `try/catch` at the entry point.
86
- * In that catch, use `logger.exception(error)` and return a safe default (`false`, `null`, or `[]`).
87
-
88
- ---
89
-
90
- ## 6. Testing Conventions
91
-
92
- Refer to the [AI Agent Unit Testing Instructions](../test/unit/AGENTS.md) for project-specific testing guidelines.
93
-
94
- ---
95
-
96
- ## 7. Ongoing Responsibilities
97
-
98
- AI agents must always:
99
-
100
- 1. **Enforce AZ-structuring** when adding or refactoring code.
101
- 2. **Preserve FQN mapping** between file paths and class names.
102
- 3. **Use DI + closures**—never attach injected services to `this`.
103
- 4. **Write English JSDoc** for every new or modified public API, including `@class` on all classes.
104
- 5. **Catch and log errors** at the top level; handle known cases locally.
105
- 6. **Generate or update unit tests** with full coverage for every behavior change (see unit testing guidelines).
106
- 7. **Maintain test-source mirror** so no source file exists without a corresponding test.
107
-
108
- These policies are **permanent** and must be validated on every code change.
@@ -1,26 +0,0 @@
1
- /**
2
- * Interface for web request handlers used by the dispatcher in this plugin.
3
- * Defines the structure and behavior of a handler.
4
- *
5
- * @interface
6
- */
7
- export default class Fl32_Web_Back_Api_Handler {
8
- /* eslint-disable no-unused-vars */
9
- /**
10
- * Handles the incoming web request.
11
- * @param {module:http.IncomingMessage|module:http2.Http2ServerRequest} req - The incoming HTTP request object.
12
- * @param {module:http.ServerResponse|module:http2.Http2ServerResponse} res - The HTTP response object.
13
- * @returns {Promise<boolean>} - Returns true if the request was handled, false otherwise.
14
- */
15
- async handle(req, res) {
16
- throw new Error('Method not implemented');
17
- }
18
-
19
- /**
20
- * Provides metadata for dispatcher registration.
21
- * @returns {Fl32_Web_Back_Dto_Handler_Info.Dto}
22
- */
23
- getRegistrationInfo() {
24
- throw new Error('Method not implemented');
25
- }
26
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * Plugin constants (hardcoded configuration) for backend code.
3
- */
4
- export default class Fl32_Web_Back_Defaults {
5
- PORT = 3000;
6
- }
@@ -1,115 +0,0 @@
1
- /**
2
- * Dispatcher for HTTP(S) requests with three-stage handler execution: pre, process, post.
3
- *
4
- * Handlers are registered via `addHandler()` and activated by `orderHandlers()`.
5
- * On each request, pre-handlers run first, then process-handlers (until one returns true),
6
- * and finally post-handlers are always executed.
7
- *
8
- * If no process handler responds, a 404 is sent. If one throws, a 500 is returned.
9
- */
10
- export default class Fl32_Web_Back_Dispatcher {
11
- /* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
12
- /**
13
- * @param {Fl32_Web_Back_Logger} logger
14
- * @param {Fl32_Web_Back_Helper_Respond} respond
15
- * @param {Fl32_Web_Back_Helper_Order_Kahn} helpOrder
16
- * @param {typeof Fl32_Web_Back_Enum_Stage} STAGE
17
- */
18
- constructor(
19
- {
20
- Fl32_Web_Back_Logger$: logger,
21
- Fl32_Web_Back_Helper_Respond$: respond,
22
- Fl32_Web_Back_Helper_Order_Kahn$: helpOrder,
23
- Fl32_Web_Back_Enum_Stage$: STAGE,
24
- }
25
- ) {
26
- /* eslint-enable jsdoc/check-param-names */
27
- // VARS
28
- /** @type {Map<string, Fl32_Web_Back_Api_Handler>} */
29
- const _handlers = new Map();
30
-
31
- /** @type {Fl32_Web_Back_Api_Handler[]} */
32
- let _pre = [], _process = [], _post = [];
33
-
34
- // MAIN
35
-
36
- /**
37
- * Registers a handler. Requires a unique name and stage.
38
- *
39
- * @param {Fl32_Web_Back_Api_Handler} handler
40
- */
41
- this.addHandler = function (handler) {
42
- const info = handler.getRegistrationInfo();
43
- _handlers.set(info.name, handler);
44
- };
45
-
46
- /**
47
- * Executes registered handlers for one HTTP request.
48
- *
49
- * @param {module:http.IncomingMessage|module:http2.Http2ServerRequest} req
50
- * @param {module:http.ServerResponse|module:http2.Http2ServerResponse} res
51
- * @returns {Promise<void>}
52
- */
53
- this.onEventRequest = async function (req, res) {
54
- try {
55
- // Execute all pre-handlers (errors are isolated)
56
- for (const h of _pre) {
57
- try {
58
- await h.handle(req, res);
59
- } catch (e) {
60
- logger.exception(e);
61
- }
62
- }
63
-
64
- // Execute process-handlers (stop at first successful)
65
- let handled = false;
66
- for (const h of _process) {
67
- try {
68
- handled = await h.handle(req, res);
69
- if (handled) break;
70
- } catch (e) {
71
- logger.exception(e);
72
- respond.code500_InternalServerError({res, body: e?.message});
73
- break;
74
- }
75
- }
76
-
77
- // If not handled at all and no error occurred
78
- if (respond.isWritable(res)) {
79
- logger.error(`404 Not Found: ${req.url}`);
80
- respond.code404_NotFound({res});
81
- }
82
- } finally {
83
- // Always run all post-handlers (errors are isolated)
84
- for (const h of _post) {
85
- try {
86
- await h.handle(req, res);
87
- } catch (e) {
88
- logger.exception(e);
89
- }
90
- }
91
- }
92
- };
93
-
94
- /**
95
- * Sorts registered handlers by stage.
96
- */
97
- this.orderHandlers = function () {
98
- const pre = [], process = [], post = [];
99
-
100
- for (const handler of _handlers.values()) {
101
- const dto = handler.getRegistrationInfo();
102
- if (dto.stage === STAGE.PRE) {
103
- pre.push(handler);
104
- } else if (dto.stage === STAGE.PROCESS) {
105
- process.push(handler);
106
- } else if (dto.stage === STAGE.POST) post.push(handler);
107
- }
108
-
109
- _pre = helpOrder.sort(pre);
110
- _process = helpOrder.sort(process);
111
- _post = helpOrder.sort(post);
112
- };
113
- }
114
-
115
- }