diff --git a/ReadMe.md b/ReadMe.md index 82733a8..0dc122a 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,5 +1,7 @@ **Welcome to Vixeny: Revolutionize Your Web Development** +`Status : Currectly restructuring the project for the beta` +

Vixeny Logo
diff --git a/components/encode/test/b64toUrl.test.mjs b/components/encode/test/b64toUrl.test.mjs deleted file mode 100644 index 07d3656..0000000 --- a/components/encode/test/b64toUrl.test.mjs +++ /dev/null @@ -1,12 +0,0 @@ -import convertBase64urlToBase64 from "../b64UrlTob64.mjs"; // Adjust this import to the name of your file -const convert = convertBase64urlToBase64(); // Since it's a higher order function -import test from "../../util/test.mjs"; - -((test) => (describe) => ( - test("Converts valid base64url to base64", () => - describe(convert("SGVsbG8tV29ybGQ_")).toEqual("SGVsbG8tV29ybGQ/")), - test("Adds padding for valid base64url strings", () => - describe(convert("U28")).toEqual("U28=")), - test("Returns null for invalid base64url strings", () => - describe(convert("Invalid*String")).toEqual(null)) -))(test.test)(test.describe); diff --git a/components/encode/test/stringify/composer/finder.test.mjs b/components/encode/test/stringify/composer/finder.test.mjs deleted file mode 100644 index c1f000f..0000000 --- a/components/encode/test/stringify/composer/finder.test.mjs +++ /dev/null @@ -1,21 +0,0 @@ -import convert from "../../../src/stringify/composer/finder.mjs"; -import test from "../../../../util/test.mjs"; - -((test) => (describe) => ( - test("hello - Test finder with nested object property", () => - describe(convert({ - type: "object", - properties: { - hello: { - type: "object", - properties: { - hello: { type: "string" }, - }, - }, - }, - required: ["hello"], - })) - .toEqual( - `"{" +'"hello":{' + '"hello":' + (typeof o.hello.hello === "string"?str(o.hello.hello):'null') + "}" + "}"`, - )) -))(test.test)(test.describe); diff --git a/components/encode/test/stringify/composer/main.test.mjs b/components/encode/test/stringify/composer/main.test.mjs deleted file mode 100644 index 3b735ca..0000000 --- a/components/encode/test/stringify/composer/main.test.mjs +++ /dev/null @@ -1,79 +0,0 @@ -import convert from "../../../src/stringify/safe.mjs"; // Adjust this import to the name of your file - -import test from "../../../../util/test.mjs"; - -((test) => (describe) => ( - test("hello - Test composer with simple string property", () => - describe(JSON.parse( - convert({ - type: "object", - properties: { - hello: { type: "string" }, - }, - required: ["hello"], - })({ hello: 'wo"rld' }), - )) - .toEqual({ hello: 'wo"rld' })), - test("hello - Test composer with nested object property", () => - describe(JSON.parse( - convert({ - type: "object", - properties: { - hello: { - type: "object", - properties: { - hello: { type: "string" }, - }, - required: ["hello"], - }, - }, - required: ["hello"], - })({ hello: { hello: "hi" } }), - )) - .toEqual({ hello: { hello: "hi" } })), - test("hello - Test composer with deeply nested object property", () => - describe(JSON.parse( - convert({ - type: "object", - properties: { - hello: { - type: "object", - properties: { - hello: { - type: "object", - properties: { - hello: { type: "string" }, - }, - required: ["hello"], - }, - }, - required: ["hello"], - }, - }, - required: ["hello"], - })({ hello: { hello: { hello: "string" } } }), - )) - .toEqual({ hello: { hello: { hello: "string" } } })), - test("hello - Test composer with number property and explicit value", () => - describe(JSON.parse( - convert({ - type: "object", - properties: { - hello: { type: "number", default: 2 }, - }, - required: ["hello"], - })({ hello: 1 }), - )) - .toEqual({ hello: 1 })), - test("hello - Test composer with number property and default value", () => - describe(JSON.parse( - convert({ - type: "object", - properties: { - hello: { type: "number", default: 2 }, - }, - required: ["hello"], - })({}), - )) - .toEqual({ hello: 2 })) -))(test.test)(test.describe); diff --git a/components/encode/test/stringify/composer/sameMatch.test.mjs b/components/encode/test/stringify/composer/sameMatch.test.mjs deleted file mode 100644 index b9c3f52..0000000 --- a/components/encode/test/stringify/composer/sameMatch.test.mjs +++ /dev/null @@ -1,95 +0,0 @@ -import convert from "../../../src/stringify/safe.mjs"; // Adjust this import to the name of your file -import test from "../../../../util/test.mjs"; - -((test) => (describe) => ( - test("hello - Test composer with simple string property", () => - describe( - convert({ - type: "object", - properties: { - hello: { type: "string" }, - }, - required: ["hello"], - })({ hello: 'wo"rld' }), - ) - .toEqual(JSON.stringify({ hello: 'wo"rld' }))), - test("hello - Test composer with nested object property", () => - describe( - convert({ - type: "object", - properties: { - hello: { - type: "object", - properties: { - hello: { type: "string" }, - }, - required: ["hello"], - }, - }, - required: ["hello"], - })({ hello: { hello: "hi" } }), - ) - .toEqual(JSON.stringify({ hello: { hello: "hi" } }))), - test("hello - Test composer with deeply nested object property", () => - describe( - convert({ - type: "object", - properties: { - hello: { - type: "object", - properties: { - hello: { - type: "object", - properties: { - hello: { type: "string" }, - }, - required: ["hello"], - }, - }, - required: ["hello"], - }, - }, - required: ["hello"], - })({ hello: { hello: { hello: "string" } } }), - ) - .toEqual(JSON.stringify({ hello: { hello: { hello: "string" } } }))), - test("hello - Test composer with number property and explicit value", () => - describe( - convert({ - type: "object", - properties: { - hello: { type: "number", default: 2 }, - }, - required: ["hello"], - })({ hello: 1 }), - ) - .toEqual(JSON.stringify({ hello: 1 }))), - test("hello - Test composer with number property and default value", () => - describe( - convert({ - type: "object", - properties: { - hello: { type: "number", default: 2 }, - }, - required: ["hello"], - })({}), - ) - .toEqual(JSON.stringify({ hello: 2 }))), - test("hello - Test composer with multiple properties including array", () => - describe( - convert({ - type: "object", - properties: { - hello: { type: "number", default: 2 }, - string: { type: "string", default: "hello" }, - array: { type: "array" }, - }, - required: ["hello", "string", "array"], - })({ array: ["hello", 2, ["hello", 2]] }), - ) - .toEqual(JSON.stringify({ - hello: 2, - string: "hello", - array: ["hello", 2, ["hello", 2]], - }))) -))(test.test)(test.describe); diff --git a/components/encode/test/stringify/composer/selector.test.mjs b/components/encode/test/stringify/composer/selector.test.mjs deleted file mode 100644 index f9af2ab..0000000 --- a/components/encode/test/stringify/composer/selector.test.mjs +++ /dev/null @@ -1,13 +0,0 @@ -import convert from "../../../src/stringify/composer/selector.mjs"; // Adjust this import to the name of your file -import test from "../../../../util/test.mjs"; - -((test) => (describe) => ( - test("hello - Test selector with string type and constant value", () => - describe(convert([{ - type: "string", - name: "hello2", - required: false, - const: "world", - }])) - .toEqual(`'"hello2":"world"'`)) -))(test.test)(test.describe); diff --git a/components/encode/test/stringify/methods/boolean.test.mjs b/components/encode/test/stringify/methods/boolean.test.mjs deleted file mode 100644 index 905f11f..0000000 --- a/components/encode/test/stringify/methods/boolean.test.mjs +++ /dev/null @@ -1,47 +0,0 @@ -import convert from "../../../src/stringify/methods/json_boolean.mjs"; // Adjust this import to the name of your file - -import test from "../../../../util/test.mjs"; - -((test) => (describe) => ( - test("hello - Handles boolean with constant value", () => - describe(convert({ - type: "boolean", - name: "hello", - required: true, - const: true, - path: ".hello", - })) - .toEqual(`'"hello":' + true`)), - test("hello - Handles required boolean with name and path", () => - describe(convert({ - type: "boolean", - name: "hello", - required: true, - path: ".hello", - })) - .toEqual(`'"hello":'+( typeof o.hello === "boolean"?o.hello:'null')`)), - test("hello - Handles required boolean with default value", () => - describe(convert({ - type: "boolean", - name: "hello", - required: true, - default: true, - path: ".hello", - })) - .toEqual(`'"hello":'+( typeof o.hello === "boolean"?o.hello:'true')`)), - test("hello - Compares required and optional boolean with default value", () => - describe(convert({ - type: "boolean", - name: "hello", - required: true, - default: true, - path: ".hello", - })) - .toEqual(convert({ - type: "boolean", - name: "hello", - required: false, - default: true, - path: ".hello", - }))) -))(test.test)(test.describe); diff --git a/components/encode/test/stringify/methods/number.test.mjs b/components/encode/test/stringify/methods/number.test.mjs deleted file mode 100644 index 1ee44a0..0000000 --- a/components/encode/test/stringify/methods/number.test.mjs +++ /dev/null @@ -1,49 +0,0 @@ -import convert from "../../../src/stringify/methods/json_number.mjs"; // Adjust this import to the name of your file - -import test from "../../../../util/test.mjs"; - -((test) => (describe) => ( - test("hello - Handles number with constant value", () => - describe(convert({ - type: "number", - name: "hello", - required: true, - const: 1, - path: ".hello", - })) - .toEqual(`'"hello":' + 1`)), - test("hello - Handles required number with name and path", () => - describe( - convert({ - type: "number", - name: "hello", - required: true, - path: ".hello", - }), - ) - .toEqual(`'"hello":'+( typeof o.hello === "number"?o.hello:'null')`)), - test("hello - Handles optional number with default value", () => - describe(convert({ - type: "number", - name: "hello", - required: false, - default: 5, - path: ".hello", - })) - .toEqual(`'"hello":'+( typeof o.hello === "number"?o.hello:'5')`)), - test("hello - Compares required and optional number with default value", () => - describe(convert({ - type: "number", - name: "hello", - required: true, - default: 5, - path: ".hello", - })) - .toEqual(convert({ - type: "number", - name: "hello", - required: false, - default: 5, - path: ".hello", - }))) -))(test.test)(test.describe); diff --git a/components/encode/test/stringify/methods/strings.test.mjs b/components/encode/test/stringify/methods/strings.test.mjs deleted file mode 100644 index e38cfa2..0000000 --- a/components/encode/test/stringify/methods/strings.test.mjs +++ /dev/null @@ -1,48 +0,0 @@ -import convert from "../../../src/stringify/methods/json_string.mjs"; - -import test from "../../../../util/test.mjs"; - -((test) => (describe) => ( - test("string - Handles required string with name and path", () => - describe( - convert({ - type: "string", - name: "hello", - required: true, - path: ".hello", - }), - ) - .toEqual(`'"hello":' + str(o.hello)`)), - test("string - Handles optional string with name and path", () => - describe(convert({ - type: "string", - name: "hello", - required: false, - path: ".hello", - })) - .toEqual( - `'"hello":' + (typeof o.hello === "string"?str(o.hello):'null')`, - )), - test("string - Handles required string with constant value", () => - describe( - convert({ type: "string", name: "hello", required: true, const: "hi" }), - ) - .toEqual(`'"hello":"hi"'`)), - test("string - Compares required and optional string with default value", () => - describe( - convert({ - type: "string", - name: "hello", - required: true, - default: "hi", - }), - ) - .toEqual( - convert({ - type: "string", - name: "hello", - required: false, - default: "hi", - }), - )) -))(test.test)(test.describe); diff --git a/components/http/src/framework/optimizer/branch/table.ts b/components/http/src/framework/optimizer/branch/table.ts deleted file mode 100644 index 1a7f2c7..0000000 --- a/components/http/src/framework/optimizer/branch/table.ts +++ /dev/null @@ -1,27 +0,0 @@ -import aComposer, { specialOptions } from "../aComposer.ts"; -import isUsing from "../tools/isUsing.ts"; -import { BranchOptions } from "./types.ts"; - -export default (o?: specialOptions) => -(path: string) => -(table: BranchOptions) => - table - .map((x) => ({ ...x, path: path })) - .map((x) => ({ - name: x.name, - f: ( - (composed) => - x.f.constructor.name === "AsyncFunction" || - composed.constructor.name === "AsyncFunction" - ? ((a) => - (k: (arg0: any) => any) => - (r: Request) => - async (b: unknown) => await k(a(r)(b)))(composed)(x.f) - : ((a) => (k: (arg0: any) => any) => (r: Request) => (b: unknown) => - k(a(r)(b)))(composed)(x.f) - )( - aComposer(o ? { ...aComposer, branch: false } : { branch: false })(x)( - isUsing(o)(x), - ), - ), - })); diff --git a/components/http/src/framework/optimizer/branch/types.ts b/components/http/src/framework/optimizer/branch/types.ts deleted file mode 100644 index 7877ca1..0000000 --- a/components/http/src/framework/optimizer/branch/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AnyMorphism, PetitionOptions, RawCommonRequest } from "../types.ts"; - -export type BranchOption = AnyMorphism & { - name: string; - options?: PetitionOptions; -}; -export type BranchOptions = BranchOption[]; - -export type ResponseResponse = (r: Request) => unknown; diff --git a/components/http/src/framework/optimizer/checkAsync.ts b/components/http/src/framework/optimizer/checkAsync.ts deleted file mode 100644 index 040f35d..0000000 --- a/components/http/src/framework/optimizer/checkAsync.ts +++ /dev/null @@ -1,15 +0,0 @@ -// deno-lint-ignore-file -import { CommonRequestMorphism, RequestMorphism } from "./types.ts"; - -type RecFunc = (f: CommonRequestMorphism | RequestMorphism) => boolean; - -export default (f: CommonRequestMorphism | RequestMorphism) => - f.f.constructor.name === "AsyncFunction" - ? true - : f.f.constructor.name === "Function" && typeof f.resolve == "undefined" - ? false - : "resolve" in f && f.resolve && Object.keys(f.resolve).some( - (x) => f.resolve && f.resolve[x].f.constructor.name === "AsyncFunction", - ) - ? true - : false; diff --git a/components/http/src/framework/optimizer/injectHtml.ts b/components/http/src/framework/optimizer/injectHtml.ts deleted file mode 100644 index cc9c13d..0000000 --- a/components/http/src/framework/optimizer/injectHtml.ts +++ /dev/null @@ -1,34 +0,0 @@ -export default ((appendString: string) => -async (aResponse: Response | Promise) => - ( - (response) => - (response.headers.get("Content-Type") || "").includes("text/html") - ? (async (clonedResponse) => - new Response( - (await clonedResponse.text()) + appendString, - { - status: clonedResponse.status, - statusText: clonedResponse.statusText, - headers: new Headers(clonedResponse.headers), - }, - ))( - response.clone(), - ) - : response - )( - await aResponse, - ))(` - - -`); diff --git a/components/http/src/framework/optimizer/optimize.ts b/components/http/src/framework/optimizer/optimize.ts deleted file mode 100644 index 6e74fcc..0000000 --- a/components/http/src/framework/optimizer/optimize.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { FunRouterOptions } from "../../../types.ts"; -import { RouteTypes } from "../builder/types.ts"; -import { - AnyMorphismMap, - CommonRequestMorphism, - CryptoOptions, - MorphismMap, - ObjectRawResponseReturn, - ObjectRawResponseStatic, - ParamOptions, - QueryOptions, - RequestMorphism, -} from "./types.ts"; -import response from "./response.ts"; -import staticFiles from "./staticFiles/main.ts"; -import vixeny from "../../../serve.ts"; -import injectHtml from "./injectHtml.ts"; - -export default ( - o?: FunRouterOptions, -): < - T extends MorphismMap, - B extends AnyMorphismMap, - Q extends QueryOptions, - P extends ParamOptions, - O extends FunRouterOptions, - CR extends CryptoOptions, ->(routes: ( - | RequestMorphism - | CommonRequestMorphism - | ObjectRawResponseReturn - | ObjectRawResponseStatic -)[]) => RouteTypes[] => -(ar) => - ar - .map( - (x) => - "type" in x - ? x.type === "response" - ? [ - x?.method ? x.method : "GET", - x.path, - o && o.enableLiveReloading - ? async (r: Request) => await injectHtml(x.r(r)) - : x.r, - false, - ] as RouteTypes - : x.type === "fileServer" - ? [ - "GET", - x.name + "*", - vixeny({ - ...o, - stateFlags: { - ...(o && o?.stateFlags ? o.stateFlags : {}), - isFileServer: true, - ...("slashIs" in x && typeof x.slashIs === "string" - ? { slashIs: x.slashIs } - : {}), - }, - })(staticFiles(x)), - "static", - ] as RouteTypes - : [ - x?.method ? x.method : "GET", - x.path, - o && o.enableLiveReloading - ? async (r: Request) => - await injectHtml( - response(o)(x as unknown as CommonRequestMorphism)(r), - ) - : response(o)(x as unknown as CommonRequestMorphism), - - false, - ] as unknown as RouteTypes - : [ - x?.method ? x.method : "GET", - x.path, - o && o.enableLiveReloading - ? async (r: Request) => - await injectHtml( - response(o)(x as unknown as CommonRequestMorphism)(r), - ) - : response(o)(x as unknown as CommonRequestMorphism), - false, - ] as unknown as RouteTypes, - ).concat( - o && o.enableLiveReloading - ? [[ - "GET", - "/timestamp-for-reload", - ((t) => () => new Response(t))(Date.now().toString()), - false, - ]] - : [], - ); diff --git a/components/http/src/framework/optimizer/recursiveCheckAsync.ts b/components/http/src/framework/optimizer/recursiveCheckAsync.ts deleted file mode 100644 index d85f9cb..0000000 --- a/components/http/src/framework/optimizer/recursiveCheckAsync.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { CommonRequestMorphism, RequestMorphism } from "./types.ts"; - -type RecFunc = (f: CommonRequestMorphism | RequestMorphism) => boolean; - -export default ((f: (x: RecFunc) => RecFunc) => - ((x: (arg: any) => any) => (v: any) => x(x)(v))( - (x: (arg: any) => any) => (v: any) => - f((y: CommonRequestMorphism | RequestMorphism) => x(x)(y))(v), - ))( - (solver: RecFunc) => (f: CommonRequestMorphism | RequestMorphism) => - f.f.constructor.name === "AsyncFunction" || ("isAsync" in f && f.isAsync) - ? true - : f.f.constructor.name === "Function" && - typeof f.resolve === "undefined" - ? false - : ("resolve" in f && f.resolve && - Object.keys(f.resolve).some((ob) => - f.resolve && - solver( - f.resolve[ob] as unknown as - | CommonRequestMorphism - | RequestMorphism, - ) - )) ?? - ("branch" in f && f.branch && - Object.keys(f.branch).some((ob) => - f.branch && - solver( - f.branch[ob] as unknown as - | CommonRequestMorphism - | RequestMorphism, - ) - )) ?? - false, - ) as unknown as (f: CommonRequestMorphism | RequestMorphism) => boolean; diff --git a/components/http/src/framework/optimizer/resolve/table.ts b/components/http/src/framework/optimizer/resolve/table.ts deleted file mode 100644 index cd7c12a..0000000 --- a/components/http/src/framework/optimizer/resolve/table.ts +++ /dev/null @@ -1,26 +0,0 @@ -import elements from "../../../util/elements.ts"; -import { FunRouterOptions } from "../../../../types.ts"; -import aComposer from "../aComposer.ts"; -import checker from "../checker.ts"; -import { CommonRequestMorphism } from "../types.ts"; -import { ResolveOptions } from "./types.ts"; -import isUsing from "../tools/isUsing.ts"; - -export default (o?: FunRouterOptions) => -(path: string) => -(table: ResolveOptions) => - table - .map((x) => ({ ...x, path: path })) - .map((x) => ({ - name: x.name, - f: ( - (composed) => - x.f.constructor.name === "AsyncFunction" || - composed.constructor.name === "AsyncFunction" - ? ((a) => (k: (arg0: any) => any) => async (r: Request) => - await k(await a(r)))(composed)(x.f) - : ((a) => (k: (arg0: any) => any) => (r: Request) => k(a(r)))( - composed, - )(x.f) - )(aComposer(o)(x as CommonRequestMorphism)(isUsing(o)(x))), - })); diff --git a/components/http/src/framework/optimizer/staticFiles/getDir.ts b/components/http/src/framework/optimizer/staticFiles/getDir.ts deleted file mode 100644 index 60ab930..0000000 --- a/components/http/src/framework/optimizer/staticFiles/getDir.ts +++ /dev/null @@ -1,16 +0,0 @@ -import syncCheckDir from "../syncCheckDir.ts"; -import bunSyncCheckDir from "../syncCheckDir_Bun.ts"; -import denoCheckRead from "../checkRead_Deno.ts"; -import joiner from "../../../../../util/joiner.mjs"; - -export default (s: string) => - ( - (denoCheck) => - typeof denoCheck == "string" - ? syncCheckDir(s).map((y) => y[0]).flat() - : bunSyncCheckDir(joiner)(denoCheck.getFiles)(denoCheck.stats)( - s, - ).map((y) => y[0]).flat() - )( - denoCheckRead, - ); diff --git a/components/http/src/framework/optimizer/staticFiles/mime.ts b/components/http/src/framework/optimizer/staticFiles/mime.ts deleted file mode 100644 index 7c1e585..0000000 --- a/components/http/src/framework/optimizer/staticFiles/mime.ts +++ /dev/null @@ -1,10 +0,0 @@ -import mime from "../../../util/mime.ts"; - -import { ObjectRawResponseStatic } from "../types.ts"; - -export default (f: ObjectRawResponseStatic) => - "mime" in f && f.mime === false - ? [] - : "extra" in f - ? mime.concat(f.extra) - : mime; diff --git a/components/http/src/framework/optimizer/syncCheckDir.ts b/components/http/src/framework/optimizer/syncCheckDir.ts deleted file mode 100644 index 00143b5..0000000 --- a/components/http/src/framework/optimizer/syncCheckDir.ts +++ /dev/null @@ -1,55 +0,0 @@ -// deno-lint-ignore-file - -// Default export function that takes a starting directory as a parameter and returns an array with all the names of the documents. -export default ((start) => - ( - (Y) => - ( - (searcher) => - ((mapper) => Y(mapper)(searcher))( - (f: (arg0: any) => any) => (m: any[]) => - m.some((x: boolean[]) => x[1] === true) - ? f(m.reduce((acc: any[], x: string[]) => - !x[1] ? acc.concat([x]) : acc.concat( - Y((f: (arg0: any) => ConcatArray) => - ((p) => (o: { next: () => any }) => - ((s) => - // Recursive call to the "Y" function with the next directory in the array. - // It continues to concatenate the contents of all directories until there are no more directories. - s.done === true - ? [] - : [[p + s.value.name, s.value.isDirectory]] - .concat(f(o)))( - o.next(), - ))(x[0] + "/") - )(Deno.readDirSync(x[0] as string)), - ), [])) - : m, - ) - )( - // Recursive call to the "Y" function with the starting directory. - // Returns a new array that contains the names of all files and directories in the directory tree. - Y((f: (arg0: any) => ConcatArray) => - ((p) => (o: { next: () => any }) => - ((s) => - // Recursive call to the "Y" function with the next directory in the array. - // It continues to concatenate the contents of all directories until there are no more directories. - s.done === true ? [] : [[ - (p.at(-2) === "/" - ? p.slice(0, -1) - : p.at(-1) === "/" - ? p - : p + "/") + s.value.name, - s.value.isDirectory, - ]].concat(f(o)))( - o.next(), - ))(start) - )(Deno.readDirSync(start)), - ) - )( - // Setting up the Y combinator. - (f: (arg0: (y: any) => any) => any) => - ((x) => x(x))((x: (arg0: any) => { (arg0: any): any; new (): any }) => - f((y: any) => x(x)(y)) - ), - )) as (s: string) => string[]; diff --git a/components/http/src/framework/optimizer/syncCheckDir_Bun.mjs b/components/http/src/framework/optimizer/syncCheckDir_Bun.mjs deleted file mode 100644 index 10bd38c..0000000 --- a/components/http/src/framework/optimizer/syncCheckDir_Bun.mjs +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Creates a function that searches through directories recursively and lists all files and directories along with a flag indicating if it's a directory. - * - * @param {Function} joiner - A function that takes a base path and a target path and returns the joined path. - * @param {Function} readdir - A function that takes a directory path and returns an array of file and directory names within it. - * @param {Function} stat - A function that takes a path and returns an object with an isDirectory method. - * @returns {Function} A function that takes a directory path and returns an array of tuples, each containing a path and a boolean flag indicating if it's a directory. - * - * @example - * const listFilesAndDirectories = myFunction(joinPathFunction, readDirectoryFunction, statsFunction); - * const items = listFilesAndDirectories('/path/to/directory'); - */ -export default (joiner) => (readdir) => (stat) => (dir) => - ( - (Y) => ( - Y((f) => ( - (dir) => - readdir(dir).flatMap((item) => - stat(joiner(dir)(item)).isDirectory() - ? [[joiner(dir)(item), true], ...f(joiner(dir)(item))] - : [[joiner(dir)(item), false]] - ) - ))(dir) - ) - )( - // Setting up the Y combinator. - (f) => ((x) => x(x))((x) => f((y) => x(x)(y))), - ); diff --git a/components/http/src/framework/optimizer/tools/isUsing.ts b/components/http/src/framework/optimizer/tools/isUsing.ts deleted file mode 100644 index 873e3dd..0000000 --- a/components/http/src/framework/optimizer/tools/isUsing.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { FunRouterOptions } from "../../../../types.ts"; -import elements from "../../../util/elements.ts"; -import checker from "../checker.ts"; -import { CommonRequestMorphism, RequestMorphism } from "../types.ts"; - -export default (o?: FunRouterOptions) => -(f: CommonRequestMorphism | RequestMorphism) => - ( - f.options && typeof f.options?.only !== "undefined" && - f.options.only.length > 0 - ) - ? Object.keys(f.options.only) - : checker( - (f.options && f.options.remove) ? Object.keys(f.options.remove) : [], - )([...elements(f), ...(Object.keys(o?.cyclePlugin || {}) || [])])( - [ - ...((f.options && f.options.add) ? f.options.add : []), - ], - )( - f.f.toString(), - ); diff --git a/components/http/src/framework/optimizer/tools/parsingToHexa.ts b/components/http/src/framework/optimizer/tools/parsingToHexa.ts deleted file mode 100644 index 628ae54..0000000 --- a/components/http/src/framework/optimizer/tools/parsingToHexa.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default (crypto: { globalKey: string }) => - typeof crypto.globalKey === "string" - // If it's a string, check if it's a valid hexadecimal - ? /^[0-9a-fA-F]+$/g.test(crypto.globalKey) - // If it's a valid hexadecimal, return it as is - ? new Uint8Array([...crypto.globalKey].map((x) => x.charCodeAt(0))) - // If it's not a valid hexadecimal, convert to Uint8Array - : new Uint8Array([...crypto.globalKey].map((x) => x.charCodeAt(0))) - // If not a string, return the value as is - : crypto.globalKey; diff --git a/components/http/src/framework/optimizer/types.ts b/components/http/src/framework/optimizer/types.ts deleted file mode 100644 index 6fce3ac..0000000 --- a/components/http/src/framework/optimizer/types.ts +++ /dev/null @@ -1,995 +0,0 @@ -import { CyclePluginMap, FunRouterOptions } from "../../../types.ts"; -import { ParamsMethod } from "../builder/types.ts"; - -// Modified ExtractPluginTypes -type ExtractPluginTypes = O["cyclePlugin"] extends - CyclePluginMap ? { - [K in keyof O["cyclePlugin"]]?: O["cyclePlugin"][K] extends - { type: infer T } ? T : never; - } - : {}; - -export type Morphism< - ResMap extends MorphismMap = MorphismMap, - BraMap extends AnyMorphismMap = AnyMorphismMap, - Query extends QueryOptions = QueryOptions, - Param extends ParamOptions = ParamOptions, - Options extends FunRouterOptions = FunRouterOptions, - Crypto extends CryptoOptions = CryptoOptions, - Mut extends MutableKey = MutableKey, - Return = any, -> = { - readonly resolve?: ResMap; - readonly branch?: BraMap; - f: ( - ctx: WithPlugins< - ResMap, - BraMap, - Query, - Param, - Options, - Crypto, - {}, - PetitionOptions< - [Extract], - Crypto - > - >, - ) => Return; - readonly query?: Query; - readonly param?: Param; - readonly options?: PetitionOptions< - [Extract], - Crypto - >; - readonly plugins?: ExtractPluginTypes; - readonly crypto?: Crypto; - readonly mutable?: Mut; - readonly isAsync?: true; -}; - -type CryptoContext = CR extends - { globalKey: any; token: infer Token } ? Token extends Record ? { - token: { [K in keyof Token]: Record }; - } & SignerAndVarifier - : { - sign: any; - verify: any; - token: any; - } - : CR extends { globalKey: any } ? { - token: Record | null>; - } & SignerAndVarifier - : { - sign: any; - verify: any; - token: any; - }; - -type SignerAndVarifier = { - verify: (s: string) => Record | null; - sign: (key: Record) => string; -}; - -export type CryptoOptions = { - globalKey: SupportedKeys; - token?: { - only?: { - [key: string]: {}; - }; - }; -} | {}; - -export type AnyMorphism< - ResMap extends MorphismMap = MorphismMap, - BraMap extends AnyMorphismMap = AnyMorphismMap, - Query extends QueryOptions = QueryOptions, - Param extends ParamOptions = ParamOptions, - Options extends FunRouterOptions = FunRouterOptions, - Crypto extends CryptoOptions = CryptoOptions, - Mut extends MutableKey = MutableKey, - Return = any, -> = Omit, "f"> & { - f: ( - ctx: WithPlugins< - ResMap, - BraMap, - Query, - Param, - Options, - Crypto, - {}, - PetitionOptions< - [Extract], - Crypto - > - >, - ) => Return; -}; -export type MorphismMap = { - [key: string]: Morphism; -}; -export type AnyMorphismMap = { - [key: string]: AnyMorphism; -}; - -type CyclePlugingFunctions = { - [K in keyof CPM]: CPM[K] extends - { isFunction: boolean; f: (...args: any) => any } - ? ReturnType> // Direct function case - : CPM[K] extends { f: (...args: any) => any } - ? Awaited>>> // Nested function case - : never; // Handle cases that do not match expected structure -}; - -type specialElements = { - readonly hasHeaders?: true; -} | {}; - -type WithPlugins< - R extends MorphismMap, - B extends AnyMorphismMap, - QS extends QueryOptions, - PA extends ParamOptions, - O extends FunRouterOptions, - CR extends CryptoOptions, - UNI extends specialElements, - OPT extends PetitionOptions, -> = - & Ctx - & (O extends { cyclePlugin: infer CPM } ? [keyof CPM] extends [never] ? {} - : CPM extends CyclePluginMap ? CyclePlugingFunctions - : never - : {}) - & CryptoContext; - -export interface Ctx< - R extends MorphismMap, - B extends AnyMorphismMap, - QS extends QueryOptions, - PA extends ParamOptions, - O extends FunRouterOptions, - CR extends CryptoOptions, - UNI extends specialElements, - OPT extends PetitionOptions, -> { - /** - * 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]: B[V]["options"] extends { arguments: infer Args } - ? (ctx: Args) => ReturnType - : (ctx: any) => ReturnType; - }; - - /** - * Adds with query to the `context` - * - * --- - * ```ts - * { - * path: "/path", - * f: async ctx => await ctx.req.blob() - * } - * --- - * { - * path: '/path', - * options: {add: ["req"]}, - * f: ctx => outOfContext(ctx) - * }; - * ``` - */ - 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 }; - - /** - * `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. - * - * ```ts - * { - * path: "/path" - * f: ctx => ctx.date > Date.now() - * ? "unreachable" - * : "date is created before ctx is passed to f" - * } - * ``` - * --- - * This behavior can be set for testing purpose - * ```ts - * { - * path: "/", - * options:{ - * setDate: 1694246953189 - * }, - * f: ctx => ctx.date === 1694246953189 - * ? "Date is bind to a state" - * : "unreachable" - * } - * ``` - */ - date: number; - randomNumber: number; - /** - * Generates a unique ID using `crypto.randomUUID()`. - * - * ```ts - * { - * path: "/path", - * f: ctx => ctx.hash === "some-random-uuid" - * ? "ID matches expected value" - * : "Generated a unique ID" - * } - * ``` - * --- - * This behavior can be set for testing purposes: - * ```ts - * { - * path: "/", - * options:{ - * setHash: "specified-uuid-value" - * }, - * f: ctx => ctx.hash === "specified-uuid-value" - * ? "UUID is bind to a state" - * : "unreachable" - * } - * ``` - */ - hash: string; - /** - * Retrieves cookies sent with the request using `ctx.cookie`. - * - * --- - * ```ts - * { - * path: '/path', - * f: ctx => ctx.cookie?.sessionID - * }; - * // If the `ctx` goes out of context - * { - * path: '/path', - * options: {add: ["cookie"]}, - * f: ctx => outOfContext(ctx) - * }; - * ``` - */ - cookie: null | { [key: string]: string | undefined }; - - /** - * `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. - * - * **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. - * - * **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: '/', - * 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. - * - * --- - * ```ts - * { - * path: '/path', - * f: ctx => ctx.branch.hello("Greetings!"), - * branch: { - * hello: { - * f: c => c.arguments - * } - * } - * }; - * ``` - * - * --- - * - * 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: { - * 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. - */ - readonly arguments: OPT extends { arguments: infer A } ? A : any; -} - -export type CommonRequestMorphism< - ResMap extends MorphismMap = MorphismMap, - BraMap extends AnyMorphismMap = AnyMorphismMap, - Query extends QueryOptions = QueryOptions, - Param extends ParamOptions = ParamOptions, - Options extends FunRouterOptions = FunRouterOptions, - Crypto extends CryptoOptions = CryptoOptions, - Mut extends MutableKey = MutableKey, - _Return = any, -> = - & Omit, "f"> - & RawCommonRequest - & { - headings?: PetitionHeader; - f: ( - ctx: WithPlugins< - ResMap, - BraMap, - Query, - Param, - Options, - Crypto, - {}, - PetitionOptions< - [Extract], - Crypto - > - >, - ) => BodyInit | Promise; - }; - -export type RequestMorphism< - ResMap extends MorphismMap = MorphismMap, - BraMap extends AnyMorphismMap = AnyMorphismMap, - Query extends QueryOptions = QueryOptions, - Param extends ParamOptions = ParamOptions, - Options extends FunRouterOptions = FunRouterOptions, - Crypto extends CryptoOptions = CryptoOptions, - Mut extends MutableKey = MutableKey, - _Return = any, -> = - & Omit, "f"> - & ObjectRawCommonRequest - & { - f: ( - ctx: WithPlugins< - ResMap, - BraMap, - Query, - Param, - Options, - Crypto, - { hasHeaders: true }, - PetitionOptions< - [Extract], - Crypto - > - >, - ) => Response | Promise; - }; - -export type BodyNull = { - [propName: string]: any; -} | null; -export type ObjectAndNullMorphism< - ResMap extends MorphismMap = MorphismMap, - BraMap extends AnyMorphismMap = AnyMorphismMap, - Query extends QueryOptions = QueryOptions, - Param extends ParamOptions = ParamOptions, - Options extends FunRouterOptions = FunRouterOptions, - Crypto extends CryptoOptions = CryptoOptions, - Mut extends MutableKey = MutableKey, - _Return = any, -> = - & Omit, "f"> - & { - f: ( - ctx: WithPlugins< - ResMap, - BraMap, - Query, - Param, - Options, - Crypto, - { hasHeaders: true }, - PetitionOptions< - [Extract], - Crypto - > - >, - ) => Promise | BodyNull; - }; - -export type ObjectaAnyMorphism< - ResMap extends MorphismMap = MorphismMap, - BraMap extends AnyMorphismMap = AnyMorphismMap, - Query extends QueryOptions = QueryOptions, - Param extends ParamOptions = ParamOptions, - Options extends FunRouterOptions = FunRouterOptions, - Crypto extends CryptoOptions = CryptoOptions, - Mut extends MutableKey = MutableKey, - T = any, // Add generic type parameter T here -> = - & Omit, "f"> - & { - f: ( - ctx: WithPlugins< - ResMap, - BraMap, - Query, - Param, - Options, - Crypto, - { hasHeaders: true }, - PetitionOptions< - [Extract], - Crypto - > - >, - ) => T; - }; - -export type Petition = - | RequestMorphism - | CommonRequestMorphism - | ObjectRawResponseReturn - | ObjectRawResponseStatic; - -/** - * Object for raw response return. - */ -export type ObjectRawResponseReturn = { - /** - * Direct interaction with Request and Response. - * - * --- - * ```ts - * { - * path: "/response/hello", - * type: "response", - * r: r => new Response("Hello world!") - * } - * ``` - */ - type: "response"; - /** - * `r` requires a functions which arguments contains the `Request` and need to return a `Response` or a `Promise` - * --- - * - * ```ts - * { - * path: "/path", - * r: () => new Response("hi") - * - * } - * - * ``` - */ - r: (r: Request) => Response | Promise; - method?: ParamsMethod; -} & PathKey; - -/** - * Object for raw common request. - */ -export type ObjectRawCommonRequest = - & { - /** - * Route Method - */ - method?: ParamsMethod; - /** - * Returns `Response` for custom statuses. - * - * --- - * ```ts - * { - * path: "/response/who/:name", - * type: "request", - * f: context => - * context.param.name === "Bun" - * ? new Response("Welcome") - * : new Response("Only devs", {status: 400}) - * } - * ``` - */ - type: "request"; - } - & RawCommonRequest; - -/** - * Common raw request object. - */ -export type RawCommonRequest = { - /** - * Route Method - */ - method?: ParamsMethod; -} & PathKey; - -type ExtendedAddOption = "globalKey" extends keyof CR - ? AddOption | "token" | "sign" | "verify" - : AddOption; - -/** - * Options for the petition. - */ -export type PetitionOptions< - T extends string[], - CR extends CryptoOptions, -> = { - readonly add?: Array | T[number]>; - readonly debug?: DebugOptions; - readonly remove?: Array | T[number]>; - readonly only?: Array | T[number]>; - readonly setHash?: string; - readonly setRandomNumber?: number; - readonly setDate?: number; - readonly arguments?: any; -}; - -/** - * List of options for adding. - */ -export type AddOptions = AddOption[]; - -/** - * Options for adding. - */ -export type AddOption = - | "req" - | "query" - | "param" - | "date" - | "randomNumber" - | "hash" - | "cookie" - | "resolve" - | "mutable" - | "branch" - | "arguments" - | "headers"; - -export type PathKey = { - /** - * Represents the endpoint path for a Vixeny petition. - * - * Remember that it have to start with `/` - * - * A "Hello World" example on `"/"`: - * ```ts - * { - * path: "/", - * f: () => "hello world", - * } - * ``` - * - * Alongside other configurations, this path determines how the server responds to specific endpoints. - */ - path: string; -}; - -/** - * Options for debugging. - */ -export type DebugOptions = { - type: "list"; - name: string; -}; - -export type MutableKey = { - mutable?: { - readonly is: true; - }; -} | {}; - -/** - * Headers for the petition. - */ -export type PetitionHeader = { - /** - * The headers initialization. - */ - headers?: HeadersInit | defaultMime; - /** - * The status text. - */ - statusText?: string; - /** - * The status number. - */ - status?: number; -}; - -export type QueryOptions = { - unique?: true; - name: string; // 'unique' is an optional boolean -} | { - only?: string[]; -} | {}; - -export type ParamOptions = { - readonly unique?: true; // 'unique' is an optional boolean -} | {}; - -type StaticFilePlugin = { - checker: (path: string) => boolean; - r: (options: { - root: string; - path: string; - relativeName: string; - }) => ObjectRawResponseReturn; -}; - -/** - * Object for raw response static. - */ -export type ObjectRawResponseStatic = - & ({ - type: "fileServer"; - name: string; - path: string; - } | { - type: "fileServer"; - name: string; - path: string; - mime?: true; - extra: [string, string][]; - } | { - type: "fileServer"; - name: string; - path: string; - mime: false; - }) - & { - template?: StaticFilePlugin[]; - removeExtensionOf?: defaultMime[]; - slashIs?: string; - }; - -export type SupportedKeys = - | string - | Uint8Array - | Uint8ClampedArray - | Uint16Array - | Uint32Array - | Int8Array - | Int16Array - | Int32Array - | BigUint64Array - | BigInt64Array - | Float32Array - | Float64Array; - -export type defaultMime = - | ".aac" - | ".abw" - | ".arc" - | ".avif" - | ".avi" - | ".azw" - | ".azw" - | ".bmp" - | ".bz" - | ".bz2" - | ".cda" - | ".csh" - | ".css" - | ".csv" - | ".doc" - | ".docx" - | ".eot" - | ".epub" - | ".gz" - | ".gif" - | ".htm" - | ".html" - | ".ico" - | ".ics" - | ".jar" - | ".jpeg" - | ".js" - | ".json" - | ".jsonld" - | ".mid" - | ".mjs" - | ".mp3" - | ".mp4" - | ".mpeg" - | ".mpkg" - | ".odp" - | ".ods" - | ".odt" - | ".oga" - | ".ogv" - | ".ogx" - | ".opus" - | ".otf" - | ".png" - | ".pdf" - | ".php" - | ".ppt" - | ".pptx" - | ".rar" - | ".rtf" - | ".sh" - | ".svg" - | ".tar" - | ".tif" - | ".tiff" - | ".ts" - | ".ttf" - | ".txt" - | ".vsd" - | ".wav" - | ".weba" - | ".webm" - | ".webp" - | ".woff" - | ".woff2" - | ".xhtml" - | ".xls" - | ".xlsx" - | ".xml" - | ".xul" - | ".zip" - | ".3gp" - | ".3g2" - | ".7z"; diff --git a/components/http/test/builder/atlas/main1.test.ts b/components/http/test/builder/atlas/main1.test.ts deleted file mode 100644 index 57232a5..0000000 --- a/components/http/test/builder/atlas/main1.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; - -import atlas from "../../../src/framework/builder/atlas/main1.ts"; -import optimize from "../../../src/framework/optimizer/optimize.ts"; -import paths from "../../util/paths.ts"; -import split from "../../../src/framework/builder/atlas/splitter.ts"; - -test( - "Atlas", - (_) => - assert.deepStrictEqual( - ((r) => [r[0], r[1], r[2]])( - atlas()(split()(optimize()(paths))), - ), - [ - [ - "GET", - "POST", - "HEAD", - "DELETE", - ], - [ - [ - 1, - 2, - 4, - ], - [ - 1, - ], - [ - 1, - ], - [ - 1, - ], - ], - [ - [ - [ - "/", - "/one", - "/two", - "/three", - "/four", - "/five", - "/six", - "/test", - ], - [ - "/test/", - ], - [ - "/test/:id/:name/", - ], - ], - [ - [ - "/", - ], - ], - [ - [ - "/", - ], - ], - [ - [ - "/", - ], - ], - ], - ], - ), -); -test( - "Atlas", - (_) => - assert.deepStrictEqual( - atlas()( - split()( - optimize()([...paths, { - path: "/hello/*", - f: () => "wild", - }, { - path: "/hello/nested/*", - f: () => "card", - }, { type: "fileServer", path: "./misc/", name: "/static/" }]), - ), - )[4].slice(0, -2) as unknown as null, - [ - [ - "GET", - ], - [ - [ - 2, - 3, - ], - ], - [ - [ - ["/hello/", "/static/"], - ["/hello/nested/"], - ], - ], - ], - ), -); diff --git a/components/http/test/builder/atlas/position.test.ts b/components/http/test/builder/atlas/position.test.ts deleted file mode 100644 index 4fd9808..0000000 --- a/components/http/test/builder/atlas/position.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import map from "../../../src/framework/builder/atlas/map.ts"; - -test( - "position", - () => - assert.deepStrictEqual( - map( - [ - [ - ["/", "/test"], - ["/test/"], - ["/test/:id/:name/"], - ], - [ - ["/"], - ], - [ - ["/"], - ], - [ - ["/"], - ], - ], - ), - [ - [ - 0, - 2, - 3, - ], - [ - 4, - ], - [ - 5, - ], - [ - 6, - ], - ], - ), -); - -test("functionName basic test", () => { - assert.deepStrictEqual( - map([ - [ - ["a", "b"], - ["c"], - ], - [ - ["d"], - ["e", "f"], - ], - ]), - [ - [0, 2], - [3, 4], - ], - ); -}); - -test("functionName edge case test", () => { - assert.deepStrictEqual( - map([ - [ - ["a"], - ["b"], - ], - [ - ["c"], - ["d"], - ], - ]), - [ - [0, 1], - [2, 3], - ], - ); -}); - -test("functionName edge case test", () => { - assert.deepStrictEqual( - map([ - [ - ["/count", "/hello_world", "/random_number"], - ], - [ - ["/plus_1", "/minus_1"], - ], - ]), - [ - [0], - [3], - ], - ); -}); diff --git a/components/http/test/builder/atlas/splitter.test.ts b/components/http/test/builder/atlas/splitter.test.ts deleted file mode 100644 index c4d03e1..0000000 --- a/components/http/test/builder/atlas/splitter.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import paths from "../../util/paths.ts"; -import optimize from "../../../src/framework/optimizer/optimize.ts"; -import split from "../../../src/framework/builder/atlas/splitter.ts"; - -test( - "arraySwap", - (_) => - assert.deepStrictEqual( - split({ hasName: "http://localhost:8080/" })( - optimize({ hasName: "http://localhost:8080/" })(paths), - )[0].map((x) => [x[0], x[1], x[2]]), - [ - [1, "/", "GET"], - [1, "/one", "GET"], - [1, "/two", "GET"], - [1, "/three", "GET"], - [1, "/four", "GET"], - [1, "/five", "GET"], - [1, "/six", "GET"], - [1, "/test", "GET"], - [2, "/test/", "GET"], - [4, "/test/:id/:name/", "GET"], - [1, "/", "POST"], - [1, "/", "HEAD"], - [1, "/", "DELETE"], - ], - ), -); - -test( - "arraySwap", - (_) => - assert.deepStrictEqual( - split()(optimize()(paths))[0].map((x) => [x[0], x[1], x[2]]), - [ - [1, "/", "GET"], - [1, "/one", "GET"], - [1, "/two", "GET"], - [1, "/three", "GET"], - [1, "/four", "GET"], - [1, "/five", "GET"], - [1, "/six", "GET"], - [1, "/test", "GET"], - [2, "/test/", "GET"], - [4, "/test/:id/:name/", "GET"], - [1, "/", "POST"], - [1, "/", "HEAD"], - [1, "/", "DELETE"], - ], - ), -); - -test( - "cheksplit", - () => { - assert.deepStrictEqual( - split()( - optimize()([{ path: "/", f: () => "hello" }, { - path: "/hello/*", - f: () => "wild", - }, { path: "/hello/hello/*", f: () => "wild" }]), - )[1][2], - [ - [ - ["/hello/"], - ["/hello/hello/"], - ], - ], - ); - }, -); - -test( - "cheksplit", - () => { - assert.deepStrictEqual( - split()( - optimize()([ - { - path: "/", - f: () => "hello", - }, - { path: "/hello/*", f: () => "wild" }, - { path: "/hello/hello/*", f: () => "wild" }, - { type: "fileServer", path: "./components/", name: "/static/" }, - ]), - )[1][2], - [ - [ - ["/hello/", "/static/"], - ["/hello/hello/"], - ], - ], - ); - }, -); -test( - "cheksplit", - () => { - assert.deepStrictEqual( - split({})(optimize()([{ path: "/", f: () => "hello" }]))[1][2], - [], - ); - }, -); - -test( - "checkSlashIs", - () => { - assert.deepStrictEqual( - split({ - stateFlags: { - slashIs: "$root", - }, - })(optimize()([{ path: "/$root", f: () => "hello" }]))[0][0][1], - "/", - ); - }, -); diff --git a/components/http/test/builder/composer/map.test.ts b/components/http/test/builder/composer/map.test.ts deleted file mode 100644 index 8edfd34..0000000 --- a/components/http/test/builder/composer/map.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import map from "../../../src/framework/builder/composer/map.ts"; -import assert from "node:assert"; -import test from "node:test"; - -test( - "only one parameter at the end", - (_) => - assert.deepStrictEqual( - map({})("/test/:id"), - { - elements: [ - ":id", - ], - endsInSlash: false, - firstParam: 5, - lastParam: 0, - path: "/test/:id", - hasName: undefined, - list: [ - "test", - ":id", - ], - map: [ - false, - true, - ], - startsWith: ":", - }, - ), -); - -test( - "only one parameter at the end", - (_) => - assert.deepStrictEqual( - map({})("/test/:id/:acc/:x/"), - { - elements: [ - ":id", - ":acc", - ":x", - ], - endsInSlash: true, - firstParam: 5, - lastParam: 0, - path: "/test/:id/:acc/:x/", - hasName: undefined, - list: [ - "test", - ":id", - ":acc", - ":x", - ], - map: [ - false, - true, - true, - true, - ], - startsWith: ":", - }, - ), -); diff --git a/components/http/test/builder/composer/methods1.test.ts b/components/http/test/builder/composer/methods1.test.ts deleted file mode 100644 index 5b4a90f..0000000 --- a/components/http/test/builder/composer/methods1.test.ts +++ /dev/null @@ -1,199 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import methods from "../../../src/framework/builder/composer/methods1.ts"; - -test( - "composer", - (_) => - ((f) => - assert.deepStrictEqual( - [ - f[0]("http://localhost:8080/"), - f[0]("http://localhost:8080/test"), - f[0]("http://localhost:8080/test/"), - f[0]("http://localhost:8080/notfound/"), - ], - [0, 1, 2, 10], - ))( - methods({ hasName: "http://localhost:8080/" })( - [ - [ - "GET", - ], - [ - [ - 1, - 2, - 4, - ], - ], - [ - [ - ["/", "/test"], - ["/test/"], - ["test/:id/:hi/"], - ], - ], - [], - [], - ], - )(0)(10)(6), - ), -); - -test( - "composer", - (_) => - ((f) => - assert.deepStrictEqual( - [ - f[0]("http://localhost:8080/"), - f[0]("http://localhost:8080/test"), - f[0]("http://localhost:8080/test/"), - f[0]("http://localhost:8080/notfound/"), - ], - [5, 6, 7, 10], - ))( - methods({ hasName: "http://localhost:8080/" })( - [ - [ - "GET", - ], - [ - [ - 1, - 2, - 4, - ], - ], - [ - [ - ["/", "/test"], - ["/test/"], - ["test/:id/:hi/"], - ], - ], - [], - [], - ], - )(5)(10)(6), - ), -); - -test( - "composer", - (_) => - ((f) => - assert.deepStrictEqual( - [ - f[0]("http://localhost:8080/"), - f[0]("http://localhost:8080/test"), - f[0]("http://localhost:8080/test/"), - f[0]("http://localhost:8080/notfound/"), - ], - [0, 1, 2, 10], - ))( - methods({ hasName: "http://localhost:8080/" })( - [ - [ - "GET", - ], - [ - [ - 1, - 2, - 4, - ], - ], - [ - [ - ["/", "/test"], - ["/test/"], - ["test/:id/:hi/"], - ], - ], - [], - [ - [ - "GET", - ], - [ - [ - 1, - 2, - 4, - ], - ], - [ - [ - ["/", "/test"], - ["/test/"], - ["test/:id/:hi/"], - ], - ], - [], - [], - ], - ], - )(0)(10)(6), - ), -); - -test( - "composer", - (_) => - ((f) => - assert.deepStrictEqual( - [ - f[0]("http://localhost:8080/"), - f[0]("http://localhost:8080/test"), - f[0]("http://localhost:8080/test/"), - f[0]("http://localhost:8080/notfound/"), - ], - [5, 6, 7, 10], - ))( - methods({ hasName: "http://localhost:8080/" })( - [ - [ - "GET", - ], - [ - [ - 1, - 2, - 4, - ], - ], - [ - [ - ["/", "/test"], - ["/test/"], - ["test/:id/:hi/"], - ], - ], - [], - [ - [ - "GET", - ], - [ - [ - 1, - 2, - 4, - ], - ], - [ - [ - ["/", "/test"], - ["/test/"], - ["test/:id/:hi/"], - ], - ], - [], - [], - ], - ], - )(5)(10)(6), - ), -); diff --git a/components/http/test/builder/composer/parameters.test.ts b/components/http/test/builder/composer/parameters.test.ts deleted file mode 100644 index 2e0bb03..0000000 --- a/components/http/test/builder/composer/parameters.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import map from "../../../src/framework/builder/composer/map.ts"; -import parameter from "../../../src/framework/builder/composer/parameters.ts"; -import assert from "node:assert"; -import test from "node:test"; - -test( - "composer", - (_) => - assert.deepStrictEqual( - parameter(0)(map()("/test/:id/hello/")), - ' s.slice(1, a0 - 1) === "test" && s.slice(a1).indexOf("hello") === 0 ? 0 : ', - ), -); - -test( - "composer", - (_) => - assert.deepStrictEqual( - parameter(0)(map()("/test/:id/hello")), - ' s.slice(1, a0 - 1) === "test" && s.slice(a1).indexOf("hello") === 0 ? 0 : ', - ), -); - -test( - "composer", - (_) => - assert.deepStrictEqual( - parameter(0)(map()("/test/:id/:hello/")), - ' s.indexOf("test/") === 1 ? 0 : ', - ), -); diff --git a/components/http/test/builder/composer/parser1.test.ts b/components/http/test/builder/composer/parser1.test.ts deleted file mode 100644 index 14f1e0f..0000000 --- a/components/http/test/builder/composer/parser1.test.ts +++ /dev/null @@ -1,186 +0,0 @@ -import parser from "../../../src/framework/builder/composer/parser1.ts"; -import assert from "node:assert"; -import test from "node:test"; - -test( - "composer", - (_) => - ( - (f) => - assert.deepStrictEqual( - [f("/"), f("/notFound"), f("/outOfTheScope/")], - [0, 50, 50], - ) - )( - parser({})([["/"]])([0])([1])(0)(50), - ), -); - -test( - "composer", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("/hello"), - f("/hello/hello"), - f("/other/bla/bla"), - f("/notFound"), - f("/outOfTheScope/"), - ], - [0, 0, 1, -1, -1], - ) - )( - parser({ - stateFlags: { - isWild: true, - }, - })([["/hello", "/other"]])([0])([1])(0)(-1), - ), -); - -test( - "composer", - (_) => - ( - (f) => - assert.deepStrictEqual( - [f("/"), f("/notFound"), f("/outOfTheScope/")], - [1, 50, 50], - ) - )( - parser({})([["/"]])([0])([1])(1)(50), - ), -); -test( - "composer", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("/"), - f("/hello"), - f("/test"), - f("/notFound"), - f("/outOfTheScope/"), - ], - [0, 1, 2, 50, 50], - ) - )( - parser({})([["/", "/hello", "/test"]])([0])([1])(0)(50), - ), -); - -test( - "composer", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("/"), - f("/hello"), - f("/test"), - f("/hello/"), - f("/test/hello"), - f("/notFound"), - f("/outOfTheScope/lol/"), - ], - [0, 1, 2, 3, 4, 50, 50], - ) - )( - parser({})([["/", "/hello", "/test"], ["/hello/", "/test/hello"]])([ - 0, - 3, - ])([1, 2])(0)(50), - ), -); - -test( - "composer", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("/"), - f("/hello"), - f("/test"), - f("/hello/"), - f("/test/hello"), - f("/hello/1/test"), - f("/test/2/hello"), - f("/hello/1/test/"), - f("/test/2/hello/"), - f("/notFound"), - f("/outOfTheScope/1/2/3/4/5/"), - ], - [0, 1, 2, 3, 4, 5, 6, 7, 8, 50, 50], - ) - )( - parser({})([["/", "/hello", "/test"], ["/hello/", "/test/hello"], [ - "/hello/:id/test", - "/test/:id/hello", - ], ["/hello/:id/test/", "/test/:id/hello/"]])([0, 3, 5, 7])([1, 2, 3, 4])( - 0, - )( - 50, - ), - ), -); - -test( - "composer", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("/"), - f("/hello"), - f("/test"), - f("/hello/"), - f("/test/hello"), - f("/hello/1/test/"), - f("/test/2/hello/"), - f("/notFound"), - f("/outOfTheScope/1/2/3/4/5/"), - ], - [0, 1, 2, 3, 4, 5, 6, 50, 50], - ) - )( - parser({})([["/", "/hello", "/test"], ["/hello/", "/test/hello"], [ - "/hello/:id/test/", - "/test/:id/hello/", - ]])([0, 3, 5])([1, 2, 4])(0)(50), - ), -); - -test( - "composer", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("/"), - f("/hello"), - f("/test"), - f("/hello/"), - f("/test/hello"), - f("/hello/1/test/"), - f("/test/2/hello/"), - f("/notFound"), - f("/outOfTheScope/1/2/3/4/5/"), - ], - [1, 2, 3, 4, 5, 6, 7, 50, 50], - ) - )( - parser({})([["/", "/hello", "/test"], ["/hello/", "/test/hello"], [ - "/hello/:id/test/", - "/test/:id/hello/", - ]])([0, 3, 5])([1, 2, 4])(1)(50), - ), -); diff --git a/components/http/test/builder/composer/specialString.test.ts b/components/http/test/builder/composer/specialString.test.ts deleted file mode 100644 index 42f55ae..0000000 --- a/components/http/test/builder/composer/specialString.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import specialString from "../../../src/framework/builder/composer/specialString.ts"; -import assert from "node:assert"; -import test from "node:test"; - -test( - "hello", - (_) => - assert.deepStrictEqual( - specialString({ hasName: "http://localhost:8080/" })(12)([ - ["GET", "/", (_) => new Response(), false], - ])("http://localhost:8080/"), - 10, - ), -); diff --git a/components/http/test/builder/composer/validator.test.ts b/components/http/test/builder/composer/validator.test.ts deleted file mode 100644 index 8c26aa9..0000000 --- a/components/http/test/builder/composer/validator.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import validator from "../../../src/framework/builder/composer/validator.ts"; -import assert from "node:assert"; -import test from "node:test"; - -test( - "composer", - (_) => - assert.deepStrictEqual( - validator({})(1)(-1)(["/", "/hello", "/test"]), - 's === "/" || s.indexOf("?") === 1 || s.indexOf("?") === 1 ? 1 : s === "/hello" || s.indexOf("hello?") === 1 ? 2 : s === "/test" || s.indexOf("test?") === 1 ? 3 : -1', - ), -); - -test( - "composer", - (_) => - assert.deepStrictEqual( - validator({})(1)(-1)(["/hello/", "/test/"]), - 's.indexOf("hello/") === 1 ? 1 : s.indexOf("test/") === 1 ? 2 : -1', - ), -); - -test( - "composer", - (_) => - assert.deepStrictEqual( - validator({})(1)(-1)(["/hello/:id/hello", "/test/:id/:bar"]), - 's.slice(1, a0 - 1) === "hello" && s.slice(a1).indexOf("hello") === 0 ? 1 : s.indexOf("test/") === 1 ? 2 : -1', - ), -); diff --git a/components/http/test/builder/solver1.test.ts b/components/http/test/builder/solver1.test.ts deleted file mode 100644 index 8af4e9c..0000000 --- a/components/http/test/builder/solver1.test.ts +++ /dev/null @@ -1,162 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import solver from "../../src/framework/builder/solver1.ts"; -import atlas from "../../src/framework/builder/atlas/main1.ts"; -import paths from "../util/paths.ts"; -import split from "../../src/framework/builder/atlas/splitter.ts"; -import optimize from "../../src/framework/optimizer/optimize.ts"; - -test( - "full routes", - (_) => - ( - (a) => - assert.deepStrictEqual( - [ - a(new Request("http://localhost:8080/")), - a(new Request("http://localhost:8080/one")), - a(new Request("http://localhost:8080/two")), - a(new Request("http://localhost:8080/three")), - a(new Request("http://localhost:8080/four")), - a(new Request("http://localhost:8080/five")), - a(new Request("http://localhost:8080/six")), - a(new Request("http://localhost:8080/test")), - a(new Request("http://localhost:8080/test/")), - a(new Request("http://localhost:8080/test/1/2/")), - a(new Request("http://localhost:8080/", { method: "POST" })), - a(new Request("http://localhost:8080/", { method: "HEAD" })), - a(new Request("http://localhost:8080/", { method: "DELETE" })), - a(new Request("http://localhost:8080/hello/nested/hello/***")), - a(new Request("http://localhost:8080/hello/nested/***")), - a(new Request("http://localhost:8080/hello/***")), - a(new Request("http://localhost:8080/NOTFOUND")), - a(new Request("http://localhost:8080/", { method: "BAD" })), - ], - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 14, 13, 16, 17], - ) - )( - solver({ hasName: "http://localhost:8080/" })( - atlas({ hasName: "http://localhost:8080/" })( - split({ hasName: "http://localhost:8080/" })( - optimize({ hasName: "http://localhost:8080/" })([ - ...paths, - { path: "/hello/nested/hello/*", f: () => "card" }, - { path: "/hello/nested/*", f: () => "card" }, - { path: "/hello/*", f: () => "wild" }, - ]), - ), - ), - ), - ), -); - -test( - "resolver", - (_) => - ( - (a) => - assert.deepStrictEqual( - [ - a(new Request("http://localhost:8080/")), - a(new Request("http://localhost:8080/one")), - a(new Request("http://localhost:8080/two")), - a(new Request("http://localhost:8080/three")), - a(new Request("http://localhost:8080/four")), - a(new Request("http://localhost:8080/five")), - a(new Request("http://localhost:8080/six")), - a(new Request("http://localhost:8080/test")), - a(new Request("http://localhost:8080/test/")), - a(new Request("http://localhost:8080/test/1/2/")), - a(new Request("http://localhost:8080/", { method: "POST" })), - a(new Request("http://localhost:8080/", { method: "HEAD" })), - a(new Request("http://localhost:8080/", { method: "DELETE" })), - a(new Request("http://localhost:8080/NOTFOUND")), - a(new Request("http://localhost:8080/", { method: "BAD" })), - ], - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - ) - )( - solver()(atlas()(split()(optimize()(paths)))), - ), -); - -test( - "resolver", - (_) => - ( - (a) => - assert.deepStrictEqual( - [ - a(new Request("http://localhost:8080/")), - a(new Request("http://localhost:8080/one")), - a(new Request("http://localhost:8080/two")), - a(new Request("http://localhost:8080/three")), - a(new Request("http://localhost:8080/four")), - a(new Request("http://localhost:8080/five")), - a(new Request("http://localhost:8080/six")), - a(new Request("http://localhost:8080/test")), - a(new Request("http://localhost:8080/test/")), - a(new Request("http://localhost:8080/test/1/2/")), - a(new Request("http://localhost:8080/", { method: "POST" })), - a(new Request("http://localhost:8080/", { method: "HEAD" })), - a(new Request("http://localhost:8080/", { method: "DELETE" })), - a(new Request("http://localhost:8080/NOTFOUND")), - a(new Request("http://localhost:8080/", { method: "BAD" })), - ], - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - ) - )( - solver()(atlas()(split()(optimize()(paths)))), - ), -); - -test( - "resolver", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f(new Request("http://localhost:8000/count")), - f(new Request("http://localhost:8000/hello_world")), - f(new Request("http://localhost:8000/random_number")), - f(new Request("http://localhost:8000/plus_1", { method: "POST" })), - f(new Request("http://localhost:8000/minus_1", { method: "POST" })), - f(new Request("http://localhost:8000/NOTFOUND")), - f(new Request("http://localhost:8000/", { method: "BAD" })), - ], - [0, 1, 2, 3, 4, 5, 6], - ) - )( - solver()( - atlas()( - split()( - optimize()([ - { - path: "/count", - f: () => "1", - }, - { - path: "/hello_world", - f: () => "2", - }, - { - path: "/random_number", - f: () => "3", - }, - { - path: "/plus_1", - f: () => "4", - method: "POST", - }, - { - path: "/minus_1", - f: () => "5", - method: "POST", - }, - ]), - ), - ), - ), - ), -); diff --git a/components/http/test/components/cookieToToken/main.test.ts b/components/http/test/components/cookieToToken/main.test.ts deleted file mode 100644 index 7891586..0000000 --- a/components/http/test/components/cookieToToken/main.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; - -import signSha256 from "../../../../jwt/signSha256.mjs"; -import cookieToTokenFilter from "../../../src/cookieToToken/cookieToTokenFilter.ts"; - -const secret = new Uint8Array([1, 2, 3, 4, 5, 6]); -const sign = signSha256()(secret); - -const request = new Request("http://localhost:3000/", { - headers: new Headers( - [[ - "cookie", - `hello=${sign({ hi: 1 })}`, - ]], - ), -}); - -test("with element", () => { - assert.deepStrictEqual( - cookieToTokenFilter(["f.token.jwt.id"])("token"), - ["jwt"], - ); -}); - -test("no element", () => { - assert.deepStrictEqual( - cookieToTokenFilter(["f.token.jwt"])("token"), - ["jwt"], - ); -}); diff --git a/components/http/test/components/tokens/main.test.ts b/components/http/test/components/tokens/main.test.ts deleted file mode 100644 index 44271af..0000000 --- a/components/http/test/components/tokens/main.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import verifier from "../../../src/framework/tokens/verifier.ts"; -import signer from "../../../src/framework/tokens/signer.ts"; -import assert from "node:assert"; -import test from "node:test"; -import jSigner from "../../../src/framework/tokens/jSigner.ts"; -import jVerify from "../../../src/framework/tokens/jVerify.ts"; - -test( - "test", - () => - ( - (sign) => - assert.deepStrictEqual( - (verifier({ seed: "hello", size: 40 }))(sign( - Array.from({ length: 40 }, (_, i) => String.fromCharCode(i + 30)) - .join(""), - )), - true, - ) - )( - signer( - { - seed: "hello", - size: 40, - }, - ), - ), -); - -test( - "test", - () => - ( - (sign) => - assert.deepStrictEqual( - (verifier({ seed: "hello" }))(sign( - Array.from({ length: 9 }, (_, i) => String.fromCharCode(i + 30)) - .join(""), - )), - true, - ) - )( - signer( - { - seed: "hello", - }, - ), - ), -); - -test( - "experies", - () => - ( - (sign) => - assert.deepStrictEqual( - verifier({ seed: "hello", expires: 1000 })(sign("01234567")), - true, - ) - )( - signer({ seed: "hello", expires: 1000 }), - ), -); -test( - "experies", - () => - ( - (sign) => - assert.deepStrictEqual( - verifier({ seed: "hello", expires: 1000 })(sign("01234567")), - true, - ) - )( - signer({ seed: "hello", expires: 1000, size: 21 }), - ), -); -test( - "experies", - () => - ( - (sign) => - assert.deepStrictEqual( - verifier({ seed: "hello", expires: 1000, size: 21 })( - sign("01234567"), - ), - true, - ) - )( - signer({ seed: "hello", expires: 1000 }), - ), -); -test( - "experies", - () => - ( - (sign) => - assert.deepStrictEqual( - verifier({ seed: "hello", expires: 1000 })(sign("01234567")), - false, - ) - )( - signer({ seed: "hello", expires: -1 }), - ), -); - -test( - "test", - () => - assert.deepStrictEqual( - jVerify({ seed: "hello" })( - jSigner({ seed: "hello" })({ hello: "world" }), - ), - { hello: "world" }, - ), -); -test( - "test", - () => - assert.deepStrictEqual( - jVerify({ seed: "hello", expires: 1000 })( - jSigner({ seed: "hello", expires: 1000 })({ hello: "world" }), - ), - { hello: "world" }, - ), -); -test( - "test", - () => - assert.deepStrictEqual( - jVerify({ seed: "hello", expires: -1 })( - jSigner({ seed: "hello", expires: -1 })({ hello: "world" }), - ), - null, - ), -); diff --git a/components/http/test/optimizer/aComposer.test.ts b/components/http/test/optimizer/aComposer.test.ts deleted file mode 100644 index f1cfbf9..0000000 --- a/components/http/test/optimizer/aComposer.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import signer from "../../src/framework/tokens/signer.ts"; -import aComposer from "../../src/framework/optimizer/aComposer.ts"; -import assert from "node:assert"; -import test from "node:test"; -//await ((hi: (arg0: any) => any)=> async (r: any)=>({hi:await hi(r)}))((hi=>async (f: any) =>({hi:await hi(f)})) (async (f: { blob: () => any; })=> await(await f.blob()).text()))(new Request("http://hi.com/", {method: "POST", body: "hello"})), -test( - "Query", - (_) => - assert.deepStrictEqual( - (aComposer({ hasName: "http://localhost:8080/" })({ - path: "/test", - f: (r) => r.query.hello || "nothing", - })(["query"]))(new Request("http://localhost:8080/test?hello=hi")).query - .hello, - "hi", - ), -); -test( - "Query", - (_) => - assert.deepStrictEqual( - (aComposer()({ - path: "/test", - f: (r) => r.query.hello || "nothing", - })(["query"]))(new Request("http://localhost:8080/test?hello=hi")).query - .hello, - "hi", - ), -); -test( - "Query", - (_) => - assert.deepStrictEqual( - (aComposer({ hasName: "http://localhost:8080/" })({ - path: "/test", - f: (r) => r.query.hello || "nothing", - })(["query", "req"]))(new Request("http://localhost:8080/test?hello=hi")) - .query.hello, - "hi", - ), -); -test( - "Params", - (_) => - assert.deepStrictEqual( - (aComposer({ hasName: "http://localhost:8080/" })({ - path: "/test/:id", - f: (r) => (r.param as Record).id, - })(["param"]))(new Request("http://localhost:8080/test/1")).param.id, - "1", - ), -); - -test( - "Params", - (_) => - assert.deepStrictEqual( - (aComposer({ hasName: "http://localhost:8080/" })({ - path: "/test/:a/:b/:c/", - f: (r) => (r.param as Record).id, - })(["param"]))(new Request("http://localhost:8080/test/1/2/3/")).param.b, - "2", - ), -); -test( - "Params", - (_) => - assert.deepStrictEqual( - (aComposer()({ - path: "/test/:a/:b/:c/", - f: (r) => (r.param as Record).id.toString(), - })(["param"]))(new Request("http://localhost:8080/test/1/2/3/")).param.b, - "2", - ), -); - -test( - "Date", - (_) => - assert.deepStrictEqual( - typeof (aComposer()({ - path: "/test/", - f: (r) => r.date.toString(), - })(["date"]))(new Request("http://localhost:8080/test/")).date, - "number", - ), -); -test( - "randomNumber", - (_) => - assert.deepStrictEqual( - typeof (aComposer()({ - path: "/test/", - f: (r) => r.randomNumber.toString(), - })(["randomNumber"]))(new Request("http://localhost:8080/test/")) - .randomNumber, - "number", - ), -); -test( - "Hash", - (_) => - assert.deepStrictEqual( - typeof (aComposer()({ - path: "/test/", - f: (r) => r.hash, - })(["hash"]))(new Request("http://localhost:8080/test/")).hash, - "string", - ), -); -test( - "cookie", - (_) => - assert.deepStrictEqual( - (aComposer()({ - path: "/test/", - f: (r) => r.cookie?.id ?? "not_found", - })(["cookie"]))( - new Request("http://localhost:8080/test/", { - headers: { - "Content-Type": "application/json", - "Cookie": "id=user", - }, - }), - ).cookie, - { id: "user" }, - ), -); diff --git a/components/http/test/optimizer/checkAsync.test.ts b/components/http/test/optimizer/checkAsync.test.ts deleted file mode 100644 index 1fa0d80..0000000 --- a/components/http/test/optimizer/checkAsync.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import checkAsync from "../../src/framework/optimizer/checkAsync.ts"; - -test( - "check for async", - () => - assert.deepStrictEqual( - checkAsync({ - path: "/", - f: async (f) => await f.req.blob(), - }), - true, - ), -); -test( - "check for sync", - () => - assert.deepStrictEqual( - checkAsync({ - path: "/", - f: () => "hello", - }), - false, - ), -); -test( - "check for nested async", - () => - assert.deepStrictEqual( - checkAsync({ - path: "/", - resolve: { - hi: { - f: async (f) => await f.req.blob(), - }, - }, - f: () => "hi", - }), - true, - ), -); -test( - "check for nested async", - () => - assert.deepStrictEqual( - checkAsync({ - path: "/", - resolve: { - hi: { - f: () => "hi", - }, - }, - f: () => "hi", - }), - false, - ), -); -test( - "check for nested array async", - () => - assert.deepStrictEqual( - checkAsync({ - path: "/", - resolve: { - "hi": { - f: () => "ji", - }, - "hi2": { - f: async (f) => await f.req.blob(), - }, - }, - f: () => "hi", - }), - true, - ), -); -test( - "check for nested array async", - () => - assert.deepStrictEqual( - checkAsync({ - path: "/", - resolve: { - hi: { - f: () => "ji", - }, - hi2: { - f: () => "hi", - }, - }, - f: () => "hi", - }), - false, - ), -); diff --git a/components/http/test/optimizer/cheker.test.ts b/components/http/test/optimizer/cheker.test.ts deleted file mode 100644 index acd73b8..0000000 --- a/components/http/test/optimizer/cheker.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import checker from "../../src/framework/optimizer/checker.ts"; - -test( - "Params", - (_) => - assert.deepStrictEqual( - checker([])(["param"])([])("r=> r. param "), - ["param"], - ), -); diff --git a/components/http/test/optimizer/parameters/finder.test.ts b/components/http/test/optimizer/parameters/finder.test.ts deleted file mode 100644 index f5b32b8..0000000 --- a/components/http/test/optimizer/parameters/finder.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import finder from "../../../src/parameters/finder.ts"; -import assert from "node:assert"; -import test from "node:test"; -import map from "../../../src/parameters/map.ts"; - -test( - "only one parameter at the end and query", - (_) => - assert.deepStrictEqual( - (new Function( - ` return ${ - finder(map()({ f: (_) => "hello", path: "/test/:id/:hi" })) - }`, - ))()("456/hi"), - { - hi: "hi", - id: "456", - }, - ), -); - -test( - "only one parameter at the end and query", - (_) => - assert.deepStrictEqual( - (new Function( - ` return ${ - finder(map()({ f: (_) => "hello", path: "/:test/:id/:hi" })) - }`, - ))()("test/456/hi"), - { - hi: "hi", - id: "456", - test: "test", - }, - ), -); - -test( - "only one parameter at the end and query", - (_) => - assert.deepStrictEqual( - (new Function( - ` return ${ - finder(map()({ f: (_) => "hello", path: "/:test/:id/hi" })) - }`, - ))()("test/456"), - { - id: "456", - test: "test", - }, - ), -); diff --git a/components/http/test/optimizer/parameters/map.test.ts b/components/http/test/optimizer/parameters/map.test.ts deleted file mode 100644 index bc9a499..0000000 --- a/components/http/test/optimizer/parameters/map.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -import map from "../../../src/parameters/map.ts"; -import assert from "node:assert"; -import test from "node:test"; - -test( - "only one parameter at the end", - (_) => - assert.deepStrictEqual( - map({})({ path: "/test/:id", f: (_) => "hello" }), - { - elements: [ - ":id", - ], - endsInSlash: false, - firstParam: 5, - lastParam: 0, - hasName: undefined, - list: [ - "test", - ":id", - ], - map: [ - false, - true, - ], - startsWith: ":", - }, - ), -); - -test( - "only one parameter at the end", - (_) => - assert.deepStrictEqual( - map({})({ path: "/test/:id/", f: (_) => "hello" }), - { - elements: [ - ":id", - ], - endsInSlash: true, - firstParam: 5, - lastParam: 1, - hasName: undefined, - list: [ - "test", - ":id", - ], - map: [ - false, - true, - ], - startsWith: ":", - }, - ), -); - -test( - "only one parameter at the end", - (_) => - assert.deepStrictEqual( - map({})({ path: "/test/:id/hi", f: (_) => "hello" }), - { - elements: [ - ":id", - ], - endsInSlash: false, - firstParam: 5, - lastParam: 3, - hasName: undefined, - list: [ - "test", - ":id", - "hi", - ], - map: [ - false, - true, - false, - ], - startsWith: ":", - }, - ), -); - -test( - "only one parameter at the end", - (_) => - assert.deepStrictEqual( - map({})({ path: "/test/:id/hi/", f: (_) => "hello" }), - { - elements: [ - ":id", - ], - endsInSlash: true, - firstParam: 5, - lastParam: 4, - hasName: undefined, - list: [ - "test", - ":id", - "hi", - ], - map: [ - false, - true, - false, - ], - startsWith: ":", - }, - ), -); - -test( - "only one parameter at the end", - (_) => - assert.deepStrictEqual( - map({})({ path: "/test/:id/:test", f: (_) => "hello" }), - { - elements: [ - ":id", - ":test", - ], - endsInSlash: false, - firstParam: 5, - lastParam: 0, - hasName: undefined, - list: [ - "test", - ":id", - ":test", - ], - map: [ - false, - true, - true, - ], - startsWith: ":", - }, - ), -); - -test( - "only one parameter at the end", - (_) => - assert.deepStrictEqual( - map({})({ path: "/:test/:id/:hi", f: (_) => "hello" }), - { - elements: [ - ":test", - ":id", - ":hi", - ], - endsInSlash: false, - firstParam: 0, - lastParam: 0, - hasName: undefined, - list: [ - ":test", - ":id", - ":hi", - ], - map: [ - true, - true, - true, - ], - startsWith: ":", - }, - ), -); diff --git a/components/http/test/optimizer/parameters/multi.test.ts b/components/http/test/optimizer/parameters/multi.test.ts deleted file mode 100644 index 11dfc6c..0000000 --- a/components/http/test/optimizer/parameters/multi.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import multi from "../../../src/parameters/multi.ts"; -import map from "../../../src/parameters/map.ts"; -import assert from "node:assert"; -import test from "node:test"; - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - multi( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/:hello", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/world"), - { - hello: "world", - id: "hello", - }, - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - multi( - map({ hasName: "http://localhost:8080/" })({ - path: "/:test/:id/:hello", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/world"), - { - hello: "world", - id: "hello", - test: "test", - }, - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - multi( - map({ hasName: "http://localhost:8080/" })({ - path: "/:test/:id/hello", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/world"), - { - id: "hello", - test: "test", - }, - ), -); diff --git a/components/http/test/optimizer/parameters/one.test.ts b/components/http/test/optimizer/parameters/one.test.ts deleted file mode 100644 index 7f0fa36..0000000 --- a/components/http/test/optimizer/parameters/one.test.ts +++ /dev/null @@ -1,299 +0,0 @@ -import one from "../../../src/parameters/one.ts"; -import map from "../../../src/parameters/map.ts"; -import assert from "node:assert"; -import test from "node:test"; - -// "/test/:id" -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello").id, - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello?a=1").id, - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello").id, - f("http://localhost:8080/test/hello").id, - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello?a=1").id, - f("http://localhost:8080/test/hello?a=1").id, - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id", f: (_) => "hello" }))}`, - )(), - ), -); - -// "/test/:id/" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/").id, - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/?a=1").id, - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/").id, - f("http://localhost:8080/test/hello/").id, - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/?a=1").id, - f("http://localhost:8080/test/hello/?a=1").id, - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/", f: (_) => "hello" }))}`, - )(), - ), -); - -// "/test/:id/hi" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi").id, - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi?a=1").id, - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi").id, - f("http://localhost:8080/test/hello/hi").id, - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/hi", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi?a=1").id, - f("http://localhost:8080/test/hello/hi?a=1").id, - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/hi", f: (_) => "hello" }))}`, - )(), - ), -); - -// "/test/:id/hi/" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi/").id, - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi/?a=1").id, - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi/").id, - f("http://localhost:8080/test/hello/hi/").id, - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/hi/", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi/?a=1").id, - f("http://localhost:8080/test/hello/hi/?a=1").id, - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/hi/", f: (_) => "hello" }))}`, - )(), - ), -); diff --git a/components/http/test/optimizer/parameters/slicer.test.ts b/components/http/test/optimizer/parameters/slicer.test.ts deleted file mode 100644 index e4f819e..0000000 --- a/components/http/test/optimizer/parameters/slicer.test.ts +++ /dev/null @@ -1,337 +0,0 @@ -import slicer from "../../../src/parameters/slicer.ts"; -import map from "../../../src/parameters/map.ts"; -import assert from "node:assert"; -import test from "node:test"; - -// "/test/:id" -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - slicer( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello"), - "hello", - ), -); - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - slicer( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/?a=1"), - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/"), - f("http://localhost:8080/test/hello/"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${slicer(map()({ path: "/test/:id/", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/?a=1"), - f("http://localhost:8080/test/hello/?a=1"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${slicer(map()({ path: "/test/:id/", f: (_) => "hello" }))}`, - )(), - ), -); - -// "/test/:id/hi" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - slicer( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi"), - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - slicer( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi?a=1"), - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi"), - f("http://localhost:8080/test/hello/hi"), - ], - ["hello", "hello"], - ) - )( - new Function(` return ${ - slicer( - map()({ - path: "/test/:id/hi", - f: (_) => "hello", - }), - ) - }`)(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi?a=1"), - f("http://localhost:8080/test/hello/hi?a=1"), - ], - ["hello", "hello"], - ) - )( - new Function(` return ${ - slicer( - map()({ - path: "/test/:id/hi", - f: (_) => "hello", - }), - ) - }`)(), - ), -); - -// "/test/:id/hi/" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - slicer( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi/"), - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - slicer( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi/?a=1"), - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi/"), - f("http://localhost:8080/test/hello/hi/"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${ - slicer( - map()({ - path: "/test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi/?a=1"), - f("http://localhost:8080/test/hello/hi/?a=1"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${ - slicer( - map()({ - path: "/test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )(), - ), -); - -// "/:test/:id/hi/" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - slicer( - map({ hasName: "http://localhost:8080/" })({ - path: "/:test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/hello/world/hi/"), - "hello/world", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - slicer( - map({ hasName: "http://localhost:8080/" })({ - path: "/:test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/hello/world/hi/?a=1"), - "hello/world", - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/hello/world/hi/"), - f("http://localhost:8080/hello/world/hi/"), - ], - ["hello/world", "hello/world"], - ) - )( - new Function( - ` return ${ - slicer( - map()({ - path: "/:test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/hello/world/hi/"), - f("http://localhost:8080/hello/world/hi/?a=1"), - ], - ["hello/world", "hello/world"], - ) - )( - new Function( - ` return ${ - slicer( - map()({ - path: "/:test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )(), - ), -); diff --git a/components/http/test/optimizer/parameters/unique.test .ts b/components/http/test/optimizer/parameters/unique.test .ts deleted file mode 100644 index e1869ee..0000000 --- a/components/http/test/optimizer/parameters/unique.test .ts +++ /dev/null @@ -1,303 +0,0 @@ -import one from "../../../src/parameters/unique.ts"; -import map from "../../../src/parameters/map.ts"; -import assert from "node:assert"; -import test from "node:test"; - -// "/test/:id" -console.log( - one(map()({ path: "/test/:id", f: (_) => "hello" })), -); - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello"), - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello?a=1"), - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello"), - f("http://localhost:8080/test/hello"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello?a=1"), - f("http://localhost:8080/test/hello?a=1"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id", f: (_) => "hello" }))}`, - )(), - ), -); - -// "/test/:id/" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/"), - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/?a=1"), - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/"), - f("http://localhost:8080/test/hello/"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/?a=1"), - f("http://localhost:8080/test/hello/?a=1"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/", f: (_) => "hello" }))}`, - )(), - ), -); - -// "/test/:id/hi" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi"), - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi?a=1"), - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi"), - f("http://localhost:8080/test/hello/hi"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/hi", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi?a=1"), - f("http://localhost:8080/test/hello/hi?a=1"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/hi", f: (_) => "hello" }))}`, - )(), - ), -); - -// "/test/:id/hi/" - -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi/"), - "hello", - ), -); -test( - "param", - (_) => - assert.deepStrictEqual( - new Function( - ` return ${ - one( - map({ hasName: "http://localhost:8080/" })({ - path: "/test/:id/hi/", - f: (_) => "hello", - }), - ) - }`, - )()("http://localhost:8080/test/hello/hi/?a=1"), - "hello", - ), -); - -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi/"), - f("http://localhost:8080/test/hello/hi/"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/hi/", f: (_) => "hello" }))}`, - )(), - ), -); -test( - "param", - (_) => - ( - (f) => - assert.deepStrictEqual( - [ - f("http://localhost:8080/test/hello/hi/?a=1"), - f("http://localhost:8080/test/hello/hi/?a=1"), - ], - ["hello", "hello"], - ) - )( - new Function( - ` return ${one(map()({ path: "/test/:id/hi/", f: (_) => "hello" }))}`, - )(), - ), -); diff --git a/components/http/test/optimizer/queries/elements.test.ts b/components/http/test/optimizer/queries/elements.test.ts deleted file mode 100644 index 106765d..0000000 --- a/components/http/test/optimizer/queries/elements.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import elements from "../../../src/queries/elements.ts"; - -test( - "queries", - () => - assert.deepStrictEqual( - elements(["hello"]), - '(p=>u=>(l=> l!==-1?p(u.slice(l)) :null)(u.indexOf("?")))(s => ({ hello: encodeURIComponent( (a =>a !== -1? (l => l !== -1 ? s.slice(a+6,l):s.slice(a+6,s.length))(s.indexOf("&",a)):null)(s.indexOf("hello=")) )}))', - ), -); - -test( - "queries", - () => - assert.deepStrictEqual( - elements(["hello", "hi"]), - '(p=>u=>(l=> l!==-1?p(u.slice(l)) :null)(u.indexOf("?")))(s => ({ hello: encodeURIComponent( (a =>a !== -1? (l => l !== -1 ? s.slice(a+6,l):s.slice(a+6,s.length))(s.indexOf("&",a)):null)(s.indexOf("hello=")) ), hi: encodeURIComponent( (a =>a !== -1? (l => l !== -1 ? s.slice(a+3,l):s.slice(a+3,s.length))(s.indexOf("&",a)):null)(s.indexOf("hi=")) )}))', - ), -); diff --git a/components/http/test/optimizer/queries/finder.test.ts b/components/http/test/optimizer/queries/finder.test.ts deleted file mode 100644 index ffebf5d..0000000 --- a/components/http/test/optimizer/queries/finder.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import finder from "../../../src/queries/finder.ts"; - -test( - "queries", - () => - assert.deepStrictEqual( - finder("hello"), - ' (a =>a !== -1? (l => l !== -1 ? s.slice(a+6,l):s.slice(a+6,s.length))(s.indexOf("&",a)):null)(s.indexOf("hello=")) ', - ), -); diff --git a/components/http/test/optimizer/resolve.test.ts b/components/http/test/optimizer/resolve.test.ts deleted file mode 100644 index 099d925..0000000 --- a/components/http/test/optimizer/resolve.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; - -import resolveComposer from "../../src/optimizer/resolveComposer.ts"; -import morphism from "../../src/optimizer/morphism.ts"; - -test( - "check for async", - async () => - assert.deepStrictEqual( - await resolveComposer()({ - first: { - f: async (f) => await (await f.req.blob()).text(), - }, - })( - new Request("http://hi.com/", { method: "POST", body: "hello" }), - ) as Record, - { first: "hello" }, - ), -); - -test( - "check for sync", - () => - assert.deepStrictEqual( - resolveComposer()({ - hi: { - f: () => "hello", - }, - })(new Request("http://hi.com/")), - { hi: "hello" }, - ), -); - -test( - "check for sync", - async () => - assert.deepStrictEqual( - await resolveComposer( - { mutable: { hi: "hello", res: new Response() } }, - )({ - resolve: { - f: (f) => f.mutable.hi, - }, - })(new Request("http://hi.com/")), - { resolve: "hello" }, - ), -); -test( - "check for async", - async () => - assert.deepStrictEqual( - await resolveComposer()({ - first: { - resolve: { - second: morphism()( - { - f: async (f) => (await f.req.blob()).text(), - }, - ), - }, - options: { - add: ["resolve"], - }, - f: (f) => f.resolve, - }, - })(new Request("http://hi.com/", { method: "POST", body: "hello" })), - { first: { second: "hello" } }, - ), -); - -test( - "check for sync", - () => - assert.deepStrictEqual( - resolveComposer()({ - first: { - resolve: { - second: morphism()({ - f: () => "hello", - }), - }, - f: (f) => f.resolve, - }, - })(new Request("http://hi.com/")), - { first: { second: "hello" } }, - ), -); diff --git a/components/http/test/optimizer/response1.test.ts b/components/http/test/optimizer/response1.test.ts deleted file mode 100644 index 7ef5540..0000000 --- a/components/http/test/optimizer/response1.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import response from "../../src/framework/optimizer/response.ts"; - -test( - "Response response", - async (_) => - assert.deepStrictEqual( - await (response()({ - path: "/hello/:id", - f: (f) => JSON.stringify(f.param), - })(new Request("http://localhost:8080/hello/hello"))).text(), - '{"id":"hello"}', - ), -); -test( - "Response response", - async (_) => - assert.deepStrictEqual( - await (response()({ - path: "/hello/:id", - type: "request", - f: (f) => new Response(JSON.stringify(f.param)), - })(new Request("http://localhost:8080/hello/hello"))).text(), - '{"id":"hello"}', - ), -); diff --git a/components/http/test/optimizer/static/composedPaths.test.ts b/components/http/test/optimizer/static/composedPaths.test.ts deleted file mode 100644 index 41a41e1..0000000 --- a/components/http/test/optimizer/static/composedPaths.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; - -import composer from "../../../src/framework/optimizer/staticFiles/composedPaths.ts"; - -test( - "hello", - () => - assert.deepStrictEqual( - "/fun.test.ts", - composer({ type: "fileServer", path: "./misc/", name: "/", mime: false })( - "./test/", - )("./")(["./test/fun.test.ts"])([])[0].path, - ), -); -test( - "hello", - () => - assert.deepStrictEqual( - "/fun.test.ts", - composer({ type: "fileServer", path: "./misc/", name: "/", mime: false })( - "./test/", - )("./")(["./test/fun.test.ts"])([[".ts", "null"]])[0] - .path, - ), -); diff --git a/components/http/test/optimizer/static/getDir.test.ts b/components/http/test/optimizer/static/getDir.test.ts deleted file mode 100644 index 2803b58..0000000 --- a/components/http/test/optimizer/static/getDir.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; - -import getDir from "../../../src/framework/optimizer/staticFiles/getDir.ts"; - -test( - "static", - () => - assert.deepStrictEqual( - getDir("./misc/").every((x) => x[0] === "." && x[1] === "/"), - true, - ), -); diff --git a/components/http/test/optimizer/static/getMime.test.ts b/components/http/test/optimizer/static/getMime.test.ts deleted file mode 100644 index 79fe2a1..0000000 --- a/components/http/test/optimizer/static/getMime.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; - -import getMime from "../../../src/framework/optimizer/staticFiles/getMime.ts"; - -test( - "test", - () => - assert.deepStrictEqual( - getMime([[".txt", "hello"]])(".hello"), - "text/html", - ), -); - -test( - "test", - () => - assert.deepStrictEqual( - getMime([[".hello", "hello"]])(".hello"), - "hello", - ), -); diff --git a/components/http/test/optimizer/static/main.test.ts b/components/http/test/optimizer/static/main.test.ts deleted file mode 100644 index eb05636..0000000 --- a/components/http/test/optimizer/static/main.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; -import main from "../../../src/framework/optimizer/staticFiles/main.ts"; - -test( - "test", - () => - assert.deepStrictEqual( - main({ type: "fileServer", path: "./misc/", name: "/hello", mime: false }) - .some((x) => x.path === "/hello/logo.png"), - true, - ), -); - -test( - "test", - () => - assert.deepStrictEqual( - main({ - type: "fileServer", - path: "./misc/", - name: "/hello", - mime: false, - removeExtensionOf: [".png"], - }) - .some((x) => x.path === "/hello/logo"), - true, - ), -); - -test( - "test", - () => - assert.deepStrictEqual( - main({ type: "fileServer", path: "./misc/", name: "/", mime: false }) - .some((x) => x.path === "/logo.png"), - true, - ), -); - -test( - "test", - () => - assert.deepStrictEqual( - main({ - type: "fileServer", - path: "./misc/", - name: "/hello/nested", - mime: false, - }).some((x) => x.path === "/hello/nested/logo.png"), - true, - ), -); - -test( - "test", - () => - assert.deepStrictEqual( - main({ - type: "fileServer", - path: "./misc/", - name: "/hello/nested", - mime: false, - template: [{ - checker: (s) => s.includes(".png"), - r: (options) => ({ - type: "response", - path: options.relativeName.slice(0, -4), - r: () => new Response(""), - }), - }], - }) - .some((x) => x.path === "/hello/nested/logo"), - true, - ), -); diff --git a/components/http/test/optimizer/static/mime.test.ts b/components/http/test/optimizer/static/mime.test.ts deleted file mode 100644 index ea5a4a3..0000000 --- a/components/http/test/optimizer/static/mime.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import assert from "node:assert"; -import test from "node:test"; - -import mime from "../../../src/framework/optimizer/staticFiles/mime.ts"; - -test( - "hello", - () => - assert.deepStrictEqual( - mime({ type: "fileServer", path: "./", name: "/hello/" }).length, - 74, - ), -); - -test( - "hello", - () => - assert.deepStrictEqual( - mime({ type: "fileServer", path: "./", name: "/hello/", mime: false }) - .length, - 0, - ), -); - -test( - "hello", - () => - assert.deepStrictEqual( - mime({ - type: "fileServer", - path: "./", - name: "/hello/", - extra: [[".ts", "hello/hello"]], - }).at(74), - [".ts", "hello/hello"], - ), -); diff --git a/components/http/test/util/paths.ts b/components/http/test/util/paths.ts deleted file mode 100644 index 899a634..0000000 --- a/components/http/test/util/paths.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Petition } from "../../src/framework/optimizer/types.ts"; - -export default [ - { type: "response", path: "/", r: (_) => new Response("GET:main") }, - { type: "response", path: "/one", r: (_) => new Response("1") }, - { type: "response", path: "/two", r: (_) => new Response("2") }, - { type: "response", path: "/three", r: (_) => new Response("3") }, - { type: "response", path: "/four", r: (_) => new Response("4") }, - { type: "response", path: "/five", r: (_) => new Response("5") }, - { type: "response", path: "/six", r: (_) => new Response("6") }, - { type: "response", path: "/test", r: (_) => new Response("GET:test") }, - { type: "response", path: "/test/", r: (_) => new Response("GET:test/") }, - { - type: "response", - path: "/test/:id/:name/", - r: (_) => new Response("GET:test/:id/:name/"), - }, - { - type: "response", - method: "POST", - path: "/", - r: (_) => new Response("POST:main"), - }, - { - type: "response", - method: "HEAD", - path: "/", - r: (_) => new Response("HEAD:main"), - }, - { - type: "response", - method: "DELETE", - path: "/", - r: (_) => new Response("DELETE:main"), - }, -] as Petition[]; diff --git a/components/http/test/util/url.ts b/components/http/test/util/url.ts deleted file mode 100644 index 6cc996d..0000000 --- a/components/http/test/util/url.ts +++ /dev/null @@ -1,16 +0,0 @@ -export default [ - [ - ["test", ""], - ["test/"], - ["test/"], - ], - [ - [""], - ], - [ - [""], - ], - [ - [""], - ], -] as string[][][]; diff --git a/docs/init.md b/docs/init.md deleted file mode 100644 index 99d0cf2..0000000 --- a/docs/init.md +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - -In the `versatile` world of JavaScript, wrap plays a key role in harmonizing the -language's polymorphic nature with Vixeny's functional approach. It ensures -scalability and maintains code purity, crucial for efficient web development. - -```ts -// name of this file: api.ts -import { wrap } from "vixeny"; -//routing options -import { options } from "somewhere.ts"; - -const api = wrap({ - //setting up options - ...options, - startWith: "/api", -})() - //creating a petition - .stdPetition({ - path: "/ping", - f: () => "pong", - }); - -export { api }; -``` - -Simplifing handling diverse HTTP requests, offering a structured, -side-effect-free programming environment. This makes building, maintaining, and -scaling web applications more intuitive and manageable, showcasing wrap as an -essential tool in the Vixeny toolkit. - -```ts -import { vixeny, wrap } from "vixeny"; -import { options } from "somewhere.ts"; -import { api } from "api.ts"; - -const router = wrap(options)() - .stdPetition({ - path: "/", - f: () => "hello world", - }) - //joining `api` to this wrap - .union(api.unwrap()) - // console logging: - // outputs: '/' - // '/api/ping' - .logPaths(); - -// unwrapping all the petitions giving them to the router -vixeny(options)(router.unwrap()); -``` - - - -Let's create a Petition without wrap and export it an create new differents -routes out of it. - -```ts -import { Petition } from "vixeny/optimizer/types"; - -const surprise: Petition = { - path: "/meow", - headings: { - status: 307, - statusText: "Temporary Redirect", - headers: { - Location: "https://www.youtube.com/watch?v=_e9yMqmXWo0", - }, - }, - f: (c) => "", -}; - -export { surprise }; -``` - -In another file: - -```ts -import { surprise } from "somewhere.ts"; - -export default wrap(options)() - .stdPetition(surprise) - .stdPetition({ ...surprise, path: "/woof" }) - .stdPetition({ ...surprise, path: "/woooow" }) - // console logging: - // outputs: '/meow' - // '/woof' - // '/woooow' - .logPaths(); -``` - -Applies to any other key in the object. - - - -There are two type of petitions: - -- `stdPetition`: where you have to return a `BodyInt` or `Promise` -- `customPetition`: where you have to return a `Response` or `Promise` - -```ts -wrap(options)() - .stdPetition({ - path: "/", - f: () => "hello world", - }) - .customPetition({ - path: "/response/who/:name", - f: (c) => new Response(c.param.name), - }); -``` - -It is important to note that `wrap` only supports these types although there are -more types which serve different purposes which must be directly inserted. - -```ts -vixeny(options)([ - //importing all the paths - ...wrap(options)() - .union(root.unwrap()) - .union(api.unwrap()) - .unwrap(), - //adding the static server - { - type: "fileServer", - path: "./public/", - name: "/public/", - }, - // petition without ctx - { - path: "/responseType", - type: "response", - r: () => new Response("Hello"), - }, -]); -``` diff --git a/docs/introduction.md b/docs/introduction.md deleted file mode 100644 index 8da8810..0000000 --- a/docs/introduction.md +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - -# Introduction: - -/// talk about vixeny in general - -## Bateries included: - -As simple as: - -```bash -npx create-vixeny -``` - -or: - -```bash -bun create vixeny -``` - -Templates for : - -- Pug -- Ejs -- Jsx -- Tsx -- Sass -- PostCSS -- Remaker and more ... - -## Testability end-to-end - -Check anything at any state - -```ts -const router = routes.testRequests(); - -test("Checking in `/`", async () => { - expect( - await router(new Request(serverName)) - .then((res) => res.status), - ).toStrictEqual(200); -}); -``` - -## Reusable - -Make your own life cycle with `resolve` - -```ts -import { isValidUser } from "../resolve.ts" - -export default wrap()() - .stdPetition({ - path: "/api/path1", - resolve:{ - user: isValidUser // <-- - } - f: (c) => c.resolve.user - ? "Hello: " + c.resolve.user.name - : "notFound", - }) - .stdPetition({ - path: "/api/path1", - resolve:{ - user: isValidUser // <-- - } - f: (c) => c.resolve.user - ? "Hello: " + c.resolve.user.name - : "notFound", - }) -``` - -## Clean an easy to transport - -Zero side effets - -```ts -import api from "./api.ts"; - -const main = wrap(o)() - .stdPetition({ - path: "/", - headings: { - headers: ".html", - }, - f: () => "hello world", - }) - .stdPetition({ - path: "/get/:id", - param: { - unique: true, - }, - f: (c) => c.param, - }); - -composeResponse()( - main.union( - api.unwrap(), - ).unwrap(), -); -``` diff --git a/docs/morphisim.md b/docs/morphisim.md deleted file mode 100644 index bc5a126..0000000 --- a/docs/morphisim.md +++ /dev/null @@ -1,372 +0,0 @@ - - - - - - -# Demystifying Vixeny: Your Guide to Building Digital Experiences - -Imagine Vixeny guiding you through the process of crafting your dream home, -simplifying the complexities of web development into something straightforward -and manageable. It employs two key concepts —`Resolve` and `Branch`— much like -the essential steps in building a house, laying the foundation and then -expanding upon it to add the final touches. - -## Foundations First: Resolve - -`Resolve` prepares your site, organizing all the essentials before construction -starts. It's like getting the land ready, ensuring materials are on-site. This -includes setting up data and permissions in advance, guaranteeing that when it's -time to build, everything's in place. - -## Adding Features: Branch - -Once the basics are set, `Branch` lets you customize, adding unique features to -your site. Think of it as choosing room designs or adding a garden, enhancing -your home's functionality and appeal without starting over. - -## Building with Intention: The Vixeny Way - -Vixeny’s approach mirrors thoughtful home construction, emphasizing: - -- **Reusability**: Modular design lets you replicate and adapt features easily, - akin to using a beloved room design throughout your home. -- **Purity**: Ensuring operations are clean and predictable, much like choosing - quality materials for your home, Vixeny keeps your project stable and - reliable. -- **Testing for the Future**: Mocking tests different scenarios, preparing your - site for future needs—just as planning for potential home additions or - changes. - -## Morphism - -### Resolve and Branch - -Now, let's talk more about the Optimizer in Vixeny; after identifying all -necessary functions, orchestrates the composition of petitions. This process -intricately weaves together all resolves and branches, both of which are -considered types of morphisms. Interestingly, a petition in itself is -conceptualized as a morphism. This highlights the functional and compositional -core of Vixeny, where both resolves and branches play pivotal roles: - -## Resolve - -- **Definition**: In any morphism, a `resolve` guarantees its resolution prior - to the execution of the petition's main function (`f`). - - ```ts - wrap(options)() - .stdPetition({ - path: "/withResolve", - resolve: { - hi: { c: () => "Hello world" }, - }, - // This petition consistently outputs "Hello world" - f: (c) => c.resolve.hi, - }); - ``` - -- **Asynchronous Functions**: Asynchronous resolves maintain the state of the - morphism, meaning the asynchronous result is merged without altering the - original state. - - ```ts - wrap()() - .stdPetition({ - path: "/withResolveAsync", - resolve: { - hi: { async c: () => await Promise.resolve("Hello world") } - }, - // Note: The function in `f` is synchronous - f: (c) => c.resolve.hi, - }) - ``` - -- **Execution Order**: All resolves are executed and completed prior to their - integration into the `CTX`, ensuring their resolved outputs are accessible - within the `CTX` for the petition's logic. - - ```ts - wrap(options)() - .stdPetition({ - path: "/helloWorld", - resolve: { - hello: { async f: () => await Promise.resolve("Hello") }, - world: { f: () => 'world' } - }, - f: c => `${c.resolve.hello} ${c.resolve.world}`, - }) - ``` - -- **Uniqueness of `CTX`**: With the exception of `mutable`, each `CTX` instance - remains unique and isolated, ensuring petitions remain decoupled. - - ```ts - wrap(options)() - .stdPetition({ - path: "/date", - resolve: { - date: morphism(o)({ f: (c) => c.date }), - }, - f: (c) => c.resolve.date !== c.date ? "Always true" : "Unreachable", - }); - ``` - -- **Nested Resolves and Branches**: Vixeny supports an unlimited nesting of - resolves and branches within each other. - - ```ts - const hello = morphism(options)({ - resolve: { - nestedHello: { - f: () => "hello world", - }, - }, - f: (c) => c.resolve.nestedHello, - }); - - wrap(options)() - .stdPetition({ - path: "/hello", - resolve: { - hello: hello, - }, - f: (c) => c.resolve.hello, - }); - ``` - -In Vixeny, `f` stands for `functor`, because: `functor preserve structure`. This -concept underscores how resolves and the main function interact while -maintaining the integrity of the petition's structure. - -### Branch - -In Vixeny, a `Branch` is used to incorporate additional logic or operations -within the execution of a petition,which has its own `CTX`. Branches, like -resolves, are morphisms but are specifically designed to execute alongside or -within the main function (`f`) of a petition, offering a direct way to extend -functionality without cluttering the primary logic. - -### 1. Defining a Simple Branch - -A branch can be as simple as a function that returns a static message. This -example demonstrates how to define and use a simple branch within a petition: - -```ts -const helloBranch = morphism(options)({ - f: (c) => "Hello from branch", -}); - -wrap(options)() - .stdPetition({ - path: "/helloBranch", - branch: { - hello: helloBranch, - }, - f: (c) => new Response(c.branch.hello(null)), - }); -``` - -- **Explanation**: Here, `helloBranch` is defined as a morphism with a function - (`f`) that returns a static string. Within the petition, this branch is - invoked, and its return value is used to construct a response. - -### 2. Branch with Parameters - -Branches can also accept parameters, making them dynamic in their operation. -This example illustrates a branch that utilizes parameters from the `CTX`: - -```ts -const greetUserBranch = morphism()({ - f: (c) => `Hello, ${c.arguments.name}`, -}); - -wrap(options)() - .stdPetition({ - path: "/greet/:name", - branch: { - greetUser: greetUserBranch, - }, - f: (c) => new Response(c.branch.greetUser({ name: c.param.name })), - }); -``` - -- **Explanation**: `greetUserBranch` takes a name parameter through - `c.arguments` and returns a personalized greeting. The branch is executed in - the petition's main function, using the name parameter extracted from the URL. - -### 3. Asynchronous Branch - -Branches can perform asynchronous operations, such as fetching data from a -database or an external API: - -```ts -const fetchUserDataBranch = morphism(options)({ - async f: (c) => { - const userId = c.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 (c) => { - const userData = await c.branch.fetchUserData({ userId: c.param.userId }); - return new Response(JSON.stringify(userData)); - }, - }) -``` - -### Explaining `Mutable` in Context - -- **Mutability Concept**: In this scenario, `Mutable` refers to the ability of - the context (`c`) to have its state changed and those changes preserved across - subsequent transformations or operations. - -- **Implementation with `mutStdPetition`**: By using `mutStdPetition`, adds the - key `mutable` which persistent. This allows subsequent operations or morphisms - to access and further modify the updated state. Specifically, any property - added to `c.mutable` becomes a preserved state that can be referenced or - altered in later stages of processing. - -### Practical Example - -```ts -// Initial operation with mutStdPetition, introducing a mutable context -.mutStdPetition({ - // Configuration and initial transformation - resolve: { - // Use morphism to modify and use the mutable state - randomNumber: morphism(options)({ f : c => - { - c.mutable.randomNumber = c.randomNumber; - return c.randomNumber; - } - ) - }, - // Subsequent operation accessing and evaluating the mutable state - f: c => c.mutable.randomNumber !== c.randomNumber - ? 'Condition based on mutable state' - : 'Potentially reachable if state matches' -}) -``` - -In this example, `c.mutable.randomNumber` is set in one operation, and this -state is then available for comparison in the next operation. This demonstrates -how `Mutable` allows for complex, state-dependent logic to be implemented across -a sequence of morphisms, leveraging the state preserved in `c.mutable`. - -It's important to notice that `mutStdPetition` and `stdPetition` are composed -differently and a pure state like `stdPetition` is totally independent of -`mutStdPetition` - -### Purpose of morphism - -`morphism` functions are designed to facilitate explicit data transformation in -a type-safe manner, especially useful in scenarios where TypeScript's type -inference capabilities are stretched thin by deep nesting or complex operations. -By breaking down transformations into manageable, clearly defined steps, -`morphism` ensures that each operation is both comprehensible and type-safe. - -### TypeScript's Typing Limitation - -TypeScript's type system, while robust, cannot always effectively manage or -infer types in deeply nested or highly complex transformations. This limitation -can lead to challenges in enforcing type safety, particularly in applications -that require detailed manipulation of nested data structures. - -### Practical Application - -To explain the provided code snippets effectively, let's break down the concept -of morphisms as used here, especially - -#### Hello Morphism - -The first morphism, `hello`, is defined to perform no direct transformation but -specifies a nested `resolve` structure with a static value and a function that -returns a string "hello". The final function `f` extracts the `nested` part of -the resolved structure. - -```ts -const hello = morphism(options)({ - resolve: { - //resolves first - nested: { - crypto: { - globalKey: "your secret", - }, - f: () => "hello", - }, - }, - // showing inference from a nested resolve - f: (f) => f.resolve.nested, -}); -``` - -#### NestedHello Morphism - -The `nestedHello` morphism wraps the `hello` morphism within its `resolve`, -showing how morphisms can be nested or chained to build upon the results of -previous transformations. - -```ts -const nestedHello = morphism(options)({ - resolve: { - hello: hello, - }, - // showing inference from a nested resolve - f: (f) => f.resolve.hello, -}); -``` - -#### isValidUser Morphism - -This morphism validates a JWT token by checking if it exists and if its `iat` -(issued at time) is before the current time. It showcases how to perform -conditional checks within a morphism. - -```ts -const isValidUser = morphism(options)({ - crypto: { - globalKey: "your secret", - token: { - jwtToken: {}, - }, - }, - f: (c) => - c.token.jwtToken && - (c.token.jwtToken as { name: string; iat: number }).iat < Date.now() - ? c.token.jwtToken - : null, -}); -``` - -#### Please Morphism - -Finally, the `check` morphism demonstrates how to resolve dependencies using -previous morphisms (`isValidUser`) and process the result further. - -```ts -const please = morphism(options)({ - resolve: { - check: isValidUser, - }, - f: (c) => c.resolve.check !== null ? "Valid token" : "Invalid token", -}); -``` - -### Key Concepts - -- **Branching and Resolving**: These operations allow for the construction of - complex data transformation pipelines. Branching can be seen as defining - various paths of data flow and logic, while resolving handles the actual - execution of these paths to produce a final outcome. -- **Modularity**: Morphisms can be nested and reused, promoting modularity and - reuse of logic. -- **Declarative Logic**: The structure allows for a clear, declarative - definition of data transformations and conditions, improving readability and - maintainability. diff --git a/docs/routing.md b/docs/routing.md deleted file mode 100644 index feaa0d2..0000000 --- a/docs/routing.md +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - -# Wrap - -### Union - -One of the most important uses of `wrap` in Vixeny is to protect and modularize -`Petitions`, especially when they are exported or modified. This ensures that -your code remains organized and maintainable. Let's demonstrate this with an -example involving multiple files: - -First, create a file named `extension.ts`: - -```ts -//file: extension.ts -export default wrap()() - .stdPetition({ - path: "/", - f: () => "helloWorld", - }); -``` - -Next, create two separate files, `a.ts` and `b.ts`, which import and utilize -`extension.ts`: - -```ts -import extencion from "./extension.ts"; - -// file: a.ts -export default wrap()() - .union(extension.unwrap()) - .stdPetition({ - path: "/hello", - f: () => "helloWorld", - }) - // console logging - // outputs: "/" - // "/hello" - .logPaths(); -``` - -```ts -import extension from "./extension.ts"; - -// file: b.ts -export default wrap()() - .union(extension.unwrap()) - .stdPetition({ - path: "/api", - f: () => "helloWorld", - }) - // console logging - // outputs: "/" - // "/api" - .logPaths(); -``` - -Additionally, you can add `extension` to its second curried function -(`wrap()(here)`) and modify the `base`. This allows for further customization of -the routing: - -```ts -import extension from "./extension.ts"; - -export default wrap()( - extension.union( - // changing the start path of the wrap `extension` - extension.changeOptions({ "startWith": "/api" }) - .unwrap(), - ), -) - .stdPetition({ - path: "/", - f: () => "helloWorld", - }) - // console logging - // outputs: "/api/" - // "/" - .logSize(); -``` - - - -In the `versatile` world of JavaScript, wrap plays a key role in harmonizing the -language's polymorphic nature with Vixeny's functional approach. It ensures -scalability and maintains code purity, crucial for efficient web development. - -```ts -// name of this file: api.ts -import { wrap } from "vixeny"; -//routing options -import { options } from "somewhere.ts"; - -const api = wrap({ - //setting up options - ...options, - startWith: "/api", -})() - //creating a petition - .stdPetition({ - path: "/ping", - f: () => "pong", - }); - -export { api }; -``` - -Simplifing handling diverse HTTP requests, offering a structured, -side-effect-free programming environment. This makes building, maintaining, and -scaling web applications more intuitive and manageable, showcasing wrap as an -essential tool in the Vixeny toolkit. - -```ts -import { vixeny, wrap } from "vixeny"; -import { options } from "somewhere.ts"; -import { api } from "api.ts"; - -const router = wrap(options)() - .stdPetition({ - path: "/", - f: () => "hello world", - }) - //joining `api` to this wrap - .union(api.unwrap()) - // console logging: - // outputs: '/' - // '/api/ping' - .logPaths(); - -// unwrapping all the petitions giving them to the router -vixeny(options)(router.unwrap()); -``` - - - -Let's create a Petition without wrap and export it an create new differents -routes out of it. - -```ts -const surprise = { - path: "/meow", - headings: { - status: 307, - statusText: "Temporary Redirect", - headers: { - Location: "https://www.youtube.com/watch?v=_e9yMqmXWo0", - }, - }, - f: (c) => "", -}; - -export { surprise }; -``` - -In another file: - -```ts -import { surprise } from "somewhere.ts"; - -export default wrap(options)() - .stdPetition(surprise) - .stdPetition({ ...surprise, path: "/woof" }) - .stdPetition({ ...surprise, path: "/woooow" }) - // console logging: - // outputs: '/meow' - // '/woof' - // '/woooow' - .logPaths(); -``` - -Applies to any other key in the object. - - - -There are two type of petitions: - -- `stdPetition`: where you have to return a `BodyInt` or `Promise` -- `customPetition`: where you have to return a `Response` or `Promise` - -```ts -wrap(options)() - .stdPetition({ - path: "/", - f: () => "hello world", - }) - .customPetition({ - path: "/response/who/:name", - f: (c) => new Response(c.param.name), - }); -``` - -It is important to note that `wrap` only supports these types although there are -more types which serve different purposes which must be directly inserted. - -```ts -vixeny(options)([ - //importing all the paths - ...wrap(options)() - .union(root.unwrap()) - .union(api.unwrap()) - .unwrap(), - //adding the static server - { - type: "fileServer", - path: "./public/", - name: "/public/", - }, - // petition without ctx - { - path: "/responseType", - type: "response", - r: () => new Response("Hello"), - }, -]); -``` diff --git a/docs/testing.md b/docs/testing.md deleted file mode 100644 index 397a144..0000000 --- a/docs/testing.md +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - -## Testing: The Keystone of Digital Construction - -In building, thorough testing and inspections are non-negotiable, ensuring every -part of the structure meets exacting standards. Vixeny embeds this principle -deeply within its framework, offering a comprehensive testing suite that -simulates various scenarios, ensuring every component functions as intended. - -### Case Study: Simulating Conditions - -Imagine a feature in our digital architecture that responds to environmental -changes, much like a smart home system adjusting to weather variations. Vixeny -enables us to test these dynamic responses, ensuring they act precisely under -varied simulated conditions. - -#### Scenario: Predicting Outcomes - -Our focus falls on a `/random` feature, designed to yield different outcomes -based on a random number generator. This setup mirrors real-life scenarios where -outcomes pivot on user interactions or external data. - -```ts -const routes = wrap(options)() - .stdPetition({ - path: "/random", - f: (c) => - c.randomNumber > .5 - ? c.randomNumber > .99 ? "winner" : "almost" - : "try again", - }); -``` - -#### Crafting the Tests - -To ensure our feature behaves predictably across a range of states, we -manipulate the random number generator, crafting scenarios to test the -anticipated outcomes. - -```ts -test("/random", async () => { - // Testing for the "try again" outcome. - expect( - await routes - .handleRequest("/random")({ - options: { - setRandomNumber: .25, - }, - })(new Request("/random")) - .then((res) => res.text()), - ).toStrictEqual("try again"); - - // Testing for the "almost" outcome. - expect( - await routes - .handleRequest("/random")({ - options: { - setRandomNumber: .51, - }, - })(new Request("/random")) - .then((res) => res.text()), - ).toStrictEqual("almost"); - - // Testing for the "winner" outcome. - expect( - await routes - .handleRequest("/random")({ - options: { - setRandomNumber: .999, - }, - })(new Request("/random")) - .then((res) => res.text()), - ).toStrictEqual("winner"); -}); -``` - -### Refining the Process with Mocked Resolves - -For a feature reliant on external APIs, like fetching current weather data, we -aim to test without real-time data fetches. Here, we introduce a mocked -synchronous function to simulate the data fetching: - -```ts -// Original asynchronous resolve function for fetching weather data -const routes = wrap(options)() - .stdPetition({ - path: "/weather", - resolve: { - currentWeather: { - async f: () => { - return await fetch("https://api.weather.com/current").then(res => res.json()); - } - } - }, - f: (c) => { - return c.resolve.currentWeather.temperature > 75 ? "It's warm outside" : "It's cool outside"; - }, - }); - -// Mocking the resolve function for controlled testing -const mockedWeatherResolve = () => ({ temperature: 80 }); - -// Injecting the mocked resolve using handleRequest -const mockRoutes = routes.handleRequest("/weather")({ - resolve: { - currentWeather: mockedWeatherResolve - } -}); - -// Verifying behavior with the mocked data -test("/weather", async () => { - expect( - await mockRoutes(new Request("/weather")) - .then(res => res.text()) - ).toStrictEqual("It's warm outside"); -}); -``` - -### The Virtues of Mocked Testing - -This approach grants us unparalleled control over the testing environment, -mirroring the precision and adaptability seen in the most advanced construction -methodologies. It allows for: - -- **Precision and Predictability**: Creating a controlled environment that - simulates specific conditions, ensuring components react as expected. -- **Efficiency**: Streamlining the testing process by removing dependencies on - external data sources. -- **Confidence**: Providing assurance in the reliability and functionality of - the digital structure, much like the final inspection before a home's - completion. - -## Conclusion: Building Digital Foundations with Assurance - -Just as thorough inspections ensure a home is ready for habitation, Vixeny's -testing capabilities, particularly the use of mocked resolves, ensure that every -digital component is built to perfection. This meticulous approach to testing -empowers developers to construct with confidence, knowing every part of the -application is crafted to meet and exceed expectations, ensuring a robust, -functional, and adaptable digital experience. Through Resolve, Branch, and -precise testing, Vixeny lays the groundwork for web applications that stand the -test of time, embodying the principles of a well-constructed home. - -Thank you for your time. diff --git a/docs/wrap.md b/docs/wrap.md deleted file mode 100644 index f2d0c4f..0000000 --- a/docs/wrap.md +++ /dev/null @@ -1,163 +0,0 @@ - - - - - -# Wrap - -### Union - -One of the most important uses of `wrap` in Vixeny is to protect and modularize -`Petitions`, especially when they are exported or modified. This ensures that -your code remains organized and maintainable. Let's demonstrate this with an -example involving multiple files: - -First, create a file named `extension.ts`: - -```ts -//file: extension.ts -export default wrap()() - .stdPetition({ - path: "/", - f: () => "helloWorld", - }); -``` - -Next, create two separate files, `a.ts` and `b.ts`, which import and utilize -`extension.ts`: - -```ts -import extencion from "./extension.ts"; - -// file: a.ts -export default wrap()() - .union(extension.unwrap()) - .stdPetition({ - path: "/hello", - f: () => "helloWorld", - }) - // console logging - // outputs: "/" - // "/hello" - .logPaths(); -``` - -```ts -import extension from "./extension.ts"; - -// file: b.ts -export default wrap()() - .union(extension.unwrap()) - .stdPetition({ - path: "/api", - f: () => "helloWorld", - }) - // console logging - // outputs: "/" - // "/api" - .logPaths(); -``` - -Additionally, you can add `extension` to its second curried function -(`wrap()(here)`) and modify the `base`. This allows for further customization of -the routing: - -```ts -import extension from "./extension.ts"; - -export default wrap()( - extension.union( - // changing the start path of the wrap `extension` - extension.changeOptions({ "startWith": "/api" }) - .unwrap(), - ), -) - .stdPetition({ - path: "/", - f: () => "helloWorld", - }) - // console logging - // outputs: "/api/" - // "/" - .logPaths(); -``` - -### Optimizer - -In Vixeny, the `optimizer` is a crucial function that oversees the `CTX` in `f`, -composes the petition, chains `resolve` and `branch`, and efficiently handles -all asynchronous and synchronous functions. But what does this mean? Let's first -explore the `CTX`, which in TypeScript shows you all the native functions -(including plugins, which are not included here): - -- `"req"`: Gives access to the `Request`. -- `"query"`: Retrieves the query parameters. -- `"param"`: Fetches the URL parameters. -- `"date"`: Returns the current date. -- `"randomNumber"`: Generates a random number from 0 to 1. -- `"hash"`: Produces a random hash (string). -- `"cookie"`: Accesses cookies. -- `"resolve"`: Resolves any `morpishim` before the execution of the current - `CTX`. -- `"branch"`: A function within `CTX` that is also a `morpishim` and it has its - own `CTX`. -- `"mutable"`: Adds a fixed point to all `morpishim` in the `petition`, - regardless of depth. -- `"arguments"`: Arguments passed to `branch`. -- `"token"`: (requires `crypto`) Checks and parses a cookie with the given key. -- `"sign"`: (requires `crypto`) Signs a JSON. -- `"verify"`: (requires `crypto`) Verifies and parses a string. - -However, none of these are actually included in the `CTX` by default. The -`Optimizer` analyzes (tokenizes `f` and checks for the keys used) your petition -and adds only what's required. If there's nothing required, it simply handles -the `Request`. - -```ts -export default wrap()() - .stdPetition({ - path: "/", - f: () => "helloWorld", - }) - // console logging - // outputs: [] - .logLastCheck() - .stdPetition({ - path: "/hello/:id", - f: (c) => c.param.id, - }) - // console logging - // outputs: ["param"] - .logLastCheck(); -``` - -There are some limitations to this, for example, the optimizer cannot understand -what functions outside of the context need, but you can manually add them: - -```ts -export default wrap()() - .stdPetition({ - path: "/hello/query1", - f: (c) => functionOutsideOfContext(c), - }) - // console logging - // outputs: [] - .logLastCheck() - .stdPetition({ - path: "/hello/query2", - f: (c) => functionOutsideOfContext(c), - options: { - add: ["query"], - }, - }) - // console logging - // outputs: ["query"] - .logLastCheck(); -``` - -You have three options for customization: - -- `only`: Bypasses the optimizer, adding only the requested functions. -- `add`: Adds specified functions to the list. -- `remove`: Removes a function if it is added but not required, which could - happen but is harmless to the `CTX`. diff --git a/misc/buildMD.mjs b/misc/buildMD.mjs deleted file mode 100644 index 9d7b05c..0000000 --- a/misc/buildMD.mjs +++ /dev/null @@ -1,58 +0,0 @@ -import findAllFiles from "../components/files/findAllFiles.mjs"; -import { duration } from "mitata/reporter/fmt.mjs"; -import { readFileSync, writeFileSync } from "node:fs"; - -function summaryToMarkdown(benchmarks) { - benchmarks = benchmarks.filter((b) => !b.error); - benchmarks.sort((a, b) => a.stats.avg - b.stats.avg); - const baseline = benchmarks.find((b) => b.baseline) || benchmarks[0]; - - let output = `## **Summary** ${ - baseline.group ? `for *${baseline.group}*` : "" - }\n\n`; - output += `### **${baseline.name}** \n\n`; - - benchmarks.filter((b) => b !== baseline).forEach((b) => { - const diff = Number((1 / baseline.stats.avg * b.stats.avg).toFixed(2)); - const indicator = diff > 1 ? "faster" : "slower"; - const color = diff > 1 ? "green" : "red"; - output += - `- ${diff}x **${indicator}** than *${b.name}*\n`; - }); - - return output; -} - -const parserd = (json) => - `\n# ${json.runtime}\n` + - Array.from( - json.benchmarks.reduce( - (acc, benchmark) => acc.add(benchmark.group), - new Set(), - ), - ).map((groupName) => - json.benchmarks.filter((benchmark) => benchmark.group === groupName).filter( - (x) => "stats" in x, - ) - ).map((groupBenchmarks) => - `\n## ${groupBenchmarks[0].group}\n` + - "| Name | Time (Avg) | Range | p75 | p99 | p995 |\n|------|------------|-------|-----|-----|------|\n" + - [ - ...groupBenchmarks.map((y, i) => - `| ${y.name} | ${duration(y.stats?.avg ?? 0)} | (${ - duration(y.stats?.min ?? 0) - } .. ${duration(y.stats?.max ?? 0)}/iter) | ${ - duration(y.stats?.p75 ?? 0) - } | ${duration(y.stats?.p99 ?? 0)} | ${duration(y.stats?.p995 ?? 0)} |` - ), - ].join("\n") + summaryToMarkdown(groupBenchmarks) + "\n\n" - ).join("\n\n"); - -findAllFiles("./bench/json/") - .map((x) => x[0]) - .forEach((x) => - writeFileSync( - "./bench/results/" + x.split("/").at(-1) + ".md", - parserd(JSON.parse(readFileSync(x))), - ) - ); diff --git a/package.json b/package.json index 67b1899..8cb8f90 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "vixeny", - "version": "0.0.955", + "version": "0.1.0", "description": "A functional router for Bun and Deno", "main": "main.ts", "directories": { "test": "test" }, "scripts": { - "test": "deno test -A ./" + "test": "deno test -A ./test/*" }, "repository": { "type": "git", @@ -32,7 +32,9 @@ "homepage": "https://vixeny.dev/", "devDependencies": { "bun-types": "^1.0.2", - "jose": "^4.14.6", "mitata": "^0.1.6" + }, + "peerDependencies": { + "typescript": "^5.0.0" } } diff --git a/src/components/cookieToToken/cookieToTokenBodyParser.ts b/src/components/cookieToToken/cookieToTokenBodyParser.ts new file mode 100644 index 0000000..36743e1 --- /dev/null +++ b/src/components/cookieToToken/cookieToTokenBodyParser.ts @@ -0,0 +1,6 @@ +export default (ar: string[]) => + new Function( + ` return (${ar.reduce((acc, x) => acc + x + "=>", "")}r => ({${ + ar.map((x) => ` ${x}: ${x}(r)`).join(",") + }}))`, + )(); diff --git a/src/components/cookieToToken/cookieToTokenFilter.ts b/src/components/cookieToToken/cookieToTokenFilter.ts new file mode 100644 index 0000000..2516454 --- /dev/null +++ b/src/components/cookieToToken/cookieToTokenFilter.ts @@ -0,0 +1,28 @@ +export default (elements: string[]) => (input: string) => + elements + .filter((x, index, arr) => + index === 0 || + (!arr[index - 1].startsWith("resolve.") && + !arr[index - 1].startsWith("branch.")) + ) + .map((element) => + // If the element matches the input and the next element exists + element.indexOf(input) !== -1 + ? element.slice( + element + .indexOf(input) + input.length + 1, + (element + .indexOf(".", element.indexOf(input) + input.length + 1) + + 1 || element.length + 1) - 1, + ) + : null + ) + .filter((x) => x !== null) + .filter((x) => x !== "") + .sort() + .filter((v, i, a) => !i || v !== a[i - 1]) + .reduce( + (acc, v) => + acc.includes(v as string) ? acc : acc.concat(v as unknown as string[]), + [] as string[], + ); diff --git a/src/components/cookieToToken/cookieToTokenGets.ts b/src/components/cookieToToken/cookieToTokenGets.ts new file mode 100644 index 0000000..e3f59e6 --- /dev/null +++ b/src/components/cookieToToken/cookieToTokenGets.ts @@ -0,0 +1,24 @@ +import verifySha256 from "../jwt/verifySha256.mjs"; +import type { SupportedKeys } from "../../morphism.ts"; + +export default (secret: SupportedKeys) => (name: string) => + ( + (sha256) => (c: string | null) => + ( + (p) => + p !== -1 + ? sha256( + //@ts-ignore + c.slice( + p + name.length + 1, + //@ts-ignore + (c.indexOf(",", p) + 1 || c.length + 1) - 1, + ), + ) + : null + )( + typeof c === "string" ? c.indexOf(name + "=") : -1, + ) + )( + verifySha256()(secret), + ); diff --git a/src/components/cookieToToken/cookieToTokenMain.ts b/src/components/cookieToToken/cookieToTokenMain.ts new file mode 100644 index 0000000..18fb762 --- /dev/null +++ b/src/components/cookieToToken/cookieToTokenMain.ts @@ -0,0 +1,36 @@ +import type { FunRouterOptions } from "../../options.ts"; +import type { Petition, SupportedKeys } from "../../morphism.ts"; +import cookieToTokenBodyParser from "./cookieToTokenBodyParser.ts"; +import cookieToTokenGets from "./cookieToTokenGets.ts"; +import cookieToTokenFilter from "./cookieToTokenFilter.ts"; + +export default (o?: FunRouterOptions) => (f: Petition) => + f.crypto && "globalKey" in f.crypto + ? ( + (getCookies) => + "token" in f.crypto + ? ((s: SupportedKeys) => (arr: string[]) => + arr.reduce( + (acc, x) => acc(cookieToTokenGets(s)(x)), + cookieToTokenBodyParser(arr), + ))(f.crypto.globalKey)( + //@ts-ignore + Object.keys(f.crypto.token), + ) + : getCookies.length > 0 + ? ((s: SupportedKeys) => (arr: string[]) => + arr.reduce( + (acc, x) => acc(cookieToTokenGets(s)(x)), + cookieToTokenBodyParser(arr), + ))(f.crypto.globalKey)( + getCookies, + ) + : void console.error("No token found, please use 'token' ") ?? + (() => null) + )( + cookieToTokenFilter( + f.f.toString() + .split(" "), + )("token"), + ) + : () => ({ SystemError: "Crypto is requieres" }); diff --git a/src/components/cookies/main.ts b/src/components/cookies/main.ts new file mode 100644 index 0000000..2f37732 --- /dev/null +++ b/src/components/cookies/main.ts @@ -0,0 +1,12 @@ +export default (_o: any) => (s: string) => + s + ? Object.fromEntries( + s + .split("; ") + .filter((x) => + x + .indexOf("=") !== -1 + ) + .map((x) => x.split("=")), + ) + : null; diff --git a/src/components/cors/mainCORS.ts b/src/components/cors/mainCORS.ts new file mode 100644 index 0000000..85048ad --- /dev/null +++ b/src/components/cors/mainCORS.ts @@ -0,0 +1,13 @@ +import type { CORSOptions } from "./types.ts"; + +export const parse = () => (o: CORSOptions) => + Object.keys(o).reduce( + //@ts-ignore + (acc, key) => ({ ...acc, [key]: o[key].toString() }), + {}, + ); + +export const stringToFunction = (obj: { [key: string]: string }) => + (new Function(`return () => (${JSON.stringify(obj)})`))() as () => { + [key: string]: string; + }; diff --git a/src/components/cors/types.ts b/src/components/cors/types.ts new file mode 100644 index 0000000..dac48e0 --- /dev/null +++ b/src/components/cors/types.ts @@ -0,0 +1,55 @@ +type RequestMethod = + | "ACL" + | "BIND" + | "CHECKOUT" + | "CONNECT" + | "COPY" + | "DELETE" + | "GET" + | "HEAD" + | "LINK" + | "LOCK" + | "M-SEARCH" + | "MERGE" + | "MKACTIVITY" + | "MKCALENDAR" + | "MKCOL" + | "MOVE" + | "NOTIFY" + | "OPTIONS" + | "PATCH" + | "POST" + | "PRI" + | "PROPFIND" + | "PROPPATCH" + | "PURGE" + | "PUT" + | "REBIND" + | "REPORT" + | "SEARCH" + | "SOURCE" + | "SUBSCRIBE" + | "TRACE" + | "UNBIND" + | "UNLINK" + | "UNLOCK" + | "UNSUBSCRIBE"; + +export type CORSOptions = { + allowOrigins?: string | string[]; + allowMethods?: string | RequestMethod | RequestMethod[]; + exposeHeaders?: string | string[]; + maxAge?: number; + allowCredentials?: boolean; + allowHeaders?: string | string[]; + appendHeaders?: boolean; +}; + +type HeadersName = + | "Access-Control-Max-Age" + | "Access-Control-Allow-Credentials" + | "Access-Control-Allow-Headers" + | "Access-Control-Expose-Headers" + | "Access-Control-Allow-Methods" + | "Vary" + | "Access-Control-Allow-Origin"; diff --git a/src/components/encode/b64UrlTob64.mjs b/src/components/encode/b64UrlTob64.mjs new file mode 100644 index 0000000..0f80707 --- /dev/null +++ b/src/components/encode/b64UrlTob64.mjs @@ -0,0 +1,11 @@ +export default () => (b64url) => + !/^[-_A-Z0-9]*?={0,2}$/i.test(b64url) ? null : ( + (l) => + l === 2 + ? b64url.replace(/-/g, "+").replace(/_/g, "/") + "==" + : l === 3 + ? b64url.replace(/-/g, "+").replace(/_/g, "/") + "=" + : l === 0 + ? b64url.replace(/-/g, "+").replace(/_/g, "/") + : null + )(b64url.length % 4); diff --git a/src/components/encode/b64toUrl.mjs b/src/components/encode/b64toUrl.mjs new file mode 100644 index 0000000..1c73580 --- /dev/null +++ b/src/components/encode/b64toUrl.mjs @@ -0,0 +1,28 @@ +import bun from "./src/base64toUrl/convertBase64ToBase64urlBun.mjs"; +import deno from "./src/base64toUrl/convertBase64ToBase64urlDeno.mjs"; +import node from "./src/base64toUrl/convertBase64ToBase64urlNode.mjs"; +import name from "../runtime/name.mjs"; + +/** + * Selects and returns a function to convert Base64 strings to Base64Url strings based on the runtime environment. + * + * The function checks the runtime (Bun, Deno, or Node) and returns the respective conversion function. If the runtime + * is not Bun or Deno, it defaults to Node. + * + * @function + * @returns {Function} A function that takes a Base64 string and returns its Base64Url representation. + * + * @example + * const base64ToBase64Url = unwrapBase64ToBase64UrlFunction(); + * const myBase64UrlString = base64ToBase64Url(myBase64String); + * + * @note + * The function relies on the `name` module to determine the runtime environment. Ensure the module correctly detects your environment. + */ + +export default () => + ( + (rt) => rt === "Bun" ? bun() : rt === "Deno" ? deno() : node() + )( + name(), + ); diff --git a/src/components/encode/jsonString.mjs b/src/components/encode/jsonString.mjs new file mode 100644 index 0000000..c283972 --- /dev/null +++ b/src/components/encode/jsonString.mjs @@ -0,0 +1,38 @@ +import unsafe from "./src/stringify/unsafe.mjs"; +import safe from "./src/stringify/safe.mjs"; + +/** + * Stringifies a given schema based on provided options. + * + * @function + * @param {Object} [options] - Configuration options for stringifying. + * @param {("unsafe" | "safe")} [options.type="safe"] - The type of stringify method to use, can be "unsafe" or "safe". + * @returns {Function} A function that takes a `JsonStringify` schema and returns the stringified result. + * + * @example + * // Using the default safe stringify method + * const str_one_string = stringify()({ + * type: "object", + * properties: { + * hello: { type: "string" }, + * }, + * required: ["hello"], + * }); + * + * // Using the unsafe stringify method + * const ustr_one_string = stringify({type:"unsafe"})({ + * type: "object", + * properties: { + * hello: { type: "string" }, + * }, + * required: ["hello"], + * }); + * @note + * To use the TypeScript typings for this function, import the `JsonStringifyFunction` type from the corresponding type definition file. Example: + * `import { JsonStringifyFunction } from 'vixeny/components/encode/types;` + */ + +export default (options) => (schema) => + typeof options === "object" && "type" in options && options.type === "unsafe" + ? unsafe(schema) + : safe(schema); diff --git a/src/components/encode/src/base64toUrl/convertBase64ToBase64urlBun.mjs b/src/components/encode/src/base64toUrl/convertBase64ToBase64urlBun.mjs new file mode 100644 index 0000000..0a50994 --- /dev/null +++ b/src/components/encode/src/base64toUrl/convertBase64ToBase64urlBun.mjs @@ -0,0 +1,2 @@ +export default () => (b64) => + b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); diff --git a/src/components/encode/src/base64toUrl/convertBase64ToBase64urlDeno.mjs b/src/components/encode/src/base64toUrl/convertBase64ToBase64urlDeno.mjs new file mode 100644 index 0000000..00f40b6 --- /dev/null +++ b/src/components/encode/src/base64toUrl/convertBase64ToBase64urlDeno.mjs @@ -0,0 +1,6 @@ +export default () => (b64) => + b64.endsWith("=") + ? b64.endsWith("==") + ? b64.replace(/\+/g, "-").replace(/\//g, "_").slice(0, -2) + : b64.replace(/\+/g, "-").replace(/\//g, "_").slice(0, -1) + : b64.replace(/\+/g, "-").replace(/\//g, "_"); diff --git a/src/components/encode/src/base64toUrl/convertBase64ToBase64urlNode.mjs b/src/components/encode/src/base64toUrl/convertBase64ToBase64urlNode.mjs new file mode 100644 index 0000000..66f0fd9 --- /dev/null +++ b/src/components/encode/src/base64toUrl/convertBase64ToBase64urlNode.mjs @@ -0,0 +1,6 @@ +export default () => (b64) => + b64.length > 2 && b64[b64.length - 1] === "=" + ? b64[b64.length - 2] === "=" + ? b64.replace(/\+/g, "-").replace(/\//g, "_").slice(0, -2) + : b64.replace(/\+/g, "-").replace(/\//g, "_").slice(0, -1) + : b64.replace(/\+/g, "-").replace(/\//g, "_"); diff --git a/src/components/encode/src/others/concatThreeUint8Array.mjs b/src/components/encode/src/others/concatThreeUint8Array.mjs new file mode 100644 index 0000000..12c3ca1 --- /dev/null +++ b/src/components/encode/src/others/concatThreeUint8Array.mjs @@ -0,0 +1,13 @@ +export default (a) => (b) => (d) => + ( + (result) => ( + ( + result.set(a), + result.set(b, a.length), + result.set(d, a.length + b.length), + result + ) + ) + )( + new Uint8Array(a.length + b.length + d.length), + ); diff --git a/src/components/encode/src/others/concatTwoUint8Array.mjs b/src/components/encode/src/others/concatTwoUint8Array.mjs new file mode 100644 index 0000000..b914899 --- /dev/null +++ b/src/components/encode/src/others/concatTwoUint8Array.mjs @@ -0,0 +1,10 @@ +export default () => (a) => (b) => + ( + (result) => ( + ( + result.set(a), result.set(b, a.length), result + ) + ) + )( + new Uint8Array(a.length + b.length), + ); diff --git a/src/components/encode/src/stringify/composer/decoder.mjs b/src/components/encode/src/stringify/composer/decoder.mjs new file mode 100644 index 0000000..c8040e1 --- /dev/null +++ b/src/components/encode/src/stringify/composer/decoder.mjs @@ -0,0 +1,8 @@ +export default (j) => + Object.keys(j.properties) + .map((x) => ({ + type: j.properties[x].type, + required: j.required?.includes(x) === true ? true : false, + name: x, + const: "const" in j.properties[x] ? j.properties.const : undefined, + })); diff --git a/src/components/encode/src/stringify/composer/decoder.ts b/src/components/encode/src/stringify/composer/decoder.ts new file mode 100644 index 0000000..ee345dc --- /dev/null +++ b/src/components/encode/src/stringify/composer/decoder.ts @@ -0,0 +1,13 @@ +import { JsonMap, JsonStringify } from "../types.ts"; + +export default (j: JsonStringify) => + Object.keys(j.properties) + .map( + (x) => + ({ + type: j.properties[x].type, + required: j.required?.includes(x) === true ? true : false, + name: x, + const: "const" in j.properties[x] ? j.properties.const : undefined, + }) as JsonMap, + ); diff --git a/src/components/encode/src/stringify/composer/finder.mjs b/src/components/encode/src/stringify/composer/finder.mjs new file mode 100644 index 0000000..ed1e894 --- /dev/null +++ b/src/components/encode/src/stringify/composer/finder.mjs @@ -0,0 +1,51 @@ +import string from "../methods/json_string.mjs"; +import boolean from "../methods/json_boolean.mjs"; +import number from "../methods/json_number.mjs"; +import array from "../methods/json_array.mjs"; + +export default (o) => + '"{" +' + (((f) => + ((x) => x(x))((x) => f((y) => x(x)(y))))((f) => (o) => (s) => + Object.keys(o.properties) + .map((x) => + o.properties[x].type !== "object" + ? o.properties[x].type === "string" + ? string( + { + ...o.properties[x], + name: x, + required: o.required?.includes(x) ?? false, + path: s + "." + x, + }, + ) + : o.properties[x].type === "number" + ? number( + { + ...o.properties[x], + name: x, + required: o.required?.includes(x) ?? false, + path: s + "." + x, + }, + ) + : o.properties[x].type === "boolean" + ? boolean( + { + ...o.properties[x], + name: x, + required: o.required?.includes(x) ?? false, + path: s + "." + x, + }, + ) + : array( + { + ...o.properties[x], + name: x, + required: o.required?.includes(x) ?? false, + path: s + "." + x, + }, + ) + : `'"${x}":{' + ` + + (f(o.properties[x])(s + "." + x).join(" + ',' +")) + ' + "}"' + ) + )(o)("")).join(" + ',' +") + + ' + "}"'; diff --git a/src/components/encode/src/stringify/composer/finder.ts b/src/components/encode/src/stringify/composer/finder.ts new file mode 100644 index 0000000..0eece56 --- /dev/null +++ b/src/components/encode/src/stringify/composer/finder.ts @@ -0,0 +1,70 @@ +// deno-lint-ignore-file no-explicit-any +import { + JsonArrayType, + JsonBooleanType, + JsonNumberType, + JsonStringify, + JsonStringType, +} from "../types.ts"; +import string from "../methods/json_string.ts"; +import boolean from "../methods/json_boolean.ts"; +import number from "../methods/json_number.ts"; +import array from "../methods/json_array.ts"; + +export default (o: JsonStringify) => + '"{" +' + (((f: (arg0: (y: any) => any) => any) => + ((x) => + x(x))((x: (arg0: any) => { (arg0: any): any; new (): any }) => + f((y: any) => x(x)(y)) + ))((f) => (o: JsonStringify) => (s: string) => + Object.keys(o.properties) + .map((x) => + o.properties[x].type !== "object" + ? o.properties[x].type === "string" + ? string( + { + ...o.properties[x], + ...{ + name: x, + required: o.required?.includes(x) ?? false, + }, + ...{ path: s + "." + x }, + } as JsonStringType, + ) + : o.properties[x].type === "number" + ? number( + { + ...o.properties[x], + ...{ + name: x, + required: o.required?.includes(x) ?? false, + }, + ...{ path: s + "." + x }, + } as JsonNumberType, + ) + : o.properties[x].type === "boolean" + ? boolean( + { + ...o.properties[x], + ...{ + name: x, + required: o.required?.includes(x) ?? false, + }, + ...{ path: s + "." + x }, + } as JsonBooleanType, + ) + : array( + { + ...o.properties[x], + ...{ + name: x, + required: o.required?.includes(x) ?? false, + }, + ...{ path: s + "." + x }, + } as JsonArrayType, + ) + : `'"${x}":{' + ` + + (f(o.properties[x])(s + "." + x).join(" + ',' +")) + ' + "}"' + ) + )(o)("")).join(" + ',' +") + + ' + "}"'; diff --git a/src/components/encode/src/stringify/composer/sanitizer.mjs b/src/components/encode/src/stringify/composer/sanitizer.mjs new file mode 100644 index 0000000..e6e1e62 --- /dev/null +++ b/src/components/encode/src/stringify/composer/sanitizer.mjs @@ -0,0 +1,12 @@ +/** + * Returns a string safely wrapped in double quotes, escaping it if necessary. + * + * @param {string} s - The input string. + * @returns {string} - The safely quoted string. + */ + +export default ( + (escapes) => (s) => escapes.test(s) ? JSON.stringify(s) : `"${s}"` +)( + /[\\"\\u0000-\\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +); diff --git a/src/components/encode/src/stringify/composer/sanitizer.ts b/src/components/encode/src/stringify/composer/sanitizer.ts new file mode 100644 index 0000000..47f1356 --- /dev/null +++ b/src/components/encode/src/stringify/composer/sanitizer.ts @@ -0,0 +1,6 @@ +export default ( + (escapes) => (s: string) => + escapes.test(s) ? JSON.stringify(s) : '"' + s + '"' +)( + /[\\"\\u0000-\\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +); diff --git a/src/components/encode/src/stringify/composer/selector.mjs b/src/components/encode/src/stringify/composer/selector.mjs new file mode 100644 index 0000000..733fcee --- /dev/null +++ b/src/components/encode/src/stringify/composer/selector.mjs @@ -0,0 +1,16 @@ +import string from "../methods/json_string.mjs"; +import boolean from "../methods/json_boolean.mjs"; +import number from "../methods/json_number.mjs"; +import array from "../methods/json_array.mjs"; + +export default (element) => + element.map( + (x) => + x.type === "string" + ? string(x) + : x.type === "boolean" + ? boolean(x) + : x.type === "number" + ? number(x) + : array(x), + ).join(" + ',' +"); diff --git a/src/components/encode/src/stringify/composer/selector.ts b/src/components/encode/src/stringify/composer/selector.ts new file mode 100644 index 0000000..61b1bfd --- /dev/null +++ b/src/components/encode/src/stringify/composer/selector.ts @@ -0,0 +1,18 @@ +import { JsonOptionsType } from "../types.ts"; +import string from "../methods/json_string.ts"; +import boolean from "../methods/json_boolean.ts"; +import number from "../methods/json_number.ts"; +import array from "../methods/json_array.ts"; + +export default (element: JsonOptionsType[]) => ( + element.map( + (x) => + x.type === "string" + ? string(x) + : x.type === "boolean" + ? boolean(x) + : x.type === "number" + ? number(x) + : array(x), + ).join(" + ',' +") +); diff --git a/src/components/encode/src/stringify/methods/json_array.mjs b/src/components/encode/src/stringify/methods/json_array.mjs new file mode 100644 index 0000000..f61a94b --- /dev/null +++ b/src/components/encode/src/stringify/methods/json_array.mjs @@ -0,0 +1 @@ +export default (x) => `'"${x.name}":' + JSON.stringify(o${x.path})`; diff --git a/src/components/encode/src/stringify/methods/json_array.ts b/src/components/encode/src/stringify/methods/json_array.ts new file mode 100644 index 0000000..bbe3298 --- /dev/null +++ b/src/components/encode/src/stringify/methods/json_array.ts @@ -0,0 +1,4 @@ +import { JsonArrayType } from "../types.ts"; + +export default (x: JsonArrayType) => + `'"${x.name}":' + JSON.stringify(o${x.path})`; diff --git a/src/components/encode/src/stringify/methods/json_boolean.mjs b/src/components/encode/src/stringify/methods/json_boolean.mjs new file mode 100644 index 0000000..ef9b8ce --- /dev/null +++ b/src/components/encode/src/stringify/methods/json_boolean.mjs @@ -0,0 +1,6 @@ +export default (x) => + "const" in x && typeof x.const === "boolean" + ? `'"${x.name}":' + ${x.const}` + : `'"${x.name}":'+( typeof o${x.path} === "boolean"?o${x.path}:'${ + "default" in x ? x.default : null + }')`; diff --git a/src/components/encode/src/stringify/methods/json_boolean.ts b/src/components/encode/src/stringify/methods/json_boolean.ts new file mode 100644 index 0000000..b69754f --- /dev/null +++ b/src/components/encode/src/stringify/methods/json_boolean.ts @@ -0,0 +1,8 @@ +import { JsonBooleanType } from "../types.ts"; + +export default (x: JsonBooleanType) => + "const" in x && typeof x.const === "boolean" + ? `'"${x.name}":' + ${x.const}` + : `'"${x.name}":'+( typeof o${x.path} === "boolean"?o${x.path}:'${ + "default" in x ? x.default : null + }')`; diff --git a/src/components/encode/src/stringify/methods/json_number.mjs b/src/components/encode/src/stringify/methods/json_number.mjs new file mode 100644 index 0000000..70319ee --- /dev/null +++ b/src/components/encode/src/stringify/methods/json_number.mjs @@ -0,0 +1,6 @@ +export default (x) => + "const" in x && typeof x.const === "number" + ? `'"${x.name}":' + ${x.const}` + : `'"${x.name}":'+( typeof o${x.path} === "number"?o${x.path}:'${ + "default" in x ? x.default : null + }')`; diff --git a/src/components/encode/src/stringify/methods/json_number.ts b/src/components/encode/src/stringify/methods/json_number.ts new file mode 100644 index 0000000..52633aa --- /dev/null +++ b/src/components/encode/src/stringify/methods/json_number.ts @@ -0,0 +1,8 @@ +import { JsonNumberType } from "../types.ts"; + +export default (x: JsonNumberType) => + "const" in x && typeof x.const === "number" + ? `'"${x.name}":' + ${x.const}` + : `'"${x.name}":'+( typeof o${x.path} === "number"?o${x.path}:'${ + "default" in x ? x.default : null + }')`; diff --git a/src/components/encode/src/stringify/methods/json_string.mjs b/src/components/encode/src/stringify/methods/json_string.mjs new file mode 100644 index 0000000..d091478 --- /dev/null +++ b/src/components/encode/src/stringify/methods/json_string.mjs @@ -0,0 +1,10 @@ +export default (x) => + "const" in x && typeof x.const === "string" + ? `'"${x.name}":"${x.const}"'` + : x.required && !("default" in x) + ? `'"${x.name}":' + str(o${x.path})` + : `'"${x.name}":' + (typeof o${x.path} === "string"?str(o${x.path}):'${ + "default" in x && typeof x.default === "string" + ? '"' + x.default + '"' + : null + }')`; diff --git a/src/components/encode/src/stringify/methods/json_string.ts b/src/components/encode/src/stringify/methods/json_string.ts new file mode 100644 index 0000000..fb1de20 --- /dev/null +++ b/src/components/encode/src/stringify/methods/json_string.ts @@ -0,0 +1,12 @@ +import { JsonStringType } from "../types.ts"; + +export default (x: JsonStringType) => + "const" in x && typeof x.const === "string" + ? `'"${x.name}":"${x.const}"'` + : x.required && !("default" in x) + ? `'"${x.name}":' + str(o${x.path})` + : `'"${x.name}":' + (typeof o${x.path} === "string"?str(o${x.path}):'${ + "default" in x && typeof x.default === "string" + ? '"' + x.default + '"' + : null + }')`; diff --git a/src/components/encode/src/stringify/safe.mjs b/src/components/encode/src/stringify/safe.mjs new file mode 100644 index 0000000..34af01f --- /dev/null +++ b/src/components/encode/src/stringify/safe.mjs @@ -0,0 +1,7 @@ +import sanitizer from "./composer/sanitizer.mjs"; +import finder from "./composer/finder.mjs"; + +export default (o) => + ( + new Function(`return str=>o=> ${finder(o)} `) + )()(sanitizer); diff --git a/src/components/encode/src/stringify/stringify.ts b/src/components/encode/src/stringify/stringify.ts new file mode 100644 index 0000000..7127595 --- /dev/null +++ b/src/components/encode/src/stringify/stringify.ts @@ -0,0 +1,8 @@ +import sanitizer from "./composer/sanitizer.ts"; +import { JsonStringify, JsonType } from "./types.ts"; +import finder from "./composer/finder.ts"; + +export default (o: JsonStringify) => + ( + new Function(`return str=>o=> ${finder(o)} `) + )()(sanitizer) as (o: JsonType | Promise) => string; diff --git a/src/components/encode/src/stringify/types.ts b/src/components/encode/src/stringify/types.ts new file mode 100644 index 0000000..39b0742 --- /dev/null +++ b/src/components/encode/src/stringify/types.ts @@ -0,0 +1,79 @@ +export type JsonTypes = "string" | "boolean" | "number"; + +type Extra = { name: string; required: boolean; path?: string }; +type JsonString = { + type: "string"; +} | { + type: "string"; + const: string; +} | { + type: "string"; + default: string; +}; +export type JsonStringType = JsonString & Extra; + +type JsonBoolean = { + type: "boolean"; +} | { + type: "boolean"; + const: boolean; +} | { + type: "boolean"; + default: boolean; +}; + +export type JsonBooleanType = JsonBoolean & Extra; + +type JsonNumber = { + type: "number"; +} | { + type: "number"; + const: number; +} | { + type: "number"; + default: number; +}; + +export type JsonNumberType = JsonNumber & Extra; + +export type JsonArray = { + type: "array"; +}; + +export type JsonArrayType = JsonBoolean & Extra; + +export type JsonStringify = { + type: "object"; + properties: { + [key: string]: JsonElements; + }; + required?: string[]; +}; + +export type JsonType = { + [x: string]: + | string + | number + | boolean + | Array + | JsonType; +}; + +export type JsonOptionsType = + | JsonStringType + | JsonBooleanType + | JsonNumberType + | JsonArrayType; + +export type JsonElements = + | JsonString + | JsonBoolean + | JsonNumber + | JsonArray + | JsonStringify; + +export type JsonMap = { name: string; type: JsonTypes; required: boolean }; + +export type JsonOptions = { + scheme: JsonStringify; +}; diff --git a/src/components/encode/src/stringify/unsafe.mjs b/src/components/encode/src/stringify/unsafe.mjs new file mode 100644 index 0000000..2ff3a03 --- /dev/null +++ b/src/components/encode/src/stringify/unsafe.mjs @@ -0,0 +1,6 @@ +import finder from "./composer/finder.mjs"; + +export default (o) => + ( + new Function(`return str=>o=> ${finder(o)} `) + )()((i) => i); diff --git a/src/components/encode/types.ts b/src/components/encode/types.ts new file mode 100644 index 0000000..4f1fd69 --- /dev/null +++ b/src/components/encode/types.ts @@ -0,0 +1,11 @@ +import { JsonStringify } from "./src/stringify/types.ts"; + +// Define the options type for the function +export interface StringifyOptions { + type?: "unsafe" | "safe"; +} + +// Define the type for the main function +export type JsonStringifyFunction = ( + options?: StringifyOptions, +) => (schema: JsonStringify) => string; diff --git a/src/components/jwt/signSha256.mjs b/src/components/jwt/signSha256.mjs new file mode 100644 index 0000000..e080ded --- /dev/null +++ b/src/components/jwt/signSha256.mjs @@ -0,0 +1,18 @@ +import signer from "./src/sign/sha256.mjs"; +import nodeCrypto from "node:crypto"; +import BufferProto from "node:buffer"; +import name from "../runtime/name.mjs"; + +export default () => + ( + (rt) => + signer( + rt === "Bun" ? Buffer : BufferProto.Buffer, + )( + rt === "Bun" + ? (d) => new Bun.CryptoHasher("sha256").update(d) + : (d) => nodeCrypto.createHash("sha256").update(d), + ) + )( + name(), + )(); diff --git a/src/components/jwt/src/sign/sha256.mjs b/src/components/jwt/src/sign/sha256.mjs new file mode 100644 index 0000000..af79afc --- /dev/null +++ b/src/components/jwt/src/sign/sha256.mjs @@ -0,0 +1,79 @@ +export default (Buffer) => +(sha256) => +( + header = Buffer.from(JSON.stringify({ + alg: "HS256", + typ: "JWT", + })).toString("base64url") + ".", +) => +(key) => + ( + (hmac) => + ((lf) => (rg) => (message) => + ( + (json) => + header + json + "." + + sha256( + Buffer.concat([ + lf, + sha256( + Buffer.concat([ + rg, + Buffer.from(header + json), + ]), + ).digest(), + ]), + ) + .digest().toString("base64url") + )( + Buffer.from(JSON.stringify(message)).toString("base64url"), + ))(new Uint8Array(64).map((_x, i) => hmac[i] ^ 0x5c))( + new Uint8Array(64).map((_x, i) => hmac[i] ^ 0x36), + ) + )( + key.length > 64 + ? sha256(key).digest() + : key.length < 64 + ? Buffer.concat([key, Buffer.alloc(64 - key.length)]) + : key, + ); + +// import concatTwoUint8Array from "../../../bytes/src/concatTwoUint8Array.mjs"; +// export default (Buffer) => +// (sha256) => +// ( +// header = Buffer.from(JSON.stringify({ +// alg: "HS256", +// typ: "JWT", +// })).toString("base64url") + ".", +// ) => +// (key) => +// ( +// (hmac) => (message) => +// ( +// (json) => +// header + json + "." + +// sha256( +// concatTwoUint8Array( +// new Uint8Array(64).map((_x, i) => hmac[i] ^ 0x5c) +// )( +// sha256( +// concatTwoUint8Array( +// new Uint8Array(64).map((_x, i) => hmac[i] ^ 0x36) +// )( +// Buffer.from(header + json) +// ) +// ).digest(), +// ) +// ) +// .digest().toString("base64url") +// )( +// Buffer.from(JSON.stringify(message)).toString("base64url"), +// ) +// )( +// key.length > 64 +// ? sha256(key).digest() +// : key.length < 64 +// ? Buffer.concat([key, Buffer.alloc(64 - key.length)]) +// : key, +// ); diff --git a/src/components/jwt/src/verify/sha256.mjs b/src/components/jwt/src/verify/sha256.mjs new file mode 100644 index 0000000..a182619 --- /dev/null +++ b/src/components/jwt/src/verify/sha256.mjs @@ -0,0 +1,41 @@ +export default (Buffer) => (hash) => (key) => + ( + (hmac) => + ( + (lf) => (rg) => (message) => + message.substring(message.lastIndexOf(".") + 1) === + hash( + Buffer.concat([ + lf, + hash( + Buffer.concat([ + rg, + Buffer.from( + message.substring(0, message.lastIndexOf(".")), + ), + ]), + ).digest(), + ]), + ) + .digest() + .toString("base64url") + ? JSON.parse( + Buffer.from( + message.substring( + message.indexOf(".") + 1, + message.lastIndexOf("."), + ), + "base64url", + ).toString(), + ) + : null + )(new Uint8Array(64).map((_x, i) => hmac[i] ^ 0x5c))( + new Uint8Array(64).map((_x, i) => hmac[i] ^ 0x36), + ) + )( + key.length > 64 + ? hash(key).digest() + : key.length < 64 + ? Buffer.concat([key, Buffer.alloc(64 - key.length)]) + : key, + ); diff --git a/src/components/jwt/verifySha256.mjs b/src/components/jwt/verifySha256.mjs new file mode 100644 index 0000000..cd9192f --- /dev/null +++ b/src/components/jwt/verifySha256.mjs @@ -0,0 +1,21 @@ +import verify from "./src/verify/sha256.mjs"; +import nodeCrypto from "node:crypto"; +import BufferProto from "node:buffer"; + +export default () => + ( + (rt) => + verify( + rt === "Bun" ? Buffer : BufferProto.Buffer, + )( + rt === "Bun" + ? (d) => new Bun.CryptoHasher("sha256").update(d) + : (d) => nodeCrypto.createHash("sha256").update(d), + ) + )( + typeof Bun !== "undefined" + ? "Bun" + : typeof Bun !== "undefined" + ? "Deno" + : "Node", + ); diff --git a/src/components/parameters/finder.ts b/src/components/parameters/finder.ts new file mode 100644 index 0000000..88308e6 --- /dev/null +++ b/src/components/parameters/finder.ts @@ -0,0 +1,31 @@ +import type { info } from "./types.ts"; + +export default (info: info) => + `s=>${ + Array.from( + { + length: info.elements.length - 1, + }, + ( + _, + i, + ) => [ + `(a${i}=>`, + `)(s.indexOf("/"${i !== 0 ? `,a${i - 1}` : ""}) + 1)`, + ], + ).reverse().reduce( + (acc, v) => v[0] + acc + v[1], + `({${ + Array.from({ + length: info.elements.length, + }, (_, id) => + id === 0 + ? ` ${info.elements[id].slice(1)} : s.slice(0, a0 - 1)` + : id === (info.elements.length - 1) + ? ` ${info.elements[id].slice(1)} : s.slice(a${id - 1})` + : ` ${info.elements[id].slice(1)} : s.slice(a${ + id - 1 + }, a${id} - 1)`).join(",") + }})`, + ) + }`; diff --git a/src/components/parameters/main.ts b/src/components/parameters/main.ts new file mode 100644 index 0000000..119000a --- /dev/null +++ b/src/components/parameters/main.ts @@ -0,0 +1,23 @@ +import type { FunRouterOptions } from "../../options.ts"; +import type { Petition } from "../../morphism.ts"; +import multi from "./multi.ts"; +import one from "./one.ts"; +import map from "./map.ts"; +import unique from "./unique.ts"; + +export default (options?: FunRouterOptions) => (f: Petition) => + ( + (info) => + f.param && "unique" in f.param && typeof f.param.unique === "boolean" && + f.param.unique + ? new Function(`return ${unique(info)}`)() + : info.firstParam === -1 + ? () => null + : info.elements.length === 1 && + (info.elements.at(-1) || "")[0] === info.startsWith && + f.path.at(-1) !== "/" + ? new Function(`return ${one(info)}`)() + : new Function(`return ${multi(info)}`)() + )( + map(options)(f), + ); diff --git a/src/components/parameters/map.ts b/src/components/parameters/map.ts new file mode 100644 index 0000000..440e2bd --- /dev/null +++ b/src/components/parameters/map.ts @@ -0,0 +1,47 @@ +import type { FunRouterOptions } from "../../options.ts"; +import type { Petition } from "../../morphism.ts"; +import type { info } from "./types.ts"; + +export default (options?: FunRouterOptions) => (f: Petition): info => + ( + (list) => ( + ((startsWith) => + ( + (endsInSlash) => + ( + (point) => ({ + list: list, + map: list.map((x) => x[0] === startsWith), + startsWith: startsWith, + elements: point.tail === point.head + ? [list[point.head]] + : list.slice(point.tail, point.head + 1), + firstParam: f.path.indexOf("/" + startsWith), + lastParam: list.slice(point.head + 1, list.length).reduce( + (acc, x) => x.length + 1 + acc, + 0, + ) + (endsInSlash ? 1 : 0), + endsInSlash: endsInSlash, + hasName: options?.hasName, + }) + )({ + tail: list.reduce( + (acc, x, i) => (x[0] === startsWith && acc === -1) ? i : acc, + -1, + ), + head: list.reduceRight( + (acc, x, i) => (x[0] === startsWith && acc === 0) ? i : acc, + 0, + ), + }) + )( + f.path.at(-1) === "/", + ))( + options && "paramsStartsWith" in options + ? options.paramsStartsWith?.at(1) + : ":", + ) + ) + )( + f.path.split("/").filter((x) => x !== ""), + ); diff --git a/src/components/parameters/multi.ts b/src/components/parameters/multi.ts new file mode 100644 index 0000000..cbd99b3 --- /dev/null +++ b/src/components/parameters/multi.ts @@ -0,0 +1,16 @@ +import type { info } from "./types.ts"; + +import finder from "./finder.ts"; +import slicer from "./slicer.ts"; + +export default (info: info) => + ( + (slice) => + ( + (find) => `( sl => (fi => p => fi(sl(p)))(${find} ))(${slice})` + )( + finder(info), + ) + )( + slicer(info), + ); diff --git a/src/components/parameters/one.ts b/src/components/parameters/one.ts new file mode 100644 index 0000000..20cd903 --- /dev/null +++ b/src/components/parameters/one.ts @@ -0,0 +1,34 @@ +import type { info } from "./types.ts"; + +export default (info: info) => + info && "hasName" in info && typeof info.hasName === "string" + ? `(s => s.indexOf("?") === -1 ? ({${info.elements[0].slice(1)}: s.slice( + ${(info.hasName?.length || 0) + info.firstParam} + ${info.lastParam === 0 ? "" : ", -" + info.lastParam})}): ({${ + info.elements[0].slice(1) + }: s.slice(${ + (info.hasName?.length || 0) + info.firstParam + }, s.indexOf("?") ${info.lastParam === 0 ? "" : " -" + info.lastParam})}) +)` + : `(n =>s=> n !== -1 ? s.indexOf("?") === -1 ? + ({${info.elements[0].slice(1)}: s.slice(n ${ + info.lastParam === 0 ? "" : " , - " + info.lastParam + })}) : + ({${info.elements[0].slice(1)}: s.slice(n , s.indexOf("?") ${ + info.lastParam === 0 ? "" : " -" + info.lastParam + }) }) : s.indexOf("?") === -1 ? + ({${ + info.elements[0].slice(1) + }: s.slice( n = s.split("/").filter((x) => x !== "" ).reduce((acc, x, u) =>u<= 1 ? acc + x.length : acc,2,0) + ${ + info.firstParam + 1 + } ${ + info.lastParam === 0 ? "" : ", -" + info.lastParam + } ).split("/").filter((x) => x !== "").join("/") }) + : + ({${ + info.elements[0].slice(1) + }: s.slice( n = s.split("/").filter((x) => x !== "").reduce((acc, x, u) =>u<= 1 ? acc + x.length : acc,2,0 ${ + info.lastParam === 0 ? "" : ", -" + info.lastParam + }) + ${info.firstParam + 1} , s.indexOf("?") ${ + info.lastParam === 0 ? "" : " -" + info.lastParam + } ).split("/").filter((x) => x !== "").join("/")}) )(-1)`; diff --git a/src/components/parameters/parser.ts b/src/components/parameters/parser.ts new file mode 100644 index 0000000..1bd0b78 --- /dev/null +++ b/src/components/parameters/parser.ts @@ -0,0 +1,4 @@ +export default ((a: string[]) => + (new Function( + `return e => ({ ${a.reduce((acc, v, i) => acc + `"${v}": e[${i}],`, "")}})`, + ))()) as (m: string[]) => (e: string[]) => Record; diff --git a/src/components/parameters/slicer.ts b/src/components/parameters/slicer.ts new file mode 100644 index 0000000..4e2337b --- /dev/null +++ b/src/components/parameters/slicer.ts @@ -0,0 +1,33 @@ +import type { info } from "./types.ts"; + +export default (info: info) => + info && "hasName" in info && typeof info.hasName === "string" + ? `(u => u.indexOf("?") === -1 + ? u.slice(${(info.hasName?.length || 0) + info.firstParam} ${ + info.lastParam === 0 ? "" : ", -" + info.lastParam + }) + : u.slice(${ + (info.hasName?.length || 0) + info.firstParam + }, u.indexOf("?") ${info.lastParam === 0 ? "" : " -" + info.lastParam}) + )` + : `(n => u => + n !== -1 + ? u.indexOf("?") === -1 + ? u.slice(n ${ + info.lastParam === 0 ? "" : " , - " + info.lastParam + }) + : u.slice(n , u.indexOf("?") ${ + info.lastParam === 0 ? "" : " -" + info.lastParam + }) + : u.indexOf("?") === -1 + ? u.slice( n = u.split("/").filter((x) => x !== "" ).reduce((acc, x, ul) =>ul<= 1 ? acc + x.length : acc,2,0) + ${ + info.firstParam + 1 + } ${ + info.lastParam === 0 ? "" : ", -" + info.lastParam + } ).split("/").filter((x) => x !== "").join("/") + : u.slice( n = u.split("/").filter((x) => x !== "").reduce((acc, x, ul) =>ul<= 1 ? acc + x.length : acc,2,0 ${ + info.lastParam === 0 ? "" : ", -" + info.lastParam + }) + ${info.firstParam + 1} , u.indexOf("?") ${ + info.lastParam === 0 ? "" : " -" + info.lastParam + } ).split("/").filter((x) => x !== "").join("/") + )(-1)`; diff --git a/src/components/parameters/types.ts b/src/components/parameters/types.ts new file mode 100644 index 0000000..f595c9a --- /dev/null +++ b/src/components/parameters/types.ts @@ -0,0 +1,10 @@ +export type info = { + list: string[]; + map: boolean[]; + startsWith: string | undefined; + elements: string[]; + firstParam: number; + lastParam: number; + endsInSlash: boolean; + hasName: string | undefined; +}; diff --git a/src/components/parameters/unique.ts b/src/components/parameters/unique.ts new file mode 100644 index 0000000..3bdebe8 --- /dev/null +++ b/src/components/parameters/unique.ts @@ -0,0 +1,25 @@ +import type { info } from "./types.ts"; + +export default (info: info) => + info && "hasName" in info && typeof info.hasName === "string" + ? `s => s.indexOf("?") === -1 ? s.slice( + ${(info.hasName?.length || 0) + info.firstParam} + ${info.lastParam === 0 ? "" : ", -" + info.lastParam}): (s.slice(${ + (info.hasName?.length || 0) + info.firstParam + }, s.indexOf("?") ${info.lastParam === 0 ? "" : " -" + info.lastParam}))` + : `(n =>s=> n !== -1 ? s.indexOf("?") === -1 ? + s.slice(n ${info.lastParam === 0 ? "" : " , - " + info.lastParam}) : + s.slice(n , s.indexOf("?") ${ + info.lastParam === 0 ? "" : " -" + info.lastParam + }) : s.indexOf("?") === -1 ? + s.slice( n = s.split("/").filter((x) => x !== "" ).reduce((acc, x, u) =>u<= 1 ? acc + x.length : acc,2,0) + ${ + info.firstParam + 1 + } ${ + info.lastParam === 0 ? "" : ", -" + info.lastParam + } ).split("/").filter((x) => x !== "").join("/") + : + s.slice( n = s.split("/").filter((x) => x !== "").reduce((acc, x, u) =>u<= 1 ? acc + x.length : acc,2,0 ${ + info.lastParam === 0 ? "" : ", -" + info.lastParam + }) + ${info.firstParam + 1} , s.indexOf("?") ${ + info.lastParam === 0 ? "" : " -" + info.lastParam + } ).split("/").filter((x) => x !== "").join("/") )(-1)`; diff --git a/src/components/queries/common.ts b/src/components/queries/common.ts new file mode 100644 index 0000000..52e9a8e --- /dev/null +++ b/src/components/queries/common.ts @@ -0,0 +1,41 @@ +import type { FunRouterOptions } from "../../options.ts"; +import type { Petition } from "../../morphism.ts"; + +export default (o?: FunRouterOptions) => (f: Petition) => + ( + (b) => + ( + (p) => + b !== -1 + ? `s=>(i=> + i!==-1? + Object.fromEntries( + s.slice(i+1).split("&").map((x) => x.split("=")) + ) + :null)(s.indexOf("?"))` + : ` (b=>s => + + b !== -1 + ? Object.fromEntries( + s.slice(b).split("&").map((x) => x.split("=")), + ) + : Object.fromEntries( + s.slice( + b = s + .split("/") + .filter((x) => x !== "") + .reduce( + (acc, x, u) => u <= 1 ? acc + x.length : acc, + 3, + ) + + ${p}, + ).split("&").map((x) => x.split("=")), + ))(-1)` + )( + f.path.includes("/" + (o?.paramsStartsWith || ":")) + ? f.path.indexOf("/" + (o?.paramsStartsWith || ":")) + : f.path.length, + ) + )( + typeof o?.hasName === "string" ? o.hasName.length : -1, + ); diff --git a/src/components/queries/elements.ts b/src/components/queries/elements.ts new file mode 100644 index 0000000..c705bf2 --- /dev/null +++ b/src/components/queries/elements.ts @@ -0,0 +1,6 @@ +import finder from "./finder.ts"; + +export default (ar: string[]) => + `(p=>u=>(l=> l!==-1?p(u.slice(l)) :null)(u.indexOf("?")))(s => ({${ + ar.map((x) => ` ${x}: encodeURIComponent(${finder(x)})`).join(",") + }}))`; diff --git a/src/components/queries/filter.ts b/src/components/queries/filter.ts new file mode 100644 index 0000000..2bc7a4e --- /dev/null +++ b/src/components/queries/filter.ts @@ -0,0 +1,14 @@ +export default (elements: string[]) => (input: string) => + elements + .filter((x, index, arr) => + index === 0 || + (!arr[index - 1].startsWith("resolve.") && + !arr[index - 1].startsWith("branch.")) + ) + .filter((v) => + (input + .match(/(?:[?.])(\w+)\s*\(\)/g) || []) // extract potential matches from input + .map((token) => token.slice(1, token.indexOf("("))) // extract function names from tokens + .includes(v) + ) + .reduce((acc, v) => acc.includes(v) ? acc : acc.concat(v), [] as string[]); diff --git a/src/components/queries/finder.ts b/src/components/queries/finder.ts new file mode 100644 index 0000000..eb38ba4 --- /dev/null +++ b/src/components/queries/finder.ts @@ -0,0 +1,4 @@ +export default (s: string) => + ` (a =>a !== -1? (l => l !== -1 ? s.slice(a+${s.length + 1},l):s.slice(a+${ + s.length + 1 + },s.length))(s.indexOf("&",a)):null)(s.indexOf("${s + "="}")) `; diff --git a/src/components/queries/main.ts b/src/components/queries/main.ts new file mode 100644 index 0000000..1595b6e --- /dev/null +++ b/src/components/queries/main.ts @@ -0,0 +1,18 @@ +import type { FunRouterOptions } from "../../options.ts"; +import type { Petition } from "../../morphism.ts"; +import common from "./common.ts"; +import elements from "./elements.ts"; + +import filter from "./filter.ts"; +import unique from "./unique.ts"; +export default (o?: FunRouterOptions) => (f: Petition) => + f.query && "name" in f.query + ? new Function(`return ${unique([f.query.name])}`)() + : f.query && "only" in f.query && Array.isArray(f.query.only) + ? new Function(`return ${elements(f.query.only)}`)() + : ( + (only) => + only.length > 0 + ? new Function(`return ${elements(only)}`)() + : new Function(`return ${common(o)(f)}`)() + )(filter(f.f.toString().split(" "))("query")); diff --git a/src/components/queries/unique.ts b/src/components/queries/unique.ts new file mode 100644 index 0000000..b0323c7 --- /dev/null +++ b/src/components/queries/unique.ts @@ -0,0 +1,8 @@ +export default (s: string[]) => + `(p=>u=>(l=> l!==-1?p(u.slice(l)) :null)(u.indexOf("?")))(s => ${` (a =>a !== -1? (l => l !== -1 ? + + encodeURIComponent(s.slice(a+${s[0].length + 1},l)) + + :encodeURIComponent(s.slice(a+${s[0].length + 1},s.length)) + + )(s.indexOf("&",a)):null)(s.indexOf("${s[0] + "="}")) `})`; diff --git a/src/components/runtime/name.mjs b/src/components/runtime/name.mjs new file mode 100644 index 0000000..505073e --- /dev/null +++ b/src/components/runtime/name.mjs @@ -0,0 +1,26 @@ +/** + * Identifies the current JavaScript runtime environment. It prioritizes the detection in the following order: + * 1. Bun + * 2. Deno + * + * By default, if neither Bun nor Deno environments are detected, it assumes the Node environment. + * + * This function is useful for applications that need to adapt their behavior based on the runtime environment. + * + * ```js + * import { runtime } from 'vixeny' + * + * //logging runtime name + * console.log( + * runtime.name() + * ) + * + * ``` + */ + +export default () => + typeof Bun !== "undefined" + ? "Bun" + : typeof Bun !== "undefined" + ? "Deno" + : "Node"; diff --git a/src/components/runtime/parseArguments.ts b/src/components/runtime/parseArguments.ts new file mode 100644 index 0000000..cfa4e30 --- /dev/null +++ b/src/components/runtime/parseArguments.ts @@ -0,0 +1,42 @@ +/** + * Parses command-line arguments passed to the script and returns an object with key-value pairs. + * For flags without explicit values, the value is set to `true`. + * + * This function is designed to work with both Deno and Node.js environments, automatically detecting + * the environment to use the appropriate source of arguments. + * + * Example usage in Node.js: + * ```bash + * node script.js --name=John --age=30 --admin + * ``` + * This would result in: `{ name: "John", age: "30", admin: true }` + * + * Example usage in Deno: + * ```bash + * deno run script.js --name=John --age=30 --admin + * ``` + * Similar output as Node.js example. + * + * @returns {ParsedArgs} An object representing the parsed command-line arguments. + */ + +//TODO: introduce `name` and add , interface +type ParsedArgs = { + [key: string]: string | boolean; +}; + +export default (): ParsedArgs => + //@ts-ignore + (typeof Deno !== "undefined" ? Deno.args : process.argv.slice(2)) + .map<[string, string | boolean]>((arg: string) => + arg.startsWith("--") + ? arg.slice(2).split("=") as [string, string] + : [arg, true] + ) + .reduce( + (acc: ParsedArgs, [key, value]: [string, string | boolean]) => ({ + ...acc, + [key]: value === undefined ? true : value, + }), + {}, + ); diff --git a/components/http/src/framework/optimizer/branch/main.ts b/src/composer/branch/main.ts similarity index 69% rename from components/http/src/framework/optimizer/branch/main.ts rename to src/composer/branch/main.ts index f8507cc..f5c2fb0 100644 --- a/components/http/src/framework/optimizer/branch/main.ts +++ b/src/composer/branch/main.ts @@ -1,16 +1,13 @@ -import recursiveCheck from "../checkAsync.ts"; -import { BranchOptions, ResponseResponse } from "./types.ts"; +import tools from "../composerTools.ts"; +import type { BranchOptions, ResponseResponse } from "./types.ts"; import table from "./table.ts"; -import { specialOptions } from "../aComposer.ts"; -import { - AnyMorphismMap, - CommonRequestMorphism, - RequestMorphism, -} from "../types.ts"; +import type { specialOptions } from "../linker.ts"; +import type { BranchMap } from "../../morphism.ts"; +import type { Petition } from "../../morphism.ts"; export default (o?: specialOptions) => (path: string) => -(input: AnyMorphismMap): ResponseResponse => +(input: BranchMap): ResponseResponse => ( (ar) => ( @@ -30,8 +27,8 @@ export default (o?: specialOptions) => )(), ))( ar.some((x) => - recursiveCheck( - x as unknown as CommonRequestMorphism | RequestMorphism, + tools.recursiveCheckAsync( + x as unknown as Petition, ) ), ) diff --git a/src/composer/branch/table.ts b/src/composer/branch/table.ts new file mode 100644 index 0000000..83ccded --- /dev/null +++ b/src/composer/branch/table.ts @@ -0,0 +1,28 @@ +import aComposer, { type specialOptions } from "../linker.ts"; +import tools from "../composerTools.ts"; +import type { BranchOptions } from "./types.ts"; + +export default (o?: specialOptions) => +(path: string) => +(table: BranchOptions) => + table + .map((x) => ({ ...x, path: path })) + .map((x) => ({ + name: x.name, + f: ( + (composed) => + x.f.constructor.name === "AsyncFunction" || + composed.constructor.name === "AsyncFunction" + ? //void console.log(composed.toString()) ?? + ((a) => + (k: (arg0: any) => any) => + (r: Request) => + async (b: unknown) => k(await a( r)(b)))(composed)(x.f) + : ((a) => (k: (arg0: any) => any) => (r: Request) => (b: unknown) => + k(a(r)(b)))(composed)(x.f) + )( + aComposer(o ? { ...o, branch: false } : { branch: false })(x)( + tools.isUsing(o)(x)(tools.elements), + ), + ), + })); diff --git a/src/composer/branch/types.ts b/src/composer/branch/types.ts new file mode 100644 index 0000000..512cfe0 --- /dev/null +++ b/src/composer/branch/types.ts @@ -0,0 +1,11 @@ +import type { Petition, PetitionOptions } from "../../morphism.ts"; + +export type BranchOption = Petition & { + name: string; + options?: PetitionOptions; +}; +export type BranchOptions = BranchOption[]; + +export type ResponseResponse = ( + r: Request, +) => { [key: string]: (arg?: any) => any }; diff --git a/src/composer/checkPetition/checkParse.ts b/src/composer/checkPetition/checkParse.ts new file mode 100644 index 0000000..59c0852 --- /dev/null +++ b/src/composer/checkPetition/checkParse.ts @@ -0,0 +1,23 @@ +import type { Petition } from "../../morphism.ts"; +import checkTool from "./checkTool.ts"; + +export default (elements: string[]) => +(remove: string[]) => +(add: string[]) => +(f: Petition): string[] => + ( + (filteredString: string): string[] => + checkTool + .isUsingResolve(f)( + checkTool + .filtersBranchAndResolve(elements)(remove)(filteredString) + .concat(add) + .reduce( + (acc: string[], element) => + acc.includes(element) === false ? acc.concat(element) : acc, + [] as string[], + ), + ) + )( + checkTool.normalize(f.f.toString()), + ); diff --git a/src/composer/checkPetition/checkTool.ts b/src/composer/checkPetition/checkTool.ts new file mode 100644 index 0000000..b7713ae --- /dev/null +++ b/src/composer/checkPetition/checkTool.ts @@ -0,0 +1,55 @@ + +import type { Petition } from "../../morphism.ts"; + +export default { + normalize: (s: string) => + s.replace(/(? (elements: string[]) => + elements.includes("resolve") + ? "resolve" in f ? elements : elements.filter((s) => s === "resolve") + : elements, + + filtersBranchAndResolve: + (elements: string[]) => (remove: string[]) => (filteredString: string) => + // First filter: Remove elements as specified and handle 'resolve.' and 'branch.' prefix cases + elements.filter((element, index, arr) => + !remove.includes(element) && + (index === 0 || + (!arr[index - 1].startsWith("resolve.") && + !arr[index - 1].startsWith("branch."))) + ) + // Second filter: Match elements against the code string for exact matches + .filter((element) => + new RegExp(`\\b${element}\\b`).test(filteredString) + ) + , + updateListOfAddAndRemove: (f: Petition) => (elements: string[]) => (plugins : {[key: string]: any}) => + ( + listOfElements => + ( + keysOnMorphisim => + ({ + add: [...new Set(keysOnMorphisim.reduce( + (acc,val) => + listOfElements.includes(val) + ? [...acc , val] + : acc + , + f.options?.add ?? [] + )) + ], + remove: f.options?.remove ?? [], + elements: listOfElements.filter(s => listOfElements.includes(s)) + }) + + + ) ( + Object.keys(f) + ) + )( + [...elements, ... Object.keys(plugins)] + ) +}; diff --git a/src/composer/checkPetition/mainCheck.ts b/src/composer/checkPetition/mainCheck.ts new file mode 100644 index 0000000..07aa1c2 --- /dev/null +++ b/src/composer/checkPetition/mainCheck.ts @@ -0,0 +1,20 @@ + +import type { Petition } from "../../morphism.ts"; +import type { FunRouterOptions } from "../../options.ts"; +import composerTools from "../composerTools.ts"; +import checkParse from "./checkParse.ts"; +import checkTool from "./checkTool.ts"; + +export default (o?: FunRouterOptions) => (f: Petition) => + "options" in f && f.options + ? f.options.only + ? f.options.only + : ( + newOptions => + checkParse(newOptions.elements)(newOptions.remove)(newOptions.add)(f) + + )( + checkTool.updateListOfAddAndRemove(f)(composerTools.elements(f))(o?.cyclePlugin ?? {}) + ) + + : checkParse(composerTools.elements(f))([])([])(f) diff --git a/components/http/src/framework/optimizer/checker.ts b/src/composer/checker.ts similarity index 100% rename from components/http/src/framework/optimizer/checker.ts rename to src/composer/checker.ts diff --git a/components/http/src/framework/optimizer/response.ts b/src/composer/compose.ts similarity index 62% rename from components/http/src/framework/optimizer/response.ts rename to src/composer/compose.ts index fb9f33c..e293248 100644 --- a/components/http/src/framework/optimizer/response.ts +++ b/src/composer/compose.ts @@ -1,39 +1,39 @@ -import { FunRouterOptions } from "../../../types.ts"; -import { CommonRequestMorphism, RequestMorphism } from "./types.ts"; -import { parse, stringToFunction } from "../../cors/mainCORS.ts"; -import checkAsync from "./recursiveCheckAsync.ts"; -import aComposer from "./aComposer.ts"; -import mime from "../../util/mime.ts"; -import isUsing from "./tools/isUsing.ts"; +import type { FunRouterOptions } from "../options.ts"; +import type { Petition } from "../morphism.ts"; +import { parse, stringToFunction } from "../components/cors/mainCORS.ts"; +import tools from "./composerTools.ts"; +import linker from "./linker.ts"; +import mime from "../util/mime.ts"; export default (o?: FunRouterOptions) => -(f: CommonRequestMorphism | RequestMorphism) => +(f: Petition): (ctx: Request) => Promise | Response => ((elementsUsed) => ( (table) => ((composition) => - (table.headers && table.json) + (table.headers && table.json && f.type !== "request") ? composition(table.json)(table.headers)(f.f)( - aComposer(o)(f)(elementsUsed), + linker(o)(f)(elementsUsed), ) : table.headers ? composition(table.headers)(f.f)( - aComposer(o)(f)(elementsUsed), + linker(o)(f)(elementsUsed), ) : table.json ? composition(table.json)(f.f)( - aComposer(o)(f)(elementsUsed), + linker(o)(f)(elementsUsed), ) : composition(f.f)( - aComposer(o)(f)(elementsUsed), + linker(o)(f)(elementsUsed), ))( - "type" in f + f.type === "request" || f.type === "morphism" || + typeof f.type === "undefined" ? new Function(` - return f=>c=>${table.async || table.asyncResolve ? "async " : ""}r=>${ - table.async || table.asyncResolve ? "await f" : "f" - }(${table.asyncResolve ? "await c" : "c"}(${ - "mutable" in f ? "[r,{res: new Response()}]" : "r" - }))`)() + return ${table.headers ? "h=>" : ""}f=>c=>${ + table.async || table.asyncResolve ? "async " : "" + }r=>${table.async || table.asyncResolve ? "await f" : "f"}(${ + table.asyncResolve ? "await c" : "c" + }(${"mutable" in f ? "[r,{res: {}}]" : "r"}))`)() : new Function( `return ${table.headers ? "h=>" : ""}${ table.async ? "f=>" : "f=>" @@ -42,11 +42,12 @@ export default (o?: FunRouterOptions) => }r=> new Response(${ table.async || table.asyncResolve ? "await f" : "f" }(${table.asyncResolve ? "await c" : "c"}(${ - "mutable" in f ? "[r,{res: new Response()}]" : "r" + "mutable" in f ? "[r,{res: {}}]" : "r" }))${table.headers ? ",h" : ""})`, )(), ) )( + //elements int table { async: f.f.constructor.name === "AsyncFunction" || ( @@ -58,7 +59,7 @@ export default (o?: FunRouterOptions) => : false ) ), - asyncResolve: checkAsync(f) || + asyncResolve: tools.recursiveCheckAsync(f) || ( o && o.cyclePlugin && Object.keys(o.cyclePlugin || {}) .some((x) => @@ -73,8 +74,9 @@ export default (o?: FunRouterOptions) => ? { ...f.headings, headers: { - "Content-Type": - mime.find((x) => x[0] === f.headings?.headers)![1], + "Content-Type": mime.find((x) => + x[0] === f.headings?.headers + )![1], ...(o && o.cors ? stringToFunction(parse()(o.cors))() : {}), }, } @@ -88,5 +90,5 @@ export default (o?: FunRouterOptions) => : null, }, ))( - isUsing(o)(f), + tools.isUsing(o)(f)(tools.elements), ); diff --git a/src/composer/composerTools.ts b/src/composer/composerTools.ts new file mode 100644 index 0000000..aef477e --- /dev/null +++ b/src/composer/composerTools.ts @@ -0,0 +1,102 @@ +import type { Petition } from "../morphism.ts"; +import type { FunRouterOptions } from "../options.ts"; +import mainCheck from "./checkPetition/mainCheck.ts"; +type RecFunc = (f: Petition) => boolean; + +export default { + syncCheckerDir: + (joiner: (base: string) => (target: string) => string) => + (readdir: (directoryPath: string) => string[]) => + (stat: (directoryPath: string) => { isDirectory: () => boolean }) => + (dir: string): [string, boolean][] => + ( + (Y) => ( + Y((f: (arg0: string) => [string, boolean][]) => ( + (dir: string): [string, boolean][] => + readdir(dir).flatMap((item) => + stat(joiner(dir)(item)).isDirectory() + ? [[joiner(dir)(item), true], ...f(joiner(dir)(item))] + : [[joiner(dir)(item), false]] + ) as [string, boolean][] + ))(dir) + ) + )( + // Setting up the Y combinator. + (f: (arg0: (y: any) => any) => any) => + ((x) => x(x))((x: (arg0: any) => { (arg0: any): any; new (): any }) => + f((y: any) => x(x)(y)) + ), + ), + recursiveCheckAsync: + ((f: (x: RecFunc) => RecFunc) => + ((x: (arg: any) => any) => (v: any) => x(x)(v))( + (x: (arg: any) => any) => (v: any) => f((y: Petition) => x(x)(y))(v), + ))( + (solver: RecFunc) => (f: Petition) => + f.f.constructor.name === "AsyncFunction" || + ("isAsync" in f && f.isAsync) + ? true + : f.f.constructor.name === "Function" && + typeof f.resolve === "undefined" + ? false + : ("resolve" in f && f.resolve && + Object.keys(f.resolve).some((ob) => + f.resolve && + solver( + f.resolve[ob] as unknown as Petition, + ) + )) ?? + ("branch" in f && f.branch && + Object.keys(f.branch).some((ob) => + f.branch && + solver( + f.branch[ob] as unknown as Petition, + ) + )) ?? + false, + ) as unknown as (f: Petition) => boolean, + parsingToHexa: (crypto: { globalKey: string }) => + typeof crypto.globalKey === "string" + ? /^[0-9a-fA-F]+$/g.test(crypto.globalKey) + ? new Uint8Array([...crypto.globalKey].map((x) => x.charCodeAt(0))) + : new Uint8Array([...crypto.globalKey].map((x) => x.charCodeAt(0))) + : crypto.globalKey, + isUsing: + (o?: FunRouterOptions) => + (f: Petition) => + (elements: { (f: Petition): string[] }) => + mainCheck(o)(f), + elements: (f: Petition) => + f.crypto && "globalKey" in f.crypto + ? [ + "cookie", + "headers", + "randomNumber", + "hash", + "param", + "query", + "req", + "date", + "resolve", + "mutable", + "branch", + "arguments", + "token", + "verify", + "sign", + ] + : [ + "cookie", + "headers", + "randomNumber", + "hash", + "param", + "query", + "req", + "date", + "resolve", + "mutable", + "branch", + "arguments", + ], +}; diff --git a/components/http/src/framework/optimizer/aComposer.ts b/src/composer/linker.ts similarity index 75% rename from components/http/src/framework/optimizer/aComposer.ts rename to src/composer/linker.ts index 3d6e9b3..49d2a80 100644 --- a/components/http/src/framework/optimizer/aComposer.ts +++ b/src/composer/linker.ts @@ -1,7 +1,7 @@ -import checkAsync from "./recursiveCheckAsync.ts"; -import { FunRouterOptions } from "../../../types.ts"; -import { CommonRequestMorphism, RequestMorphism } from "./types.ts"; -import nativeComponets from "./nativeComponets.ts"; +import tools from "./composerTools.ts"; +import type { FunRouterOptions } from "../options.ts"; +import type { Petition } from "../morphism.ts"; +import nativeComponets from "./nativeComponents.ts"; import nativeMaps from "./nativeMaps.ts"; export type specialOptions = { @@ -9,9 +9,7 @@ export type specialOptions = { branch?: boolean; } & FunRouterOptions; -export default (o?: specialOptions) => -(f: CommonRequestMorphism | RequestMorphism) => -(ar: string[]) => +export default (o?: specialOptions) => (f: Petition) => (ar: string[]) => ar.length === 0 && !(o && "branch" in o) ? ((r: Request) => r) : ( (el) => el )( @@ -25,7 +23,7 @@ export default (o?: specialOptions) => ` return ${ table.map((x) => x.type === 1 ? x.name + "=>" : "").join("") } ${ - f.resolve && checkAsync(f) || + f.resolve && tools.recursiveCheckAsync(f) || f.f.constructor.name === "AsyncFunction" || table.some((x) => "isAsync" in x && x.isAsync === true) ? o && "branch" in o ? " r=>async b=> " : " async r=> " diff --git a/components/http/src/framework/optimizer/nativeComponets.ts b/src/composer/nativeComponents.ts similarity index 69% rename from components/http/src/framework/optimizer/nativeComponets.ts rename to src/composer/nativeComponents.ts index 3db09b5..201bee5 100644 --- a/components/http/src/framework/optimizer/nativeComponets.ts +++ b/src/composer/nativeComponents.ts @@ -1,21 +1,17 @@ -import params from "../../parameters/main.ts"; -import query from "../../queries/main.ts"; -import cookies from "../../cookies/main.ts"; +import params from "../components/parameters/main.ts"; +import query from "../components/queries/main.ts"; +import cookies from "../components/cookies/main.ts"; import resolve from "./resolve/main.ts"; import branch from "./branch/main.ts"; -import cookieToTokenMain from "../../cookieToToken/cookieToTokenMain.ts"; -import signSha256 from "../../../../jwt/signSha256.mjs"; -import verifySha256 from "../../../../jwt/verifySha256.mjs"; -import { parse, stringToFunction } from "../../cors/mainCORS.ts"; +import cookieToTokenMain from "../components/cookieToToken/cookieToTokenMain.ts"; +import signSha256 from "../components/jwt/signSha256.mjs"; +import verifySha256 from "../components/jwt/verifySha256.mjs"; +import { parse, stringToFunction } from "../components/cors/mainCORS.ts"; -import { FunRouterOptions } from "../../../types.ts"; -import { - AnyMorphismMap, - CommonRequestMorphism, - MorphismMap, - RequestMorphism, -} from "./types.ts"; -import parsingToHexa from "./tools/parsingToHexa.ts"; +import type { FunRouterOptions } from "../options.ts"; +import type { Petition } from "../morphism.ts"; + +import tools from "./composerTools.ts"; type NativeMaps = { name: string; @@ -24,7 +20,7 @@ type NativeMaps = { }; export default (o?: FunRouterOptions) => -(f: CommonRequestMorphism | RequestMorphism) => +(f: Petition) => (native: NativeMaps[]) => ((list) => native.map((x) => @@ -42,7 +38,9 @@ export default (o?: FunRouterOptions) => condition: (x: NativeMaps) => x.name === "verify", action: () => f.crypto && "globalKey" in f.crypto - ? verifySha256()(parsingToHexa(f.crypto as { globalKey: string })) + ? verifySha256()( + tools.parsingToHexa(f.crypto as { globalKey: string }), + ) : void console.error( "I don't know you got this message, contact me in discord," + " also verify will always return `false` ", @@ -52,7 +50,9 @@ export default (o?: FunRouterOptions) => condition: (x: NativeMaps) => x.name === "sign", action: () => f.crypto && "globalKey" in f.crypto - ? signSha256()(parsingToHexa(f.crypto as { globalKey: string })) + ? signSha256()( + tools.parsingToHexa(f.crypto as { globalKey: string }), + ) : void console.error( "I don't know you got this message, contact me in discord," + " also sign will always return '' ", @@ -65,7 +65,7 @@ export default (o?: FunRouterOptions) => ...f, crypto: { ...f.crypto, - globalKey: parsingToHexa(f.crypto as { globalKey: string }), + globalKey: tools.parsingToHexa(f.crypto as { globalKey: string }), }, }), }, @@ -80,16 +80,14 @@ export default (o?: FunRouterOptions) => { condition: (x: NativeMaps) => x.name === "resolve", action: () => - ("resolve" in f) - ? resolve(o)(f.path)(f.resolve as MorphismMap) - : null, + ("resolve" in f) ? resolve(o)(f.path as string)(f.resolve) : null, }, { condition: (x: NativeMaps) => x.name === "branch", action: () => ("branch" in f) ? branch({ ...o, branch: true })(f.path)( - f!.branch as AnyMorphismMap, + f!.branch, ) : null, }, diff --git a/components/http/src/framework/optimizer/nativeMaps.ts b/src/composer/nativeMaps.ts similarity index 86% rename from components/http/src/framework/optimizer/nativeMaps.ts rename to src/composer/nativeMaps.ts index 1351cad..aff5f4f 100644 --- a/components/http/src/framework/optimizer/nativeMaps.ts +++ b/src/composer/nativeMaps.ts @@ -1,9 +1,10 @@ -import { FunRouterOptions } from "../../../types.ts"; -import { CommonRequestMorphism, RequestMorphism } from "./types.ts"; -import checkAsync from "./recursiveCheckAsync.ts"; +import type { FunRouterOptions } from "../options.ts"; +import type { Petition } from "../morphism.ts"; + +import tools from "./composerTools.ts"; export default (o?: FunRouterOptions) => -(f: CommonRequestMorphism | RequestMorphism) => +(f: Petition) => (ar: string[]) => (mutable: boolean) => ([ @@ -58,13 +59,17 @@ export default (o?: FunRouterOptions) => }, { name: "resolve", - value: `${checkAsync(f) ? " await resolve(r)" : "resolve(r)"}`, + value: `${ + tools.recursiveCheckAsync(f) ? " await resolve(r)" : "resolve(r)" + }`, type: 1, }, { name: "mutable", value: mutable ? "r[1]" : "{}", type: 0 }, { name: "branch", - value: `${checkAsync(f) ? " await branch(r)" : "branch(r)"}`, + value: `${ + tools.recursiveCheckAsync(f) ? " await branch(r)" : "branch(r)" + }`, type: 1, }, { diff --git a/components/http/src/framework/optimizer/resolve/main.ts b/src/composer/resolve/main.ts similarity index 69% rename from components/http/src/framework/optimizer/resolve/main.ts rename to src/composer/resolve/main.ts index dba8a69..3f3b413 100644 --- a/components/http/src/framework/optimizer/resolve/main.ts +++ b/src/composer/resolve/main.ts @@ -1,17 +1,13 @@ -import recursiveCheck from "../checkAsync.ts"; -import { ResolveOptions, ResponseResponse } from "./types.ts"; -import { FunRouterOptions } from "../../../../types.ts"; -import { - CommonRequestMorphism, - MorphismMap, - RequestMorphism, -} from "../types.ts"; +import tools from "../composerTools.ts"; +import type { ResolveOptions, ResponseResponse } from "./types.ts"; +import type { FunRouterOptions } from "../../options.ts"; +import type { Petition, ResolveMap } from "../../morphism.ts"; import table from "./table.ts"; export default (o?: FunRouterOptions) => (path: string) => -(input: MorphismMap): ResponseResponse => +(input: ResolveMap): ResponseResponse => ( (ar) => ( @@ -31,8 +27,8 @@ export default (o?: FunRouterOptions) => )(), ) as unknown as ResponseResponse)( ar.some((x) => - recursiveCheck( - x as unknown as CommonRequestMorphism | RequestMorphism, + tools.recursiveCheckAsync( + x as unknown as Petition, ) ), ) diff --git a/src/composer/resolve/table.ts b/src/composer/resolve/table.ts new file mode 100644 index 0000000..1c0fcf8 --- /dev/null +++ b/src/composer/resolve/table.ts @@ -0,0 +1,28 @@ +import aComposer from "../linker.ts"; +import tools from "../composerTools.ts"; +import type { ResolveOptions } from "./types.ts"; +import type { FunRouterOptions } from "../../options.ts"; +import type { Petition } from "../../morphism.ts"; + +export default (o?: FunRouterOptions) => +(path: string) => +(table: ResolveOptions) => + table + .map((x) => ({ ...x, path: path })) + .map((x) => ({ + name: x.name, + f: ( + (composed: Petition["f"]) => + x.f.constructor.name === "AsyncFunction" || + composed.constructor.name === "AsyncFunction" + ? ((a) => (k: (arg0: any) => any) => async (r: any) => + await k(await a(r)))(composed)(x.f) + : ((a) => (k: (arg0: any) => any) => (r: any) => k(a(r)))( + composed, + )(x.f) + )( + aComposer(o)(x as unknown as Petition)( + tools.isUsing(o)(x as unknown as Petition)(tools.elements), + ), + ), + })); diff --git a/components/http/src/framework/optimizer/resolve/types.ts b/src/composer/resolve/types.ts similarity index 56% rename from components/http/src/framework/optimizer/resolve/types.ts rename to src/composer/resolve/types.ts index 64bea65..9e100ea 100644 --- a/components/http/src/framework/optimizer/resolve/types.ts +++ b/src/composer/resolve/types.ts @@ -1,12 +1,12 @@ -import { Morphism, PetitionOptions } from "../types.ts"; +import type { PetitionOptions, ResolveMorphism } from "../../morphism.ts"; -export type ResolveOption = Morphism & { +export type ResolveOption = ResolveMorphism & { name: string; options?: PetitionOptions; }; export type ResolveOptions = ResolveOption[]; -export type ResponseResponse = (r: Request) => unknown; +export type ResponseResponse = (r: Request) => { [key: string]: any }; export type TypeResolveOptions = | ({ async: false; f: (f: any) => BodyInit })[] diff --git a/src/composer/tokens/jSigner.ts b/src/composer/tokens/jSigner.ts new file mode 100644 index 0000000..0cc9a12 --- /dev/null +++ b/src/composer/tokens/jSigner.ts @@ -0,0 +1,24 @@ +import signer from "./signer.ts"; + +import stringify from "../../../../encode/src/stringify/safe.mjs"; + +import { SignVerifyOptions } from "./types.ts"; +import { + JsonStringify, + JsonType, +} from "../../../../encode/src/stringify/types.ts"; +export type JsonSinger = SignVerifyOptions & { + schema?: JsonStringify; +}; + +export default (o: JsonSinger) => + ( + (sign) => + "schema" in o && typeof o.schema == "object" + ? ((str) => (obj: JsonType) => sign(btoa(str(obj))))( + stringify(o.schema), + ) + : (obj: JsonType) => sign(btoa(JSON.stringify(obj))) + )( + signer(o), + ); diff --git a/src/composer/tokens/jVerify.ts b/src/composer/tokens/jVerify.ts new file mode 100644 index 0000000..45151e7 --- /dev/null +++ b/src/composer/tokens/jVerify.ts @@ -0,0 +1,14 @@ +import verifier from "./verifier.ts"; +import { SignVerifyOptions } from "./types.ts"; +type JSONVerifier = SignVerifyOptions; + +export default (o: JSONVerifier) => + typeof o.expires == "number" + ? ((v) => (s: string) => + v(s) ? JSON.parse(atob(s.substring(13, s.indexOf(".")))) : null)( + verifier(o), + ) + : ((v) => (s: string) => + v(s) ? JSON.parse(atob(s.substring(0, s.indexOf(".")))) : null)( + verifier(o), + ); diff --git a/src/composer/tokens/readme.md b/src/composer/tokens/readme.md new file mode 100644 index 0000000..528b401 --- /dev/null +++ b/src/composer/tokens/readme.md @@ -0,0 +1,215 @@ +# Vixeny's Tokens + +## Introduction + +JSON Web Tokens (JWTs) are renowned for their effectiveness and robustness in +asynchronously signing claims and authenticating with either public or private +keys. Despite their advantages, there are several inherent challenges with JWTs: + +- JWTs necessitate payload decoding from Base64 and parsing to JSON to validate + claims like expiration dates. Even though this operation is straightforward, + it could be computationally intensive, particularly in high-traffic + applications or in resource-constrained environments. +- Waiting for the entire hash to determine the validity of a token can be + inefficient as there is no mechanism for early detection of invalid tokens. + The hashing process depends on the entire message and the key. +- JWTs are not specifically designed or optimized for signing strings and they + return an "Error" upon failure, which is not always the most helpful response. + +In response to these challenges, we propose an innovative approach that draws +upon the strengths of two well-established encryption algorithms: RC4 and +Salsa20. Using these algorithms as our foundation, we developed a prototype that +utilizes a seed value to generate a unique signing and verifying algorithm for +each signer and verifier. This strategy employs the concept of function +composition, the process of crafting a complex function by integrating simpler +ones, and takes full advantage of a runtime environment, in this case, +JavaScript. + +Our proposed solution presents numerous advantages: + +- The payload does not need to be decoded to check the expiration, enhancing + performance efficiency. +- The algorithm checks the validity at each character level, enabling early + rejection of invalid tokens. +- Instead of returning an "Error", it returns "False" or "Null" depending on the + mode when an error occurs, providing more versatile error handling. +- Key strength issues become irrelevant, as the seed used to plot the unique + algorithm ensures robust security without the need for a separate secret key. +- The size of the claim decreases significantly for small claims, optimizing + data transfer efficiency. + +However, our approach is not without potential limitations: + +- The nature of the algorithm makes it synchronous, meaning only those with the + correct seed can verify the claim. +- The solution requires a minimum length of 8 for functionality. + +## Retrieving Signer and Verifier + +We provide two sets of signers and verifiers, each set tailored to handle either +JSON-like objects or plain strings. + +Let's fetch these functions: + +### Using Deno + +```ts +import jSigner from "https://deno.land/x/endofunctor/components/tokens/jSigner.ts"; +import jVerifier from "https://deno.land/x/endofunctor/components/tokens/jVerify.ts"; +import signer from "https://deno.land/x/endofunctor/components/tokens/signer.ts"; +import verifier from "https://deno.land/x/endofunctor/components/tokens/verify.ts"; +``` + +### Using Bun + +```ts +import jSigner from "vixeny/components/tokens/jSigner"; +import jVerifier from "vixeny/components/tokens/jVerify"; +import signer from "vixeny/components/tokens/signer"; +import verifier from "vixeny/components/tokens/verifier"; +``` + +## Handling Plain Claims + +We've designed this mode with session ID in mind. For optimal security, we +strongly recommend adding expiration to all your claims. + +The SignVerifyOptions type contains these parameters: + +```ts +type SignVerifyOptions = { + seed: string; + // this option allows you to fix the token size, improving performance. Note that it does not include the size of expiration - you'll need to add 13 to the size of your token. + size?: NumericRange, 40>; + // set at 4 by default, this parameter adjusts the complexity of the function. The value of 4 is four times more complex than 1. + sequence?: 1 | 2 | 3 | 4; + // add the time in ms based on Date.now() + expires?: number; +}; +``` + +Let's sign and verify a message: + +```ts +const seed = "hello", + message = "12345678", + inValidClaim = "12345678.xT7u34qa"; + +const sign = signer({ seed: seed }); +const verify = verifier({ seed: seed }); + +console.log(sign(message)); +// Output: 12345678.xT7u34qz + +console.log(verify(sign(message))); +// Output: true +console.log(verify(inValidClaim)); +// Output: false +``` + +Adding expiration: + +```ts +const seed = "hello", + message = "12345678"; + +const sign = signer({ seed: seed, expires: 10_000 }); +// This signer will generate tokens but the verifiers will always reject them. +const signExpired = signer({ seed: seed, expires: -1 }); +// The expires option does not affect the verifier directly but informs it that it should expect an expiration date. +const verify = verifier({ seed: seed, expires: 10_000 }); + +console.log(verify(sign(message))); +// Output: true +console.log(verify(signExpired(message))); +// Output: false +``` + +Working with a fixed size: + +```ts +const seed = "hello", + message = "12345678"; + +// The length of our message is 8, and the timestamp size is 13, so our size in this case will be 21. +const sign = signer({ seed: seed, expires: 10_000, size: 21 }); + +const CommonVerify = verifier({ seed: seed, expires: 10_000 }); +const verify = verifier({ seed: seed, expires: 10_000, size: 21 }); + +console.log(verify(sign(message))); +// Output: true +console.log(verify(signer(message))); +// Output: true +console.log(CommonVerify(signer(message))); +// Output: true +``` + +It's important to note: + +```ts +const seed = "hello", + message = "12345678"; + +const sign = signer({ seed: seed }); +const sign_sequence3 = signer({ seed: seed, sequence: 3 }); + +// These two signed messages will be completely different +console.log(sign(message) === sign_sequence3(message)); +// Output: false +``` + +## Managing Tokens + +The handling of tokens follows the same principle as plain claims, but setting a +fixed size isn't recommended for tokens. + +> Important: Always add an expiration time to your tokens. The following are +> merely illustrative examples: + +```ts +const seed = "hello", + obj = { hello: "world" }, + invalidToken = "eyJoZWxsbyI6IndvcmxkIn0=.rwY85qhxKJXaHivubE+sDunr"; + +const sign = jSigner({ seed: seed }); +const verify = jVerifier({ seed: seed }); + +console.log(sign(obj)); +// Output: eyJoZWxsbyI6IndvcmxkIn0=.rwY85qhxKJXaHivubE+sDunt + +console.log(verify(sign(message))); +// Output: {seed: seed } +console.log(verify(invalidToken)); +// Output: null +``` + +You can utilize a schema to accelerate the process. This is based on JSONSchema: + +```ts +const seed = "hello", + obj = { hello: "world" }; + +const sign = jSigner({ + seed: "hello", + schema: { + type: "object", + properties: { + hello: { type: "string" }, + }, + required: ["hello"], + }, +}); + +const verify = jVerifier({ seed: seed }); + +console.log(sign(obj)); +// Output: eyJoZWxsbyI6IndvcmxkIn0=.rwY85qhxKJXaHivubE+sDunt + +console.log(verify(sign(message))); +// Output: {hello: "world"} +``` + +Utilizing a schema not only makes the token generation process faster, but it +also provides a clear structure for the data encapsulated within the token, +ensuring its validity and consistency. diff --git a/src/composer/tokens/signer.ts b/src/composer/tokens/signer.ts new file mode 100644 index 0000000..8de7e8b --- /dev/null +++ b/src/composer/tokens/signer.ts @@ -0,0 +1,87 @@ +import solver from "./src/solver.ts"; + +import validChar from "../../../../util/validChar.ts"; +import { SignVerifyOptions } from "./types.ts"; +import signerWithSize from "./src/signerWithSize.ts"; + +export default (seed: SignVerifyOptions) => + ( + (ar) => + ( + (p) => + typeof seed.size == "number" + ? typeof seed.expires === "number" + ? ( + (f) => + ((n: number) => (s: string) => + f((Date.now() + n).toString() + s))(seed.expires) + )( + signerWithSize(seed.size)(ar)(p), + ) + : signerWithSize(seed.size)(ar)(p) + : typeof seed.expires === "number" + ? ((n: number) => (s: string) => + ((d) => + [...d].map((x) => x.charCodeAt(0)).map((x, i, a) => + i < 7 + ? p[ + ar[i % 8]([ + a.at(i - 7) as number, + a.at(i - 6) as number, + a.at(i - 5) as number, + a.at(i - 4) as number, + a.at(i - 3) as number, + a.at(i - 2) as number, + a.at(i - 1) as number, + x, + ]) + ] + : p[ + ar[i % 8]([ + a[i - 7], + a[i - 6], + a[i - 5], + a[i - 4], + a[i - 3], + a[i - 2], + a[i - 1], + x, + ]) + ] + ).reduce((acc, x) => acc + x, d + "."))( + (Date.now() + n).toString() + s, + ))(seed.expires) + : (s: string) => + [...s].map((x) => x.charCodeAt(0)).map((x, i, a) => + i < 7 + ? p[ + ar[i % 8]([ + a.at(i - 7) as number, + a.at(i - 6) as number, + a.at(i - 5) as number, + a.at(i - 4) as number, + a.at(i - 3) as number, + a.at(i - 2) as number, + a.at(i - 1) as number, + x, + ]) + ] + : p[ + ar[i % 8]([ + a[i - 7], + a[i - 6], + a[i - 5], + a[i - 4], + a[i - 3], + a[i - 2], + a[i - 1], + x, + ]) + ] + ).reduce((acc, x) => acc + x, s + ".") + )( + [...validChar], + ) + )( + solver(seed), + ); diff --git a/src/composer/tokens/src/crcTable.ts b/src/composer/tokens/src/crcTable.ts new file mode 100644 index 0000000..f921a58 --- /dev/null +++ b/src/composer/tokens/src/crcTable.ts @@ -0,0 +1,18 @@ +export default (start = 0xEDB88320) => + new Int32Array( + (() => + Array.from( + { length: 256 }, + (_, i) => + Array.from( + { length: 8 }, + ).reduce( + (acc: number) => ((acc & 1) + ? (start ^ (acc >>> 1)) + : (acc >>> 1) === 0 + ? 0xFFFFFFF + : (acc >>> 1)), + i, + ), + ))() as number[], + ); diff --git a/src/composer/tokens/src/digits.ts b/src/composer/tokens/src/digits.ts new file mode 100644 index 0000000..de7fe93 --- /dev/null +++ b/src/composer/tokens/src/digits.ts @@ -0,0 +1,12 @@ +import table from "./table.ts"; +import shaTable from "./tableSha.ts"; +import { SignVerifyOptions } from "../types.ts"; + +export default (txt: string) => (seed: SignVerifyOptions) => + ( + ( + new Function(` + return a=> ar => ${table(seed)(txt)} + `) + )() + )(shaTable(txt + seed.seed)) as (ar: number[]) => number; diff --git a/src/composer/tokens/src/hash.ts b/src/composer/tokens/src/hash.ts new file mode 100644 index 0000000..9efabfb --- /dev/null +++ b/src/composer/tokens/src/hash.ts @@ -0,0 +1,5 @@ +import { SignVerifyOptions } from "../types.ts"; +import crypto from "node:crypto"; + +export default (o?: SignVerifyOptions) => (message: string) => + crypto.createHash("sha256").update(message).digest("hex"); diff --git a/src/composer/tokens/src/signerWithSize.ts b/src/composer/tokens/src/signerWithSize.ts new file mode 100644 index 0000000..0ed2cdd --- /dev/null +++ b/src/composer/tokens/src/signerWithSize.ts @@ -0,0 +1,13 @@ +export default (n: number) => + new Function( + `return ar => p=> s=> ( a=>[${ + Array.from({ length: n }, (_, i) => + `p[ar[${i % 8}]([${ + Array.from({ length: 8 }, (_, j) => `a[${(n - 7 + i + j) % n}]`).join( + ",", + ) + }])]`).join(",") + }].reduce((acc,x)=> acc+x, s + "." ))([${ + Array.from({ length: n }, (_, i) => `s.charCodeAt(${i})`).join(",") + }]) `, + )(); diff --git a/src/composer/tokens/src/solver.ts b/src/composer/tokens/src/solver.ts new file mode 100644 index 0000000..49c89a7 --- /dev/null +++ b/src/composer/tokens/src/solver.ts @@ -0,0 +1,8 @@ +import digits from "./digits.ts"; +import { SignVerifyOptions } from "../types.ts"; + +export default (o: SignVerifyOptions) => + Array.from( + { length: (typeof o.size === "number" ? o.size : 8) }, + (_, i) => digits(o.seed + i)(o), + ); diff --git a/src/composer/tokens/src/table.ts b/src/composer/tokens/src/table.ts new file mode 100644 index 0000000..13a2d6e --- /dev/null +++ b/src/composer/tokens/src/table.ts @@ -0,0 +1,45 @@ +import hash from "./hash.ts"; +import { SignVerifyOptions } from "../types.ts"; + +export default (o: SignVerifyOptions) => (key: string) => + ( + (s) => + ( + (p) => + ( + (el) => + "(((" + Array.from( + { + length: 8 * (typeof o.sequence === "number" ? o.sequence : 4), + }, + (_, i) => `( ${p[i]} ^ a[ar[${el[i]}]])`, + ).join("+") + ") >>> 0) % 65)" + )( + Array.from( + { length: 16 }, + () => + Array.from( + { length: 8 }, + (_, i) => "" + i, + ), + ).flat(), + ) + )( + Array.from( + { length: 64 }, + (_, i) => "0x" + s.slice(i === 0 ? 0 : i * 8, i * 8 + 8), + ), + ) + )( + ( + (s1) => + (Array.from( + { + length: 16, + }, + (_, i) => hash(o)(s1 + " " + i), + )).join("") + )( + hash(o)(key), + ), + ); diff --git a/src/composer/tokens/src/tableSha.ts b/src/composer/tokens/src/tableSha.ts new file mode 100644 index 0000000..49107d5 --- /dev/null +++ b/src/composer/tokens/src/tableSha.ts @@ -0,0 +1,28 @@ +import hash from "./hash.ts"; + +export default (seed: string) => + ( + (s1) => + new Int32Array( + (Array.from( + { + length: 32, + }, + (_, i) => hash()(s1 + "table" + i), + )).map( + (x) => + [ + x.slice(0, 7), + x.slice(8, 15), + x.slice(16, 23), + x.slice(24, 31), + x.slice(32, 39), + x.slice(40, 47), + x.slice(48, 55), + x.slice(56, 63), + ].map((x) => Number("0x" + x)), + ).flatMap((x) => x), + ) + )( + hash()(seed), + ); diff --git a/src/composer/tokens/src/verifyWithSize.ts b/src/composer/tokens/src/verifyWithSize.ts new file mode 100644 index 0000000..82a5880 --- /dev/null +++ b/src/composer/tokens/src/verifyWithSize.ts @@ -0,0 +1,13 @@ +export default (n: number) => + new Function( + ` return ar => p=> s=>( a=> s.length===${(n * 2) + 1} &&${ + Array.from({ length: n }, (_, i) => + `p[ar[${i % 8}]([${ + Array.from({ length: 8 }, (_, j) => `a[${(n - 7 + i + j) % n}]`).join( + ",", + ) + }])] === s[${i + 1 + n}]`).join("&&") + })([${ + Array.from({ length: n }, (_, i) => `s.charCodeAt(${i})`).join(",") + }]) `, + )(); diff --git a/src/composer/tokens/types.ts b/src/composer/tokens/types.ts new file mode 100644 index 0000000..07b839f --- /dev/null +++ b/src/composer/tokens/types.ts @@ -0,0 +1,19 @@ +type CreateArrayWithLengthX< + LENGTH extends number, + ACC extends unknown[] = [], +> = ACC["length"] extends LENGTH ? ACC + : CreateArrayWithLengthX; + +type NumericRange< + START_ARR extends number[], + END extends number, + ACC extends number = never, +> = START_ARR["length"] extends END ? ACC | END + : NumericRange<[...START_ARR, 1], END, ACC | START_ARR["length"]>; + +export type SignVerifyOptions = { + seed: string; + size?: NumericRange, 40>; + sequence?: 1 | 2 | 3 | 4; + expires?: number; +}; diff --git a/src/composer/tokens/verifier.ts b/src/composer/tokens/verifier.ts new file mode 100644 index 0000000..5a85c07 --- /dev/null +++ b/src/composer/tokens/verifier.ts @@ -0,0 +1,71 @@ +import solver from "./src/solver.ts"; +import validChar from "../../../../util/validChar.ts"; +import { SignVerifyOptions } from "./types.ts"; +import verifyWithSize from "./src/verifyWithSize.ts"; + +export default (seed: SignVerifyOptions) => + ( + (ar) => + ( + (p) => + typeof seed.size == "number" + ? typeof seed.expires === "number" + ? ( + (f) => (s: string) => + Number(s.slice(0, 13)) > Date.now() && f(s) + )( + verifyWithSize(seed.size)(ar)(p), + ) + : verifyWithSize(seed.size)(ar)(p) + : ( + (f) => + typeof seed.expires === "number" + ? (s: string) => Number(s.slice(0, 13)) > Date.now() && f(s) + : f + )( + ((ar) => (p: string[]) => ((s: string) => + s.length >= 17 + ? ( + (m) => + [...s + .slice(0, m - 1)] + .map((x) => x.charCodeAt(0)) + .every((x, i, a) => + i < 7 + ? p[ + ar[i % 8]([ + a.at(i - 7) as number, + a.at(i - 6) as number, + a.at(i - 5) as number, + a.at(i - 4) as number, + a.at(i - 3) as number, + a.at(i - 2) as number, + a.at(i - 1) as number, + x, + ]) + ] === s[m + i] + : p[ + ar[i % 8]([ + a[i - 7], + a[i - 6], + a[i - 5], + a[i - 4], + a[i - 3], + a[i - 2], + a[i - 1], + x, + ]) + ] === + s[m + i] + ) + )( + (s.length / 2 >> 0) + 1, + ) + : false))(ar)(p), + ) + )( + [...validChar], + ) + )( + solver(seed), + ); diff --git a/checkTypes.ts b/src/morphism.ts similarity index 79% rename from checkTypes.ts rename to src/morphism.ts index fc1984a..cf4c73f 100644 --- a/checkTypes.ts +++ b/src/morphism.ts @@ -1,24 +1,53 @@ -import response from "./components/http/src/framework/optimizer/response"; -import { CyclePluginMap } from "./components/http/types"; -import test from "./components/util/test.mjs"; -import { FunRouterOptions } from "./types"; +import type { CyclePluginMap, FunRouterOptions } from "./options.ts"; +import compose from "./composer/compose.ts"; + +export type Petition = Morphism< + { + isAPetition: true; + type: typeMorphisim; + hasPath: true; + }, + any, + any, + any, + any, + any, + any, + any, + any +>; +export type ResolveMorphism = Morphism< + { + type: "morphism"; + }, + any, + any, + any, + any, + any, + any, + any, + any +>; export const petitions = { - standart: (O?: RO) => < - RM extends ResolveMap, - BM extends BranchMap, + standart: (O?: RO) => + < + RM extends ResolveMap, + BM extends BranchMap, QO extends QueryOptions, PO extends ParamOptions, RO extends FunRouterOptions, CO extends CryptoOptions, AR = any, - R = any >( + R = any, + >( I: Morphism< { type: "request"; hasPath: true; isAPetition: true; - typeNotNeeded: true + typeNotNeeded: true; }, RM, BM, @@ -29,23 +58,11 @@ export const petitions = { AR, R >, - ): Morphism<{ - type: "request"; - hasPath: true; - isAPetition: true; - }, - RM, - BM, - QO, - PO, - RO, - CO, - AR, - R - > => ({ ...I, type: "request" }), - common: (O?: RO) => < - RM extends ResolveMap, - BM extends BranchMap, + ) => ({ ...I, type: "request" }) as unknown as Petition, + common: (O?: RO) => + < + RM extends ResolveMap, + BM extends BranchMap, QO extends QueryOptions, PO extends ParamOptions, RO extends FunRouterOptions, @@ -69,23 +86,11 @@ export const petitions = { AR, R >, - ): Morphism<{ - type: "base"; - hasPath: true; - isAPetition: true; - }, - RM, - BM, - QO, - PO, - RO, - CO, - AR, - R> => ({ ...I, type: "base" }), + ) => ({ ...I, type: "base" }) as unknown as Petition, response: (O?: RO) => < - RM extends ResolveMap, - BM extends BranchMap, + RM extends ResolveMap, + BM extends BranchMap, QO extends QueryOptions, PO extends ParamOptions, RO extends FunRouterOptions, @@ -93,14 +98,10 @@ export const petitions = { AR = any, R = any, >(I: { - path: string; - f: { - (ctx: Request): Response | Promise; - }; + f: { (ctx: Request): Response | Promise }; }): Morphism< { type: "response"; - hasPath: true; }, RM, BM, @@ -113,8 +114,8 @@ export const petitions = { > => ({ ...I, type: "response" }), resolve: (O?: RO) => < - RM extends ResolveMap, - BM extends BranchMap, + RM extends ResolveMap, + BM extends BranchMap, QO extends QueryOptions, PO extends ParamOptions, RO extends FunRouterOptions, @@ -138,8 +139,8 @@ export const petitions = { ) => I, branch: (O?: RO) => < - RM extends ResolveMap, - BM extends BranchMap, + RM extends ResolveMap, + BM extends BranchMap, QO extends QueryOptions, PO extends ParamOptions, RO extends FunRouterOptions, @@ -162,75 +163,124 @@ export const petitions = { R >, ) => I, + join: (O?: RO) => + < + RM extends ResolveMap, + BM extends BranchMap, + QO extends QueryOptions, + PO extends ParamOptions, + RO extends FunRouterOptions, + CO extends CryptoOptions, + AT = any, + R = any, + >( + A: Morphism< + { + isAPetition: true; + hasPath: true; + }, + RM, + BM, + QO, + PO, + RO, + CO, + AT, + R + >[], + ) => + < + RM extends ResolveMap, + BM extends BranchMap, + QO extends QueryOptions, + PO extends ParamOptions, + RO extends FunRouterOptions, + CO extends CryptoOptions, + AT = any, + R = any, + >( + B: + | Morphism< + { + isAPetition: true; + hasPath: true; + }, + RM, + BM, + QO, + PO, + RO, + CO, + AT, + R + > + | { + type: typeMorphisim; + path: string; + f: any; + }, + ) => [ + ...A, + B as Morphism< + { + isAPetition: true; + hasPath: true; + }, + RM, + BM, + QO, + PO, + RO, + CO, + AT, + R + >, + ], }; -const a = petitions.branch()({ - arguments: 'string', - crypto: { - globalKey: 'hello' - }, - f(ctx) { - return new Response(ctx.arguments); - }, -}); - - -const b = petitions.standart()({ - path: '/string', - branch: { - test: a - }, - f(ctx) { - - return ctx.branch.test('hello') - } -}) -const c:Morphism<{ - isAPetition: true -},any,any,any,any,any,any,any,any>[] = [b] - - type typeMorphisim = "response" | "request" | "morphism" | "base"; -type ResolveMap = { - [key: string]: Morphism< +export type ResolveMap = { + [K in keyof T]: T[K] extends Morphism< { type: "morphism"; - }, - any, - any, - any, - any, - any, - any, - any, - any - >; + } + > ? Morphism< + { + type: "morphism"; + } + > + : T[K] extends { f: any } ? any + : never; }; -type BranchMap = { - [key: string]: Morphism< +const composed = { + resolve: 1, + branch: 1, +}; + +export type BranchMap = { + [K in keyof T]: T[K] extends Morphism< { type: "morphism"; branch: true; - }, - any, - any, - any, - any, - any, - any, - any, - any - >; + } + > ? Morphism< + { + type: "morphism"; + branch: true; + } + > + : T[K] extends { f: any } ? any + : never; }; type MapOptions = { - hasPath?: true; - typeNotNeeded?: true; + hasPath?: boolean; + typeNotNeeded?: boolean; type?: typeMorphisim; - branch?: true; - isAPetition?: true; + branch?: boolean; + isAPetition?: boolean; }; type HasPath

