perfect-payload 1.8.0-beta.0 → 2.0.0-beta.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/package.json CHANGED
@@ -1,22 +1,31 @@
1
1
  {
2
2
  "name": "perfect-payload",
3
- "version": "1.8.0-beta.0",
3
+ "version": "2.0.0-beta.0",
4
4
  "type": "module",
5
5
  "description": "Lightweight JSON payload validation library with structured errors, nested validation, field paths, and customizable validation rules.",
6
- "main": "index.js",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
7
8
  "exports": {
8
- ".": "./index.js",
9
- "./express": "./express.js",
10
- "./fastify": "./fastify.js"
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./express": {
14
+ "types": "./dist/express.d.ts",
15
+ "import": "./dist/express.js"
16
+ },
17
+ "./fastify": {
18
+ "types": "./dist/fastify.d.ts",
19
+ "import": "./dist/fastify.js"
20
+ }
11
21
  },
12
22
  "scripts": {
13
- "test": "jest"
23
+ "test": "jest",
24
+ "build": "tsc",
25
+ "test:types": "tsc type-test.ts --noEmit --strict --module NodeNext --moduleResolution NodeNext --target ES2022 --ignoreConfig --skipLibCheck"
14
26
  },
15
27
  "files": [
16
- "index.js",
17
- "framework.js",
18
- "express.js",
19
- "fastify.js"
28
+ "dist"
20
29
  ],
