Browse Source

Update internals

Use Map instead of Set for easy referencing of endpoints and operations.
refactor/new-arch
TheoryOfNekomata 5 months ago
parent
commit
37034d23ae
5 changed files with 43 additions and 28 deletions
  1. +12
    -22
      packages/core/src/common/app.ts
  2. +26
    -0
      packages/core/src/common/common.ts
  3. +3
    -3
      packages/core/src/common/endpoint.ts
  4. +1
    -2
      packages/core/src/extenders/http/backend/core.ts
  5. +1
    -1
      packages/core/test/http/default.test.ts

+ 12
- 22
packages/core/src/common/app.ts View File

@@ -1,5 +1,6 @@
import {Endpoint, EndpointOperations} from './endpoint'; import {Endpoint, EndpointOperations} from './endpoint';
import {Operation} from './operation'; import {Operation} from './operation';
import {NamedSet, PredicateMap} from './common';


export interface BaseAppState { export interface BaseAppState {
endpoints: unknown; endpoints: unknown;
@@ -20,8 +21,8 @@ export type AppOperations<T extends App> = (


export interface App<AppName extends string = string, AppState extends BaseAppState = BaseAppState> { export interface App<AppName extends string = string, AppState extends BaseAppState = BaseAppState> {
name: AppName; name: AppName;
operations: Set<Operation>;
endpoints: Set<Endpoint>;
endpoints: NamedSet<Endpoint>;
operations: NamedSet<Operation>;
operation<NewOperation extends Operation>(newOperation: NewOperation): App< operation<NewOperation extends Operation>(newOperation: NewOperation): App<
AppName, AppName,
{ {
@@ -48,39 +49,28 @@ interface AppParams<Name extends string = string> {


class AppInstance<Params extends AppParams, State extends BaseAppState> implements App<Params['name'], State> { class AppInstance<Params extends AppParams, State extends BaseAppState> implements App<Params['name'], State> {
readonly name: Params['name']; readonly name: Params['name'];
readonly endpoints: Set<Endpoint>;
readonly operations: Set<Operation>;
readonly endpoints: NamedSet<Endpoint>;
readonly operations: NamedSet<Operation>;


constructor(params: Params) { constructor(params: Params) {
this.name = params.name; this.name = params.name;
this.endpoints = new Set<Endpoint>();
this.operations = new Set<Operation>();
this.endpoints = new Map<Endpoint['name'], Endpoint>();
this.operations = new PredicateMap<Operation['name'], Operation>((newOperation, s) => (
s.method === newOperation.method
));
} }


operation<NewOperation extends Operation>(newOperation: NewOperation) { operation<NewOperation extends 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; return this;
} }


endpoint<NewEndpoint extends Endpoint = Endpoint>(newEndpoint: NewEndpoint) { endpoint<NewEndpoint extends Endpoint = 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}`); throw new Error(`Cannot add duplicate endpoint with name: ${newEndpoint.name}`);
} }


this.endpoints.add(newEndpoint);

this.endpoints.set(newEndpoint.name, newEndpoint);
return this; return this;
} }
} }


+ 26
- 0
packages/core/src/common/common.ts View File

@@ -0,0 +1,26 @@
type ObjectWithName<Name extends string = string> = {
name: Name;
};

export type NamedSet<T extends ObjectWithName> = Map<T['name'], T>;

export class PredicateMap<K, V> extends Map<K, V> {
static get [Symbol.species]() {
return Map;
}

constructor(private readonly predicate: (newItem: V, existingItem: V) => boolean, arg0?: ConstructorParameters<typeof Map<K, V>>[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;
}
}

+ 3
- 3
packages/core/src/common/endpoint.ts View File

@@ -1,5 +1,6 @@
import {DataSource} from '../backend/data-source'; import {DataSource} from '../backend/data-source';
import {validation as v} from '.'; import {validation as v} from '.';
import {NamedSet} from './common';


export type EndpointQueue = [Endpoint, Record<string, unknown> | undefined][]; export type EndpointQueue = [Endpoint, Record<string, unknown> | undefined][];


@@ -20,16 +21,15 @@ export const serializeEndpointQueue = (endpointQueue: EndpointQueue) => {
.join('') .join('')
}; };


export const parseToEndpointQueue = (urlWithoutBase: string, endpoints: Set<Endpoint>) => {
export const parseToEndpointQueue = (urlWithoutBase: string, endpoints: NamedSet<Endpoint>) => {
const [urlWithoutQueryParams] = urlWithoutBase.split('?'); const [urlWithoutQueryParams] = urlWithoutBase.split('?');
const fragments = urlWithoutQueryParams.split('/').filter((s) => s.trim().length > 0); const fragments = urlWithoutQueryParams.split('/').filter((s) => s.trim().length > 0);
const endpointsArray = Array.from(endpoints);


return fragments.reduce( return fragments.reduce(
(theEndpointQueueRaw, s) => { (theEndpointQueueRaw, s) => {
const theEndpointQueue = theEndpointQueueRaw as EndpointQueue; const theEndpointQueue = theEndpointQueueRaw as EndpointQueue;
const [lastEndpoint, lastEndpointParams] = theEndpointQueue.at(-1) ?? []; 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 endpoint !== 'undefined') {
if (typeof lastEndpoint === 'undefined') { if (typeof lastEndpoint === 'undefined') {
return [ return [


+ 1
- 2
packages/core/src/extenders/http/backend/core.ts View File

@@ -39,7 +39,7 @@ class ServerInstance<Backend extends BaseBackend> implements Server<Backend> {
const endpoints = parseToEndpointQueue(req.url, this.backend.app.endpoints); const endpoints = parseToEndpointQueue(req.url, this.backend.app.endpoints);
const [endpoint, endpointParams] = endpoints.at(-1) ?? []; 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 const foundAppOperation = appOperations
.find((op) => op.method === req.method?.toUpperCase()); .find((op) => op.method === req.method?.toUpperCase());


@@ -95,7 +95,6 @@ class ServerInstance<Backend extends BaseBackend> implements Server<Backend> {
} }


const bodyToSerialize = responseSpec.body; const bodyToSerialize = responseSpec.body;
console.log(bodyToSerialize);


res.statusMessage = responseSpec.statusMessage; // TODO add default status message per status code res.statusMessage = responseSpec.statusMessage; // TODO add default status message per status code
res.writeHead(responseSpec.statusCode, {}); res.writeHead(responseSpec.statusCode, {});


+ 1
- 1
packages/core/test/http/default.test.ts View File

@@ -61,7 +61,7 @@ describe('default', () => {
app: theRawApp, app: theRawApp,
}); });


theRawEndpoint = Array.from(theApp.endpoints).find((e) => e.name === 'users');
theRawEndpoint = theApp.endpoints.get('users');
theOperation = operations.fetch; theOperation = operations.fetch;


theServer = server({ theServer = server({


Loading…
Cancel
Save