zibri 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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/assets/assets.svg +1 -0
  4. package/assets/favicon-16x16.png +0 -0
  5. package/assets/favicon-32x32.png +0 -0
  6. package/assets/favicon-96x96.png +0 -0
  7. package/assets/favicon.ico +0 -0
  8. package/assets/logo-original.jpg +0 -0
  9. package/assets/logo.jpg +0 -0
  10. package/assets/swagger.png +0 -0
  11. package/cspell.words.txt +4 -0
  12. package/eslint.config.mjs +5 -0
  13. package/jest.config.mjs +25 -0
  14. package/package.json +50 -0
  15. package/sandbox/assets/assets.svg +1 -0
  16. package/sandbox/assets/favicon.ico +0 -0
  17. package/sandbox/assets/favicon.png +0 -0
  18. package/sandbox/assets/logo.jpg +0 -0
  19. package/sandbox/assets/open-api/custom.css +65 -0
  20. package/sandbox/assets/open-api/swagger-ui-bundle.js +2 -0
  21. package/sandbox/assets/open-api/swagger-ui-standalone-preset.js +2 -0
  22. package/sandbox/assets/open-api/swagger-ui.css +3 -0
  23. package/sandbox/assets/swagger.png +0 -0
  24. package/sandbox/assets/template/assets.hbs +51 -0
  25. package/sandbox/assets/template/error.hbs +41 -0
  26. package/sandbox/assets/template/index.hbs +34 -0
  27. package/sandbox/assets/template/style.css +76 -0
  28. package/sandbox/package-lock.json +1923 -0
  29. package/sandbox/package.json +26 -0
  30. package/sandbox/src/controllers/test.controller.ts +77 -0
  31. package/sandbox/src/index.ts +11 -0
  32. package/sandbox/src/services/dep.service.ts +9 -0
  33. package/sandbox/src/services/test.service.ts +12 -0
  34. package/sandbox/tsconfig.json +16 -0
  35. package/sandbox/webpack.config.js +71 -0
  36. package/src/application-options.model.ts +10 -0
  37. package/src/application.ts +73 -0
  38. package/src/assets/asset-service.interface.ts +9 -0
  39. package/src/assets/asset.service.ts +171 -0
  40. package/src/assets/index.ts +2 -0
  41. package/src/di/decorators/index.ts +2 -0
  42. package/src/di/decorators/inject.decorator.ts +13 -0
  43. package/src/di/decorators/injectable.decorator.ts +17 -0
  44. package/src/di/default/index.ts +1 -0
  45. package/src/di/default/zibri-di-providers.default.ts +37 -0
  46. package/src/di/default/zibri-di-tokens.default.ts +12 -0
  47. package/src/di/di-container.ts +81 -0
  48. package/src/di/errors/get-dependency-stack-trace.function.ts +11 -0
  49. package/src/di/errors/index.ts +2 -0
  50. package/src/di/errors/no-provider.error.ts +36 -0
  51. package/src/di/index.ts +4 -0
  52. package/src/di/inject.function.ts +7 -0
  53. package/src/di/models/di-provider.model.ts +8 -0
  54. package/src/di/models/di-token.model.ts +3 -0
  55. package/src/di/models/index.ts +2 -0
  56. package/src/di/register.function.ts +11 -0
  57. package/src/encapsulation/index.ts +4 -0
  58. package/src/encapsulation/is-date.function.ts +10 -0
  59. package/src/encapsulation/is-numeric.function.ts +9 -0
  60. package/src/encapsulation/metadata-injection-keys.enum.ts +14 -0
  61. package/src/encapsulation/metadata.utilities.ts +96 -0
  62. package/src/encapsulation/reflect.utilities.ts +51 -0
  63. package/src/entity/decorators/index.ts +1 -0
  64. package/src/entity/decorators/property.decorator.ts +48 -0
  65. package/src/entity/index.ts +1 -0
  66. package/src/error-handling/error-handler.model.ts +3 -0
  67. package/src/error-handling/error-handler.ts +72 -0
  68. package/src/error-handling/errors/bad-request.error.ts +9 -0
  69. package/src/error-handling/errors/http.error.ts +20 -0
  70. package/src/error-handling/errors/index.ts +6 -0
  71. package/src/error-handling/errors/internal-server.error.ts +9 -0
  72. package/src/error-handling/errors/not-found.error.ts +9 -0
  73. package/src/error-handling/errors/unmatched-route.error.ts +14 -0
  74. package/src/error-handling/errors/validation.error.ts +24 -0
  75. package/src/error-handling/index.ts +3 -0
  76. package/src/error-handling/is-error.function.ts +3 -0
  77. package/src/global/global-registry.ts +85 -0
  78. package/src/global/index.ts +1 -0
  79. package/src/http/http-method.enum.ts +8 -0
  80. package/src/http/http-status.enum.ts +6 -0
  81. package/src/http/index.ts +4 -0
  82. package/src/http/known-header.type.ts +25 -0
  83. package/src/http/mime-type.enum.ts +8 -0
  84. package/src/index.ts +16 -0
  85. package/src/jest.setup.ts +2 -0
  86. package/src/logging/index.ts +2 -0
  87. package/src/logging/logger.interface.ts +8 -0
  88. package/src/logging/logger.ts +87 -0
  89. package/src/open-api/index.ts +3 -0
  90. package/src/open-api/open-api-service.interface.ts +10 -0
  91. package/src/open-api/open-api.model.ts +13 -0
  92. package/src/open-api/open-api.service.ts +334 -0
  93. package/src/parsing/body-parser.interface.ts +8 -0
  94. package/src/parsing/decorators/body-parser.decorator.ts +17 -0
  95. package/src/parsing/decorators/index.ts +1 -0
  96. package/src/parsing/functions/index.ts +12 -0
  97. package/src/parsing/functions/parse-array-query-param.function.ts +15 -0
  98. package/src/parsing/functions/parse-array-query-param.test.ts +46 -0
  99. package/src/parsing/functions/parse-boolean-header-param.function.ts +15 -0
  100. package/src/parsing/functions/parse-boolean-header-param.test.ts +33 -0
  101. package/src/parsing/functions/parse-boolean-query-param.function.ts +15 -0
  102. package/src/parsing/functions/parse-boolean-query-param.test.ts +33 -0
  103. package/src/parsing/functions/parse-date-header-param.function.ts +8 -0
  104. package/src/parsing/functions/parse-date-header-param.test.ts +32 -0
  105. package/src/parsing/functions/parse-date-query-param.function.ts +8 -0
  106. package/src/parsing/functions/parse-date-query-param.test.ts +32 -0
  107. package/src/parsing/functions/parse-number-header-param.function.ts +8 -0
  108. package/src/parsing/functions/parse-number-header-param.test.ts +30 -0
  109. package/src/parsing/functions/parse-number-path-param.function.ts +8 -0
  110. package/src/parsing/functions/parse-number-path-param.test.ts +30 -0
  111. package/src/parsing/functions/parse-number-query-param.function.ts +8 -0
  112. package/src/parsing/functions/parse-number-query-param.test.ts +34 -0
  113. package/src/parsing/functions/parse-object-query-param.function.ts +15 -0
  114. package/src/parsing/functions/parse-object-query-param.test.ts +69 -0
  115. package/src/parsing/functions/parse-string-header-param.function.ts +4 -0
  116. package/src/parsing/functions/parse-string-path-param.function.ts +4 -0
  117. package/src/parsing/functions/parse-string-query-param.function.ts +4 -0
  118. package/src/parsing/index.ts +5 -0
  119. package/src/parsing/json.body-parser.ts +34 -0
  120. package/src/parsing/parser.interface.ts +12 -0
  121. package/src/parsing/parser.ts +85 -0
  122. package/src/routing/controller-route-configuration.model.ts +9 -0
  123. package/src/routing/decorators/body.decorator.ts +28 -0
  124. package/src/routing/decorators/controller.decorator.ts +18 -0
  125. package/src/routing/decorators/create-http-decorator.function.ts +15 -0
  126. package/src/routing/decorators/delete.decorator.ts +7 -0
  127. package/src/routing/decorators/get.decorator.ts +7 -0
  128. package/src/routing/decorators/index.ts +7 -0
  129. package/src/routing/decorators/param.decorator.ts +112 -0
  130. package/src/routing/decorators/patch.decorator.ts +7 -0
  131. package/src/routing/decorators/post.decorator.ts +7 -0
  132. package/src/routing/index.ts +5 -0
  133. package/src/routing/missing-base-route.error.ts +9 -0
  134. package/src/routing/route-configuration.model.ts +10 -0
  135. package/src/routing/router.interface.ts +12 -0
  136. package/src/routing/router.ts +164 -0
  137. package/src/types/deep-partial.type.ts +10 -0
  138. package/src/types/index.ts +3 -0
  139. package/src/types/newable.model.ts +2 -0
  140. package/src/types/omit-strict.type.ts +4 -0
  141. package/src/validation/functions/index.ts +11 -0
  142. package/src/validation/functions/validate-boolean-header-param.function.ts +16 -0
  143. package/src/validation/functions/validate-boolean-query-param.function.ts +16 -0
  144. package/src/validation/functions/validate-date-header-param.function.ts +16 -0
  145. package/src/validation/functions/validate-date-query-param.function.ts +16 -0
  146. package/src/validation/functions/validate-number-header-param.function.ts +16 -0
  147. package/src/validation/functions/validate-number-path-param.function.ts +16 -0
  148. package/src/validation/functions/validate-number-property.function.ts +21 -0
  149. package/src/validation/functions/validate-number-query-param.function.ts +16 -0
  150. package/src/validation/functions/validate-string-header-param.function.ts +17 -0
  151. package/src/validation/functions/validate-string-path-param.function.ts +17 -0
  152. package/src/validation/functions/validate-string-property.function.ts +21 -0
  153. package/src/validation/functions/validate-string-query-param.function.ts +17 -0
  154. package/src/validation/index.ts +3 -0
  155. package/src/validation/validation-problem.model.ts +9 -0
  156. package/src/validation/validation-service.interface.ts +9 -0
  157. package/src/validation/validation.service.ts +295 -0
  158. package/tsconfig.json +17 -0
  159. package/tsup.config.ts +13 -0
