@cqrs-ddd/pipeline-zod 0.5.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/COMMERCIAL_LICENSE.txt +34 -0
- package/LICENSE +661 -0
- package/README.md +33 -0
- package/dist/create-zod-mapper.d.ts +37 -0
- package/dist/create-zod-mapper.d.ts.map +1 -0
- package/dist/create-zod-mapper.js +37 -0
- package/dist/create-zod-mapper.js.map +1 -0
- package/dist/create-zod-request.d.ts +142 -0
- package/dist/create-zod-request.d.ts.map +1 -0
- package/dist/create-zod-request.js +175 -0
- package/dist/create-zod-request.js.map +1 -0
- package/dist/errors/zod-validation.error.d.ts +17 -0
- package/dist/errors/zod-validation.error.d.ts.map +1 -0
- package/dist/errors/zod-validation.error.js +21 -0
- package/dist/errors/zod-validation.error.js.map +1 -0
- package/dist/helpers/request-output.d.ts +15 -0
- package/dist/helpers/request-output.d.ts.map +1 -0
- package/dist/helpers/request-output.js +34 -0
- package/dist/helpers/request-output.js.map +1 -0
- package/dist/helpers/validated.intent.d.ts +17 -0
- package/dist/helpers/validated.intent.d.ts.map +1 -0
- package/dist/helpers/validated.intent.js +18 -0
- package/dist/helpers/validated.intent.js.map +1 -0
- package/dist/helpers/zod-data.helpers.d.ts +40 -0
- package/dist/helpers/zod-data.helpers.d.ts.map +1 -0
- package/dist/helpers/zod-data.helpers.js +195 -0
- package/dist/helpers/zod-data.helpers.js.map +1 -0
- package/dist/http.d.ts +19 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +24 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/updatable.d.ts +44 -0
- package/dist/updatable.d.ts.map +1 -0
- package/dist/updatable.js +76 -0
- package/dist/updatable.js.map +1 -0
- package/dist/zod-validation.behavior.d.ts +85 -0
- package/dist/zod-validation.behavior.d.ts.map +1 -0
- package/dist/zod-validation.behavior.js +115 -0
- package/dist/zod-validation.behavior.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @cqrs-ddd/pipeline-zod
|
|
2
|
+
|
|
3
|
+
Zod validation for `@cqrs-ddd/pipeline`. `validated(schema)` checks a wrapped function's
|
|
4
|
+
input and hands the function the parsed copy (coerced values, defaults, unknown keys
|
|
5
|
+
removed) without changing the caller's value; `createCommand()`, `createQuery()` and
|
|
6
|
+
`createZodRequest()` build request classes that carry their schema, validated in place by
|
|
7
|
+
`ZodValidationBehavior`. `toHttpResponse()` from `@cqrs-ddd/pipeline-zod/http` turns a
|
|
8
|
+
`ZodValidationError` into a 400 answer.
|
|
9
|
+
|
|
10
|
+
**Documentation:** [guide](https://aristoteliss.github.io/ddd-cqrs/packages/pipeline-zod/) · [API reference](https://aristoteliss.github.io/ddd-cqrs/api/cqrs-ddd/pipeline-zod/) · [all packages](https://aristoteliss.github.io/ddd-cqrs/)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @cqrs-ddd/pipeline-zod @cqrs-ddd/pipeline zod
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires Node.js 22.12 or later. Published as an ES module; a CommonJS application loads
|
|
19
|
+
it with `require()`.
|
|
20
|
+
|
|
21
|
+
## Example
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
export const order = pipeline.wrap(
|
|
25
|
+
{ name: 'order', kind: 'command' },
|
|
26
|
+
validated(z.object({ sku: z.string(), qty: z.coerce.number().int().positive() })),
|
|
27
|
+
)(async (input) => orders.place(input));
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
Dual-licensed under **AGPLv3** and a **Commercial License**. See `LICENSE` and
|
|
33
|
+
`COMMERCIAL_LICENSE.txt`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ZodType } from 'zod';
|
|
2
|
+
/** A schema-backed mapper returned by {@link createZodMapper}. */
|
|
3
|
+
export interface ZodMapper<TInput, TOutput> {
|
|
4
|
+
/** The schema the mapper parses with, for reuse (e.g. `.extend(...)`). */
|
|
5
|
+
readonly schema: ZodType<TOutput, TInput>;
|
|
6
|
+
/**
|
|
7
|
+
* Parses `input` and returns the schema output, including any `.transform()`.
|
|
8
|
+
*
|
|
9
|
+
* @throws ZodValidationError when parsing fails.
|
|
10
|
+
*/
|
|
11
|
+
map(input: TInput): TOutput;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a mapper that parses input through a Zod schema, typically turning a
|
|
15
|
+
* validated request body into a command at the HTTP edge.
|
|
16
|
+
*
|
|
17
|
+
* It parses synchronously, so the schema must not use async refinements or transforms. A
|
|
18
|
+
* failure throws `ZodValidationError`, whose `details` hold `{ formErrors, fieldErrors }`;
|
|
19
|
+
* `toHttpResponse` of `@cqrs-ddd/pipeline-zod/http` answers it with HTTP 400.
|
|
20
|
+
*
|
|
21
|
+
* @param schema - Schema whose output is the mapped value.
|
|
22
|
+
* @returns A {@link ZodMapper} over `schema`.
|
|
23
|
+
* @example Map a request body to a command
|
|
24
|
+
* ```ts
|
|
25
|
+
* export const CreateUserMapper = createZodMapper(
|
|
26
|
+
* CreateUserDtoSchema.transform(
|
|
27
|
+
* ({ name, email }) => new CreateUserCommand({ username: name, email }),
|
|
28
|
+
* ),
|
|
29
|
+
* );
|
|
30
|
+
*
|
|
31
|
+
* app.post('/users', async (req, res) => {
|
|
32
|
+
* res.json(await commandBus.execute(CreateUserMapper.map(req.body)));
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function createZodMapper<TInput, TOutput>(schema: ZodType<TOutput, TInput>): ZodMapper<TInput, TOutput>;
|
|
37
|
+
//# sourceMappingURL=create-zod-mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-zod-mapper.d.ts","sourceRoot":"","sources":["../src/create-zod-mapper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAGnC,kEAAkE;AAClE,MAAM,WAAW,SAAS,CAAC,MAAM,EAAE,OAAO;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAC7C,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,GAC/B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAS5B"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
|
+
import { ZodValidationError } from './errors/zod-validation.error.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a mapper that parses input through a Zod schema, typically turning a
|
|
5
|
+
* validated request body into a command at the HTTP edge.
|
|
6
|
+
*
|
|
7
|
+
* It parses synchronously, so the schema must not use async refinements or transforms. A
|
|
8
|
+
* failure throws `ZodValidationError`, whose `details` hold `{ formErrors, fieldErrors }`;
|
|
9
|
+
* `toHttpResponse` of `@cqrs-ddd/pipeline-zod/http` answers it with HTTP 400.
|
|
10
|
+
*
|
|
11
|
+
* @param schema - Schema whose output is the mapped value.
|
|
12
|
+
* @returns A {@link ZodMapper} over `schema`.
|
|
13
|
+
* @example Map a request body to a command
|
|
14
|
+
* ```ts
|
|
15
|
+
* export const CreateUserMapper = createZodMapper(
|
|
16
|
+
* CreateUserDtoSchema.transform(
|
|
17
|
+
* ({ name, email }) => new CreateUserCommand({ username: name, email }),
|
|
18
|
+
* ),
|
|
19
|
+
* );
|
|
20
|
+
*
|
|
21
|
+
* app.post('/users', async (req, res) => {
|
|
22
|
+
* res.json(await commandBus.execute(CreateUserMapper.map(req.body)));
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function createZodMapper(schema) {
|
|
27
|
+
return {
|
|
28
|
+
schema,
|
|
29
|
+
map(input) {
|
|
30
|
+
const result = schema.safeParse(input);
|
|
31
|
+
if (!result.success)
|
|
32
|
+
throw new ZodValidationError(result.error);
|
|
33
|
+
return result.data;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=create-zod-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-zod-mapper.js","sourceRoot":"","sources":["../src/create-zod-mapper.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAGtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AActE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAgC;IAEhC,OAAO;QACL,MAAM;QACN,GAAG,CAAC,KAAa;YACf,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACvC,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChE,OAAO,MAAM,CAAC,IAAe,CAAC;QAChC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { ZodType, z } from 'zod';
|
|
2
|
+
import { ZOD_SCHEMA_KEY } from './zod-validation.behavior.js';
|
|
3
|
+
/**
|
|
4
|
+
* Constructor shape accepted as the optional base class for generated requests.
|
|
5
|
+
*
|
|
6
|
+
* The instance side defaults to `object` so generated request properties remain
|
|
7
|
+
* strongly typed. Constructor arguments stay unconstrained so a base class can
|
|
8
|
+
* take any constructor parameters.
|
|
9
|
+
*/
|
|
10
|
+
export type AbstractConstructor<T = object> = abstract new (...args: any[]) => T;
|
|
11
|
+
/**
|
|
12
|
+
* Extracts the input type of a command or query class generated from a Zod schema.
|
|
13
|
+
*/
|
|
14
|
+
export type InferInput<T> = T extends {
|
|
15
|
+
readonly [ZOD_SCHEMA_KEY]: infer S;
|
|
16
|
+
} ? S extends ZodType ? z.input<S> : never : never;
|
|
17
|
+
/**
|
|
18
|
+
* Extracts the output (parsed) type of a command or query class generated from a Zod schema.
|
|
19
|
+
*/
|
|
20
|
+
export type InferOutput<T> = T extends {
|
|
21
|
+
readonly [ZOD_SCHEMA_KEY]: infer S;
|
|
22
|
+
} ? S extends ZodType ? z.output<S> : never : never;
|
|
23
|
+
export type ZodRequestClass<TSchema extends ZodType, TBase extends AbstractConstructor> = {
|
|
24
|
+
new (input: z.input<TSchema>, ...baseArgs: ConstructorParameters<TBase>): InstanceType<TBase> & z.output<TSchema>;
|
|
25
|
+
readonly [ZOD_SCHEMA_KEY]: TSchema;
|
|
26
|
+
readonly schema: TSchema;
|
|
27
|
+
readonly '~standard': unknown;
|
|
28
|
+
parse(input: z.input<TSchema>, ...baseArgs: ConstructorParameters<TBase>): InstanceType<TBase> & z.output<TSchema>;
|
|
29
|
+
/**
|
|
30
|
+
* Validates asynchronously, then constructs the request instance.
|
|
31
|
+
*
|
|
32
|
+
* Use this for schemas with async refinements/transforms. The normal
|
|
33
|
+
* constructor and {@link parse} are synchronous and therefore require a
|
|
34
|
+
* synchronously parseable schema.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const command = await CreateUserCommand.parseAsync(input);
|
|
39
|
+
* await commandBus.execute(command);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
parseAsync(input: z.input<TSchema>, ...baseArgs: ConstructorParameters<TBase>): Promise<InstanceType<TBase> & z.output<TSchema>>;
|
|
43
|
+
safeParse(input: unknown): ReturnType<TSchema['safeParse']>;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Generates a strongly-typed Command, Query, or Event class from a Zod schema.
|
|
47
|
+
*
|
|
48
|
+
* Compatible with NestJS CQRS, Standard Schema, and {@link ZodValidationBehavior}:
|
|
49
|
+
* - Attaches the schema as static `_zodSchema` (`ZOD_SCHEMA_KEY`) and `schema`.
|
|
50
|
+
* - Forwards Standard Schema specification (`~standard`) for schema interoperability.
|
|
51
|
+
* - Provides static `parse()` and `safeParse()` directly on the class.
|
|
52
|
+
* - Inherits from an optional base class, preserving its prototype,
|
|
53
|
+
* instanceof checks, and constructor arguments.
|
|
54
|
+
* - Validates input and applies transformations (e.g. email trimming / lowercasing) on construction,
|
|
55
|
+
* throwing {@link ZodValidationError} on invalid payload.
|
|
56
|
+
* - Safely assigns transformed output properties onto the instance including own keys whose parsed value is `undefined`.
|
|
57
|
+
*
|
|
58
|
+
* @example Defining a Command with a base class
|
|
59
|
+
* ```ts
|
|
60
|
+
* abstract class AppCommand {
|
|
61
|
+
* constructor(readonly actorId?: string) {}
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* const CreateUserSchema = z.object({
|
|
65
|
+
* username: z.string().min(3),
|
|
66
|
+
* email: z.email(),
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* export class CreateUserCommand extends createCommand(CreateUserSchema, AppCommand) {}
|
|
70
|
+
* const command = new CreateUserCommand(input, actorId);
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example Defining a Query without a base class
|
|
74
|
+
* ```ts
|
|
75
|
+
* const GetUserSchema = z.object({ id: z.uuid() });
|
|
76
|
+
* export class GetUserQuery extends createQuery(GetUserSchema) {}
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare function createZodRequest<TSchema extends ZodType, TBase extends AbstractConstructor = AbstractConstructor>(schema: TSchema, Base?: TBase): ZodRequestClass<TSchema, TBase>;
|
|
80
|
+
export type ZodCommandClass<TSchema extends ZodType, TBase extends AbstractConstructor> = ZodRequestClass<TSchema, TBase> & {
|
|
81
|
+
readonly requestKind: 'command';
|
|
82
|
+
/**
|
|
83
|
+
* The top-level fields marked with {@link updatable}, in shape order and
|
|
84
|
+
* frozen: the list of fields to pass to field-level authorization.
|
|
85
|
+
*/
|
|
86
|
+
readonly updatableFields: readonly (keyof z.output<TSchema> & string)[];
|
|
87
|
+
};
|
|
88
|
+
export type ZodQueryClass<TSchema extends ZodType, TBase extends AbstractConstructor> = ZodRequestClass<TSchema, TBase> & {
|
|
89
|
+
readonly requestKind: 'query';
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Generates a strongly-typed CQRS Command class from a Zod schema.
|
|
93
|
+
*
|
|
94
|
+
* Automatically marks the class with `requestKind = 'command'`,
|
|
95
|
+
* attaches the Zod schema as static `_zodSchema`, forwards Standard Schema metadata (`~standard`),
|
|
96
|
+
* lists the fields marked with {@link updatable} as static `updatableFields`,
|
|
97
|
+
* and preserves any Base class inheritance.
|
|
98
|
+
*
|
|
99
|
+
* @example Defining a Command with a base class
|
|
100
|
+
* ```ts
|
|
101
|
+
* abstract class AppCommand {
|
|
102
|
+
* constructor(readonly actorId?: string) {}
|
|
103
|
+
* }
|
|
104
|
+
*
|
|
105
|
+
* const CreateUserSchema = z.object({
|
|
106
|
+
* username: z.string().min(3),
|
|
107
|
+
* email: z.email(),
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* export class CreateUserCommand extends createCommand(CreateUserSchema, AppCommand) {}
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @example Declaring the fields an update command changes
|
|
114
|
+
* ```ts
|
|
115
|
+
* export class UpdateRoleCommand extends createCommand(
|
|
116
|
+
* z.object({ id: z.uuid(), name: z.string().trim().apply(updatable).min(3) }),
|
|
117
|
+
* ) {}
|
|
118
|
+
*
|
|
119
|
+
* // The fields to pass to field-level authorization:
|
|
120
|
+
* UpdateRoleCommand.updatableFields; // ['name']
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export declare function createCommand<TSchema extends ZodType, TBase extends AbstractConstructor = AbstractConstructor>(schema: TSchema, Base?: TBase): ZodCommandClass<TSchema, TBase>;
|
|
124
|
+
/**
|
|
125
|
+
* Generates a strongly-typed CQRS Query class from a Zod schema.
|
|
126
|
+
*
|
|
127
|
+
* Automatically marks the class with `requestKind = 'query'`,
|
|
128
|
+
* attaches the Zod schema as static `_zodSchema`, forwards Standard Schema metadata (`~standard`),
|
|
129
|
+
* and preserves the prototype and constructor arguments of an optional base class.
|
|
130
|
+
*
|
|
131
|
+
* @example Defining a Query with a base class
|
|
132
|
+
* ```ts
|
|
133
|
+
* abstract class AppQuery {
|
|
134
|
+
* constructor(readonly actorId?: string) {}
|
|
135
|
+
* }
|
|
136
|
+
*
|
|
137
|
+
* const GetUserSchema = z.object({ id: z.uuid() });
|
|
138
|
+
* export class GetUserQuery extends createQuery(GetUserSchema, AppQuery) {}
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export declare function createQuery<TSchema extends ZodType, TBase extends AbstractConstructor = AbstractConstructor>(schema: TSchema, Base?: TBase): ZodQueryClass<TSchema, TBase>;
|
|
142
|
+
//# sourceMappingURL=create-zod-request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-zod-request.d.ts","sourceRoot":"","sources":["../src/create-zod-request.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
|
+
import { ZodValidationError } from './errors/zod-validation.error.js';
|
|
3
|
+
import { assertPlainRequestOutput, defineEnumerableDataProperties, } from './helpers/request-output.js';
|
|
4
|
+
import { setRawInput, setValidatedData } from './helpers/zod-data.helpers.js';
|
|
5
|
+
import { updatableFieldsOf } from './updatable.js';
|
|
6
|
+
import { ZOD_SCHEMA_KEY } from './zod-validation.behavior.js';
|
|
7
|
+
/**
|
|
8
|
+
* Marker for an instance being built from data that has already been validated.
|
|
9
|
+
*
|
|
10
|
+
* A symbol key cannot collide with a real schema property, so the constructor
|
|
11
|
+
* can tell a pre-validated hand-off from ordinary input without a sentinel that
|
|
12
|
+
* a caller could forge by accident.
|
|
13
|
+
*/
|
|
14
|
+
const PRE_VALIDATED = Symbol('createZodRequest.preValidated');
|
|
15
|
+
function isPreValidated(value) {
|
|
16
|
+
return typeof value === 'object' && value !== null && PRE_VALIDATED in value;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generates a strongly-typed Command, Query, or Event class from a Zod schema.
|
|
20
|
+
*
|
|
21
|
+
* Compatible with NestJS CQRS, Standard Schema, and {@link ZodValidationBehavior}:
|
|
22
|
+
* - Attaches the schema as static `_zodSchema` (`ZOD_SCHEMA_KEY`) and `schema`.
|
|
23
|
+
* - Forwards Standard Schema specification (`~standard`) for schema interoperability.
|
|
24
|
+
* - Provides static `parse()` and `safeParse()` directly on the class.
|
|
25
|
+
* - Inherits from an optional base class, preserving its prototype,
|
|
26
|
+
* instanceof checks, and constructor arguments.
|
|
27
|
+
* - Validates input and applies transformations (e.g. email trimming / lowercasing) on construction,
|
|
28
|
+
* throwing {@link ZodValidationError} on invalid payload.
|
|
29
|
+
* - Safely assigns transformed output properties onto the instance including own keys whose parsed value is `undefined`.
|
|
30
|
+
*
|
|
31
|
+
* @example Defining a Command with a base class
|
|
32
|
+
* ```ts
|
|
33
|
+
* abstract class AppCommand {
|
|
34
|
+
* constructor(readonly actorId?: string) {}
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* const CreateUserSchema = z.object({
|
|
38
|
+
* username: z.string().min(3),
|
|
39
|
+
* email: z.email(),
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* export class CreateUserCommand extends createCommand(CreateUserSchema, AppCommand) {}
|
|
43
|
+
* const command = new CreateUserCommand(input, actorId);
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example Defining a Query without a base class
|
|
47
|
+
* ```ts
|
|
48
|
+
* const GetUserSchema = z.object({ id: z.uuid() });
|
|
49
|
+
* export class GetUserQuery extends createQuery(GetUserSchema) {}
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function createZodRequest(schema, Base) {
|
|
53
|
+
const Parent = (Base ?? class {
|
|
54
|
+
});
|
|
55
|
+
class Request extends Parent {
|
|
56
|
+
static [ZOD_SCHEMA_KEY] = schema;
|
|
57
|
+
static schema = schema;
|
|
58
|
+
static parse(input, ...baseArgs) {
|
|
59
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic subclass instantiation for polymorphic parse()
|
|
60
|
+
const Target = this;
|
|
61
|
+
return new Target(input, ...baseArgs);
|
|
62
|
+
}
|
|
63
|
+
static async parseAsync(input, ...baseArgs) {
|
|
64
|
+
const result = await schema.safeParseAsync(input);
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
throw new ZodValidationError(result.error);
|
|
67
|
+
}
|
|
68
|
+
assertPlainRequestOutput(result.data, 'parseAsync');
|
|
69
|
+
// biome-ignore lint/complexity/noThisInStatic: dynamic subclass instantiation for polymorphic parseAsync()
|
|
70
|
+
const Target = this;
|
|
71
|
+
return new Target({ [PRE_VALIDATED]: result.data, rawInput: input }, ...baseArgs);
|
|
72
|
+
}
|
|
73
|
+
static safeParse(input) {
|
|
74
|
+
return schema.safeParse(input);
|
|
75
|
+
}
|
|
76
|
+
constructor(input, ...baseArgs) {
|
|
77
|
+
super(...baseArgs);
|
|
78
|
+
let data;
|
|
79
|
+
let rawInput;
|
|
80
|
+
if (isPreValidated(input)) {
|
|
81
|
+
// Handed over by parseAsync(), which has already validated and asserted
|
|
82
|
+
// the output shape. Re-running safeParse here would defeat the point:
|
|
83
|
+
// an async schema cannot be parsed synchronously at all.
|
|
84
|
+
data = input[PRE_VALIDATED];
|
|
85
|
+
rawInput = input.rawInput;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const result = schema.safeParse(input);
|
|
89
|
+
if (!result.success) {
|
|
90
|
+
throw new ZodValidationError(result.error);
|
|
91
|
+
}
|
|
92
|
+
assertPlainRequestOutput(result.data, 'constructor');
|
|
93
|
+
data = result.data;
|
|
94
|
+
rawInput = input;
|
|
95
|
+
}
|
|
96
|
+
setRawInput(this, rawInput);
|
|
97
|
+
defineEnumerableDataProperties(this, data);
|
|
98
|
+
setValidatedData(this, schema, data);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const standard = schema['~standard'];
|
|
102
|
+
if (standard !== undefined) {
|
|
103
|
+
Object.defineProperty(Request, '~standard', {
|
|
104
|
+
value: standard,
|
|
105
|
+
writable: false,
|
|
106
|
+
enumerable: true,
|
|
107
|
+
configurable: true,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return Request;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Generates a strongly-typed CQRS Command class from a Zod schema.
|
|
114
|
+
*
|
|
115
|
+
* Automatically marks the class with `requestKind = 'command'`,
|
|
116
|
+
* attaches the Zod schema as static `_zodSchema`, forwards Standard Schema metadata (`~standard`),
|
|
117
|
+
* lists the fields marked with {@link updatable} as static `updatableFields`,
|
|
118
|
+
* and preserves any Base class inheritance.
|
|
119
|
+
*
|
|
120
|
+
* @example Defining a Command with a base class
|
|
121
|
+
* ```ts
|
|
122
|
+
* abstract class AppCommand {
|
|
123
|
+
* constructor(readonly actorId?: string) {}
|
|
124
|
+
* }
|
|
125
|
+
*
|
|
126
|
+
* const CreateUserSchema = z.object({
|
|
127
|
+
* username: z.string().min(3),
|
|
128
|
+
* email: z.email(),
|
|
129
|
+
* });
|
|
130
|
+
*
|
|
131
|
+
* export class CreateUserCommand extends createCommand(CreateUserSchema, AppCommand) {}
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* @example Declaring the fields an update command changes
|
|
135
|
+
* ```ts
|
|
136
|
+
* export class UpdateRoleCommand extends createCommand(
|
|
137
|
+
* z.object({ id: z.uuid(), name: z.string().trim().apply(updatable).min(3) }),
|
|
138
|
+
* ) {}
|
|
139
|
+
*
|
|
140
|
+
* // The fields to pass to field-level authorization:
|
|
141
|
+
* UpdateRoleCommand.updatableFields; // ['name']
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export function createCommand(schema, Base) {
|
|
145
|
+
const RequestClass = createZodRequest(schema, Base);
|
|
146
|
+
Object.assign(RequestClass, { requestKind: 'command' });
|
|
147
|
+
Object.defineProperty(RequestClass, 'updatableFields', {
|
|
148
|
+
value: updatableFieldsOf(schema),
|
|
149
|
+
enumerable: true,
|
|
150
|
+
});
|
|
151
|
+
return RequestClass;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Generates a strongly-typed CQRS Query class from a Zod schema.
|
|
155
|
+
*
|
|
156
|
+
* Automatically marks the class with `requestKind = 'query'`,
|
|
157
|
+
* attaches the Zod schema as static `_zodSchema`, forwards Standard Schema metadata (`~standard`),
|
|
158
|
+
* and preserves the prototype and constructor arguments of an optional base class.
|
|
159
|
+
*
|
|
160
|
+
* @example Defining a Query with a base class
|
|
161
|
+
* ```ts
|
|
162
|
+
* abstract class AppQuery {
|
|
163
|
+
* constructor(readonly actorId?: string) {}
|
|
164
|
+
* }
|
|
165
|
+
*
|
|
166
|
+
* const GetUserSchema = z.object({ id: z.uuid() });
|
|
167
|
+
* export class GetUserQuery extends createQuery(GetUserSchema, AppQuery) {}
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
export function createQuery(schema, Base) {
|
|
171
|
+
const RequestClass = createZodRequest(schema, Base);
|
|
172
|
+
Object.assign(RequestClass, { requestKind: 'query' });
|
|
173
|
+
return RequestClass;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=create-zod-request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-zod-request.js","sourceRoot":"","sources":["../src/create-zod-request.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ZodError } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a command, query, or event fails Zod validation: by
|
|
4
|
+
* {@link ZodValidationBehavior}, and by the constructor, `parse()`, and
|
|
5
|
+
* `parseAsync()` of a class generated with {@link createZodRequest}.
|
|
6
|
+
*
|
|
7
|
+
* This is framework-agnostic — it carries serializable `details` so the HTTP
|
|
8
|
+
* layer (or any transport layer) can map it to the appropriate response format.
|
|
9
|
+
*
|
|
10
|
+
* `toHttpResponse` of `@cqrs-ddd/pipeline-zod/http` maps it to HTTP 400.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ZodValidationError extends Error {
|
|
13
|
+
/** Structured details from {@link ZodError.flatten}. */
|
|
14
|
+
readonly details: ReturnType<ZodError['flatten']>;
|
|
15
|
+
constructor(error: ZodError);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=zod-validation.error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-validation.error.d.ts","sourceRoot":"","sources":["../../src/errors/zod-validation.error.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAE/B;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,wDAAwD;IACxD,SAAgB,OAAO,EAAE,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAEzD,YAAY,KAAK,EAAE,QAAQ,EAI1B;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a command, query, or event fails Zod validation: by
|
|
4
|
+
* {@link ZodValidationBehavior}, and by the constructor, `parse()`, and
|
|
5
|
+
* `parseAsync()` of a class generated with {@link createZodRequest}.
|
|
6
|
+
*
|
|
7
|
+
* This is framework-agnostic — it carries serializable `details` so the HTTP
|
|
8
|
+
* layer (or any transport layer) can map it to the appropriate response format.
|
|
9
|
+
*
|
|
10
|
+
* `toHttpResponse` of `@cqrs-ddd/pipeline-zod/http` maps it to HTTP 400.
|
|
11
|
+
*/
|
|
12
|
+
export class ZodValidationError extends Error {
|
|
13
|
+
/** Structured details from {@link ZodError.flatten}. */
|
|
14
|
+
details;
|
|
15
|
+
constructor(error) {
|
|
16
|
+
super('Validation failed');
|
|
17
|
+
this.name = 'ZodValidationError';
|
|
18
|
+
this.details = error.flatten();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=zod-validation.error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-validation.error.js","sourceRoot":"","sources":["../../src/errors/zod-validation.error.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAItE;;;;;;;;;GASG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,wDAAwD;IACxC,OAAO,CAAkC;IAEzD,YAAY,KAAe;QACzB,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Returns true when a parsed top-level value can be applied in-place to a CQRS request instance. */
|
|
2
|
+
export declare function isPlainRequestOutput(value: unknown): value is Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* Pipeline Zod requests preserve the original class instance, so a top-level
|
|
5
|
+
* schema transform must still produce a plain record. Arrays, primitives, Date,
|
|
6
|
+
* Map, class instances, etc. cannot be copied onto that instance faithfully.
|
|
7
|
+
*
|
|
8
|
+
* Keeping this assertion shared between the generated request constructor and
|
|
9
|
+
* {@link ZodValidationBehavior} prevents the two validation paths from accepting
|
|
10
|
+
* different top-level output shapes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function assertPlainRequestOutput(value: unknown, source: 'constructor' | 'parseAsync' | 'behavior'): asserts value is Record<string, unknown>;
|
|
13
|
+
/** Applies every parsed own enumerable key, including explicit undefined. */
|
|
14
|
+
export declare function defineEnumerableDataProperties(target: object, source: Record<string, unknown>): void;
|
|
15
|
+
//# sourceMappingURL=request-output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-output.d.ts","sourceRoot":"","sources":["../../src/helpers/request-output.ts"],"names":[],"mappings":"AAEA,qGAAqG;AACrG,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIlC;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,aAAa,GAAG,YAAY,GAAG,UAAU,GAChD,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAK1C;AAED,6EAA6E;AAC7E,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI,CASN"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
|
+
/** Returns true when a parsed top-level value can be applied in-place to a CQRS request instance. */
|
|
3
|
+
export function isPlainRequestOutput(value) {
|
|
4
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
5
|
+
return false;
|
|
6
|
+
const prototype = Object.getPrototypeOf(value);
|
|
7
|
+
return prototype === Object.prototype || prototype === null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Pipeline Zod requests preserve the original class instance, so a top-level
|
|
11
|
+
* schema transform must still produce a plain record. Arrays, primitives, Date,
|
|
12
|
+
* Map, class instances, etc. cannot be copied onto that instance faithfully.
|
|
13
|
+
*
|
|
14
|
+
* Keeping this assertion shared between the generated request constructor and
|
|
15
|
+
* {@link ZodValidationBehavior} prevents the two validation paths from accepting
|
|
16
|
+
* different top-level output shapes.
|
|
17
|
+
*/
|
|
18
|
+
export function assertPlainRequestOutput(value, source) {
|
|
19
|
+
if (isPlainRequestOutput(value))
|
|
20
|
+
return;
|
|
21
|
+
throw new TypeError(`${source === 'behavior' ? 'ZodValidationBehavior' : `createZodRequest.${source}`} requires the top-level parsed output to be a plain object so it can be applied to the existing pipeline request instance.`);
|
|
22
|
+
}
|
|
23
|
+
/** Applies every parsed own enumerable key, including explicit undefined. */
|
|
24
|
+
export function defineEnumerableDataProperties(target, source) {
|
|
25
|
+
for (const key of Object.keys(source)) {
|
|
26
|
+
Object.defineProperty(target, key, {
|
|
27
|
+
value: source[key],
|
|
28
|
+
writable: true,
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=request-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-output.js","sourceRoot":"","sources":["../../src/helpers/request-output.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,qGAAqG;AACrG,MAAM,UAAU,oBAAoB,CAClC,KAAc;IAEd,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9E,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC;AAC9D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAc,EACd,MAAiD;IAEjD,IAAI,oBAAoB,CAAC,KAAK,CAAC;QAAE,OAAO;IACxC,MAAM,IAAI,SAAS,CACjB,GAAG,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,oBAAoB,MAAM,EAAE,4HAA4H,CAC9M,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,8BAA8B,CAC5C,MAAc,EACd,MAA+B;IAE/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE;YACjC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;YAClB,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PipelineBehaviorTuple } from '@cqrs-ddd/pipeline';
|
|
2
|
+
import type { ZodType } from 'zod';
|
|
3
|
+
import { ZodValidationBehavior, type ZodValidationOptions } from '../zod-validation.behavior.js';
|
|
4
|
+
/**
|
|
5
|
+
* The entry that validates an operation's input against `schema` and hands the handler
|
|
6
|
+
* the parsed copy.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* export const order = pipeline.wrap(
|
|
11
|
+
* { name: 'order', kind: 'command' },
|
|
12
|
+
* validated(z.object({ sku: z.string(), qty: z.coerce.number().int().positive() })),
|
|
13
|
+
* )(async (input) => orders.place(input));
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function validated(schema: ZodType): PipelineBehaviorTuple<ZodValidationBehavior, ZodValidationOptions>;
|
|
17
|
+
//# sourceMappingURL=validated.intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validated.intent.d.ts","sourceRoot":"","sources":["../../src/helpers/validated.intent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,EACL,qBAAqB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,+BAA+B,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,OAAO,GACd,qBAAqB,CAAC,qBAAqB,EAAE,oBAAoB,CAAC,CAEpE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
|
+
import { ZodValidationBehavior, } from '../zod-validation.behavior.js';
|
|
3
|
+
/**
|
|
4
|
+
* The entry that validates an operation's input against `schema` and hands the handler
|
|
5
|
+
* the parsed copy.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* export const order = pipeline.wrap(
|
|
10
|
+
* { name: 'order', kind: 'command' },
|
|
11
|
+
* validated(z.object({ sku: z.string(), qty: z.coerce.number().int().positive() })),
|
|
12
|
+
* )(async (input) => orders.place(input));
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function validated(schema) {
|
|
16
|
+
return [ZodValidationBehavior, { schema }];
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=validated.intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validated.intent.js","sourceRoot":"","sources":["../../src/helpers/validated.intent.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAItE,OAAO,EACL,qBAAqB,GAEtB,MAAM,+BAA+B,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CACvB,MAAe;IAEf,OAAO,CAAC,qBAAqB,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ZodType } from 'zod';
|
|
2
|
+
export declare const ZOD_RAW_INPUT_KEY: unique symbol;
|
|
3
|
+
export declare const ZOD_VALIDATED_DATA_KEY: unique symbol;
|
|
4
|
+
/** The schema a request was last parsed with, and the payload that parse produced. */
|
|
5
|
+
interface ValidationState {
|
|
6
|
+
readonly schema: ZodType;
|
|
7
|
+
readonly snapshot: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
/** Records constructor input, including explicitly supplied null or undefined. */
|
|
10
|
+
export declare function setRawInput(request: object, rawInput: unknown): void;
|
|
11
|
+
/** Returns original constructor input by reference, or the request when no input was recorded. */
|
|
12
|
+
export declare function getRawInput<T = unknown>(request: unknown): T | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Records the parsed payload a request now carries, detached from the live
|
|
15
|
+
* instance. The snapshot covers exactly the keys the schema produced, so fields
|
|
16
|
+
* the request class owns itself are neither tracked nor removed later.
|
|
17
|
+
*/
|
|
18
|
+
export declare function setValidatedData(request: object, schema: ZodType, data: Record<string, unknown>): void;
|
|
19
|
+
/** Internal validation provenance; caller-supplied symbol metadata cannot establish it. */
|
|
20
|
+
export declare function getValidationState(request: object): ValidationState | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Returns a detached copy of the parsed payload for inspection. Its top level is
|
|
23
|
+
* frozen and every call returns a fresh copy, so changing it cannot change the
|
|
24
|
+
* validation state the behavior relies on. Without an internal validation
|
|
25
|
+
* record, it falls back to a value stored under `ZOD_VALIDATED_DATA_KEY`; that
|
|
26
|
+
* value is returned as data but never establishes a trusted validation result.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getValidatedData<T = Record<string, unknown>>(request: unknown): T | undefined;
|
|
29
|
+
/** Clones enumerable data, rich collections and binary values, preserving cycles and shared references. */
|
|
30
|
+
export declare function cloneData<T>(data: T): T;
|
|
31
|
+
/** Structural data equality with one-to-one collection matching and cycle detection. */
|
|
32
|
+
export declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Reports whether the request still carries the payload it was validated with.
|
|
35
|
+
* Only the schema-produced fields are compared: class-owned fields and
|
|
36
|
+
* non-enumerable CQRS/session metadata are outside the validated payload.
|
|
37
|
+
*/
|
|
38
|
+
export declare function hasBeenMutated(request: Record<string, unknown>, snapshot: Record<string, unknown>): boolean;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=zod-data.helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-data.helpers.d.ts","sourceRoot":"","sources":["../../src/helpers/zod-data.helpers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEnC,eAAO,MAAM,iBAAiB,eAAiD,CAAC;AAChF,eAAO,MAAM,sBAAsB,eAElC,CAAC;AAEF,sFAAsF;AACtF,UAAU,eAAe;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAKD,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAEpE;AAED,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,GAAG,SAAS,CAQxE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,IAAI,CAKN;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,GACd,eAAe,GAAG,SAAS,CAE7B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,OAAO,EAAE,OAAO,GACf,CAAC,GAAG,SAAS,CAQf;AAED,2GAA2G;AAC3G,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAEvC;AAsED,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAEzD;AA+ED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAKT"}
|