21
30
  "jest": {
22
31
  "testPathIgnorePatterns": [
@@ -56,7 +65,12 @@
56
65
  "devDependencies": {
57
66
  "@babel/core": "^7.25.2",
58
67
  "@babel/preset-env": "^7.25.4",
68
+ "@types/express": "^5.0.6",
69
+ "@types/node": "^26.6.2",
59
70
  "babel-jest": "^29.7.0",
60
- "jest": "^29.7.0"
71
+ "express": "^5.2.1",
72
+ "fastify": "^5.12.5",
73
+ "jest": "^29.7.0",
74
+ "typescript": "^7.0.2"
61
75
  }
62
76
  }
package/express.js DELETED
@@ -1,75 +0,0 @@
1
- import {
2
- validateFrameworkConfig,
3
- validateFrameworkSources,
4
- validateFrameworkSourcesAsync,
5
- } from "./framework.js";
6
-
7
- export function validatePayload(config = {}) {
8
- const { rule, options = {} } = config;
9
-
10
- // Validate developer configuration when middleware is created,
11
- // not on every incoming request.
12
- validateFrameworkConfig({
13
- rule,
14
- options,
15
- });
16
-
17
- return function perfectPayloadExpressMiddleware(req, res, next) {
18
- try {
19
- const result = validateFrameworkSources({
20
- data: {
21
- headers: req.headers,
22
- params: req.params,
23
- query: req.query,
24
- body: req.body,
25
- },
26
- rule,
27
- options,
28
- });
29
-
30
- if (result.valid === true) {
31
- req.validatedPayload = result.validatedPayload;
32
- return next();
33
- }
34
-
35
- return res.status(result.statusCode ?? 400).json(result);
36
- } catch (error) {
37
- return next(error);
38
- }
39
- };
40
- }
41
-
42
- export function validatePayloadAsync(config = {}) {
43
- const { rule, options = {} } = config;
44
-
45
- // Validate developer configuration when middleware is created,
46
- // not on every incoming request.
47
- validateFrameworkConfig({
48
- rule,
49
- options,
50
- });
51
-
52
- return async function perfectPayloadExpressAsyncMiddleware(req, res, next) {
53
- try {
54
- const result = await validateFrameworkSourcesAsync({
55
- data: {
56
- headers: req.headers,
57
- params: req.params,
58
- query: req.query,
59
- body: req.body,
60
- },
61
- rule,
62
- options,
63
- });
64
-
65
- if (result.valid === true) {
66
- req.validatedPayload = result.validatedPayload;
67
- return next();
68
- }
69
-
70
- return res.status(result.statusCode ?? 400).json(result);
71
- } catch (error) {
72
- return next(error);
73
- }
74
- };
75
- }
package/fastify.js DELETED
@@ -1,67 +0,0 @@
1
- import {
2
- validateFrameworkConfig,
3
- validateFrameworkSources,
4
- validateFrameworkSourcesAsync,
5
- } from "./framework.js";
6
-
7
- export function validatePayload(config = {}) {
8
- const { rule, options = {} } = config;
9
-
10
- // Validate developer configuration when the hook is created,
11
- // not on every incoming request.
12
- validateFrameworkConfig({
13
- rule,
14
- options,
15
- });
16
-
17
- return async function perfectPayloadFastifyHook(request, reply) {
18
- const result = validateFrameworkSources({
19
- data: {
20
- headers: request.headers,
21
- params: request.params,
22
- query: request.query,
23
- body: request.body,
24
- },
25
- rule,
26
- options,
27
- });
28
-
29
- if (result.valid === true) {
30
- request.validatedPayload = result.validatedPayload;
31
- return;
32
- }
33
-
34
- return reply.code(result.statusCode ?? 400).send(result);
35
- };
36
- }
37
-
38
- export function validatePayloadAsync(config = {}) {
39
- const { rule, options = {} } = config;
40
-
41
- // Validate developer configuration when the hook is created,
42
- // not on every incoming request.
43
- validateFrameworkConfig({
44
- rule,
45
- options,
46
- });
47
-
48
- return async function perfectPayloadFastifyAsyncHook(request, reply) {
49
- const result = await validateFrameworkSourcesAsync({
50
- data: {
51
- headers: request.headers,
52
- params: request.params,
53
- query: request.query,
54
- body: request.body,
55
- },
56
- rule,
57
- options,
58
- });
59
-
60
- if (result.valid === true) {
61
- request.validatedPayload = result.validatedPayload;
62
- return;
63
- }
64
-
65
- return reply.code(result.statusCode ?? 400).send(result);
66
- };
67
- }
package/framework.js DELETED
@@ -1,175 +0,0 @@
1
- import { perfectPayload, perfectPayloadAsync } from "./index.js";
2
-
3
- const SUPPORTED_REQUEST_SOURCES = ["headers", "params", "query", "body"];
4
-
5
- export function validateFrameworkConfig({ rule, options } = {}) {
6
- if (!rule || typeof rule !== "object" || Array.isArray(rule)) {
7
- throw new Error("perfect-payload:- framework rule must be an object");
8
- }
9
-
10
- const configuredSources = Object.keys(rule);
11
-
12
- if (configuredSources.length === 0) {
13
- throw new Error(
14
- "perfect-payload:- framework rule must contain at least one request source",
15
- );
16
- }
17
-
18
- for (const source of configuredSources) {
19
- if (!SUPPORTED_REQUEST_SOURCES.includes(source)) {
20
- throw new Error(`perfect-payload:- unsupported request source ${source}`);
21
- }
22
-
23
- const sourceRule = rule[source];
24
-
25
- if (
26
- !sourceRule ||
27
- typeof sourceRule !== "object" ||
28
- Array.isArray(sourceRule)
29
- ) {
30
- throw new Error(
31
- `perfect-payload:- rule for request source ${source} must be an object`,
32
- );
33
- }
34
- }
35
-
36
- if (
37
- options !== undefined &&
38
- (options === null || typeof options !== "object" || Array.isArray(options))
39
- ) {
40
- throw new Error("perfect-payload:- framework options must be an object");
41
- }
42
-
43
- return SUPPORTED_REQUEST_SOURCES.filter((source) =>
44
- Object.prototype.hasOwnProperty.call(rule, source),
45
- );
46
- }
47
-
48
- export function validateFrameworkSources({
49
- data = {},
50
- rule,
51
- options = {},
52
- } = {}) {
53
- const configuredSources = validateFrameworkConfig({
54
- rule,
55
- options,
56
- });
57
-
58
- const validatedPayload = {};
59
- const errors = [];
60
-
61
- let invalidResponse = null;
62
-
63
- for (const source of configuredSources) {
64
- const sourceData =
65
- data?.[source] !== undefined && data?.[source] !== null
66
- ? data[source]
67
- : {};
68
-
69
- const result = perfectPayload(sourceData, rule[source], options);
70
-
71
- if (result.valid === true) {
72
- validatedPayload[source] = result.validatedPayload;
73
- continue;
74
- }
75
-
76
- if (!invalidResponse) {
77
- const { errors: _errors, ...response } = result;
78
- invalidResponse = response;
79
- }
80
-
81
- for (const error of result.errors ?? []) {
82
- if (typeof error === "string") {
83
- errors.push(error);
84
- } else {
85
- errors.push({
86
- ...error,
87
- path: `${source}.${error.path}`,
88
- });
89
- }
90
- }
91
- }
92
-
93
- if (errors.length > 0) {
94
- return {
95
- ...invalidResponse,
96
- errors,
97
- };
98
- }
99
-
100
- return {
101
- valid: true,
102
- validatedPayload,
103
- };
104
- }
105
-
106
- export async function validateFrameworkSourcesAsync({
107
- data = {},
108
- rule,
109
- options = {},
110
- } = {}) {
111
- const configuredSources = validateFrameworkConfig({
112
- rule,
113
- options,
114
- });
115
-
116
- const results = await Promise.all(
117
- configuredSources.map(async (source) => {
118
- const sourceData =
119
- data?.[source] !== undefined && data?.[source] !== null
120
- ? data[source]
121
- : {};
122
-
123
- const result = await perfectPayloadAsync(
124
- sourceData,
125
- rule[source],
126
- options,
127
- );
128
-
129
- return {
130
- source,
131
- result,
132
- };
133
- }),
134
- );
135
-
136
- const validatedPayload = {};
137
- const errors = [];
138
-
139
- let invalidResponse = null;
140
-
141
- for (const { source, result } of results) {
142
- if (result.valid === true) {
143
- validatedPayload[source] = result.validatedPayload;
144
- continue;
145
- }
146
-
147
- if (!invalidResponse) {
148
- const { errors: _errors, ...response } = result;
149
- invalidResponse = response;
150
- }
151
-
152
- for (const error of result.errors ?? []) {
153
- if (typeof error === "string") {
154
- errors.push(error);
155
- } else {
156
- errors.push({
157
- ...error,
158
- path: `${source}.${error.path}`,
159
- });
160
- }
161
- }
162
- }
163
-
164
- if (errors.length > 0) {
165
- return {
166
- ...invalidResponse,
167
- errors,
168
- };
169
- }
170
-
171
- return {
172
- valid: true,
173
- validatedPayload,
174
- };
175
- }