structured-fw 1.0.8 → 1.0.9
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 +5 -2
- package/build/system/EventEmitter.d.ts +1 -1
- package/build/system/RequestHandler.d.ts +1 -0
- package/build/system/RequestHandler.js +1 -0
- package/build/system/RequestMethod.d.ts +1 -0
- package/build/system/RequestMethod.js +1 -0
- package/build/system/Types.d.ts +10 -175
- package/build/system/Types.js +10 -1
- package/build/system/Util.d.ts +2 -1
- package/build/system/client/ClientComponent.d.ts +2 -1
- package/build/system/client/ClientComponent.js +42 -10
- package/build/system/client/DataStore.d.ts +1 -1
- package/build/system/client/DataStoreView.d.ts +1 -1
- package/build/system/client/Net.d.ts +2 -1
- package/build/system/client/NetRequest.d.ts +1 -1
- package/build/system/request.d.ts +1 -0
- package/build/system/request.js +1 -0
- package/build/system/server/Application.d.ts +4 -1
- package/build/system/server/Application.js +1 -1
- package/build/system/server/Component.d.ts +2 -1
- package/build/system/server/Component.js +11 -8
- package/build/system/server/Components.d.ts +2 -1
- package/build/system/server/Components.js +7 -7
- package/build/system/server/Cookies.d.ts +1 -1
- package/build/system/server/Document.d.ts +5 -3
- package/build/system/server/Document.js +3 -3
- package/build/system/server/DocumentHead.d.ts +1 -1
- package/build/system/server/FormValidation.d.ts +2 -1
- package/build/system/server/Handlebars.d.ts +1 -1
- package/build/system/server/Layout.d.ts +4 -2
- package/build/system/server/Layout.js +3 -1
- package/build/system/server/Request.d.ts +2 -1
- package/build/system/server/Request.js +1 -0
- package/build/system/server/Session.d.ts +2 -1
- package/build/system/server/dom/DOMNode.d.ts +1 -0
- package/build/system/server/dom/DOMNode.js +9 -3
- package/build/system/server/dom/HTMLParser.d.ts +1 -0
- package/build/system/server/dom/HTMLParser.js +31 -8
- package/build/system/types/application.types.d.ts +1 -0
- package/build/system/types/application.types.js +1 -0
- package/build/system/types/component.types.d.ts +59 -0
- package/build/system/types/component.types.js +1 -0
- package/build/system/types/document.types.d.ts +5 -0
- package/build/system/types/document.types.js +1 -0
- package/build/system/types/eventEmitter.types.d.ts +1 -0
- package/build/system/types/eventEmitter.types.js +1 -0
- package/build/system/types/general.types.d.ts +1 -0
- package/build/system/types/general.types.js +1 -0
- package/build/system/types/request.d.ts +57 -0
- package/build/system/types/request.js +1 -0
- package/build/system/types/request.types.d.ts +58 -0
- package/build/system/types/request.types.js +1 -0
- package/build/system/types/session.types.d.ts +6 -0
- package/build/system/types/session.types.js +1 -0
- package/build/system/types/store.types.d.ts +1 -0
- package/build/system/types/store.types.js +1 -0
- package/build/system/types/structured.types.d.ts +32 -0
- package/build/system/types/structured.types.js +1 -0
- package/build/system/types/validation.types.d.ts +18 -0
- package/build/system/types/validation.types.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -275,6 +275,9 @@ type RequestContext<Body extends LooseObject | undefined = LooseObject> = {
|
|
|
275
275
|
// true if x-requested-with header is received and it equals 'xmlhttprequest'
|
|
276
276
|
isAjax: boolean,
|
|
277
277
|
|
|
278
|
+
// time when request was received (unix timestamp in milliseconds)
|
|
279
|
+
timeStart: number,
|
|
280
|
+
|
|
278
281
|
// URL GET arguments
|
|
279
282
|
getArgs: PostedDataDecoded,
|
|
280
283
|
|
|
@@ -766,9 +769,9 @@ Version 0.8.7 introduced the `Layout` class, which allows accomplishing the abov
|
|
|
766
769
|
<template></template>
|
|
767
770
|
...
|
|
768
771
|
```
|
|
769
|
-
2) `/index.ts` (`app` is an instance of `Application`)
|
|
772
|
+
2) `/index.ts` (`app` is an instance of `Application`), 3rd argument is optional BCP 47 language tag
|
|
770
773
|
```
|
|
771
|
-
export const layout = new Layout(app, 'layout');
|
|
774
|
+
export const layout = new Layout(app, 'layout', 'en');
|
|
772
775
|
```
|
|
773
776
|
3) `/app/routes/Test.ts`
|
|
774
777
|
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventEmitterCallback } from
|
|
1
|
+
import { EventEmitterCallback } from './types/eventEmitter.types.js';
|
|
2
2
|
export declare class EventEmitter<T extends Record<string, any> = Record<string, any>> {
|
|
3
3
|
protected listeners: Partial<Record<Extract<keyof T, string>, Array<EventEmitterCallback<any>>>>;
|
|
4
4
|
on<K extends Extract<keyof T, string>>(eventName: K, callback: EventEmitterCallback<T[K]>): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/build/system/Types.d.ts
CHANGED
|
@@ -1,175 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
removeTrailingSlash: boolean;
|
|
12
|
-
componentRender: false | string;
|
|
13
|
-
isAsset: (url: string) => boolean;
|
|
14
|
-
};
|
|
15
|
-
routes: {
|
|
16
|
-
readonly path: string;
|
|
17
|
-
};
|
|
18
|
-
components: {
|
|
19
|
-
readonly path: string;
|
|
20
|
-
readonly componentNameAttribute: string;
|
|
21
|
-
};
|
|
22
|
-
session: {
|
|
23
|
-
readonly cookieName: string;
|
|
24
|
-
readonly keyLength: number;
|
|
25
|
-
readonly durationSeconds: number;
|
|
26
|
-
readonly garbageCollectIntervalSeconds: number;
|
|
27
|
-
};
|
|
28
|
-
http: {
|
|
29
|
-
host?: string;
|
|
30
|
-
port: number;
|
|
31
|
-
linkHeaderRel: 'preload' | 'preconnect';
|
|
32
|
-
};
|
|
33
|
-
readonly runtime: 'Node.js' | 'Deno';
|
|
34
|
-
};
|
|
35
|
-
export type StructuredClientConfig = {
|
|
36
|
-
componentRender: StructuredConfig['url']['componentRender'];
|
|
37
|
-
componentNameAttribute: string;
|
|
38
|
-
};
|
|
39
|
-
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
40
|
-
export type RequestCallback<R extends any, Body extends LooseObject | undefined> = (ctx: RequestContext<Body>) => Promise<R>;
|
|
41
|
-
export type RequestHandler = {
|
|
42
|
-
match: Array<URISegmentPattern> | RegExp;
|
|
43
|
-
methods: Array<RequestMethod>;
|
|
44
|
-
callback: RequestCallback<any, LooseObject | undefined>;
|
|
45
|
-
scope: any;
|
|
46
|
-
staticAsset: boolean;
|
|
47
|
-
};
|
|
48
|
-
export type RequestContext<Body extends LooseObject | undefined = LooseObject> = {
|
|
49
|
-
request: IncomingMessage;
|
|
50
|
-
response: ServerResponse;
|
|
51
|
-
args: URIArguments;
|
|
52
|
-
handler: null | RequestHandler;
|
|
53
|
-
cookies: Record<string, string>;
|
|
54
|
-
body: Body;
|
|
55
|
-
bodyRaw?: Buffer;
|
|
56
|
-
files?: Record<string, RequestBodyRecordValue>;
|
|
57
|
-
data: RequestContextData;
|
|
58
|
-
sessionId?: string;
|
|
59
|
-
isAjax: boolean;
|
|
60
|
-
getArgs: PostedDataDecoded;
|
|
61
|
-
respondWith: (data: any) => void;
|
|
62
|
-
redirect: (to: string, statusCode?: number) => void;
|
|
63
|
-
show404: () => Promise<void>;
|
|
64
|
-
};
|
|
65
|
-
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>>>;
|
|
66
|
-
export type RequestBodyRecordValue = string | Array<RequestBodyRecordValue> | {
|
|
67
|
-
[key: string]: RequestBodyRecordValue;
|
|
68
|
-
} | {
|
|
69
|
-
[key: string]: RequestBodyFile;
|
|
70
|
-
} | Array<RequestBodyFile> | RequestBodyFile;
|
|
71
|
-
export interface RequestBodyArguments {
|
|
72
|
-
[key: string]: RequestBodyRecordValue;
|
|
73
|
-
[symbolArrays]?: {
|
|
74
|
-
[key: string]: Array<string>;
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
export type RequestBodyFiles = {
|
|
78
|
-
[key: string]: RequestBodyFile;
|
|
79
|
-
};
|
|
80
|
-
export type RequestBodyFile = {
|
|
81
|
-
fileName: string;
|
|
82
|
-
data: Buffer;
|
|
83
|
-
type: string;
|
|
84
|
-
};
|
|
85
|
-
export type URISegmentPattern = {
|
|
86
|
-
pattern: string | RegExp;
|
|
87
|
-
name?: string;
|
|
88
|
-
type?: 'string' | 'number';
|
|
89
|
-
};
|
|
90
|
-
export type URIArguments = {
|
|
91
|
-
[key: string]: string | number | RegExpExecArray;
|
|
92
|
-
};
|
|
93
|
-
export type DocumentResource = {
|
|
94
|
-
path: string;
|
|
95
|
-
attributes: Record<string, string | null>;
|
|
96
|
-
priority: number;
|
|
97
|
-
};
|
|
98
|
-
export type ComponentEntry = {
|
|
99
|
-
name: string;
|
|
100
|
-
path: {
|
|
101
|
-
absolute: string;
|
|
102
|
-
relative: string;
|
|
103
|
-
relativeToViews: string;
|
|
104
|
-
build: string;
|
|
105
|
-
html: string;
|
|
106
|
-
jsServer?: string;
|
|
107
|
-
jsClient?: string;
|
|
108
|
-
};
|
|
109
|
-
hasJS: boolean;
|
|
110
|
-
html: string;
|
|
111
|
-
static: boolean;
|
|
112
|
-
module?: ComponentScaffold;
|
|
113
|
-
renderTagName?: string;
|
|
114
|
-
exportData: boolean;
|
|
115
|
-
exportFields?: Array<string>;
|
|
116
|
-
attributes?: Record<string, string>;
|
|
117
|
-
initializer?: InitializerFunction;
|
|
118
|
-
};
|
|
119
|
-
export interface ComponentScaffold {
|
|
120
|
-
tagName?: string;
|
|
121
|
-
exportData?: boolean;
|
|
122
|
-
exportFields?: Array<string>;
|
|
123
|
-
static?: boolean;
|
|
124
|
-
deferred?: (data: Record<string, any>, ctx: RequestContext | undefined, app: Application) => boolean;
|
|
125
|
-
attributes?: Record<string, string>;
|
|
126
|
-
getData: (this: ComponentScaffold, data: RequestBodyArguments | LooseObject, ctx: undefined | RequestContext, app: Application, component: Component) => Promise<LooseObject | null>;
|
|
127
|
-
[key: string]: any;
|
|
128
|
-
}
|
|
129
|
-
export type LooseObject = Record<string, any>;
|
|
130
|
-
export type ApplicationEvents = 'serverStarted' | 'beforeRequestHandler' | 'afterRequestHandler' | 'beforeRoutes' | 'afterRoutes' | 'beforeComponentsLoad' | 'afterComponentsLoaded' | 'documentCreated' | 'beforeAssetAccess' | 'afterAssetAccess' | 'pageNotFound';
|
|
131
|
-
export type SessionEntry = {
|
|
132
|
-
sessionId: string;
|
|
133
|
-
lastRequest: number;
|
|
134
|
-
data: LooseObject;
|
|
135
|
-
};
|
|
136
|
-
export type ValidationRuleWithArguments = [string, any];
|
|
137
|
-
export type FormValidationEntry = {
|
|
138
|
-
field: [string, string];
|
|
139
|
-
rules: Array<string | ValidationRuleWithArguments | ValidatorFunction>;
|
|
140
|
-
};
|
|
141
|
-
export type ValidatorFunction = (data: PostedDataDecoded, field: string, arg: number, rules: Array<string | ValidationRuleWithArguments | ValidatorFunction>) => Promise<boolean>;
|
|
142
|
-
export type ValidatorErrorDecorator = (fieldHumanReadable: string, data: PostedDataDecoded, field: string, arg: any) => string | Promise<string>;
|
|
143
|
-
export type ValidationErrors = {
|
|
144
|
-
[field: string]: Array<string>;
|
|
145
|
-
};
|
|
146
|
-
export type ValidationErrorsSingle = {
|
|
147
|
-
[field: string]: string;
|
|
148
|
-
};
|
|
149
|
-
export type ValidationResult = {
|
|
150
|
-
valid: boolean;
|
|
151
|
-
errors: ValidationErrors | ValidationErrorsSingle;
|
|
152
|
-
};
|
|
153
|
-
export type InitializerFunction = (this: ClientComponent, ctx: InitializerFunctionContext) => Promise<void>;
|
|
154
|
-
export type Initializers = {
|
|
155
|
-
[key: string]: InitializerFunction;
|
|
156
|
-
};
|
|
157
|
-
export type InitializerFunctionContext = {
|
|
158
|
-
net: Net;
|
|
159
|
-
isRedraw: boolean;
|
|
160
|
-
};
|
|
161
|
-
export type StoreChangeCallback = (key: string, value: any, oldValue: any, componentId: string) => void;
|
|
162
|
-
export type ClientComponentEventCallback<T> = (e: Event, data: T, element: HTMLElement | Window) => void;
|
|
163
|
-
export type ClientComponentBoundEvent<T extends LooseObject | undefined = undefined> = {
|
|
164
|
-
element: HTMLElement | Window;
|
|
165
|
-
event: keyof HTMLElementEventMap;
|
|
166
|
-
callback: (e: Event) => void;
|
|
167
|
-
callbackOriginal: ClientComponentEventCallback<T>;
|
|
168
|
-
};
|
|
169
|
-
export type ClientComponentTransition = {
|
|
170
|
-
fade: false | number;
|
|
171
|
-
slide: false | number;
|
|
172
|
-
};
|
|
173
|
-
export type ClientComponentTransitionEvent = 'show' | 'hide';
|
|
174
|
-
export type ClientComponentTransitions = Record<ClientComponentTransitionEvent, ClientComponentTransition>;
|
|
175
|
-
export type EventEmitterCallback<T> = (payload: T, eventName: string) => void;
|
|
1
|
+
export * from './types/request.types.js';
|
|
2
|
+
export * from './types/document.types.js';
|
|
3
|
+
export * from './types/component.types.js';
|
|
4
|
+
export * from './types/session.types.js';
|
|
5
|
+
export * from './types/store.types.js';
|
|
6
|
+
export * from './types/validation.types.js';
|
|
7
|
+
export * from './types/application.types.js';
|
|
8
|
+
export * from './types/eventEmitter.types.js';
|
|
9
|
+
export * from './types/general.types.js';
|
|
10
|
+
export * from './types/structured.types.js';
|
package/build/system/Types.js
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
export * from './types/request.types.js';
|
|
2
|
+
export * from './types/document.types.js';
|
|
3
|
+
export * from './types/component.types.js';
|
|
4
|
+
export * from './types/session.types.js';
|
|
5
|
+
export * from './types/store.types.js';
|
|
6
|
+
export * from './types/validation.types.js';
|
|
7
|
+
export * from './types/application.types.js';
|
|
8
|
+
export * from './types/eventEmitter.types.js';
|
|
9
|
+
export * from './types/general.types.js';
|
|
10
|
+
export * from './types/structured.types.js';
|
package/build/system/Util.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { LooseObject
|
|
1
|
+
import { LooseObject } from './types/general.types.js';
|
|
2
|
+
import { PostedDataDecoded } from "./types/request.types.js";
|
|
2
3
|
export declare function queryStringDecode(queryString: string, initialValue?: PostedDataDecoded, trimValues?: boolean): PostedDataDecoded;
|
|
3
4
|
export declare function queryStringDecodedSetValue(obj: PostedDataDecoded | string, value: any): LooseObject;
|
|
4
5
|
export declare function objectEach<T>(obj: T, callbackEach: (key: keyof T, value: T[keyof T]) => void): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LooseObject } from '../types/general.types.js';
|
|
2
|
+
import { ClientComponentEventCallback } from '../types/component.types.js';
|
|
2
3
|
import { DataStoreView } from './DataStoreView.js';
|
|
3
4
|
import { DataStore } from './DataStore.js';
|
|
4
5
|
import { Net } from './Net.js';
|
|
@@ -407,7 +407,7 @@ export class ClientComponent extends EventEmitter {
|
|
|
407
407
|
return isTrue;
|
|
408
408
|
}
|
|
409
409
|
else {
|
|
410
|
-
const parts = /^(!)?\s*([a-zA-
|
|
410
|
+
const parts = /^(!)?\s*([a-zA-Z0-9_]+)\s*((?:==)|(?:===)|(?:!=)|(?:!==)|<|>|(?:<=)|(?:>=))?\s?([^=]+)?$/.exec(condition);
|
|
411
411
|
if (parts === null) {
|
|
412
412
|
console.error(`Could not parse condition ${condition}`);
|
|
413
413
|
return false;
|
|
@@ -619,25 +619,41 @@ export class ClientComponent extends EventEmitter {
|
|
|
619
619
|
const onTransitionEnd = (e) => {
|
|
620
620
|
domNode.style.opacity = '1';
|
|
621
621
|
domNode.style.transition = '';
|
|
622
|
-
domNode.style.transformOrigin = '
|
|
622
|
+
domNode.style.transformOrigin = '';
|
|
623
|
+
domNode.style.transform = '';
|
|
623
624
|
domNode.removeEventListener('transitionend', onTransitionEnd);
|
|
624
625
|
domNode.removeEventListener('transitioncancel', onTransitionEnd);
|
|
625
626
|
};
|
|
626
627
|
domNode.addEventListener('transitionend', onTransitionEnd);
|
|
627
628
|
domNode.addEventListener('transitioncancel', onTransitionEnd);
|
|
628
629
|
if (transitionsActive.slide) {
|
|
630
|
+
const axis = this.transitionAxis(domNode, 'show');
|
|
631
|
+
let slideDirection = axis === 'X' ? 'left' : 'up';
|
|
632
|
+
const invert = domNode.hasAttribute('data-transition-slide-invert');
|
|
633
|
+
if (invert) {
|
|
634
|
+
slideDirection = slideDirection === 'left' ? 'right' : (slideDirection === 'up' ? 'down' : 'up');
|
|
635
|
+
}
|
|
636
|
+
const slideLengthMultiplier = slideDirection === 'down' || slideDirection === 'right' ? -1 : 1;
|
|
637
|
+
const slideLength = (axis === 'X' ? domNode.clientWidth : domNode.clientHeight) * 0.5 * slideLengthMultiplier * -1;
|
|
638
|
+
domNode.style.transform = `translate${axis === 'X' ? 'X' : 'Y'}(${slideLength}px)`;
|
|
639
|
+
setTimeout(() => {
|
|
640
|
+
domNode.style.transition = `transform ${transitionsActive.slide}ms linear`;
|
|
641
|
+
domNode.style.transform = `translate${axis === 'X' ? 'X' : 'Y'}(0)`;
|
|
642
|
+
}, 50);
|
|
643
|
+
}
|
|
644
|
+
if (transitionsActive.grow) {
|
|
629
645
|
const transformOrigin = domNode.getAttribute('data-transform-origin-show') || '50% 0';
|
|
630
646
|
domNode.style.transformOrigin = transformOrigin;
|
|
631
647
|
const axis = this.transitionAxis(domNode, 'show');
|
|
632
648
|
domNode.style.transform = `scale${axis}(0.01)`;
|
|
633
|
-
domNode.style.transition = `transform ${transitionsActive.
|
|
649
|
+
domNode.style.transition = `transform ${transitionsActive.grow}ms`;
|
|
634
650
|
setTimeout(() => {
|
|
635
651
|
domNode.style.transform = `scale${axis}(1)`;
|
|
636
652
|
}, 100);
|
|
637
653
|
}
|
|
638
654
|
if (transitionsActive.fade) {
|
|
639
655
|
domNode.style.opacity = '0';
|
|
640
|
-
domNode.style.transition = `opacity ${transitionsActive.fade
|
|
656
|
+
domNode.style.transition = `opacity ${transitionsActive.fade}ms`;
|
|
641
657
|
setTimeout(() => {
|
|
642
658
|
domNode.style.opacity = '1';
|
|
643
659
|
}, 100);
|
|
@@ -674,20 +690,34 @@ export class ClientComponent extends EventEmitter {
|
|
|
674
690
|
domNode.addEventListener('transitionend', onTransitionEnd);
|
|
675
691
|
domNode.addEventListener('transitioncancel', onTransitionEnd);
|
|
676
692
|
if (transitionsActive.slide) {
|
|
693
|
+
domNode.style.transition = `transform ${transitionsActive.slide}ms linear`;
|
|
694
|
+
const axis = this.transitionAxis(domNode, 'hide');
|
|
695
|
+
let slideDirection = axis === 'X' ? 'left' : 'up';
|
|
696
|
+
const invert = domNode.hasAttribute('data-transition-slide-invert');
|
|
697
|
+
if (invert) {
|
|
698
|
+
slideDirection = slideDirection === 'left' ? 'right' : (slideDirection === 'up' ? 'down' : 'up');
|
|
699
|
+
}
|
|
700
|
+
setTimeout(() => {
|
|
701
|
+
const slideLengthMultiplier = slideDirection === 'down' || slideDirection === 'right' ? -1 : 1;
|
|
702
|
+
const slideLength = (axis === 'X' ? domNode.clientWidth : domNode.clientHeight) * 0.5 * slideLengthMultiplier * -1;
|
|
703
|
+
domNode.style.transform = `translate${axis === 'X' ? 'X' : 'Y'}(${slideLength}px)`;
|
|
704
|
+
}, 50);
|
|
705
|
+
}
|
|
706
|
+
if (transitionsActive.grow) {
|
|
677
707
|
const transformOrigin = domNode.getAttribute('data-transform-origin-hide') || '50% 100%';
|
|
678
708
|
domNode.style.transformOrigin = transformOrigin;
|
|
679
|
-
domNode.style.transition = `transform ${transitionsActive.
|
|
709
|
+
domNode.style.transition = `transform ${transitionsActive.grow}ms ease`;
|
|
680
710
|
setTimeout(() => {
|
|
681
711
|
const axis = this.transitionAxis(domNode, 'hide');
|
|
682
712
|
domNode.style.transform = `scale${axis}(0.01)`;
|
|
683
|
-
},
|
|
713
|
+
}, 50);
|
|
684
714
|
}
|
|
685
715
|
if (transitionsActive.fade) {
|
|
686
716
|
domNode.style.opacity = '1';
|
|
687
|
-
domNode.style.transition = `opacity ${transitionsActive.fade
|
|
717
|
+
domNode.style.transition = `opacity ${transitionsActive.fade}ms`;
|
|
688
718
|
setTimeout(() => {
|
|
689
719
|
domNode.style.opacity = '0';
|
|
690
|
-
},
|
|
720
|
+
}, 50);
|
|
691
721
|
}
|
|
692
722
|
}
|
|
693
723
|
}
|
|
@@ -695,11 +725,13 @@ export class ClientComponent extends EventEmitter {
|
|
|
695
725
|
const transitions = {
|
|
696
726
|
show: {
|
|
697
727
|
slide: false,
|
|
698
|
-
fade: false
|
|
728
|
+
fade: false,
|
|
729
|
+
grow: false,
|
|
699
730
|
},
|
|
700
731
|
hide: {
|
|
701
732
|
slide: false,
|
|
702
|
-
fade: false
|
|
733
|
+
fade: false,
|
|
734
|
+
grow: false,
|
|
703
735
|
}
|
|
704
736
|
};
|
|
705
737
|
objectEach(transitions, (transitionEvent, transition) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IncomingHttpHeaders } from 'node:http';
|
|
2
|
-
import { LooseObject
|
|
2
|
+
import { LooseObject } from '../types/general.types.js';
|
|
3
|
+
import { RequestMethod } from "../types/request.types.js";
|
|
3
4
|
export declare class Net {
|
|
4
5
|
request(method: RequestMethod, url: string, headers?: IncomingHttpHeaders, body?: any, responseType?: XMLHttpRequestResponseType): Promise<string>;
|
|
5
6
|
get(url: string, headers?: IncomingHttpHeaders): Promise<string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Server } from 'node:http';
|
|
2
|
-
import { ApplicationEvents
|
|
2
|
+
import { ApplicationEvents } from '../types/application.types.js';
|
|
3
|
+
import { StructuredConfig } from '../types/structured.types.js';
|
|
4
|
+
import { LooseObject } from '../types/general.types.js';
|
|
5
|
+
import { RequestContext } from "../types/request.types.js";
|
|
3
6
|
import { Document } from './Document.js';
|
|
4
7
|
import { Components } from './Components.js';
|
|
5
8
|
import { Session } from './Session.js';
|
|
@@ -154,7 +154,7 @@ export class Application {
|
|
|
154
154
|
const document = new Document(this, '', ctx);
|
|
155
155
|
const data = attributes;
|
|
156
156
|
await document.loadComponent(component.name, data);
|
|
157
|
-
const exportedData = component.exportData ? document.data : (component.exportFields ? component.exportFields.reduce((prev, curr) => {
|
|
157
|
+
const exportedData = component.exportData ? document.children[0].data : (component.exportFields ? component.exportFields.reduce((prev, curr) => {
|
|
158
158
|
prev[curr] = document.children[0].data[curr];
|
|
159
159
|
return prev;
|
|
160
160
|
}, {}) : {});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Document } from './Document.js';
|
|
2
|
-
import {
|
|
2
|
+
import { LooseObject } from '../types/general.types.js';
|
|
3
|
+
import { ComponentEntry } from "../types/component.types.js";
|
|
3
4
|
import { DOMNode } from './dom/DOMNode.js';
|
|
4
5
|
import { EventEmitter } from '../EventEmitter.js';
|
|
5
6
|
export declare class Component<Events extends Record<string, any> = {
|
|
@@ -81,9 +81,9 @@ export class Component extends EventEmitter {
|
|
|
81
81
|
});
|
|
82
82
|
this.data = exportedContextData;
|
|
83
83
|
if (this.entry !== null &&
|
|
84
|
-
typeof this.entry.
|
|
85
|
-
typeof this.entry.
|
|
86
|
-
this.entry.
|
|
84
|
+
typeof this.entry.serverPart !== 'undefined' &&
|
|
85
|
+
typeof this.entry.serverPart.deferred === 'function' &&
|
|
86
|
+
this.entry.serverPart.deferred(this.attributes, this.document.ctx, this.document.application) &&
|
|
87
87
|
this.attributes.deferred !== false) {
|
|
88
88
|
this.setAttributes({ deferred: true }, 'data-', true);
|
|
89
89
|
return;
|
|
@@ -91,14 +91,14 @@ export class Component extends EventEmitter {
|
|
|
91
91
|
const importedParentData = this.parent ? this.importedParentData(this.parent.data) : {};
|
|
92
92
|
let dataServerSidePart = {};
|
|
93
93
|
try {
|
|
94
|
-
dataServerSidePart = (this.entry && this.entry.
|
|
95
|
-
await this.entry.
|
|
94
|
+
dataServerSidePart = (this.entry && this.entry.serverPart ?
|
|
95
|
+
await this.entry.serverPart.getData(Object.assign(importedParentData, this.attributes, data || {}), this.document.ctx, this.document.application, this) : {}) || {};
|
|
96
96
|
}
|
|
97
97
|
catch (e) {
|
|
98
98
|
throw new Error(`Error executing getData in component ${this.name}: ${e.message}`);
|
|
99
99
|
}
|
|
100
100
|
if (data === undefined) {
|
|
101
|
-
if (this.entry && this.entry.
|
|
101
|
+
if (this.entry && this.entry.hasServerPart) {
|
|
102
102
|
this.data = Object.assign(this.data, dataServerSidePart);
|
|
103
103
|
}
|
|
104
104
|
else {
|
|
@@ -110,10 +110,10 @@ export class Component extends EventEmitter {
|
|
|
110
110
|
this.data = Object.assign(this.data, Object.assign(importedParentData, data), this.attributes, dataServerSidePart);
|
|
111
111
|
}
|
|
112
112
|
this.fillData(this.data);
|
|
113
|
-
if (this.entry
|
|
113
|
+
if (!this.entry?.hasServerPart) {
|
|
114
114
|
this.setAttributes(this.data, 'data-');
|
|
115
115
|
}
|
|
116
|
-
else
|
|
116
|
+
else {
|
|
117
117
|
if (this.entry.exportFields) {
|
|
118
118
|
this.setAttributes(this.entry.exportFields.reduce((prev, field) => {
|
|
119
119
|
if (this.data[field] !== undefined) {
|
|
@@ -122,6 +122,9 @@ export class Component extends EventEmitter {
|
|
|
122
122
|
return prev;
|
|
123
123
|
}, {}), 'data-');
|
|
124
124
|
}
|
|
125
|
+
else if (this.entry.exportData) {
|
|
126
|
+
this.setAttributes(this.data, 'data-');
|
|
127
|
+
}
|
|
125
128
|
if (this.entry.attributes) {
|
|
126
129
|
this.setAttributes(this.entry.attributes, '', false);
|
|
127
130
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StructuredConfig } from '../types/structured.types.js';
|
|
2
|
+
import { ComponentEntry } from "../types/component.types.js";
|
|
2
3
|
import { Application } from './Application.js';
|
|
3
4
|
export declare class Components {
|
|
4
5
|
config: StructuredConfig;
|
|
@@ -45,7 +45,7 @@ export class Components {
|
|
|
45
45
|
jsClient: hasClientJS ? jsClientPath : undefined,
|
|
46
46
|
jsServer: hasServerJS ? jsServerPath : undefined
|
|
47
47
|
},
|
|
48
|
-
|
|
48
|
+
hasServerPart: existsSync(jsServerPath),
|
|
49
49
|
html: this.loadHTML(absolutePath),
|
|
50
50
|
exportData: false,
|
|
51
51
|
static: false
|
|
@@ -56,12 +56,12 @@ export class Components {
|
|
|
56
56
|
}
|
|
57
57
|
if (hasServerJS) {
|
|
58
58
|
const componentConstructor = await import('file:///' + entry.path.jsServer);
|
|
59
|
-
entry.
|
|
60
|
-
entry.renderTagName = entry.
|
|
61
|
-
entry.exportData = typeof entry.
|
|
62
|
-
entry.exportFields = entry.
|
|
63
|
-
entry.attributes = entry.
|
|
64
|
-
entry.static = typeof entry.
|
|
59
|
+
entry.serverPart = new componentConstructor.default();
|
|
60
|
+
entry.renderTagName = entry.serverPart?.tagName || 'div';
|
|
61
|
+
entry.exportData = typeof entry.serverPart?.exportData === 'boolean' ? entry.serverPart.exportData : false;
|
|
62
|
+
entry.exportFields = entry.serverPart?.exportFields;
|
|
63
|
+
entry.attributes = entry.serverPart?.attributes;
|
|
64
|
+
entry.static = typeof entry.serverPart?.static === 'boolean' ? entry.serverPart.static : false;
|
|
65
65
|
}
|
|
66
66
|
this.components[componentName.toUpperCase()] = entry;
|
|
67
67
|
this.componentNames.push(entry.name);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
import { LooseObject } from
|
|
2
|
+
import { LooseObject } from '../types/general.types.js';
|
|
3
3
|
export declare class Cookies {
|
|
4
4
|
parse(request: IncomingMessage): LooseObject;
|
|
5
5
|
set(response: ServerResponse, name: string, value: string | number, lifetimeSeconds: number, path?: string, sameSite?: 'Strict' | 'Lax' | 'None', domain?: string): void;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ServerResponse } from 'node:http';
|
|
2
|
-
import {
|
|
2
|
+
import { LooseObject } from '../types/general.types.js';
|
|
3
|
+
import { Initializers } from '../types/component.types.js';
|
|
4
|
+
import { RequestContext } from "../types/request.types.js";
|
|
3
5
|
import { Application } from './Application.js';
|
|
4
6
|
import { DocumentHead } from './DocumentHead.js';
|
|
5
7
|
import { Component } from './Component.js';
|
|
@@ -21,6 +23,6 @@ export declare class Document extends Component<{
|
|
|
21
23
|
private initClientConfig;
|
|
22
24
|
toString(): string;
|
|
23
25
|
allocateId(): string;
|
|
24
|
-
loadView(pathRelative: string, data?: LooseObject): Promise<
|
|
25
|
-
loadComponent(componentName: string, data?: LooseObject): Promise<
|
|
26
|
+
loadView(pathRelative: string, data?: LooseObject): Promise<Document>;
|
|
27
|
+
loadComponent(componentName: string, data?: LooseObject): Promise<Document>;
|
|
26
28
|
}
|
|
@@ -69,14 +69,13 @@ export class Document extends Component {
|
|
|
69
69
|
async loadView(pathRelative, data) {
|
|
70
70
|
const viewPath = path.resolve('../' + this.application.config.components.path + '/' + pathRelative + (pathRelative.endsWith('.html') ? '' : '.html'));
|
|
71
71
|
if (!existsSync(viewPath)) {
|
|
72
|
-
|
|
73
|
-
return false;
|
|
72
|
+
throw new Error(`Couldn't load document ${this.document.head.title}: ${viewPath}`);
|
|
74
73
|
}
|
|
75
74
|
const html = readFileSync(viewPath, {
|
|
76
75
|
encoding: 'utf-8'
|
|
77
76
|
}).toString();
|
|
78
77
|
await this.init(stripBOM(html).replace(/\r/g, ''), data);
|
|
79
|
-
return
|
|
78
|
+
return this;
|
|
80
79
|
}
|
|
81
80
|
async loadComponent(componentName, data) {
|
|
82
81
|
const componentEntry = this.document.application.components.getByName(componentName);
|
|
@@ -87,5 +86,6 @@ export class Document extends Component {
|
|
|
87
86
|
}, []).join(' ');
|
|
88
87
|
await this.init(`<${componentName} ${dataString}></${componentName}>`, data);
|
|
89
88
|
}
|
|
89
|
+
return this;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LooseObject } from '../types/general.types.js';
|
|
2
|
+
import { ValidationResult, ValidatorErrorDecorator, ValidatorFunction, FormValidationEntry, ValidationRuleWithArguments } from '../types/validation.types.js';
|
|
2
3
|
export declare class FormValidation {
|
|
3
4
|
fieldRules: Array<FormValidationEntry>;
|
|
4
5
|
singleError: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HelperDelegate } from "handlebars";
|
|
2
2
|
import { default as HandlebarsInstance } from 'handlebars';
|
|
3
|
-
import { LooseObject } from
|
|
3
|
+
import { LooseObject } from '../types/general.types.js';
|
|
4
4
|
export declare class Handlebars {
|
|
5
5
|
readonly instance: typeof HandlebarsInstance;
|
|
6
6
|
readonly helpers: Record<string, HelperDelegate>;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { LooseObject
|
|
1
|
+
import { LooseObject } from '../types/general.types.js';
|
|
2
|
+
import { RequestContext } from "../types/request.types.js";
|
|
2
3
|
import { Application } from "./Application.js";
|
|
3
4
|
import { Document } from "./Document.js";
|
|
4
5
|
export declare class Layout {
|
|
5
6
|
layoutComponent: string;
|
|
6
7
|
app: Application;
|
|
7
|
-
|
|
8
|
+
language: string;
|
|
9
|
+
constructor(app: Application, layoutComponent: string, language?: string);
|
|
8
10
|
private layoutComponentExists;
|
|
9
11
|
document(ctx: RequestContext, title: string, componentName: string, data?: LooseObject): Promise<Document>;
|
|
10
12
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Component } from "./Component.js";
|
|
2
2
|
import { Document } from "./Document.js";
|
|
3
3
|
export class Layout {
|
|
4
|
-
constructor(app, layoutComponent) {
|
|
4
|
+
constructor(app, layoutComponent, language = 'en') {
|
|
5
5
|
this.app = app;
|
|
6
6
|
this.layoutComponent = layoutComponent;
|
|
7
|
+
this.language = language;
|
|
7
8
|
if (this.app.initialized) {
|
|
8
9
|
this.layoutComponentExists();
|
|
9
10
|
}
|
|
@@ -18,6 +19,7 @@ export class Layout {
|
|
|
18
19
|
}
|
|
19
20
|
async document(ctx, title, componentName, data) {
|
|
20
21
|
const doc = new Document(this.app, title, ctx);
|
|
22
|
+
doc.language = this.language;
|
|
21
23
|
await doc.loadComponent(this.layoutComponent, data);
|
|
22
24
|
const layoutComponent = doc.dom.queryByTagName('template');
|
|
23
25
|
if (layoutComponent.length === 0) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
import { LooseObject
|
|
2
|
+
import { LooseObject } from '../types/general.types.js';
|
|
3
|
+
import { RequestMethod, PostedDataDecoded, RequestBodyFile, RequestCallback } from "../types/request.types.js";
|
|
3
4
|
import { Application } from "./Application.js";
|
|
4
5
|
export declare class Request {
|
|
5
6
|
private app;
|
|
@@ -105,6 +105,7 @@ export class Request {
|
|
|
105
105
|
body: undefined,
|
|
106
106
|
getArgs,
|
|
107
107
|
cookies: this.app.cookies.parse(request),
|
|
108
|
+
timeStart: new Date().getTime(),
|
|
108
109
|
isAjax: request.headers['x-requested-with'] == 'xmlhttprequest',
|
|
109
110
|
respondWith: function (data) {
|
|
110
111
|
if (typeof data === 'string' || Buffer.isBuffer(data)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { LooseObject
|
|
1
|
+
import { LooseObject } from '../types/general.types.js';
|
|
2
|
+
import { SessionEntry } from '../types/session.types.js';
|
|
2
3
|
import { Application } from './Application.js';
|
|
3
4
|
export declare class Session {
|
|
4
5
|
application: Application;
|
|
@@ -21,6 +21,7 @@ export declare class DOMNode {
|
|
|
21
21
|
attributeMap: Record<string, DOMNodeAttribute>;
|
|
22
22
|
style: Partial<CSSStyleDeclaration>;
|
|
23
23
|
selfClosing: boolean;
|
|
24
|
+
explicitSelfClosing: boolean;
|
|
24
25
|
potentialComponentChildren: Array<DOMNode>;
|
|
25
26
|
constructor(root: DOMFragment | null, parentNode: DOMNode | null, tagName: string);
|
|
26
27
|
appendChild(node: DOMNode | string): void;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { HTMLParser } from "./HTMLParser.js";
|
|
2
|
-
export const selfClosingTags = ['br', 'wbr', 'hr', 'input', 'img', 'link', 'meta', 'source', 'embed',
|
|
3
|
-
|
|
2
|
+
export const selfClosingTags = ['br', 'wbr', 'hr', 'input', 'img', 'link', 'meta', 'source', 'embed',
|
|
3
|
+
'path', 'area', 'rect', 'ellipse', 'circle', 'line', 'polygon', 'image'
|
|
4
|
+
];
|
|
5
|
+
export const recognizedHTMLTags = [
|
|
6
|
+
'body', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'b', 'i', 'a', 'em', 'strong', 'br', 'wbr', 'hr', 'abbr', 'bdi', 'bdo', 'blockquote', 'cite', 'code', 'del', 'dfn', 'ins', 'kbd', 'mark', 'pre', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'small', 'span', 'sub', 'sup', 'time', 'u', 'var', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'img', 'area', 'map', 'object', 'param', 'table', 'tr', 'td', 'th', 'caption', 'colgroup', 'col', 'form', 'input', 'label', 'select', 'option', 'textarea', 'button', 'fieldset', 'datalist', 'iframe', 'audio', 'video', 'source', 'track', 'script', 'noscript', 'div', 'nav', 'aside', 'canvas', 'embed', 'template',
|
|
7
|
+
'svg', 'g', 'text', 'path', 'circle', 'clipPath', 'defs', 'ellipse', 'rect', 'polygon', 'image', 'style',
|
|
8
|
+
];
|
|
4
9
|
export class DOMNode {
|
|
5
10
|
constructor(root, parentNode, tagName) {
|
|
6
11
|
this.parentNode = null;
|
|
@@ -8,6 +13,7 @@ export class DOMNode {
|
|
|
8
13
|
this.attributes = [];
|
|
9
14
|
this.attributeMap = {};
|
|
10
15
|
this.style = {};
|
|
16
|
+
this.explicitSelfClosing = false;
|
|
11
17
|
this.potentialComponentChildren = [];
|
|
12
18
|
this.root = root === null ? this : root;
|
|
13
19
|
this.isRoot = root === null;
|
|
@@ -116,7 +122,7 @@ export class DOMNode {
|
|
|
116
122
|
style += ` ${styleName}: ${styleValue};`;
|
|
117
123
|
return style;
|
|
118
124
|
}, '');
|
|
119
|
-
return `<${this.tagName}${attributes}${style.trim().length > 0 ? ` style="${style}"` : ''}>${this.selfClosing ? '' : `${this.innerHTML}</${this.tagName}>`}`;
|
|
125
|
+
return `<${this.tagName}${attributes}${style.trim().length > 0 ? ` style="${style}"` : ''}${this.explicitSelfClosing ? '/' : ''}>${this.selfClosing || this.explicitSelfClosing ? '' : `${this.innerHTML}</${this.tagName}>`}`;
|
|
120
126
|
}
|
|
121
127
|
toObject() {
|
|
122
128
|
return {
|
|
@@ -6,6 +6,7 @@ export class HTMLParser {
|
|
|
6
6
|
this.state = 'idle';
|
|
7
7
|
this.tokenCurrent = '';
|
|
8
8
|
this.fragment = new DOMFragment();
|
|
9
|
+
this.explicitSelfClosing = false;
|
|
9
10
|
this.attributeOpenQuote = '"';
|
|
10
11
|
this.attributeNameCurrent = '';
|
|
11
12
|
this.attributeContext = null;
|
|
@@ -46,6 +47,7 @@ export class HTMLParser {
|
|
|
46
47
|
}
|
|
47
48
|
if (this.isLetter(charCode)) {
|
|
48
49
|
this.state = 'tagOpen';
|
|
50
|
+
this.explicitSelfClosing = false;
|
|
49
51
|
this.tokenCurrent = char;
|
|
50
52
|
return true;
|
|
51
53
|
}
|
|
@@ -58,6 +60,7 @@ export class HTMLParser {
|
|
|
58
60
|
if (this.tokenCurrent.length === 0) {
|
|
59
61
|
throw this.error(`Unexpected tag closing sequence "</", expected opening tag`);
|
|
60
62
|
}
|
|
63
|
+
this.explicitSelfClosing = true;
|
|
61
64
|
return true;
|
|
62
65
|
}
|
|
63
66
|
if (char === '>') {
|
|
@@ -66,9 +69,10 @@ export class HTMLParser {
|
|
|
66
69
|
}
|
|
67
70
|
const node = new DOMNode(this.fragment, this.context, this.tokenCurrent);
|
|
68
71
|
this.context.appendChild(node);
|
|
69
|
-
this.state = '
|
|
72
|
+
this.state = 'text';
|
|
70
73
|
this.tokenCurrent = '';
|
|
71
|
-
|
|
74
|
+
node.explicitSelfClosing = this.explicitSelfClosing;
|
|
75
|
+
if (!node.selfClosing && !node.explicitSelfClosing) {
|
|
72
76
|
this.context = node;
|
|
73
77
|
}
|
|
74
78
|
this.attributeContext = node;
|
|
@@ -82,7 +86,8 @@ export class HTMLParser {
|
|
|
82
86
|
const node = new DOMNode(this.fragment, this.context, this.tokenCurrent);
|
|
83
87
|
this.context.appendChild(node);
|
|
84
88
|
this.tokenCurrent = '';
|
|
85
|
-
|
|
89
|
+
node.explicitSelfClosing = this.explicitSelfClosing;
|
|
90
|
+
if (!node.selfClosing && !node.explicitSelfClosing) {
|
|
86
91
|
this.context = node;
|
|
87
92
|
}
|
|
88
93
|
this.attributeContext = node;
|
|
@@ -99,7 +104,7 @@ export class HTMLParser {
|
|
|
99
104
|
return true;
|
|
100
105
|
}
|
|
101
106
|
if (char === '>') {
|
|
102
|
-
if (this.tokenCurrent !== this.context.tagName) {
|
|
107
|
+
if (this.tokenCurrent !== this.context.tagName && !this.attributeContext?.explicitSelfClosing) {
|
|
103
108
|
throw this.error(`Found closing tag ${this.tokenCurrent}, expected ${this.context.tagName}`);
|
|
104
109
|
}
|
|
105
110
|
this.context = this.context.parentNode || this.fragment;
|
|
@@ -123,12 +128,25 @@ export class HTMLParser {
|
|
|
123
128
|
}
|
|
124
129
|
else if (this.state === 'attributeName') {
|
|
125
130
|
const boundsChar = char === ' ' || char === '\n' || char === '\t';
|
|
126
|
-
if (boundsChar || char === '=' || char === '>') {
|
|
131
|
+
if (boundsChar || char === '=' || char === '>' || char === '/') {
|
|
127
132
|
if (char === '=') {
|
|
128
133
|
this.state = 'attributeValueStart';
|
|
129
134
|
}
|
|
130
135
|
else if (char === '>') {
|
|
131
|
-
this.state = '
|
|
136
|
+
this.state = 'text';
|
|
137
|
+
if (this.tokenCurrent.trim().length === 0) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (char === '/') {
|
|
142
|
+
if (this.attributeContext && this.attributeContext.parentNode) {
|
|
143
|
+
this.context = this.attributeContext.parentNode;
|
|
144
|
+
this.attributeContext.explicitSelfClosing = true;
|
|
145
|
+
this.state = 'tagClose';
|
|
146
|
+
if (this.tokenCurrent.trim().length === 0) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
132
150
|
}
|
|
133
151
|
if (this.tokenCurrent.length > 0) {
|
|
134
152
|
if (this.attributeContext !== null && this.tokenCurrent.trim().length > 0) {
|
|
@@ -164,14 +182,19 @@ export class HTMLParser {
|
|
|
164
182
|
}
|
|
165
183
|
else if (this.state === 'attributeEnd') {
|
|
166
184
|
if (char === '>') {
|
|
167
|
-
this.state = '
|
|
185
|
+
this.state = 'text';
|
|
168
186
|
return true;
|
|
169
187
|
}
|
|
170
188
|
if (char === ' ' || char === '\n') {
|
|
171
189
|
this.state = 'attributeName';
|
|
172
190
|
return true;
|
|
173
191
|
}
|
|
174
|
-
|
|
192
|
+
if (char === '/') {
|
|
193
|
+
if (this.attributeContext && this.attributeContext.parentNode) {
|
|
194
|
+
this.context = this.attributeContext.parentNode;
|
|
195
|
+
this.attributeContext.explicitSelfClosing = true;
|
|
196
|
+
this.tokenCurrent = '';
|
|
197
|
+
}
|
|
175
198
|
return true;
|
|
176
199
|
}
|
|
177
200
|
else {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ApplicationEvents = 'serverStarted' | 'beforeRequestHandler' | 'afterRequestHandler' | 'beforeRoutes' | 'afterRoutes' | 'beforeComponentsLoad' | 'afterComponentsLoaded' | 'documentCreated' | 'beforeAssetAccess' | 'afterAssetAccess' | 'pageNotFound';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ClientComponent } from "../client/ClientComponent.js";
|
|
2
|
+
import { Net } from "../client/Net.js";
|
|
3
|
+
import { Application } from "../server/Application.js";
|
|
4
|
+
import { Component } from "../server/Component.js";
|
|
5
|
+
import { LooseObject } from './general.types.js';
|
|
6
|
+
import { RequestContext, RequestBodyArguments } from "./request.types.js";
|
|
7
|
+
export type ComponentEntry = {
|
|
8
|
+
name: string;
|
|
9
|
+
path: {
|
|
10
|
+
absolute: string;
|
|
11
|
+
relative: string;
|
|
12
|
+
relativeToViews: string;
|
|
13
|
+
build: string;
|
|
14
|
+
html: string;
|
|
15
|
+
jsServer?: string;
|
|
16
|
+
jsClient?: string;
|
|
17
|
+
};
|
|
18
|
+
hasServerPart: boolean;
|
|
19
|
+
serverPart?: ComponentScaffold;
|
|
20
|
+
html: string;
|
|
21
|
+
static: boolean;
|
|
22
|
+
renderTagName?: string;
|
|
23
|
+
exportData: boolean;
|
|
24
|
+
exportFields?: Array<string>;
|
|
25
|
+
attributes?: Record<string, string>;
|
|
26
|
+
initializer?: InitializerFunction;
|
|
27
|
+
};
|
|
28
|
+
export interface ComponentScaffold {
|
|
29
|
+
tagName?: string;
|
|
30
|
+
exportData?: boolean;
|
|
31
|
+
exportFields?: Array<string>;
|
|
32
|
+
static?: boolean;
|
|
33
|
+
deferred?: (data: Record<string, any>, ctx: RequestContext | undefined, app: Application) => boolean;
|
|
34
|
+
attributes?: Record<string, string>;
|
|
35
|
+
getData: (this: ComponentScaffold, data: RequestBodyArguments | LooseObject, ctx: undefined | RequestContext, app: Application, component: Component) => Promise<LooseObject | null>;
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}
|
|
38
|
+
export type ClientComponentTransition = {
|
|
39
|
+
fade: false | number;
|
|
40
|
+
slide: false | number;
|
|
41
|
+
grow: false | number;
|
|
42
|
+
};
|
|
43
|
+
export type ClientComponentTransitionEvent = 'show' | 'hide';
|
|
44
|
+
export type ClientComponentTransitions = Record<ClientComponentTransitionEvent, ClientComponentTransition>;
|
|
45
|
+
export type ClientComponentBoundEvent<T extends LooseObject | undefined = undefined> = {
|
|
46
|
+
element: HTMLElement | Window;
|
|
47
|
+
event: keyof HTMLElementEventMap;
|
|
48
|
+
callback: (e: Event) => void;
|
|
49
|
+
callbackOriginal: ClientComponentEventCallback<T>;
|
|
50
|
+
};
|
|
51
|
+
export type ClientComponentEventCallback<T> = (e: Event, data: T, element: HTMLElement | Window) => void;
|
|
52
|
+
export type InitializerFunction = (this: ClientComponent, ctx: InitializerFunctionContext) => Promise<void>;
|
|
53
|
+
export type Initializers = {
|
|
54
|
+
[key: string]: InitializerFunction;
|
|
55
|
+
};
|
|
56
|
+
export type InitializerFunctionContext = {
|
|
57
|
+
net: Net;
|
|
58
|
+
isRedraw: boolean;
|
|
59
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type EventEmitterCallback<T> = (payload: T, eventName: string) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type LooseObject = Record<string, any>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from "http";
|
|
2
|
+
import { LooseObject } from "../Types.js";
|
|
3
|
+
import { symbolArrays } from "../Symbols.js";
|
|
4
|
+
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
5
|
+
export type RequestCallback<R extends any, Body extends LooseObject | undefined> = (ctx: RequestContext<Body>) => Promise<R>;
|
|
6
|
+
export type RequestHandler = {
|
|
7
|
+
match: Array<URISegmentPattern> | RegExp;
|
|
8
|
+
methods: Array<RequestMethod>;
|
|
9
|
+
callback: RequestCallback<any, LooseObject | undefined>;
|
|
10
|
+
scope: any;
|
|
11
|
+
staticAsset: boolean;
|
|
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
|
+
respondWith: (data: any) => void;
|
|
27
|
+
redirect: (to: string, statusCode?: number) => void;
|
|
28
|
+
show404: () => Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
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>>>;
|
|
31
|
+
export type RequestBodyRecordValue = string | Array<RequestBodyRecordValue> | {
|
|
32
|
+
[key: string]: RequestBodyRecordValue;
|
|
33
|
+
} | {
|
|
34
|
+
[key: string]: RequestBodyFile;
|
|
35
|
+
} | Array<RequestBodyFile> | RequestBodyFile;
|
|
36
|
+
export interface RequestBodyArguments {
|
|
37
|
+
[key: string]: RequestBodyRecordValue;
|
|
38
|
+
[symbolArrays]?: {
|
|
39
|
+
[key: string]: Array<string>;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export type RequestBodyFile = {
|
|
43
|
+
fileName: string;
|
|
44
|
+
data: Buffer;
|
|
45
|
+
type: string;
|
|
46
|
+
};
|
|
47
|
+
export type RequestBodyFiles = {
|
|
48
|
+
[key: string]: RequestBodyFile;
|
|
49
|
+
};
|
|
50
|
+
export type URISegmentPattern = {
|
|
51
|
+
pattern: string | RegExp;
|
|
52
|
+
name?: string;
|
|
53
|
+
type?: 'string' | 'number';
|
|
54
|
+
};
|
|
55
|
+
export type URIArguments = {
|
|
56
|
+
[key: string]: string | number | RegExpExecArray;
|
|
57
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { symbolArrays } from "../Symbols.js";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from "http";
|
|
2
|
+
import { LooseObject } from './general.types.js';
|
|
3
|
+
import { symbolArrays } from "../Symbols.js";
|
|
4
|
+
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
5
|
+
export type RequestCallback<R extends any, Body extends LooseObject | undefined> = (ctx: RequestContext<Body>) => Promise<R>;
|
|
6
|
+
export type RequestHandler = {
|
|
7
|
+
match: Array<URISegmentPattern> | RegExp;
|
|
8
|
+
methods: Array<RequestMethod>;
|
|
9
|
+
callback: RequestCallback<any, LooseObject | undefined>;
|
|
10
|
+
scope: any;
|
|
11
|
+
staticAsset: boolean;
|
|
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) => void;
|
|
28
|
+
redirect: (to: string, statusCode?: number) => void;
|
|
29
|
+
show404: () => Promise<void>;
|
|
30
|
+
};
|
|
31
|
+
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
|
+
export type RequestBodyRecordValue = string | Array<RequestBodyRecordValue> | {
|
|
33
|
+
[key: string]: RequestBodyRecordValue;
|
|
34
|
+
} | {
|
|
35
|
+
[key: string]: RequestBodyFile;
|
|
36
|
+
} | Array<RequestBodyFile> | RequestBodyFile;
|
|
37
|
+
export interface RequestBodyArguments {
|
|
38
|
+
[key: string]: RequestBodyRecordValue;
|
|
39
|
+
[symbolArrays]?: {
|
|
40
|
+
[key: string]: Array<string>;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export type RequestBodyFile = {
|
|
44
|
+
fileName: string;
|
|
45
|
+
data: Buffer;
|
|
46
|
+
type: string;
|
|
47
|
+
};
|
|
48
|
+
export type RequestBodyFiles = {
|
|
49
|
+
[key: string]: RequestBodyFile;
|
|
50
|
+
};
|
|
51
|
+
export type URISegmentPattern = {
|
|
52
|
+
pattern: string | RegExp;
|
|
53
|
+
name?: string;
|
|
54
|
+
type?: 'string' | 'number';
|
|
55
|
+
};
|
|
56
|
+
export type URIArguments = {
|
|
57
|
+
[key: string]: string | number | RegExpExecArray;
|
|
58
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { symbolArrays } from "../Symbols.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type StoreChangeCallback = (key: string, value: any, oldValue: any, componentId: string) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type StructuredConfig = {
|
|
2
|
+
readonly envPrefix?: string;
|
|
3
|
+
readonly autoInit: boolean;
|
|
4
|
+
url: {
|
|
5
|
+
removeTrailingSlash: boolean;
|
|
6
|
+
componentRender: false | string;
|
|
7
|
+
isAsset: (url: string) => boolean;
|
|
8
|
+
};
|
|
9
|
+
routes: {
|
|
10
|
+
readonly path: string;
|
|
11
|
+
};
|
|
12
|
+
components: {
|
|
13
|
+
readonly path: string;
|
|
14
|
+
readonly componentNameAttribute: string;
|
|
15
|
+
};
|
|
16
|
+
session: {
|
|
17
|
+
readonly cookieName: string;
|
|
18
|
+
readonly keyLength: number;
|
|
19
|
+
readonly durationSeconds: number;
|
|
20
|
+
readonly garbageCollectIntervalSeconds: number;
|
|
21
|
+
};
|
|
22
|
+
http: {
|
|
23
|
+
host?: string;
|
|
24
|
+
port: number;
|
|
25
|
+
linkHeaderRel: 'preload' | 'preconnect';
|
|
26
|
+
};
|
|
27
|
+
readonly runtime: 'Node.js' | 'Deno';
|
|
28
|
+
};
|
|
29
|
+
export type StructuredClientConfig = {
|
|
30
|
+
componentRender: StructuredConfig['url']['componentRender'];
|
|
31
|
+
componentNameAttribute: string;
|
|
32
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PostedDataDecoded } from './request.types.js';
|
|
2
|
+
export type ValidationRuleWithArguments = [string, any];
|
|
3
|
+
export type FormValidationEntry = {
|
|
4
|
+
field: [string, string];
|
|
5
|
+
rules: Array<string | ValidationRuleWithArguments | ValidatorFunction>;
|
|
6
|
+
};
|
|
7
|
+
export type ValidatorFunction = (data: PostedDataDecoded, field: string, arg: number, rules: Array<string | ValidationRuleWithArguments | ValidatorFunction>) => Promise<boolean>;
|
|
8
|
+
export type ValidatorErrorDecorator = (fieldHumanReadable: string, data: PostedDataDecoded, field: string, arg: any) => string | Promise<string>;
|
|
9
|
+
export type ValidationErrors = {
|
|
10
|
+
[field: string]: Array<string>;
|
|
11
|
+
};
|
|
12
|
+
export type ValidationErrorsSingle = {
|
|
13
|
+
[field: string]: string;
|
|
14
|
+
};
|
|
15
|
+
export type ValidationResult = {
|
|
16
|
+
valid: boolean;
|
|
17
|
+
errors: ValidationErrors | ValidationErrorsSingle;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"main": "build/index",
|
|
22
|
-
"version": "1.0.
|
|
22
|
+
"version": "1.0.9",
|
|
23
23
|
"scripts": {
|
|
24
24
|
"develop": "tsc --watch",
|
|
25
|
-
"startDev": "cd build && nodemon --watch '../app/**/*' --watch '../build/**/*' -e js,html,css index.js",
|
|
25
|
+
"startDev": "cd build && nodemon --watch '../app/**/*' --watch '../build/**/*' -e js,html,hbs,css index.js",
|
|
26
26
|
"start": "cd build && node index.js",
|
|
27
27
|
"compileAndPack": "tsc && npm pack",
|
|
28
28
|
"compileAndPublish": "tsc && npm publish"
|