@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
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import {describe, it, beforeEach} from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import {buildTestContainer} from '../../common.js';
|
|
4
|
-
|
|
5
|
-
/** Minimal HTTP/2 constants mock */
|
|
6
|
-
const mockHttp2 = {
|
|
7
|
-
constants: {
|
|
8
|
-
HTTP2_HEADER_ALLOW: 'allow',
|
|
9
|
-
HTTP_STATUS_OK: 200,
|
|
10
|
-
HTTP_STATUS_CREATED: 201,
|
|
11
|
-
HTTP_STATUS_NO_CONTENT: 204,
|
|
12
|
-
HTTP_STATUS_MOVED_PERMANENTLY: 301,
|
|
13
|
-
HTTP_STATUS_FOUND: 302,
|
|
14
|
-
HTTP_STATUS_SEE_OTHER: 303,
|
|
15
|
-
HTTP_STATUS_NOT_MODIFIED: 304,
|
|
16
|
-
HTTP_STATUS_BAD_REQUEST: 400,
|
|
17
|
-
HTTP_STATUS_UNAUTHORIZED: 401,
|
|
18
|
-
HTTP_STATUS_PAYMENT_REQUIRED: 402,
|
|
19
|
-
HTTP_STATUS_FORBIDDEN: 403,
|
|
20
|
-
HTTP_STATUS_NOT_FOUND: 404,
|
|
21
|
-
HTTP_STATUS_METHOD_NOT_ALLOWED: 405,
|
|
22
|
-
HTTP_STATUS_CONFLICT: 409,
|
|
23
|
-
HTTP_STATUS_INTERNAL_SERVER_ERROR: 500,
|
|
24
|
-
HTTP_STATUS_BAD_GATEWAY: 502,
|
|
25
|
-
HTTP_STATUS_SERVICE_UNAVAILABLE: 503,
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
class MockRes {
|
|
30
|
-
constructor() {
|
|
31
|
-
this.statusCode = undefined;
|
|
32
|
-
this.headers = undefined;
|
|
33
|
-
this.body = undefined;
|
|
34
|
-
this.headersSent = false;
|
|
35
|
-
this.writableEnded = false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
writeHead(status, headers) {
|
|
39
|
-
this.statusCode = status;
|
|
40
|
-
this.headers = headers;
|
|
41
|
-
this.headersSent = true;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
end(chunk = '') {
|
|
45
|
-
this.body = chunk;
|
|
46
|
-
this.writableEnded = true;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
describe('Fl32_Web_Back_Helper_Respond', () => {
|
|
51
|
-
let container;
|
|
52
|
-
let respond;
|
|
53
|
-
|
|
54
|
-
beforeEach(async () => {
|
|
55
|
-
container = buildTestContainer();
|
|
56
|
-
container.register('node:http2', mockHttp2);
|
|
57
|
-
respond = await container.get('Fl32_Web_Back_Helper_Respond$');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('sends 200 OK response', () => {
|
|
61
|
-
const res = new MockRes();
|
|
62
|
-
const ok = respond.code200_Ok({res, headers: {a: 'b'}, body: 'hi'});
|
|
63
|
-
assert.strictEqual(ok, true);
|
|
64
|
-
assert.strictEqual(res.statusCode, 200);
|
|
65
|
-
assert.deepStrictEqual(res.headers, {a: 'b'});
|
|
66
|
-
assert.strictEqual(res.body, 'hi');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('adds Allow header for 405 Method Not Allowed', () => {
|
|
70
|
-
const res = new MockRes();
|
|
71
|
-
respond.code405_MethodNotAllowed({res});
|
|
72
|
-
assert.strictEqual(res.statusCode, 405);
|
|
73
|
-
assert.strictEqual(res.headers.allow, 'HEAD, GET, POST');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('isWritable detects ended responses', () => {
|
|
77
|
-
const res = new MockRes();
|
|
78
|
-
respond.code200_Ok({res});
|
|
79
|
-
assert.strictEqual(respond.isWritable(res), false);
|
|
80
|
-
const again = respond.code200_Ok({res});
|
|
81
|
-
assert.strictEqual(again, false);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import {describe, it, beforeEach} from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import {buildTestContainer} from '../common.js';
|
|
4
|
-
|
|
5
|
-
describe('Fl32_Web_Back_Server (mocked)', () => {
|
|
6
|
-
|
|
7
|
-
/** @type {import('@teqfw/di').Container} */
|
|
8
|
-
let container;
|
|
9
|
-
/** @type {Array<*>} */
|
|
10
|
-
const log = [];
|
|
11
|
-
|
|
12
|
-
// Mocks for HTTP/1 and HTTP/2 servers
|
|
13
|
-
const mockHttp = {
|
|
14
|
-
createServer: () => ({
|
|
15
|
-
listen: () => { log.push('http.listen'); },
|
|
16
|
-
on: () => { log.push('http.on'); },
|
|
17
|
-
close: (cb) => { log.push('http.close'); cb && cb(); },
|
|
18
|
-
}),
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const mockHttp2 = {
|
|
22
|
-
createServer: () => ({
|
|
23
|
-
listen: () => { log.push('http2.listen'); },
|
|
24
|
-
on: () => { log.push('http2.on'); },
|
|
25
|
-
close: (cb) => { log.push('http2.close'); cb && cb(); },
|
|
26
|
-
}),
|
|
27
|
-
createSecureServer: (tlsOpts) => ({
|
|
28
|
-
listen: () => { log.push('http2s.listen'); },
|
|
29
|
-
on: () => { log.push('http2s.on'); },
|
|
30
|
-
close: (cb) => { log.push('http2s.close'); cb && cb(); },
|
|
31
|
-
})
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
log.length = 0;
|
|
36
|
-
container = buildTestContainer();
|
|
37
|
-
|
|
38
|
-
container.register('node:http', mockHttp);
|
|
39
|
-
container.register('node:http2', mockHttp2);
|
|
40
|
-
|
|
41
|
-
container.register('Fl32_Web_Back_Logger$', {
|
|
42
|
-
info: (...args) => log.push(['info', ...args]),
|
|
43
|
-
error: (...args) => log.push(['error', ...args]),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
container.register('Fl32_Web_Back_Dispatcher$', {
|
|
47
|
-
orderHandlers: () => log.push('dispatcher.orderHandlers'),
|
|
48
|
-
onEventRequest: () => {},
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should start in HTTP/1 mode by default', async () => {
|
|
53
|
-
const server = await container.get('Fl32_Web_Back_Server$');
|
|
54
|
-
await server.start(); // default mode is HTTP/1
|
|
55
|
-
assert.deepStrictEqual(log, [
|
|
56
|
-
'dispatcher.orderHandlers',
|
|
57
|
-
['info', 'Starting server in HTTP/1 mode on port 3000...'],
|
|
58
|
-
'http.on',
|
|
59
|
-
'http.listen',
|
|
60
|
-
]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should start in HTTP/2 mode if specified', async () => {
|
|
64
|
-
const server = await container.get('Fl32_Web_Back_Server$');
|
|
65
|
-
await server.start({type: 'http2', port: 8080});
|
|
66
|
-
assert.deepStrictEqual(log, [
|
|
67
|
-
'dispatcher.orderHandlers',
|
|
68
|
-
['info', 'Starting server in HTTP/2 mode on port 8080...'],
|
|
69
|
-
'http2.on',
|
|
70
|
-
'http2.listen',
|
|
71
|
-
]);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should start in HTTPS/2 mode with TLS config', async () => {
|
|
75
|
-
const server = await container.get('Fl32_Web_Back_Server$');
|
|
76
|
-
await server.start({type: 'https', port: 8443, tls: {key: 'a', cert: 'b'}});
|
|
77
|
-
assert.deepStrictEqual(log, [
|
|
78
|
-
'dispatcher.orderHandlers',
|
|
79
|
-
['info', 'Starting server in HTTPS (HTTP/2 + TLS) mode on port 8443...'],
|
|
80
|
-
'http2s.on',
|
|
81
|
-
'http2s.listen',
|
|
82
|
-
]);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should throw error if TLS config is missing in HTTPS mode', async () => {
|
|
86
|
-
const server = await container.get('Fl32_Web_Back_Server$');
|
|
87
|
-
await assert.rejects(
|
|
88
|
-
() => server.start({type: 'https', port: 1234}),
|
|
89
|
-
/TLS key and certificate are required/
|
|
90
|
-
);
|
|
91
|
-
assert.deepStrictEqual(log.at(-1), ['error', 'HTTPS server requires TLS key and certificate']);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('should throw error on unsupported server type', async () => {
|
|
95
|
-
const server = await container.get('Fl32_Web_Back_Server$');
|
|
96
|
-
await assert.rejects(
|
|
97
|
-
() => server.start({type: 'ftp', port: 21}),
|
|
98
|
-
/not supported/
|
|
99
|
-
);
|
|
100
|
-
assert.deepStrictEqual(log.at(-1), ['error', 'Unsupported server type: ftp']);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should stop the server', async () => {
|
|
104
|
-
const server = await container.get('Fl32_Web_Back_Server$');
|
|
105
|
-
await server.start();
|
|
106
|
-
await server.stop();
|
|
107
|
-
assert.deepStrictEqual(log.slice(-2), [
|
|
108
|
-
'http.close',
|
|
109
|
-
['info', 'Server stopped'],
|
|
110
|
-
]);
|
|
111
|
-
});
|
|
112
|
-
});
|
package/test/unit/common.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provides a utility to create a configured TeqFW DI container for unit testing.
|
|
3
|
-
*/
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import Container from '@teqfw/di';
|
|
6
|
-
|
|
7
|
-
// Resolve the plugin source path relative to this script
|
|
8
|
-
const SRC = path.resolve(import.meta.dirname, '../../src');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Builds a test DI container for unit tests.
|
|
12
|
-
* Registers plugin namespace and enables test mode.
|
|
13
|
-
*
|
|
14
|
-
* @returns {TeqFw_Di_Container} Test container instance.
|
|
15
|
-
*/
|
|
16
|
-
export function buildTestContainer() {
|
|
17
|
-
const container = new Container();
|
|
18
|
-
const resolver = container.getResolver();
|
|
19
|
-
resolver.addNamespaceRoot('Fl32_Web_', SRC);
|
|
20
|
-
container.enableTestMode();
|
|
21
|
-
return container;
|
|
22
|
-
}
|