@koala-ts/framework 2.9.1 → 2.11.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 (70) hide show
  1. package/dist/Application/Response.test.js +1 -4
  2. package/dist/Application/Response.test.js.map +1 -1
  3. package/dist/Routing/Router.test.js +1 -5
  4. package/dist/Routing/Router.test.js.map +1 -1
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/serializer/normalizer/normalizer.test.js +60 -0
  10. package/dist/serializer/normalizer/normalizer.test.js.map +1 -1
  11. package/dist/serializer/normalizer/record-normalizer.d.ts.map +1 -1
  12. package/dist/serializer/normalizer/record-normalizer.js +25 -4
  13. package/dist/serializer/normalizer/record-normalizer.js.map +1 -1
  14. package/dist/serializer/normalizer/record-normalizer.test.js +4 -4
  15. package/dist/serializer/normalizer/record-normalizer.test.js.map +1 -1
  16. package/dist/serializer/normalizer/types.d.ts +3 -1
  17. package/dist/serializer/normalizer/types.d.ts.map +1 -1
  18. package/dist/validator/constraints/basic/not-blank.d.ts +3 -0
  19. package/dist/validator/constraints/basic/not-blank.d.ts.map +1 -0
  20. package/dist/validator/constraints/basic/not-blank.js +21 -0
  21. package/dist/validator/constraints/basic/not-blank.js.map +1 -0
  22. package/dist/validator/constraints/basic/not-blank.test.d.ts +2 -0
  23. package/dist/validator/constraints/basic/not-blank.test.d.ts.map +1 -0
  24. package/dist/validator/constraints/basic/not-blank.test.js +79 -0
  25. package/dist/validator/constraints/basic/not-blank.test.js.map +1 -0
  26. package/dist/validator/constraints/index.d.ts +7 -0
  27. package/dist/validator/constraints/index.d.ts.map +1 -0
  28. package/dist/validator/constraints/index.js +9 -0
  29. package/dist/validator/constraints/index.js.map +1 -0
  30. package/dist/validator/constraints/other/compound.d.ts +3 -0
  31. package/dist/validator/constraints/other/compound.d.ts.map +1 -0
  32. package/dist/validator/constraints/other/compound.integration.test.d.ts +2 -0
  33. package/dist/validator/constraints/other/compound.integration.test.d.ts.map +1 -0
  34. package/dist/validator/constraints/other/compound.integration.test.js +76 -0
  35. package/dist/validator/constraints/other/compound.integration.test.js.map +1 -0
  36. package/dist/validator/constraints/other/compound.js +6 -0
  37. package/dist/validator/constraints/other/compound.js.map +1 -0
  38. package/dist/validator/constraints/other/compound.test.d.ts +2 -0
  39. package/dist/validator/constraints/other/compound.test.d.ts.map +1 -0
  40. package/dist/validator/constraints/other/compound.test.js +62 -0
  41. package/dist/validator/constraints/other/compound.test.js.map +1 -0
  42. package/dist/validator/constraints/string/email.d.ts +3 -0
  43. package/dist/validator/constraints/string/email.d.ts.map +1 -0
  44. package/dist/validator/constraints/string/email.js +29 -0
  45. package/dist/validator/constraints/string/email.js.map +1 -0
  46. package/dist/validator/constraints/string/email.test.d.ts +2 -0
  47. package/dist/validator/constraints/string/email.test.d.ts.map +1 -0
  48. package/dist/validator/constraints/string/email.test.js +119 -0
  49. package/dist/validator/constraints/string/email.test.js.map +1 -0
  50. package/dist/validator/errors.d.ts +4 -0
  51. package/dist/validator/errors.d.ts.map +1 -0
  52. package/dist/validator/errors.js +7 -0
  53. package/dist/validator/errors.js.map +1 -0
  54. package/dist/validator/index.d.ts +4 -0
  55. package/dist/validator/index.d.ts.map +1 -0
  56. package/dist/validator/index.js +4 -0
  57. package/dist/validator/index.js.map +1 -0
  58. package/dist/validator/types.d.ts +31 -0
  59. package/dist/validator/types.d.ts.map +1 -0
  60. package/dist/validator/types.js +1 -0
  61. package/dist/validator/types.js.map +1 -0
  62. package/dist/validator/validator.d.ts +3 -0
  63. package/dist/validator/validator.d.ts.map +1 -0
  64. package/dist/validator/validator.js +59 -0
  65. package/dist/validator/validator.js.map +1 -0
  66. package/dist/validator/validator.test.d.ts +2 -0
  67. package/dist/validator/validator.test.d.ts.map +1 -0
  68. package/dist/validator/validator.test.js +281 -0
  69. package/dist/validator/validator.test.js.map +1 -0
  70. package/package.json +6 -7
