@duplojs/http 0.6.1 → 0.7.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.
Files changed (187) hide show
  1. package/dist/client/getBody.cjs +1 -1
  2. package/dist/client/getBody.mjs +1 -1
  3. package/dist/client/hooks.d.ts +1 -25
  4. package/dist/client/httpClient.d.ts +29 -25
  5. package/dist/client/promiseRequest.cjs +3 -0
  6. package/dist/client/promiseRequest.d.ts +31 -39
  7. package/dist/client/promiseRequest.mjs +4 -1
  8. package/dist/client/types/clientResponse.d.ts +6 -6
  9. package/dist/client/types/hooks.d.ts +25 -0
  10. package/dist/client/types/index.cjs +2 -0
  11. package/dist/client/types/index.d.ts +2 -0
  12. package/dist/client/types/index.mjs +2 -0
  13. package/dist/client/types/promiseRequestParams.cjs +2 -0
  14. package/dist/client/types/promiseRequestParams.d.ts +9 -0
  15. package/dist/client/types/promiseRequestParams.mjs +1 -0
  16. package/dist/client/types/serverRoute.d.ts +19 -1
  17. package/dist/client/unexpectedResponseError.d.ts +1 -2
  18. package/dist/core/builders/preflight/route.cjs +1 -0
  19. package/dist/core/builders/preflight/route.d.ts +4 -2
  20. package/dist/core/builders/preflight/route.mjs +1 -0
  21. package/dist/core/builders/route/builder.cjs +1 -0
  22. package/dist/core/builders/route/builder.d.ts +4 -2
  23. package/dist/core/builders/route/builder.mjs +1 -0
  24. package/dist/core/clean/constraint.cjs +24 -0
  25. package/dist/core/clean/constraint.d.ts +8 -0
  26. package/dist/core/clean/constraint.mjs +22 -0
  27. package/dist/core/clean/constraintsSet.cjs +27 -0
  28. package/dist/core/clean/constraintsSet.d.ts +8 -0
  29. package/dist/core/clean/constraintsSet.mjs +25 -0
  30. package/dist/core/clean/entity.cjs +33 -0
  31. package/dist/core/clean/entity.d.ts +20 -0
  32. package/dist/core/clean/entity.mjs +31 -0
  33. package/dist/core/clean/index.cjs +8 -0
  34. package/dist/core/clean/index.d.ts +5 -0
  35. package/dist/core/clean/index.mjs +5 -0
  36. package/dist/core/clean/newType.cjs +15 -0
  37. package/dist/core/clean/newType.d.ts +8 -0
  38. package/dist/core/clean/newType.mjs +13 -0
  39. package/dist/core/clean/primitive.cjs +12 -0
  40. package/dist/core/clean/primitive.d.ts +8 -0
  41. package/dist/core/clean/primitive.mjs +10 -0
  42. package/dist/core/defaultHooks/index.cjs +50 -0
  43. package/dist/core/defaultHooks/index.d.ts +5 -0
  44. package/dist/core/defaultHooks/index.mjs +48 -0
  45. package/dist/{interfaces/node/error → core/errors}/bodyParseWrongChunkReceived.cjs +6 -4
  46. package/dist/core/errors/bodyParseWrongChunkReceived.d.ts +9 -0
  47. package/dist/core/errors/bodyParseWrongChunkReceived.mjs +14 -0
  48. package/dist/{interfaces/node/error → core/errors}/bodySizeExceedsLimitError.cjs +2 -2
  49. package/dist/{interfaces/node/error → core/errors}/bodySizeExceedsLimitError.d.ts +2 -2
  50. package/dist/{interfaces/node/error → core/errors}/bodySizeExceedsLimitError.mjs +2 -2
  51. package/dist/{interfaces/node/error → core/errors}/index.cjs +6 -4
  52. package/dist/{interfaces/node/error → core/errors}/index.d.ts +3 -2
  53. package/dist/{interfaces/node/error → core/errors}/index.mjs +3 -2
  54. package/dist/core/errors/parseJsonError.cjs +16 -0
  55. package/dist/core/errors/parseJsonError.d.ts +9 -0
  56. package/dist/core/errors/parseJsonError.mjs +14 -0
  57. package/dist/core/errors/wrongContentTypeError.cjs +16 -0
  58. package/dist/core/errors/wrongContentTypeError.d.ts +9 -0
  59. package/dist/core/errors/wrongContentTypeError.mjs +14 -0
  60. package/dist/core/functionsBuilders/route/create.d.ts +2 -2
  61. package/dist/core/functionsBuilders/route/default.cjs +0 -11
  62. package/dist/core/functionsBuilders/route/default.mjs +0 -11
  63. package/dist/core/functionsBuilders/route/hook.d.ts +2 -2
  64. package/dist/core/functionsBuilders/steps/create.d.ts +2 -2
  65. package/dist/core/functionsBuilders/steps/defaults/cutStep.cjs +15 -18
  66. package/dist/core/functionsBuilders/steps/defaults/cutStep.mjs +15 -18
  67. package/dist/core/functionsBuilders/steps/defaults/extractStep.cjs +43 -19
  68. package/dist/core/functionsBuilders/steps/defaults/extractStep.d.ts +2 -1
  69. package/dist/core/functionsBuilders/steps/defaults/extractStep.mjs +44 -20
  70. package/dist/core/functionsBuilders/steps/defaults/handlerStep.cjs +14 -8
  71. package/dist/core/functionsBuilders/steps/defaults/handlerStep.mjs +14 -8
  72. package/dist/core/hub/defaultBodyController.cjs +8 -0
  73. package/dist/core/hub/defaultBodyController.d.ts +1 -0
  74. package/dist/core/hub/defaultBodyController.mjs +6 -0
  75. package/dist/core/hub/hooks.cjs +3 -1
  76. package/dist/core/hub/hooks.d.ts +2 -3
  77. package/dist/core/hub/hooks.mjs +3 -1
  78. package/dist/core/hub/index.cjs +101 -127
  79. package/dist/core/hub/index.d.ts +33 -34
  80. package/dist/core/hub/index.mjs +100 -128
  81. package/dist/core/implementHttpServer.cjs +5 -5
  82. package/dist/core/implementHttpServer.d.ts +2 -1
  83. package/dist/core/implementHttpServer.mjs +5 -5
  84. package/dist/core/index.cjs +35 -10
  85. package/dist/core/index.d.ts +3 -0
  86. package/dist/core/index.mjs +14 -2
  87. package/dist/core/request/bodyController/base.cjs +36 -0
  88. package/dist/core/request/bodyController/base.d.ts +28 -0
  89. package/dist/core/request/bodyController/base.mjs +34 -0
  90. package/dist/core/request/bodyController/formData.cjs +28 -0
  91. package/dist/core/request/bodyController/formData.d.ts +22 -0
  92. package/dist/core/request/bodyController/formData.mjs +25 -0
  93. package/dist/core/request/bodyController/index.cjs +13 -0
  94. package/dist/core/request/bodyController/index.d.ts +3 -0
  95. package/dist/core/request/bodyController/index.mjs +3 -0
  96. package/dist/core/request/bodyController/text.cjs +14 -0
  97. package/dist/core/request/bodyController/text.d.ts +10 -0
  98. package/dist/core/request/bodyController/text.mjs +11 -0
  99. package/dist/core/{request.cjs → request/index.cjs} +21 -3
  100. package/dist/core/{request.d.ts → request/index.d.ts} +10 -3
  101. package/dist/core/request/index.mjs +50 -0
  102. package/dist/core/response/contract.d.ts +1 -1
  103. package/dist/core/route/hooks.d.ts +0 -2
  104. package/dist/core/route/index.d.ts +2 -1
  105. package/dist/core/router/index.cjs +27 -8
  106. package/dist/core/router/index.d.ts +2 -1
  107. package/dist/core/router/index.mjs +28 -10
  108. package/dist/core/router/notFoundBodyReaderImplementationError.cjs +16 -0
  109. package/dist/core/router/notFoundBodyReaderImplementationError.d.ts +11 -0
  110. package/dist/core/router/notFoundBodyReaderImplementationError.mjs +14 -0
  111. package/dist/core/router/types/buildedRouter.d.ts +3 -3
  112. package/dist/core/steps/extract.d.ts +3 -1
  113. package/dist/core/steps/types/steps.d.ts +1 -3
  114. package/dist/core/types/hosts.cjs +2 -0
  115. package/dist/core/types/hosts.d.ts +4 -0
  116. package/dist/core/types/hosts.mjs +1 -0
  117. package/dist/core/types/httpServerParams.cjs +2 -0
  118. package/dist/core/types/httpServerParams.d.ts +11 -0
  119. package/dist/core/types/httpServerParams.mjs +1 -0
  120. package/dist/core/types/index.cjs +2 -0
  121. package/dist/core/types/index.d.ts +2 -0
  122. package/dist/core/types/index.mjs +2 -0
  123. package/dist/interfaces/bun/types/request.cjs +1 -1
  124. package/dist/interfaces/bun/types/request.mjs +1 -1
  125. package/dist/interfaces/node/bodyReaders/formData/error.cjs +14 -0
  126. package/dist/interfaces/node/bodyReaders/formData/error.d.ts +8 -0
  127. package/dist/interfaces/node/bodyReaders/formData/error.mjs +12 -0
  128. package/dist/interfaces/node/bodyReaders/formData/index.cjs +94 -0
  129. package/dist/interfaces/node/bodyReaders/formData/index.d.ts +4 -0
  130. package/dist/interfaces/node/bodyReaders/formData/index.mjs +90 -0
  131. package/dist/interfaces/node/bodyReaders/formData/readRequestFormData.cjs +175 -0
  132. package/dist/interfaces/node/bodyReaders/formData/readRequestFormData.d.ts +21 -0
  133. package/dist/interfaces/node/bodyReaders/formData/readRequestFormData.mjs +173 -0
  134. package/dist/interfaces/node/bodyReaders/index.cjs +9 -0
  135. package/dist/interfaces/node/bodyReaders/index.d.ts +2 -0
  136. package/dist/interfaces/node/bodyReaders/index.mjs +2 -0
  137. package/dist/interfaces/node/bodyReaders/text/index.cjs +41 -0
  138. package/dist/interfaces/node/bodyReaders/text/index.d.ts +3 -0
  139. package/dist/interfaces/node/bodyReaders/text/index.mjs +38 -0
  140. package/dist/interfaces/node/bodyReaders/text/readRequestText.cjs +37 -0
  141. package/dist/interfaces/node/bodyReaders/text/readRequestText.d.ts +6 -0
  142. package/dist/interfaces/node/bodyReaders/text/readRequestText.mjs +35 -0
  143. package/dist/interfaces/node/createHttpServer.cjs +13 -5
  144. package/dist/interfaces/node/createHttpServer.d.ts +13 -12
  145. package/dist/interfaces/node/createHttpServer.mjs +13 -5
  146. package/dist/interfaces/node/hooks/index.cjs +47 -0
  147. package/dist/interfaces/node/hooks/index.d.ts +5 -0
  148. package/dist/interfaces/node/hooks/index.mjs +45 -0
  149. package/dist/interfaces/node/index.cjs +13 -9
  150. package/dist/interfaces/node/index.d.ts +1 -1
  151. package/dist/interfaces/node/index.mjs +7 -5
  152. package/dist/interfaces/node/types/index.cjs +0 -1
  153. package/dist/interfaces/node/types/index.d.ts +0 -1
  154. package/dist/interfaces/node/types/index.mjs +0 -1
  155. package/dist/interfaces/node/types/request.cjs +1 -1
  156. package/dist/interfaces/node/types/request.mjs +1 -1
  157. package/dist/plugins/codeGenerator/index.cjs +2 -0
  158. package/dist/plugins/codeGenerator/index.mjs +1 -1
  159. package/dist/plugins/codeGenerator/plugin.cjs +8 -4
  160. package/dist/plugins/codeGenerator/plugin.mjs +9 -5
  161. package/dist/plugins/codeGenerator/routeToDataParser.cjs +24 -1
  162. package/dist/plugins/codeGenerator/routeToDataParser.d.ts +34 -0
  163. package/dist/plugins/codeGenerator/routeToDataParser.mjs +24 -3
  164. package/dist/plugins/codeGenerator/typescriptTransfomer.cjs +9 -0
  165. package/dist/plugins/codeGenerator/typescriptTransfomer.d.ts +1 -0
  166. package/dist/plugins/codeGenerator/typescriptTransfomer.mjs +7 -0
  167. package/dist/plugins/openApiGenerator/makeOpenApiRoute.d.ts +3 -0
  168. package/dist/plugins/openApiGenerator/plugin.cjs +3 -3
  169. package/dist/plugins/openApiGenerator/plugin.mjs +4 -4
  170. package/dist/plugins/openApiGenerator/routeToOpenApi.cjs +16 -1
  171. package/dist/plugins/openApiGenerator/routeToOpenApi.d.ts +10 -1
  172. package/dist/plugins/openApiGenerator/routeToOpenApi.mjs +16 -1
  173. package/dist/plugins/openApiGenerator/types/entrypoint.d.ts +6 -1
  174. package/dist/plugins/openApiGenerator/types/openApiMethod.d.ts +1 -1
  175. package/package.json +5 -6
  176. package/dist/core/request.mjs +0 -32
  177. package/dist/interfaces/node/error/bodyParseUnknownError.cjs +0 -16
  178. package/dist/interfaces/node/error/bodyParseUnknownError.d.ts +0 -9
  179. package/dist/interfaces/node/error/bodyParseUnknownError.mjs +0 -14
  180. package/dist/interfaces/node/error/bodyParseWrongChunkReceived.d.ts +0 -8
  181. package/dist/interfaces/node/error/bodyParseWrongChunkReceived.mjs +0 -12
  182. package/dist/interfaces/node/hooks.cjs +0 -126
  183. package/dist/interfaces/node/hooks.d.ts +0 -8
  184. package/dist/interfaces/node/hooks.mjs +0 -124
  185. package/dist/interfaces/node/types/host.d.ts +0 -4
  186. /package/dist/{interfaces/node/types/host.cjs → client/types/hooks.cjs} +0 -0
  187. /package/dist/{interfaces/node/types/host.mjs → client/types/hooks.mjs} +0 -0
