Skip to content

Commit

Permalink
#129 Realized there is a need to update server pa…
Browse files Browse the repository at this point in the history
…ckage again, so that it supports AppEndpoints with 'never' context
type.
  • Loading branch information
stazz committed Feb 13, 2024
1 parent 1775488 commit f95f9c5
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 9 deletions.
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ty-ras/server",
"version": "2.2.1",
"version": "2.3.0",
"author": {
"name": "Stanislav Muhametsin",
"email": "[email protected]",
Expand Down
26 changes: 26 additions & 0 deletions server/src/__test__/flow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import test, { ExecutionContext } from "ava";
import * as evtUtil from "./events";
import * as flowUtil from "./flow";
import * as dataBE from "@ty-ras/data-backend";
import type * as ep from "@ty-ras/endpoint";
import * as stream from "node:stream";
import * as spec from "../flow";

test("Validate typicalServerFlow works", async (t) => {
t.plan(1);
Expand Down Expand Up @@ -1456,6 +1458,30 @@ test("Validate that handling OPTIONS works as intended by typicalServeFlow when
]);
});

test("Verify that typing for createTypicalServerFlow works", (c) => {
const endpoints: ReadonlyArray<ep.AppEndpoint<never, undefined>> = [];
const { callbacks } = flowUtil.createTrackingCallback();
spec.createTypicalServerFlow<spec.TContextBase, undefined, undefined>(
endpoints,
callbacks,
undefined,
);
const endpoints2: ReadonlyArray<
ep.AppEndpoint<spec.TContextBase, undefined>
> = [];
spec.createTypicalServerFlow<spec.TContextBase, undefined, undefined>(
endpoints2,
callbacks,
undefined,
);
spec.createTypicalServerFlow<spec.TContextBase, undefined, undefined>(
[...endpoints, ...endpoints2],
callbacks,
undefined,
);
c.pass();
});

const getHumanReadableMessage = () => "";

const errorMessage = "This should never be called";
Expand Down
21 changes: 15 additions & 6 deletions server/src/endpoint-squash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,36 @@ import type * as ep from "@ty-ras/endpoint";
* @returns One single {@link ep.FinalizedAppEndpoint} which will have its regexp as combination of regexps of all given {@link ep.AppEndpoint}s, and will delegate the implementation to the endpoint which matches the correct regexp.
*/
export default <TContext, TStateInfo>(
endpoints: ReadonlyArray<ep.AppEndpoint<TContext, TStateInfo>>,
endpoints: ReadonlyArray<
ep.AppEndpoint<never, TStateInfo> | ep.AppEndpoint<TContext, TStateInfo>
>,
): ep.FinalizedAppEndpoint<TContext, TStateInfo> => {
const { builtEndpoints, regExp } = buildEndpoints(endpoints);
const { builtEndpoints, regExp } = buildEndpoints<TContext, TStateInfo>(
endpoints,
);
return {
url: regExp,
handler: createPrefixedHandlerImpl(builtEndpoints),
handler: createPrefixedHandlerImpl<TContext, TStateInfo>(builtEndpoints),
};
};

const buildEndpoints = <TContext, TStateInfo>(
endpoints: ReadonlyArray<ep.AppEndpoint<TContext, TStateInfo>>,
endpoints: ReadonlyArray<
ep.AppEndpoint<never, TStateInfo> | ep.AppEndpoint<TContext, TStateInfo>
>,
): PrefixedAppEndpointsInfo<TContext, TStateInfo> => {
// TODO maybe throw if multiple endpoints have same regex?
// Since currently, that is not supported.
// The builder in endpoint-spec project already makes checks like that, so doing this here is not top prio.
// In 99% of the cases, the endpoints we receive here, will be endpoint created and validated by endpoint-spec project.
const builtEndpointInfo = endpoints.map(({ getRegExpAndHandler }, idx) => {
const regExpGroupName = `e_${idx}`;
const builtEndpoint = getRegExpAndHandler(
`${regExpGroupName}_`,
) as ep.FinalizedAppEndpoint<TContext | never, TStateInfo>;
return {
regExpGroupName,
builtEndpoint: getRegExpAndHandler(`${regExpGroupName}_`),
builtEndpoint,
};
});

Expand Down Expand Up @@ -99,7 +108,7 @@ const createPrefixedHandlerImpl =
interface PrefixedAppEndpointsInfo<TContext, TStateInfo> {
builtEndpoints: {
regExpGroupName: string;
handler: ep.AppEndpointHandlerGetter<TContext, TStateInfo>;
handler: ep.AppEndpointHandlerGetter<TContext | never, TStateInfo>;
}[];
regExp: RegExp;
}
16 changes: 14 additions & 2 deletions server/src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ export const createTypicalServerFlow = <
>(
// Notice - endpoints don't have GetContext<TContext> as their context!
// The extra fields of GetContext<TContext> are meant to be used only by event handler!
endpoints: ReadonlyArray<ep.AppEndpoint<TContext, TStateInfo>>,
endpoints: ServerEndpoints<TContext, TStateInfo>,
callbacks: ServerFlowCallbacks<TContext, TStateInfo>,
events: evt.ServerEventHandler<GetContext<TContext>, TState> | undefined,
): ((context: TContext) => Promise<void>) => {
const { handler, url: regExp } = squashEndpoints(endpoints);
const { handler, url: regExp } = squashEndpoints<TContext, TStateInfo>(
endpoints,
);
const cb: ServerFlowCallbacks<GetContext<TContext>, TStateInfo> = {
...callbacks,
setStatusCode: (...params) => {
Expand Down Expand Up @@ -293,6 +295,16 @@ export const createTypicalServerFlow = <
};
};

/**
* This is helper type to encapsulate array of {@link ep.AppEndpoint}s with correct type arguments for given context and state information.
*/
export type ServerEndpoints<
TContext extends TContextBase,
TStateInfo,
> = ReadonlyArray<
ep.AppEndpoint<never, TStateInfo> | ep.AppEndpoint<TContext, TStateInfo>
>;

/**
* The base type constraint for any server context in {@link createTypicalServerFlow}.
*/
Expand Down

0 comments on commit f95f9c5

Please sign in to comment.