karos 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +112 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
karos
|
|
2
|
+
=====
|
|
3
|
+
|
|
4
|
+
**Opinionated, minimal API response & error standardization for Express.**
|
|
5
|
+
|
|
6
|
+
Karos enforces predictable JSON response shapes and centralized error handling in Node.js APIs — without adding business logic, configuration, or framework lock-in.
|
|
7
|
+
|
|
8
|
+
The Problem
|
|
9
|
+
-----------
|
|
10
|
+
|
|
11
|
+
In most Express backends:
|
|
12
|
+
|
|
13
|
+
* Every route formats responses differently
|
|
14
|
+
|
|
15
|
+
* Errors are sometimes strings, sometimes objects
|
|
16
|
+
|
|
17
|
+
* Status codes are inconsistent
|
|
18
|
+
|
|
19
|
+
* Frontend logic becomes fragile and full of conditionals
|
|
20
|
+
|
|
21
|
+
* Teams re-implement the same response boilerplate in every project
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
There is no shared contract between backend and frontend.
|
|
25
|
+
|
|
26
|
+
What Karos Enforces
|
|
27
|
+
-------------------
|
|
28
|
+
|
|
29
|
+
Karos enforces **one response contract** for your entire API.
|
|
30
|
+
|
|
31
|
+
Success
|
|
32
|
+
-------
|
|
33
|
+
|
|
34
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` json{ "success": true, "data": {} } `
|
|
35
|
+
|
|
36
|
+
Error
|
|
37
|
+
-----
|
|
38
|
+
|
|
39
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` json{ "success": false, "error": { "code": "NOT_FOUND", "message": "User not found" } } `
|
|
40
|
+
|
|
41
|
+
No exceptions. No special cases.
|
|
42
|
+
|
|
43
|
+
Install + Usage (60 seconds)
|
|
44
|
+
----------------------------
|
|
45
|
+
|
|
46
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` bashnpm install karos `
|
|
47
|
+
|
|
48
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` const express = require('express'); const { ok, notFoundError, errorHandler } = require('karos'); const app = express(); app.use(express.json()); // Your routes here... app.get('/users/:id', async (req, res) => { const user = await db.findUser(req.params.id); if (!user) { notFoundError('User not found'); // Throws → middleware catches } ok(res, user); }); // ONE middleware catches everything app.use(errorHandler); app.listen(3000); `
|
|
49
|
+
|
|
50
|
+
No try/catch. No per-route error formatting. One consistent API surface.
|
|
51
|
+
|
|
52
|
+
Core API
|
|
53
|
+
--------
|
|
54
|
+
|
|
55
|
+
**ok(res, data, message?, meta?)**Formats a successful response.
|
|
56
|
+
|
|
57
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` ok(res, user); ok(res, user, 'User fetched'); `
|
|
58
|
+
|
|
59
|
+
**notFoundError(message?)**Prebuilt helpers (autocomplete-safe).
|
|
60
|
+
|
|
61
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` notFoundError(); // 404 "Not found" validationError('Invalid email'); // 400 unauthorizedError(); // 401 `
|
|
62
|
+
|
|
63
|
+
**errorHandler**Express middleware that:
|
|
64
|
+
|
|
65
|
+
* Catches all thrown Karos errors
|
|
66
|
+
|
|
67
|
+
* Formats unknown errors as INTERNAL\_ERROR
|
|
68
|
+
|
|
69
|
+
* Preserves correct HTTP status codes
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` app.use(errorHandler); // Last, after all routes `
|
|
73
|
+
|
|
74
|
+
Error Codes (Autocomplete)
|
|
75
|
+
--------------------------
|
|
76
|
+
|
|
77
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` ErrorCode.NOT_FOUND // 404 ErrorCode.VALIDATION_FAILED // 400 ErrorCode.UNAUTHORIZED // 401 ErrorCode.FORBIDDEN // 403 ErrorCode.CONFLICT // 409 ErrorCode.INTERNAL_ERROR // 500 `
|
|
78
|
+
|
|
79
|
+
You control: message, status, structured details.Karos only enforces the response shape.
|
|
80
|
+
|
|
81
|
+
Database Errors (Auto-handled)
|
|
82
|
+
------------------------------
|
|
83
|
+
|
|
84
|
+
Plain textANTLR4BashCC#CSSCoffeeScriptCMakeDartDjangoDockerEJSErlangGitGoGraphQLGroovyHTMLJavaJavaScriptJSONJSXKotlinLaTeXLessLuaMakefileMarkdownMATLABMarkupObjective-CPerlPHPPowerShell.propertiesProtocol BuffersPythonRRubySass (Sass)Sass (Scss)SchemeSQLShellSwiftSVGTSXTypeScriptWebAssemblyYAMLXML` app.get('/orders', async (req, res) => { const orders = await prisma.order.findMany(); // Crashes? → 500 ok(res, orders); }); `
|
|
85
|
+
|
|
86
|
+
**Zero try/catch needed.** Middleware formats everything.
|
|
87
|
+
|
|
88
|
+
What Karos Does NOT Do (By Design)
|
|
89
|
+
----------------------------------
|
|
90
|
+
|
|
91
|
+
❌ No request validation❌ No authentication helpers❌ No logging❌ No database handling❌ No config files❌ No framework adapters (Fastify, Hono, etc.)
|
|
92
|
+
|
|
93
|
+
**Pure formatting layer only.** Your business logic stays yours.
|
|
94
|
+
|
|
95
|
+
TypeScript ✅
|
|
96
|
+
------------
|
|
97
|
+
|
|
98
|
+
Full types + autocomplete for ErrorCode.NOT\_FOUND, response shapes.
|
|
99
|
+
|
|
100
|
+
License
|
|
101
|
+
-------
|
|
102
|
+
|
|
103
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Response, Request, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
declare const ErrorCode: {
|
|
4
|
+
readonly VALIDATION_FAILED: "VALIDATION_FAILED";
|
|
5
|
+
readonly UNAUTHORIZED: "UNAUTHORIZED";
|
|
6
|
+
readonly NOT_FOUND: "NOT_FOUND";
|
|
7
|
+
readonly FORBIDDEN: "FORBIDDEN";
|
|
8
|
+
readonly CONFLICT: "CONFLICT";
|
|
9
|
+
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
10
|
+
};
|
|
11
|
+
type ErrorCodeType = typeof ErrorCode[keyof typeof ErrorCode];
|
|
12
|
+
interface ApiSuccessResponse<T = unknown> {
|
|
13
|
+
success: true;
|
|
14
|
+
data: T;
|
|
15
|
+
message?: string;
|
|
16
|
+
meta?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
interface ApiErrorResponse {
|
|
19
|
+
success: false;
|
|
20
|
+
error: {
|
|
21
|
+
code: ErrorCodeType;
|
|
22
|
+
message: string;
|
|
23
|
+
errors?: Record<string, unknown>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare function ok<T = unknown>(res: Response, data: T, message?: string, meta?: Record<string, unknown>): void;
|
|
28
|
+
declare function fail(res: Response, code: ErrorCodeType, message: string, status: number, errors?: Record<string, unknown>): void;
|
|
29
|
+
|
|
30
|
+
declare class KarosError extends Error {
|
|
31
|
+
readonly code: ErrorCodeType;
|
|
32
|
+
readonly status: number;
|
|
33
|
+
readonly errors?: Record<string, unknown>;
|
|
34
|
+
readonly isKarosError = true;
|
|
35
|
+
constructor(code: ErrorCodeType, message: string, status: number, errors?: Record<string, unknown>);
|
|
36
|
+
}
|
|
37
|
+
declare const notFoundError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
38
|
+
declare const validationError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
39
|
+
declare const unauthorizedError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
40
|
+
declare const httpError: (code: ErrorCodeType, message: string, status: number, errors?: Record<string, unknown>) => never;
|
|
41
|
+
|
|
42
|
+
declare function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction): void;
|
|
43
|
+
|
|
44
|
+
export { type ApiErrorResponse, type ApiSuccessResponse, ErrorCode, type ErrorCodeType, KarosError, errorHandler, fail, httpError, notFoundError, ok, unauthorizedError, validationError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ErrorCode: () => ErrorCode,
|
|
24
|
+
KarosError: () => KarosError,
|
|
25
|
+
errorHandler: () => errorHandler,
|
|
26
|
+
fail: () => fail,
|
|
27
|
+
httpError: () => httpError,
|
|
28
|
+
notFoundError: () => notFoundError,
|
|
29
|
+
ok: () => ok,
|
|
30
|
+
unauthorizedError: () => unauthorizedError,
|
|
31
|
+
validationError: () => validationError
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(index_exports);
|
|
34
|
+
|
|
35
|
+
// src/types.ts
|
|
36
|
+
var ErrorCode = {
|
|
37
|
+
VALIDATION_FAILED: "VALIDATION_FAILED",
|
|
38
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
39
|
+
NOT_FOUND: "NOT_FOUND",
|
|
40
|
+
FORBIDDEN: "FORBIDDEN",
|
|
41
|
+
CONFLICT: "CONFLICT",
|
|
42
|
+
INTERNAL_ERROR: "INTERNAL_ERROR"
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/responses.ts
|
|
46
|
+
function ok(res, data, message, meta) {
|
|
47
|
+
const response = {
|
|
48
|
+
success: true,
|
|
49
|
+
data,
|
|
50
|
+
...message && { message },
|
|
51
|
+
...meta && { meta }
|
|
52
|
+
};
|
|
53
|
+
res.status(200).json(response);
|
|
54
|
+
}
|
|
55
|
+
function fail(res, code, message, status, errors) {
|
|
56
|
+
const response = {
|
|
57
|
+
success: false,
|
|
58
|
+
error: { code, message, ...errors && { errors } }
|
|
59
|
+
};
|
|
60
|
+
res.status(status).json(response);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/errors.ts
|
|
64
|
+
var KarosError = class _KarosError extends Error {
|
|
65
|
+
constructor(code, message, status, errors) {
|
|
66
|
+
super(message);
|
|
67
|
+
this.isKarosError = true;
|
|
68
|
+
this.code = code;
|
|
69
|
+
this.status = status;
|
|
70
|
+
this.errors = errors;
|
|
71
|
+
if (Error.captureStackTrace) {
|
|
72
|
+
Error.captureStackTrace(this, _KarosError);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var notFoundError = (message = "Not found", errors) => {
|
|
77
|
+
throw new KarosError("NOT_FOUND", message, 404, errors);
|
|
78
|
+
};
|
|
79
|
+
var validationError = (message = "Validation failed", errors) => {
|
|
80
|
+
throw new KarosError("VALIDATION_FAILED", message, 400, errors);
|
|
81
|
+
};
|
|
82
|
+
var unauthorizedError = (message = "Unauthorized", errors) => {
|
|
83
|
+
throw new KarosError("UNAUTHORIZED", message, 401, errors);
|
|
84
|
+
};
|
|
85
|
+
var httpError = (code, message, status, errors) => {
|
|
86
|
+
throw new KarosError(code, message, status, errors);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// src/middleware.ts
|
|
90
|
+
function isKarosError(err) {
|
|
91
|
+
return typeof err === "object" && err !== null && err.isKarosError === true;
|
|
92
|
+
}
|
|
93
|
+
function errorHandler(err, req, res, next) {
|
|
94
|
+
if (res.headersSent) return next(err);
|
|
95
|
+
if (isKarosError(err)) {
|
|
96
|
+
fail(res, err.code, err.message, err.status, err.errors);
|
|
97
|
+
} else {
|
|
98
|
+
fail(res, ErrorCode.INTERNAL_ERROR, "Internal server error", 500);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
102
|
+
0 && (module.exports = {
|
|
103
|
+
ErrorCode,
|
|
104
|
+
KarosError,
|
|
105
|
+
errorHandler,
|
|
106
|
+
fail,
|
|
107
|
+
httpError,
|
|
108
|
+
notFoundError,
|
|
109
|
+
ok,
|
|
110
|
+
unauthorizedError,
|
|
111
|
+
validationError
|
|
112
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "karos",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Opinionated API response and error handling for Node.js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": ["dist"],
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsup src/index.ts --format cjs --dts",
|
|
10
|
+
"dev": "tsup src/index.ts --format cjs --watch --dts",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["api", "express", "error-handling", "node", "backend"],
|
|
14
|
+
"author": "Krishna Shrivastava",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": "https://github.com/Krishna-Shrivastava-1/Karos",
|
|
17
|
+
"bugs": "https://github.com/Krishna-Shrivastava-1/Karos/issues",
|
|
18
|
+
"type": "commonjs",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/express": "^5.0.6",
|
|
21
|
+
"@types/node": "^25.0.3",
|
|
22
|
+
"ts-node": "^10.9.2",
|
|
23
|
+
"tsup": "^8.5.1",
|
|
24
|
+
"typescript": "^5.9.3"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"express": "^4.21.1"
|
|
28
|
+
}
|
|
29
|
+
}
|