diff --git a/packages/core/src/common/app.ts b/packages/core/src/common/app.ts index 723706c..265038e 100644 --- a/packages/core/src/common/app.ts +++ b/packages/core/src/common/app.ts @@ -1,5 +1,6 @@ import {Endpoint, EndpointOperations} from './endpoint'; import {Operation} from './operation'; +import {NamedSet, PredicateMap} from './common'; export interface BaseAppState { endpoints: unknown; @@ -20,8 +21,8 @@ export type AppOperations = ( export interface App { name: AppName; - operations: Set; - endpoints: Set; + endpoints: NamedSet; + operations: NamedSet; operation(newOperation: NewOperation): App< AppName, { @@ -48,39 +49,28 @@ interface AppParams { class AppInstance implements App { readonly name: Params['name']; - readonly endpoints: Set; - readonly operations: Set; + readonly endpoints: NamedSet; + readonly operations: NamedSet; constructor(params: Params) { this.name = params.name; - this.endpoints = new Set(); - this.operations = new Set(); + this.endpoints = new Map(); + this.operations = new PredicateMap((newOperation, s) => ( + s.method === newOperation.method + )); } operation(newOperation: NewOperation) { - const existingOperations = Array.from(this.operations); - - if (!existingOperations.some((s) => ( - s.name === newOperation.name - && s.method === newOperation.method - ))) { - this.operations.add(newOperation); - } - + this.operations.set(newOperation.name, newOperation); return this; } endpoint(newEndpoint: NewEndpoint) { - const existingEndpoints = Array.from(this.endpoints); - - if (existingEndpoints.some((s) => ( - s.name === newEndpoint.name - ))) { + if (this.endpoints.has(newEndpoint.name)) { throw new Error(`Cannot add duplicate endpoint with name: ${newEndpoint.name}`); } - this.endpoints.add(newEndpoint); - + this.endpoints.set(newEndpoint.name, newEndpoint); return this; } } diff --git a/packages/core/src/common/common.ts b/packages/core/src/common/common.ts new file mode 100644 index 0000000..9893257 --- /dev/null +++ b/packages/core/src/common/common.ts @@ -0,0 +1,26 @@ +type ObjectWithName = { + name: Name; +}; + +export type NamedSet = Map; + +export class PredicateMap extends Map { + static get [Symbol.species]() { + return Map; + } + + constructor(private readonly predicate: (newItem: V, existingItem: V) => boolean, arg0?: ConstructorParameters>[0]) { + super(arg0); + } + + set(key: K, value: V) { + for (const a of this.values()) { + if (this.predicate(value, a)) { + return this; + } + } + + super.set(key, value); + return this; + } +} diff --git a/packages/core/src/common/endpoint.ts b/packages/core/src/common/endpoint.ts index 2047fd4..485e6c4 100644 --- a/packages/core/src/common/endpoint.ts +++ b/packages/core/src/common/endpoint.ts @@ -1,5 +1,6 @@ import {DataSource} from '../backend/data-source'; import {validation as v} from '.'; +import {NamedSet} from './common'; export type EndpointQueue = [Endpoint, Record | undefined][]; @@ -20,16 +21,15 @@ export const serializeEndpointQueue = (endpointQueue: EndpointQueue) => { .join('') }; -export const parseToEndpointQueue = (urlWithoutBase: string, endpoints: Set) => { +export const parseToEndpointQueue = (urlWithoutBase: string, endpoints: NamedSet) => { const [urlWithoutQueryParams] = urlWithoutBase.split('?'); const fragments = urlWithoutQueryParams.split('/').filter((s) => s.trim().length > 0); - const endpointsArray = Array.from(endpoints); return fragments.reduce( (theEndpointQueueRaw, s) => { const theEndpointQueue = theEndpointQueueRaw as EndpointQueue; const [lastEndpoint, lastEndpointParams] = theEndpointQueue.at(-1) ?? []; - const endpoint = endpointsArray.find((e) => e.name === s); + const endpoint = endpoints.get(s); if (typeof endpoint !== 'undefined') { if (typeof lastEndpoint === 'undefined') { return [ diff --git a/packages/core/src/extenders/http/backend/core.ts b/packages/core/src/extenders/http/backend/core.ts index 555ccf0..7fc733a 100644 --- a/packages/core/src/extenders/http/backend/core.ts +++ b/packages/core/src/extenders/http/backend/core.ts @@ -39,7 +39,7 @@ class ServerInstance implements Server { const endpoints = parseToEndpointQueue(req.url, this.backend.app.endpoints); const [endpoint, endpointParams] = endpoints.at(-1) ?? []; - const appOperations = Array.from(this.backend.app.operations) + const appOperations = Array.from(this.backend.app.operations.values()) const foundAppOperation = appOperations .find((op) => op.method === req.method?.toUpperCase()); @@ -95,7 +95,6 @@ class ServerInstance implements Server { } const bodyToSerialize = responseSpec.body; - console.log(bodyToSerialize); res.statusMessage = responseSpec.statusMessage; // TODO add default status message per status code res.writeHead(responseSpec.statusCode, {}); diff --git a/packages/core/test/http/default.test.ts b/packages/core/test/http/default.test.ts index d599241..74514c3 100644 --- a/packages/core/test/http/default.test.ts +++ b/packages/core/test/http/default.test.ts @@ -61,7 +61,7 @@ describe('default', () => { app: theRawApp, }); - theRawEndpoint = Array.from(theApp.endpoints).find((e) => e.name === 'users'); + theRawEndpoint = theApp.endpoints.get('users'); theOperation = operations.fetch; theServer = server({