@zipbul/baker 5.0.0 → 5.2.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/CHANGELOG.md +101 -0
- package/README.md +11 -14
- package/dist/index.d.ts +11 -12
- package/dist/index.js +1 -1
- package/dist/src/baker.d.ts +19 -9
- package/dist/src/baker.js +1 -1
- package/dist/src/common/enums.d.ts +10 -0
- package/dist/src/common/enums.js +1 -0
- package/dist/src/common/index.d.ts +6 -0
- package/dist/src/common/index.js +1 -0
- package/dist/src/common/interfaces.d.ts +4 -0
- package/dist/src/common/types.d.ts +2 -0
- package/dist/src/config/config-normalizer.d.ts +7 -0
- package/dist/src/config/config-normalizer.js +1 -0
- package/dist/src/config/constants.d.ts +6 -0
- package/dist/src/config/constants.js +1 -0
- package/dist/src/config/index.d.ts +3 -0
- package/dist/src/config/index.js +1 -0
- package/dist/src/{configure.d.ts → config/interfaces.d.ts} +1 -8
- package/dist/src/config/interfaces.js +0 -0
- package/dist/src/decorators/constants.d.ts +2 -0
- package/dist/src/decorators/constants.js +1 -0
- package/dist/src/decorators/enums.d.ts +5 -0
- package/dist/src/decorators/enums.js +1 -0
- package/dist/src/decorators/field.d.ts +3 -54
- package/dist/src/decorators/field.js +1 -1
- package/dist/src/decorators/index.d.ts +2 -2
- package/dist/src/decorators/index.js +1 -1
- package/dist/src/decorators/interfaces.d.ts +50 -0
- package/dist/src/decorators/interfaces.js +0 -0
- package/dist/src/decorators/public.d.ts +2 -0
- package/dist/src/decorators/public.js +1 -0
- package/dist/src/decorators/types.d.ts +6 -0
- package/dist/src/decorators/types.js +0 -0
- package/dist/src/metadata/enums.d.ts +5 -0
- package/dist/src/metadata/enums.js +1 -0
- package/dist/src/metadata/index.d.ts +3 -0
- package/dist/src/metadata/index.js +1 -0
- package/dist/src/metadata/interfaces.d.ts +90 -0
- package/dist/src/metadata/interfaces.js +0 -0
- package/dist/src/metadata/meta-store.d.ts +49 -0
- package/dist/src/metadata/meta-store.js +1 -0
- package/dist/src/metadata/types.d.ts +10 -0
- package/dist/src/metadata/types.js +0 -0
- package/dist/src/rules/array.d.ts +2 -2
- package/dist/src/rules/array.js +1 -1
- package/dist/src/rules/binary.d.ts +2 -2
- package/dist/src/rules/binary.js +1 -1
- package/dist/src/rules/combinators.d.ts +1 -1
- package/dist/src/rules/combinators.js +1 -1
- package/dist/src/rules/common.d.ts +3 -3
- package/dist/src/rules/common.js +1 -1
- package/dist/src/rules/constants.d.ts +10 -0
- package/dist/src/rules/constants.js +1 -0
- package/dist/src/{create-rule.d.ts → rules/create-rule.d.ts} +1 -1
- package/dist/src/rules/create-rule.js +1 -0
- package/dist/src/rules/date.d.ts +1 -1
- package/dist/src/rules/date.js +1 -1
- package/dist/src/{enums.d.ts → rules/enums.d.ts} +0 -20
- package/dist/src/rules/enums.js +1 -0
- package/dist/src/rules/index.d.ts +5 -13
- package/dist/src/rules/index.js +1 -1
- package/dist/src/rules/interfaces.d.ts +43 -0
- package/dist/src/rules/interfaces.js +0 -0
- package/dist/src/rules/locales.d.ts +1 -1
- package/dist/src/rules/locales.js +1 -1
- package/dist/src/rules/number.d.ts +3 -3
- package/dist/src/rules/number.js +1 -1
- package/dist/src/rules/object.d.ts +1 -1
- package/dist/src/rules/object.js +1 -1
- package/dist/src/rules/public.d.ts +14 -0
- package/dist/src/rules/public.js +1 -0
- package/dist/src/{rule-metadata.d.ts → rules/rule-metadata.d.ts} +1 -1
- package/dist/src/{rule-metadata.js → rules/rule-metadata.js} +1 -1
- package/dist/src/{rule-plan.d.ts → rules/rule-plan.d.ts} +4 -7
- package/dist/src/rules/rule-plan.js +1 -0
- package/dist/src/rules/string-basic.d.ts +23 -0
- package/dist/src/rules/string-basic.js +1 -0
- package/dist/src/rules/string-crypto.d.ts +5 -0
- package/dist/src/rules/string-crypto.js +1 -0
- package/dist/src/rules/string-datetime.d.ts +3 -0
- package/dist/src/rules/string-datetime.js +1 -0
- package/dist/src/rules/string-encoding.d.ts +14 -0
- package/dist/src/rules/string-encoding.js +1 -0
- package/dist/src/rules/string-finance.d.ts +18 -0
- package/dist/src/rules/string-finance.js +10 -0
- package/dist/src/rules/string-format.d.ts +38 -0
- package/dist/src/rules/string-format.js +1 -0
- package/dist/src/rules/string-geo.d.ts +5 -0
- package/dist/src/rules/string-geo.js +1 -0
- package/dist/src/rules/string-identifier.d.ts +16 -0
- package/dist/src/rules/string-identifier.js +3 -0
- package/dist/src/rules/string-shared.d.ts +3 -0
- package/dist/src/rules/string-shared.js +1 -0
- package/dist/src/rules/string-width.d.ts +6 -0
- package/dist/src/rules/string-width.js +1 -0
- package/dist/src/rules/string.d.ts +14 -110
- package/dist/src/rules/string.js +1 -12
- package/dist/src/rules/typechecker.d.ts +10 -10
- package/dist/src/rules/typechecker.js +5 -5
- package/dist/src/rules/types.d.ts +26 -0
- package/dist/src/rules/types.js +0 -0
- package/dist/src/{functions → runtime}/check-call-options.d.ts +1 -1
- package/dist/src/runtime/check-call-options.js +1 -0
- package/dist/src/runtime/constants.d.ts +3 -0
- package/dist/src/runtime/constants.js +1 -0
- package/dist/src/runtime/deserialize.d.ts +6 -0
- package/dist/src/runtime/deserialize.js +1 -0
- package/dist/src/runtime/index.d.ts +3 -0
- package/dist/src/runtime/index.js +1 -0
- package/dist/src/runtime/serialize.d.ts +11 -0
- package/dist/src/runtime/serialize.js +1 -0
- package/dist/src/runtime/validate.d.ts +6 -0
- package/dist/src/runtime/validate.js +1 -0
- package/dist/src/seal/async-analyzer.d.ts +20 -0
- package/dist/src/seal/async-analyzer.js +1 -0
- package/dist/src/seal/circular-analyzer.d.ts +9 -6
- package/dist/src/seal/circular-analyzer.js +1 -1
- package/dist/src/seal/circular-placeholder.d.ts +20 -0
- package/dist/src/seal/circular-placeholder.js +1 -0
- package/dist/src/seal/codegen-utils.d.ts +15 -0
- package/dist/src/seal/codegen-utils.js +1 -1
- package/dist/src/seal/compile-cache.d.ts +38 -0
- package/dist/src/seal/compile-cache.js +1 -0
- package/dist/src/seal/constants.d.ts +62 -0
- package/dist/src/seal/constants.js +1 -0
- package/dist/src/seal/deserialize-builder.d.ts +6 -9
- package/dist/src/seal/deserialize-builder.js +199 -262
- package/dist/src/seal/deserialize-codegen.d.ts +58 -0
- package/dist/src/seal/deserialize-codegen.js +64 -0
- package/dist/src/seal/enums.d.ts +1 -2
- package/dist/src/seal/enums.js +1 -1
- package/dist/src/seal/expose-validator.d.ts +2 -2
- package/dist/src/seal/expose-validator.js +1 -1
- package/dist/src/seal/index.d.ts +3 -0
- package/dist/src/seal/index.js +1 -0
- package/dist/src/seal/inheritance-merger.d.ts +18 -0
- package/dist/src/seal/inheritance-merger.js +1 -0
- package/dist/src/seal/interfaces.d.ts +89 -0
- package/dist/src/seal/interfaces.js +0 -0
- package/dist/src/seal/meta-validator.d.ts +16 -0
- package/dist/src/seal/meta-validator.js +1 -0
- package/dist/src/seal/seal.d.ts +5 -30
- package/dist/src/seal/seal.js +1 -1
- package/dist/src/seal/serialize-builder.d.ts +6 -4
- package/dist/src/seal/serialize-builder.js +63 -63
- package/dist/src/seal/type-normalizer.d.ts +9 -0
- package/dist/src/seal/type-normalizer.js +1 -0
- package/dist/src/seal/type-resolver.d.ts +2 -0
- package/dist/src/seal/type-resolver.js +1 -0
- package/dist/src/seal/types.d.ts +6 -0
- package/dist/src/seal/types.js +0 -0
- package/dist/src/symbols.d.ts +2 -4
- package/dist/src/symbols.js +1 -1
- package/dist/src/transformers/{collection.transformer.d.ts → collection.d.ts} +1 -1
- package/dist/src/transformers/constants.d.ts +2 -0
- package/dist/src/transformers/constants.js +1 -0
- package/dist/src/transformers/{date.transformer.d.ts → date.d.ts} +1 -1
- package/dist/src/transformers/date.js +1 -0
- package/dist/src/transformers/index.d.ts +3 -8
- package/dist/src/transformers/index.js +1 -1
- package/dist/src/transformers/interfaces.d.ts +26 -0
- package/dist/src/transformers/interfaces.js +0 -0
- package/dist/src/transformers/luxon.d.ts +3 -0
- package/dist/src/transformers/luxon.js +1 -0
- package/dist/src/transformers/moment.d.ts +3 -0
- package/dist/src/transformers/moment.js +1 -0
- package/dist/src/transformers/{number.transformer.d.ts → number.d.ts} +1 -1
- package/dist/src/transformers/public.d.ts +7 -0
- package/dist/src/transformers/public.js +1 -0
- package/dist/src/transformers/{string.transformer.d.ts → string.d.ts} +1 -1
- package/dist/src/transformers/types.d.ts +3 -0
- package/dist/src/transformers/types.js +0 -0
- package/package.json +7 -7
- package/dist/src/collect.d.ts +0 -15
- package/dist/src/collect.js +0 -1
- package/dist/src/configure.js +0 -1
- package/dist/src/create-rule.js +0 -1
- package/dist/src/enums.js +0 -1
- package/dist/src/functions/check-call-options.js +0 -1
- package/dist/src/functions/deserialize.d.ts +0 -19
- package/dist/src/functions/deserialize.js +0 -1
- package/dist/src/functions/serialize.d.ts +0 -16
- package/dist/src/functions/serialize.js +0 -1
- package/dist/src/functions/validate.d.ts +0 -18
- package/dist/src/functions/validate.js +0 -1
- package/dist/src/interfaces.d.ts +0 -32
- package/dist/src/meta-access.d.ts +0 -19
- package/dist/src/meta-access.js +0 -1
- package/dist/src/rule-plan.js +0 -1
- package/dist/src/seal/validate-meta.d.ts +0 -13
- package/dist/src/seal/validate-meta.js +0 -1
- package/dist/src/transformers/date.transformer.js +0 -1
- package/dist/src/transformers/luxon.transformer.d.ts +0 -8
- package/dist/src/transformers/luxon.transformer.js +0 -1
- package/dist/src/transformers/moment.transformer.d.ts +0 -7
- package/dist/src/transformers/moment.transformer.js +0 -1
- package/dist/src/types.d.ts +0 -177
- /package/dist/src/{errors.d.ts → common/errors.d.ts} +0 -0
- /package/dist/src/{errors.js → common/errors.js} +0 -0
- /package/dist/src/{interfaces.js → common/interfaces.js} +0 -0
- /package/dist/src/{types.js → common/types.js} +0 -0
- /package/dist/src/{utils.d.ts → common/utils.d.ts} +0 -0
- /package/dist/src/{utils.js → common/utils.js} +0 -0
- /package/dist/src/transformers/{collection.transformer.js → collection.js} +0 -0
- /package/dist/src/transformers/{number.transformer.js → number.js} +0 -0
- /package/dist/src/transformers/{string.transformer.js → string.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,106 @@
|
|
|
1
1
|
# @zipbul/baker
|
|
2
2
|
|
|
3
|
+
## 5.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f768694: Fix four correctness bugs found in a package-wide audit. Two of them change observable behavior for
|
|
8
|
+
input that previously "worked", so review before upgrading:
|
|
9
|
+
|
|
10
|
+
- **`@IsEnum` with numeric enums (behavior change).** TypeScript numeric enums compile to a reverse-mapped
|
|
11
|
+
object (`{ 0: 'Inactive', 1: 'Active', Active: 1, Inactive: 0 }`), so the previous `Object.values()`
|
|
12
|
+
lookup wrongly accepted the member-_name_ strings (e.g. `'Active'`) as valid values. Values are now read
|
|
13
|
+
through the non-numeric keys, so only real members pass — correct for string, numeric, and heterogeneous
|
|
14
|
+
enums. Input that relied on the member-name strings being accepted will now be rejected.
|
|
15
|
+
|
|
16
|
+
- **`momentTransformer` parses in UTC (behavior change).** It now uses `moment.utc(value)` so a zoneless
|
|
17
|
+
datetime string resolves to the same instant on every host; previously local-time parsing made the
|
|
18
|
+
serialized output depend on the machine timezone. Zoneless inputs that were parsed in local time will now
|
|
19
|
+
be parsed as UTC. Matches `luxonTransformer`'s UTC default.
|
|
20
|
+
|
|
21
|
+
- **`luxonTransformer` invalid-date passthrough.** An unparseable date string / `Date` now passes through
|
|
22
|
+
untouched instead of being laundered into an Invalid `DateTime` (which serialized to `null` /
|
|
23
|
+
`"Invalid DateTime"` and corrupted data). Matches `momentTransformer`'s pass-through contract.
|
|
24
|
+
|
|
25
|
+
- **Per-call `groups` option validation.** A non-`string[]` `groups` value now throws a clear `BakerError`
|
|
26
|
+
at the call boundary instead of silently misbehaving inside the generated executor.
|
|
27
|
+
|
|
28
|
+
- 26e13af: Fix declared-collection element validation (RED tests added first), speed up collection `validate`, and
|
|
29
|
+
land an internal layering cleanup. One item changes observable behavior — review before upgrading:
|
|
30
|
+
|
|
31
|
+
- **Declared `@Type(() => Set)` / `@Type(() => Map)` now validate their elements (behavior change).** The
|
|
32
|
+
declared-collection codegen path hand-rolled its per-element loop separately from the canonical
|
|
33
|
+
(`type: null`) path and had three defects: a declared **Map** dropped every per-element `each` rule
|
|
34
|
+
entirely; declared Set/Map `each` rules ignored the runtime `groups` filter; and a function `message` on
|
|
35
|
+
an `each` rule received the whole collection as `value` instead of the failing element. All four sites
|
|
36
|
+
(Set/Map × deserialize/validate) now route through one shared emitter with the same rule-major ordering,
|
|
37
|
+
group filtering, per-element `value` binding, and `field[i]` paths as the canonical path. Input that was
|
|
38
|
+
silently accepted because a Map's element rules never ran will now be validated.
|
|
39
|
+
|
|
40
|
+
- **Collection `validate` is ~4.7× faster on large arrays.** The inline-nested validate path eagerly
|
|
41
|
+
allocated a per-element error-path string (`field[i].`) on every element even for valid input; it is now
|
|
42
|
+
built only at the (cold) error-push sites. A 1000-element nested-DTO `validate` drops from ~10µs to
|
|
43
|
+
~2.2µs (now on par with TypeBox and ahead of Ajv). `deserialize` and all error paths are byte-identical.
|
|
44
|
+
|
|
45
|
+
- **`createRule` is now also exported from the `@zipbul/baker/rules` subpath** (it was already exported from
|
|
46
|
+
the package root).
|
|
47
|
+
|
|
48
|
+
- **`luxonTransformer` / `momentTransformer` peer-dep error is now precise.** A genuinely-missing peer still
|
|
49
|
+
throws the "install it" `BakerError`; a peer that IS installed but throws during evaluation now surfaces
|
|
50
|
+
its real error instead of the misleading install hint.
|
|
51
|
+
|
|
52
|
+
Internal-only (no API change): the seal stage's TypeDef normalization was extracted out of the `sealOne`
|
|
53
|
+
god-function, large static lookup tables and the `string-format` validators were split into cohesive
|
|
54
|
+
modules, and several stateless helpers were simplified. Public surface is unchanged except the `createRule`
|
|
55
|
+
subpath export above (verified by an export-diff).
|
|
56
|
+
|
|
57
|
+
- 96ed92c: Fix five reproduced correctness bugs (each added as a RED test first) and unify the unknown-key failure
|
|
58
|
+
model. Several change observable behavior — review before upgrading:
|
|
59
|
+
|
|
60
|
+
- **Discriminated arrays now work (was broken).** A field typed `type: () => [Base]` with a `discriminator`
|
|
61
|
+
previously read the discriminator off the _array itself_ (`undefined`) and rejected every valid input with
|
|
62
|
+
`invalidDiscriminator`. `deserialize`/`validate` now dispatch the discriminator switch **per element**,
|
|
63
|
+
reporting nested errors at `field[i].path` and the invalid-discriminator error at the `field[i]` element
|
|
64
|
+
path. (serialize already handled arrays.)
|
|
65
|
+
|
|
66
|
+
- **serialize throws on an unmatched discriminator subtype (behavior change).** When an instance matched no
|
|
67
|
+
`instanceof` branch, serialize silently emitted the raw, un-serialized object (leaking undeclared fields).
|
|
68
|
+
It now throws a `BakerError`, symmetric with deserialize rejecting an unknown discriminator value.
|
|
69
|
+
|
|
70
|
+
- **`each` rule messages receive the failing element (behavior change).** A `message`/`context` function on
|
|
71
|
+
an `arrayOf(...)` rule was passed the whole collection as `value` while the path pointed at `field[i]`.
|
|
72
|
+
It now receives the failing element, consistent with the element-level path.
|
|
73
|
+
|
|
74
|
+
- **`isDateString` / `isISO8601({ strict: true })` leap-year for years 0–99.** Calendar validity used
|
|
75
|
+
`new Date(year, …)`, which remaps a 0–99 year argument to 1900–1999 — so `0000-02-29` (a valid leap date
|
|
76
|
+
by the 400 rule) was wrongly rejected. Now computed with the proleptic Gregorian rule for all years (and
|
|
77
|
+
without allocating a `Date`).
|
|
78
|
+
|
|
79
|
+
- **`isHash` / `isTaxId` reject an unknown algorithm/locale at construction (behavior change).** They
|
|
80
|
+
previously returned a rule that always failed at runtime; they now throw a `BakerError` when called with
|
|
81
|
+
an unsupported key, matching `isMobilePhone`/`isPostalCode`/`isIdentityCard`/`isPassportNumber`.
|
|
82
|
+
|
|
83
|
+
- **`isURL` no longer shares its default-protocols array across rules.** With default protocols, every
|
|
84
|
+
`isURL()` rule exposed the same module-level `['http','https','ftp']` array on `rule.constraints`;
|
|
85
|
+
mutating one rule's constraints would have corrupted every other. Each rule now owns an independent copy.
|
|
86
|
+
|
|
87
|
+
## 5.1.0
|
|
88
|
+
|
|
89
|
+
### Minor Changes
|
|
90
|
+
|
|
91
|
+
- 2d61542: Baker-scoped runtime: `deserialize`/`validate`/`serialize` (plus the `*Sync`/`*Async`
|
|
92
|
+
variants) are now methods on a `Baker` instance — `app.deserialize(Dto, input)`. Each
|
|
93
|
+
baker compiles its own executor per class into its own map, so the **same class sealed by
|
|
94
|
+
two bakers with different configs behaves per each baker's config** — apps in one process
|
|
95
|
+
never mix. An undecorated subclass resolves to its nearest sealed ancestor within that
|
|
96
|
+
baker. Same-config bakers transparently share one compiled executor via a `(class, config)`
|
|
97
|
+
cache (compile once, no behavior change).
|
|
98
|
+
|
|
99
|
+
**BREAKING CHANGE:** the global `deserialize`/`validate`/`serialize` functions (and their
|
|
100
|
+
`*Sync`/`*Async` variants) are removed from the package entry, along with the published
|
|
101
|
+
`SEALED` symbol (`RAW` remains on `@zipbul/baker/symbols`). Migrate `deserialize(Dto, input)`
|
|
102
|
+
to `app.deserialize(Dto, input)` on the `Baker` instance that sealed the class.
|
|
103
|
+
|
|
3
104
|
## 5.0.0
|
|
4
105
|
|
|
5
106
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Zero `reflect-metadata`. Sealed codegen.
|
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
29
|
```typescript
|
|
30
|
-
import { Baker, Field,
|
|
30
|
+
import { Baker, Field, isBakerIssueSet } from '@zipbul/baker';
|
|
31
31
|
import { isString, isNumber, isEmail, min, minLength } from '@zipbul/baker/rules';
|
|
32
32
|
|
|
33
33
|
const baker = new Baker();
|
|
@@ -43,7 +43,7 @@ class UserDto {
|
|
|
43
43
|
baker.seal();
|
|
44
44
|
|
|
45
45
|
// All rules here are sync, so deserialize returns the value directly (no await).
|
|
46
|
-
const result = deserialize(UserDto, {
|
|
46
|
+
const result = baker.deserialize(UserDto, {
|
|
47
47
|
name: 'Alice',
|
|
48
48
|
age: 30,
|
|
49
49
|
email: 'alice@test.com',
|
|
@@ -67,7 +67,7 @@ if (isBakerIssueSet(result)) {
|
|
|
67
67
|
| `@app.Recipe` | Marks a class as a DTO of that baker. Only `@Field` properties are part of the contract. |
|
|
68
68
|
| `@Field(...rules)` | Declares a validated field. Global — works with any baker. |
|
|
69
69
|
| `app.seal()` | Compiles that baker's DTOs into executor functions. Call once, at startup. |
|
|
70
|
-
| `deserialize` / `validate` / `serialize` |
|
|
70
|
+
| `app.deserialize` / `app.validate` / `app.serialize` | Run that baker's compiled executors: parse+validate, validate-only, or emit a plain object. |
|
|
71
71
|
|
|
72
72
|
> Examples below assume a `const baker = new Baker()` in scope and a single `baker.seal()` after the DTOs are defined.
|
|
73
73
|
|
|
@@ -143,8 +143,8 @@ class UserDto {
|
|
|
143
143
|
displayName!: string;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
deserialize(UserDto, input); // `ssn` is skipped
|
|
147
|
-
deserialize(UserDto, input, { groups: ['admin'] }); // `ssn` is included
|
|
146
|
+
baker.deserialize(UserDto, input); // `ssn` is skipped
|
|
147
|
+
baker.deserialize(UserDto, input, { groups: ['admin'] }); // `ssn` is included
|
|
148
148
|
```
|
|
149
149
|
|
|
150
150
|
A field with no `groups` is always included; a field tagged with `groups` participates only when a matching group is passed via [runtime options](#runtime-options). See [`RuntimeOptions`](#runtime-options) for the call-site shape.
|
|
@@ -351,9 +351,9 @@ const app = new Baker({
|
|
|
351
351
|
});
|
|
352
352
|
```
|
|
353
353
|
|
|
354
|
-
`deserialize` / `serialize` / `validate`
|
|
354
|
+
`app.deserialize` / `app.serialize` / `app.validate` run that baker's compiled executors and throw `BakerError` if the class was not sealed by this baker.
|
|
355
355
|
|
|
356
|
-
**Isolation:**
|
|
356
|
+
**Isolation:** each baker compiles its own executor per class into its own map, so the **same class sealed by two bakers behaves per each baker's config** — apps never mix. (An undecorated subclass resolves to its nearest sealed ancestor within that baker.)
|
|
357
357
|
|
|
358
358
|
### `deserialize` / `serialize` / `validate`
|
|
359
359
|
|
|
@@ -418,7 +418,7 @@ baker separates two failure modes:
|
|
|
418
418
|
- **`BakerIssueSet` (returned)** — a validation failure. `deserialize` and `validate` return it instead of throwing. Guard with `isBakerIssueSet` and read `.errors`.
|
|
419
419
|
|
|
420
420
|
```typescript
|
|
421
|
-
const result = deserialize(UserDto, input);
|
|
421
|
+
const result = baker.deserialize(UserDto, input);
|
|
422
422
|
|
|
423
423
|
if (isBakerIssueSet(result)) {
|
|
424
424
|
for (const issue of result.errors) {
|
|
@@ -445,11 +445,11 @@ Yes. If any rule or transformer is async, baker automatically detects it at seal
|
|
|
445
445
|
|
|
446
446
|
### Can I use baker with NestJS?
|
|
447
447
|
|
|
448
|
-
Yes. baker's `@Field` decorator works alongside NestJS pipes. Use `deserialize()` in a custom validation pipe.
|
|
448
|
+
Yes. baker's `@Field` decorator works alongside NestJS pipes. Use `app.deserialize()` (your `Baker` instance) in a custom validation pipe.
|
|
449
449
|
|
|
450
450
|
### How does the AOT code generation work?
|
|
451
451
|
|
|
452
|
-
Calling `app.seal()` once at startup walks the baker's DTOs (and their nested DTOs), analyzes field metadata, generates optimized JavaScript executor functions, and stores them
|
|
452
|
+
Calling `app.seal()` once at startup walks the baker's DTOs (and their nested DTOs), analyzes field metadata, generates optimized JavaScript executor functions, and stores them in that baker's map. Subsequent `app.deserialize` / `app.serialize` / `app.validate` calls run the pre-compiled functions directly. There is no auto-seal — using a DTO before `app.seal()` raises `BakerError`.
|
|
453
453
|
|
|
454
454
|
> baker builds its executors with `new Function()`. Under a strict Content-Security-Policy this requires `'unsafe-eval'`; baker will not run in environments that forbid runtime code generation.
|
|
455
455
|
|
|
@@ -457,10 +457,7 @@ Calling `app.seal()` once at startup walks the baker's DTOs (and their nested DT
|
|
|
457
457
|
|
|
458
458
|
```typescript
|
|
459
459
|
import {
|
|
460
|
-
Baker,
|
|
461
|
-
deserialize, deserializeSync, deserializeAsync,
|
|
462
|
-
validate, validateSync, validateAsync,
|
|
463
|
-
serialize, serializeSync, serializeAsync,
|
|
460
|
+
Baker, // .deserialize / .validate / .serialize (+ *Sync / *Async) live on the instance
|
|
464
461
|
createRule, Field, arrayOf, isBakerIssueSet, BakerError, RequiredType, ExcludeMode,
|
|
465
462
|
} from '@zipbul/baker';
|
|
466
463
|
import type { Transformer, TransformParams, BakerIssue, BakerIssueSet, FieldOptions, EmittableRule, RuntimeOptions, BakerConfig } from '@zipbul/baker';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export { createRule } from './src/create-rule';
|
|
5
|
-
export { Field, arrayOf } from './src/decorators/index';
|
|
6
|
-
export type { FieldOptions, ArrayOfMarker } from './src/decorators/index';
|
|
1
|
+
export { createRule } from './src/rules';
|
|
2
|
+
export { Field, arrayOf } from './src/decorators';
|
|
3
|
+
export type { FieldOptions, ArrayOfMarker } from './src/decorators';
|
|
7
4
|
export { Baker } from './src/baker';
|
|
8
|
-
export { ExcludeMode
|
|
9
|
-
export
|
|
10
|
-
export {
|
|
11
|
-
export
|
|
12
|
-
export type {
|
|
13
|
-
export type {
|
|
5
|
+
export { ExcludeMode } from './src/decorators';
|
|
6
|
+
export { RequiredType } from './src/rules';
|
|
7
|
+
export type { BakerIssue, BakerIssueSet } from './src/common';
|
|
8
|
+
export { isBakerIssueSet, BakerError } from './src/common';
|
|
9
|
+
export type { EmittableRule } from './src/rules';
|
|
10
|
+
export type { Transformer, TransformParams } from './src/transformers';
|
|
11
|
+
export type { BakerConfig } from './src/config';
|
|
12
|
+
export type { RuntimeOptions } from './src/common';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{createRule}from"./src/rules/index.js";export{Field,arrayOf}from"./src/decorators/index.js";export{Baker}from"./src/baker.js";export{ExcludeMode}from"./src/decorators/index.js";export{RequiredType}from"./src/rules/index.js";export{isBakerIssueSet,BakerError}from"./src/common/index.js";
|
package/dist/src/baker.d.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BakerIssueSet, ClassCtor, RuntimeOptions } from './common';
|
|
2
|
+
import type { BakerConfig } from './config';
|
|
2
3
|
/**
|
|
3
|
-
* A baker — an isolated registration + seal boundary. Each `new Baker()` owns its own
|
|
4
|
-
* config, so multiple bakers in one process (or a bundler-duplicated
|
|
5
|
-
* fragment each other. `@Field
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
4
|
+
* A baker — an isolated registration + seal + runtime boundary. Each `new Baker()` owns its own
|
|
5
|
+
* registry, config, and compiled executors, so multiple bakers in one process (or a bundler-duplicated
|
|
6
|
+
* copy of the library) never fragment each other. `@Field` and the rule factories stay global (they
|
|
7
|
+
* write class-intrinsic schema); registration (`Recipe`), sealing (`seal`), and running
|
|
8
|
+
* (`deserialize`/`serialize`/`validate`) all belong to the instance.
|
|
8
9
|
*
|
|
9
10
|
* ```ts
|
|
10
11
|
* const app = new Baker({ autoConvert: true });
|
|
11
12
|
* @app.Recipe class UserDto { @Field(isString) name!: string }
|
|
12
13
|
* app.seal();
|
|
13
|
-
* deserialize(UserDto, input);
|
|
14
|
+
* app.deserialize(UserDto, input);
|
|
14
15
|
* ```
|
|
15
16
|
*
|
|
16
|
-
* Isolation boundary is class identity
|
|
17
|
-
* baker's config
|
|
17
|
+
* Isolation boundary is class identity, scoped per baker: the SAME class sealed by two bakers with
|
|
18
|
+
* different configs behaves per each baker's config (each compiles its own executor into its own map).
|
|
18
19
|
*
|
|
19
20
|
* `Recipe` and `seal` are arrow-field properties, not prototype methods, by design: `@app.Recipe`
|
|
20
21
|
* is applied as a detached value (the runtime calls the decorator with no `this` receiver), so it
|
|
@@ -28,4 +29,13 @@ export declare class Baker {
|
|
|
28
29
|
readonly Recipe: (value: Function, _context: ClassDecoratorContext) => void;
|
|
29
30
|
/** Seal every root registered to this baker (and its nested DTOs) with this baker's config. */
|
|
30
31
|
readonly seal: () => void;
|
|
32
|
+
deserialize: <T>(Class: ClassCtor<T>, input: unknown, options?: RuntimeOptions) => T | BakerIssueSet | Promise<T | BakerIssueSet>;
|
|
33
|
+
deserializeSync: <T>(Class: ClassCtor<T>, input: unknown, options?: RuntimeOptions) => T | BakerIssueSet;
|
|
34
|
+
deserializeAsync: <T>(Class: ClassCtor<T>, input: unknown, options?: RuntimeOptions) => Promise<T | BakerIssueSet>;
|
|
35
|
+
validate: <T>(Class: ClassCtor<T>, input: unknown, options?: RuntimeOptions) => true | BakerIssueSet | Promise<true | BakerIssueSet>;
|
|
36
|
+
validateSync: <T>(Class: ClassCtor<T>, input: unknown, options?: RuntimeOptions) => true | BakerIssueSet;
|
|
37
|
+
validateAsync: <T>(Class: ClassCtor<T>, input: unknown, options?: RuntimeOptions) => Promise<true | BakerIssueSet>;
|
|
38
|
+
serialize: <T>(instance: T, options?: RuntimeOptions) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
39
|
+
serializeSync: <T>(instance: T, options?: RuntimeOptions) => Record<string, unknown>;
|
|
40
|
+
serializeAsync: <T>(instance: T, options?: RuntimeOptions) => Promise<Record<string, unknown>>;
|
|
31
41
|
}
|
package/dist/src/baker.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export class Baker{#
|
|
1
|
+
export class Baker{#G=new Set;#F=new Map;#H;#I=!1;constructor(j){this.#H=L(j===void 0?{}:j)}Recipe=(j,F)=>{this.#G.add(j)};seal=()=>{if(this.#I)return;_(this.#G,this.#H,this.#F);this.#I=!0};#j(j){let F=j;while(F){const H=this.#F.get(F);if(H){if(F!==j)this.#F.set(j,H);return H}const J=Object.getPrototypeOf(F);F=typeof J==="function"?J:null}const G=j.name||"<anonymous class>";throw new K(`${G} is not sealed by this baker`)}deserialize=(j,F,G)=>M(this.#j(j),F,G);deserializeSync=(j,F,G)=>N(this.#j(j),j.name,F,G);deserializeAsync=(j,F,G)=>P(this.#j(j),F,G);validate=(j,F,G)=>X(this.#j(j),F,G);validateSync=(j,F,G)=>Y(this.#j(j),j.name,F,G);validateAsync=(j,F,G)=>Z(this.#j(j),F,G);serialize=(j,F)=>Q(this.#j(I(j,"serialize")),j,F);serializeSync=(j,F)=>{const G=I(j,"serializeSync");return U(this.#j(G),G.name,j,F)};serializeAsync=(j,F)=>W(this.#j(I(j,"serializeAsync")),j,F)}import{BakerError as K}from"./common/index.js";import{normalizeConfig as L}from"./config/index.js";import{runDeserialize as M,runDeserializeSync as N,runDeserializeAsync as P,resolveSerializeClass as I,runSerialize as Q,runSerializeSync as U,runSerializeAsync as W,runValidate as X,runValidateSync as Y,runValidateAsync as Z}from"./runtime/index.js";import{sealRegistry as _}from"./seal/index.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Direction of a (de)serialization pass. */
|
|
2
|
+
export declare enum Direction {
|
|
3
|
+
Deserialize = "deserialize",
|
|
4
|
+
Serialize = "serialize"
|
|
5
|
+
}
|
|
6
|
+
/** Cached accessor a RulePlan reuses across checks. */
|
|
7
|
+
export declare enum CacheKey {
|
|
8
|
+
Length = "length",
|
|
9
|
+
Time = "time"
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var Direction;((b)=>{b.Deserialize="deserialize";b.Serialize="serialize"})(Direction||={});export var CacheKey;((b)=>{b.Length="length";b.Time="time"})(CacheKey||={});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { BakerError, isBakerIssueSet, toBakerIssueSet } from './errors';
|
|
2
|
+
export type { BakerIssue, BakerIssueSet } from './errors';
|
|
3
|
+
export { Direction, CacheKey } from './enums';
|
|
4
|
+
export type { ClassCtor } from './types';
|
|
5
|
+
export type { RuntimeOptions } from './interfaces';
|
|
6
|
+
export { isAsyncFunction, isPromiseLike } from './utils';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{BakerError,isBakerIssueSet,toBakerIssueSet}from"./errors.js";export{Direction,CacheKey}from"./enums.js";export{isAsyncFunction,isPromiseLike}from"./utils.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SealOptions } from '../seal';
|
|
2
|
+
import type { BakerConfig } from './interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Validates a {@link BakerConfig} and maps it to the internal {@link SealOptions}. Used by
|
|
5
|
+
* `new Baker(config)`. Stateless — a plain function (no instance/class needed).
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeConfig(config: BakerConfig): SealOptions;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{BakerError as v}from"../common/index.js";import{BAKER_CONFIG_KEYS as x}from"./constants.js";export function normalizeConfig(j){if(j===null||typeof j!=="object"||Array.isArray(j))throw new v(`[baker] config requires a plain object. Received: ${j===null?"null":Array.isArray(j)?"array":typeof j}.`);for(const q of Object.keys(j))if(!x.has(q))throw new v(`[baker] unknown key '${q}'. Valid keys: ${[...x].join(", ")}.`);return Object.freeze({enableImplicitConversion:j.autoConvert??!1,exposeDefaultValues:j.allowClassDefaults??!1,stopAtFirstError:j.stopAtFirstError??!1,whitelist:j.forbidUnknown??!1,debug:j.debug??!1})}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BakerConfig } from './interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* The valid {@link BakerConfig} keys. Shared single source: the ConfigNormalizer rejects unknown keys
|
|
4
|
+
* with it, and the per-call options guard (runtime) rejects a seal-time config key passed at call time.
|
|
5
|
+
*/
|
|
6
|
+
export declare const BAKER_CONFIG_KEYS: Set<keyof BakerConfig>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const BAKER_CONFIG_KEYS=new Set(["autoConvert","allowClassDefaults","stopAtFirstError","forbidUnknown","debug"]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{normalizeConfig}from"./config-normalizer.js";export{BAKER_CONFIG_KEYS}from"./constants.js";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
interface BakerConfig {
|
|
1
|
+
export interface BakerConfig {
|
|
3
2
|
/** Automatic type conversion ("123" → 123). @default false */
|
|
4
3
|
autoConvert?: boolean;
|
|
5
4
|
/** Use class default values when key is missing from input. @default false */
|
|
@@ -11,9 +10,3 @@ interface BakerConfig {
|
|
|
11
10
|
/** Include field exclusion reasons as comments in generated code. @default false */
|
|
12
11
|
debug?: boolean;
|
|
13
12
|
}
|
|
14
|
-
/**
|
|
15
|
-
* Validate a BakerConfig and map it to the internal SealOptions. Used by `new Baker(config)`.
|
|
16
|
-
*/
|
|
17
|
-
declare function normalizeConfig(config: BakerConfig): SealOptions;
|
|
18
|
-
export { normalizeConfig };
|
|
19
|
-
export type { BakerConfig };
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ARRAY_OF=Symbol.for("baker:arrayOf");export const FIELD_OPTION_KEYS=new Set(Object.keys({type:!0,discriminator:!0,keepDiscriminatorProperty:!0,rules:!0,optional:!0,nullable:!0,name:!0,deserializeName:!0,serializeName:!0,exclude:!0,groups:!0,when:!0,transform:!0,message:!0,context:!0,mapValue:!0,setValue:!0}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var ExcludeMode;((i)=>{i.DeserializeOnly="deserializeOnly";i.SerializeOnly="serializeOnly"})(ExcludeMode||={});
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
readonly [key: symbol]: true;
|
|
5
|
-
readonly rules: EmittableRule[];
|
|
6
|
-
}
|
|
1
|
+
import type { EmittableRule } from '../rules';
|
|
2
|
+
import type { ArrayOfMarker, FieldOptions } from './interfaces';
|
|
3
|
+
import type { FieldDecorator, RuleArg } from './types';
|
|
7
4
|
/**
|
|
8
5
|
* Apply rules to each element of an array.
|
|
9
6
|
*
|
|
@@ -14,54 +11,6 @@ interface ArrayOfMarker {
|
|
|
14
11
|
* ```
|
|
15
12
|
*/
|
|
16
13
|
declare function arrayOf(...rules: EmittableRule[]): ArrayOfMarker;
|
|
17
|
-
interface FieldOptions {
|
|
18
|
-
/** Nested DTO type. Thunk — supports circular references. [Dto] for arrays. */
|
|
19
|
-
type?: () => ClassCtor | ClassCtor[] | MapConstructor | SetConstructor;
|
|
20
|
-
/** Polymorphic discriminator configuration — used with type */
|
|
21
|
-
discriminator?: {
|
|
22
|
-
property: string;
|
|
23
|
-
subTypes: {
|
|
24
|
-
value: Function;
|
|
25
|
-
name: string;
|
|
26
|
-
}[];
|
|
27
|
-
};
|
|
28
|
-
/** Whether to keep the discriminator property in the result object */
|
|
29
|
-
keepDiscriminatorProperty?: boolean;
|
|
30
|
-
/** Validation rules array */
|
|
31
|
-
rules?: (EmittableRule | ArrayOfMarker)[];
|
|
32
|
-
/** Allow undefined */
|
|
33
|
-
optional?: boolean;
|
|
34
|
-
/** Allow null */
|
|
35
|
-
nullable?: boolean;
|
|
36
|
-
/** JSON key mapping (bidirectional) */
|
|
37
|
-
name?: string;
|
|
38
|
-
/** Deserialize direction key mapping (cannot be used with name) */
|
|
39
|
-
deserializeName?: string;
|
|
40
|
-
/** Serialize direction key mapping (cannot be used with name) */
|
|
41
|
-
serializeName?: string;
|
|
42
|
-
/** Field exclusion — true: bidirectional, 'deserializeOnly': deserialization only, 'serializeOnly': serialization only */
|
|
43
|
-
exclude?: boolean | ExcludeMode;
|
|
44
|
-
/** Groups — field visibility control + conditional validation rule application */
|
|
45
|
-
groups?: string[];
|
|
46
|
-
/** Conditional validation — skip all field validation when false */
|
|
47
|
-
when?: (obj: Record<string, unknown>) => boolean;
|
|
48
|
-
/** Transformer or array of transformers (serialize direction applies in reverse order) */
|
|
49
|
-
transform?: Transformer | Transformer[];
|
|
50
|
-
/** Error message on validation failure — applied to all rules of the field (rule's own message takes precedence) */
|
|
51
|
-
message?: string | ((args: {
|
|
52
|
-
property: string;
|
|
53
|
-
value: unknown;
|
|
54
|
-
constraints: Record<string, unknown>;
|
|
55
|
-
}) => string);
|
|
56
|
-
/** Error context on validation failure — applied to all rules of the field (rule's own context takes precedence) */
|
|
57
|
-
context?: unknown;
|
|
58
|
-
/** Nested DTO class thunk for Map values — used with type: () => Map */
|
|
59
|
-
mapValue?: () => ClassCtor;
|
|
60
|
-
/** Nested DTO class thunk for Set elements — used with type: () => Set */
|
|
61
|
-
setValue?: () => ClassCtor;
|
|
62
|
-
}
|
|
63
|
-
type RuleArg = EmittableRule | ArrayOfMarker;
|
|
64
|
-
type FieldDecorator = (value: undefined, context: ClassFieldDecoratorContext) => void;
|
|
65
14
|
/** `@Field`() — empty field registration */
|
|
66
15
|
declare function Field(): FieldDecorator;
|
|
67
16
|
/** `@Field`(isString(), email()) — variadic rules */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Direction as I,BakerError as X,isAsyncFunction as j,isPromiseLike as z}from"../common/index.js";import{metaStore as _}from"../metadata/index.js";import{ARRAY_OF as h,FIELD_OPTION_KEYS as w}from"./constants.js";import{ExcludeMode as L}from"./enums.js";function arrayOf(...b){return{rules:b,[h]:!0}}function V(b){return typeof b==="object"&&b!==null&&b[h]===!0}function P(b){if(typeof b==="function")return!1;if(typeof b!=="object"||b===null)return!1;if(V(b))return!1;const q=Object.keys(b);if(q.length===0)return!0;return q.some((H)=>w.has(H))}function S(b,q,H){const J=H?`${q} ${H}`:q;if(typeof b==="function"){const U=b;if(typeof U.emit!=="function"||typeof U.ruleName!=="string"){const C=U.name?` Did you forget to call '${U.name}()'? Factories must be invoked (e.g., '${U.name}()'). Rule constants are passed directly (e.g., 'isString' without parentheses).`:" Use createRule() or import a rule from @zipbul/baker/rules.";throw new X(`@Field on ${J}: argument is not a baker rule.${C} Valid @Field forms: @Field(), @Field(rule, ...), @Field(options), @Field(rule, ..., options).`)}return}throw new X(`@Field on ${J}: expected a baker rule (function with .emit and .ruleName), got ${b===null?"null":typeof b}. Use createRule() or import a rule from @zipbul/baker/rules. Valid @Field forms: @Field(), @Field(rule, ...), @Field(options), @Field(rule, ..., options).`)}function M(b){if(b.length===0)return{rules:[],options:{}};if(b.length===1&&P(b[0])){const H=b[0];return{rules:H.rules??[],options:H}}const q=b[b.length-1];if(P(q)){const H=q;let J=b.slice(0,-1);if(H.rules)J=[...J,...H.rules];return{rules:J,options:H}}return{rules:b,options:{}}}function G(b,q){if(q.groups!==void 0)b.groups=q.groups;if(q.message!==void 0)b.message=q.message;if(q.context!==void 0)b.context=q.context;return b}function N(b,q){if(q.groups!==void 0)b.groups=q.groups;return b}function T(b,q,H){for(const J of q)if(V(J))for(const Q of J.rules)b.validation.push(G({rule:Q,each:!0},H));else b.validation.push(G({rule:J},H))}function D(b,q){if(q.name)b.expose.push(N({name:q.name},q));else if(q.deserializeName||q.serializeName){if(q.deserializeName)b.expose.push(N({name:q.deserializeName,deserializeOnly:!0},q));if(q.serializeName)b.expose.push(N({name:q.serializeName,serializeOnly:!0},q))}else if(q.groups)b.expose.push({groups:q.groups});else b.expose.push({})}function Y(b,q,H){const J=j(H);return{fn:(U)=>{const C=H(U);if(!J&&z(C))throw new X(`@Field(${b}) ${q} transform returned Promise. Declare the transform with async if it is asynchronous.`);return C},isAsync:J}}function B(b,q,H){if(!H.transform)return;const J=Array.isArray(H.transform)?H.transform:[H.transform];for(const Q of J){const U=Y(q,I.Deserialize,Q.deserialize),C=Y(q,I.Serialize,Q.serialize);b.transform.push({fn:U.fn,isAsync:U.isAsync,options:{deserializeOnly:!0}},{fn:C.fn,isAsync:C.isAsync,options:{serializeOnly:!0}})}}function Field(...b){return(q,H)=>{if(H.static)throw new X("@Field cannot decorate static fields.");if(H.private)throw new X("@Field cannot decorate private fields.");if(typeof H.name==="symbol")throw new X("@Field: symbol property keys are not supported. Use a string property name.");const J=H.name,Q=_.ensure(H.metadata,J),{rules:U,options:C}=M(b);if(C.name&&(C.deserializeName||C.serializeName))throw new X(`@Field on ${J}: 'name' cannot be combined with 'deserializeName'/'serializeName'. Use one or the other.`);if(C.mapValue!==void 0&&C.setValue!==void 0)throw new X(`@Field on ${J}: 'mapValue' and 'setValue' cannot both be set \u2014 use 'mapValue' for a Map value type and 'setValue' for a Set element type.`);for(let W=0;W<U.length;W++){const Z=U[W];if(V(Z))for(let $=0;$<Z.rules.length;$++)S(Z.rules[$],J,`arrayOf[${$}]`);else S(Z,J)}T(Q,U,C);if(C.context!==void 0)Q.context=C.context;if(C.message!==void 0)Q.message=C.message;if(C.optional)Q.flags.isOptional=!0;if(C.nullable)Q.flags.isNullable=!0;if(C.when)Q.flags.validateIf=C.when;if(C.type){const W={fn:C.type};if(C.discriminator!==void 0)W.discriminator=C.discriminator;if(C.keepDiscriminatorProperty!==void 0)W.keepDiscriminatorProperty=C.keepDiscriminatorProperty;const Z=C.mapValue??C.setValue;if(Z!==void 0)W.collectionValue=Z;Q.type=W}D(Q,C);if(C.exclude){if(C.exclude===!0)Q.exclude={};else if(C.exclude===L.DeserializeOnly)Q.exclude={deserializeOnly:!0};else if(C.exclude===L.SerializeOnly)Q.exclude={serializeOnly:!0}}B(Q,J,C)}}export{arrayOf,Field};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from './public';
|
|
2
|
+
export { ExcludeMode } from './enums';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export*from"./public.js";export{ExcludeMode}from"./enums.js";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ClassCtor } from '../common';
|
|
2
|
+
import type { DiscriminatorDef } from '../metadata';
|
|
3
|
+
import type { EmittableRule } from '../rules';
|
|
4
|
+
import type { Transformer } from '../transformers';
|
|
5
|
+
import type { ExcludeMode } from './enums';
|
|
6
|
+
import { ARRAY_OF } from './constants';
|
|
7
|
+
export interface ArrayOfMarker {
|
|
8
|
+
readonly [ARRAY_OF]: true;
|
|
9
|
+
readonly rules: EmittableRule[];
|
|
10
|
+
}
|
|
11
|
+
export interface FieldOptions {
|
|
12
|
+
/** Nested DTO type. Thunk — supports circular references. [Dto] for arrays. */
|
|
13
|
+
type?: () => ClassCtor | ClassCtor[] | MapConstructor | SetConstructor;
|
|
14
|
+
/** Polymorphic discriminator configuration — used with type */
|
|
15
|
+
discriminator?: DiscriminatorDef;
|
|
16
|
+
/** Whether to keep the discriminator property in the result object */
|
|
17
|
+
keepDiscriminatorProperty?: boolean;
|
|
18
|
+
/** Validation rules array */
|
|
19
|
+
rules?: (EmittableRule | ArrayOfMarker)[];
|
|
20
|
+
/** Allow undefined */
|
|
21
|
+
optional?: boolean;
|
|
22
|
+
/** Allow null */
|
|
23
|
+
nullable?: boolean;
|
|
24
|
+
/** JSON key mapping (bidirectional) */
|
|
25
|
+
name?: string;
|
|
26
|
+
/** Deserialize direction key mapping (cannot be used with name) */
|
|
27
|
+
deserializeName?: string;
|
|
28
|
+
/** Serialize direction key mapping (cannot be used with name) */
|
|
29
|
+
serializeName?: string;
|
|
30
|
+
/** Field exclusion — true: bidirectional, 'deserializeOnly': deserialization only, 'serializeOnly': serialization only */
|
|
31
|
+
exclude?: boolean | ExcludeMode;
|
|
32
|
+
/** Groups — field visibility control + conditional validation rule application */
|
|
33
|
+
groups?: string[];
|
|
34
|
+
/** Conditional validation — skip all field validation when false */
|
|
35
|
+
when?: (obj: Record<string, unknown>) => boolean;
|
|
36
|
+
/** Transformer or array of transformers (serialize direction applies in reverse order) */
|
|
37
|
+
transform?: Transformer | Transformer[];
|
|
38
|
+
/** Error message on validation failure — applied to all rules of the field (rule's own message takes precedence) */
|
|
39
|
+
message?: string | ((args: {
|
|
40
|
+
property: string;
|
|
41
|
+
value: unknown;
|
|
42
|
+
constraints: Record<string, unknown>;
|
|
43
|
+
}) => string);
|
|
44
|
+
/** Error context on validation failure — applied to all rules of the field (rule's own context takes precedence) */
|
|
45
|
+
context?: unknown;
|
|
46
|
+
/** Nested DTO class thunk for Map values — used with type: () => Map */
|
|
47
|
+
mapValue?: () => ClassCtor;
|
|
48
|
+
/** Nested DTO class thunk for Set elements — used with type: () => Set */
|
|
49
|
+
setValue?: () => ClassCtor;
|
|
50
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Field,arrayOf}from"./field.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { EmittableRule } from '../rules';
|
|
2
|
+
import type { ArrayOfMarker } from './interfaces';
|
|
3
|
+
/** A positional @Field argument — either a rule or an arrayOf(...) element-rules marker. */
|
|
4
|
+
export type RuleArg = EmittableRule | ArrayOfMarker;
|
|
5
|
+
/** The class-field decorator @Field returns — TC39 field decorators receive `undefined` as the value. */
|
|
6
|
+
export type FieldDecorator = (value: undefined, context: ClassFieldDecoratorContext) => void;
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var CollectionType;((e)=>{e.Map="Map";e.Set="Set"})(CollectionType||={});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{CollectionType}from"./enums.js";export{MetaStore,metaStore}from"./meta-store.js";
|