structured-fw 1.4.0 → 1.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.
- package/README.md +64 -23
- package/build/system/EventEmitter.js +4 -6
- package/build/system/StructuredError.d.ts +8 -0
- package/build/system/StructuredError.js +40 -0
- package/build/system/bin/structured.js +1 -2
- package/build/system/client/ClientApplication.js +3 -2
- package/build/system/client/ClientComponent.js +21 -13
- package/build/system/client/DataStore.js +2 -4
- package/build/system/client/DataStoreView.js +3 -1
- package/build/system/client/NetRequest.js +7 -2
- package/build/system/server/Application.d.ts +3 -2
- package/build/system/server/Application.js +19 -7
- package/build/system/server/Component.js +15 -7
- package/build/system/server/Components.js +3 -2
- package/build/system/server/Document.d.ts +1 -1
- package/build/system/server/Document.js +10 -7
- package/build/system/server/DocumentHead.js +9 -8
- package/build/system/server/FormValidation.js +121 -123
- package/build/system/server/Handlebars.js +2 -4
- package/build/system/server/Layout.d.ts +2 -2
- package/build/system/server/Layout.js +4 -0
- package/build/system/server/Request.d.ts +1 -11
- package/build/system/server/Request.js +12 -269
- package/build/system/server/RequestContext.d.ts +43 -0
- package/build/system/server/RequestContext.js +322 -0
- package/build/system/server/Session.js +3 -2
- package/build/system/server/dom/DOMNode.js +11 -7
- package/build/system/server/dom/HTMLParser.js +10 -8
- package/build/system/types/application.types.d.ts +1 -1
- package/build/system/types/component.types.d.ts +1 -1
- package/build/system/types/request.types.d.ts +1 -19
- package/package.json +2 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { LooseObject, PostedDataDecoded, RequestBodyRecordValue, RequestHandler, URIArguments } from "../Types.js";
|
|
3
|
+
import { Application } from "./Application.js";
|
|
4
|
+
import { Document } from "./Document.js";
|
|
5
|
+
import { Layout } from "./Layout.js";
|
|
6
|
+
export declare class RequestContext<Body extends LooseObject | undefined = LooseObject> {
|
|
7
|
+
private executionStartedAt;
|
|
8
|
+
private executionCompletedAt;
|
|
9
|
+
readonly app: Application;
|
|
10
|
+
uri: string;
|
|
11
|
+
private readonly handler;
|
|
12
|
+
readonly request: IncomingMessage;
|
|
13
|
+
readonly response: ServerResponse;
|
|
14
|
+
args: URIArguments;
|
|
15
|
+
cookies: Record<string, string>;
|
|
16
|
+
body: Body;
|
|
17
|
+
bodyRaw?: Buffer;
|
|
18
|
+
files?: Record<string, RequestBodyRecordValue>;
|
|
19
|
+
data: RequestContextData;
|
|
20
|
+
sessionId?: string;
|
|
21
|
+
getArgs: PostedDataDecoded;
|
|
22
|
+
readonly timeStart: number;
|
|
23
|
+
private streamingData;
|
|
24
|
+
constructor(app: Application, request: IncomingMessage, response: ServerResponse, handler: RequestHandler | null);
|
|
25
|
+
exec(): Promise<void>;
|
|
26
|
+
respondWith(data: any): Promise<void>;
|
|
27
|
+
private sendResponse;
|
|
28
|
+
createDocument(title: string, component: string, data?: LooseObject): Promise<Document>;
|
|
29
|
+
layoutDocument(layout: Layout, title: string, component: string, data?: LooseObject, attributes?: Record<string, string>): Promise<Document>;
|
|
30
|
+
show404(): Promise<void>;
|
|
31
|
+
private initGetArgs;
|
|
32
|
+
private parseCookies;
|
|
33
|
+
redirect(to: string, statusCode?: number): void;
|
|
34
|
+
private dataRaw;
|
|
35
|
+
private parseBody;
|
|
36
|
+
private parseBodyMultipart;
|
|
37
|
+
private multipartBodyFiles;
|
|
38
|
+
private extractURIArguments;
|
|
39
|
+
private handle;
|
|
40
|
+
isAjax(): boolean;
|
|
41
|
+
duration(): number;
|
|
42
|
+
complete(): boolean;
|
|
43
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import zlib from "node:zlib";
|
|
2
|
+
import { mergeDeep, queryStringDecode, queryStringDecodedSetValue } from "../Util.js";
|
|
3
|
+
import { Document } from "./Document.js";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { existsSync, readFileSync, ReadStream } from "node:fs";
|
|
6
|
+
import { StructuredError } from "../StructuredError.js";
|
|
7
|
+
export class RequestContext {
|
|
8
|
+
executionStartedAt = null;
|
|
9
|
+
executionCompletedAt = null;
|
|
10
|
+
app;
|
|
11
|
+
uri;
|
|
12
|
+
handler;
|
|
13
|
+
request;
|
|
14
|
+
response;
|
|
15
|
+
args = {};
|
|
16
|
+
cookies = {};
|
|
17
|
+
body;
|
|
18
|
+
bodyRaw;
|
|
19
|
+
files;
|
|
20
|
+
data = {};
|
|
21
|
+
sessionId;
|
|
22
|
+
getArgs = {};
|
|
23
|
+
timeStart;
|
|
24
|
+
streamingData = false;
|
|
25
|
+
constructor(app, request, response, handler) {
|
|
26
|
+
this.timeStart = Date.now();
|
|
27
|
+
this.uri = request.url || '/';
|
|
28
|
+
this.app = app;
|
|
29
|
+
this.request = request;
|
|
30
|
+
this.response = response;
|
|
31
|
+
this.handler = handler;
|
|
32
|
+
this.body = undefined;
|
|
33
|
+
}
|
|
34
|
+
async exec() {
|
|
35
|
+
if (this.executionStartedAt !== null) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.executionStartedAt = Date.now();
|
|
39
|
+
try {
|
|
40
|
+
this.initGetArgs();
|
|
41
|
+
this.parseCookies();
|
|
42
|
+
if (this.handler) {
|
|
43
|
+
await this.parseBody();
|
|
44
|
+
}
|
|
45
|
+
await this.handle();
|
|
46
|
+
this.executionCompletedAt = Date.now();
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
const res = await this.app.emit('requestHandleError', this);
|
|
50
|
+
if (res.length > 0) {
|
|
51
|
+
if (!!res[0]) {
|
|
52
|
+
await this.respondWith(res[0]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.response.end();
|
|
56
|
+
throw new StructuredError(`Error in request to ${this.uri}`, e);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async respondWith(data) {
|
|
60
|
+
if (typeof data === 'string' || Buffer.isBuffer(data)) {
|
|
61
|
+
this.sendResponse(data, 'text/plain; charset=utf-8');
|
|
62
|
+
}
|
|
63
|
+
else if (typeof data === 'number') {
|
|
64
|
+
this.sendResponse(data.toString(), 'text/plain; charset=utf-8');
|
|
65
|
+
}
|
|
66
|
+
else if (data instanceof Document) {
|
|
67
|
+
this.sendResponse(await data.toString(), 'text/html; charset=utf-8');
|
|
68
|
+
}
|
|
69
|
+
else if (data === undefined || data === null) {
|
|
70
|
+
this.sendResponse('', 'text/plain; charset=utf-8');
|
|
71
|
+
}
|
|
72
|
+
else if (data instanceof ReadStream) {
|
|
73
|
+
this.streamingData = true;
|
|
74
|
+
data.once('end', () => {
|
|
75
|
+
this.response.end();
|
|
76
|
+
});
|
|
77
|
+
data.pipe(this.response);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.sendResponse(JSON.stringify(data, null, 4), 'application/json; charset=utf-8');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
sendResponse(buffer, contentType) {
|
|
84
|
+
const mimeType = contentType.split(';')[0];
|
|
85
|
+
const gzipResponse = this.app.config.gzip.enabled &&
|
|
86
|
+
this.app.config.gzip.types.includes(mimeType) &&
|
|
87
|
+
this.request.headers['accept-encoding']?.includes('gzip') &&
|
|
88
|
+
buffer.length >= this.app.config.gzip.minSize;
|
|
89
|
+
if (!this.response.hasHeader('Content-Type')) {
|
|
90
|
+
this.response.setHeader('Content-Type', contentType);
|
|
91
|
+
}
|
|
92
|
+
if (typeof buffer === 'string') {
|
|
93
|
+
buffer = Buffer.from(buffer, 'utf-8');
|
|
94
|
+
}
|
|
95
|
+
if (gzipResponse) {
|
|
96
|
+
this.response.setHeader('Content-Encoding', 'gzip');
|
|
97
|
+
const compressed = zlib.gzipSync(buffer, {
|
|
98
|
+
level: this.app.config.gzip.compressionLevel
|
|
99
|
+
});
|
|
100
|
+
this.response.setHeader('Content-Length', compressed.length);
|
|
101
|
+
this.response.write(compressed);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.response.setHeader('Content-Length', buffer.length);
|
|
105
|
+
this.response.write(buffer);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async createDocument(title, component, data) {
|
|
109
|
+
const doc = new Document(this.app, title, this);
|
|
110
|
+
await doc.loadComponent(component, data);
|
|
111
|
+
return doc;
|
|
112
|
+
}
|
|
113
|
+
async layoutDocument(layout, title, component, data, attributes) {
|
|
114
|
+
return await layout.document(this, title, component, data, attributes);
|
|
115
|
+
}
|
|
116
|
+
async show404() {
|
|
117
|
+
this.response.statusCode = 404;
|
|
118
|
+
const res = await this.app.emit('pageNotFound', this);
|
|
119
|
+
if (res.length > 0 && !!res[0]) {
|
|
120
|
+
await this.respondWith(res[0]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
initGetArgs() {
|
|
124
|
+
if (this.uri.indexOf('?') > -1) {
|
|
125
|
+
const uriParts = this.uri.split('?');
|
|
126
|
+
this.uri = uriParts[0];
|
|
127
|
+
this.getArgs = queryStringDecode(uriParts[1]);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
parseCookies() {
|
|
131
|
+
this.cookies = this.app.cookies.parse(this.request);
|
|
132
|
+
}
|
|
133
|
+
redirect(to, statusCode = 302) {
|
|
134
|
+
this.response.setHeader('Location', to);
|
|
135
|
+
this.response.writeHead(statusCode);
|
|
136
|
+
}
|
|
137
|
+
dataRaw(request) {
|
|
138
|
+
const chunks = [];
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
request.on('data', (chunk) => {
|
|
141
|
+
chunks.push(chunk);
|
|
142
|
+
});
|
|
143
|
+
request.on('close', () => {
|
|
144
|
+
const size = chunks.reduce((prev, curr) => {
|
|
145
|
+
return prev + curr.length;
|
|
146
|
+
}, 0);
|
|
147
|
+
const data = Buffer.concat(chunks, size);
|
|
148
|
+
resolve(data);
|
|
149
|
+
});
|
|
150
|
+
request.on('error', (e) => {
|
|
151
|
+
reject(e);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async parseBody() {
|
|
156
|
+
if (this.request.headers['content-type']) {
|
|
157
|
+
this.bodyRaw = await this.dataRaw(this.request);
|
|
158
|
+
if (this.request.headers['content-type'].indexOf('urlencoded') > -1) {
|
|
159
|
+
const bodyRaw = this.bodyRaw.toString('utf-8');
|
|
160
|
+
try {
|
|
161
|
+
this.body = queryStringDecode(bodyRaw);
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
throw new StructuredError(`Error parsing urlencoded request body, raw data: ${bodyRaw}`, e);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (this.request.headers['content-type'].indexOf('multipart/form-data') > -1) {
|
|
168
|
+
let boundary = /^multipart\/form-data; boundary=(.+)$/.exec(this.request.headers['content-type']);
|
|
169
|
+
if (boundary) {
|
|
170
|
+
boundary = `--${boundary[1]}`;
|
|
171
|
+
try {
|
|
172
|
+
this.body = this.parseBodyMultipart(this.bodyRaw.toString('utf-8'), boundary);
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
throw new StructuredError(`Error parsing multipart request body, raw data: ${this.bodyRaw.toString('utf-8')}`, e);
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
this.files = this.multipartBodyFiles(this.bodyRaw.toString('binary'), boundary);
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
throw new StructuredError(`Error parsing multipart request body files, raw data ${this.bodyRaw.toString('utf-8')}`, e);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if (this.request.headers['content-type'].indexOf('application/json') > -1) {
|
|
186
|
+
try {
|
|
187
|
+
this.body = JSON.parse(this.bodyRaw.toString());
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
throw new StructuredError(`Error parsing JSON request body, raw data: ${this.bodyRaw.toString('utf-8')}`, e);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
parseBodyMultipart(bodyRaw, boundary) {
|
|
196
|
+
const pairsRaw = bodyRaw.split(boundary);
|
|
197
|
+
const pairs = pairsRaw.map((pair) => {
|
|
198
|
+
const parts = pair.split(/\r?\n\r?\n/, 2).filter((part) => { return part.length > 0; });
|
|
199
|
+
if (parts.length > 0) {
|
|
200
|
+
const header = parts[0];
|
|
201
|
+
const data = typeof parts[1] === 'string' ? parts[1].trim() : '';
|
|
202
|
+
const headerParts = /Content-Disposition: form-data; name="([^\r\n"]+)"/m.exec(header);
|
|
203
|
+
if (headerParts) {
|
|
204
|
+
return {
|
|
205
|
+
key: headerParts[1],
|
|
206
|
+
value: data
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
});
|
|
212
|
+
const urlEncoded = pairs.reduce((prev, curr) => {
|
|
213
|
+
if (curr !== null) {
|
|
214
|
+
prev.push(`${curr.key}=${encodeURIComponent(curr.value.replaceAll('&', '%26'))}`);
|
|
215
|
+
}
|
|
216
|
+
return prev;
|
|
217
|
+
}, []).join('&');
|
|
218
|
+
return queryStringDecode(urlEncoded);
|
|
219
|
+
}
|
|
220
|
+
multipartBodyFiles(bodyRaw, boundary) {
|
|
221
|
+
let files = {};
|
|
222
|
+
const pairsRaw = bodyRaw.split(boundary);
|
|
223
|
+
pairsRaw.map((pair) => {
|
|
224
|
+
const parts = /Content-Disposition: form-data; name="(.+?)"; filename="(.+?)"\r\nContent-Type: (.*)\r\n\r\n([\s\S]+)$/m.exec(pair);
|
|
225
|
+
if (parts) {
|
|
226
|
+
const file = {
|
|
227
|
+
data: Buffer.from(parts[4].substring(0, parts[4].length - 2).trim(), 'binary'),
|
|
228
|
+
fileName: parts[2],
|
|
229
|
+
type: parts[3]
|
|
230
|
+
};
|
|
231
|
+
files = mergeDeep(files, queryStringDecodedSetValue(parts[1], file));
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
});
|
|
235
|
+
return files;
|
|
236
|
+
}
|
|
237
|
+
extractURIArguments(uri, match) {
|
|
238
|
+
if (match instanceof RegExp) {
|
|
239
|
+
const matches = match.exec(uri);
|
|
240
|
+
if (matches) {
|
|
241
|
+
return {
|
|
242
|
+
matches
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const uriArgs = {};
|
|
250
|
+
const segments = uri.split('/');
|
|
251
|
+
match.forEach((segmentPattern, i) => {
|
|
252
|
+
if (segmentPattern.name) {
|
|
253
|
+
uriArgs[segmentPattern.name] = segmentPattern.type === 'number' ? parseInt(segments[i]) : segments[i];
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return uriArgs;
|
|
257
|
+
}
|
|
258
|
+
async handle() {
|
|
259
|
+
if (this.handler !== null) {
|
|
260
|
+
if (!this.handler.staticAsset) {
|
|
261
|
+
const results = await this.app.emit('beforeRequestHandler', this);
|
|
262
|
+
if (results.includes(false)) {
|
|
263
|
+
this.response.end();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const URIArgs = this.extractURIArguments(this.uri, this.handler.match);
|
|
267
|
+
this.args = URIArgs;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
const response = await this.handler.callback.apply(this.handler.scope, [this]);
|
|
271
|
+
if (!this.response.headersSent) {
|
|
272
|
+
await this.respondWith(response);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
throw new StructuredError(`Error executing request handler ${this.handler.callback.name}`, e);
|
|
277
|
+
}
|
|
278
|
+
if (!this.handler.staticAsset) {
|
|
279
|
+
await this.app.emit('afterRequestHandler', this);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
let staticAsset = false;
|
|
284
|
+
if (this.app.config.url.isAsset(this.request.url || '')) {
|
|
285
|
+
const basePath = this.request.url?.startsWith('/assets/ts/') ? './' : '../';
|
|
286
|
+
const assetPath = path.resolve(basePath + this.request.url);
|
|
287
|
+
if (existsSync(assetPath)) {
|
|
288
|
+
await this.app.emit('beforeAssetAccess', this);
|
|
289
|
+
const extension = (this.request.url || '').split('.').pop();
|
|
290
|
+
let contentType = 'application/javascript';
|
|
291
|
+
if (extension) {
|
|
292
|
+
const typeByExtension = this.app.contentType(extension);
|
|
293
|
+
if (typeByExtension) {
|
|
294
|
+
contentType = typeByExtension;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
this.sendResponse(readFileSync(assetPath), contentType);
|
|
298
|
+
staticAsset = true;
|
|
299
|
+
await this.app.emit('afterAssetAccess', this);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (!staticAsset) {
|
|
303
|
+
await this.show404();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (!this.streamingData) {
|
|
307
|
+
this.response.end();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
isAjax() {
|
|
311
|
+
return this.request.headers['x-requested-with'] == 'xmlhttprequest';
|
|
312
|
+
}
|
|
313
|
+
duration() {
|
|
314
|
+
if (this.executionStartedAt === null || this.executionCompletedAt === null) {
|
|
315
|
+
return 0;
|
|
316
|
+
}
|
|
317
|
+
return this.executionCompletedAt - this.executionStartedAt;
|
|
318
|
+
}
|
|
319
|
+
complete() {
|
|
320
|
+
return this.executionCompletedAt !== null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { randomString } from '../Util.js';
|
|
2
2
|
export class Session {
|
|
3
|
+
application;
|
|
4
|
+
enabled = false;
|
|
5
|
+
sessions = {};
|
|
3
6
|
constructor(app) {
|
|
4
|
-
this.enabled = false;
|
|
5
|
-
this.sessions = {};
|
|
6
7
|
this.application = app;
|
|
7
8
|
this.application.on('beforeRequestHandler', async (ctx) => {
|
|
8
9
|
if (this.enabled) {
|
|
@@ -7,14 +7,18 @@ export const recognizedHTMLTags = [
|
|
|
7
7
|
'svg', 'g', 'text', 'path', 'circle', 'clipPath', 'defs', 'ellipse', 'rect', 'polygon', 'image', 'style',
|
|
8
8
|
];
|
|
9
9
|
export class DOMNode {
|
|
10
|
+
tagName;
|
|
11
|
+
root;
|
|
12
|
+
parentNode = null;
|
|
13
|
+
children = [];
|
|
14
|
+
isRoot;
|
|
15
|
+
attributes = [];
|
|
16
|
+
attributeMap = {};
|
|
17
|
+
style = {};
|
|
18
|
+
selfClosing;
|
|
19
|
+
explicitSelfClosing = false;
|
|
20
|
+
potentialComponentChildren = [];
|
|
10
21
|
constructor(root, parentNode, tagName) {
|
|
11
|
-
this.parentNode = null;
|
|
12
|
-
this.children = [];
|
|
13
|
-
this.attributes = [];
|
|
14
|
-
this.attributeMap = {};
|
|
15
|
-
this.style = {};
|
|
16
|
-
this.explicitSelfClosing = false;
|
|
17
|
-
this.potentialComponentChildren = [];
|
|
18
22
|
this.root = root === null ? this : root;
|
|
19
23
|
this.isRoot = root === null;
|
|
20
24
|
this.parentNode = parentNode;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { DOMFragment } from "./DOMFragment.js";
|
|
2
2
|
import { DOMNode } from "./DOMNode.js";
|
|
3
3
|
export class HTMLParser {
|
|
4
|
+
html;
|
|
5
|
+
offset = 0;
|
|
6
|
+
context;
|
|
7
|
+
state = 'idle';
|
|
8
|
+
tokenCurrent = '';
|
|
9
|
+
fragment = new DOMFragment();
|
|
10
|
+
explicitSelfClosing = false;
|
|
11
|
+
attributeOpenQuote = '"';
|
|
12
|
+
attributeNameCurrent = '';
|
|
13
|
+
attributeContext = null;
|
|
4
14
|
constructor(html) {
|
|
5
|
-
this.offset = 0;
|
|
6
|
-
this.state = 'idle';
|
|
7
|
-
this.tokenCurrent = '';
|
|
8
|
-
this.fragment = new DOMFragment();
|
|
9
|
-
this.explicitSelfClosing = false;
|
|
10
|
-
this.attributeOpenQuote = '"';
|
|
11
|
-
this.attributeNameCurrent = '';
|
|
12
|
-
this.attributeContext = null;
|
|
13
15
|
this.html = html;
|
|
14
16
|
this.context = this.fragment;
|
|
15
17
|
while (this.parse()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type ApplicationEvents = 'serverStarted' | 'beforeRequestHandler' | 'afterRequestHandler' | 'beforeRoutes' | 'afterRoutes' | 'beforeComponentsLoad' | 'afterComponentsLoaded' | 'documentCreated' | 'beforeAssetAccess' | 'afterAssetAccess' | 'pageNotFound';
|
|
1
|
+
export type ApplicationEvents = 'serverStarted' | 'beforeRequestHandler' | 'afterRequestHandler' | 'requestHandleError' | 'beforeRoutes' | 'afterRoutes' | 'beforeComponentsLoad' | 'afterComponentsLoaded' | 'documentCreated' | 'beforeAssetAccess' | 'afterAssetAccess' | 'pageNotFound';
|
|
@@ -2,9 +2,9 @@ import { ClientComponent } from "../client/ClientComponent.js";
|
|
|
2
2
|
import { Net } from "../client/Net.js";
|
|
3
3
|
import { Application } from "../server/Application.js";
|
|
4
4
|
import { Component } from "../server/Component.js";
|
|
5
|
+
import { RequestContext } from "../server/RequestContext.js";
|
|
5
6
|
import { EventEmitterCallback } from "./eventEmitter.types.js";
|
|
6
7
|
import { KeysOfUnion, LooseObject } from './general.types.js';
|
|
7
|
-
import { RequestContext } from "./request.types.js";
|
|
8
8
|
export type ComponentEntry = {
|
|
9
9
|
name: string;
|
|
10
10
|
path: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { IncomingMessage, ServerResponse } from "http";
|
|
2
1
|
import { LooseObject } from './general.types.js';
|
|
3
2
|
import { symbolArrays } from "../Symbols.js";
|
|
3
|
+
import { RequestContext } from '../server/RequestContext.js';
|
|
4
4
|
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
5
5
|
export type RequestCallback<R extends any, Body extends LooseObject | undefined> = (ctx: RequestContext<Body>) => Promise<R>;
|
|
6
6
|
export type RequestHandler = {
|
|
@@ -10,24 +10,6 @@ export type RequestHandler = {
|
|
|
10
10
|
scope: any;
|
|
11
11
|
staticAsset: boolean;
|
|
12
12
|
};
|
|
13
|
-
export type RequestContext<Body extends LooseObject | undefined = LooseObject> = {
|
|
14
|
-
request: IncomingMessage;
|
|
15
|
-
response: ServerResponse;
|
|
16
|
-
args: URIArguments;
|
|
17
|
-
handler: null | RequestHandler;
|
|
18
|
-
cookies: Record<string, string>;
|
|
19
|
-
body: Body;
|
|
20
|
-
bodyRaw?: Buffer;
|
|
21
|
-
files?: Record<string, RequestBodyRecordValue>;
|
|
22
|
-
data: RequestContextData;
|
|
23
|
-
sessionId?: string;
|
|
24
|
-
isAjax: boolean;
|
|
25
|
-
getArgs: PostedDataDecoded;
|
|
26
|
-
timeStart: number;
|
|
27
|
-
respondWith: (data: any) => Promise<void>;
|
|
28
|
-
redirect: (to: string, statusCode?: number) => void;
|
|
29
|
-
show404: () => Promise<void>;
|
|
30
|
-
};
|
|
31
13
|
export type PostedDataDecoded = Record<string, string | boolean | Array<string | boolean | PostedDataDecoded> | Record<string, string | boolean | Array<string | boolean | PostedDataDecoded>> | Record<string, string | boolean | Array<string | boolean>>>;
|
|
32
14
|
export type RequestBodyRecordValue = string | Array<RequestBodyRecordValue> | {
|
|
33
15
|
[key: string]: RequestBodyRecordValue;
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"main": "build/index",
|
|
22
|
-
"version": "1.
|
|
22
|
+
"version": "1.6.0",
|
|
23
23
|
"scripts": {
|
|
24
24
|
"develop": "tsc --watch",
|
|
25
25
|
"startDev": "cd build && nodemon --watch '../app/**/*' --watch '../build/**/*' -e js,html,hbs,css index.js",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
],
|
|
50
50
|
"exports": {
|
|
51
51
|
"./Types": "./build/system/Types.js",
|
|
52
|
+
"./RequestContext": "./build/system/server/RequestContext.js",
|
|
52
53
|
"./Symbols": "./build/system/Symbols.js",
|
|
53
54
|
"./Util": "./build/system/Util.js",
|
|
54
55
|
"./Application": "./build/system/server/Application.js",
|