Skip to content

Commit 31406b6

Browse files
committed
emitter and router rework. added request and response from ingest so other projects can use
1 parent 5f611ea commit 31406b6

25 files changed

+3099
-616
lines changed

package.json

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,20 @@
6767
"import": "./esm/data/ReadonlySet.js"
6868
},
6969
"./EventEmitter": {
70-
"require": "./cjs/event/EventEmitter.js",
71-
"import": "./esm/event/EventEmitter.js"
70+
"require": "./cjs/emitter/EventEmitter.js",
71+
"import": "./esm/emitter/EventEmitter.js"
7272
},
73-
"./EventRouter": {
74-
"require": "./cjs/event/EventRouter.js",
75-
"import": "./esm/event/EventRouter.js"
73+
"./ExpressEmitter": {
74+
"require": "./cjs/emitter/ExpressEmitter.js",
75+
"import": "./esm/emitter/ExpressEmitter.js"
7676
},
77-
"./EventTerminal": {
78-
"require": "./cjs/event/EventTerminal.js",
79-
"import": "./esm/event/EventTerminal.js"
77+
"./HttpEmitter": {
78+
"require": "./cjs/emitter/HttpEmitter.js",
79+
"import": "./esm/emitter/HttpEmitter.js"
80+
},
81+
"./RouteEmitter": {
82+
"require": "./cjs/emitter/RouteEmitter.js",
83+
"import": "./esm/emitter/RouteEmitter.js"
8084
},
8185
"./ItemQueue": {
8286
"require": "./cjs/queue/ItemQueue.js",
@@ -86,6 +90,30 @@
8690
"require": "./cjs/queue/TaskQueue.js",
8791
"import": "./esm/queue/TaskQueue.js"
8892
},
93+
"./HttpRouter": {
94+
"require": "./cjs/router/HttpRouter.js",
95+
"import": "./esm/router/HttpRouter.js"
96+
},
97+
"./Request": {
98+
"require": "./cjs/router/Request.js",
99+
"import": "./esm/router/Request.js"
100+
},
101+
"./Response": {
102+
"require": "./cjs/router/Response.js",
103+
"import": "./esm/router/Response.js"
104+
},
105+
"./Router": {
106+
"require": "./cjs/router/Router.js",
107+
"import": "./esm/router/Router.js"
108+
},
109+
"./Session": {
110+
"require": "./cjs/router/Session.js",
111+
"import": "./esm/router/Session.js"
112+
},
113+
"./Terminal": {
114+
"require": "./cjs/router/Terminal.js",
115+
"import": "./esm/router/Terminal.js"
116+
},
89117
"./FileLoader": {
90118
"require": "./cjs/system/FileLoader.js",
91119
"import": "./esm/system/FileLoader.js"
@@ -110,11 +138,18 @@
110138
"ReadonlyMap": [ "./cjs/data/ReadonlyMap.d.ts" ],
111139
"ReadonlyNest": [ "./cjs/data/ReadonlyNest.d.ts" ],
112140
"ReadonlySet": [ "./cjs/data/ReadonlySet.d.ts" ],
113-
"EventEmitter": [ "./cjs/event/EventEmitter.d.ts" ],
114-
"EventRouter": [ "./cjs/event/EventRouter.d.ts" ],
115-
"EventTerminal": [ "./cjs/event/EventTerminal.d.ts" ],
141+
"EventEmitter": [ "./cjs/emitter/EventEmitter.d.ts" ],
142+
"ExpressEmitter": [ "./cjs/emitter/ExpressEmitter.d.ts" ],
143+
"HttpEmitter": [ "./cjs/emitter/HttpEmitter.d.ts" ],
144+
"RouteEmitter": [ "./cjs/emitter/RouteEmitter.d.ts" ],
116145
"ItemQueue": [ "./cjs/queue/ItemQueue.d.ts" ],
117146
"TaskQueue": [ "./cjs/queue/TaskQueue.d.ts" ],
147+
"HttpRouter": [ "./cjs/router/HttpRouter.d.ts" ],
148+
"Request": [ "./cjs/router/Request.d.ts" ],
149+
"Response": [ "./cjs/router/Response.d.ts" ],
150+
"Router": [ "./cjs/router/Router.d.ts" ],
151+
"Session": [ "./cjs/router/Session.d.ts" ],
152+
"Terminal": [ "./cjs/router/Terminal.d.ts" ],
118153
"FileLoader": [ "./cjs/system/FileLoader.d.ts" ],
119154
"NodeFS": [ "./cjs/system/NodeFS.d.ts" ]
120155
}
@@ -138,7 +173,8 @@
138173
"report": "nyc report -r lcov"
139174
},
140175
"dependencies": {
141-
"@inquirer/prompts": "7.1.0"
176+
"@inquirer/prompts": "7.1.0",
177+
"cookie": "1.0.2"
142178
},
143179
"devDependencies": {
144180
"@types/chai": "4.3.20",

src/data/Nest.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,26 @@ export default class Nest<M extends UnknownNest = UnknownNest>
160160
}
161161
}
162162

