@mabdolalizadeh/responser 0.0.0-stage → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mohammad Abdolalizadeh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,135 @@
1
- # Temporary Holding Version
1
+ # @mabdolalizadeh/responser
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Standardized and lightweight HTTP response helper for Express.js APIs.
4
+
5
+ Supports both **ES Modules (`import`)** and **CommonJS (`require`)**, with full TypeScript declarations included.
6
+
7
+ ---
8
+
9
+ ## 📦 Installation
10
+
11
+ ### Option 1: Install directly from GitHub (No registry or publishing needed!)
12
+
13
+ You can install directly into any project immediately:
14
+
15
+ ```bash
16
+ npm install github:mabdolalizadeh/responser
17
+ ```
18
+
19
+ Or using shorthand:
20
+ ```bash
21
+ npm install mabdolalizadeh/responser
22
+ ```
23
+
24
+ To lock to a specific branch, commit, or tag:
25
+ ```bash
26
+ npm install mabdolalizadeh/responser#v1.0.0
27
+ ```
28
+
29
+ ### Option 2: Install from NPM or GitHub Packages (If published)
30
+
31
+ ```bash
32
+ npm install @mabdolalizadeh/responser
33
+ ```
34
+
35
+ ---
36
+
37
+ ## 🚀 Quick Start
38
+
39
+ ### ES Modules (ESM)
40
+ ```javascript
41
+ import express from 'express';
42
+ import { Responser } from '@mabdolalizadeh/responser';
43
+
44
+ const app = express();
45
+ app.use(express.json());
46
+
47
+ app.get('/users/:id', (req, res) => {
48
+ const responser = new Responser(res);
49
+
50
+ if (req.params.id !== '1') {
51
+ return responser.send404({ entity: 'User' });
52
+ }
53
+
54
+ return responser.send200({ body: { id: 1, name: 'Alice' } });
55
+ });
56
+ ```
57
+
58
+ ### CommonJS (CJS)
59
+ ```javascript
60
+ const express = require('express');
61
+ const { Responser } = require('@mabdolalizadeh/responser');
62
+ // or: const Responser = require('@mabdolalizadeh/responser');
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 💡 Usage Patterns
68
+
69
+ ### 1. Instance-based (Recommended)
70
+
71
+ ```javascript
72
+ app.post('/register', (req, res) => {
73
+ const responser = new Responser(res);
74
+
75
+ // Validate required fields
76
+ const [ok, missing] = Responser.checkMissing(['email', 'password'], req.body);
77
+ if (!ok) {
78
+ return responser.send400({ missings: missing });
79
+ }
80
+
81
+ return responser.send201({ body: { message: 'Registered successfully' } });
82
+ });
83
+ ```
84
+
85
+ ### 2. Static Methods (No `new` needed)
86
+
87
+ ```javascript
88
+ app.get('/health', (req, res) => {
89
+ return Responser.send200(res, { body: { status: 'healthy' } });
90
+ });
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 📖 API Reference
96
+
97
+ ### Response Format
98
+
99
+ - **Success (200, 201):** `{ "ok": true, "body": ... }`
100
+ - **No Content (204):** Empty body
101
+ - **Error (400, 401, 403, 404, 500):** `{ "ok": false, "error": "..." }`
102
+
103
+ ### Methods
104
+
105
+ | Method | Status | Default Error / Response Format | Options |
106
+ |---|---|---|---|
107
+ | `send200({ body })` | `200` | `{ ok: true, body }` | `{ body?: any }` |
108
+ | `send201({ body })` | `201` | `{ ok: true, body }` | `{ body?: any }` |
109
+ | `send204()` | `204` | Empty body | None |
110
+ | `send400({ msg, missings })` | `400` | Auto-formats missing fields or `"Bad request"` | `{ msg?: string, missings?: string[] }` |
111
+ | `send401({ msg })` | `401` | `"You should be authenticated"` | `{ msg?: string }` |
112
+ | `send403({ msg })` | `403` | `"You don't have permission to see this resource"` | `{ msg?: string }` |
113
+ | `send404({ msg, entity })` | `404` | `"<Entity> not found"` or `"Not found"` | `{ msg?: string, entity?: string }` |
114
+ | `send500({ msg, err })` | `500` | `"Internal server error"` (logs `err` to console) | `{ msg?: string, err?: Error }` |
115
+
116
+ ### Field Validation Helper: `Responser.checkMissing`
117
+
118
+ ```javascript
119
+ const [isValid, missingFields] = Responser.checkMissing(
120
+ ['name', 'email', 'password'],
121
+ req.body
122
+ );
123
+
124
+ if (!isValid) {
125
+ // missingFields is ['password'] if password wasn't provided
126
+ return responser.send400({ missings: missingFields });
127
+ // Output: { ok: false, error: "1 field is missing. password" }
128
+ }
129
+ ```
130
+
131
+ ---
132
+
133
+ ## 📄 License
134
+
135
+ MIT © [Mohammad Abdolalizadeh](https://github.com/mabdolalizadeh)
package/index.cjs ADDED
@@ -0,0 +1,162 @@
1
+ class Responser {
2
+ constructor(res) {
3
+ this.res = res;
4
+ }
5
+
6
+ /**
7
+ * Check whether required fields are missing.
8
+ *
9
+ * @param {Array} base - Required fields.
10
+ * @param {Array|Object} taken - Provided fields or object.
11
+ * @returns {[boolean, Array|null]}
12
+ */
13
+ static checkMissing(base = [], taken = []) {
14
+ const takenKeys = new Set(
15
+ Array.isArray(taken)
16
+ ? taken
17
+ : Object.keys(taken ?? {})
18
+ );
19
+
20
+ const missing = base.filter(key => !takenKeys.has(key));
21
+
22
+ return missing.length === 0
23
+ ? [true, null]
24
+ : [false, missing];
25
+ }
26
+
27
+ /**
28
+ * Send a JSON response.
29
+ *
30
+ * @private
31
+ */
32
+ _sendJson(status, payload) {
33
+ return this.res.status(status).json(payload);
34
+ }
35
+
36
+ /**
37
+ * Send a successful response.
38
+ */
39
+ send200({ body } = {}) {
40
+ return this._sendJson(200, {
41
+ ok: true,
42
+ body
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Send a resource-created response.
48
+ */
49
+ send201({ body } = {}) {
50
+ return this._sendJson(201, {
51
+ ok: true,
52
+ body
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Send an empty successful response.
58
+ */
59
+ send204() {
60
+ return this.res.status(204).send();
61
+ }
62
+
63
+ /**
64
+ * Send a bad-request response.
65
+ */
66
+ send400({ msg, missings, missing } = {}) {
67
+ const missingFields = missings ?? missing;
68
+
69
+ const error = msg ?? (
70
+ Array.isArray(missingFields) && missingFields.length > 0
71
+ ? `${missingFields.length} field${missingFields.length > 1 ? 's are' : ' is'} missing. ${missingFields.join(', ')}`
72
+ : 'Bad request'
73
+ );
74
+
75
+ return this._sendJson(400, {
76
+ ok: false,
77
+ error
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Send an unauthorized response.
83
+ */
84
+ send401({ msg } = {}) {
85
+ return this._sendJson(401, {
86
+ ok: false,
87
+ error: msg ?? 'You should be authenticated'
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Send a forbidden response.
93
+ */
94
+ send403({ msg } = {}) {
95
+ return this._sendJson(403, {
96
+ ok: false,
97
+ error: msg ?? "You don't have permission to see this resource"
98
+ });
99
+ }
100
+
101
+ /**
102
+ * Send a not-found response.
103
+ */
104
+ send404({ msg, entity } = {}) {
105
+ return this._sendJson(404, {
106
+ ok: false,
107
+ error: msg ?? `${entity ? `${entity} not` : 'Not'} found`
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Send an internal-server-error response.
113
+ */
114
+ send500({ msg, err } = {}) {
115
+ if (err) {
116
+ console.error(err);
117
+ }
118
+
119
+ return this._sendJson(500, {
120
+ ok: false,
121
+ error: msg ?? 'Internal server error'
122
+ });
123
+ }
124
+
125
+ // Static shortcuts (backward compatible).
126
+
127
+ static send200(res, options) {
128
+ return new Responser(res).send200(options);
129
+ }
130
+
131
+ static send201(res, options) {
132
+ return new Responser(res).send201(options);
133
+ }
134
+
135
+ static send204(res) {
136
+ return new Responser(res).send204();
137
+ }
138
+
139
+ static send400(res, options) {
140
+ return new Responser(res).send400(options);
141
+ }
142
+
143
+ static send401(res, options) {
144
+ return new Responser(res).send401(options);
145
+ }
146
+
147
+ static send403(res, options) {
148
+ return new Responser(res).send403(options);
149
+ }
150
+
151
+ static send404(res, options) {
152
+ return new Responser(res).send404(options);
153
+ }
154
+
155
+ static send500(res, options) {
156
+ return new Responser(res).send500(options);
157
+ }
158
+ }
159
+
160
+ module.exports = Responser;
161
+ module.exports.Responser = Responser;
162
+ module.exports.default = Responser;
package/index.d.ts ADDED
@@ -0,0 +1,89 @@
1
+ export interface SendOptions {
2
+ body?: any;
3
+ }
4
+
5
+ export interface Send400Options {
6
+ msg?: string;
7
+ missings?: string[];
8
+ missing?: string[];
9
+ }
10
+
11
+ export interface SendErrorOptions {
12
+ msg?: string;
13
+ }
14
+
15
+ export interface Send404Options {
16
+ msg?: string;
17
+ entity?: string;
18
+ }
19
+
20
+ export interface Send500Options {
21
+ msg?: string;
22
+ err?: any;
23
+ }
24
+
25
+ export declare class Responser {
26
+ res: any;
27
+ constructor(res: any);
28
+
29
+ /**
30
+ * Check whether required fields are missing.
31
+ *
32
+ * @param base Required fields.
33
+ * @param taken Provided fields array or request body object.
34
+ * @returns [isValid, missingFieldsOrNull]
35
+ */
36
+ static checkMissing(base?: string[], taken?: string[] | Record<string, any> | null): [boolean, string[] | null];
37
+
38
+ /**
39
+ * Send a 200 OK response with `{ ok: true, body }`.
40
+ */
41
+ send200(options?: SendOptions): any;
42
+
43
+ /**
44
+ * Send a 201 Created response with `{ ok: true, body }`.
45
+ */
46
+ send201(options?: SendOptions): any;
47
+
48
+ /**
49
+ * Send a 204 No Content response.
50
+ */
51
+ send204(): any;
52
+
53
+ /**
54
+ * Send a 400 Bad Request response with `{ ok: false, error }`.
55
+ */
56
+ send400(options?: Send400Options): any;
57
+
58
+ /**
59
+ * Send a 401 Unauthorized response with `{ ok: false, error }`.
60
+ */
61
+ send401(options?: SendErrorOptions): any;
62
+
63
+ /**
64
+ * Send a 403 Forbidden response with `{ ok: false, error }`.
65
+ */
66
+ send403(options?: SendErrorOptions): any;
67
+
68
+ /**
69
+ * Send a 404 Not Found response with `{ ok: false, error }`.
70
+ */
71
+ send404(options?: Send404Options): any;
72
+
73
+ /**
74
+ * Send a 500 Internal Server Error response with `{ ok: false, error }`.
75
+ */
76
+ send500(options?: Send500Options): any;
77
+
78
+ // Static helper methods
79
+ static send200(res: any, options?: SendOptions): any;
80
+ static send201(res: any, options?: SendOptions): any;
81
+ static send204(res: any): any;
82
+ static send400(res: any, options?: Send400Options): any;
83
+ static send401(res: any, options?: SendErrorOptions): any;
84
+ static send403(res: any, options?: SendErrorOptions): any;
85
+ static send404(res: any, options?: Send404Options): any;
86
+ static send500(res: any, options?: Send500Options): any;
87
+ }
88
+
89
+ export default Responser;
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@mabdolalizadeh/responser",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Standardized JSON response helper for Express.js",
5
+ "type": "module",
6
+ "main": "./index.cjs",
7
+ "module": "./responser.js",
8
+ "types": "./index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "import": "./responser.js",
13
+ "require": "./index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "responser.js",
18
+ "index.cjs",
19
+ "index.d.ts"
20
+ ],
21
+ "scripts": {
22
+ "test": "node --test"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/mabdolalizadeh/responser.git"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/mabdolalizadeh/responser/issues"
30
+ },
31
+ "homepage": "https://github.com/mabdolalizadeh/responser#readme",
32
+ "keywords": [
33
+ "express",
34
+ "response",
35
+ "json",
36
+ "http",
37
+ "api",
38
+ "rest",
39
+ "helper",
40
+ "responser"
41
+ ],
42
+ "author": "mabdolalizadeh <m.abdolalizadeh@outlook.com>",
43
+ "license": "MIT",
44
+ "publishConfig": {
45
+ "access": "public"
46
+ }
47
+ }
package/responser.js ADDED
@@ -0,0 +1,162 @@
1
+
2
+ export default class Responser {
3
+ constructor(res) {
4
+ this.res = res;
5
+ }
6
+
7
+ /**
8
+ * Check whether required fields are missing.
9
+ *
10
+ * @param {Array} base - Required fields.
11
+ * @param {Array|Object} taken - Provided fields or object.
12
+ * @returns {[boolean, Array|null]}
13
+ */
14
+ static checkMissing(base = [], taken = []) {
15
+ const takenKeys = new Set(
16
+ Array.isArray(taken)
17
+ ? taken
18
+ : Object.keys(taken ?? {})
19
+ );
20
+
21
+ const missing = base.filter(key => !takenKeys.has(key));
22
+
23
+ return missing.length === 0
24
+ ? [true, null]
25
+ : [false, missing];
26
+ }
27
+
28
+ /**
29
+ * Send a JSON response.
30
+ *
31
+ * @private
32
+ */
33
+ _sendJson(status, payload) {
34
+ return this.res.status(status).json(payload);
35
+ }
36
+
37
+ /**
38
+ * Send a successful response.
39
+ */
40
+ send200({ body } = {}) {
41
+ return this._sendJson(200, {
42
+ ok: true,
43
+ body
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Send a resource-created response.
49
+ */
50
+ send201({ body } = {}) {
51
+ return this._sendJson(201, {
52
+ ok: true,
53
+ body
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Send an empty successful response.
59
+ */
60
+ send204() {
61
+ return this.res.status(204).send();
62
+ }
63
+
64
+ /**
65
+ * Send a bad-request response.
66
+ */
67
+ send400({ msg, missings, missing } = {}) {
68
+ const missingFields = missings ?? missing;
69
+
70
+ const error = msg ?? (
71
+ Array.isArray(missingFields) && missingFields.length > 0
72
+ ? `${missingFields.length} field${missingFields.length > 1 ? 's are' : ' is'} missing. ${missingFields.join(', ')}`
73
+ : 'Bad request'
74
+ );
75
+
76
+ return this._sendJson(400, {
77
+ ok: false,
78
+ error
79
+ });
80
+ }
81
+
82
+ /**
83
+ * Send an unauthorized response.
84
+ */
85
+ send401({ msg } = {}) {
86
+ return this._sendJson(401, {
87
+ ok: false,
88
+ error: msg ?? 'You should be authenticated'
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Send a forbidden response.
94
+ */
95
+ send403({ msg } = {}) {
96
+ return this._sendJson(403, {
97
+ ok: false,
98
+ error: msg ?? "You don't have permission to see this resource"
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Send a not-found response.
104
+ */
105
+ send404({ msg, entity } = {}) {
106
+ return this._sendJson(404, {
107
+ ok: false,
108
+ error: msg ?? `${entity ? `${entity} not` : 'Not'} found`
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Send an internal-server-error response.
114
+ */
115
+ send500({ msg, err } = {}) {
116
+ if (err) {
117
+ console.error(err);
118
+ }
119
+
120
+ return this._sendJson(500, {
121
+ ok: false,
122
+ error: msg ?? 'Internal server error'
123
+ });
124
+ }
125
+
126
+ // Static shortcuts (backward compatible).
127
+
128
+ static send200(res, options) {
129
+ return new Responser(res).send200(options);
130
+ }
131
+
132
+ static send201(res, options) {
133
+ return new Responser(res).send201(options);
134
+ }
135
+
136
+ static send204(res) {
137
+ return new Responser(res).send204();
138
+ }
139
+
140
+ static send400(res, options) {
141
+ return new Responser(res).send400(options);
142
+ }
143
+
144
+ static send401(res, options) {
145
+ return new Responser(res).send401(options);
146
+ }
147
+
148
+ static send403(res, options) {
149
+ return new Responser(res).send403(options);
150
+ }
151
+
152
+ static send404(res, options) {
153
+ return new Responser(res).send404(options);
154
+ }
155
+
156
+ static send500(res, options) {
157
+ return new Responser(res).send500(options);
158
+ }
159
+ }
160
+
161
+ export { Responser };
162
+