@@ -0,0 +1,295 @@
1
+ import { MetadataUtilities } from '../encapsulation';
2
+ import { ArrayPropertyMetadata, PropertyMetadata } from '../entity';
3
+ import { ValidationError } from '../error-handling';
4
+ import { ArrayQueryParamMetadata, HeaderParamMetadata, PathParamMetadata, QueryParamMetadata } from '../routing';
5
+ import { Newable } from '../types';
6
+ import { validateBooleanHeaderParam, validateBooleanQueryParam, validateDateHeaderParam, validateDateQueryParam, validateNumberPathParam, validateNumberProperty, validateNumberQueryParam, validateStringHeaderParam, validateStringPathParam, validateStringProperty, validateStringQueryParam } from './functions';
7
+ import { validateNumberHeaderParam } from './functions/validate-number-header-param.function';
8
+ import { IsRequiredValidationProblem, ValidationProblem } from './validation-problem.model';
9
+ import { ValidationServiceInterface } from './validation-service.interface';
10
+
11
+ type PathParamValidationFunction = (param: unknown, meta: PathParamMetadata) => ValidationProblem[];
12
+
13
+ type QueryParamValidationFunction = (param: unknown, meta: QueryParamMetadata, parentKey: string | undefined) => ValidationProblem[];
14
+
15
+ type HeaderParamValidationFunction = (param: unknown, meta: HeaderParamMetadata) => ValidationProblem[];
16
+
17
+ type PropertyValidationFunction = (
18
+ key: string,
19
+ property: unknown,
20
+ metadata: PropertyMetadata,
21
+ parentKey: string | undefined
22
+ ) => ValidationProblem[];
23
+
24
+ export class ValidationService implements ValidationServiceInterface {
25
+
26
+ private readonly pathParamValidationFunctions: Record<PathParamMetadata['type'], PathParamValidationFunction> = {
27
+ string: validateStringPathParam,
28
+ number: validateNumberPathParam
29
+ };
30
+
31
+ private readonly queryParamValidationFunctions: Record<QueryParamMetadata['type'], QueryParamValidationFunction> = {
32
+ string: validateStringQueryParam,
33
+ number: validateNumberQueryParam,
34
+ boolean: validateBooleanQueryParam,
35
+ date: validateDateQueryParam,
36
+ object: this.validateObjectQueryParam.bind(this),
37
+ array: this.validateArrayQueryParam.bind(this)
38
+ };
39
+
40
+ private readonly headerParamValidationFunctions: Record<HeaderParamMetadata['type'], HeaderParamValidationFunction> = {
41
+ string: validateStringHeaderParam,
42
+ number: validateNumberHeaderParam,
43
+ boolean: validateBooleanHeaderParam,
44
+ date: validateDateHeaderParam
45
+ };
46
+
47
+ private readonly propertyValidationFunctions: Record<PropertyMetadata['type'], PropertyValidationFunction> = {
48
+ object: this.validateObjectProperty.bind(this),
49
+ array: this.validateArrayProperty.bind(this),
50
+ number: validateNumberProperty,
51
+ string: validateStringProperty
52
+ };
53
+
54
+ private validateObjectQueryParam(param: unknown, meta: QueryParamMetadata, parentKey: string | undefined): ValidationProblem[] {
55
+ const fullKey: string = parentKey ? `${parentKey}.${meta.name}` : meta.name;
56
+ if (meta.type !== 'object') {
57
+ throw new Error('Tried to do object based validation on a non object value.');
58
+ }
59
+
60
+ if (param == undefined && meta.required) {
61
+ return [new IsRequiredValidationProblem(fullKey)];
62
+ }
63
+ if (param == undefined && !meta.required) {
64
+ return [];
65
+ }
66
+ if (typeof param !== 'object') {
67
+ return [{ key: fullKey, message: 'should be an object' }];
68
+ }
69
+ return this.validateModel(param, meta.cls, fullKey);
70
+ }
71
+
72
+ private validateArrayQueryParam(param: unknown, meta: QueryParamMetadata, parentKey: string | undefined): ValidationProblem[] {
73
+ const fullKey: string = parentKey ? `${parentKey}.${meta.name}` : meta.name;
74
+ if (meta.type !== 'array') {
75
+ throw new Error('Tried to do array based validation on a non array value.');
76
+ }
77
+ if (param == undefined && meta.required) {
78
+ return [new IsRequiredValidationProblem(fullKey)];
79
+ }
80
+ if (param == undefined && !meta.required) {
81
+ return [];
82
+ }
83
+ if (!Array.isArray(param)) {
84
+ return [{ key: meta.name, message: 'should be an array' }];
85
+ }
86
+
87
+ const res: ValidationProblem[] = [];
88
+ for (let i: number = 0; i < param.length; i++) {
89
+ const item: unknown = param[i];
90
+ const m: QueryParamMetadata = this.getQueryArrayItemMetadata(i, meta);
91
+ const validate: QueryParamValidationFunction | undefined = this.queryParamValidationFunctions[m.type];
92
+ if (validate == undefined) {
93
+ throw new Error(`Unknown type for query parameter "${fullKey}.${m.name}": ${m.type}`);
94
+ }
95
+ const errors: ValidationProblem[] = validate(item, m, fullKey);
96
+ res.push(...errors);
97
+ }
98
+ return res;
99
+ }
100
+
101
+ validateHeaderParam(param: unknown, meta: HeaderParamMetadata): void {
102
+ const validate: HeaderParamValidationFunction | undefined = this.headerParamValidationFunctions[meta.type];
103
+ if (validate == undefined) {
104
+ throw new Error(`Unknown type for header parameter "${meta.name}": ${meta.type}`);
105
+ }
106
+ const res: ValidationProblem[] = validate(param, meta);
107
+ if (res.length) {
108
+ throw new ValidationError('header', res);
109
+ }
110
+ }
111
+
112
+ validatePathParam(param: unknown, meta: PathParamMetadata): void {
113
+ const validate: PathParamValidationFunction | undefined = this.pathParamValidationFunctions[meta.type];
114
+ if (validate == undefined) {
115
+ throw new Error(`Unknown type for path parameter "${meta.name}": ${meta.type}`);
116
+ }
117
+ const res: ValidationProblem[] = validate(param, meta);
118
+ if (res.length) {
119
+ throw new ValidationError('path', res);
120
+ }
121
+ }
122
+
123
+ validateQueryParam(param: unknown, meta: QueryParamMetadata): void {
124
+ const validate: QueryParamValidationFunction | undefined = this.queryParamValidationFunctions[meta.type];
125
+ if (validate == undefined) {
126
+ throw new Error(`Unknown type for query parameter "${meta.name}": ${meta.type}`);
127
+ }
128
+ const res: ValidationProblem[] = validate(param, meta, undefined);
129
+ if (res.length) {
130
+ throw new ValidationError('query', res);
131
+ }
132
+ }
133
+
134
+ validateRequestBody(model: unknown, cls: Newable<unknown>): void {
135
+ const res: ValidationProblem[] = this.validateModel(model, cls, undefined);
136
+ if (res.length) {
137
+ throw new ValidationError('body', res);
138
+ }
139
+ }
140
+
141
+ private validateModel(model: unknown, cls: Newable<unknown>, parentKey: string | undefined): ValidationProblem[] {
142
+ const modelProperties: Record<string, PropertyMetadata> = MetadataUtilities.getModelProperties(cls);
143
+
144
+ const keysOfBody: string[] = Object.keys(model as Record<string, unknown>);
145
+ const keysOfModel: string[] = Object.keys(modelProperties);
146
+ const unknownKeys: string[] = keysOfBody.filter(k => !keysOfModel.includes(k));
147
+ const res: ValidationProblem[] = [];
148
+ for (const key of unknownKeys) {
149
+ const fullKey: string = parentKey ? `${parentKey}.${key}` : key;
150
+ res.push({ key: fullKey, message: 'this key is unknown' });
151
+ }
152
+ for (const [propertyKey, metadata] of Object.entries(modelProperties)) {
153
+ const property: unknown = (model as Record<string, unknown>)[propertyKey];
154
+ const errors: ValidationProblem[] = this.validateProperty(propertyKey, property, metadata, parentKey);
155
+ res.push(...errors);
156
+ }
157
+ return res;
158
+ }
159
+
160
+ private validateProperty(
161
+ key: string,
162
+ property: unknown,
163
+ metadata: PropertyMetadata,
164
+ parentKey: string | undefined
165
+ ): ValidationProblem[] {
166
+ const fullKey: string = parentKey ? `${parentKey}.${key}` : key;
167
+
168
+ const validate: PropertyValidationFunction | undefined = this.propertyValidationFunctions[metadata.type];
169
+ if (validate == undefined) {
170
+ throw new Error(`Unknown type for property "${fullKey}": ${metadata.type}`);
171
+ }
172
+ const res: ValidationProblem[] = validate(key, property, metadata, parentKey);
173
+ return res;
174
+ }
175
+
176
+ private validateArrayProperty(
177
+ key: string,
178
+ property: unknown,
179
+ metadata: PropertyMetadata,
180
+ parentKey: string | undefined
181
+ ): ValidationProblem[] {
182
+ if (metadata.type !== 'array') {
183
+ throw new Error('Tried to do array based validation on a non array value.');
184
+ }
185
+ const fullKey: string = parentKey ? `${parentKey}.${key}` : key;
186
+
187
+ if (property == undefined && metadata.required) {
188
+ return [new IsRequiredValidationProblem(fullKey)];
189
+ }
190
+ if (property == undefined && !metadata.required) {
191
+ return [];
192
+ }
193
+ if (!Array.isArray(property)) {
194
+ return [{ key: fullKey, message: 'should be an array' }];
195
+ }
196
+
197
+ const res: ValidationProblem[] = [];
198
+ for (let i: number = 0; i < property.length; i++) {
199
+ const item: unknown = property[i];
200
+ const m: PropertyMetadata = this.getPropertyArrayItemMetadata(metadata);
201
+ const errors: ValidationProblem[] = this.validateProperty(String(i), item, m, key);
202
+ res.push(...errors);
203
+ }
204
+ return res;
205
+ }
206
+
207
+ private getPropertyArrayItemMetadata(metadata: ArrayPropertyMetadata): PropertyMetadata {
208
+ switch (metadata.itemType) {
209
+ case 'number': {
210
+ return {
211
+ type: 'number',
212
+ required: true
213
+ };
214
+ }
215
+ case 'string': {
216
+ return {
217
+ type: 'string',
218
+ required: true
219
+ };
220
+ }
221
+ default: {
222
+ return {
223
+ type: 'object',
224
+ cls: metadata.itemType,
225
+ required: true
226
+ };
227
+ }
228
+ }
229
+ }
230
+
231
+ private getQueryArrayItemMetadata(index: number, metadata: ArrayQueryParamMetadata): QueryParamMetadata {
232
+ switch (metadata.itemType) {
233
+ case 'date':
234
+ case 'string':
235
+ case 'boolean':
236
+ case 'number': {
237
+ return {
238
+ name: String(index),
239
+ type: metadata.itemType,
240
+ required: true
241
+ };
242
+ }
243
+ default: {
244
+ return {
245
+ name: String(index),
246
+ type: 'object',
247
+ cls: metadata.itemType,
248
+ required: true
249
+ };
250
+ }
251
+ }
252
+ }
253
+
254
+ private validateObjectProperty(
255
+ key: string,
256
+ property: unknown,
257
+ metadata: PropertyMetadata,
258
+ parentKey: string | undefined
259
+ ): ValidationProblem[] {
260
+ if (metadata.type !== 'object') {
261
+ throw new Error('Tried to do object based validation on a non object value.');
262
+ }
263
+ const fullKey: string = parentKey ? `${parentKey}.${key}` : key;
264
+
265
+ if (property == undefined && metadata.required) {
266
+ return [new IsRequiredValidationProblem(fullKey)];
267
+ }
268
+ if (property == undefined && !metadata.required) {
269
+ return [];
270
+ }
271
+ if (typeof property !== 'object') {
272
+ return [{ key: fullKey, message: 'should be an object' }];
273
+ }
274
+
275
+ const objectProperties: Record<string, PropertyMetadata> = MetadataUtilities.getModelProperties(metadata.cls);
276
+ const keysOfBody: string[] = Object.keys(property as Record<string, unknown>);
277
+ const keysOfModel: string[] = Object.keys(objectProperties);
278
+ const unknownKeys: string[] = keysOfBody.filter(k => !keysOfModel.includes(k));
279
+
280
+ const res: ValidationProblem[] = [];
281
+ for (const key of unknownKeys) {
282
+ res.push({ key, message: 'this key is unknown' });
283
+ }
284
+ if (res.length) {
285
+ throw new ValidationError('body', res);
286
+ }
287
+
288
+ for (const [propertyKey, m] of Object.entries(objectProperties)) {
289
+ const childProperty: unknown = (property as Record<string, unknown>)[propertyKey];
290
+ const errors: ValidationProblem[] = this.validateProperty(propertyKey, childProperty, m, key);
291
+ res.push(...errors);
292
+ }
293
+ return res;
294
+ }
295
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "experimentalDecorators": true,
12
+ "emitDecoratorMetadata": true,
13
+ "sourceMap": true,
14
+ "forceConsistentCasingInFileNames": true
15
+ },
16
+ "include": ["src"]
17
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ outDir: 'dist',
6
+ target: 'node20',
7
+ format: ['esm', 'cjs'],
8
+ ignoreWatch: 'sandbox',
9
+ splitting: false,
10
+ sourcemap: true,
11
+ clean: true,
12
+ dts: true
13
+ });