@@ -0,0 +1,175 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@duplojs/utils');
4
+ var error = require('./error.cjs');
5
+ require('../../../../core/errors/index.cjs');
6
+ var bodySizeExceedsLimitError = require('../../../../core/errors/bodySizeExceedsLimitError.cjs');
7
+ var bodyParseWrongChunkReceived = require('../../../../core/errors/bodyParseWrongChunkReceived.cjs');
8
+
9
+ const endHeaderPart = Buffer.from("\r\n\r\n");
10
+ const bufferStart = Buffer.from("\r\n");
11
+ const regexBoundary = /boundary=(?<boundary>[^; ]+)/i;
12
+ const regexHeaderPart = /name="(?<name>(?:\\"|[^"])+)"(?:; filename="(?<filename>(?:\\"|[^"])+)")?(?:;\s+filename\*=[^']+'[^']*'(?<encodedFilename>[^;\r\n\s]+))?/i;
13
+ function safeDecode(value) {
14
+ try {
15
+ return decodeURIComponent(value);
16
+ }
17
+ catch {
18
+ return value;
19
+ }
20
+ }
21
+ async function readRequestFormData(request, firstValueAccumulator, params, onReceiveHeader) {
22
+ const boundary = utils.S.extract(request.headers["content-type"] ?? "", regexBoundary)?.namedGroups?.boundary;
23
+ if (!boundary) {
24
+ return utils.E.error(new error.BodyParseFormDataError("Wrong boundary."));
25
+ }
26
+ let valueAccumulator = firstValueAccumulator;
27
+ const startPart = Buffer.from(`\r\n--${boundary}`);
28
+ const endMultiPart = Buffer.from(`\r\n--${boundary}--`);
29
+ let currentBuffer = bufferStart;
30
+ let size = 0;
31
+ const keep = endMultiPart.length - 1;
32
+ let currentStream = undefined;
33
+ let fileQuantity = 0;
34
+ let currentFileSize = undefined;
35
+ const checkSize = (receivedChunk) => {
36
+ size += receivedChunk.length;
37
+ return size > params.maxBodySize
38
+ ? new bodySizeExceedsLimitError.BodySizeExceedsLimitError(params.maxBodySize)
39
+ : true;
40
+ };
41
+ const flushReceiveHeader = async (headerPart) => {
42
+ valueAccumulator = await currentStream?.onEndPart(valueAccumulator) ?? valueAccumulator;
43
+ const sizeResult = checkSize(headerPart);
44
+ if (sizeResult !== true) {
45
+ return sizeResult;
46
+ }
47
+ const extract = utils.S.extract(headerPart.toString("utf-8"), regexHeaderPart)?.namedGroups;
48
+ const header = extract?.name
49
+ ? {
50
+ name: extract.name.trim(),
51
+ filename: (extract.encodedFilename !== undefined
52
+ ? safeDecode(extract.encodedFilename)
53
+ : extract.filename)?.trim(),
54
+ }
55
+ : null;
56
+ if (!header) {
57
+ return new error.BodyParseFormDataError("Bad content header part.");
58
+ }
59
+ if (header.name.length > params.maxKeyLength) {
60
+ return new error.BodyParseFormDataError("key length exceeds limit.");
61
+ }
62
+ if (header.filename !== undefined) {
63
+ currentFileSize = 0;
64
+ fileQuantity++;
65
+ if (fileQuantity > params.maxFileQuantity) {
66
+ return new error.BodyParseFormDataError("File quantity exceeds limit.");
67
+ }
68
+ else if (params.mimeType !== undefined
69
+ && !params.mimeType.test(utils.Path.getExtensionName(header.filename) ?? "")) {
70
+ return new error.BodyParseFormDataError("File have wrong mimeType.");
71
+ }
72
+ }
73
+ else {
74
+ currentFileSize = undefined;
75
+ }
76
+ const newStream = await onReceiveHeader(header);
77
+ if (newStream instanceof Error) {
78
+ return newStream;
79
+ }
80
+ currentStream = newStream;
81
+ return true;
82
+ };
83
+ const flushReceiveChunk = async (chunk) => {
84
+ if (chunk.length === 0) {
85
+ return true;
86
+ }
87
+ const sizeResult = checkSize(chunk);
88
+ if (sizeResult !== true) {
89
+ return sizeResult;
90
+ }
91
+ if (!currentStream) {
92
+ return new error.BodyParseFormDataError("Receive chunk before header part.");
93
+ }
94
+ if (typeof currentFileSize === "number") {
95
+ currentFileSize += chunk.length;
96
+ if (params.fileMaxSize !== undefined && currentFileSize > params.fileMaxSize) {
97
+ return new error.BodyParseFormDataError("File size exceeds limit.");
98
+ }
99
+ }
100
+ await currentStream.onReceiveChunk(chunk);
101
+ return true;
102
+ };
103
+ const treatError = async (error) => {
104
+ await currentStream?.onError?.(error, valueAccumulator);
105
+ return utils.E.error(error);
106
+ };
107
+ try {
108
+ for await (const chunk of request) {
109
+ if (!(chunk instanceof Buffer)) {
110
+ return await treatError(new bodyParseWrongChunkReceived.BodyParseWrongChunkReceived("Buffer.", chunk));
111
+ }
112
+ currentBuffer = Buffer.concat([currentBuffer, chunk]);
113
+ if (currentBuffer.length > params.maxBufferSize) {
114
+ return await treatError(new error.BodyParseFormDataError("Buffer size exceeds limit."));
115
+ }
116
+ while (true) {
117
+ const endMultiPartIndex = currentBuffer.indexOf(endMultiPart);
118
+ if (endMultiPartIndex !== -1) {
119
+ // check if buffer contain end of transmissions
120
+ currentBuffer = currentBuffer.subarray(0, endMultiPartIndex);
121
+ }
122
+ const startPartIndex = currentBuffer.indexOf(startPart);
123
+ const endHeaderPartIndex = currentBuffer.indexOf(endHeaderPart);
124
+ if (startPartIndex !== -1 && endHeaderPartIndex !== -1) {
125
+ // check if buffer contain an entire header of part
126
+ const resultChunk = await flushReceiveChunk(currentBuffer.subarray(0, startPartIndex));
127
+ if (resultChunk !== true) {
128
+ return await treatError(resultChunk);
129
+ }
130
+ const endIndex = endHeaderPartIndex + endHeaderPart.length;
131
+ const resultHeader = await flushReceiveHeader(currentBuffer.subarray(startPartIndex, endIndex));
132
+ if (resultHeader !== true) {
133
+ return await treatError(resultHeader);
134
+ }
135
+ currentBuffer = currentBuffer.subarray(endIndex);
136
+ }
137
+ else if (startPartIndex === -1 && endHeaderPartIndex === -1) {
138
+ // check if buffer contain only data
139
+ if (currentBuffer.length > keep) {
140
+ const bufferRestIndex = currentBuffer.length - keep;
141
+ const resultChunk = await flushReceiveChunk(currentBuffer.subarray(0, bufferRestIndex));
142
+ if (resultChunk !== true) {
143
+ return await treatError(resultChunk);
144
+ }
145
+ currentBuffer = currentBuffer.subarray(bufferRestIndex);
146
+ }
147
+ break;
148
+ }
149
+ else if (startPartIndex !== -1 && endHeaderPartIndex === -1) {
150
+ // check if buffer contain start of header but not contain end
151
+ break;
152
+ }
153
+ else {
154
+ // check if buffer contain only end of header part
155
+ return await treatError(new error.BodyParseFormDataError("Wrong content."));
156
+ }
157
+ }
158
+ }
159
+ const resultChunk = await flushReceiveChunk(currentBuffer);
160
+ if (resultChunk !== true) {
161
+ return await treatError(resultChunk);
162
+ }
163
+ valueAccumulator = await currentStream?.onEndPart(valueAccumulator) ?? valueAccumulator;
164
+ return valueAccumulator;
165
+ }
166
+ catch (error) {
167
+ await currentStream?.onError?.(error, valueAccumulator);
168
+ return utils.E.left("server-error", error);
169
+ }
170
+ finally {
171
+ request.destroy();
172
+ }
173
+ }
174
+
175
+ exports.readRequestFormData = readRequestFormData;
@@ -0,0 +1,21 @@
1
+ import { E, type MaybePromise } from "@duplojs/utils";
2
+ import type http from "http";
3
+ interface HeaderPartInformation {
4
+ name: string;
5
+ filename?: string;
6
+ }
7
+ export interface ReadRequestFormDataStreamChunkEvent<GenericValueAccumulator extends unknown = unknown> {
8
+ onReceiveChunk(chunk: Buffer): MaybePromise<void>;
9
+ onEndPart(valueAccumulator: GenericValueAccumulator): MaybePromise<GenericValueAccumulator>;
10
+ onError: ((error: unknown, valueAccumulator: GenericValueAccumulator) => MaybePromise<void>) | null;
11
+ }
12
+ export interface ReadRequestFormDataParams {
13
+ maxBodySize: number;
14
+ maxFileQuantity: number;
15
+ maxBufferSize: number;
16
+ maxKeyLength: number;
17
+ fileMaxSize?: number;
18
+ mimeType?: RegExp;
19
+ }
20
+ export declare function readRequestFormData<GenericValueAccumulator extends unknown, GenericOutputHeader extends E.Left = never>(request: http.IncomingMessage, firstValueAccumulator: GenericValueAccumulator, params: ReadRequestFormDataParams, onReceiveHeader: (header: HeaderPartInformation) => MaybePromise<ReadRequestFormDataStreamChunkEvent<GenericValueAccumulator> | Error>): Promise<E.Left<"server-error", unknown> | GenericOutputHeader | E.Error<Error> | GenericValueAccumulator>;
21
+ export {};
@@ -0,0 +1,173 @@
1
+ import { S, E, Path } from '@duplojs/utils';
2
+ import { BodyParseFormDataError } from './error.mjs';
3
+ import '../../../../core/errors/index.mjs';
4
+ import { BodySizeExceedsLimitError } from '../../../../core/errors/bodySizeExceedsLimitError.mjs';
5
+ import { BodyParseWrongChunkReceived } from '../../../../core/errors/bodyParseWrongChunkReceived.mjs';
6
+
7
+ const endHeaderPart = Buffer.from("\r\n\r\n");
8
+ const bufferStart = Buffer.from("\r\n");
9
+ const regexBoundary = /boundary=(?<boundary>[^; ]+)/i;
10
+ const regexHeaderPart = /name="(?<name>(?:\\"|[^"])+)"(?:; filename="(?<filename>(?:\\"|[^"])+)")?(?:;\s+filename\*=[^']+'[^']*'(?<encodedFilename>[^;\r\n\s]+))?/i;
11
+ function safeDecode(value) {
12
+ try {
13
+ return decodeURIComponent(value);
14
+ }
15
+ catch {
16
+ return value;
17
+ }
18
+ }
19
+ async function readRequestFormData(request, firstValueAccumulator, params, onReceiveHeader) {
20
+ const boundary = S.extract(request.headers["content-type"] ?? "", regexBoundary)?.namedGroups?.boundary;
21
+ if (!boundary) {
22
+ return E.error(new BodyParseFormDataError("Wrong boundary."));
23
+ }
24
+ let valueAccumulator = firstValueAccumulator;
25
+ const startPart = Buffer.from(`\r\n--${boundary}`);
26
+ const endMultiPart = Buffer.from(`\r\n--${boundary}--`);
27
+ let currentBuffer = bufferStart;
28
+ let size = 0;
29
+ const keep = endMultiPart.length - 1;
30
+ let currentStream = undefined;
31
+ let fileQuantity = 0;
32
+ let currentFileSize = undefined;
33
+ const checkSize = (receivedChunk) => {
34
+ size += receivedChunk.length;
35
+ return size > params.maxBodySize
36
+ ? new BodySizeExceedsLimitError(params.maxBodySize)
37
+ : true;
38
+ };
39
+ const flushReceiveHeader = async (headerPart) => {
40
+ valueAccumulator = await currentStream?.onEndPart(valueAccumulator) ?? valueAccumulator;
41
+ const sizeResult = checkSize(headerPart);
42
+ if (sizeResult !== true) {
43
+ return sizeResult;
44
+ }
45
+ const extract = S.extract(headerPart.toString("utf-8"), regexHeaderPart)?.namedGroups;
46
+ const header = extract?.name
47
+ ? {
48
+ name: extract.name.trim(),
49
+ filename: (extract.encodedFilename !== undefined
50
+ ? safeDecode(extract.encodedFilename)
51
+ : extract.filename)?.trim(),
52
+ }
53
+ : null;
54
+ if (!header) {
55
+ return new BodyParseFormDataError("Bad content header part.");
56
+ }
57
+ if (header.name.length > params.maxKeyLength) {
58
+ return new BodyParseFormDataError("key length exceeds limit.");
59
+ }
60
+ if (header.filename !== undefined) {
61
+ currentFileSize = 0;
62
+ fileQuantity++;
63
+ if (fileQuantity > params.maxFileQuantity) {
64
+ return new BodyParseFormDataError("File quantity exceeds limit.");
65
+ }
66
+ else if (params.mimeType !== undefined
67
+ && !params.mimeType.test(Path.getExtensionName(header.filename) ?? "")) {
68
+ return new BodyParseFormDataError("File have wrong mimeType.");
69
+ }
70
+ }
71
+ else {
72
+ currentFileSize = undefined;
73
+ }
74
+ const newStream = await onReceiveHeader(header);
75
+ if (newStream instanceof Error) {
76
+ return newStream;
77
+ }
78
+ currentStream = newStream;
79
+ return true;
80
+ };
81
+ const flushReceiveChunk = async (chunk) => {
82
+ if (chunk.length === 0) {
83
+ return true;
84
+ }
85
+ const sizeResult = checkSize(chunk);
86
+ if (sizeResult !== true) {
87
+ return sizeResult;
88
+ }
89
+ if (!currentStream) {
90
+ return new BodyParseFormDataError("Receive chunk before header part.");
91
+ }
92
+ if (typeof currentFileSize === "number") {
93
+ currentFileSize += chunk.length;
94
+ if (params.fileMaxSize !== undefined && currentFileSize > params.fileMaxSize) {
95
+ return new BodyParseFormDataError("File size exceeds limit.");
96
+ }
97
+ }
98
+ await currentStream.onReceiveChunk(chunk);
99
+ return true;
100
+ };
101
+ const treatError = async (error) => {
102
+ await currentStream?.onError?.(error, valueAccumulator);
103
+ return E.error(error);
104
+ };
105
+ try {
106
+ for await (const chunk of request) {
107
+ if (!(chunk instanceof Buffer)) {
108
+ return await treatError(new BodyParseWrongChunkReceived("Buffer.", chunk));
109
+ }
110
+ currentBuffer = Buffer.concat([currentBuffer, chunk]);
111
+ if (currentBuffer.length > params.maxBufferSize) {
112
+ return await treatError(new BodyParseFormDataError("Buffer size exceeds limit."));
113
+ }
114
+ while (true) {
115
+ const endMultiPartIndex = currentBuffer.indexOf(endMultiPart);
116
+ if (endMultiPartIndex !== -1) {
117
+ // check if buffer contain end of transmissions
118
+ currentBuffer = currentBuffer.subarray(0, endMultiPartIndex);
119
+ }
120
+ const startPartIndex = currentBuffer.indexOf(startPart);
121
+ const endHeaderPartIndex = currentBuffer.indexOf(endHeaderPart);
122
+ if (startPartIndex !== -1 && endHeaderPartIndex !== -1) {
123
+ // check if buffer contain an entire header of part
124
+ const resultChunk = await flushReceiveChunk(currentBuffer.subarray(0, startPartIndex));
125
+ if (resultChunk !== true) {
126
+ return await treatError(resultChunk);
127
+ }
128
+ const endIndex = endHeaderPartIndex + endHeaderPart.length;
129
+ const resultHeader = await flushReceiveHeader(currentBuffer.subarray(startPartIndex, endIndex));
130
+ if (resultHeader !== true) {
131
+ return await treatError(resultHeader);
132
+ }
133
+ currentBuffer = currentBuffer.subarray(endIndex);
134
+ }
135
+ else if (startPartIndex === -1 && endHeaderPartIndex === -1) {
136
+ // check if buffer contain only data
137
+ if (currentBuffer.length > keep) {
138
+ const bufferRestIndex = currentBuffer.length - keep;
139
+ const resultChunk = await flushReceiveChunk(currentBuffer.subarray(0, bufferRestIndex));
140
+ if (resultChunk !== true) {
141
+ return await treatError(resultChunk);
142
+ }
143
+ currentBuffer = currentBuffer.subarray(bufferRestIndex);
144
+ }
145
+ break;
146
+ }
147
+ else if (startPartIndex !== -1 && endHeaderPartIndex === -1) {
148
+ // check if buffer contain start of header but not contain end
149
+ break;
150
+ }
151
+ else {
152
+ // check if buffer contain only end of header part
153
+ return await treatError(new BodyParseFormDataError("Wrong content."));
154
+ }
155
+ }
156
+ }
157
+ const resultChunk = await flushReceiveChunk(currentBuffer);
158
+ if (resultChunk !== true) {
159
+ return await treatError(resultChunk);
160
+ }
161
+ valueAccumulator = await currentStream?.onEndPart(valueAccumulator) ?? valueAccumulator;
162
+ return valueAccumulator;
163
+ }
164
+ catch (error) {
165
+ await currentStream?.onError?.(error, valueAccumulator);
166
+ return E.left("server-error", error);
167
+ }
168
+ finally {
169
+ request.destroy();
170
+ }
171
+ }
172
+
173
+ export { readRequestFormData };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ var index = require('./formData/index.cjs');
4
+ var index$1 = require('./text/index.cjs');
5
+
6
+
7
+
8
+ exports.createFormDataBodyReaderImplementation = index.createFormDataBodyReaderImplementation;
9
+ exports.createTextBodyReaderImplementation = index$1.createTextBodyReaderImplementation;
@@ -0,0 +1,2 @@
1
+ export * from "./formData";
2
+ export * from "./text";
@@ -0,0 +1,2 @@
1
+ export { createFormDataBodyReaderImplementation } from './formData/index.mjs';
2
+ export { createTextBodyReaderImplementation } from './text/index.mjs';
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ require('../../../../core/request/index.cjs');
4
+ var readRequestText = require('./readRequestText.cjs');
5
+ var utils = require('@duplojs/utils');
6
+ require('../../../../core/errors/index.cjs');
7
+ var text = require('../../../../core/request/bodyController/text.cjs');
8
+ var wrongContentTypeError = require('../../../../core/errors/wrongContentTypeError.cjs');
9
+ var parseJsonError = require('../../../../core/errors/parseJsonError.cjs');
10
+
11
+ function createTextBodyReaderImplementation(serverParams) {
12
+ const serverMaxBodySize = utils.stringToBytes(serverParams.maxBodySize);
13
+ return text.TextBodyController.createReaderImplementation(async (request, params) => {
14
+ if (!request.headers["content-type"]?.includes("application/json")
15
+ && !request.headers["content-type"]?.includes("text/plain")) {
16
+ return utils.E.error(new wrongContentTypeError.WrongContentTypeError("application/json or text/plain", utils.A.join(utils.A.coalescing(request.headers["content-type"] ?? ""), " ")));
17
+ }
18
+ const result = await readRequestText.readRequestText(request.raw.request, { maxBodySize: params.bodyMaxSize ?? serverMaxBodySize }, (result) => {
19
+ if (request.headers["content-type"]?.includes("application/json")) {
20
+ try {
21
+ return utils.E.success(JSON.parse(result));
22
+ }
23
+ catch (error) {
24
+ return utils.E.error(new parseJsonError.ParseJsonError(result, error));
25
+ }
26
+ }
27
+ return utils.E.success(result);
28
+ });
29
+ if (utils.E.isLeft(result)) {
30
+ // mandatory in case of error to avoid monopolizing the client connection if a stream is not finished.
31
+ request.raw.response.setHeader("Connection", "close");
32
+ }
33
+ if (utils.E.hasInformation(result, "server-error")) {
34
+ throw utils.unwrap(result);
35
+ }
36
+ return result;
37
+ });
38
+ }
39
+
40
+ exports.readRequestText = readRequestText.readRequestText;
41
+ exports.createTextBodyReaderImplementation = createTextBodyReaderImplementation;
@@ -0,0 +1,3 @@
1
+ import { type HttpServerParams } from "../../../../core/types";
2
+ export * from "./readRequestText";
3
+ export declare function createTextBodyReaderImplementation(serverParams: HttpServerParams): import("../../../../core/request").BodyReaderImplementation<"text", import("../../../../core/request").TextBodyReaderParams>;
@@ -0,0 +1,38 @@
1
+ import '../../../../core/request/index.mjs';
2
+ import { readRequestText } from './readRequestText.mjs';
3
+ import { stringToBytes, E, A, unwrap } from '@duplojs/utils';
4
+ import '../../../../core/errors/index.mjs';
5
+ import { TextBodyController } from '../../../../core/request/bodyController/text.mjs';
6
+ import { WrongContentTypeError } from '../../../../core/errors/wrongContentTypeError.mjs';
7
+ import { ParseJsonError } from '../../../../core/errors/parseJsonError.mjs';
8
+
9
+ function createTextBodyReaderImplementation(serverParams) {
10
+ const serverMaxBodySize = stringToBytes(serverParams.maxBodySize);
11
+ return TextBodyController.createReaderImplementation(async (request, params) => {
12
+ if (!request.headers["content-type"]?.includes("application/json")
13
+ && !request.headers["content-type"]?.includes("text/plain")) {
14
+ return E.error(new WrongContentTypeError("application/json or text/plain", A.join(A.coalescing(request.headers["content-type"] ?? ""), " ")));
15
+ }
16
+ const result = await readRequestText(request.raw.request, { maxBodySize: params.bodyMaxSize ?? serverMaxBodySize }, (result) => {
17
+ if (request.headers["content-type"]?.includes("application/json")) {
18
+ try {
19
+ return E.success(JSON.parse(result));
20
+ }
21
+ catch (error) {
22
+ return E.error(new ParseJsonError(result, error));
23
+ }
24
+ }
25
+ return E.success(result);
26
+ });
27
+ if (E.isLeft(result)) {
28
+ // mandatory in case of error to avoid monopolizing the client connection if a stream is not finished.
29
+ request.raw.response.setHeader("Connection", "close");
30
+ }
31
+ if (E.hasInformation(result, "server-error")) {
32
+ throw unwrap(result);
33
+ }
34
+ return result;
35
+ });
36
+ }
37
+
38
+ export { createTextBodyReaderImplementation, readRequestText };
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ require('../../../../core/errors/index.cjs');
4
+ var utils = require('@duplojs/utils');
5
+ var bodyParseWrongChunkReceived = require('../../../../core/errors/bodyParseWrongChunkReceived.cjs');
6
+ var bodySizeExceedsLimitError = require('../../../../core/errors/bodySizeExceedsLimitError.cjs');
7
+
8
+ async function readRequestText(request, params, onEnd) {
9
+ let result = "";
10
+ let size = 0;
11
+ try {
12
+ for await (const chunk of request) {
13
+ if (!(chunk instanceof Buffer) && typeof chunk !== "string") {
14
+ return utils.E.error(new bodyParseWrongChunkReceived.BodyParseWrongChunkReceived("Buffer or String.", chunk));
15
+ }
16
+ size += chunk instanceof Buffer
17
+ ? chunk.byteLength
18
+ : Buffer.byteLength(chunk);
19
+ if (size > params.maxBodySize) {
20
+ return utils.E.error(new bodySizeExceedsLimitError.BodySizeExceedsLimitError(params.maxBodySize));
21
+ }
22
+ result += chunk.toString();
23
+ }
24
+ if (onEnd) {
25
+ return await onEnd(result);
26
+ }
27
+ return result;
28
+ }
29
+ catch (error) {
30
+ return utils.E.left("server-error", error);
31
+ }
32
+ finally {
33
+ request.destroy();
34
+ }
35
+ }
36
+
37
+ exports.readRequestText = readRequestText;
@@ -0,0 +1,6 @@
1
+ import { E } from "@duplojs/utils";
2
+ import type http from "http";
3
+ export interface ReadRequestTextParams {
4
+ maxBodySize: number;
5
+ }
6
+ export declare function readRequestText<GenericOutputValue extends unknown = string>(request: http.IncomingMessage, params: ReadRequestTextParams, onEnd?: (result: string) => GenericOutputValue): Promise<E.Left<"server-error", unknown> | E.Error<Error> | GenericOutputValue>;
@@ -0,0 +1,35 @@
1
+ import '../../../../core/errors/index.mjs';
2
+ import { E } from '@duplojs/utils';
3
+ import { BodyParseWrongChunkReceived } from '../../../../core/errors/bodyParseWrongChunkReceived.mjs';
4
+ import { BodySizeExceedsLimitError } from '../../../../core/errors/bodySizeExceedsLimitError.mjs';
5
+
6
+ async function readRequestText(request, params, onEnd) {
7
+ let result = "";
8
+ let size = 0;
9
+ try {
10
+ for await (const chunk of request) {
11
+ if (!(chunk instanceof Buffer) && typeof chunk !== "string") {
12
+ return E.error(new BodyParseWrongChunkReceived("Buffer or String.", chunk));
13
+ }
14
+ size += chunk instanceof Buffer
15
+ ? chunk.byteLength
16
+ : Buffer.byteLength(chunk);
17
+ if (size > params.maxBodySize) {
18
+ return E.error(new BodySizeExceedsLimitError(params.maxBodySize));
19
+ }
20
+ result += chunk.toString();
21
+ }
22
+ if (onEnd) {
23
+ return await onEnd(result);
24
+ }
25
+ return result;
26
+ }
27
+ catch (error) {
28
+ return E.left("server-error", error);
29
+ }
30
+ finally {
31
+ request.destroy();
32
+ }
33
+ }
34
+
35
+ export { readRequestText };
@@ -1,12 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var utils = require('@duplojs/utils');
4
3
  var http = require('http');
5
4
  var https = require('https');
6
- var hooks = require('./hooks.cjs');
5
+ var index$3 = require('./hooks/index.cjs');
7
6
  var implementHttpServer = require('../../core/implementHttpServer.cjs');
7
+ var utils = require('@duplojs/utils');
8
+ var index$2 = require('../../core/defaultHooks/index.cjs');
9
+ require('./bodyReaders/index.cjs');
10
+ var index = require('./bodyReaders/text/index.cjs');
11
+ var index$1 = require('./bodyReaders/formData/index.cjs');
8
12
 
9
- function createHttpServer(inputHub, params) {
13
+ function createHttpServer(hub, params) {
10
14
  const httpServerParams = utils.O.override({
11
15
  host: "localhost",
12
16
  port: 80,
@@ -15,9 +19,13 @@ function createHttpServer(inputHub, params) {
15
19
  predictedHeaderKey: "predicted",
16
20
  fromHookHeaderKey: "from-hook",
17
21
  interface: "node",
22
+ uploadFolder: "./upload",
18
23
  }, params);
19
- const hooks$1 = hooks.makeNodeHook(inputHub, httpServerParams);
20
- const hub = inputHub.addRouteHooks(hooks$1);
24
+ hub.addBodyReaderImplementation([
25
+ index.createTextBodyReaderImplementation(httpServerParams),
26
+ index$1.createFormDataBodyReaderImplementation(httpServerParams),
27
+ ]);
28
+ hub.addRouteHooks([index$2.initDefaultHook(hub, httpServerParams), index$3.nodeHook]);
21
29
  function whenUncaughtError(error, routerInitializationData) {
22
30
  const serverResponse = routerInitializationData.raw.response;
23
31
  if (!serverResponse.headersSent && !serverResponse.writableEnded) {
@@ -1,20 +1,21 @@
1
- import { type HttpServerParams, type Hub } from "../../core/hub";
2
- import { type Hosts } from "./types/host";
3
- import { type BytesInString, O } from "@duplojs/utils";
1
+ import { type Hub } from "../../core/hub";
4
2
  import http from "http";
5
3
  import https from "https";
6
- declare module "../../core/hub" {
4
+ import { O } from "@duplojs/utils";
5
+ import { type HttpServerParams } from "../../core/types";
6
+ declare module "../../core/types" {
7
7
  interface HttpServerParams {
8
8
  readonly interface: "node";
9
- readonly host: Hosts;
10
- readonly port: number;
11
- readonly maxBodySize: BytesInString | number;
12
- readonly informationHeaderKey: string;
13
- readonly predictedHeaderKey: string;
14
- readonly fromHookHeaderKey: string;
15
9
  readonly http?: http.ServerOptions;
16
10
  readonly https?: https.ServerOptions;
17
11
  }
12
+ interface HostCustom {
13
+ "::": true;
14
+ "0.0.0.0": true;
15
+ localhost: true;
16
+ "127.0.0.1": true;
17
+ "::1": true;
18
+ }
18
19
  }
19
- export type CreateHttpServerParams = O.PartialKeys<Omit<HttpServerParams, "interface">, "maxBodySize" | "informationHeaderKey" | "predictedHeaderKey" | "fromHookHeaderKey">;
20
- export declare function createHttpServer(inputHub: Hub, params: CreateHttpServerParams): Promise<https.Server<typeof http.IncomingMessage, typeof http.ServerResponse> | http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
20
+ export type CreateHttpServerParams = O.PartialKeys<Omit<HttpServerParams, "interface">, "maxBodySize" | "informationHeaderKey" | "predictedHeaderKey" | "fromHookHeaderKey" | "uploadFolder">;
21
+ export declare function createHttpServer(hub: Hub, params: CreateHttpServerParams): Promise<https.Server<typeof http.IncomingMessage, typeof http.ServerResponse> | http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
@@ -1,10 +1,14 @@
1
- import { O } from '@duplojs/utils';
2
1
  import http from 'http';
3
2
  import https from 'https';
4
- import { makeNodeHook } from './hooks.mjs';
3
+ import { nodeHook } from './hooks/index.mjs';
5
4
  import { implementHttpServer } from '../../core/implementHttpServer.mjs';
5
+ import { O } from '@duplojs/utils';
6
+ import { initDefaultHook } from '../../core/defaultHooks/index.mjs';
7
+ import './bodyReaders/index.mjs';
8
+ import { createTextBodyReaderImplementation } from './bodyReaders/text/index.mjs';
9
+ import { createFormDataBodyReaderImplementation } from './bodyReaders/formData/index.mjs';
6
10
 
7
- function createHttpServer(inputHub, params) {
11
+ function createHttpServer(hub, params) {
8
12
  const httpServerParams = O.override({
9
13
  host: "localhost",
10
14
  port: 80,
@@ -13,9 +17,13 @@ function createHttpServer(inputHub, params) {
13
17
  predictedHeaderKey: "predicted",
14
18
  fromHookHeaderKey: "from-hook",
15
19
  interface: "node",
20
+ uploadFolder: "./upload",
16
21
  }, params);
17
- const hooks = makeNodeHook(inputHub, httpServerParams);
18
- const hub = inputHub.addRouteHooks(hooks);
22
+ hub.addBodyReaderImplementation([
23
+ createTextBodyReaderImplementation(httpServerParams),
24
+ createFormDataBodyReaderImplementation(httpServerParams),
25
+ ]);
26
+ hub.addRouteHooks([initDefaultHook(hub, httpServerParams), nodeHook]);
19
27
  function whenUncaughtError(error, routerInitializationData) {
20
28
  const serverResponse = routerInitializationData.raw.response;
21
29
  if (!serverResponse.headersSent && !serverResponse.writableEnded) {