@flancer32/teq-web 0.3.1 → 0.5.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.
- package/CHANGELOG.md +26 -1
- package/README.md +236 -127
- package/ai/AGENTS.md +36 -0
- package/ai/abstractions.md +75 -0
- package/ai/examples/minimal-server.md +78 -0
- package/ai/overview.md +27 -0
- package/ai/rules.md +41 -0
- package/package.json +43 -28
- package/src/Back/Api/Handler.mjs +26 -0
- package/src/Back/{Defaults.js → Defaults.mjs} +6 -1
- package/src/Back/Dto/Info.mjs +66 -0
- package/src/Back/Dto/{Handler/Source.js → Source.mjs} +26 -23
- package/src/Back/Enum/Server/Type.mjs +13 -0
- package/src/Back/Enum/Stage.mjs +13 -0
- package/src/Back/Handler/Pre/Log.mjs +59 -0
- package/src/Back/Handler/Static/A/{Config.js → Config.mjs} +14 -3
- package/src/Back/Handler/Static/A/{Fallback.js → Fallback.mjs} +16 -4
- package/src/Back/Handler/Static/A/{FileService.js → FileService.mjs} +31 -14
- package/src/Back/Handler/Static/A/{Registry.js → Registry.mjs} +18 -6
- package/src/Back/Handler/Static/A/{Resolver.js → Resolver.mjs} +13 -2
- package/src/Back/Handler/Static.mjs +83 -0
- package/src/Back/Helper/Cast.mjs +116 -0
- package/src/Back/Helper/{Mime.js → Mime.mjs} +2 -0
- package/src/Back/Helper/Order/Kahn.mjs +69 -0
- package/src/Back/Helper/{Respond.js → Respond.mjs} +14 -3
- package/src/Back/Logger.mjs +57 -0
- package/src/Back/PipelineEngine.mjs +228 -0
- package/src/Back/Server/Config/{Tls.js → Tls.mjs} +35 -33
- package/src/Back/Server/Config.mjs +69 -0
- package/src/Back/{Server.js → Server.mjs} +35 -24
- package/types.d.ts +30 -0
- package/.github/workflows/npm-publish.yml +0 -48
- package/AGENTS.md +0 -101
- package/eslint.config.js +0 -37
- package/src/AGENTS.md +0 -108
- package/src/Back/Api/Handler.js +0 -26
- package/src/Back/Dispatcher.js +0 -115
- package/src/Back/Dto/Handler/Info.js +0 -68
- package/src/Back/Enum/Server/Type.js +0 -12
- package/src/Back/Enum/Stage.js +0 -10
- package/src/Back/Handler/Pre/Log.js +0 -45
- package/src/Back/Handler/Static.js +0 -63
- package/src/Back/Helper/Cast.js +0 -114
- package/src/Back/Helper/Order/Kahn.js +0 -66
- package/src/Back/Logger.js +0 -53
- package/src/Back/Server/Config.js +0 -69
- package/teqfw.json +0 -8
- package/test/accept/ExternalServer.test.mjs +0 -66
- package/test/accept/Server.test.mjs +0 -203
- package/test/certs/ca.pem +0 -19
- package/test/certs/cert.pem +0 -19
- package/test/certs/key.pem +0 -28
- package/test/dev/app/Plugin/Start.js +0 -40
- package/test/dev/app.mjs +0 -65
- package/test/dev/web/favicon.ico +0 -0
- package/test/dev/web/index.html +0 -11
- package/test/unit/AGENTS.md +0 -106
- package/test/unit/Back/Dispatcher.test.mjs +0 -150
- package/test/unit/Back/Dto/Handler/Source.test.mjs +0 -40
- package/test/unit/Back/Handler/Pre/Log.test.mjs +0 -22
- package/test/unit/Back/Handler/Static/A/Config.test.mjs +0 -52
- package/test/unit/Back/Handler/Static/A/Fallback.test.mjs +0 -60
- package/test/unit/Back/Handler/Static/A/FileService.test.mjs +0 -225
- package/test/unit/Back/Handler/Static/A/Registry.test.mjs +0 -83
- package/test/unit/Back/Handler/Static/A/Resolver.test.mjs +0 -73
- package/test/unit/Back/Handler/Static/Static.test.mjs +0 -235
- package/test/unit/Back/Helper/Order/Kahn.test.mjs +0 -59
- package/test/unit/Back/Helper/Respond.test.mjs +0 -83
- package/test/unit/Back/Server.test.mjs +0 -112
- package/test/unit/common.js +0 -22
package/ai/rules.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Usage Rules
|
|
2
|
+
|
|
3
|
+
## Structural Rules
|
|
4
|
+
|
|
5
|
+
- The Pipeline Engine is the only request-lifecycle coordinator.
|
|
6
|
+
- Request processing always uses the fixed stage sequence `INIT -> PROCESS -> FINALIZE`.
|
|
7
|
+
- Handlers are registered during setup and must not be added, removed, or reordered after startup.
|
|
8
|
+
- Handler ordering is declarative and derived from `name`, `before`, and `after`.
|
|
9
|
+
|
|
10
|
+
## Completion Rules
|
|
11
|
+
|
|
12
|
+
- Request completion is a monotonic flag for one request.
|
|
13
|
+
- Only `PROCESS` handlers may call `context.complete()` or set completion to `true`.
|
|
14
|
+
- `INIT` and `FINALIZE` handlers must never mark the request completed.
|
|
15
|
+
- Completion cannot be reset once set.
|
|
16
|
+
|
|
17
|
+
## Response Rules
|
|
18
|
+
|
|
19
|
+
- Exactly one terminal HTTP response must result from each request.
|
|
20
|
+
- If no `PROCESS` handler completes the request and the response is still writable, the Pipeline Engine returns `404 Not Found`.
|
|
21
|
+
- If a `PROCESS` handler throws and the response is still writable, the Pipeline Engine returns `500 Internal Server Error` and stops further PROCESS handlers.
|
|
22
|
+
- Exceptions in `INIT` and `FINALIZE` handlers are isolated and do not abort the pipeline.
|
|
23
|
+
|
|
24
|
+
## Transport Boundary
|
|
25
|
+
|
|
26
|
+
- Handlers work on request context and response specification only.
|
|
27
|
+
- Handlers must not perform transport-level transmission or socket management.
|
|
28
|
+
- Actual network response transmission belongs to the built-in `Server` or to the external transport framework that called the Pipeline Engine.
|
|
29
|
+
|
|
30
|
+
## Environment Assumptions
|
|
31
|
+
|
|
32
|
+
- Runtime is Node.js 20 or newer.
|
|
33
|
+
- The package is intended for long-running server processes.
|
|
34
|
+
- Process supervision, restart, scaling, reverse proxying, and load balancing are external concerns.
|
|
35
|
+
- Multiple instances may run concurrently, but they are independent and must not rely on shared in-memory state.
|
|
36
|
+
|
|
37
|
+
## DI Usage Rules
|
|
38
|
+
|
|
39
|
+
- The package is designed for `@teqfw/di`.
|
|
40
|
+
- Consumer code should resolve package modules through the `Fl32_Web_` namespace mapped to the package `src/` path.
|
|
41
|
+
- Treat helper internals under deep implementation paths as non-essential unless the task specifically requires them. Preferred consumer entry points are the Pipeline Engine, Server, handler API, DTO factories, enums, and built-in handlers.
|
package/package.json
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flancer32/teq-web",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Server-side web request coordination infrastructure for TeqFW modular monolith applications.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"es6+",
|
|
8
|
-
"teqfw",
|
|
9
|
-
"tequila framework",
|
|
10
|
-
"web applications"
|
|
11
|
-
],
|
|
12
|
-
"homepage": "https://github.com/flancer32/teq-web#readme",
|
|
13
|
-
"bugs": {
|
|
14
|
-
"url": "https://github.com/flancer32/teq-web/issues"
|
|
15
|
-
},
|
|
16
6
|
"license": "Apache-2.0",
|
|
17
7
|
"author": {
|
|
18
8
|
"name": "Alex Gusev",
|
|
@@ -23,26 +13,51 @@
|
|
|
23
13
|
"type": "git",
|
|
24
14
|
"url": "git+https://github.com/flancer32/teq-web.git"
|
|
25
15
|
},
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
16
|
+
"homepage": "https://github.com/flancer32/teq-web",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/flancer32/teq-web/issues"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"teqfw",
|
|
22
|
+
"tequila-framework",
|
|
23
|
+
"web",
|
|
24
|
+
"dispatcher",
|
|
25
|
+
"request-pipeline",
|
|
26
|
+
"dependency-injection",
|
|
27
|
+
"modular-monolith",
|
|
28
|
+
"nodejs"
|
|
29
|
+
],
|
|
30
|
+
"types": "types.d.ts",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"teqfw": {
|
|
35
|
+
"namespaces": [
|
|
36
|
+
{
|
|
37
|
+
"prefix": "Fl32_Web_",
|
|
38
|
+
"path": "./src",
|
|
39
|
+
"ext": ".mjs"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
30
42
|
},
|
|
31
43
|
"dependencies": {
|
|
32
|
-
"@teqfw/di": "
|
|
44
|
+
"@teqfw/di": "^2.0.4"
|
|
33
45
|
},
|
|
34
46
|
"devDependencies": {
|
|
35
|
-
"@
|
|
36
|
-
"eslint": "^
|
|
37
|
-
"eslint-plugin-jsdoc": "^50.6.8",
|
|
38
|
-
"express": "^4.18.2",
|
|
39
|
-
"fastify": "^4.27.2"
|
|
47
|
+
"@types/node": "^25.4.0",
|
|
48
|
+
"eslint": "^10.0.3"
|
|
40
49
|
},
|
|
41
|
-
"
|
|
42
|
-
"
|
|
50
|
+
"scripts": {
|
|
51
|
+
"test:unit": "find test/unit -name '*.test.mjs' -print0 | xargs -0 node --test",
|
|
52
|
+
"test:integration": "find test/integration -name '*.test.mjs' -print0 | xargs -0 node --test",
|
|
53
|
+
"test": "npm run test:unit && npm run test:integration"
|
|
43
54
|
},
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
55
|
+
"files": [
|
|
56
|
+
"ai/",
|
|
57
|
+
"src/",
|
|
58
|
+
"CHANGELOG.md",
|
|
59
|
+
"LICENSE",
|
|
60
|
+
"README.md",
|
|
61
|
+
"types.d.ts"
|
|
62
|
+
]
|
|
48
63
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for web request handlers used by the Pipeline Engine.
|
|
5
|
+
*
|
|
6
|
+
* @interface
|
|
7
|
+
*/
|
|
8
|
+
export default class Fl32_Web_Back_Api_Handler {
|
|
9
|
+
/* eslint-disable no-unused-vars */
|
|
10
|
+
/**
|
|
11
|
+
* Handles one request context in a pipeline stage.
|
|
12
|
+
* @param {Fl32_Web_Back_PipelineEngine_RequestContext} context
|
|
13
|
+
* @returns {Promise<void>}
|
|
14
|
+
*/
|
|
15
|
+
async handle(context) {
|
|
16
|
+
throw new Error('Method not implemented');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Provides metadata for pipeline registration.
|
|
21
|
+
* @returns {Fl32_Web_Back_Dto_Info}
|
|
22
|
+
*/
|
|
23
|
+
getRegistrationInfo() {
|
|
24
|
+
throw new Error('Method not implemented');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const __deps__ = Object.freeze({
|
|
4
|
+
cast: 'Fl32_Web_Back_Helper_Cast$',
|
|
5
|
+
STAGE: 'Fl32_Web_Back_Enum_Stage$',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} Fl32_Web_Back_Dto_InfoFactoryParams
|
|
10
|
+
* @property {Fl32_Web_Back_Helper_Cast} cast
|
|
11
|
+
* @property {Fl32_Web_Back_Enum_Stage} STAGE
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default class Fl32_Web_Back_Dto_Info {
|
|
15
|
+
/**
|
|
16
|
+
* Handlers to run before this one.
|
|
17
|
+
* @type {string[]}
|
|
18
|
+
*/
|
|
19
|
+
after = [];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Handlers to run after this one.
|
|
23
|
+
* @type {string[]}
|
|
24
|
+
*/
|
|
25
|
+
before = [];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Unique handler name for ordering.
|
|
29
|
+
* @type {string|undefined}
|
|
30
|
+
*/
|
|
31
|
+
name;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Execution stage: `INIT`, `PROCESS`, or `FINALIZE`.
|
|
35
|
+
* @type {string|undefined}
|
|
36
|
+
* @see Fl32_Web_Back_Enum_Stage
|
|
37
|
+
*/
|
|
38
|
+
stage;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class Factory {
|
|
42
|
+
/* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
|
|
43
|
+
/**
|
|
44
|
+
* @param {Fl32_Web_Back_Dto_InfoFactoryParams} params
|
|
45
|
+
*/
|
|
46
|
+
constructor(
|
|
47
|
+
{
|
|
48
|
+
cast,
|
|
49
|
+
STAGE,
|
|
50
|
+
}
|
|
51
|
+
) {
|
|
52
|
+
/* eslint-enable jsdoc/check-param-names */
|
|
53
|
+
/**
|
|
54
|
+
* @param {*} [data]
|
|
55
|
+
* @returns {Fl32_Web_Back_Dto_Info}
|
|
56
|
+
*/
|
|
57
|
+
this.create = function (data) {
|
|
58
|
+
const res = new Fl32_Web_Back_Dto_Info();
|
|
59
|
+
res.after = cast.array(data?.after, cast.string);
|
|
60
|
+
res.before = cast.array(data?.before, cast.string);
|
|
61
|
+
res.name = cast.string(data?.name);
|
|
62
|
+
res.stage = cast.enum(data?.stage, STAGE, {upper: true});
|
|
63
|
+
return Object.freeze(res);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -1,46 +1,49 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const __deps__ = Object.freeze({
|
|
4
|
+
cast: 'Fl32_Web_Back_Helper_Cast$',
|
|
5
|
+
});
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
|
-
*
|
|
8
|
+
* @typedef {object} Fl32_Web_Back_Dto_SourceFactoryParams
|
|
9
|
+
* @property {Fl32_Web_Back_Helper_Cast} cast
|
|
3
10
|
*/
|
|
4
|
-
|
|
11
|
+
|
|
12
|
+
export default class Fl32_Web_Back_Dto_Source {
|
|
13
|
+
/** @type {string} */
|
|
14
|
+
root;
|
|
15
|
+
/** @type {string} */
|
|
16
|
+
prefix;
|
|
17
|
+
/** @type {{[key: string]: string[]}} */
|
|
18
|
+
allow = {};
|
|
19
|
+
/** @type {string[]} */
|
|
20
|
+
defaults = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class Factory {
|
|
5
24
|
/* eslint-disable jsdoc/require-param-description */
|
|
6
25
|
/**
|
|
7
|
-
* @param {
|
|
26
|
+
* @param {Fl32_Web_Back_Dto_SourceFactoryParams} params
|
|
8
27
|
*/
|
|
9
28
|
constructor(
|
|
10
29
|
{
|
|
11
|
-
|
|
30
|
+
cast,
|
|
12
31
|
}
|
|
13
32
|
) {
|
|
14
33
|
/* eslint-enable jsdoc/require-param-description */
|
|
15
34
|
/**
|
|
16
|
-
* Create validated DTO for source configuration.
|
|
17
|
-
*
|
|
18
35
|
* @param {*} [data]
|
|
19
|
-
* @returns {
|
|
36
|
+
* @returns {Fl32_Web_Back_Dto_Source}
|
|
20
37
|
*/
|
|
21
38
|
this.create = function (data) {
|
|
22
|
-
const res = new
|
|
39
|
+
const res = new Fl32_Web_Back_Dto_Source();
|
|
23
40
|
if (data) {
|
|
24
41
|
res.root = cast.string(data.root);
|
|
25
42
|
res.prefix = cast.string(data.prefix);
|
|
26
43
|
res.allow = cast.stringArrayMap(data.allow);
|
|
27
44
|
res.defaults = cast.array(data.defaults, cast.string);
|
|
28
45
|
}
|
|
29
|
-
return res;
|
|
46
|
+
return Object.freeze(res);
|
|
30
47
|
};
|
|
31
48
|
}
|
|
32
49
|
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @memberOf Fl32_Web_Back_Dto_Handler_Source
|
|
36
|
-
*/
|
|
37
|
-
class Dto {
|
|
38
|
-
/** @type {string} */
|
|
39
|
-
root;
|
|
40
|
-
/** @type {string} */
|
|
41
|
-
prefix;
|
|
42
|
-
/** @type {{[key: string]: string[]}} */
|
|
43
|
-
allow = {};
|
|
44
|
-
/** @type {string[]} */
|
|
45
|
-
defaults = [];
|
|
46
|
-
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enum-like DTO for built-in server transport modes.
|
|
5
|
+
*/
|
|
6
|
+
export default class Fl32_Web_Back_Enum_Server_Type {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.HTTP2 = 'http2';
|
|
9
|
+
this.HTTP = 'http';
|
|
10
|
+
this.HTTPS = 'https';
|
|
11
|
+
Object.freeze(this);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enum-like DTO for web request processing stages.
|
|
5
|
+
*/
|
|
6
|
+
export default class Fl32_Web_Back_Enum_Stage {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.INIT = 'INIT';
|
|
9
|
+
this.PROCESS = 'PROCESS';
|
|
10
|
+
this.FINALIZE = 'FINALIZE';
|
|
11
|
+
Object.freeze(this);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Logs basic request information at the beginning of the request lifecycle.
|
|
5
|
+
*
|
|
6
|
+
* @implements Fl32_Web_Back_Api_Handler
|
|
7
|
+
*/
|
|
8
|
+
export const __deps__ = Object.freeze({
|
|
9
|
+
logger: 'Fl32_Web_Back_Logger$',
|
|
10
|
+
dtoInfoFactory: 'Fl32_Web_Back_Dto_Info__Factory$',
|
|
11
|
+
STAGE: 'Fl32_Web_Back_Enum_Stage$',
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} Fl32_Web_Back_Handler_Pre_LogConstructorParams
|
|
16
|
+
* @property {Fl32_Web_Back_Logger} logger
|
|
17
|
+
* @property {Fl32_Web_Back_Dto_Info$Factory} dtoInfoFactory
|
|
18
|
+
* @property {Fl32_Web_Back_Enum_Stage} STAGE
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export default class Fl32_Web_Back_Handler_Pre_Log {
|
|
22
|
+
/* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
|
|
23
|
+
/**
|
|
24
|
+
* @param {Fl32_Web_Back_Handler_Pre_LogConstructorParams} params
|
|
25
|
+
*/
|
|
26
|
+
constructor(
|
|
27
|
+
{
|
|
28
|
+
logger,
|
|
29
|
+
dtoInfoFactory,
|
|
30
|
+
STAGE,
|
|
31
|
+
}
|
|
32
|
+
) {
|
|
33
|
+
/* eslint-enable jsdoc/check-param-names */
|
|
34
|
+
// VARS
|
|
35
|
+
const _info = dtoInfoFactory.create({
|
|
36
|
+
name: this.constructor.name,
|
|
37
|
+
stage: STAGE.INIT,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// MAIN
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Log request method and URL.
|
|
44
|
+
*
|
|
45
|
+
* @param {Fl32_Web_Back_PipelineEngine_RequestContext} context
|
|
46
|
+
* @returns {Promise<void>}
|
|
47
|
+
*/
|
|
48
|
+
this.handle = async function (context) {
|
|
49
|
+
logger.debug(`${context.request.method} ${context.request.url}`);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Return handler registration info.
|
|
54
|
+
*
|
|
55
|
+
* @returns {Fl32_Web_Back_Dto_Info}
|
|
56
|
+
*/
|
|
57
|
+
this.getRegistrationInfo = () => _info;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const __deps__ = Object.freeze({
|
|
4
|
+
path: 'node_path',
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {object} Fl32_Web_Back_Handler_Static_A_ConfigConstructorParams
|
|
9
|
+
* @property {typeof import('node:path')} path
|
|
10
|
+
*/
|
|
11
|
+
|
|
1
12
|
export default class Fl32_Web_Back_Handler_Static_A_Config {
|
|
2
13
|
static DEFAULT_FILES = ['index.html', 'index.htm', 'index.txt'];
|
|
3
14
|
/* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
|
|
4
15
|
/**
|
|
5
|
-
* @param {
|
|
16
|
+
* @param {Fl32_Web_Back_Handler_Static_A_ConfigConstructorParams} params
|
|
6
17
|
*/
|
|
7
18
|
constructor(
|
|
8
19
|
{
|
|
9
|
-
|
|
20
|
+
path,
|
|
10
21
|
}
|
|
11
22
|
) {
|
|
12
23
|
/* eslint-enable jsdoc/check-param-names */
|
|
@@ -14,7 +25,7 @@ export default class Fl32_Web_Back_Handler_Static_A_Config {
|
|
|
14
25
|
/**
|
|
15
26
|
* Normalize DTO fields into configuration object.
|
|
16
27
|
*
|
|
17
|
-
* @param {
|
|
28
|
+
* @param {Fl32_Web_Back_Dto_Source} dto
|
|
18
29
|
* @returns {{root:string,prefix:string,allow?:Record<string,string[]>,defaults:string[]}}
|
|
19
30
|
* @throws {Error} When required fields are missing or invalid.
|
|
20
31
|
*/
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const __deps__ = Object.freeze({
|
|
4
|
+
fs: 'node_fs',
|
|
5
|
+
path: 'node_path',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} Fl32_Web_Back_Handler_Static_A_FallbackConstructorParams
|
|
10
|
+
* @property {typeof import('node:fs')} fs
|
|
11
|
+
* @property {typeof import('node:path')} path
|
|
12
|
+
*/
|
|
13
|
+
|
|
1
14
|
export default class Fl32_Web_Back_Handler_Static_A_Fallback {
|
|
2
15
|
/* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
|
|
3
16
|
/**
|
|
4
|
-
* @param {
|
|
5
|
-
* @param {typeof import('node:path')} path
|
|
17
|
+
* @param {Fl32_Web_Back_Handler_Static_A_FallbackConstructorParams} params
|
|
6
18
|
*/
|
|
7
19
|
constructor(
|
|
8
20
|
{
|
|
9
|
-
|
|
10
|
-
|
|
21
|
+
fs,
|
|
22
|
+
path,
|
|
11
23
|
}
|
|
12
24
|
) {
|
|
13
25
|
/* eslint-enable jsdoc/check-param-names */
|
|
@@ -1,23 +1,40 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const __deps__ = Object.freeze({
|
|
4
|
+
fs: 'node_fs',
|
|
5
|
+
http2: 'node_http2',
|
|
6
|
+
path: 'node_path',
|
|
7
|
+
logger: 'Fl32_Web_Back_Logger$',
|
|
8
|
+
helpMime: 'Fl32_Web_Back_Helper_Mime$',
|
|
9
|
+
resolver: 'Fl32_Web_Back_Handler_Static_A_Resolver$',
|
|
10
|
+
fallback: 'Fl32_Web_Back_Handler_Static_A_Fallback$',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {object} Fl32_Web_Back_Handler_Static_A_FileServiceConstructorParams
|
|
15
|
+
* @property {typeof import('node:fs')} fs
|
|
16
|
+
* @property {typeof import('node:http2')} http2
|
|
17
|
+
* @property {typeof import('node:path')} path
|
|
18
|
+
* @property {Fl32_Web_Back_Logger} logger
|
|
19
|
+
* @property {Fl32_Web_Back_Helper_Mime} helpMime
|
|
20
|
+
* @property {Fl32_Web_Back_Handler_Static_A_Resolver} resolver
|
|
21
|
+
* @property {Fl32_Web_Back_Handler_Static_A_Fallback} fallback
|
|
22
|
+
*/
|
|
23
|
+
|
|
1
24
|
export default class Fl32_Web_Back_Handler_Static_A_FileService {
|
|
2
25
|
/* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
|
|
3
26
|
/**
|
|
4
|
-
* @param {
|
|
5
|
-
* @param {typeof import('node:http2')} http2
|
|
6
|
-
* @param {typeof import('node:path')} path
|
|
7
|
-
* @param {Fl32_Web_Back_Logger} logger
|
|
8
|
-
* @param {Fl32_Web_Back_Helper_Mime} helpMime
|
|
9
|
-
* @param {Fl32_Web_Back_Handler_Static_A_Resolver} resolver
|
|
10
|
-
* @param {Fl32_Web_Back_Handler_Static_A_Fallback} fallback
|
|
27
|
+
* @param {Fl32_Web_Back_Handler_Static_A_FileServiceConstructorParams} params
|
|
11
28
|
*/
|
|
12
29
|
constructor(
|
|
13
30
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
fs,
|
|
32
|
+
http2,
|
|
33
|
+
path,
|
|
34
|
+
logger,
|
|
35
|
+
helpMime,
|
|
36
|
+
resolver,
|
|
37
|
+
fallback,
|
|
21
38
|
}
|
|
22
39
|
) {
|
|
23
40
|
/* eslint-enable jsdoc/check-param-names */
|
|
@@ -1,24 +1,36 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const __deps__ = Object.freeze({
|
|
4
|
+
configFactory: 'Fl32_Web_Back_Handler_Static_A_Config$',
|
|
5
|
+
logger: 'Fl32_Web_Back_Logger$',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} Fl32_Web_Back_Handler_Static_A_RegistryConstructorParams
|
|
10
|
+
* @property {Fl32_Web_Back_Handler_Static_A_Config} configFactory
|
|
11
|
+
* @property {Fl32_Web_Back_Logger} logger
|
|
12
|
+
*/
|
|
13
|
+
|
|
1
14
|
export default class Fl32_Web_Back_Handler_Static_A_Registry {
|
|
2
15
|
/* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
|
|
3
16
|
/**
|
|
4
|
-
* @param {
|
|
5
|
-
* @param {Fl32_Web_Back_Logger} logger
|
|
17
|
+
* @param {Fl32_Web_Back_Handler_Static_A_RegistryConstructorParams} params
|
|
6
18
|
*/
|
|
7
19
|
constructor(
|
|
8
20
|
{
|
|
9
|
-
|
|
10
|
-
|
|
21
|
+
configFactory,
|
|
22
|
+
logger,
|
|
11
23
|
}
|
|
12
24
|
) {
|
|
13
25
|
/* eslint-enable jsdoc/check-param-names */
|
|
14
|
-
/** @type {
|
|
26
|
+
/** @type {Fl32_Web_Back_Dto_Source[]} */
|
|
15
27
|
let _configs = [];
|
|
16
28
|
|
|
17
29
|
/**
|
|
18
30
|
* Add configurations ensuring unique prefixes.
|
|
19
31
|
* Existing entries are not modified.
|
|
20
32
|
*
|
|
21
|
-
* @param {
|
|
33
|
+
* @param {Fl32_Web_Back_Dto_Source[]} dtoList
|
|
22
34
|
*/
|
|
23
35
|
this.addConfigs = function (dtoList = []) {
|
|
24
36
|
const list = dtoList.map(dto => configFactory.create(dto));
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Enforces allow‐list rules and security checks when resolving
|
|
3
5
|
* a relative URL to an absolute filesystem path under a given root.
|
|
4
6
|
*/
|
|
7
|
+
export const __deps__ = Object.freeze({
|
|
8
|
+
path: 'node_path',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {object} Fl32_Web_Back_Handler_Static_A_ResolverConstructorParams
|
|
13
|
+
* @property {typeof import('node:path')} path
|
|
14
|
+
*/
|
|
15
|
+
|
|
5
16
|
export default class Fl32_Web_Back_Handler_Static_A_Resolver {
|
|
6
17
|
/**
|
|
7
|
-
* @param {
|
|
18
|
+
* @param {Fl32_Web_Back_Handler_Static_A_ResolverConstructorParams} params
|
|
8
19
|
*/
|
|
9
|
-
constructor({
|
|
20
|
+
constructor({path}) {
|
|
10
21
|
/**
|
|
11
22
|
* Resolve a filesystem path for given config and relative URL part.
|
|
12
23
|
* Applies allow rules and prevents path traversal.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Universal static-file PROCESS handler.
|
|
5
|
+
*
|
|
6
|
+
* @implements Fl32_Web_Back_Api_Handler
|
|
7
|
+
*/
|
|
8
|
+
export const __deps__ = Object.freeze({
|
|
9
|
+
registry: 'Fl32_Web_Back_Handler_Static_A_Registry$',
|
|
10
|
+
fileService: 'Fl32_Web_Back_Handler_Static_A_FileService$',
|
|
11
|
+
respond: 'Fl32_Web_Back_Helper_Respond$',
|
|
12
|
+
logger: 'Fl32_Web_Back_Logger$',
|
|
13
|
+
dtoInfoFactory: 'Fl32_Web_Back_Dto_Info__Factory$',
|
|
14
|
+
STAGE: 'Fl32_Web_Back_Enum_Stage$',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {object} Fl32_Web_Back_Handler_StaticConstructorParams
|
|
19
|
+
* @property {Fl32_Web_Back_Handler_Static_A_Registry} registry
|
|
20
|
+
* @property {Fl32_Web_Back_Handler_Static_A_FileService} fileService
|
|
21
|
+
* @property {Fl32_Web_Back_Helper_Respond} respond
|
|
22
|
+
* @property {Fl32_Web_Back_Logger} logger
|
|
23
|
+
* @property {Fl32_Web_Back_Dto_Info$Factory} dtoInfoFactory
|
|
24
|
+
* @property {Fl32_Web_Back_Enum_Stage} STAGE
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export default class Fl32_Web_Back_Handler_Static {
|
|
28
|
+
/* eslint-disable jsdoc/require-param-description,jsdoc/check-param-names */
|
|
29
|
+
/**
|
|
30
|
+
* @param {Fl32_Web_Back_Handler_StaticConstructorParams} params
|
|
31
|
+
*/
|
|
32
|
+
constructor(
|
|
33
|
+
{
|
|
34
|
+
registry,
|
|
35
|
+
fileService,
|
|
36
|
+
respond,
|
|
37
|
+
logger,
|
|
38
|
+
dtoInfoFactory,
|
|
39
|
+
STAGE,
|
|
40
|
+
}
|
|
41
|
+
) {
|
|
42
|
+
/* eslint-enable jsdoc/check-param-names */
|
|
43
|
+
|
|
44
|
+
const _info = dtoInfoFactory.create({
|
|
45
|
+
name: this.constructor.name,
|
|
46
|
+
stage: STAGE.PROCESS,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Initialize registry with provided sources.
|
|
51
|
+
*
|
|
52
|
+
* @param {{sources: Fl32_Web_Back_Dto_Source[]}} params
|
|
53
|
+
* @returns {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
this.init = async ({sources = []} = {}) => {
|
|
56
|
+
registry.addConfigs(sources);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Attempt to handle incoming request.
|
|
61
|
+
*
|
|
62
|
+
* @param {Fl32_Web_Back_PipelineEngine_RequestContext} context
|
|
63
|
+
* @returns {Promise<void>}
|
|
64
|
+
*/
|
|
65
|
+
this.handle = async (context) => {
|
|
66
|
+
const req = context.request;
|
|
67
|
+
const res = context.response;
|
|
68
|
+
if (!respond.isWritable(res)) return;
|
|
69
|
+
const urlPath = decodeURIComponent(req.url.split('?')[0]);
|
|
70
|
+
const match = registry.find(urlPath);
|
|
71
|
+
if (!match) return;
|
|
72
|
+
const served = await fileService.serve(match.config, match.rel, req, res);
|
|
73
|
+
if (served) {
|
|
74
|
+
context.complete();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @returns {Fl32_Web_Back_Dto_Info}
|
|
80
|
+
*/
|
|
81
|
+
this.getRegistrationInfo = () => _info;
|
|
82
|
+
}
|
|
83
|
+
}
|