diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..187282b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.env +.proto \ No newline at end of file diff --git a/README.md b/README.md index 3452cc0..3f05aef 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,19 @@ # QuorielEdge -An extended set of functions for ForgeScript, designed to optimize workflows, simplify the execution of various tasks, and support script integration and processing. +A super-set of ForgeScript, designed to optimize workflows, apply community patches, simplify the execution of various tasks, and support script integration and processing. + + ## Installation -``` -npm i github:quoriel/edge +```bash +# Install using your package managers + +# Using NPM +npm install github:quoriel/edge + +# or using 3rd package managers +yarn install github:quoriel/edge +pnpm install github:quoriel/edge +bun install github:quoriel/edge ``` ## Connection @@ -18,4 +28,21 @@ const client = new ForgeClient({ }); client.login("..."); +``` + +## Building from source +You can clone this repository to your project workspace +> The code were build with bun's transpiler (ESM) and TypeScript (CJS) + +```bash +# Run the following commands +# Using Bun +bun run build + +# or using NodeJS v23 +# Make sure to install bun if you're using nodejs +# npm install --save-dev bun + +node --experimental-strip-types scripts/build.ts + ``` \ No newline at end of file diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..a07ccab --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,7 @@ +# Roadmap +This is an extension focused as a super-set for forgescript and potentially optimizing the internal interpeter's workflow for better time execution + +## Core Features +- [] Better approach for function implementation +- [] Bytecode +- [] Serverless execution \ No newline at end of file diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..5656d10 --- /dev/null +++ b/bun.lock @@ -0,0 +1,106 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "@quoriel/edge", + "dependencies": { + "@tryforge/forgescript": "^2.4.1", + "zod": "^4.1.12", + }, + "devDependencies": { + "@types/bun": "^1.3.1", + "toml": "^3.0.0", + "typescript": "^5.9.3", + }, + }, + }, + "packages": { + "@discordjs/builders": ["@discordjs/builders@1.13.0", "", { "dependencies": { "@discordjs/formatters": "^0.6.1", "@discordjs/util": "^1.1.1", "@sapphire/shapeshift": "^4.0.0", "discord-api-types": "^0.38.31", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", "tslib": "^2.6.3" } }, "sha512-COK0uU6ZaJI+LA67H/rp8IbEkYwlZf3mAoBI5wtPh5G5cbEQGNhVpzINg2f/6+q/YipnNIKy6fJDg6kMUKUw4Q=="], + + "@discordjs/collection": ["@discordjs/collection@1.5.3", "", {}, "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="], + + "@discordjs/formatters": ["@discordjs/formatters@0.6.1", "", { "dependencies": { "discord-api-types": "^0.38.1" } }, "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg=="], + + "@discordjs/rest": ["@discordjs/rest@2.6.0", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.16", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w=="], + + "@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="], + + "@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="], + + "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], + + "@sapphire/async-queue": ["@sapphire/async-queue@1.5.5", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="], + + "@sapphire/shapeshift": ["@sapphire/shapeshift@4.0.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" } }, "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg=="], + + "@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="], + + "@tryforge/forgescript": ["@tryforge/forgescript@2.5.0", "", { "dependencies": { "chalk": "^4.1.2", "discord.js": "^14.22.1", "ms-utility": "^3.3.0", "tiny-typed-emitter": "^2.1.0", "undici": "^5.23.0" } }, "sha512-DhD7FfgzbR81Oc6l6PCgKQTP5O67CovZxL6IEIPCHZOg72ptsg9OQzJQbLOFCJspRiGZIZcZYhnOvErLXm/04g=="], + + "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], + + "@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + + "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], + + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + + "@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.7", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="], + + "discord.js": ["discord.js@14.24.0", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.31", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-KNq/ekT8bsmT3ZAfVre8cPbl+DfVYSdlLnDmGZPoz7Cw21LYeWHllRA9MivqNq5b1GPGAxGvyUN1vxbTb/PQWw=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.snakecase": ["lodash.snakecase@4.1.1", "", {}, "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="], + + "magic-bytes.js": ["magic-bytes.js@1.12.1", "", {}, "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA=="], + + "ms-utility": ["ms-utility@3.3.0", "", {}, "sha512-PM1brH9z25ICcU17rK8yLwjH5sekCHs1xiftUTKmkoSlBY+F/l7G+zyUMmZ148SkVwOPb0qDGZgawKvELJ+klQ=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "tiny-typed-emitter": ["tiny-typed-emitter@2.1.0", "", {}, "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA=="], + + "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], + + "ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="], + + "@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], + + "@discordjs/rest/undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], + + "@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], + + "discord.js/undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], + } +} diff --git a/metadata/functions.json b/metadata/functions.json index 6721f2c..c49bff5 100644 --- a/metadata/functions.json +++ b/metadata/functions.json @@ -1 +1 @@ -[{"name":"$execution","description":"Executes code exported as a string from the specified file","version":"1.0.0","output":["Unknown"],"brackets":true,"unwrap":true,"args":[{"name":"path","description":"File path","type":"String","required":true,"rest":false}]},{"name":"$jsonAssign","description":"Combines multiple JSON objects into a single JSON object","version":"1.0.0","output":["Json"],"brackets":true,"unwrap":true,"args":[{"name":"json","description":"JSON objects for merging","type":"Json","required":true,"rest":true}]},{"name":"$require","description":"Dynamically loads the module","version":"1.0.0","output":["Unknown"],"brackets":true,"unwrap":true,"args":[{"name":"path","description":"Module path","type":"String","required":true,"rest":false}]},{"name":"$requireCache","description":"Deletes a module from the cache or reloads it (including dependencies)","version":"1.0.0","output":["Boolean"],"brackets":true,"unwrap":true,"args":[{"name":"path","description":"Module path","type":"String","required":true,"rest":false},{"name":"type","description":"Type of action","type":"Enum","enum":["delete","update"],"required":true,"rest":false,"enumName":"ActionType"},{"name":"recursive","description":"Whether to clear cache recursively","type":"Boolean","rest":false}]}] \ No newline at end of file +[{"name":"$execution","description":"Executes code exported as a string from the specified file","version":"1.0.0","output":["Unknown"],"brackets":true,"unwrap":true,"args":[{"name":"path","description":"File path","type":"String","required":true,"rest":false}]},{"name":"$jsonAssign","description":"Combines multiple JSON objects into a single JSON object","version":"1.0.0","output":["Json"],"brackets":true,"unwrap":true,"args":[{"name":"json","description":"JSON objects for merging","type":"Json","required":true,"rest":true}]},{"name":"$append","description":"Appends items into the last of list","unwrap":true,"brackets":true,"args":[{"name":"sourcesToAppend","description":"The source or target of items to be inserted","type":"Unknown","rest":false,"required":true},{"name":"items","description":"The items to append","type":"Unknown","rest":true,"required":true}],"category":"object","version":"1.0.0"},{"name":"$delete","description":"Deletes a key from an object or context","unwrap":true,"brackets":true,"args":[{"name":"propertyKey","description":"The name of the property or key of object","type":"String","rest":false,"required":true},{"name":"object","description":"The source of object","type":"Unknown","rest":false,"required":false}],"category":"object","version":"1.0.0"},{"name":"$deserialize","description":"Deserializes a serialized input into an object","unwrap":true,"brackets":true,"args":[{"name":"input","description":"The input that is to be deserialized","type":"String","rest":false,"required":false},{"name":"deserializer","description":"A deserializer instance or name of a registered service","type":"Unknown","rest":false,"required":false}],"category":"object","version":"1.0.0"},{"name":"$require","description":"Loads the module to $require","output":["Unknown"],"brackets":true,"unwrap":true,"args":[{"name":"path","description":"Module path","type":"String","required":true,"rest":false}],"category":"object","version":"1.0.0"},{"name":"$serialize","description":"Serializes an object into a serializer","unwrap":true,"brackets":true,"args":[{"name":"object","description":"The object that is to be serialized","type":"Unknown","rest":false,"required":false},{"name":"serializer","description":"A serializer instance or name of a registered service","type":"Unknown","rest":false,"required":false}],"category":"object","version":"1.0.0"},{"name":"$requireCache","description":"Deletes a module from the cache or reloads it (including dependencies)","version":"1.0.0","output":["Boolean"],"brackets":true,"unwrap":true,"args":[{"name":"path","description":"Module path","type":"String","required":true,"rest":false},{"name":"type","description":"Type of action","type":"Enum","enum":["delete","update"],"required":true,"rest":false,"enumName":"ActionType"},{"name":"recursive","description":"Whether to clear cache recursively","type":"Boolean","rest":false}]}] \ No newline at end of file diff --git a/metadata/paths.json b/metadata/paths.json index 9567c77..36cbd83 100644 --- a/metadata/paths.json +++ b/metadata/paths.json @@ -1 +1 @@ -{"functions":"src/functions"} +{"functions":"src/functions"} \ No newline at end of file diff --git a/package.json b/package.json index 13f3908..e2e5212 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,27 @@ "name": "@quoriel/edge", "description": "A set of additional functions for ForgeScript to simplify work with various scripts and tasks", "version": "1.0.0", - "main": "src/main.js", "author": "Mlad (https://github.com/eolthar)", "license": "GPL-3.0", "scripts": { - "docgen": "node src/docgen.js" + "docgen": "bun run scripts/docgen", + "build": "bun run build:src && bun run build:types", + "build:src": "bun run scripts/build", + "build:types": "bunx tsc -p tsconfig.json", + "watch:types": "bunx tsc --watch -p tsconfig.json" }, "dependencies": { - "@tryforge/forgescript": "^2.4.1" - } + "@tryforge/forgescript": "^2.4.1", + "zod": "^4.1.12" + }, + "devDependencies": { + "@types/bun": "^1.3.1", + "toml": "^3.0.0", + "typescript": "^5.9.3" + }, + "exports": [ + "./dist/index.js", + "./dist/index.mjs" + ], + "types": "./dist/types" } diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100644 index 0000000..79e1473 --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,28 @@ +import { Glob } from "bun"; +import { rmSync } from "fs"; + +const sourceDirectory = 'src/**/*.ts' +const glob = new Glob(sourceDirectory) +var entrypoints = [...glob.scanSync()] +entrypoints = entrypoints.map((x) => `${x}`) + +const BuildOptions: Bun.BuildConfig = { + entrypoints: entrypoints, + splitting: true, + target: 'node', + tsconfig: 'tsconfig.json', + external: ['@tryforge/forgescript'], + naming: '[dir]/[name].mjs', + root: 'src' +} + +// Clear existing dist +rmSync('dist', { recursive: true, force: true }) + +// Transpiling for ES Modules +Bun.build({...BuildOptions, format: 'esm', outdir: 'dist'}) + +// Transpiling for CommonJS +// # Use tsc since its cleaner +// BuildOptions.splitting = false +// Bun.build({...BuildOptions, format: 'cjs', outdir: 'dist/cjs'}) \ No newline at end of file diff --git a/scripts/docgen.ts b/scripts/docgen.ts new file mode 100644 index 0000000..0a643bf --- /dev/null +++ b/scripts/docgen.ts @@ -0,0 +1,4 @@ +import { generateMetadata } from "@tryforge/forgescript"; +import { resolve } from "path"; + +generateMetadata(resolve(process.cwd(), 'dist/functions'), "functions") \ No newline at end of file diff --git a/src/docgen.js b/src/docgen.js deleted file mode 100644 index 8a7cc97..0000000 --- a/src/docgen.js +++ /dev/null @@ -1,2 +0,0 @@ -const { generateMetadata } = require("@tryforge/forgescript"); -generateMetadata(`${__dirname}/functions`, "functions"); diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..a3a13fd --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,35 @@ +import { ForgeExtension, FunctionManager, type ForgeClient } from "@tryforge/forgescript"; +import { name, description, version } from '../package.json' +import { resolve, extname } from "path"; +import { globSync } from "fs"; + +export class QuorielExtension extends ForgeExtension { + public readonly name = name + public readonly description = description + public readonly version = version + + public init(client: ForgeClient) { + // The internal handler doesn't support + // Dynamic script extension + // eg. .mjs, .ts, .cjs + const fnEntrypoints = globSync(`${__dirname}/functions/**/*${extname(__filename)}`) + let modules = [] + + for (const fname of fnEntrypoints) { + const module = require(fname) + if ('default' in module) { + modules.push(module.default) + continue; + } + + if (Array.isArray(module)) { + modules.push(...module) + continue; + } + + modules.push(module); + } + + FunctionManager.addMany(modules) + } +} \ No newline at end of file diff --git a/src/functions/execution.js b/src/functions/execution.ts similarity index 76% rename from src/functions/execution.js rename to src/functions/execution.ts index 65cd736..52ed860 100644 --- a/src/functions/execution.js +++ b/src/functions/execution.ts @@ -1,8 +1,14 @@ -const { Logger, NativeFunction, ArgType, Compiler, Interpreter } = require("@tryforge/forgescript"); -const { resolve } = require("path"); -const { existsSync } = require("fs"); +import { + Compiler, + Interpreter, + ArgType, + NativeFunction, + Logger +} from '@tryforge/forgescript' +import { resolve } from 'path' +import { existsSync } from 'fs'; -exports.default = new NativeFunction({ +export default new NativeFunction({ name: "$execution", description: "Executes code exported as a string from the specified file", version: "1.0.0", @@ -31,9 +37,9 @@ exports.default = new NativeFunction({ data: Compiler.compile(code) }); return result === null ? this.stop() : this.success(result); - } catch (error) { + } catch (error:unknown) { Logger.error(error); - return this.error(error); + return this.error(error as Error); } } }); \ No newline at end of file diff --git a/src/functions/jsonAssign.js b/src/functions/jsonAssign.ts similarity index 69% rename from src/functions/jsonAssign.js rename to src/functions/jsonAssign.ts index 50a3636..becee67 100644 --- a/src/functions/jsonAssign.js +++ b/src/functions/jsonAssign.ts @@ -1,22 +1,22 @@ -const { NativeFunction, ArgType } = require("@tryforge/forgescript"); - -exports.default = new NativeFunction({ - name: "$jsonAssign", - description: "Combines multiple JSON objects into a single JSON object", - version: "1.0.0", - output: ArgType.Json, - brackets: true, - unwrap: true, - args: [ - { - name: "json", - description: "JSON objects for merging", - type: ArgType.Json, - required: true, - rest: true - } - ], - async execute(ctx, [json]) { - return this.successJSON(Object.assign(...json)); - } +import { NativeFunction, ArgType } from "@tryforge/forgescript"; + +export default new NativeFunction({ + name: "$jsonAssign", + description: "Combines multiple JSON objects into a single JSON object", + version: "1.0.0", + output: ArgType.Json, + brackets: true, + unwrap: true, + args: [ + { + name: "json", + description: "JSON objects for merging", + type: ArgType.Json, + required: true, + rest: true + } + ], + async execute(ctx, [json]) { + return this.successJSON(json); + } }); \ No newline at end of file diff --git a/src/functions/object/append.ts b/src/functions/object/append.ts new file mode 100644 index 0000000..826d500 --- /dev/null +++ b/src/functions/object/append.ts @@ -0,0 +1,38 @@ +import { + NativeFunction, + ArgType +} from '@tryforge/forgescript' + + +export default new NativeFunction( + { + name: '$append', + description: 'Appends items into the last of list', + unwrap: true, + brackets: true, + args: [ + { + name: 'sourcesToAppend', + description: 'The source or target of items to be inserted', + type: ArgType.Unknown, + rest: false, + required: true + }, + { + name: 'items', + description: 'The items to append', + type: ArgType.Unknown, + rest: true, + required: true + } + ], + execute(ctx, [source, itemsOrArray]) { + if (! Array.isArray(source)) { + return this.customError("Expected #1 arg 'source' as an array-like object") + } + + source.push(...itemsOrArray) + return this.success() + } + } +) \ No newline at end of file diff --git a/src/functions/object/delete.ts b/src/functions/object/delete.ts new file mode 100644 index 0000000..5041e4d --- /dev/null +++ b/src/functions/object/delete.ts @@ -0,0 +1,51 @@ +import { + NativeFunction, + ArgType +} from '@tryforge/forgescript' + +const containerSymbol = Symbol() + +export default new NativeFunction( + { + name: '$delete', + description: 'Deletes a key from an object or context', + unwrap: true, + brackets: true, + args: [ + { + name: 'propertyKey', + description: 'The name of the property or key of object', + type: ArgType.String, + rest: false, + required: true + }, + { + name: 'object', + description: 'The source of object', + type: ArgType.Unknown, + rest: false, + required: false, + } + ], + execute(ctx, [propertyKey, obj = containerSymbol]) { + if (obj === containerSymbol) { + return this.success(ctx.deleteKeyword(propertyKey)) + } + + if ( + obj === null || + typeof(obj) === 'string' || + typeof(obj) === 'number' || + typeof(obj) === 'boolean' + ) { + const _t = obj === null ? 'null' : typeof(obj) + return this.customError('Expected #2 an object with value, got ' + _t) + } + + let exists = propertyKey in (obj as object) + if (exists) delete obj[propertyKey] + + return this.success(exists) + } + } +) \ No newline at end of file diff --git a/src/functions/object/deserialize.ts b/src/functions/object/deserialize.ts new file mode 100644 index 0000000..9cb76a6 --- /dev/null +++ b/src/functions/object/deserialize.ts @@ -0,0 +1,45 @@ +import { + NativeFunction, + ArgType +} from '@tryforge/forgescript' + +import { IDeserializer } from '../../utils/sandbox' +import { Resolver } from '../../utils/resolver' + +const noop = () => void 0 + +export default new NativeFunction( + { + name: '$deserialize', + description: 'Deserializes a serialized input into an object', + unwrap: true, + brackets: true, + args: [ + { + name: 'input', + description: 'The input that is to be deserialized', + type: ArgType.String, + rest: false, + required: false, + }, + { + name: 'deserializer', + description: 'A deserializer instance or name of a registered service', + type: ArgType.Unknown, + rest: false, + required: false, + } + ], + execute(ctx, [object, deserializer]) { + const { deserialize } = + Resolver.GetInstance(deserializer as IDeserializer) || + { deserialize: noop } + + if (deserialize === noop) { + return this.customError('Expected a deserializer with \'deserialize\' function, got none') + } + + return this.success(deserialize(object)) + } + } +) \ No newline at end of file diff --git a/src/functions/require.js b/src/functions/object/require.ts similarity index 69% rename from src/functions/require.js rename to src/functions/object/require.ts index e7c262e..f1e94fb 100644 --- a/src/functions/require.js +++ b/src/functions/object/require.ts @@ -1,31 +1,30 @@ -const { NativeFunction, ArgType } = require("@tryforge/forgescript"); -const { resolve } = require("path"); -const { existsSync } = require("fs"); - -exports.default = new NativeFunction({ - name: "$require", - description: "Dynamically loads the module", - version: "1.0.0", - output: ArgType.Unknown, - brackets: true, - unwrap: true, - args: [ - { - name: "path", - description: "Module path", - type: ArgType.String, - required: true, - rest: false - } - ], - async execute(ctx, [path]) { - const full = resolve(process.cwd(), path); - if (!existsSync(full)) return this.stop(); - try { - const result = require(full); - return this.success(typeof result === "object" ? JSON.stringify(result) : result); - } catch { - return this.success(); - } - } +import { NativeFunction, ArgType } from "@tryforge/forgescript"; +import { resolve } from 'path' +import { existsSync } from "fs"; + +export default new NativeFunction({ + name: "$require", + description: "Loads the module to $require", + output: ArgType.Unknown, + brackets: true, + unwrap: true, + args: [ + { + name: "path", + description: "Module path", + type: ArgType.String, + required: true, + rest: false + } + ], + async execute(ctx, [path]) { + const full = resolve(process.cwd(), path); + if (!existsSync(full)) return this.stop(); + try { + const result = require(full); + return this.success(typeof result === "object" ? JSON.stringify(result) : result); + } catch { + return this.success(); + } + } }); \ No newline at end of file diff --git a/src/functions/object/serialize.ts b/src/functions/object/serialize.ts new file mode 100644 index 0000000..09d7a31 --- /dev/null +++ b/src/functions/object/serialize.ts @@ -0,0 +1,46 @@ +import { + NativeFunction, + ArgType +} from '@tryforge/forgescript' + +import { Json } from '../../serializer'; +import { ISerializer } from '../../utils/sandbox' +import { Resolver } from '../../utils/resolver' + +const noop = () => void 0 + +export default new NativeFunction( + { + name: '$serialize', + description: 'Serializes an object into a serializer', + unwrap: true, + brackets: true, + args: [ + { + name: 'object', + description: 'The object that is to be serialized', + type: ArgType.Unknown, + rest: false, + required: false, + }, + { + name: 'serializer', + description: 'A serializer instance or name of a registered service', + type: ArgType.Unknown, + rest: false, + required: false, + } + ], + execute(ctx, [object, serializer = Json]) { + const { serialize } = + Resolver.GetInstance(serializer as ISerializer) || + { serialize: noop } + + if (serialize === noop) { + return this.customError('Expected a serializer with \'serialize\' function, got none') + } + + return this.success(serialize(object)) + } + } +) \ No newline at end of file diff --git a/src/functions/requireCache.js b/src/functions/requireCache.ts similarity index 84% rename from src/functions/requireCache.js rename to src/functions/requireCache.ts index 92342db..6e32379 100644 --- a/src/functions/requireCache.js +++ b/src/functions/requireCache.ts @@ -1,68 +1,68 @@ -const { NativeFunction, ArgType } = require("@tryforge/forgescript"); -const { resolve } = require("path"); -const { existsSync } = require("fs"); - -const ActionType = { - delete: "delete", - update: "update" -} - -exports.default = new NativeFunction({ - name: "$requireCache", - description: "Deletes a module from the cache or reloads it (including dependencies)", - version: "1.0.0", - output: ArgType.Boolean, - brackets: true, - unwrap: true, - args: [ - { - name: "path", - description: "Module path", - type: ArgType.String, - required: true, - rest: false - }, - { - name: "type", - description: "Type of action", - type: ArgType.Enum, - enum: ActionType, - required: true, - rest: false - }, - { - name: "recursive", - description: "Whether to clear cache recursively", - type: ArgType.Boolean, - rest: false - } - ], - async execute(ctx, [path, type, recursive]) { - const full = resolve(process.cwd(), path); - if (!existsSync(full)) { - return this.success(false); - } - if (recursive) { - clearModuleCacheRecursively(full); - } else { - delete require.cache[full]; - } - if (type === "update") { - try { - require(full); - } catch (e) { - return this.success(false); - } - } - return this.success(true); - } -}); - -function clearModuleCacheRecursively(path) { - const mod = require.cache[path]; - if (!mod) return; - mod.children.forEach(child => { - clearModuleCacheRecursively(child.id); - }); - delete require.cache[path]; +import { NativeFunction, ArgType } from "@tryforge/forgescript"; +import { resolve } from 'path' +import { existsSync } from "fs"; + +const ActionType = { + delete: "delete", + update: "update" +} + +export default new NativeFunction({ + name: "$requireCache", + description: "Deletes a module from the cache or reloads it (including dependencies)", + version: "1.0.0", + output: ArgType.Boolean, + brackets: true, + unwrap: true, + args: [ + { + name: "path", + description: "Module path", + type: ArgType.String, + required: true, + rest: false + }, + { + name: "type", + description: "Type of action", + type: ArgType.Enum, + enum: ActionType, + required: true, + rest: false + }, + { + name: "recursive", + description: "Whether to clear cache recursively", + type: ArgType.Boolean, + rest: false + } + ], + async execute(ctx, [path, type, recursive]) { + const full = resolve(process.cwd(), path); + if (!existsSync(full)) { + return this.success(false); + } + if (recursive) { + clearModuleCacheRecursively(full); + } else { + delete require.cache[full]; + } + if (type === "update") { + try { + require(full); + } catch (e) { + return this.success(false); + } + } + return this.success(true); + } +}); + +function clearModuleCacheRecursively(path: string) { + const mod = require.cache[path]; + if (!mod) return; + mod.children.forEach(child => { + clearModuleCacheRecursively(child.id); + }); + delete require.cache[path]; } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3f37380 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,6 @@ +import * as Serializers from './serializer' +import { Resolver } from "./utils/resolver"; + +Resolver.RegisterType(Serializers.Json) + +export { QuorielExtension as QuorielEdge } from './extension' \ No newline at end of file diff --git a/src/main.js b/src/main.js deleted file mode 100644 index 029c0b3..0000000 --- a/src/main.js +++ /dev/null @@ -1,14 +0,0 @@ -const { ForgeExtension } = require("@tryforge/forgescript"); -const { description, version } = require("../package.json"); - -class QuorielEdge extends ForgeExtension { - name = "QuorielEdge"; - description = description; - version = version; - - init() { - this.load(__dirname + "/functions"); - } -} - -module.exports = { QuorielEdge }; \ No newline at end of file diff --git a/src/serializer/Json.ts b/src/serializer/Json.ts new file mode 100644 index 0000000..c074d85 --- /dev/null +++ b/src/serializer/Json.ts @@ -0,0 +1,4 @@ +import { ISerializer, IDeserializer } from "../utils/sandbox"; + +export const serialize: ISerializer['serialize'] = (obj) => JSON.stringify(obj) +export const deserialize: IDeserializer['deserialize'] = (input: string) => JSON.parse(input) \ No newline at end of file diff --git a/src/serializer/index.ts b/src/serializer/index.ts new file mode 100644 index 0000000..1c6b673 --- /dev/null +++ b/src/serializer/index.ts @@ -0,0 +1 @@ +export * as Json from './JSON'; \ No newline at end of file diff --git a/src/utils/resolver.ts b/src/utils/resolver.ts new file mode 100644 index 0000000..9dad63b --- /dev/null +++ b/src/utils/resolver.ts @@ -0,0 +1,28 @@ +export namespace Resolver { + const Registered = new Map() + const Relation = new Map() + + export const RegisterType = (type: T) => { + if (Registered.has(type.constructor)) { + return false; + } + + Registered.set(type.constructor, type) + return true; + } + + export const RegisterWithId = (type: T, id: string) => { + + } + + export const UnregisterType = (type: T) => { + if (type.constructor === Object) { + + } + return Registered.delete(type.constructor) + } + + export const GetInstance = (type: T) => { + return Registered.get(type.constructor) as T + } +} \ No newline at end of file diff --git a/src/utils/sandbox.ts b/src/utils/sandbox.ts new file mode 100644 index 0000000..a743d9f --- /dev/null +++ b/src/utils/sandbox.ts @@ -0,0 +1,13 @@ +import z from "zod"; + +export const ISerializer = z.object({ + serialize: z.function({ input: [z.any()] }) +}) + +export type ISerializer = z.infer + +export const IDeserializer = z.object({ + deserialize: z.function({ input: [z.union([z.string(), z.unknown()]) ]}) +}) + +export type IDeserializer = z.infer \ No newline at end of file diff --git a/tests/client.test.js b/tests/client.test.js new file mode 100644 index 0000000..4cb68b5 --- /dev/null +++ b/tests/client.test.js @@ -0,0 +1,20 @@ +// This file serves as a test for +// CommonJS modules + +const { ForgeClient } = require("@tryforge/forgescript") +const { QuorielEdge } = require("@quoriel/edge") + +const path = require("node:path") + +const client = new ForgeClient({ + intents: ["Guilds"], + events: [ + "clientReady" + ], + extensions: [ + new QuorielEdge() + ] +}) + +client.commands.load(path.resolve(__dirname, 'commands')) +client.login(process.env.TOKEN) \ No newline at end of file diff --git a/tests/client.test.ts b/tests/client.test.ts new file mode 100644 index 0000000..4fed988 --- /dev/null +++ b/tests/client.test.ts @@ -0,0 +1,10 @@ +import { ForgeClient } from "@tryforge/forgescript"; +import { QuorielEdge } from '@quoriel/edge' + +import { resolve } from "path"; + +const client = new ForgeClient({ intents: ['Guilds'], extensions: [new QuorielEdge()], events: ['clientReady'] }) + +client.commands.load(resolve(__dirname, 'commands')) + +client.login(process.env.TOKEN) \ No newline at end of file diff --git a/tests/docgen.ts b/tests/docgen.ts new file mode 100644 index 0000000..dc78061 --- /dev/null +++ b/tests/docgen.ts @@ -0,0 +1,83 @@ +/** + * Trying to make a more dynamic approach + * for generating metadata + */ + +import { ArgType, NativeFunction } from "@tryforge/forgescript"; +import { Glob } from "bun"; +import z from "zod"; +import fs from 'fs' +import { resolve } from 'path' + +const sourceDirectory = 'src/functions' +const fileExtension = '.ts' +const glob = new Glob(sourceDirectory + '/**/*' + fileExtension) +var entrypoints = [...glob.scanSync()] +entrypoints = entrypoints.map(x => resolve(process.cwd(), x)) + +const functions = [] + +for (const path of entrypoints) { + const module = require(path) + if ('default' in module) { + module.default['path'] = path + functions.push(module.default) + continue; + } + + if (Array.isArray(module)) { + functions.push(...module.map(x => { + x['path'] = path; + return x + })) + continue; + } + + module['path'] = path + functions.push(module); +} + +/** + * Serves as a template + */ +const template = z.object({ + name: z.string(), + description: z.string(), + unwrap: z.boolean(), + + // Optional fields + brackets: z.optional(z.boolean()), + version: z.optional(z.string()), + experimental: z.optional(z.boolean()), + + // why does this even exists + aliases: z.optional(z.array(z.string())), + + // Args + args: z.optional(z.array(z.object({ + name: z.string(), + description: z.string(), + rest: z.boolean(), + required: z.optional(z.boolean()), + type: z.number().transform((n) => ArgType[n]), + enum: z.optional(z.any().transform((x) => Object.values(x).flat())), + enumName: z.optional(z.string()) + }))) +}) + +const metadata = functions.map((x: NativeFunction) => template.parse(x.data)) +const fn = metadata.filter(x => x.args?.filter(x => !!x.enum)) + .map(x => x.args) + // .reduce((list, x) => [...list, ...x], []) + +/** + * Writing metadata + */ +fs.mkdirSync('./metadata', { recursive: true }) + +// fs.writeFileSync('./metadata/functions.json', JSON.stringify(metadata)) +Bun.file('./metadata/functions.json').write(JSON.stringify(metadata)) + + +// Working with enums +// console.log(fn) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..99a73bd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationDir": "./dist/types", + "emitDeclarationOnly": false, + "rootDir": "./src", + "outDir": "./dist", + "lib": ["ES2024"], + "target": "ES2024", + "module": "commonjs", + "resolveJsonModule": true, + "skipLibCheck": true, + "skipDefaultLibCheck": true + }, + "include": ["src"], + "exclude": ["dist", "node_modules", "scripts"] +} \ No newline at end of file