@igosprojects/rivet-ws
Advanced tools
| import type { RivetPlugin } from '@igosprojects/rivet'; | ||
| export declare const WSPlugin: RivetPlugin; | ||
| //# sourceMappingURL=plugin.d.ts.map |
| {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIvD,eAAO,MAAM,QAAQ,EAAE,WAiCtB,CAAC"} |
| "use strict"; | ||
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.WSPlugin = void 0; | ||
| const ws_1 = require("ws"); | ||
| exports.WSPlugin = { | ||
| name: 'logging', | ||
| version: '1.0.0', | ||
| install: function (app) { | ||
| console.log('Started WS plugin!'); | ||
| const WSRoutes = new Map(); | ||
| // Add WS function to app | ||
| app.ws = (path, handler) => { | ||
| WSRoutes.set(path, handler); | ||
| return app; | ||
| }; | ||
| // Start WS server(no port, handles HTTP upgrades) | ||
| const wss = new ws_1.WebSocketServer({ noServer: true }); | ||
| // Hook into server creation | ||
| app.OnServerCreate((server) => { | ||
| server.on('upgrade', (req, socket, head) => { | ||
| const url = req.url?.split('?')[0] || '/'; | ||
| const handler = WSRoutes.get(url); | ||
| if (handler) { | ||
| wss.handleUpgrade(req, socket, head, (ws) => { | ||
| handler(ws, req); | ||
| }); | ||
| } | ||
| else { | ||
| socket.destroy(); | ||
| } | ||
| }); | ||
| }); | ||
| }, | ||
| }; | ||
| //# sourceMappingURL=plugin.js.map |
| {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";AAAA;;;EAGE;;;AAEF,2BAAqC;AAKxB,QAAA,QAAQ,GAAgB;IACjC,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,UAAU,GAAQ;QACvB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAElC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmD,CAAC;QAE5E,yBAAyB;QACzB,GAAG,CAAC,EAAE,GAAG,CAAC,IAAY,EAAE,OAAgD,EAAE,EAAE;YACxE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5B,OAAO,GAAG,CAAC;QACf,CAAC,CAAC;QAEF,kDAAkD;QAClD,MAAM,GAAG,GAAG,IAAI,oBAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAEpD,4BAA4B;QAC5B,GAAG,CAAC,cAAc,CAAC,CAAC,MAAW,EAAE,EAAE;YAC/B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAoB,EAAE,MAAc,EAAE,IAAY,EAAE,EAAE;gBACxE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;gBAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAElC,IAAI,OAAO,EAAE,CAAC;oBACV,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE;wBACxC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;oBACrB,CAAC,CAAC,CAAC;gBACP,CAAC;qBAAM,CAAC;oBACJ,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrB,CAAC;YACL,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAC"} |
| { | ||
| "hoistPattern": [ | ||
| "*" | ||
| ], | ||
| "included": { | ||
| "dependencies": true, | ||
| "devDependencies": true, | ||
| "optionalDependencies": true | ||
| }, | ||
| "injectedDeps": {}, | ||
| "layoutVersion": 5, | ||
| "hoistedLocations": { | ||
| "@igosprojects/rivet@1.0.6": [ | ||
| "node_modules\\@igosprojects\\rivet" | ||
| ] | ||
| }, | ||
| "nodeLinker": "hoisted", | ||
| "packageManager": "pnpm@10.33.0", | ||
| "pendingBuilds": [], | ||
| "publicHoistPattern": [], | ||
| "prunedAt": "Sat, 30 May 2026 12:39:12 GMT", | ||
| "registries": { | ||
| "default": "https://registry.npmjs.org/", | ||
| "@jsr": "https://npm.jsr.io/" | ||
| }, | ||
| "skipped": [], | ||
| "storeDir": "C:\\Users\\Igor\\AppData\\Local\\pnpm\\store\\v10", | ||
| "virtualStoreDir": "E:\\Igor\\rivet-ws\\test\\node_modules\\.pnpm", | ||
| "virtualStoreDirMaxLength": 60 | ||
| } |
| { | ||
| "lastValidatedTimestamp": 1780145917139, | ||
| "projects": {}, | ||
| "pnpmfiles": [], | ||
| "settings": { | ||
| "autoInstallPeers": true, | ||
| "dedupeDirectDeps": false, | ||
| "dedupeInjectedDeps": true, | ||
| "dedupePeerDependents": true, | ||
| "dedupePeers": false, | ||
| "dev": true, | ||
| "excludeLinksFromLockfile": false, | ||
| "hoistPattern": [ | ||
| "*" | ||
| ], | ||
| "hoistWorkspacePackages": true, | ||
| "injectWorkspacePackages": false, | ||
| "linkWorkspacePackages": false, | ||
| "nodeLinker": "hoisted", | ||
| "optional": true, | ||
| "peersSuffixMaxLength": 1000, | ||
| "preferWorkspacePackages": false, | ||
| "production": true, | ||
| "publicHoistPattern": [] | ||
| }, | ||
| "filteredInstall": false | ||
| } |
| lockfileVersion: '9.0' | ||
| settings: | ||
| autoInstallPeers: true | ||
| excludeLinksFromLockfile: false | ||
| importers: | ||
| .: | ||
| dependencies: | ||
| '@igosprojects/rivet': | ||
| specifier: ^1.0.6 | ||
| version: 1.0.6 | ||
| packages: | ||
| '@igosprojects/rivet@1.0.6': | ||
| resolution: {integrity: sha512-LhkQRwKrSEOGvC5cd/LFmlKH1Gbz4J7nMD7qug+xzOavdiSfqxseRDRt1u4YnHApsB4XJDEQ/SRF/HSjSgivig==} | ||
| snapshots: | ||
| '@igosprojects/rivet@1.0.6': {} |
| import type { RivetResponse, CorsOptions } from './types'; | ||
| export declare function AddCORSHeader(res: RivetResponse, options?: CorsOptions): void; | ||
| export declare function HandlePreflight(req: any, res: RivetResponse): boolean; | ||
| //# sourceMappingURL=cors.d.ts.map |
| {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../src/cors.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAU1D,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,GAAE,WAAgC,QAgB1F;AAGD,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAOrE"} |
| "use strict"; | ||
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.AddCORSHeader = AddCORSHeader; | ||
| exports.HandlePreflight = HandlePreflight; | ||
| const DefaultCorsOptions = { | ||
| origin: '*', | ||
| methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], | ||
| headers: ['Content-Type', 'Authorization'], | ||
| credentials: false, | ||
| }; | ||
| // Adds the needed CORS tags to the response header | ||
| function AddCORSHeader(res, options = DefaultCorsOptions) { | ||
| const opts = { ...DefaultCorsOptions, ...options }; | ||
| // Origin | ||
| const origin = Array.isArray(opts.origin) ? opts.origin.join(', ') : opts.origin; | ||
| res.setHeader('Access-Control-Allow-Origin', origin || '*'); | ||
| // Headers | ||
| if (opts.headers) { | ||
| res.setHeader('Access-Control-Allow-Headers', opts.headers.join(', ')); | ||
| } | ||
| // Credentials | ||
| if (opts.credentials) { | ||
| res.setHeader('Access-Control-Allow-Credentials', 'true'); | ||
| } | ||
| } | ||
| // Handles preflight request sent by browsers | ||
| function HandlePreflight(req, res) { | ||
| if (req.method === 'OPTIONS') { | ||
| res.writeHead(204); // 204: No content | ||
| res.end(); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| //# sourceMappingURL=cors.js.map |
| {"version":3,"file":"cors.js","sourceRoot":"","sources":["../src/cors.ts"],"names":[],"mappings":";AAAA;;;EAGE;;AAcF,sCAgBC;AAGD,0CAOC;AAlCD,MAAM,kBAAkB,GAAgB;IACpC,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC;IAC7D,OAAO,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;IAC1C,WAAW,EAAE,KAAK;CACrB,CAAC;AAEF,mDAAmD;AACnD,SAAgB,aAAa,CAAC,GAAkB,EAAE,UAAuB,kBAAkB;IACvF,MAAM,IAAI,GAAG,EAAE,GAAG,kBAAkB,EAAE,GAAG,OAAO,EAAE,CAAC;IAEnD,SAAS;IACT,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IACjF,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC;IAE5D,UAAU;IACV,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,cAAc;IACd,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,GAAG,CAAC,SAAS,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;IAC9D,CAAC;AACL,CAAC;AAED,6CAA6C;AAC7C,SAAgB,eAAe,CAAC,GAAQ,EAAE,GAAkB;IACxD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC3B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,kBAAkB;QACtC,GAAG,CAAC,GAAG,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC"} |
| export { Rivet } from './rivet'; | ||
| export type { RivetResponse, RivetPlugin } from './types'; | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"} |
| "use strict"; | ||
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Rivet = void 0; | ||
| var rivet_1 = require("./rivet"); | ||
| Object.defineProperty(exports, "Rivet", { enumerable: true, get: function () { return rivet_1.Rivet; } }); | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;EAGE;;;AAEF,iCAAgC;AAAvB,8FAAA,KAAK,OAAA"} |
| export declare function GetMimeType(filePath: string): string; | ||
| //# sourceMappingURL=mime.d.ts.map |
| {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBpD"} |
| "use strict"; | ||
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.GetMimeType = GetMimeType; | ||
| // Returns the MIME type of any file | ||
| function GetMimeType(filePath) { | ||
| const ext = filePath.split('.').pop()?.toLowerCase() || ''; | ||
| const mimeTypes = { | ||
| html: 'text/html', | ||
| css: 'text/css', | ||
| js: 'application/javascript', | ||
| json: 'application/json', | ||
| png: 'image/png', | ||
| jpg: 'image/jpeg', | ||
| jpeg: 'image/jpeg', | ||
| gif: 'image/gif', | ||
| svg: 'image/svg+xml', | ||
| txt: 'text/plain', | ||
| pdf: 'application/pdf', | ||
| }; | ||
| return mimeTypes[ext] || 'application/octet-stream'; | ||
| } | ||
| //# sourceMappingURL=mime.js.map |
| {"version":3,"file":"mime.js","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":";AAAA;;;EAGE;;AAGF,kCAgBC;AAjBD,oCAAoC;AACpC,SAAgB,WAAW,CAAC,QAAgB;IACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC3D,MAAM,SAAS,GAA2B;QACtC,IAAI,EAAE,WAAW;QACjB,GAAG,EAAE,UAAU;QACf,EAAE,EAAE,wBAAwB;QAC5B,IAAI,EAAE,kBAAkB;QACxB,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,YAAY;QACjB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,eAAe;QACpB,GAAG,EAAE,YAAY;QACjB,GAAG,EAAE,iBAAiB;KACzB,CAAC;IACF,OAAO,SAAS,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;AACxD,CAAC"} |
| import type { RivetPlugin } from '../types'; | ||
| export declare const LoggingPlugin: RivetPlugin; | ||
| //# sourceMappingURL=logging.d.ts.map |
| {"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../src/plugins/logging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,UAAU,CAAC;AAExD,eAAO,MAAM,aAAa,EAAE,WAmB3B,CAAC"} |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.LoggingPlugin = void 0; | ||
| exports.LoggingPlugin = { | ||
| name: 'logging', | ||
| version: '1.0.0', | ||
| install(app) { | ||
| app.UseMiddleware((req, res, next) => { | ||
| const start = Date.now(); | ||
| const method = req.method; | ||
| const url = req.url; | ||
| console.log(`${method} ${url}`); | ||
| // Use 'finish' event instead of overriding end | ||
| res.once('finish', () => { | ||
| const duration = Date.now() - start; | ||
| console.log(`${method} ${url} - ${duration}ms`); | ||
| }); | ||
| next(); | ||
| }); | ||
| }, | ||
| }; | ||
| //# sourceMappingURL=logging.js.map |
| {"version":3,"file":"logging.js","sourceRoot":"","sources":["../../src/plugins/logging.ts"],"names":[],"mappings":";;;AAEa,QAAA,aAAa,GAAgB;IACtC,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,OAAO;IAChB,OAAO,CAAC,GAAG;QACP,GAAG,CAAC,aAAa,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YACjC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;YAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;YAEhC,+CAA+C;YAC/C,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;gBACpC,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,GAAG,MAAM,QAAQ,IAAI,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC;YAEH,IAAI,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAC"} |
| import { RivetResponse } from './types'; | ||
| export declare function InjectResponseHelpers(res: RivetResponse): void; | ||
| //# sourceMappingURL=response.d.ts.map |
| {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAsC9D"} |
| "use strict"; | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.InjectResponseHelpers = InjectResponseHelpers; | ||
| const fs_1 = __importDefault(require("fs")); | ||
| const path_1 = __importDefault(require("path")); | ||
| const mime_1 = require("./mime"); | ||
| function InjectResponseHelpers(res) { | ||
| res.send = (data) => { | ||
| res.writeHead(200, { 'Content-Type': 'text/plain' }); | ||
| res.end(String(data)); | ||
| }; | ||
| res.json = (data) => { | ||
| res.writeHead(200, { 'Content-Type': 'application/json' }); | ||
| res.end(JSON.stringify(data)); | ||
| }; | ||
| res.SendFile = (filePath, contentType) => { | ||
| const fullPath = path_1.default.resolve(filePath); | ||
| if (!fullPath.startsWith(process.cwd())) { | ||
| res.writeHead(403, { 'Content-Type': 'text/plain' }); | ||
| res.end('403: Forbidden'); | ||
| return; | ||
| } | ||
| fs_1.default.access(fullPath, fs_1.default.constants.R_OK, (err) => { | ||
| if (err) { | ||
| res.writeHead(404, { 'Content-Type': 'text/plain' }); | ||
| res.end('404: File not found'); | ||
| return; | ||
| } | ||
| const mimeType = contentType || (0, mime_1.GetMimeType)(fullPath); | ||
| const stream = fs_1.default.createReadStream(fullPath); | ||
| res.writeHead(200, { 'Content-Type': mimeType }); | ||
| stream.pipe(res); | ||
| stream.on('error', () => { | ||
| res.writeHead(500, { 'Content-Type': 'text/plain' }); | ||
| res.end('500: Internal Server Error'); | ||
| }); | ||
| }); | ||
| }; | ||
| } | ||
| //# sourceMappingURL=response.js.map |
| {"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":";;;;;AAKA,sDAsCC;AA3CD,4CAAoB;AACpB,gDAAwB;AAExB,iCAAqC;AAErC,SAAgB,qBAAqB,CAAC,GAAkB;IACpD,GAAG,CAAC,IAAI,GAAG,CAAC,IAAS,EAAE,EAAE;QACrB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;QACrD,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC;IAEF,GAAG,CAAC,IAAI,GAAG,CAAC,IAAS,EAAE,EAAE;QACrB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC;IAEF,GAAG,CAAC,QAAQ,GAAG,CAAC,QAAgB,EAAE,WAAoB,EAAE,EAAE;QACtD,MAAM,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAExC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;YACtC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;YACrD,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;YAC1B,OAAO;QACX,CAAC;QAED,YAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3C,IAAI,GAAG,EAAE,CAAC;gBACN,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;gBACrD,GAAG,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;gBAC/B,OAAO;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,WAAW,IAAI,IAAA,kBAAW,EAAC,QAAQ,CAAC,CAAC;YACtD,MAAM,MAAM,GAAG,YAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC7C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAEjB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACpB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;gBACrD,GAAG,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;AACN,CAAC"} |
| import type { RivetResponse, RouteHandlers, CorsOptions, RivetPlugin, Middleware } from './types'; | ||
| import type { IncomingMessage } from 'http'; | ||
| export declare class Rivet { | ||
| private plugins; | ||
| private middlewares; | ||
| private server; | ||
| routes: { | ||
| GET: RouteHandlers; | ||
| POST: RouteHandlers; | ||
| PUT: RouteHandlers; | ||
| DELETE: RouteHandlers; | ||
| }; | ||
| constructor(); | ||
| private corsOptions; | ||
| private serverCallbacks; | ||
| cors(options: CorsOptions): this; | ||
| start(port: number, callback?: () => void): void; | ||
| UseMiddleware(middleware: Middleware): this; | ||
| OnServerCreate(callback: (server: any) => void): void; | ||
| use(plugin: RivetPlugin): this; | ||
| private ParsePath; | ||
| get(path: string, handler: Function): void; | ||
| post(path: string, handler: Function): void; | ||
| OnRequest(req: IncomingMessage, res: RivetResponse): Promise<void>; | ||
| HandleRoute(req: IncomingMessage, res: RivetResponse): Promise<void>; | ||
| static(prefix: string, directory: string): void; | ||
| } | ||
| //# sourceMappingURL=rivet.d.ts.map |
| {"version":3,"file":"rivet.d.ts","sourceRoot":"","sources":["../src/rivet.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAKlG,OAAO,KAAK,EAAE,eAAe,EAA0B,MAAM,MAAM,CAAC;AAkDpE,qBAAa,KAAK;IACd,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,WAAW,CAAoB;IAEvC,OAAO,CAAC,MAAM,CAAuB;IACrC,MAAM,EAAE;QACJ,GAAG,EAAE,aAAa,CAAC;QACnB,IAAI,EAAE,aAAa,CAAC;QACpB,GAAG,EAAE,aAAa,CAAC;QACnB,MAAM,EAAE,aAAa,CAAC;KACzB,CAAC;;IAYF,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,eAAe,CAAoC;IAG3D,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAMhC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI;IAchD,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAM3C,cAAc,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI;IAM9C,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAO9B,OAAO,CAAC,SAAS;IAkBjB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI;IAU1C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI;IAUrC,SAAS,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlE,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA8E1E,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAgBlD"} |
| "use strict"; | ||
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Rivet = void 0; | ||
| const http_1 = __importDefault(require("http")); | ||
| const response_1 = require("./response"); | ||
| const cors_1 = require("./cors"); | ||
| const static_1 = require("./static"); | ||
| // Parses the request body | ||
| async function ParseBody(req) { | ||
| return new Promise((resolve, reject) => { | ||
| const chunks = []; | ||
| req.on('data', (chunk) => { | ||
| chunks.push(chunk); | ||
| }); | ||
| req.on('end', () => { | ||
| const body = Buffer.concat(chunks).toString(); | ||
| // No body | ||
| if (!body) { | ||
| resolve({}); | ||
| return; | ||
| } | ||
| // Check content type | ||
| const contentType = req.headers['content-type'] || ''; | ||
| if (contentType.includes('application/json')) { | ||
| try { | ||
| resolve(JSON.parse(body)); | ||
| } | ||
| catch (e) { | ||
| resolve({}); | ||
| } | ||
| } | ||
| else if (contentType.includes('application/x-www-form-urlencoded')) { | ||
| // Parse form data: "name=Alice&age=30" → { name: "Alice", age: "30" } | ||
| const params = new URLSearchParams(body); | ||
| const result = {}; | ||
| params.forEach((value, key) => { | ||
| result[key] = value; | ||
| }); | ||
| resolve(result); | ||
| } | ||
| else { | ||
| // Plain text | ||
| resolve(body); | ||
| } | ||
| }); | ||
| req.on('error', (err) => { | ||
| reject(err); | ||
| }); | ||
| }); | ||
| } | ||
| // Main library class | ||
| class Rivet { | ||
| constructor() { | ||
| this.plugins = []; | ||
| this.middlewares = []; | ||
| this.server = null; | ||
| this.corsOptions = {}; | ||
| this.serverCallbacks = []; | ||
| // Route function handlers are defined in here | ||
| this.routes = { | ||
| GET: {}, | ||
| POST: {}, | ||
| PUT: {}, | ||
| DELETE: {}, | ||
| }; | ||
| } | ||
| // Allows editing of CORS options before the server is started, or while its running | ||
| cors(options) { | ||
| this.corsOptions = { ...this.corsOptions, ...options }; | ||
| return this; | ||
| } | ||
| // Starts the HTTP server and serves on the provided port | ||
| start(port, callback) { | ||
| this.server = http_1.default.createServer((req, res) => { | ||
| this.OnRequest(req, res); | ||
| }); | ||
| this.serverCallbacks.forEach(cb => cb(this.server)); // Call the callbacks | ||
| this.server.listen(port, () => { | ||
| console.log(`Rivet fastened on ${port}`); | ||
| callback?.(); | ||
| }); | ||
| } | ||
| // Registers new middleware to run every request | ||
| UseMiddleware(middleware) { | ||
| this.middlewares.push(middleware); | ||
| return this; | ||
| } | ||
| // Registers a callback to be called when the server starts | ||
| OnServerCreate(callback) { | ||
| this.serverCallbacks.push(callback); | ||
| if (this.server) | ||
| callback(this.server); | ||
| } | ||
| // Registers a plugin | ||
| use(plugin) { | ||
| plugin.install(this); // Call the install function | ||
| this.plugins.push(plugin); | ||
| return this; | ||
| } | ||
| // Parses a path and returns the regex and parameters | ||
| ParsePath(path) { | ||
| const paramNames = []; | ||
| // Replace :param with regex capture groups | ||
| const regexPattern = path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, paramName) => { | ||
| paramNames.push(paramName); | ||
| return '([^/]+)'; // Capture anything except slash | ||
| }); | ||
| console.log('Path:', path, '→ Regex:', regexPattern); // Debug | ||
| // Create regex that matches exactly | ||
| const regex = new RegExp(`^${regexPattern}$`); | ||
| return { regex, paramNames }; | ||
| } | ||
| // Registers a new GET handler | ||
| get(path, handler) { | ||
| const { regex, paramNames } = this.ParsePath(path); | ||
| this.routes.GET[path] = { | ||
| handler, | ||
| params: paramNames, | ||
| regex, | ||
| }; | ||
| } | ||
| // Registers a new POST handler | ||
| post(path, handler) { | ||
| const { regex, paramNames } = this.ParsePath(path); | ||
| this.routes.POST[path] = { | ||
| handler, | ||
| params: paramNames, | ||
| regex, | ||
| }; | ||
| } | ||
| // Called when the server recives an request | ||
| async OnRequest(req, res) { | ||
| // Run all the middleware | ||
| let index = 0; | ||
| const next = async () => { | ||
| if (index < this.middlewares.length) { | ||
| const middleware = this.middlewares[index++]; | ||
| await middleware(req, res, next); | ||
| } | ||
| else { | ||
| await this.HandleRoute(req, res); | ||
| } | ||
| }; | ||
| await next(); | ||
| } | ||
| // Called when after middleware runs | ||
| async HandleRoute(req, res) { | ||
| // Always apply CORS headers | ||
| (0, cors_1.AddCORSHeader)(res, this.corsOptions); | ||
| // Handle preflight | ||
| if ((0, cors_1.HandlePreflight)(req, res)) | ||
| return; | ||
| const method = req.method; // Get the HTTP method | ||
| const url = req.url?.split('?')[0] || '/'; // Get the URL and strip the query params | ||
| // Parse query parameters | ||
| const urlObj = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`); | ||
| const query = Object.fromEntries(urlObj.searchParams); | ||
| req.query = query; | ||
| // Parse body for POST, PUT, PATCH requests | ||
| if (method === 'POST' || method === 'PUT') { | ||
| try { | ||
| const body = await ParseBody(req); | ||
| req.body = body; | ||
| } | ||
| catch (err) { | ||
| res.writeHead(400, { 'Content-Type': 'application/json' }); | ||
| res.end(JSON.stringify({ error: 'Invalid request body' })); | ||
| return; | ||
| } | ||
| } | ||
| const methodRoutes = this.routes[method]; | ||
| if (!methodRoutes) { | ||
| res.writeHead(405, { 'Content-Type': 'text/plain' }); | ||
| res.end('405: Method Not Allowed'); | ||
| return; | ||
| } | ||
| // Try exact match first (fast path) | ||
| let route = methodRoutes[url]; | ||
| let params = {}; | ||
| // If no exact match, try regex | ||
| if (!route) { | ||
| for (const [pattern, r] of Object.entries(methodRoutes)) { | ||
| if (pattern.endsWith('*')) | ||
| continue; // Check if "/*" or such | ||
| const match = url.match(r.regex); | ||
| if (match) { | ||
| route = r; | ||
| // Extract params from capture groups | ||
| r.params.forEach((paramName, index) => { | ||
| params[paramName] = match[index + 1]; | ||
| }); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| // If still no match, try wildcard (static files) | ||
| if (!route) { | ||
| for (const [pattern, r] of Object.entries(methodRoutes)) { | ||
| if (pattern.endsWith('*') && url.startsWith(pattern.slice(0, -1))) { | ||
| route = r; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| // If a handler was registered, call it | ||
| if (route && route.handler) { | ||
| (0, response_1.InjectResponseHelpers)(res); // Injects response helpers into the res object | ||
| req.params = params; | ||
| await route.handler(req, res); // Now finnaly, call the handler | ||
| } | ||
| else { | ||
| res.writeHead(404, { 'Content-Type': 'text/plain' }); | ||
| res.end('404: Page not found!'); | ||
| } | ||
| } | ||
| // Serves a static directory on the server | ||
| static(prefix, directory) { | ||
| const StaticHandler = (0, static_1.ServeStatic)(directory); | ||
| // Register a catch-all handler for this prefix | ||
| this.routes.GET[`${prefix}*`] = { | ||
| handler: async (req, res) => { | ||
| const served = await StaticHandler(req, res); | ||
| if (!served) { | ||
| res.writeHead(404, { 'Content-Type': 'text/plain' }); | ||
| res.end('404: File not found!'); | ||
| } | ||
| }, | ||
| params: [], | ||
| regex: new RegExp(`^${prefix}.*$`), | ||
| }; | ||
| } | ||
| } | ||
| exports.Rivet = Rivet; | ||
| //# sourceMappingURL=rivet.js.map |
| {"version":3,"file":"rivet.js","sourceRoot":"","sources":["../src/rivet.ts"],"names":[],"mappings":"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"} |
| import { IncomingMessage, ServerResponse } from 'http'; | ||
| export declare function ServeStatic(rootDir: string): (req: IncomingMessage, res: ServerResponse) => Promise<boolean>; | ||
| //# sourceMappingURL=static.d.ts.map |
| {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAIvD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,IAGzB,KAAK,eAAe,EAAE,KAAK,cAAc,KAAG,OAAO,CAAC,OAAO,CAAC,CAyC7E"} |
| "use strict"; | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.ServeStatic = ServeStatic; | ||
| const fs_1 = __importDefault(require("fs")); | ||
| const path_1 = __importDefault(require("path")); | ||
| const mime_1 = require("./mime"); | ||
| // Serves the contents of a folder as static files on the server | ||
| function ServeStatic(rootDir) { | ||
| const absoluteRoot = path_1.default.resolve(rootDir); | ||
| return async (req, res) => { | ||
| const url = req.url?.split('?')[0] || '/'; | ||
| // Convert URL to file path | ||
| let filePath = path_1.default.join(absoluteRoot, url); | ||
| // Security: prevent directory traversal | ||
| if (!filePath.startsWith(absoluteRoot)) { | ||
| return false; | ||
| } | ||
| // Check if file exists | ||
| try { | ||
| const stat = await fs_1.default.promises.stat(filePath); | ||
| if (stat.isDirectory()) { | ||
| // Try index.html, if its there make it so the root returns index.html | ||
| filePath = path_1.default.join(filePath, 'index.html'); | ||
| try { | ||
| await fs_1.default.promises.stat(filePath); | ||
| } | ||
| catch { | ||
| return false; // No index.html | ||
| } | ||
| } | ||
| // Serve the file | ||
| const mimeType = (0, mime_1.GetMimeType)(filePath); | ||
| const stream = fs_1.default.createReadStream(filePath); | ||
| res.writeHead(200, { 'Content-Type': mimeType }); | ||
| stream.pipe(res); | ||
| stream.on('error', () => { | ||
| res.writeHead(500); | ||
| res.end(); | ||
| }); | ||
| return true; | ||
| } | ||
| catch { | ||
| return false; // File not found | ||
| } | ||
| }; | ||
| } | ||
| //# sourceMappingURL=static.js.map |
| {"version":3,"file":"static.js","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":";;;;;AAMA,kCA4CC;AAlDD,4CAAoB;AACpB,gDAAwB;AAExB,iCAAqC;AAErC,gEAAgE;AAChE,SAAgB,WAAW,CAAC,OAAe;IACvC,MAAM,YAAY,GAAG,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAE3C,OAAO,KAAK,EAAE,GAAoB,EAAE,GAAmB,EAAoB,EAAE;QACzE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QAE1C,2BAA2B;QAC3B,IAAI,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QAE5C,wCAAwC;QACxC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACrC,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,uBAAuB;QACvB,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,YAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAE9C,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACrB,sEAAsE;gBACtE,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;gBAC7C,IAAI,CAAC;oBACD,MAAM,YAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACrC,CAAC;gBAAC,MAAM,CAAC;oBACL,OAAO,KAAK,CAAC,CAAC,gBAAgB;gBAClC,CAAC;YACL,CAAC;YAED,iBAAiB;YACjB,MAAM,QAAQ,GAAG,IAAA,kBAAW,EAAC,QAAQ,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,YAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC7C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAEjB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACpB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;YACd,CAAC,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,KAAK,CAAC,CAAC,iBAAiB;QACnC,CAAC;IACL,CAAC,CAAC;AACN,CAAC"} |
| import { ServerResponse, IncomingMessage } from 'node:http'; | ||
| import type { Rivet } from './rivet'; | ||
| export type Route = { | ||
| handler: Function; | ||
| params: string[]; | ||
| regex: RegExp; | ||
| }; | ||
| export type RouteHandlers = { | ||
| [key: string]: Route; | ||
| }; | ||
| export type RivetResponse = ServerResponse & { | ||
| send: (data: any) => void; | ||
| json: (data: any) => void; | ||
| SendFile: (filePath: string, contentType?: string) => void; | ||
| }; | ||
| export interface CorsOptions { | ||
| origin?: string | string[]; | ||
| methods?: string[]; | ||
| headers?: string[]; | ||
| credentials?: boolean; | ||
| } | ||
| export interface RivetPlugin { | ||
| name: string; | ||
| version?: string; | ||
| install(app: Rivet): void | Promise<void>; | ||
| } | ||
| export type Middleware = (req: IncomingMessage, res: RivetResponse, next: () => void) => void; | ||
| //# sourceMappingURL=types.d.ts.map |
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGrC,MAAM,MAAM,KAAK,GAAG;IAChB,OAAO,EAAE,QAAQ,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;CACxB,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG;IACzC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC1B,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9D,CAAC;AAEF,MAAM,WAAW,WAAW;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC"} |
| "use strict"; | ||
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| //# sourceMappingURL=types.js.map |
| {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;EAGE"} |
| MIT License | ||
| Copyright (c) 2026 IgosProjects | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| { | ||
| "name": "@igosprojects/rivet", | ||
| "version": "1.0.6", | ||
| "description": "Rivet is a small server framework for NodeJS", | ||
| "main": "./dist/index.js", | ||
| "types": "./dist/index.d.ts", | ||
| "files": [ | ||
| "dist", | ||
| "src", | ||
| "README.md", | ||
| "LICENSE" | ||
| ], | ||
| "keywords": [ | ||
| "rivet" | ||
| ], | ||
| "author": "EyeDev", | ||
| "license": "MIT", | ||
| "devDependencies": { | ||
| "@types/node": "^25.9.1", | ||
| "prettier": "^3.8.3", | ||
| "ts-node": "^10.9.2", | ||
| "typescript": "^6.0.3" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/IgosProjects/rivet.git" | ||
| }, | ||
| "bugs": { | ||
| "url": "https://github.com/IgosProjects/rivet/issues" | ||
| }, | ||
| "homepage": "https://github.com/IgosProjects/rivet#readme", | ||
| "scripts": { | ||
| "build": "tsc", | ||
| "dev": "tsc --watch", | ||
| "test": "echo \"Error: no test specified\" && exit 1", | ||
| "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", | ||
| "format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"" | ||
| } | ||
| } |
| # Rivet | ||
|  | ||
|  | ||
|  | ||
|  | ||
| Rivet is a small Typescript based server platform for NodeJS, it is a Express inspired library for servers | ||
| # Installing | ||
| To install Rivet you will need to have an NPM project setup, if not run the necessary commands. | ||
| Then you can run this command to install rivet:```npm install @igosprojects/rivet``` | ||
| # Usage | ||
| To use Rivet you first need to install it using your package manager(look at [Installing](#installing)). | ||
| ## Creating a basic server | ||
| To create a basic server you will need to initilize Rivet, and start the server. The following code does just that: | ||
| ```ts | ||
| import { IncomingMessage } from 'node:http'; | ||
| import { Rivet, RivetResponse } from '@igosprojects/rivet'; | ||
| const app = new Rivet(); | ||
| // This function is called when the route "/" is called | ||
| app.get('/', (req: IncomingMessage, res: RivetResponse) => { | ||
| res.send("hi!"); | ||
| }); | ||
| app.start(3000); | ||
| ``` | ||
| ## Serving static files | ||
| Rivet supports serving static files and folders, to do so you can use the ```app.static``` function after initilizing the server. | ||
| For example this code will serve the public directory next to the file: | ||
| ```ts | ||
| import { Rivet } from '@igosprojects/rivet'; | ||
| const app = new Rivet(); | ||
| app.static("/", "./public"); | ||
| app.start(3000); | ||
| ``` | ||
| ## Reading parameters(/:id) | ||
| Rivet can read parameters passed on the URL bar, to do that you can use the ```req.params``` option for that. | ||
| To read the parameters you can do: ```const { paramname } = req.params;```, now paramname is equal to the parameter if it was passed in. | ||
| To register routes with parameters you shouldnt register ```/users``` but instead ```/users/:id``` otherwise it wont work! |
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| // This file handles CORS on the server side, it is used for security | ||
| import type { RivetResponse, CorsOptions } from './types'; | ||
| const DefaultCorsOptions: CorsOptions = { | ||
| origin: '*', | ||
| methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], | ||
| headers: ['Content-Type', 'Authorization'], | ||
| credentials: false, | ||
| }; | ||
| // Adds the needed CORS tags to the response header | ||
| export function AddCORSHeader(res: RivetResponse, options: CorsOptions = DefaultCorsOptions) { | ||
| const opts = { ...DefaultCorsOptions, ...options }; | ||
| // Origin | ||
| const origin = Array.isArray(opts.origin) ? opts.origin.join(', ') : opts.origin; | ||
| res.setHeader('Access-Control-Allow-Origin', origin || '*'); | ||
| // Headers | ||
| if (opts.headers) { | ||
| res.setHeader('Access-Control-Allow-Headers', opts.headers.join(', ')); | ||
| } | ||
| // Credentials | ||
| if (opts.credentials) { | ||
| res.setHeader('Access-Control-Allow-Credentials', 'true'); | ||
| } | ||
| } | ||
| // Handles preflight request sent by browsers | ||
| export function HandlePreflight(req: any, res: RivetResponse): boolean { | ||
| if (req.method === 'OPTIONS') { | ||
| res.writeHead(204); // 204: No content | ||
| res.end(); | ||
| return true; | ||
| } | ||
| return false; | ||
| } |
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| export { Rivet } from './rivet'; | ||
| export type { RivetResponse, RivetPlugin } from './types'; |
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| // Returns the MIME type of any file | ||
| export function GetMimeType(filePath: string): string { | ||
| const ext = filePath.split('.').pop()?.toLowerCase() || ''; | ||
| const mimeTypes: Record<string, string> = { | ||
| html: 'text/html', | ||
| css: 'text/css', | ||
| js: 'application/javascript', | ||
| json: 'application/json', | ||
| png: 'image/png', | ||
| jpg: 'image/jpeg', | ||
| jpeg: 'image/jpeg', | ||
| gif: 'image/gif', | ||
| svg: 'image/svg+xml', | ||
| txt: 'text/plain', | ||
| pdf: 'application/pdf', | ||
| }; | ||
| return mimeTypes[ext] || 'application/octet-stream'; | ||
| } |
| import type { RivetPlugin, Middleware } from '../types'; | ||
| export const LoggingPlugin: RivetPlugin = { | ||
| name: 'logging', | ||
| version: '1.0.0', | ||
| install(app) { | ||
| app.UseMiddleware((req, res, next) => { | ||
| const start = Date.now(); | ||
| const method = req.method; | ||
| const url = req.url; | ||
| console.log(`${method} ${url}`); | ||
| // Use 'finish' event instead of overriding end | ||
| res.once('finish', () => { | ||
| const duration = Date.now() - start; | ||
| console.log(`${method} ${url} - ${duration}ms`); | ||
| }); | ||
| next(); | ||
| }); | ||
| }, | ||
| }; |
| import fs from 'fs'; | ||
| import path from 'path'; | ||
| import { RivetResponse } from './types'; | ||
| import { GetMimeType } from './mime'; | ||
| export function InjectResponseHelpers(res: RivetResponse): void { | ||
| res.send = (data: any) => { | ||
| res.writeHead(200, { 'Content-Type': 'text/plain' }); | ||
| res.end(String(data)); | ||
| }; | ||
| res.json = (data: any) => { | ||
| res.writeHead(200, { 'Content-Type': 'application/json' }); | ||
| res.end(JSON.stringify(data)); | ||
| }; | ||
| res.SendFile = (filePath: string, contentType?: string) => { | ||
| const fullPath = path.resolve(filePath); | ||
| if (!fullPath.startsWith(process.cwd())) { | ||
| res.writeHead(403, { 'Content-Type': 'text/plain' }); | ||
| res.end('403: Forbidden'); | ||
| return; | ||
| } | ||
| fs.access(fullPath, fs.constants.R_OK, (err) => { | ||
| if (err) { | ||
| res.writeHead(404, { 'Content-Type': 'text/plain' }); | ||
| res.end('404: File not found'); | ||
| return; | ||
| } | ||
| const mimeType = contentType || GetMimeType(fullPath); | ||
| const stream = fs.createReadStream(fullPath); | ||
| res.writeHead(200, { 'Content-Type': mimeType }); | ||
| stream.pipe(res); | ||
| stream.on('error', () => { | ||
| res.writeHead(500, { 'Content-Type': 'text/plain' }); | ||
| res.end('500: Internal Server Error'); | ||
| }); | ||
| }); | ||
| }; | ||
| } |
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| import http from 'http'; | ||
| import type { RivetResponse, RouteHandlers, CorsOptions, RivetPlugin, Middleware } from './types'; | ||
| import { InjectResponseHelpers } from './response'; | ||
| import { AddCORSHeader, HandlePreflight } from './cors'; | ||
| import { ServeStatic } from './static'; | ||
| import type { IncomingMessage, Server, ServerResponse } from 'http'; | ||
| // Parses the request body | ||
| async function ParseBody(req: IncomingMessage): Promise<any> { | ||
| return new Promise((resolve, reject) => { | ||
| const chunks: Buffer[] = []; | ||
| req.on('data', (chunk) => { | ||
| chunks.push(chunk); | ||
| }); | ||
| req.on('end', () => { | ||
| const body = Buffer.concat(chunks).toString(); | ||
| // No body | ||
| if (!body) { | ||
| resolve({}); | ||
| return; | ||
| } | ||
| // Check content type | ||
| const contentType = req.headers['content-type'] || ''; | ||
| if (contentType.includes('application/json')) { | ||
| try { | ||
| resolve(JSON.parse(body)); | ||
| } catch (e) { | ||
| resolve({}); | ||
| } | ||
| } else if (contentType.includes('application/x-www-form-urlencoded')) { | ||
| // Parse form data: "name=Alice&age=30" → { name: "Alice", age: "30" } | ||
| const params = new URLSearchParams(body); | ||
| const result: Record<string, string> = {}; | ||
| params.forEach((value, key) => { | ||
| result[key] = value; | ||
| }); | ||
| resolve(result); | ||
| } else { | ||
| // Plain text | ||
| resolve(body); | ||
| } | ||
| }); | ||
| req.on('error', (err) => { | ||
| reject(err); | ||
| }); | ||
| }); | ||
| } | ||
| // Main library class | ||
| export class Rivet { | ||
| private plugins: RivetPlugin[] = []; | ||
| private middlewares: Middleware[] = []; | ||
| private server: Server | null = null; | ||
| routes: { | ||
| GET: RouteHandlers; | ||
| POST: RouteHandlers; | ||
| PUT: RouteHandlers; | ||
| DELETE: RouteHandlers; | ||
| }; | ||
| constructor() { | ||
| // Route function handlers are defined in here | ||
| this.routes = { | ||
| GET: {}, | ||
| POST: {}, | ||
| PUT: {}, | ||
| DELETE: {}, | ||
| }; | ||
| } | ||
| private corsOptions: CorsOptions = {}; | ||
| private serverCallbacks: Array<(server: any) => void> = []; | ||
| // Allows editing of CORS options before the server is started, or while its running | ||
| cors(options: CorsOptions): this { | ||
| this.corsOptions = { ...this.corsOptions, ...options }; | ||
| return this; | ||
| } | ||
| // Starts the HTTP server and serves on the provided port | ||
| start(port: number, callback?: () => void): void { | ||
| this.server = http.createServer((req: IncomingMessage, res: ServerResponse) => { | ||
| this.OnRequest(req, res as RivetResponse); | ||
| }); | ||
| this.serverCallbacks.forEach(cb => cb(this.server)); // Call the callbacks | ||
| this.server.listen(port, () => { | ||
| console.log(`Rivet fastened on ${port}`); | ||
| callback?.(); | ||
| }); | ||
| } | ||
| // Registers new middleware to run every request | ||
| UseMiddleware(middleware: Middleware): this { | ||
| this.middlewares.push(middleware); | ||
| return this; | ||
| } | ||
| // Registers a callback to be called when the server starts | ||
| OnServerCreate(callback: (server: any) => void) { | ||
| this.serverCallbacks.push(callback); | ||
| if (this.server) callback(this.server); | ||
| } | ||
| // Registers a plugin | ||
| use(plugin: RivetPlugin): this { | ||
| plugin.install(this); // Call the install function | ||
| this.plugins.push(plugin); | ||
| return this; | ||
| } | ||
| // Parses a path and returns the regex and parameters | ||
| private ParsePath(path: string): { regex: RegExp; paramNames: string[] } { | ||
| const paramNames: string[] = []; | ||
| // Replace :param with regex capture groups | ||
| const regexPattern = path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, paramName) => { | ||
| paramNames.push(paramName); | ||
| return '([^/]+)'; // Capture anything except slash | ||
| }); | ||
| console.log('Path:', path, '→ Regex:', regexPattern); // Debug | ||
| // Create regex that matches exactly | ||
| const regex = new RegExp(`^${regexPattern}$`); | ||
| return { regex, paramNames }; | ||
| } | ||
| // Registers a new GET handler | ||
| get(path: string, handler: Function): void { | ||
| const { regex, paramNames } = this.ParsePath(path); | ||
| this.routes.GET[path] = { | ||
| handler, | ||
| params: paramNames, | ||
| regex, | ||
| }; | ||
| } | ||
| // Registers a new POST handler | ||
| post(path: string, handler: Function): void { | ||
| const { regex, paramNames } = this.ParsePath(path); | ||
| this.routes.POST[path] = { | ||
| handler, | ||
| params: paramNames, | ||
| regex, | ||
| }; | ||
| } | ||
| // Called when the server recives an request | ||
| async OnRequest(req: IncomingMessage, res: RivetResponse): Promise<void> { | ||
| // Run all the middleware | ||
| let index = 0; | ||
| const next = async () => { | ||
| if (index < this.middlewares.length) { | ||
| const middleware = this.middlewares[index++]; | ||
| await middleware(req, res, next); | ||
| } else { | ||
| await this.HandleRoute(req, res); | ||
| } | ||
| }; | ||
| await next(); | ||
| } | ||
| // Called when after middleware runs | ||
| async HandleRoute(req: IncomingMessage, res: RivetResponse): Promise<void> { | ||
| // Always apply CORS headers | ||
| AddCORSHeader(res, this.corsOptions); | ||
| // Handle preflight | ||
| if (HandlePreflight(req, res)) return; | ||
| const method = req.method as keyof typeof this.routes; // Get the HTTP method | ||
| const url = req.url?.split('?')[0] || '/'; // Get the URL and strip the query params | ||
| // Parse query parameters | ||
| const urlObj = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`); | ||
| const query = Object.fromEntries(urlObj.searchParams); | ||
| (req as any).query = query; | ||
| // Parse body for POST, PUT, PATCH requests | ||
| if (method === 'POST' || method === 'PUT') { | ||
| try { | ||
| const body = await ParseBody(req); | ||
| (req as any).body = body; | ||
| } catch (err) { | ||
| res.writeHead(400, { 'Content-Type': 'application/json' }); | ||
| res.end(JSON.stringify({ error: 'Invalid request body' })); | ||
| return; | ||
| } | ||
| } | ||
| const methodRoutes = this.routes[method]; | ||
| if (!methodRoutes) { | ||
| res.writeHead(405, { 'Content-Type': 'text/plain' }); | ||
| res.end('405: Method Not Allowed'); | ||
| return; | ||
| } | ||
| // Try exact match first (fast path) | ||
| let route = methodRoutes[url]; | ||
| let params: Record<string, string> = {}; | ||
| // If no exact match, try regex | ||
| if (!route) { | ||
| for (const [pattern, r] of Object.entries(methodRoutes)) { | ||
| if (pattern.endsWith('*')) continue; // Check if "/*" or such | ||
| const match = url.match(r.regex); | ||
| if (match) { | ||
| route = r; | ||
| // Extract params from capture groups | ||
| r.params.forEach((paramName, index) => { | ||
| params[paramName] = match[index + 1]; | ||
| }); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| // If still no match, try wildcard (static files) | ||
| if (!route) { | ||
| for (const [pattern, r] of Object.entries(methodRoutes)) { | ||
| if (pattern.endsWith('*') && url.startsWith(pattern.slice(0, -1))) { | ||
| route = r; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| // If a handler was registered, call it | ||
| if (route && route.handler) { | ||
| InjectResponseHelpers(res); // Injects response helpers into the res object | ||
| (req as any).params = params; | ||
| await route.handler(req, res); // Now finnaly, call the handler | ||
| } else { | ||
| res.writeHead(404, { 'Content-Type': 'text/plain' }); | ||
| res.end('404: Page not found!'); | ||
| } | ||
| } | ||
| // Serves a static directory on the server | ||
| static(prefix: string, directory: string): void { | ||
| const StaticHandler = ServeStatic(directory); | ||
| // Register a catch-all handler for this prefix | ||
| this.routes.GET[`${prefix}*`] = { | ||
| handler: async (req: IncomingMessage, res: RivetResponse) => { | ||
| const served = await StaticHandler(req, res); | ||
| if (!served) { | ||
| res.writeHead(404, { 'Content-Type': 'text/plain' }); | ||
| res.end('404: File not found!'); | ||
| } | ||
| }, | ||
| params: [], | ||
| regex: new RegExp(`^${prefix}.*$`), | ||
| }; | ||
| } | ||
| } |
| import fs from 'fs'; | ||
| import path from 'path'; | ||
| import { IncomingMessage, ServerResponse } from 'http'; | ||
| import { GetMimeType } from './mime'; | ||
| // Serves the contents of a folder as static files on the server | ||
| export function ServeStatic(rootDir: string) { | ||
| const absoluteRoot = path.resolve(rootDir); | ||
| return async (req: IncomingMessage, res: ServerResponse): Promise<boolean> => { | ||
| const url = req.url?.split('?')[0] || '/'; | ||
| // Convert URL to file path | ||
| let filePath = path.join(absoluteRoot, url); | ||
| // Security: prevent directory traversal | ||
| if (!filePath.startsWith(absoluteRoot)) { | ||
| return false; | ||
| } | ||
| // Check if file exists | ||
| try { | ||
| const stat = await fs.promises.stat(filePath); | ||
| if (stat.isDirectory()) { | ||
| // Try index.html, if its there make it so the root returns index.html | ||
| filePath = path.join(filePath, 'index.html'); | ||
| try { | ||
| await fs.promises.stat(filePath); | ||
| } catch { | ||
| return false; // No index.html | ||
| } | ||
| } | ||
| // Serve the file | ||
| const mimeType = GetMimeType(filePath); | ||
| const stream = fs.createReadStream(filePath); | ||
| res.writeHead(200, { 'Content-Type': mimeType }); | ||
| stream.pipe(res); | ||
| stream.on('error', () => { | ||
| res.writeHead(500); | ||
| res.end(); | ||
| }); | ||
| return true; | ||
| } catch { | ||
| return false; // File not found | ||
| } | ||
| }; | ||
| } |
| /* | ||
| This file is licensed under the MIT license, the terms must be followed! | ||
| Copyright(c) 2026 EyeDev | ||
| */ | ||
| import { ServerResponse, IncomingMessage } from 'node:http'; | ||
| import type { Rivet } from './rivet'; | ||
| // New route type, replaced old simple one | ||
| export type Route = { | ||
| handler: Function; | ||
| params: string[]; // Store param names like ['id', 'name'] | ||
| regex: RegExp; // Converted regex for matching | ||
| }; | ||
| // Route handler type | ||
| export type RouteHandlers = { | ||
| [key: string]: Route; // Changed from Function to Route | ||
| }; | ||
| // HTTP response type | ||
| export type RivetResponse = ServerResponse & { | ||
| send: (data: any) => void; | ||
| json: (data: any) => void; | ||
| SendFile: (filePath: string, contentType?: string) => void; | ||
| }; | ||
| export interface CorsOptions { | ||
| origin?: string | string[]; | ||
| methods?: string[]; | ||
| headers?: string[]; | ||
| credentials?: boolean; | ||
| } | ||
| export interface RivetPlugin { | ||
| name: string; | ||
| version?: string; | ||
| install(app: Rivet): void | Promise<void>; | ||
| } | ||
| export type Middleware = (req: IncomingMessage, res: RivetResponse, next: () => void) => void; |
| lockfileVersion: '9.0' | ||
| settings: | ||
| autoInstallPeers: true | ||
| excludeLinksFromLockfile: false | ||
| importers: | ||
| .: | ||
| dependencies: | ||
| '@igosprojects/rivet': | ||
| specifier: ^1.0.6 | ||
| version: 1.0.6 | ||
| packages: | ||
| '@igosprojects/rivet@1.0.6': | ||
| resolution: {integrity: sha512-LhkQRwKrSEOGvC5cd/LFmlKH1Gbz4J7nMD7qug+xzOavdiSfqxseRDRt1u4YnHApsB4XJDEQ/SRF/HSjSgivig==} | ||
| snapshots: | ||
| '@igosprojects/rivet@1.0.6': {} |
+6
-1
| { | ||
| "name": "@igosprojects/rivet-ws", | ||
| "version": "1.0.1", | ||
| "version": "1.0.2", | ||
| "description": "WebSocket plugin for @igosprojects/rivet", | ||
| "main": "index.js", | ||
| "files": [ | ||
| "src", | ||
| "test", | ||
| "dist" | ||
| ], | ||
| "keywords": [ | ||
@@ -7,0 +12,0 @@ "rivet" |
| module.exports = { | ||
| semi: true, | ||
| singleQuote: true, | ||
| trailingComma: 'es5', | ||
| tabWidth: 4, | ||
| printWidth: 100, | ||
| arrowParens: 'always', | ||
| }; |
| // tsconfig.json | ||
| { | ||
| "compilerOptions": { | ||
| "target": "ES2020", | ||
| "module": "commonjs", | ||
| "lib": ["ES2020"], | ||
| "outDir": "./dist", | ||
| "rootDir": "./src", | ||
| "declaration": true, // generates .d.ts files for TypeScript users | ||
| "declarationMap": true, // helps with editor go-to-definition | ||
| "sourceMap": true, // debugging | ||
| "strict": true, | ||
| "esModuleInterop": true, | ||
| "skipLibCheck": true, | ||
| "types": ["node"] | ||
| }, | ||
| "include": ["src/**/*"], | ||
| "exclude": ["node_modules", "dist"] | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Network access
Supply chain riskThis module accesses the network.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
65963
895.97%58
544.44%1041
1288%4
Infinity%5
Infinity%