From 2dbba044dbbaca19836a90b10f229082bb7074c0 Mon Sep 17 00:00:00 2001 From: mimiMonads Date: Sat, 16 Mar 2024 19:53:54 +0000 Subject: [PATCH] CTX typed --- .../http/src/framework/optimizer/aComposer.ts | 4 +- .../framework/optimizer/nativeComponets.ts | 2 +- .../http/src/framework/optimizer/types.ts | 553 +++++++++--------- main.ts | 1 - 4 files changed, 279 insertions(+), 281 deletions(-) diff --git a/components/http/src/framework/optimizer/aComposer.ts b/components/http/src/framework/optimizer/aComposer.ts index cef74e5..3d6e9b3 100644 --- a/components/http/src/framework/optimizer/aComposer.ts +++ b/components/http/src/framework/optimizer/aComposer.ts @@ -18,7 +18,7 @@ export default (o?: specialOptions) => ( (table) => ( - (functions) => + (functions) => functions.reduce( (s, k) => s(k), new Function( @@ -36,7 +36,7 @@ export default (o?: specialOptions) => )(), ) )( - ((or) => nativeComponets(or)(f)(table))( + ((or) => nativeComponets(or)(f)(table))( "mutable" in f ? { ...o, mutable: true } as FunRouterOptions : o, ), ) diff --git a/components/http/src/framework/optimizer/nativeComponets.ts b/components/http/src/framework/optimizer/nativeComponets.ts index bc107a1..3db09b5 100644 --- a/components/http/src/framework/optimizer/nativeComponets.ts +++ b/components/http/src/framework/optimizer/nativeComponets.ts @@ -75,7 +75,7 @@ export default (o?: FunRouterOptions) => }, { condition: (x: NativeMaps) => x.name === "headers", - action: () => stringToFunction(parse()(o?.cors ?? {})), + action: () => stringToFunction(parse()(o?.cors ?? {})), }, { condition: (x: NativeMaps) => x.name === "resolve", diff --git a/components/http/src/framework/optimizer/types.ts b/components/http/src/framework/optimizer/types.ts index 3bbc66e..75dba18 100644 --- a/components/http/src/framework/optimizer/types.ts +++ b/components/http/src/framework/optimizer/types.ts @@ -127,163 +127,163 @@ export interface Ctx< CR extends CryptoOptions, UNI extends specialElements, > { -/** - * The `resolve` property is integral to ensuring that all necessary data is fetched or calculations are performed before the main function (`f`) of a morphism is executed. It consists of a map where each key corresponds to a resolve function that is executed prior to `f`. The results of these resolves are then made available in the `CTX` for use in the main function. - * - * **Key Features**: - * - Ensures data dependencies are resolved beforehand. - * - Supports both synchronous and asynchronous operations. - * - Maintains the original state, allowing for clean and predictable code execution. - * - Executes all resolves before integrating their outputs into the `CTX`. - * - Supports an unlimited nesting of resolves and branches (using morphism), providing a flexible structure for complex data handling. - * - * **Examples**: - * --- - * Basic usage with synchronous data fetching: - * ```js - * wrap(options)() - * .stdPetition({ - * path: "/withResolve", - * resolve: { - * hi: { f: () => "Hello world" }, - * }, - * f: (ctx) => ctx.resolve.hi, - * }); - * ``` - * --- - * Incorporating asynchronous functions: - * ```js - * wrap()() - * .stdPetition({ - * path: "/withResolveAsync", - * resolve: { - * hi: { async f: () => await Promise.resolve("Hello world") } - * }, - * f: (ctx) => ctx.resolve.hi, - * }) - * ``` - * --- - * Execution order and integration into `CTX`: - * ```js - * wrap(options)() - * .stdPetition({ - * path: "/helloWorld", - * resolve: { - * hello: { async f: () => await Promise.resolve("Hello") }, - * world: { f: () => 'world' } - * }, - * f: ctx => `${ctx.resolve.hello} ${ctx.resolve.world}`, - * }) - * ``` - * --- - * Using `morphism` with `resolve`: - * - * Suppose you need to fetch user data and perform some preprocessing on it before responding to a request. You can define a `morphism` for fetching and preprocessing the data, and then use it within the `resolve` to ensure the data is ready by the time you need to use it in `f`. - * - * ```js - * // Define a morphism for fetching and preprocessing user data - * const fetchAndProcessUserData = morphism({ - * resolve: { - * userData: { - * f: async (c) => { - * // Imagine fetching user data asynchronously - * const userData = await fetchUserData(c.param.userId); - * // Preprocess the fetched data - * const processedData = processData(userData); - * return processedData; - * } - * } - * }, - * f: (c) => c.resolve.userData, // This function simply returns the processed user data - * }); - * - * // Use the above morphism in a petition - * wrap()() - * .stdPetition({ - * path: "/user/:userId", - * resolve: { - * // Utilize the morphism to fetch and preprocess user data before executing the main function - * processedUserData: fetchAndProcessUserData, - * }, - * f: (c) => { - * // Access the resolved and processed user data directly in the main function - * const userData = c.resolve.processedUserData; - * // Use the processed user data to construct the response - * return new Response(JSON.stringify(userData)); - * } - * }); - * ``` - */ + /** + * The `resolve` property is integral to ensuring that all necessary data is fetched or calculations are performed before the main function (`f`) of a morphism is executed. It consists of a map where each key corresponds to a resolve function that is executed prior to `f`. The results of these resolves are then made available in the `CTX` for use in the main function. + * + * **Key Features**: + * - Ensures data dependencies are resolved beforehand. + * - Supports both synchronous and asynchronous operations. + * - Maintains the original state, allowing for clean and predictable code execution. + * - Executes all resolves before integrating their outputs into the `CTX`. + * - Supports an unlimited nesting of resolves and branches (using morphism), providing a flexible structure for complex data handling. + * + * **Examples**: + * --- + * Basic usage with synchronous data fetching: + * ```js + * wrap(options)() + * .stdPetition({ + * path: "/withResolve", + * resolve: { + * hi: { f: () => "Hello world" }, + * }, + * f: (ctx) => ctx.resolve.hi, + * }); + * ``` + * --- + * Incorporating asynchronous functions: + * ```js + * wrap()() + * .stdPetition({ + * path: "/withResolveAsync", + * resolve: { + * hi: { async f: () => await Promise.resolve("Hello world") } + * }, + * f: (ctx) => ctx.resolve.hi, + * }) + * ``` + * --- + * Execution order and integration into `CTX`: + * ```js + * wrap(options)() + * .stdPetition({ + * path: "/helloWorld", + * resolve: { + * hello: { async f: () => await Promise.resolve("Hello") }, + * world: { f: () => 'world' } + * }, + * f: ctx => `${ctx.resolve.hello} ${ctx.resolve.world}`, + * }) + * ``` + * --- + * Using `morphism` with `resolve`: + * + * Suppose you need to fetch user data and perform some preprocessing on it before responding to a request. You can define a `morphism` for fetching and preprocessing the data, and then use it within the `resolve` to ensure the data is ready by the time you need to use it in `f`. + * + * ```js + * // Define a morphism for fetching and preprocessing user data + * const fetchAndProcessUserData = morphism({ + * resolve: { + * userData: { + * f: async (c) => { + * // Imagine fetching user data asynchronously + * const userData = await fetchUserData(c.param.userId); + * // Preprocess the fetched data + * const processedData = processData(userData); + * return processedData; + * } + * } + * }, + * f: (c) => c.resolve.userData, // This function simply returns the processed user data + * }); + * + * // Use the above morphism in a petition + * wrap()() + * .stdPetition({ + * path: "/user/:userId", + * resolve: { + * // Utilize the morphism to fetch and preprocess user data before executing the main function + * processedUserData: fetchAndProcessUserData, + * }, + * f: (c) => { + * // Access the resolved and processed user data directly in the main function + * const userData = c.resolve.processedUserData; + * // Use the processed user data to construct the response + * return new Response(JSON.stringify(userData)); + * } + * }); + * ``` + */ resolve: { [V in keyof R]: Awaited> }; - /** - * The `branch` property allows for additional logic or operations to be executed alongside or within the main function (`f`) of a petition. Each key within the `branch` object maps to a branch function, executed with its context. The results of these branches are then accessible under the `branch` property of the `CTX`, complementing the main logic without overcrowding it. - * - * **Key Features**: - * - Enables the execution of side operations or additional logic in parallel to the main function. - * - Each branch operates with its own context, allowing for independent execution. - * - Supports dynamic operations with parameters and asynchronous actions, enhancing flexibility. - * - * **Examples**: - * --- - * Defining a simple branch: - * ```typescript - * const helloBranch = morphism(options)({ - * f: (ctx) => "Hello from branch", - * }); - * - * wrap(options)() - * .stdPetition({ - * path: "/helloBranch", - * branch: { - * hello: helloBranch, - * }, - * f: (ctx) => new Response(ctx.branch.hello(null)), - * }); - * ``` - * --- - * Branch with parameters: - * ```typescript - * const greetUserBranch = morphism()({ - * f: (ctx) => `Hello, ${ctc.arguments.name}`, - * }); - * - * wrap(options)() - * .stdPetition({ - * path: "/greet/:name", - * branch: { - * greetUser: greetUserBranch, - * }, - * f: (ctx) => new Response(c.branch.greetUser({ name: ctx.param.name })), - * }); - * ``` - * --- - * Asynchronous branch example: - * ```js - * const fetchUserDataBranch = morphism(options)({ - * async f: (ctx) => { - * const userId = ctc.arguments.userId; - * return await fetch(`https://api.example.com/users/${userId}`).then(res => res.json()); - * }, - * }); - * - * wrap(options)() - * .stdPetition({ - * path: "/user/:userId", - * branch: { - * fetchUserData: fetchUserDataBranch, - * }, - * f: async (ctx) => { - * const userData = await ctx.branch.fetchUserData({ userId: ctx.param.userId }); - * return new Response(JSON.stringify(userData)); - * }, - * }) - * ``` - */ -branch: { - [V in keyof B]: { - (ctx: any): ReturnType; + /** + * The `branch` property allows for additional logic or operations to be executed alongside or within the main function (`f`) of a petition. Each key within the `branch` object maps to a branch function, executed with its context. The results of these branches are then accessible under the `branch` property of the `CTX`, complementing the main logic without overcrowding it. + * + * **Key Features**: + * - Enables the execution of side operations or additional logic in parallel to the main function. + * - Each branch operates with its own context, allowing for independent execution. + * - Supports dynamic operations with parameters and asynchronous actions, enhancing flexibility. + * + * **Examples**: + * --- + * Defining a simple branch: + * ```typescript + * const helloBranch = morphism(options)({ + * f: (ctx) => "Hello from branch", + * }); + * + * wrap(options)() + * .stdPetition({ + * path: "/helloBranch", + * branch: { + * hello: helloBranch, + * }, + * f: (ctx) => new Response(ctx.branch.hello(null)), + * }); + * ``` + * --- + * Branch with parameters: + * ```typescript + * const greetUserBranch = morphism()({ + * f: (ctx) => `Hello, ${ctc.arguments.name}`, + * }); + * + * wrap(options)() + * .stdPetition({ + * path: "/greet/:name", + * branch: { + * greetUser: greetUserBranch, + * }, + * f: (ctx) => new Response(c.branch.greetUser({ name: ctx.param.name })), + * }); + * ``` + * --- + * Asynchronous branch example: + * ```js + * const fetchUserDataBranch = morphism(options)({ + * async f: (ctx) => { + * const userId = ctc.arguments.userId; + * return await fetch(`https://api.example.com/users/${userId}`).then(res => res.json()); + * }, + * }); + * + * wrap(options)() + * .stdPetition({ + * path: "/user/:userId", + * branch: { + * fetchUserData: fetchUserDataBranch, + * }, + * f: async (ctx) => { + * const userData = await ctx.branch.fetchUserData({ userId: ctx.param.userId }); + * return new Response(JSON.stringify(userData)); + * }, + * }) + * ``` + */ + branch: { + [V in keyof B]: { + (ctx: any): ReturnType; + }; }; -}; /** * Adds with query to the `context` @@ -303,70 +303,101 @@ branch: { * ``` */ req: Request; - /** - * `query`: Facilitates access to URL query parameters within the petition's execution context. - * - * **Examples**: - * - * Accessing a simple query parameter: - * ```typescript - * { - * path: '/query', - * f: ctx => ctx.query.name ?? "NotFound" - * }; - * ``` - * In this scenario, `ctx.query.name` directly accesses the `name` query parameter from the URL. - * - * --- - * - * Using query parameters with optimization for unique queries: - * ```typescript - * .stdPetition({ - * path: "/query", - * query: { - * unique: true, - * name: "hello" - * }, - * f: ctx => ctx.query ?? "NotFound" - * }) - * ``` -*/ -query: QS extends { unique: true } ? (string | null) : { [key: string]: string }; + /** + * `query`: Facilitates access to URL query parameters within the petition's execution context. + * + * **Examples**: + * + * Accessing a simple query parameter: + * ```typescript + * { + * path: '/query', + * f: ctx => ctx.query.name ?? "NotFound" + * }; + * ``` + * In this scenario, `ctx.query.name` directly accesses the `name` query parameter from the URL. + * + * --- + * + * Using query parameters with optimization for unique queries: + * ```typescript + * .stdPetition({ + * path: "/query", + * query: { + * unique: true, + * name: "hello" + * }, + * f: ctx => ctx.query ?? "NotFound" + * }) + * ``` + */ + query: QS extends { unique: true } ? (string | null) + : { [key: string]: string }; -/** - * `param`: Enables the extraction of URL path parameters within the petition's execution context. This feature simplifies accessing dynamic segments of the URL path, allowing petitions to respond to varied requests efficiently. - * - * **Examples**: - * - * Accessing a path parameter: - * ```typescript - * { - * path: '/user/:userId', - * f: ctx => `User ID: ${ctx.param.userId}` - * }; - * ``` - * In this example, `ctx.param.userId` retrieves the `userId` path parameter, enabling dynamic response content based on the URL. - * - * --- - * - * Using path parameters with optimization for unique paths: - * ```typescript - * .stdPetition({ - * path: "/user/:userId", - * param: { - * unique: true - * }, - * f: ctx => `User ID: ${ctx.param}` - * }) - * ``` - * Here, setting `unique: true` within the `param` configuration optimizes retrieval for a scenario where only one path parameter is expected, allowing direct access to the parameter value as `ctx.param`. - * - * */ -param: PA extends { unique: true } ? string : Record; + /** + * `param`: Enables the extraction of URL path parameters within the petition's execution context. This feature simplifies accessing dynamic segments of the URL path, allowing petitions to respond to varied requests efficiently. + * + * **Examples**: + * + * Accessing a path parameter: + * ```typescript + * { + * path: '/user/:userId', + * f: ctx => `User ID: ${ctx.param.userId}` + * }; + * ``` + * In this example, `ctx.param.userId` retrieves the `userId` path parameter, enabling dynamic response content based on the URL. + * + * --- + * + * Using path parameters with optimization for unique paths: + * ```typescript + * .stdPetition({ + * path: "/user/:userId", + * param: { + * unique: true + * }, + * f: ctx => `User ID: ${ctx.param}` + * }) + * ``` + * Here, setting `unique: true` within the `param` configuration optimizes retrieval for a scenario where only one path parameter is expected, allowing direct access to the parameter value as `ctx.param`. + */ + param: PA extends { unique: true } ? string : Record; + /** + * `headers`: Provides access to HTTP request headers within the petition's execution. + * + * **Examples**: + * + * Accessing request headers: + * ```typescript + * export const root = wrap({ + * cors: { + * 'maxAge': 14556156 + * }, + * })() + * .customPetition({ + * path: "/getHeaders", + * f: c => new Response( + * null, { + * headers: c.headers + * } + * ) + * }); + * + * const handles = root.handleRequest("/getHeaders")({}); + * + * console.log( + * await handles( + * new Request("http://localhost/getHeaders") + * ) + * ); + * ``` + */ headers: UNI extends { readonly hasHeaders: true; } ? Record : null; + /** * Adds a Date.now() returning the number of milliseconds elapsed since the epoch. * @@ -393,27 +424,6 @@ param: PA extends { unique: true } ? string : Record; * ``` */ date: number; - /** - * Resolve Behavior - * - * In a `petition` that includes a `resolve`, every element within the `resolve` object is initially unknown. - * It must be explicitly set within the containing `petition` to be recognized and processed. - * - * ```ts - * // Example - * { - * path: "/sample", - * resolve: { - * nestedElement : { - * f: () => "Hello World" - * } - * }, - * // 'nestedElement' is set here for the main function to recognize it - * f: context => context.resolve.nestedElement - * } - * ``` - * Ensure that every element you want to use from `resolve` is appropriately defined in your `petition`. - */ randomNumber: number; /** * Generates a unique ID using `crypto.randomUUID()`. @@ -459,39 +469,39 @@ param: PA extends { unique: true } ? string : Record; * ``` */ cookie: null | { [key: string]: string | undefined }; + /** - * Utilizes `ctx.mutable` for scenarios where mutable state is needed in Vixeny. + * `mutable`: A property designed to facilitate state mutation within the execution context of a petition. It enables the dynamic alteration of state across different parts of your application's flow, allowing for sophisticated state management strategies and interactions. * - * --- - * ```ts - * { - * path: '/mutable', - * mutable: true, - * // the function is "example", resolves with name "hello", which mutates "result" - * resolve: {...example_r_$hello_m_$result_string}, - * f: ctx => ctx.mutable.result as string - * }; - * ``` - * The mutable state is global and can be accessed at any depth: + * **Caution**: Mutable state should be handled with care to prevent unintended side effects. It's recommended to use this feature judiciously, ensuring that state mutations are predictable and well-understood within your application's context. * - * ```ts + * **Key Concept**: + * - All morphisms composing a petition can share and mutate this state, providing a powerful mechanism for stateful logic and data management. + * - This shared mutable state can be particularly useful for maintaining state across asynchronous operations, user authentication status, or other complex interaction patterns within a petition. + * + * **Example Usage**: + * ```js * { - * path: '/deepMutable', - * mutable: true, - * // the function is "example", resolves with name "hello", which mutates "result" - * resolve: {...example_r_$hello_m_$result_string}, - * f: ctx => ctx.branch.function("Greetings") as string, - * branch: { - * name: "function", - * f: c => `${c.arguments} ${c.mutable.result}` - * } - * }; + * path: '/', + * resolve: { + * // Define a resolve function that mutates state within `mutable` + * world: morphism()({ + * f: c => { + * c.mutable.hello = "hello "; // Mutating state + * return 'world'; + * } + * }) + * }, + * // The main function leverages the mutated state for constructing the response + * f: c => new Response(c.mutable.hello + c.resolve.world) // Accessing mutated state + * } * ``` + * **Note**: The structure and usage of `mutable` enable developers to architect complex and dynamic data flows within their Vixeny applications, offering flexibility in handling stateful operations. */ - mutable: { [keys: string]: any; }; + /** * Interacts with the `arguments` property in `ctx.branch` to receive input for branch functions. * @@ -501,42 +511,31 @@ param: PA extends { unique: true } ? string : Record; * path: '/path', * f: ctx => ctx.branch.hello("Greetings!"), * branch: { - * name: "hello", + * hello: { * f: c => c.arguments + * } * } * }; * ``` - * When invoking a branch function, any parameters passed are accessible as `arguments` within the branch function. * * --- + * + * When invoking a branch function, any parameters passed are accessible as `arguments` within the branch function. + * * ```ts * { * path: '/multipleArgs', * f: ctx => ctx.branch.greet("Hello", "world!"), * branch: { - * name: "greet", + * greet: { * f: c => `${c.arguments[0]} ${c.arguments[1]}` + * } * } * }; * ``` * In this example, multiple arguments are passed to the branch function, and they're accessed via index in the branch. */ arguments: unknown; - /** - * Takes a string and sign it, it has to: - * - Be longer than 7 - * - Have a `seed`, witch it has to be declare in the `Petition` , `branch` or `resolve` - * - * ```ts - * { - * path:"/path", - * signer: { - * seed: "SECRET_SEED", - * }, - * f: ctx => ctx.sign(ctx.cookie.id) - * } - * ``` - */ } export type CommonRequestMorphism< diff --git a/main.ts b/main.ts index d9b8655..a2f1c0f 100644 --- a/main.ts +++ b/main.ts @@ -9,7 +9,6 @@ import { } from "./components/http/src/optimizer/pluginUtil.ts"; import checker from "./components/http/src/framework/optimizer/checker.ts"; import applyResolver from "./components/http/src/optimizer/branchComposer.ts"; -import applyBranch from "./components/http/src/optimizer/branchComposer.ts"; import anyRequest from "./components/http/src/optimizer/anyRequest.ts"; /**