structured-fw 0.7.2
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/Config.ts +47 -0
- package/LICENSE +21 -0
- package/README.md +332 -0
- package/app/Types.ts +1 -0
- package/app/models/README.md +9 -0
- package/app/routes/README.md +19 -0
- package/app/views/README.md +1 -0
- package/app/views/layout.html +1 -0
- package/bin/structured +114 -0
- package/build/Config.d.ts +2 -0
- package/build/Config.js +31 -0
- package/build/app/Types.d.ts +1 -0
- package/build/app/Types.js +1 -0
- package/build/app/models/Users.d.ts +0 -0
- package/build/app/models/Users.js +1 -0
- package/build/app/routes/Auth.d.ts +0 -0
- package/build/app/routes/Auth.js +1 -0
- package/build/app/routes/Test.d.ts +2 -0
- package/build/app/routes/Test.js +101 -0
- package/build/app/routes/Todo.d.ts +0 -0
- package/build/app/routes/Todo.js +1 -0
- package/build/app/routes/Upload.d.ts +0 -0
- package/build/app/routes/Upload.js +1 -0
- package/build/app/routes/Validation.d.ts +2 -0
- package/build/app/routes/Validation.js +34 -0
- package/build/app/views/components/ClientImport/ClientImport.client.d.ts +2 -0
- package/build/app/views/components/ClientImport/ClientImport.client.js +4 -0
- package/build/app/views/components/ClientImport/Export.d.ts +1 -0
- package/build/app/views/components/ClientImport/Export.js +1 -0
- package/build/app/views/components/Conditionals/Conditionals.client.d.ts +2 -0
- package/build/app/views/components/Conditionals/Conditionals.client.js +43 -0
- package/build/app/views/components/FormTest/FormTestNested/FormTestNested.d.ts +8 -0
- package/build/app/views/components/FormTest/FormTestNested/FormTestNested.js +7 -0
- package/build/app/views/components/ModelsTest/ModelsTest.client.d.ts +2 -0
- package/build/app/views/components/ModelsTest/ModelsTest.client.js +5 -0
- package/build/app/views/components/MultipartForm/MultipartForm.client.d.ts +0 -0
- package/build/app/views/components/MultipartForm/MultipartForm.client.js +1 -0
- package/build/app/views/components/PassObject/PassObject.d.ts +10 -0
- package/build/app/views/components/PassObject/PassObject.js +10 -0
- package/build/app/views/components/PassObject/ReceiveObj/ReceiveObj.d.ts +6 -0
- package/build/app/views/components/PassObject/ReceiveObj/ReceiveObj.js +6 -0
- package/build/app/views/components/RedrawAbort/RedrawAbort.client.d.ts +2 -0
- package/build/app/views/components/RedrawAbort/RedrawAbort.client.js +6 -0
- package/build/app/views/components/RedrawAbort/RedrawAbort.d.ts +8 -0
- package/build/app/views/components/RedrawAbort/RedrawAbort.js +8 -0
- package/build/app/views/components/ServerSideContext/ServerSideContext.d.ts +7 -0
- package/build/app/views/components/ServerSideContext/ServerSideContext.js +10 -0
- package/build/assets/ts/Export.d.ts +1 -0
- package/build/assets/ts/Export.js +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +3 -0
- package/build/system/Helpers.d.ts +3 -0
- package/build/system/Helpers.js +72 -0
- package/build/system/Symbols.d.ts +3 -0
- package/build/system/Symbols.js +3 -0
- package/build/system/Types.d.ts +171 -0
- package/build/system/Types.js +1 -0
- package/build/system/Util.d.ts +20 -0
- package/build/system/Util.js +336 -0
- package/build/system/client/App.d.ts +7 -0
- package/build/system/client/App.js +8 -0
- package/build/system/client/Client.d.ts +6 -0
- package/build/system/client/Client.js +9 -0
- package/build/system/client/ClientComponent.d.ts +68 -0
- package/build/system/client/ClientComponent.js +734 -0
- package/build/system/client/DataStore.d.ts +22 -0
- package/build/system/client/DataStore.js +64 -0
- package/build/system/client/DataStoreView.d.ts +19 -0
- package/build/system/client/DataStoreView.js +56 -0
- package/build/system/client/EventEmitter.d.ts +7 -0
- package/build/system/client/EventEmitter.js +31 -0
- package/build/system/client/Net.d.ts +13 -0
- package/build/system/client/Net.js +39 -0
- package/build/system/client/NetRequest.d.ts +13 -0
- package/build/system/client/NetRequest.js +45 -0
- package/build/system/server/Application.d.ts +31 -0
- package/build/system/server/Application.js +171 -0
- package/build/system/server/Component.d.ts +27 -0
- package/build/system/server/Component.js +249 -0
- package/build/system/server/Components.d.ts +12 -0
- package/build/system/server/Components.js +77 -0
- package/build/system/server/Cookies.d.ts +6 -0
- package/build/system/server/Cookies.js +19 -0
- package/build/system/server/Document.d.ts +24 -0
- package/build/system/server/Document.js +107 -0
- package/build/system/server/DocumentHead.d.ts +32 -0
- package/build/system/server/DocumentHead.js +118 -0
- package/build/system/server/FormValidation.d.ts +16 -0
- package/build/system/server/FormValidation.js +197 -0
- package/build/system/server/Handlebars.d.ts +11 -0
- package/build/system/server/Handlebars.js +34 -0
- package/build/system/server/Request.d.ts +21 -0
- package/build/system/server/Request.js +356 -0
- package/build/system/server/Session.d.ts +23 -0
- package/build/system/server/Session.js +114 -0
- package/build/system/server/dom/DOMFragment.d.ts +4 -0
- package/build/system/server/dom/DOMFragment.js +6 -0
- package/build/system/server/dom/DOMNode.d.ts +31 -0
- package/build/system/server/dom/DOMNode.js +110 -0
- package/build/system/server/dom/HTMLParser.d.ts +21 -0
- package/build/system/server/dom/HTMLParser.js +204 -0
- package/index.ts +4 -0
- package/package.json +31 -0
- package/system/Helpers.ts +97 -0
- package/system/Symbols.ts +6 -0
- package/system/Types.ts +234 -0
- package/system/Util.ts +488 -0
- package/system/client/App.ts +11 -0
- package/system/client/Client.ts +9 -0
- package/system/client/ClientComponent.ts +1117 -0
- package/system/client/DataStore.ts +101 -0
- package/system/client/DataStoreView.ts +82 -0
- package/system/client/EventEmitter.ts +38 -0
- package/system/client/Net.ts +58 -0
- package/system/client/NetRequest.ts +64 -0
- package/system/server/Application.ts +230 -0
- package/system/server/Component.ts +404 -0
- package/system/server/Components.ts +111 -0
- package/system/server/Cookies.ts +29 -0
- package/system/server/Document.ts +163 -0
- package/system/server/DocumentHead.ts +150 -0
- package/system/server/FormValidation.ts +231 -0
- package/system/server/Handlebars.ts +51 -0
- package/system/server/Request.ts +497 -0
- package/system/server/Session.ts +151 -0
- package/system/server/dom/DOMFragment.ts +7 -0
- package/system/server/dom/DOMNode.ts +140 -0
- package/system/server/dom/HTMLParser.ts +238 -0
- package/tsconfig.json +35 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AsteriskAny, StoreChangeCallback } from '../Types.js';
|
|
2
|
+
import { ClientComponent } from './ClientComponent.js';
|
|
3
|
+
export declare class DataStore {
|
|
4
|
+
protected data: {
|
|
5
|
+
[componentId: string]: {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
protected changeListeners: {
|
|
10
|
+
[componentId: string]: {
|
|
11
|
+
[key: string]: Array<StoreChangeCallback>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
set(component: ClientComponent, key: string, val: any, force?: boolean, triggerListeners?: boolean): DataStore;
|
|
15
|
+
get(componentId: string, key?: string): any;
|
|
16
|
+
hasKey(componentId: string, key: string): boolean;
|
|
17
|
+
clear(componentId: string): void;
|
|
18
|
+
destroy(componentId: string): void;
|
|
19
|
+
onChange(componentId: string, key: string | AsteriskAny, callback: StoreChangeCallback): DataStore;
|
|
20
|
+
onChangeCallbacks(componentId: string): Record<string, Array<StoreChangeCallback>>;
|
|
21
|
+
private unbindAll;
|
|
22
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { equalDeep } from '../Util.js';
|
|
2
|
+
export class DataStore {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.data = {};
|
|
5
|
+
this.changeListeners = {};
|
|
6
|
+
}
|
|
7
|
+
set(component, key, val, force = false, triggerListeners = true) {
|
|
8
|
+
const componentId = component.getData('componentId');
|
|
9
|
+
const oldValue = this.get(componentId, key);
|
|
10
|
+
if (!force && equalDeep({ value: oldValue }, { value: val })) {
|
|
11
|
+
return this;
|
|
12
|
+
}
|
|
13
|
+
if (!this.data[componentId]) {
|
|
14
|
+
this.data[componentId] = {};
|
|
15
|
+
}
|
|
16
|
+
this.data[componentId][key] = val;
|
|
17
|
+
if (triggerListeners) {
|
|
18
|
+
if (this.changeListeners[componentId] && (this.changeListeners[componentId][key] || this.changeListeners[componentId]['*'])) {
|
|
19
|
+
(this.changeListeners[componentId][key] || []).concat(this.changeListeners[componentId]['*'] || []).forEach((cb) => {
|
|
20
|
+
cb.apply(component, [key, val, oldValue, componentId]);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
get(componentId, key) {
|
|
27
|
+
if (!this.data[componentId]) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
if (typeof key !== 'string') {
|
|
31
|
+
return this.data[componentId];
|
|
32
|
+
}
|
|
33
|
+
return this.data[componentId][key];
|
|
34
|
+
}
|
|
35
|
+
hasKey(componentId, key) {
|
|
36
|
+
if (!(componentId in this.data)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return key in this.data[componentId];
|
|
40
|
+
}
|
|
41
|
+
clear(componentId) {
|
|
42
|
+
this.data[componentId] = {};
|
|
43
|
+
}
|
|
44
|
+
destroy(componentId) {
|
|
45
|
+
this.unbindAll(componentId);
|
|
46
|
+
this.clear(componentId);
|
|
47
|
+
}
|
|
48
|
+
onChange(componentId, key, callback) {
|
|
49
|
+
if (!(componentId in this.changeListeners)) {
|
|
50
|
+
this.changeListeners[componentId] = {};
|
|
51
|
+
}
|
|
52
|
+
if (!(key in this.changeListeners[componentId])) {
|
|
53
|
+
this.changeListeners[componentId][key] = [];
|
|
54
|
+
}
|
|
55
|
+
this.changeListeners[componentId][key].push(callback);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
onChangeCallbacks(componentId) {
|
|
59
|
+
return this.changeListeners[componentId];
|
|
60
|
+
}
|
|
61
|
+
unbindAll(componentId) {
|
|
62
|
+
delete this.changeListeners[componentId];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AsteriskAny, StoreChangeCallback } from '../Types.js';
|
|
2
|
+
import { ClientComponent } from './ClientComponent.js';
|
|
3
|
+
import { DataStore } from './DataStore.js';
|
|
4
|
+
export declare class DataStoreView {
|
|
5
|
+
private store;
|
|
6
|
+
private component;
|
|
7
|
+
private destroyed;
|
|
8
|
+
constructor(store: DataStore, component: ClientComponent);
|
|
9
|
+
set(key: string, val: any, force?: boolean, triggerListeners?: boolean): DataStoreView;
|
|
10
|
+
get<T>(key: string): T | undefined;
|
|
11
|
+
toggle(key: string): void;
|
|
12
|
+
keys(): Array<string>;
|
|
13
|
+
import(fields?: Array<string>, force?: boolean, triggerListeners?: boolean): void;
|
|
14
|
+
clear(): void;
|
|
15
|
+
destroy(): void;
|
|
16
|
+
private componentId;
|
|
17
|
+
onChange(key: string | AsteriskAny, callback: StoreChangeCallback): DataStoreView;
|
|
18
|
+
onChangeCallbacks(): Record<string, Array<StoreChangeCallback>>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export class DataStoreView {
|
|
2
|
+
constructor(store, component) {
|
|
3
|
+
this.destroyed = false;
|
|
4
|
+
this.store = store;
|
|
5
|
+
this.component = component;
|
|
6
|
+
}
|
|
7
|
+
set(key, val, force = false, triggerListeners = true) {
|
|
8
|
+
if (!this.destroyed) {
|
|
9
|
+
this.store.set(this.component, key, val, force, triggerListeners);
|
|
10
|
+
}
|
|
11
|
+
return this;
|
|
12
|
+
}
|
|
13
|
+
get(key) {
|
|
14
|
+
if (this.destroyed) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return this.store.get(this.componentId(), key);
|
|
18
|
+
}
|
|
19
|
+
toggle(key) {
|
|
20
|
+
this.set(key, !this.get(key));
|
|
21
|
+
}
|
|
22
|
+
keys() {
|
|
23
|
+
if (this.destroyed) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
return Object.keys(this.store.get(this.componentId()));
|
|
27
|
+
}
|
|
28
|
+
import(fields, force = false, triggerListeners = true) {
|
|
29
|
+
const fieldsImported = Array.isArray(fields) ? fields : Object.keys(this.component.getData());
|
|
30
|
+
fieldsImported.forEach((field) => {
|
|
31
|
+
if (force || !this.store.hasKey(this.componentId(), field)) {
|
|
32
|
+
this.set(field, this.component.getData(field), force, triggerListeners);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
clear() {
|
|
37
|
+
this.store.clear(this.componentId());
|
|
38
|
+
}
|
|
39
|
+
destroy() {
|
|
40
|
+
this.store.destroy(this.componentId());
|
|
41
|
+
this.destroyed = true;
|
|
42
|
+
}
|
|
43
|
+
componentId() {
|
|
44
|
+
return this.component.getData('componentId');
|
|
45
|
+
}
|
|
46
|
+
onChange(key, callback) {
|
|
47
|
+
if (this.destroyed) {
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
this.store.onChange(this.componentId(), key, callback);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
onChangeCallbacks() {
|
|
54
|
+
return this.store.onChangeCallbacks(this.componentId());
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { EventEmitterCallback } from "../Types.js";
|
|
2
|
+
export declare class EventEmitter {
|
|
3
|
+
protected listeners: Record<string, Array<EventEmitterCallback>>;
|
|
4
|
+
on(eventName: string, callback: EventEmitterCallback): void;
|
|
5
|
+
emit(eventName: string, payload?: any): void;
|
|
6
|
+
unbind(eventName: string, callback: EventEmitterCallback): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class EventEmitter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.listeners = {};
|
|
4
|
+
}
|
|
5
|
+
on(eventName, callback) {
|
|
6
|
+
if (!Array.isArray(this.listeners[eventName])) {
|
|
7
|
+
this.listeners[eventName] = [];
|
|
8
|
+
}
|
|
9
|
+
this.listeners[eventName].push(callback);
|
|
10
|
+
}
|
|
11
|
+
emit(eventName, payload) {
|
|
12
|
+
if (Array.isArray(this.listeners[eventName])) {
|
|
13
|
+
this.listeners[eventName].forEach((callback) => {
|
|
14
|
+
callback(payload);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
unbind(eventName, callback) {
|
|
19
|
+
if (Array.isArray(this.listeners[eventName])) {
|
|
20
|
+
while (true) {
|
|
21
|
+
const index = this.listeners[eventName].indexOf(callback);
|
|
22
|
+
if (index > -1) {
|
|
23
|
+
this.listeners[eventName].splice(index, 1);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
import { LooseObject, RequestMethod } from '../Types.js';
|
|
3
|
+
export declare class Net {
|
|
4
|
+
request(method: RequestMethod, url: string, headers?: IncomingHttpHeaders, body?: any, responseType?: XMLHttpRequestResponseType): Promise<string>;
|
|
5
|
+
get(url: string, headers?: IncomingHttpHeaders): Promise<string>;
|
|
6
|
+
delete(url: string, headers?: IncomingHttpHeaders): Promise<string>;
|
|
7
|
+
post(url: string, data: any, headers?: IncomingHttpHeaders): Promise<string>;
|
|
8
|
+
put(url: string, data: any, headers?: IncomingHttpHeaders): Promise<string>;
|
|
9
|
+
getJSON<T>(url: string, headers?: IncomingHttpHeaders): Promise<T>;
|
|
10
|
+
postJSON<T>(url: string, data: LooseObject, headers?: IncomingHttpHeaders): Promise<T>;
|
|
11
|
+
deleteJSON<T>(url: string, headers?: IncomingHttpHeaders): Promise<T>;
|
|
12
|
+
putJSON<T>(url: string, data: LooseObject, headers?: IncomingHttpHeaders): Promise<T>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { NetRequest } from './NetRequest.js';
|
|
2
|
+
export class Net {
|
|
3
|
+
async request(method, url, headers = {}, body, responseType = 'text') {
|
|
4
|
+
const request = new NetRequest(method, url, headers, responseType);
|
|
5
|
+
return request.send(body);
|
|
6
|
+
}
|
|
7
|
+
async get(url, headers = {}) {
|
|
8
|
+
return this.request('GET', url, headers);
|
|
9
|
+
}
|
|
10
|
+
async delete(url, headers = {}) {
|
|
11
|
+
return this.request('DELETE', url, headers);
|
|
12
|
+
}
|
|
13
|
+
async post(url, data, headers = {}) {
|
|
14
|
+
if (typeof data === 'object' && !headers['content-type'] && !(data instanceof FormData)) {
|
|
15
|
+
headers['content-type'] = 'application/json';
|
|
16
|
+
data = JSON.stringify(data);
|
|
17
|
+
}
|
|
18
|
+
return await this.request('POST', url, headers, data);
|
|
19
|
+
}
|
|
20
|
+
async put(url, data, headers = {}) {
|
|
21
|
+
if (typeof data === 'object' && !headers['content-type']) {
|
|
22
|
+
headers['content-type'] = 'application/json';
|
|
23
|
+
data = JSON.stringify(data);
|
|
24
|
+
}
|
|
25
|
+
return this.request('PUT', url, headers, data);
|
|
26
|
+
}
|
|
27
|
+
async getJSON(url, headers) {
|
|
28
|
+
return JSON.parse(await this.get(url, headers));
|
|
29
|
+
}
|
|
30
|
+
async postJSON(url, data, headers) {
|
|
31
|
+
return JSON.parse(await this.post(url, data, headers));
|
|
32
|
+
}
|
|
33
|
+
async deleteJSON(url, headers) {
|
|
34
|
+
return JSON.parse(await this.delete(url, headers));
|
|
35
|
+
}
|
|
36
|
+
async putJSON(url, data, headers) {
|
|
37
|
+
return JSON.parse(await this.put(url, data, headers));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
import { RequestMethod } from '../Types.js';
|
|
3
|
+
export declare class NetRequest {
|
|
4
|
+
xhr: XMLHttpRequest;
|
|
5
|
+
method: RequestMethod;
|
|
6
|
+
url: string;
|
|
7
|
+
headers: IncomingHttpHeaders;
|
|
8
|
+
responseType: XMLHttpRequestResponseType;
|
|
9
|
+
body: any;
|
|
10
|
+
requestSent: boolean;
|
|
11
|
+
constructor(method: RequestMethod, url: string, headers?: IncomingHttpHeaders, responseType?: XMLHttpRequestResponseType, body?: any);
|
|
12
|
+
send(body?: any): Promise<string>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export class NetRequest {
|
|
2
|
+
constructor(method, url, headers = {}, responseType = 'text', body) {
|
|
3
|
+
this.xhr = new XMLHttpRequest();
|
|
4
|
+
this.requestSent = false;
|
|
5
|
+
this.method = method;
|
|
6
|
+
this.url = url;
|
|
7
|
+
this.headers = headers;
|
|
8
|
+
this.responseType = responseType;
|
|
9
|
+
this.body = body;
|
|
10
|
+
this.xhr.open(this.method, this.url);
|
|
11
|
+
this.xhr.responseType = this.responseType;
|
|
12
|
+
if (!('x-requested-with' in headers)) {
|
|
13
|
+
headers['x-requested-with'] = 'xmlhttprequest';
|
|
14
|
+
}
|
|
15
|
+
for (const header in headers) {
|
|
16
|
+
const headerValue = headers[header];
|
|
17
|
+
if (typeof headerValue === 'string') {
|
|
18
|
+
this.xhr.setRequestHeader(header, headerValue);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
console.warn('Only string header values are supported');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async send(body) {
|
|
26
|
+
if (this.requestSent) {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
this.requestSent = true;
|
|
30
|
+
if (typeof body !== 'undefined') {
|
|
31
|
+
this.body = body;
|
|
32
|
+
}
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
this.xhr.onreadystatechange = () => {
|
|
35
|
+
if (this.xhr.readyState == 4) {
|
|
36
|
+
resolve(this.xhr.responseText);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
this.xhr.onerror = (err) => {
|
|
40
|
+
reject(err);
|
|
41
|
+
};
|
|
42
|
+
this.xhr.send(body);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Server } from 'node:http';
|
|
2
|
+
import { ApplicationEvents, LooseObject, RequestCallback, StructuredConfig } from '../Types';
|
|
3
|
+
import { Components } from './Components.js';
|
|
4
|
+
import { Session } from './Session.js';
|
|
5
|
+
import { Request } from './Request.js';
|
|
6
|
+
import { Handlebars } from './Handlebars.js';
|
|
7
|
+
import { Cookies } from './Cookies.js';
|
|
8
|
+
import { RequestContextData } from '../../app/Types.js';
|
|
9
|
+
export declare class Application {
|
|
10
|
+
readonly config: StructuredConfig;
|
|
11
|
+
server: null | Server;
|
|
12
|
+
listening: boolean;
|
|
13
|
+
private readonly eventEmitter;
|
|
14
|
+
readonly cookies: Cookies;
|
|
15
|
+
readonly session: Session;
|
|
16
|
+
readonly request: Request;
|
|
17
|
+
readonly components: Components;
|
|
18
|
+
readonly handlebars: Handlebars;
|
|
19
|
+
readonly exportedRequestContextData: Array<keyof RequestContextData>;
|
|
20
|
+
constructor(config: StructuredConfig);
|
|
21
|
+
init(): Promise<void>;
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
on(evt: ApplicationEvents, callback: RequestCallback | ((payload?: any) => void)): void;
|
|
24
|
+
emit(eventName: ApplicationEvents, payload?: any): Promise<Array<any>>;
|
|
25
|
+
importEnv<T extends LooseObject>(smartPrimitives?: boolean): T;
|
|
26
|
+
exportContextFields(...fields: Array<keyof RequestContextData>): void;
|
|
27
|
+
contentType(extension: string): string | false;
|
|
28
|
+
private respondWithComponent;
|
|
29
|
+
memoryUsage(): NodeJS.MemoryUsage;
|
|
30
|
+
printMemoryUsage(): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as mime from 'mime-types';
|
|
6
|
+
import { Document } from './Document.js';
|
|
7
|
+
import { Components } from './Components.js';
|
|
8
|
+
import { Session } from './Session.js';
|
|
9
|
+
import { toSnakeCase } from '../Util.js';
|
|
10
|
+
import { Request } from './Request.js';
|
|
11
|
+
import { Handlebars } from './Handlebars.js';
|
|
12
|
+
import { Cookies } from './Cookies.js';
|
|
13
|
+
export class Application {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.server = null;
|
|
16
|
+
this.listening = false;
|
|
17
|
+
this.eventEmitter = new EventEmitter();
|
|
18
|
+
this.handlebars = new Handlebars();
|
|
19
|
+
this.exportedRequestContextData = [];
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.cookies = new Cookies();
|
|
22
|
+
this.session = new Session(this);
|
|
23
|
+
this.request = new Request(this);
|
|
24
|
+
this.components = new Components(this);
|
|
25
|
+
this.session.start();
|
|
26
|
+
if (this.config.autoInit) {
|
|
27
|
+
this.init();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async init() {
|
|
31
|
+
this.eventEmitter.setMaxListeners(10);
|
|
32
|
+
try {
|
|
33
|
+
await this.handlebars.loadHelpers('../Helpers.js');
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
console.error(e.message);
|
|
37
|
+
}
|
|
38
|
+
await this.emit('beforeComponentLoad');
|
|
39
|
+
this.components.loadComponents();
|
|
40
|
+
await this.emit('afterComponentLoad');
|
|
41
|
+
await this.emit('beforeRoutes');
|
|
42
|
+
await this.request.loadHandlers();
|
|
43
|
+
await this.emit('afterRoutes');
|
|
44
|
+
if (this.config.url.componentRender !== false) {
|
|
45
|
+
this.request.on('POST', `${this.config.url.componentRender}`, async (ctx) => {
|
|
46
|
+
const input = ctx.body;
|
|
47
|
+
await this.respondWithComponent(ctx, input.component, input.attributes || undefined, typeof input.unwrap === 'boolean' ? input.unwrap : true);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
this.request.on('GET', /^\/assets\/client-js/, async ({ request, response }) => {
|
|
51
|
+
const uri = request.url?.substring(18);
|
|
52
|
+
const filePath = path.resolve('./system/', uri);
|
|
53
|
+
if (existsSync(filePath)) {
|
|
54
|
+
response.setHeader('Content-Type', 'application/javascript');
|
|
55
|
+
response.write(readFileSync(filePath));
|
|
56
|
+
response.end();
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
response.statusCode = 404;
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}, this, true);
|
|
63
|
+
await this.start();
|
|
64
|
+
}
|
|
65
|
+
start() {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
this.server = createServer((req, res) => {
|
|
68
|
+
this.request.handle(req, res);
|
|
69
|
+
});
|
|
70
|
+
this.server.listen(this.config.http.port, this.config.http.host || '127.0.0.1', async () => {
|
|
71
|
+
const address = (this.config.http.host !== undefined ? this.config.http.host : '') + ':' + this.config.http.port;
|
|
72
|
+
await this.emit('serverStarted');
|
|
73
|
+
console.log(`Server started on ${address}`);
|
|
74
|
+
resolve();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
on(evt, callback) {
|
|
79
|
+
this.eventEmitter.on(evt, callback);
|
|
80
|
+
}
|
|
81
|
+
async emit(eventName, payload) {
|
|
82
|
+
const promises = [];
|
|
83
|
+
const listeners = this.eventEmitter.rawListeners(eventName);
|
|
84
|
+
for (let i = 0; i < listeners.length; i++) {
|
|
85
|
+
promises.push(listeners[i](payload));
|
|
86
|
+
}
|
|
87
|
+
const results = await Promise.allSettled(promises);
|
|
88
|
+
return results.filter((res) => {
|
|
89
|
+
return res.status === 'fulfilled';
|
|
90
|
+
}).map((res) => {
|
|
91
|
+
return res.value;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
importEnv(smartPrimitives = true) {
|
|
95
|
+
const values = {};
|
|
96
|
+
const usePrefix = typeof this.config.envPrefix === 'string';
|
|
97
|
+
const prefixLength = usePrefix ? this.config.envPrefix.length : 0;
|
|
98
|
+
for (const key in process.env) {
|
|
99
|
+
if (!usePrefix || key.startsWith(this.config.envPrefix)) {
|
|
100
|
+
let value = process.env[key];
|
|
101
|
+
const keyWithoutPrefix = key.substring(prefixLength + 1);
|
|
102
|
+
if (smartPrimitives) {
|
|
103
|
+
if (value === 'undefined') {
|
|
104
|
+
value = undefined;
|
|
105
|
+
}
|
|
106
|
+
else if (value === 'null') {
|
|
107
|
+
value = null;
|
|
108
|
+
}
|
|
109
|
+
else if (value === 'true') {
|
|
110
|
+
value = true;
|
|
111
|
+
}
|
|
112
|
+
else if (value === 'false') {
|
|
113
|
+
value = false;
|
|
114
|
+
}
|
|
115
|
+
else if (/^-?\d+$/.test(value)) {
|
|
116
|
+
value = parseInt(value);
|
|
117
|
+
}
|
|
118
|
+
else if (/^\d+\.\d+$/.test(value)) {
|
|
119
|
+
value = parseFloat(value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
values[keyWithoutPrefix] = value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return values;
|
|
126
|
+
}
|
|
127
|
+
exportContextFields(...fields) {
|
|
128
|
+
fields.forEach((field) => {
|
|
129
|
+
if (!this.exportedRequestContextData.includes(field)) {
|
|
130
|
+
this.exportedRequestContextData.push(field);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
contentType(extension) {
|
|
135
|
+
return mime.contentType(extension);
|
|
136
|
+
}
|
|
137
|
+
async respondWithComponent(ctx, componentName, attributes, unwrap = true) {
|
|
138
|
+
const component = this.components.getByName(componentName);
|
|
139
|
+
if (component) {
|
|
140
|
+
const document = new Document(this, '', ctx);
|
|
141
|
+
const data = attributes;
|
|
142
|
+
await document.loadComponent(component.name, data);
|
|
143
|
+
const exportedData = component.exportData ? document.data : (component.exportFields ? component.exportFields.reduce((prev, curr) => {
|
|
144
|
+
prev[curr] = document.children[0].data[curr];
|
|
145
|
+
return prev;
|
|
146
|
+
}, {}) : {});
|
|
147
|
+
ctx.respondWith({
|
|
148
|
+
html: document.children[0].dom[unwrap ? 'innerHTML' : 'outerHTML'],
|
|
149
|
+
initializers: document.initInitializers(),
|
|
150
|
+
data: exportedData
|
|
151
|
+
});
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
memoryUsage() {
|
|
157
|
+
return process.memoryUsage();
|
|
158
|
+
}
|
|
159
|
+
printMemoryUsage() {
|
|
160
|
+
const usage = this.memoryUsage();
|
|
161
|
+
let total = 0;
|
|
162
|
+
const totals = Object.keys(usage).reduce((prev, key) => {
|
|
163
|
+
const usedMb = usage[key] / 1000000;
|
|
164
|
+
prev[toSnakeCase(key).replaceAll('_', ' ')] = [parseFloat(usedMb.toFixed(1)), 'Mb'];
|
|
165
|
+
total += usedMb;
|
|
166
|
+
return prev;
|
|
167
|
+
}, {});
|
|
168
|
+
totals.total = [parseFloat(total.toFixed(1)), 'Mb'];
|
|
169
|
+
console.table(totals);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Document } from './Document.js';
|
|
2
|
+
import { ComponentEntry, LooseObject } from '../Types.js';
|
|
3
|
+
import { DOMNode } from './dom/DOMNode.js';
|
|
4
|
+
export declare class Component {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
document: Document;
|
|
8
|
+
parent: null | Document | Component;
|
|
9
|
+
children: Array<Component>;
|
|
10
|
+
path: Array<string>;
|
|
11
|
+
attributesRaw: Record<string, string | true>;
|
|
12
|
+
attributes: Record<string, string | number | boolean | LooseObject | null>;
|
|
13
|
+
dom: DOMNode;
|
|
14
|
+
data: LooseObject;
|
|
15
|
+
entry: null | ComponentEntry;
|
|
16
|
+
isRoot: boolean;
|
|
17
|
+
constructor(name: string, node?: DOMNode, parent?: Document | Component, autoInit?: boolean);
|
|
18
|
+
init(html: string, data?: LooseObject): Promise<void>;
|
|
19
|
+
setAttributes(attributes: Record<string, any>, prefix?: string, encode?: boolean): void;
|
|
20
|
+
private initChildren;
|
|
21
|
+
protected importedParentData(parentData: LooseObject): LooseObject;
|
|
22
|
+
protected initAttributesData(domNode?: DOMNode): void;
|
|
23
|
+
private attributePreffix;
|
|
24
|
+
private attributeDataType;
|
|
25
|
+
private attributeUnpreffixed;
|
|
26
|
+
protected fillData(data: LooseObject): void;
|
|
27
|
+
}
|