@@ -0,0 +1,62 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { compound } from './compound.js';
3
+ describe('compound', () => {
4
+ it('creates a constraint that applies nested constraints from arrays', () => {
5
+ const applyConstraints = vi.fn(() => [
6
+ {
7
+ path: 'user',
8
+ constraint: 'notBlank',
9
+ message: 'Required',
10
+ value: '',
11
+ },
12
+ ]);
13
+ const nestedConstraints = ['notBlank'];
14
+ const context = {
15
+ path: 'user',
16
+ root: { user: '' },
17
+ value: '',
18
+ constraint: 'requiredEmail',
19
+ options: {},
20
+ runNestedRules: applyConstraints,
21
+ };
22
+ const requiredEmail = compound(nestedConstraints);
23
+ const violations = requiredEmail('', context);
24
+ expect(applyConstraints).toHaveBeenCalledWith('', ['notBlank'], 'user');
25
+ expect(violations).toHaveLength(1);
26
+ expect(violations[0]).toEqual({
27
+ path: 'user',
28
+ constraint: 'notBlank',
29
+ message: 'Required',
30
+ value: '',
31
+ });
32
+ });
33
+ it('creates a constraint that applies nested constraints from objects', () => {
34
+ const applyConstraints = vi.fn(() => [
35
+ {
36
+ path: 'user',
37
+ constraint: 'notNull',
38
+ message: 'Required',
39
+ value: null,
40
+ },
41
+ ]);
42
+ const context = {
43
+ path: 'user',
44
+ root: { user: null },
45
+ value: null,
46
+ constraint: 'requiredEmail',
47
+ options: {},
48
+ runNestedRules: applyConstraints,
49
+ };
50
+ const requiredEmail = compound({ notNull: {} });
51
+ const violations = requiredEmail(null, context);
52
+ expect(applyConstraints).toHaveBeenCalledWith(null, { notNull: {} }, 'user');
53
+ expect(violations).toHaveLength(1);
54
+ expect(violations[0]).toEqual({
55
+ path: 'user',
56
+ constraint: 'notNull',
57
+ message: 'Required',
58
+ value: null,
59
+ });
60
+ });
61
+ });
62
+ //# sourceMappingURL=compound.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compound.test.js","sourceRoot":"","sources":["../../../../src/validator/constraints/other/compound.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAElD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,gBAAgB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAgB,EAAE,CAAC;YAChD;gBACE,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,UAAU;gBACtB,OAAO,EAAE,UAAU;gBACnB,KAAK,EAAE,EAAE;aACV;SACF,CAAC,CAAC;QAEH,MAAM,iBAAiB,GAAG,CAAC,UAAU,CAAC,CAAC;QAEvC,MAAM,OAAO,GAAsB;YACjC,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAClB,KAAK,EAAE,EAAE;YACT,UAAU,EAAE,eAAe;YAC3B,OAAO,EAAE,EAAE;YACX,cAAc,EAAE,gBAAgB;SACjC,CAAC;QAEF,MAAM,aAAa,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAAC;QAClD,MAAM,UAAU,GAAG,aAAa,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAE9C,MAAM,CAAC,gBAAgB,CAAC,CAAC,oBAAoB,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC;QACxE,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC5B,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,UAAU;YACtB,OAAO,EAAE,UAAU;YACnB,KAAK,EAAE,EAAE;SACV,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,gBAAgB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAgB,EAAE,CAAC;YAChD;gBACE,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,SAAS;gBACrB,OAAO,EAAE,UAAU;gBACnB,KAAK,EAAE,IAAI;aACZ;SACF,CAAC,CAAC;QAEH,MAAM,OAAO,GAAsB;YACjC,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YACpB,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,eAAe;YAC3B,OAAO,EAAE,EAAE;YACX,cAAc,EAAE,gBAAgB;SACjC,CAAC;QAEF,MAAM,aAAa,GAAG,QAAQ,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEhD,MAAM,CAAC,gBAAgB,CAAC,CAAC,oBAAoB,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QAC7E,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC5B,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,SAAS;YACrB,OAAO,EAAE,UAAU;YACnB,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ConstraintContext, Violation } from '../../types.js';
2
+ export declare function email(value: unknown, context: ConstraintContext): Violation[];
3
+ //# sourceMappingURL=email.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../../../src/validator/constraints/string/email.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAWhE,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,SAAS,EAAE,CA6B7E"}
@@ -0,0 +1,29 @@
1
+ const DEFAULT_MESSAGE = 'This value is not a valid email address.';
2
+ const strictEmailRegex = /^[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/;
3
+ export function email(value, context) {
4
+ const options = context.options;
5
+ const message = options.message ?? DEFAULT_MESSAGE;
6
+ if (typeof value !== 'string') {
7
+ return [
8
+ {
9
+ path: context.path,
10
+ constraint: context.constraint,
11
+ message,
12
+ value,
13
+ },
14
+ ];
15
+ }
16
+ const normalized = options.normalizer ? options.normalizer(value) : value;
17
+ if (!strictEmailRegex.test(normalized)) {
18
+ return [
19
+ {
20
+ path: context.path,
21
+ constraint: context.constraint,
22
+ message,
23
+ value,
24
+ },
25
+ ];
26
+ }
27
+ return [];
28
+ }
29
+ //# sourceMappingURL=email.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.js","sourceRoot":"","sources":["../../../../src/validator/constraints/string/email.ts"],"names":[],"mappings":"AAEA,MAAM,eAAe,GAAG,0CAA0C,CAAC;AAOnE,MAAM,gBAAgB,GAAG,sEAAsE,CAAC;AAEhG,MAAM,UAAU,KAAK,CAAC,KAAc,EAAE,OAA0B;IAC9D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAuB,CAAC;IAChD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC;IAEnD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO;YACL;gBACE,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,OAAO;gBACP,KAAK;aACN;SACF,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAE1E,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACvC,OAAO;YACL;gBACE,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,OAAO;gBACP,KAAK;aACN;SACF,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=email.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.test.d.ts","sourceRoot":"","sources":["../../../../src/validator/constraints/string/email.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,119 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { email } from './email.js';
3
+ describe('email', () => {
4
+ it('rejects invalid email by default', () => {
5
+ const context = {
6
+ path: 'email',
7
+ root: { email: 'invalid' },
8
+ value: 'invalid',
9
+ constraint: 'email',
10
+ options: {},
11
+ runNestedRules: () => [],
12
+ };
13
+ const violations = email('invalid', context);
14
+ expect(violations).toHaveLength(1);
15
+ expect(violations[0]).toEqual({
16
+ path: 'email',
17
+ constraint: 'email',
18
+ message: 'This value is not a valid email address.',
19
+ value: 'invalid',
20
+ });
21
+ });
22
+ it('accepts valid email by default', () => {
23
+ const context = {
24
+ path: 'email',
25
+ root: { email: 'user@example.com' },
26
+ value: 'user@example.com',
27
+ constraint: 'email',
28
+ options: {},
29
+ runNestedRules: () => [],
30
+ };
31
+ const violations = email('user@example.com', context);
32
+ expect(violations).toHaveLength(0);
33
+ });
34
+ it('rejects localhost domains by default', () => {
35
+ const context = {
36
+ path: 'email',
37
+ root: { email: 'user@localhost' },
38
+ value: 'user@localhost',
39
+ constraint: 'email',
40
+ options: {},
41
+ runNestedRules: () => [],
42
+ };
43
+ const violations = email('user@localhost', context);
44
+ expect(violations).toHaveLength(1);
45
+ expect(violations[0]).toEqual({
46
+ path: 'email',
47
+ constraint: 'email',
48
+ message: 'This value is not a valid email address.',
49
+ value: 'user@localhost',
50
+ });
51
+ });
52
+ it('normalizes values before validation', () => {
53
+ const context = {
54
+ path: 'email',
55
+ root: { email: ' USER@EXAMPLE.COM ' },
56
+ value: ' USER@EXAMPLE.COM ',
57
+ constraint: 'email',
58
+ options: { normalizer: (value) => value.trim().toLowerCase() },
59
+ runNestedRules: () => [],
60
+ };
61
+ const violations = email(' USER@EXAMPLE.COM ', context);
62
+ expect(violations).toHaveLength(0);
63
+ });
64
+ it('uses a custom message when provided', () => {
65
+ const context = {
66
+ path: 'email',
67
+ root: { email: 'invalid' },
68
+ value: 'invalid',
69
+ constraint: 'email',
70
+ options: { message: 'Invalid email' },
71
+ runNestedRules: () => [],
72
+ };
73
+ const violations = email('invalid', context);
74
+ expect(violations).toHaveLength(1);
75
+ expect(violations[0]).toEqual({
76
+ path: 'email',
77
+ constraint: 'email',
78
+ message: 'Invalid email',
79
+ value: 'invalid',
80
+ });
81
+ });
82
+ it('rejects non-string values with the default message', () => {
83
+ const context = {
84
+ path: 'email',
85
+ root: { email: 42 },
86
+ value: 42,
87
+ constraint: 'email',
88
+ options: {},
89
+ runNestedRules: () => [],
90
+ };
91
+ const violations = email(42, context);
92
+ expect(violations).toHaveLength(1);
93
+ expect(violations[0]).toEqual({
94
+ path: 'email',
95
+ constraint: 'email',
96
+ message: 'This value is not a valid email address.',
97
+ value: 42,
98
+ });
99
+ });
100
+ it('uses a custom message for non-string values', () => {
101
+ const context = {
102
+ path: 'email',
103
+ root: { email: 42 },
104
+ value: 42,
105
+ constraint: 'email',
106
+ options: { message: 'Invalid email' },
107
+ runNestedRules: () => [],
108
+ };
109
+ const violations = email(42, context);
110
+ expect(violations).toHaveLength(1);
111
+ expect(violations[0]).toEqual({
112
+ path: 'email',
113
+ constraint: 'email',
114
+ message: 'Invalid email',
115
+ value: 42,
116
+ });
117
+ });
118
+ });
119
+ //# sourceMappingURL=email.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.test.js","sourceRoot":"","sources":["../../../../src/validator/constraints/string/email.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare class UnknownConstraintError extends Error {
2
+ constructor(field: string, constraint: string);
3
+ }
4
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/validator/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAI9C"}
@@ -0,0 +1,7 @@
1
+ export class UnknownConstraintError extends Error {
2
+ constructor(field, constraint) {
3
+ super(`Field "${field}" references unregistered constraint "${constraint}".`);
4
+ this.name = 'UnknownConstraintError';
5
+ }
6
+ }
7
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/validator/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,KAAa,EAAE,UAAkB;QAC3C,KAAK,CAAC,UAAU,KAAK,yCAAyC,UAAU,IAAI,CAAC,CAAC;QAC9E,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export * from './validator.js';
2
+ export * from './types.js';
3
+ export * from './constraints/index.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validator/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from './validator.js';
2
+ export * from './types.js';
3
+ export * from './constraints/index.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validator/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC"}
@@ -0,0 +1,31 @@
1
+ export type Violation = {
2
+ path: string;
3
+ message: string;
4
+ constraint: string;
5
+ value: unknown;
6
+ };
7
+ export type ConstraintOptions = {
8
+ groups?: string[];
9
+ } & Record<string, unknown>;
10
+ export type ConstraintContext = {
11
+ path: string;
12
+ root: unknown;
13
+ value: unknown;
14
+ constraint: string;
15
+ options: ConstraintOptions;
16
+ runNestedRules: (value: unknown, rules: FieldRules, path: string) => Violation[];
17
+ };
18
+ export type ConstraintValidator = (value: unknown, context: ConstraintContext) => Violation[];
19
+ export type ConstraintsMap = Record<string, ConstraintValidator>;
20
+ export type FieldRuleEntry = string | Record<string, ConstraintOptions>;
21
+ export type FieldRules = Record<string, ConstraintOptions> | FieldRuleEntry[];
22
+ export type ValidationRules = Record<string, FieldRules>;
23
+ export type ValidatorOptions = {
24
+ constraints: ConstraintsMap;
25
+ };
26
+ export type ValidateOptions = {
27
+ groups?: string[];
28
+ };
29
+ export type Payload = Record<string, unknown>;
30
+ export type Validator = (payload: Payload, rules: ValidationRules, options?: ValidateOptions) => Violation[];
31
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/validator/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAChF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,cAAc,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;CAClF,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,KAAK,SAAS,EAAE,CAAC;AAE9F,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAEjE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAExE,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,cAAc,EAAE,CAAC;AAE9E,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG;IAAE,WAAW,EAAE,cAAc,CAAA;CAAE,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEpD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,SAAS,EAAE,CAAC"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/validator/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import { Validator, ValidatorOptions } from './types.js';
2
+ export declare const createValidator: (options: ValidatorOptions) => Validator;
3
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/validator/validator.ts"],"names":[],"mappings":"AACA,OAAO,EAUL,SAAS,EACT,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAIjB,eAAO,MAAM,eAAe,GAAI,SAAS,gBAAgB,KAAG,SAc3D,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { UnknownConstraintError } from './errors.js';
2
+ export const createValidator = (options) => {
3
+ const { constraints } = options;
4
+ return function validate(payload, rules, options) {
5
+ const ruleEntries = Object.entries(rules);
6
+ // If no groups are provided, implicitly validate against "Default".
7
+ const activeGroups = options?.groups && options.groups.length > 0 ? options.groups : ['Default'];
8
+ // Apply all field rules and aggregate results.
9
+ return ruleEntries.flatMap(function (fieldEntry) {
10
+ return applyFieldRules(constraints, payload, fieldEntry, activeGroups);
11
+ });
12
+ };
13
+ };
14
+ function applyFieldRules(constraintsByName, payload, [field, rulesForField], activeGroups, currentValue) {
15
+ const isGroupActive = (groups) => groups.length === 0 || groups.some(group => activeGroups.includes(group));
16
+ const normalizedFieldRules = normalizeFieldRules(rulesForField);
17
+ return normalizedFieldRules.flatMap(([constraintName, options]) => {
18
+ // Check if the constraint should be applied based on groups.
19
+ const groups = options?.groups ?? [];
20
+ if (!isGroupActive(groups))
21
+ return [];
22
+ // Helper function to apply nested rules.
23
+ const applyNestedRules = (nextValue, rules, path) => applyFieldRules(constraintsByName, payload, [path, rules], activeGroups, nextValue);
24
+ // Find the constraint, build the context, and apply it.
25
+ const constraintValidator = resolveConstraint(constraintsByName, field, constraintName);
26
+ const value = currentValue ?? payload[field];
27
+ const context = {
28
+ path: field,
29
+ root: payload,
30
+ value,
31
+ constraint: constraintName,
32
+ options,
33
+ runNestedRules: applyNestedRules,
34
+ };
35
+ return constraintValidator(value, context);
36
+ });
37
+ }
38
+ function resolveConstraint(constraintValidatorMap, field, constraintName) {
39
+ const constraintValidator = constraintValidatorMap[constraintName];
40
+ if (!constraintValidator) {
41
+ throw new UnknownConstraintError(field, constraintName);
42
+ }
43
+ return constraintValidator;
44
+ }
45
+ function normalizeFieldRules(fieldRules) {
46
+ // Normalize rule shapes (array or map) into a consistent tuple list.
47
+ if (Array.isArray(fieldRules)) {
48
+ return fieldRules.flatMap(entry => normalizeFieldRuleEntry(entry));
49
+ }
50
+ return Object.entries(fieldRules).map(([constraintName, options]) => [constraintName, options]);
51
+ }
52
+ function normalizeFieldRuleEntry(entry) {
53
+ // Convert shorthand rule declarations into a full [name, options] tuple.
54
+ if (typeof entry === 'string') {
55
+ return [[entry, {}]];
56
+ }
57
+ return Object.entries(entry).map(([constraintName, options]) => [constraintName, options]);
58
+ }
59
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/validator/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.test.d.ts","sourceRoot":"","sources":["../../src/validator/validator.test.ts"],"names":[],"mappings":""}