163+
/**
164+
* Returns the parsed form data from the request body (if any)
165+
*/
166+
export function formDataToObject(type: string, body: string) {
167+
return type.endsWith('/json')
168+
? objectFromJson(body)
169+
: type.endsWith('/x-www-form-urlencoded')
170+
? objectFromQuery(body)
171+
: type.startsWith('multipart/form-data')
172+
? objectFromFormData(body)
173+
: {} as Record<string, unknown>;
174+
};
175+
176+
/**
177+
* Returns true if the value is a native JS object
178+
*/
179+
export function isObject(value: unknown) {
180+
return typeof value === 'object' && value?.constructor?.name === 'Object';
181+
};
182+
163183
/**
164184
* Transforms an object into an array
165185
*/
@@ -183,6 +203,60 @@ export function makeObject(array: any[]): NestedObject<unknown> {
183203
return Object.assign({}, array as unknown);
184204
}
185205

206+
/**
207+
* Returns the parsed data from the given cli arguments
208+
*/
209+
export function objectFromArgs(args: string) {
210+
if (args) {
211+
const nest = new Nest();
212+
nest.withArgs.set(args);
213+
return nest.get();
214+
}
215+
return {};
216+
}
217+
218+
/**
219+
* Transform JSON string into object
220+
* This is usually from body application/json
221+
* or text/json
222+
*/
223+
export function objectFromJson(json: string) {
224+
if (json.startsWith('{')) {
225+
return JSON.parse(json) as Record<string, unknown>;
226+
}
227+
return {} as Record<string, unknown>;
228+
};
229+
230+
/**
231+
* Transform query string into object
232+
* This is usually from URL.search or
233+
* body application/x-www-form-urlencoded
234+
*/
235+
export function objectFromQuery(query: string) {
236+
if (query.startsWith('?')) {
237+
query = query.substring(1);
238+
}
239+
if (query) {
240+
const nest = new Nest();
241+
nest.withQuery.set(query);
242+
return nest.get();
243+
}
244+
return {};
245+
};
246+
247+
/**
248+
* Transform form data into object
249+
* This is usually from body multipart/form-data
250+
*/
251+
export function objectFromFormData(data: string) {
252+
if (data) {
253+
const nest = new Nest();
254+
nest.withFormData.set(data);
255+
return nest.get();
256+
}
257+
return {};
258+
};
259+
186260
/**
187261
* Returns true if object keys is all numbers
188262
*/
Lines changed: 36 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
TaskItem,
55
Event,
66
EventMap,
7+
EventData,
78
EventName,
89
EventHook,
910
EventMatch
@@ -19,15 +20,14 @@ import TaskQueue from '../queue/TaskQueue';
1920
* right after the event has triggered.
2021
*/
2122
export default class EventEmitter<M extends EventMap> {
22-
//Event regular expression map
23-
public readonly regexp = new Set<string>();
2423
//called after each task
2524
protected _after?: EventHook<M[keyof M]>;
2625
//called before each task
2726
protected _before?: EventHook<M[keyof M]>;
2827
//Static event data analyzer
2928
protected _event?: Event<M[keyof M]>;
3029
//A route map to task queues
30+
//ie. { event -> [ ...{ item, priority } ] }
3131
protected _listeners: { [ K in keyof M ]?: Set<TaskItem<M[K]>> } = {};
3232

3333
/**
@@ -95,68 +95,24 @@ export default class EventEmitter<M extends EventMap> {
9595
*/
9696
public match(event: string) {
9797
const matches = new Map<string, EventMatch>();
98-
//first do the obvious match
98+
//exact match
9999
if (typeof this.listeners[event] !== 'undefined') {
100-
const data: EventMatch['data'] = { args: [], params: {} };
100+
//this is a placeholder for class extensions...
101+
const data: EventData = { args: [], params: {} };
101102
matches.set(event, { event, pattern: event, data });
102103
}
103-
//next do the calculated matches
104-
this.regexp.forEach(pattern => {
105-
//make regexp so we can compare against the trigger
106-
const regexp = new RegExp(
107-
// pattern,
108-
pattern.substring(
109-
pattern.indexOf('/') + 1,
110-
pattern.lastIndexOf('/')
111-
),
112-
// flag
113-
pattern.substring(
114-
pattern.lastIndexOf('/') + 1
115-
)
116-
);
117-
//because String.matchAll only works for global flags ...
118-
const data: EventMatch['data'] = { args: [], params: {} };
119-
if (regexp.flags.indexOf('g') === -1) {
120-
const match = event.match(regexp);
121-
if (!match || !match.length) {
122-
return;
123-
}
124-
if (Array.isArray(match)) {
125-
data.args = match.slice();
126-
data.args.shift();
127-
}
128-
} else {
129-
const match = Array.from(event.matchAll(regexp));
130-
if (!Array.isArray(match[0]) || !match[0].length) {
131-
return;
132-
}
133-
134-
data.args = match[0].slice();
135-
data.args.shift();
136-
}
137-
matches.set(pattern, { event, pattern, data });
138-
});
139-
104+
//return all the matches
140105
return matches;
141106
}
142107

143108
/**
144109
* Adds a callback to the given event listener
145110
*/
146111
public on<N extends EventName<M>>(
147-
event: N|RegExp,
112+
event: N,
148113
action: Task<M[N]>,
149114
priority = 0
150115
) {
151-
//if it is a regexp object
152-
if (event instanceof RegExp) {
153-
//make it into a string
154-
event = event.toString() as N;
155-
//go ahead and add the pattern
156-
//set guarantees uniqueness
157-
this.regexp.add(event);
158-
}
159-
160116
//add the event to the listeners
161117
if (typeof this._listeners[event] === 'undefined') {
162118
this._listeners[event] = new Set<TaskItem<M[N]>>();
@@ -182,27 +138,7 @@ export default class EventEmitter<M extends EventMap> {
182138
//then loop the observers
183139
const tasks = this._listeners[event] as Set<TaskItem<M[keyof M]>>;
184140
tasks.forEach(task => {
185-
queue.add(async (...args) => {
186-
//set the current
187-
this._event = { ...match, ...task, args, action: task.item };
188-
//before hook
189-
if (typeof this._before === 'function'
190-
&& await this._before(this._event) === false
191-
) {
192-
return false;
193-
}
194-
//if this is the same event, call the
195-
//method, if the method returns false
196-
if (await task.item(...args) === false) {
197-
return false;
198-
}
199-
//after hook
200-
if (typeof this._after === 'function'
201-
&& await this._after(this._event) === false
202-
) {
203-
return false;
204-
}
205-
}, task.priority);
141+
queue.add(this._task(match, task), task.priority);
206142
});
207143
}
208144

@@ -228,8 +164,6 @@ export default class EventEmitter<M extends EventMap> {
228164
* Allows events from other emitters to apply here
229165
*/
230166
public use(emitter: EventEmitter<M>) {
231-
//first concat their regexp with this one
232-
emitter.regexp.forEach(pattern => this.regexp.add(pattern));
233167
//next this listen to what they were listening to
234168
//event listeners = event -> Set
235169
//loop through the listeners of the emitter
@@ -249,4 +183,32 @@ export default class EventEmitter<M extends EventMap> {
249183
}
250184
return this;
251185
}
186+
187+
/**
188+
* Returns a task for the given event and task
189+
* Allows for class extensions to overload this method
190+
*/
191+
protected _task(match: EventMatch, task: TaskItem<M[keyof M]>) {
192+
return async (...args: M[keyof M]) => {
193+
//set the current
194+
this._event = { ...match, ...task, args, action: task.item };
195+
//before hook
196+
if (typeof this._before === 'function'
197+
&& await this._before(this._event) === false
198+
) {
199+
return false;
200+
}
201+
//if this is the same event, call the
202+
//method, if the method returns false
203+
if (await task.item(...args) === false) {
204+
return false;
205+
}
206+
//after hook
207+
if (typeof this._after === 'function'
208+
&& await this._after(this._event) === false
209+
) {
210+
return false;
211+
}
212+
};
213+
}
252214
}

0 commit comments

Comments
 (0)