= P extends { hasPath: true } @@ -245,10 +295,25 @@ type HasType

= P extends { type: typeMorphisim } type ExtraKeys

= HasPath

& HasType

; +type PetitionHeader = { + /** + * The headers initialization. + */ + headers?: HeadersInit | defaultMime; + /** + * The status text. + */ + statusText?: string; + /** + * The status number. + */ + status?: number; +}; + type Morphism< MO extends MapOptions = MapOptions, - RM extends ResolveMap = ResolveMap, - BM extends BranchMap = BranchMap, + RM extends ResolveMap = any, + BM extends BranchMap = any, QO extends QueryOptions = QueryOptions, PO extends ParamOptions = ParamOptions, RO extends FunRouterOptions = FunRouterOptions, @@ -262,6 +327,7 @@ type Morphism< readonly arguments?: MO extends { branch: true } ? AT : never; readonly query?: QO; readonly param?: PO; + readonly headings?: PetitionHeader; readonly isAsync?: MO["isAPetition"] extends true ? true : never; readonly options?: PetitionOptions< [Extract], @@ -310,7 +376,7 @@ type AddOption = | "arguments" | "headers"; -type PetitionOptions< +export type PetitionOptions< T extends string[], CR extends CryptoOptions, > = { @@ -345,8 +411,8 @@ type specialElements = { } | {}; type WithPlugins< - R extends ResolveMap, - B extends BranchMap, + R extends ResolveMap, + B extends BranchMap, QS extends QueryOptions, PA extends ParamOptions, O extends FunRouterOptions, @@ -365,10 +431,10 @@ type WithPlugins< type CyclePlugingFunctions = { [K in keyof CPM]: CPM[K] extends { isFunction: boolean; f: (...args: any) => any } - ? ReturnType> + ? ReturnType> // Direct function case : CPM[K] extends { f: (...args: any) => any } - ? Awaited>>> - : never; + ? Awaited>>> // Nested function case + : never; // Handle cases that do not match expected structure }; type SignerAndVarifier = { @@ -381,22 +447,22 @@ type CryptoContext = CR extends token: { [K in keyof Token]: Record }; } & SignerAndVarifier : { - sign?: never; - verify?: never; - token?: never; + sign: any; + verify: any; + token: any; } : CR extends { globalKey: any } ? { token: Record | null>; } & SignerAndVarifier : { - sign?: never; - verify?: never; - token?: never; + sign: any; + verify: any; + token: any; }; interface Ctx< - R extends ResolveMap, - B extends BranchMap, + R extends ResolveMap, + B extends BranchMap, QS extends QueryOptions, PA extends ParamOptions, O extends FunRouterOptions, @@ -823,7 +889,43 @@ type CryptoOptions = { }; } | {}; -type SupportedKeys = +type StaticFilePlugin = { + checker: (path: string) => boolean; + async?: boolean; + r: (options: { + root: string; + path: string; + relativeName: string; + }) => typeof petitions.response; +}; + +/** + * Object for raw response static. + */ +export type fileServerPetition = + & ({ + type: "fileServer"; + name: string; + path: string; + } | { + type: "fileServer"; + name: string; + path: string; + mime?: true; + extra: [string, string][]; + } | { + type: "fileServer"; + name: string; + path: string; + mime: false; + }) + & { + template?: StaticFilePlugin[]; + removeExtensionOf?: defaultMime[]; + slashIs?: string; + }; + +export type SupportedKeys = | string | Uint8Array | Uint8ClampedArray @@ -836,3 +938,79 @@ type SupportedKeys = | BigInt64Array | Float32Array | Float64Array; + +export type defaultMime = + | ".aac" + | ".abw" + | ".arc" + | ".avif" + | ".avi" + | ".azw" + | ".azw" + | ".bmp" + | ".bz" + | ".bz2" + | ".cda" + | ".csh" + | ".css" + | ".csv" + | ".doc" + | ".docx" + | ".eot" + | ".epub" + | ".gz" + | ".gif" + | ".htm" + | ".html" + | ".ico" + | ".ics" + | ".jar" + | ".jpeg" + | ".js" + | ".json" + | ".jsonld" + | ".mid" + | ".mjs" + | ".mp3" + | ".mp4" + | ".mpeg" + | ".mpkg" + | ".odp" + | ".ods" + | ".odt" + | ".oga" + | ".ogv" + | ".ogx" + | ".opus" + | ".otf" + | ".png" + | ".pdf" + | ".php" + | ".ppt" + | ".pptx" + | ".rar" + | ".rtf" + | ".sh" + | ".svg" + | ".tar" + | ".tif" + | ".tiff" + | ".ts" + | ".ttf" + | ".txt" + | ".vsd" + | ".wav" + | ".weba" + | ".webm" + | ".webp" + | ".woff" + | ".woff2" + | ".xhtml" + | ".xls" + | ".xlsx" + | ".xml" + | ".xul" + | ".zip" + | ".3gp" + | ".3g2" + | ".7z"; diff --git a/src/options.ts b/src/options.ts new file mode 100644 index 0000000..681cd68 --- /dev/null +++ b/src/options.ts @@ -0,0 +1,97 @@ +import type { CORSOptions } from "./components/cors/types.ts"; +import type { Petition } from "./morphism.ts"; +/** + * Options for the router, it is `optional` + * ```ts + * import vixeny from "vixeny/fun" + * import pettitions from "./someWhere" + * vixeny({ + * hasName: "http://127.0.0.1:8080/", + * 404: r => new Response("Insert Not Found"), + * 405: r => new Response("Insert Bad Method"), + * //default + * paramsStartsWith: ":" + * })(...pettions) + * ``` + */ +export type FunRouterOptions = { + /** + * Optimize the router. It always has to end with "/" + * + * ```ts + * //Correct + * { + * "http://127.0.0.1:8080/" + * } + * //Incorrect + * { + * "http://127.0.0.1:8080" + * } + * ``` + */ + readonly hasName?: string; + readonly cors?: CORSOptions; + readonly router?: { + /** + * When true, the router treats URLs with a trailing slash as distinct from those without. For example, /hello and /hello/ are considered different routes, and each can have its own handler. + */ + strictTrailingSlash?: false; + }; + /** + * When an URL with a valid URL is detected and used it will be added to yout `context` + * + * ```ts + * // default + * { + * path: "/route/:name", + * f: ctx => ctx.param.name + * } + * // this behavour can change with this option, for example: + * { + * path: "/route/-name", + * f: ctx => ctx.param.name + * } + * // assuming that `paramsStartsWith` was set to `-`, this option only takes the first character + * ``` + */ + readonly paramsStartsWith?: string; + readonly stateFlags?: { + isFileServer?: true; + slashIs?: string; + isWild?: true; + }; + readonly retruns?: any; + readonly enableLiveReloading?: true; + /** + * A function that takes a Request and returns a Response for 404 errors. + */ + 404?: (x: Request) => Response; + + /** + * A function that takes a Request and returns a Response for 405 errors. + */ + 405?: (x: Request) => Response; + + readonly cyclePlugin?: CyclePluginMap; +}; + +export type CyclePluginMap = { + readonly [key: string]: CyclePlugin; +}; + +export type CyclePlugin = { + readonly name: symbol; + readonly f: ( + o?: FunRouterOptions, + ) => ( + p: Petition, + ) => (r: Request | [Request, Record]) => any; + readonly type: unknown; + readonly options?: { [k: string]: any }; +} | { + readonly name: symbol; + readonly isFunction: true; + readonly f: (o?: FunRouterOptions) => (p: Petition) => any; + readonly type: unknown; + readonly options?: { [k: string]: any }; +} | {}; diff --git a/src/runtime/name.mjs b/src/runtime/name.mjs new file mode 100644 index 0000000..505073e --- /dev/null +++ b/src/runtime/name.mjs @@ -0,0 +1,26 @@ +/** + * Identifies the current JavaScript runtime environment. It prioritizes the detection in the following order: + * 1. Bun + * 2. Deno + * + * By default, if neither Bun nor Deno environments are detected, it assumes the Node environment. + * + * This function is useful for applications that need to adapt their behavior based on the runtime environment. + * + * ```js + * import { runtime } from 'vixeny' + * + * //logging runtime name + * console.log( + * runtime.name() + * ) + * + * ``` + */ + +export default () => + typeof Bun !== "undefined" + ? "Bun" + : typeof Bun !== "undefined" + ? "Deno" + : "Node"; diff --git a/src/runtime/parseArguments.ts b/src/runtime/parseArguments.ts new file mode 100644 index 0000000..cfa4e30 --- /dev/null +++ b/src/runtime/parseArguments.ts @@ -0,0 +1,42 @@ +/** + * Parses command-line arguments passed to the script and returns an object with key-value pairs. + * For flags without explicit values, the value is set to `true`. + * + * This function is designed to work with both Deno and Node.js environments, automatically detecting + * the environment to use the appropriate source of arguments. + * + * Example usage in Node.js: + * ```bash + * node script.js --name=John --age=30 --admin + * ``` + * This would result in: `{ name: "John", age: "30", admin: true }` + * + * Example usage in Deno: + * ```bash + * deno run script.js --name=John --age=30 --admin + * ``` + * Similar output as Node.js example. + * + * @returns {ParsedArgs} An object representing the parsed command-line arguments. + */ + +//TODO: introduce `name` and add , interface +type ParsedArgs = { + [key: string]: string | boolean; +}; + +export default (): ParsedArgs => + //@ts-ignore + (typeof Deno !== "undefined" ? Deno.args : process.argv.slice(2)) + .map<[string, string | boolean]>((arg: string) => + arg.startsWith("--") + ? arg.slice(2).split("=") as [string, string] + : [arg, true] + ) + .reduce( + (acc: ParsedArgs, [key, value]: [string, string | boolean]) => ({ + ...acc, + [key]: value === undefined ? true : value, + }), + {}, + ); diff --git a/components/http/src/framework/optimizer/checkRead_Deno.ts b/src/staticFiles/checkRead_Deno.ts similarity index 84% rename from components/http/src/framework/optimizer/checkRead_Deno.ts rename to src/staticFiles/checkRead_Deno.ts index 58dcdac..450a447 100644 --- a/components/http/src/framework/optimizer/checkRead_Deno.ts +++ b/src/staticFiles/checkRead_Deno.ts @@ -6,10 +6,8 @@ type FsPromisesType = { export default await (async () => // deno-lint-ignore no-async-promise-executor await new Promise(async (resolve, err) => - typeof Deno === "object" - ? err() - //@ts-ignore - : resolve(await import("fs")) + //@ts-ignore + resolve(await import("fs")) ))().then((x: unknown) => ({ getFiles: (x as { @@ -22,4 +20,4 @@ export default await (async () => stat: (directoryPath: string) => { isDirectory: () => boolean }; }).statSync, }) as FsPromisesType - ).catch(() => "Deno"); + ); diff --git a/components/http/src/framework/optimizer/staticFiles/composedPaths.ts b/src/staticFiles/composedPaths.ts similarity index 89% rename from components/http/src/framework/optimizer/staticFiles/composedPaths.ts rename to src/staticFiles/composedPaths.ts index 3fa8582..44a34fe 100644 --- a/components/http/src/framework/optimizer/staticFiles/composedPaths.ts +++ b/src/staticFiles/composedPaths.ts @@ -1,7 +1,9 @@ -import { ObjectRawResponseStatic, Petition } from "../types.ts"; +import type { fileServerPetition, Petition } from "../morphism.ts"; import getMime from "./getMime.ts"; -export default (f: ObjectRawResponseStatic) => +//TODO: make it more redable 🙏 + +export default (f: fileServerPetition) => (name: string) => (root: string) => (paths: string[]) => @@ -26,6 +28,7 @@ export default (f: ObjectRawResponseStatic) => path: root.slice(1, -1) + x.slice(name.length - 1), type: "response", r: (new Function( + //@ts-ignore typeof Deno === "object" ? `return async () => new Response( await Deno.readFile("${x}"), {headers: {'Content-Type': '${ checker("." + x.split(".").at(-1)) @@ -39,12 +42,13 @@ export default (f: ObjectRawResponseStatic) => y.checker(root.slice(1, -1) + x.slice(name.length - 1)) ), ), - ) + ) as unknown as Petition[] : paths.map( (x) => ({ path: root.slice(1, -1) + x.slice(name.length - 1), type: "response", r: (new Function( + //@ts-ignore typeof Deno === "object" ? `return async () => new Response( await Deno.readFile("${x}"), {headers: {'Content-Type': '${ checker("." + x.split(".").at(-1)) @@ -54,7 +58,7 @@ export default (f: ObjectRawResponseStatic) => }'}})`, ))() as () => Promise, }), - ) + ) as unknown as Petition[] )( getMime(mimes), ) @@ -75,6 +79,7 @@ export default (f: ObjectRawResponseStatic) => path: root.slice(1, -1) + x.slice(name.length - 1), type: "response", r: (new Function( + //@ts-ignore typeof Deno === "object" ? `return async () => new Response( await Deno.readFile("${x}"))` : `return async ()=> new Response(await ( Bun.file("${x}")).arrayBuffer())`, @@ -84,15 +89,16 @@ export default (f: ObjectRawResponseStatic) => y.checker(root.slice(1, -1) + x.slice(name.length - 1)) ), ), - ) + ) as unknown as Petition[] : paths.map( (x) => ({ path: root.slice(1, -1) + x.slice(name.length - 1), type: "response", r: (new Function( + //@ts-ignore typeof Deno === "object" ? `return async () => new Response( await Deno.readFile("${x}"))` : `return async ()=> new Response(await ( Bun.file("${x}")).arrayBuffer())`, ))() as () => Promise, }), - ); + ) as unknown as Petition[]; diff --git a/src/staticFiles/getDir.ts b/src/staticFiles/getDir.ts new file mode 100644 index 0000000..62e9ef6 --- /dev/null +++ b/src/staticFiles/getDir.ts @@ -0,0 +1,8 @@ +import bunSyncCheckDir from "./transverseFiles.ts"; +import denoCheckRead from "./checkRead_Deno.ts"; +import joiner from "./joiner.ts"; + +export default (s: string) => + bunSyncCheckDir(joiner)(denoCheckRead.getFiles)(denoCheckRead.stats)( + s, + ).map((y) => y[0]).flat(); diff --git a/components/http/src/framework/optimizer/staticFiles/getMime.ts b/src/staticFiles/getMime.ts similarity index 100% rename from components/http/src/framework/optimizer/staticFiles/getMime.ts rename to src/staticFiles/getMime.ts diff --git a/src/staticFiles/joiner.ts b/src/staticFiles/joiner.ts new file mode 100644 index 0000000..2aba635 --- /dev/null +++ b/src/staticFiles/joiner.ts @@ -0,0 +1,2 @@ +export default (base: string) => (target: string): string => + base.endsWith("/") ? base + target : base + "/" + target; diff --git a/components/http/src/framework/optimizer/staticFiles/main.ts b/src/staticFiles/main.ts similarity index 79% rename from components/http/src/framework/optimizer/staticFiles/main.ts rename to src/staticFiles/main.ts index bb8e9d7..a29b4eb 100644 --- a/components/http/src/framework/optimizer/staticFiles/main.ts +++ b/src/staticFiles/main.ts @@ -1,10 +1,10 @@ -import { ObjectRawResponseStatic } from "../types.ts"; +import type { fileServerPetition } from "../morphism.ts"; import composedPaths from "./composedPaths.ts"; import getDir from "./getDir.ts"; -import mime from "./mime.ts"; +import mime from "./mimeFrom.ts"; import removeExtensionOf from "./removeExtensionOf.ts"; -export default (f: ObjectRawResponseStatic) => +export default (f: fileServerPetition) => ( (rectify) => removeExtensionOf(f)( diff --git a/src/staticFiles/mimeFrom.ts b/src/staticFiles/mimeFrom.ts new file mode 100644 index 0000000..ad8e8ef --- /dev/null +++ b/src/staticFiles/mimeFrom.ts @@ -0,0 +1,10 @@ +import mime from "../util/mime.ts"; + +import type { fileServerPetition } from "../morphism.ts"; + +export default (f: fileServerPetition) => + "mime" in f && f.mime === false + ? [] + : "extra" in f + ? mime.concat(f.extra) + : mime; diff --git a/components/http/src/framework/optimizer/staticFiles/removeExtensionOf.ts b/src/staticFiles/removeExtensionOf.ts similarity index 75% rename from components/http/src/framework/optimizer/staticFiles/removeExtensionOf.ts rename to src/staticFiles/removeExtensionOf.ts index 0f4c85b..90aeb5d 100644 --- a/components/http/src/framework/optimizer/staticFiles/removeExtensionOf.ts +++ b/src/staticFiles/removeExtensionOf.ts @@ -1,6 +1,6 @@ -import { defaultMime, ObjectRawResponseStatic, Petition } from "../types.ts"; +import type { defaultMime, fileServerPetition, Petition } from "../morphism.ts"; -export default (f: ObjectRawResponseStatic) => (petitions: Petition[]) => +export default (f: fileServerPetition) => (petitions: Petition[]) => f.removeExtensionOf && Array.isArray(f.removeExtensionOf) ? petitions.map( (x) => diff --git a/components/http/src/framework/optimizer/syncCheckDir_Bun.ts b/src/staticFiles/transverseFiles.ts similarity index 100% rename from components/http/src/framework/optimizer/syncCheckDir_Bun.ts rename to src/staticFiles/transverseFiles.ts diff --git a/src/util/mime.ts b/src/util/mime.ts new file mode 100644 index 0000000..1119853 --- /dev/null +++ b/src/util/mime.ts @@ -0,0 +1,85 @@ +export default [ + [".aac", "audio/aac"], + [".abw", "application/x-abiword"], + [".arc", "application/x-freearc"], + [".avif", "image/avif"], + [".avi", "video/x-msvideo"], + [".azw", "application/vnd.amazon.ebook"], + [".azw", "application/vnd.amazon.ebook"], + [".bmp", "image/bmp"], + [".bz", "application/x-bzip"], + [".bz2", "application/x-bzip2"], + [".cda", "application/x-cdf"], + [".csh", "application/x-csh"], + [".css", "text/css"], + [".csv", "text/csv"], + [".doc", "application/msword"], + [ + ".docx", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ], + [".eot", "application/vnd.ms-fontobject"], + [".epub", "application/epub+zip"], + [".gz", "application/gzip"], + [".gif", "image/gif"], + [".htm", "text/html"], + [".html", "text/html"], + [".ico", "image/x-icon"], + [".ics", "text/calendar"], + [".jar", "application/java-archive"], + [".jpeg", "image/jpeg"], + [".js", "text/javascript"], + [".json", "application/json"], + [".jsonld", "application/ld+json"], + [".mid", "audio/x-midi"], + [".mjs", "text/javascript"], + [".mp3", "audio/mpeg"], + [".mp4", "video/mp4"], + [".mpeg", "video/mpeg"], + [".mpkg", "application/vnd.apple.installer+xml"], + [".odp", "application/vnd.oasis.opendocument.presentation"], + [".ods", "application/vnd.oasis.opendocument.spreadsheet"], + [".odt", "application/vnd.oasis.opendocument.text"], + [".oga", "audio/ogg"], + [".ogv", "video/ogg"], + [".ogx", "application/ogg"], + [".opus", "audio/opus"], + [".otf", "font/otf"], + [".png", "image/png"], + [".pdf", "application/pdf"], + [".php", "application/x-httpd-php"], + [".ppt", "application/vnd.ms-powerpoint"], + [ + ".pptx", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + ], + [".rar", "application/vnd.rar"], + [".rtf", "application/rtf"], + [".sh", "application/x-sh"], + [".svg", "image/svg+xml"], + [".tar", "application/x-tar"], + [".tif", "image/tiff"], + [".tiff", "image/tiff"], + [".ts", "video/mp2t"], + [".ttf", "font/ttf"], + [".txt", "text/plain"], + [".vsd", "application/vnd.visio"], + [".wav", "audio/wav"], + [".weba", "audio/webm"], + [".webm", "video/webm"], + [".webp", "image/webp"], + [".woff", "font/woff"], + [".woff2", "font/woff2"], + [".xhtml", "application/xhtml+xml"], + [".xls", "application/vnd.ms-excel"], + [ + ".xlsx", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ], + [".xml", "application/xml"], + [".xul", "application/vnd.mozilla.xul+xml"], + [".zip", "archive application/zip"], + [".3gp", "audio/video"], + [".3g2", "audio/video"], + [".7z", "application/x-7z-compressed"], +] as [string, string][]; diff --git a/test/composer/checker.test.ts b/test/composer/checker.test.ts new file mode 100644 index 0000000..f5ae453 --- /dev/null +++ b/test/composer/checker.test.ts @@ -0,0 +1,174 @@ +import mainCheck from "../../src/composer/checkPetition/mainCheck.ts"; +import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; +import { petitions } from "../../src/morphism.ts"; + +// Test +Deno.test("check behaivour", async () => { + + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + f: () => 'query' + })) + ,[]); + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + f: ctx => ctx.query.hello ?? 'hello' + })) + ,['query']); + + }); + + +Deno.test("check only behaivour", async () => { + + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + only: ['query'] + }, + f: () => 'someString' + })) + ,['query']); + + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + only: ['query'] + }, + f: ctx => ctx.param.hello + })) + ,['query']); + + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + only: ['query'] + }, + f: ctx => ctx.query.hello ?? 'hello' + })) + ,['query']); + + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + remove: ["query"], + only: ['query'] + }, + f: ctx => ctx.query.hello ?? 'hello' + })) + ,['query']); + + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + add: ["query"], + only: ['query'] + }, + f: ctx => ctx.query.hello ?? 'hello' + })) + ,['query']); + + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + remove: ["query"], + add: ['query'], + only: ['query'] + }, + f: ctx => ctx.query.hello ?? 'hello' + })) + ,['query']); + + + }); + + Deno.test("check remove behaivour", async () => { + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + remove: ["query"], + }, + f: ctx => ctx.query.param ?? 'hello' + })), + ['param'] + ) + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + remove: ["query","param"], + }, + f: ctx => ctx.query.param ?? 'hello' + })), + [] + ) + //duplicate + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + remove: ["query","param","query"], + }, + f: ctx => ctx.query.param ?? 'hello' + })), + [] + ) + //value not needed + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + remove: ["query","param","query", "req"], + }, + f: ctx => ctx.query.param ?? 'hello' + })), + [] + ) + }) + + + Deno.test("check remove behaivour", async () => { + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + add: ['req'], + }, + f: () => 'hello' + })), + ['req'] + ) + //duplicates + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + add: ['req','req'], + }, + f: () => 'hello' + })), + ['req'] + ) + //overrides remove and it's unique + assertEquals( + mainCheck()(petitions.common()({ + path:'/test', + options: { + remove: ['req'], + add: ['req','req'], + }, + f: () => 'hello' + })), + ['req'] + ) + }) + diff --git a/test/composer/morphisim.test.ts b/test/composer/morphisim.test.ts new file mode 100644 index 0000000..90999b1 --- /dev/null +++ b/test/composer/morphisim.test.ts @@ -0,0 +1,85 @@ +import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; +import resolve from "../../src/composer/resolve/main.ts"; +import branch from "../../src/composer/branch/main.ts"; +import { petitions } from "../../src/morphism.ts"; + +// Resolve + +const nestedResolve = petitions.resolve()({ + f: (_) => "syncResolve", +}); + +const syncResolve = petitions.resolve()({ + resolve: { + sync: nestedResolve, + }, + f: (ctx) => ctx.resolve.sync, +}); + +const asyncNestedResolve = petitions.resolve()({ + f: async (f) => await f.req.json(), +}); + +const asyncResolve = petitions.resolve()({ + resolve: { + async: asyncNestedResolve, + }, + f: (ctx) => ctx.resolve.async, +}); + +// Branch + +const nestedBranch = petitions.branch()({ + arguments: "string", + f: (ctx) => ctx.arguments, +}); + +const asyncNestedBranch = petitions.branch()({ + f: async (ctx) => await ctx.req.json(), +}); + +// Test +Deno.test("sync resolve", async () => { + const map = await resolve()("test")({ + nestedResolve: nestedResolve, + sync: syncResolve, + })(new Request("http://test/")); + + assertEquals(map.sync, map.nestedResolve); +}); + +Deno.test("async resolve", async () => { + const map = await resolve()("test")({ + nestedResolve: asyncResolve, + })( + new Request("http://test/", { + body: '{"hello":1}', + method: "POST", + }), + ); + + assertEquals(map.nestedResolve.hello, 1); +}); + +// Branch + +Deno.test("sync branch", async () => { + const map = await branch()("test")({ + sync: nestedBranch, + })(new Request("http://test/")); + + assertEquals(map.sync("sync"), "sync"); +}); + +Deno.test("async branch", async () => { + const map = await branch()("test")({ + async: asyncNestedBranch, + })( + new Request("http://test/", { + body: '{"hello":1}', + method: "POST", + }), + ) + + assertEquals(await map.async(), {hello: 1}); +}); diff --git a/test/composer/petitions.test.ts b/test/composer/petitions.test.ts new file mode 100644 index 0000000..a0ca123 --- /dev/null +++ b/test/composer/petitions.test.ts @@ -0,0 +1,163 @@ +import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; +import compose from "../../src/composer/compose.ts"; +import { petitions } from "../../src/morphism.ts"; + +const nestedResolve = petitions.resolve()({ + f: (_) => "syncResolve", +}); +const syncResolve = petitions.resolve()({ + resolve: { + sync: nestedResolve, + }, + f: (ctx) => ctx.resolve.sync, +}); + +const asyncNestedResolve = petitions.resolve()({ + f: async (c) => await c.req.json() as Object, +}); + +const asyncResolve = petitions.resolve()({ + resolve: { + async: asyncNestedResolve, + }, + f: (ctx) => ctx.resolve.async, +}); + +Deno.test("base case", async () => { + const base = await compose()({ + type: "base", + path: "/", + f: (_) => "base", + })(new Request("http://hello.com/")); + + const baseWithHeadings = await compose()({ + type: "base", + path: "/", + headings: { + status: 201, + statusText: "statusTextBase", + headers: ".html", + }, + f: (_) => "baseWithHeadings", + })(new Request("http://hello.com/")); + + assertEquals(await base.text(), "base"); + assertEquals(base.status, 200); + + assertEquals(await baseWithHeadings.text(), "baseWithHeadings"); + assertEquals(baseWithHeadings.status, 201); + assertEquals(baseWithHeadings.statusText, "statusTextBase"); + assertEquals(baseWithHeadings.headers.get("content-type"), "text/html"); +}); + +Deno.test("base case with resolve", async () => { + const baseResponse = petitions.common()({ + path: "/", + resolve: { + sync: syncResolve, + }, + f: (ctx) => ctx.resolve.sync, + }); + + const base = await compose()( + baseResponse, + )(new Request("http://hello.com/")); + + assertEquals(await base.text(), "syncResolve"); + assertEquals(base.status, 200); +}); + +Deno.test("base case with async resolve", async () => { + const baseResponse = petitions.common()({ + path: "/", + resolve: { + async: asyncResolve, + }, + f: (ctx) => JSON.stringify(ctx.resolve.async), + }); + + const base = await compose()( + baseResponse, + )( + new Request("http://test/", { + body: '{"hello":1}', + method: "POST", + }), + ); + + assertEquals(await base.json(), { "hello": 1 }); + assertEquals(base.status, 200); +}); + +Deno.test("standart case", async () => { + const base = await compose()({ + type: "request", + path: "/", + f: (_) => new Response("request"), + })(new Request("http://hello.com/")); + + + + assertEquals(await base.text(), "request"); + assertEquals(base.status, 200); + + +}); + + +Deno.test("standart case with resolve", async () => { + const baseResponse = petitions.standart()({ + path: "/", + resolve: { + sync: syncResolve, + }, + f: (ctx) => new Response(ctx.resolve.sync), + }); + + const base = await compose()( + baseResponse, + )(new Request("http://hello.com/")); + + assertEquals(await base.text(), "syncResolve"); + assertEquals(base.status, 200); +}); + +Deno.test("standart case with resolve", async () => { + const baseResponse = petitions.standart()({ + path: "/", + resolve: { + sync: syncResolve, + }, + f: (ctx) => new Response(ctx.resolve.sync), + }); + + const base = await compose()( + baseResponse, + )(new Request("http://hello.com/")); + + assertEquals(await base.text(), "syncResolve"); + assertEquals(base.status, 200); +}); + + +Deno.test("standart case with async resolve", async () => { + const baseResponse = petitions.standart()({ + path: "/", + resolve: { + async: asyncResolve, + }, + f: (ctx) => new Response(JSON.stringify(ctx.resolve.async)), + }); + + const base = await compose()( + baseResponse, + )( + new Request("http://test/", { + body: '{"hello":1}', + method: "POST", + }), + ); + + assertEquals(await base.json(), { "hello": 1 }); + assertEquals(base.status, 200); +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 24328df..238655f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,27 @@ { "compilerOptions": { - "module": "ESNext", - "esModuleInterop": true, + // Enable latest features + "lib": ["ESNext", "DOM"], "target": "ESNext", - "skipLibCheck": true, + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", "allowImportingTsExtensions": true, - "resolveJsonModule": true - // Removed "type": "module" as it's not a standard compiler option - // Removed "types": ["bun-types"] to align with Deno - }, - "exclude": [ - "node_modules" // Exclude node_modules if present - ] + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } }