@zipbul/baker 5.1.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.
Files changed (201) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/dist/index.d.ts +11 -9
  3. package/dist/index.js +1 -1
  4. package/dist/src/baker.d.ts +8 -9
  5. package/dist/src/baker.js +1 -1
  6. package/dist/src/common/enums.d.ts +10 -0
  7. package/dist/src/common/enums.js +1 -0
  8. package/dist/src/common/index.d.ts +6 -0
  9. package/dist/src/common/index.js +1 -0
  10. package/dist/src/common/interfaces.d.ts +4 -0
  11. package/dist/src/common/types.d.ts +2 -0
  12. package/dist/src/config/config-normalizer.d.ts +7 -0
  13. package/dist/src/config/config-normalizer.js +1 -0
  14. package/dist/src/config/constants.d.ts +6 -0
  15. package/dist/src/config/constants.js +1 -0
  16. package/dist/src/config/index.d.ts +3 -0
  17. package/dist/src/config/index.js +1 -0
  18. package/dist/src/{configure.d.ts → config/interfaces.d.ts} +1 -8
  19. package/dist/src/config/interfaces.js +0 -0
  20. package/dist/src/decorators/constants.d.ts +2 -0
  21. package/dist/src/decorators/constants.js +1 -0
  22. package/dist/src/decorators/enums.d.ts +5 -0
  23. package/dist/src/decorators/enums.js +1 -0
  24. package/dist/src/decorators/field.d.ts +3 -54
  25. package/dist/src/decorators/field.js +1 -1
  26. package/dist/src/decorators/index.d.ts +2 -2
  27. package/dist/src/decorators/index.js +1 -1
  28. package/dist/src/decorators/interfaces.d.ts +50 -0
  29. package/dist/src/decorators/interfaces.js +0 -0
  30. package/dist/src/decorators/public.d.ts +2 -0
  31. package/dist/src/decorators/public.js +1 -0
  32. package/dist/src/decorators/types.d.ts +6 -0
  33. package/dist/src/decorators/types.js +0 -0
  34. package/dist/src/metadata/enums.d.ts +5 -0
  35. package/dist/src/metadata/enums.js +1 -0
  36. package/dist/src/metadata/index.d.ts +3 -0
  37. package/dist/src/metadata/index.js +1 -0
  38. package/dist/src/metadata/interfaces.d.ts +90 -0
  39. package/dist/src/metadata/interfaces.js +0 -0
  40. package/dist/src/metadata/meta-store.d.ts +49 -0
  41. package/dist/src/metadata/meta-store.js +1 -0
  42. package/dist/src/metadata/types.d.ts +10 -0
  43. package/dist/src/metadata/types.js +0 -0
  44. package/dist/src/rules/array.d.ts +2 -2
  45. package/dist/src/rules/array.js +1 -1
  46. package/dist/src/rules/binary.d.ts +2 -2
  47. package/dist/src/rules/binary.js +1 -1
  48. package/dist/src/rules/combinators.d.ts +1 -1
  49. package/dist/src/rules/combinators.js +1 -1
  50. package/dist/src/rules/common.d.ts +3 -3
  51. package/dist/src/rules/common.js +1 -1
  52. package/dist/src/rules/constants.d.ts +10 -0
  53. package/dist/src/rules/constants.js +1 -0
  54. package/dist/src/{create-rule.d.ts → rules/create-rule.d.ts} +1 -1
  55. package/dist/src/rules/create-rule.js +1 -0
  56. package/dist/src/rules/date.d.ts +1 -1
  57. package/dist/src/rules/date.js +1 -1
  58. package/dist/src/{enums.d.ts → rules/enums.d.ts} +0 -20
  59. package/dist/src/rules/enums.js +1 -0
  60. package/dist/src/rules/index.d.ts +5 -13
  61. package/dist/src/rules/index.js +1 -1
  62. package/dist/src/rules/interfaces.d.ts +43 -0
  63. package/dist/src/rules/interfaces.js +0 -0
  64. package/dist/src/rules/locales.d.ts +1 -1
  65. package/dist/src/rules/locales.js +1 -1
  66. package/dist/src/rules/number.d.ts +3 -3
  67. package/dist/src/rules/number.js +1 -1
  68. package/dist/src/rules/object.d.ts +1 -1
  69. package/dist/src/rules/object.js +1 -1
  70. package/dist/src/rules/public.d.ts +14 -0
  71. package/dist/src/rules/public.js +1 -0
  72. package/dist/src/{rule-metadata.d.ts → rules/rule-metadata.d.ts} +1 -1
  73. package/dist/src/{rule-metadata.js → rules/rule-metadata.js} +1 -1
  74. package/dist/src/{rule-plan.d.ts → rules/rule-plan.d.ts} +4 -7
  75. package/dist/src/rules/rule-plan.js +1 -0
  76. package/dist/src/rules/string-basic.d.ts +23 -0
  77. package/dist/src/rules/string-basic.js +1 -0
  78. package/dist/src/rules/string-crypto.d.ts +5 -0
  79. package/dist/src/rules/string-crypto.js +1 -0
  80. package/dist/src/rules/string-datetime.d.ts +3 -0
  81. package/dist/src/rules/string-datetime.js +1 -0
  82. package/dist/src/rules/string-encoding.d.ts +14 -0
  83. package/dist/src/rules/string-encoding.js +1 -0
  84. package/dist/src/rules/string-finance.d.ts +18 -0
  85. package/dist/src/rules/string-finance.js +10 -0
  86. package/dist/src/rules/string-format.d.ts +38 -0
  87. package/dist/src/rules/string-format.js +1 -0
  88. package/dist/src/rules/string-geo.d.ts +5 -0
  89. package/dist/src/rules/string-geo.js +1 -0
  90. package/dist/src/rules/string-identifier.d.ts +16 -0
  91. package/dist/src/rules/string-identifier.js +3 -0
  92. package/dist/src/rules/string-shared.d.ts +3 -0
  93. package/dist/src/rules/string-shared.js +1 -0
  94. package/dist/src/rules/string-width.d.ts +6 -0
  95. package/dist/src/rules/string-width.js +1 -0
  96. package/dist/src/rules/string.d.ts +14 -110
  97. package/dist/src/rules/string.js +1 -12
  98. package/dist/src/rules/typechecker.d.ts +10 -10
  99. package/dist/src/rules/typechecker.js +5 -5
  100. package/dist/src/rules/types.d.ts +26 -0
  101. package/dist/src/rules/types.js +0 -0
  102. package/dist/src/{functions → runtime}/check-call-options.d.ts +1 -1
  103. package/dist/src/runtime/check-call-options.js +1 -0
  104. package/dist/src/runtime/constants.d.ts +3 -0
  105. package/dist/src/runtime/constants.js +1 -0
  106. package/dist/src/{functions → runtime}/deserialize.d.ts +2 -3
  107. package/dist/src/runtime/deserialize.js +1 -0
  108. package/dist/src/runtime/index.d.ts +3 -0
  109. package/dist/src/runtime/index.js +1 -0
  110. package/dist/src/{functions → runtime}/serialize.d.ts +2 -2
  111. package/dist/src/runtime/serialize.js +1 -0
  112. package/dist/src/{functions → runtime}/validate.d.ts +2 -3
  113. package/dist/src/runtime/validate.js +1 -0
  114. package/dist/src/seal/async-analyzer.d.ts +20 -0
  115. package/dist/src/seal/async-analyzer.js +1 -0
  116. package/dist/src/seal/circular-analyzer.d.ts +9 -6
  117. package/dist/src/seal/circular-analyzer.js +1 -1
  118. package/dist/src/seal/circular-placeholder.d.ts +20 -0
  119. package/dist/src/seal/circular-placeholder.js +1 -0
  120. package/dist/src/seal/codegen-utils.d.ts +15 -0
  121. package/dist/src/seal/codegen-utils.js +1 -1
  122. package/dist/src/seal/compile-cache.d.ts +38 -0
  123. package/dist/src/seal/compile-cache.js +1 -0
  124. package/dist/src/seal/constants.d.ts +62 -0
  125. package/dist/src/seal/constants.js +1 -0
  126. package/dist/src/seal/deserialize-builder.d.ts +3 -6
  127. package/dist/src/seal/deserialize-builder.js +200 -263
  128. package/dist/src/seal/deserialize-codegen.d.ts +58 -0
  129. package/dist/src/seal/deserialize-codegen.js +64 -0
  130. package/dist/src/seal/enums.d.ts +1 -2
  131. package/dist/src/seal/enums.js +1 -1
  132. package/dist/src/seal/expose-validator.d.ts +2 -2
  133. package/dist/src/seal/expose-validator.js +1 -1
  134. package/dist/src/seal/index.d.ts +3 -0
  135. package/dist/src/seal/index.js +1 -0
  136. package/dist/src/seal/inheritance-merger.d.ts +18 -0
  137. package/dist/src/seal/inheritance-merger.js +1 -0
  138. package/dist/src/seal/interfaces.d.ts +89 -0
  139. package/dist/src/seal/interfaces.js +0 -0
  140. package/dist/src/seal/meta-validator.d.ts +16 -0
  141. package/dist/src/seal/meta-validator.js +1 -0
  142. package/dist/src/seal/seal.d.ts +4 -28
  143. package/dist/src/seal/seal.js +1 -1
  144. package/dist/src/seal/serialize-builder.d.ts +5 -3
  145. package/dist/src/seal/serialize-builder.js +64 -64
  146. package/dist/src/seal/type-normalizer.d.ts +9 -0
  147. package/dist/src/seal/type-normalizer.js +1 -0
  148. package/dist/src/seal/type-resolver.d.ts +2 -0
  149. package/dist/src/seal/type-resolver.js +1 -0
  150. package/dist/src/seal/types.d.ts +6 -0
  151. package/dist/src/seal/types.js +0 -0
  152. package/dist/src/transformers/{collection.transformer.d.ts → collection.d.ts} +1 -1
  153. package/dist/src/transformers/constants.d.ts +2 -0
  154. package/dist/src/transformers/constants.js +1 -0
  155. package/dist/src/transformers/{date.transformer.d.ts → date.d.ts} +1 -1
  156. package/dist/src/transformers/date.js +1 -0
  157. package/dist/src/transformers/index.d.ts +3 -8
  158. package/dist/src/transformers/index.js +1 -1
  159. package/dist/src/transformers/interfaces.d.ts +26 -0
  160. package/dist/src/transformers/interfaces.js +0 -0
  161. package/dist/src/transformers/luxon.d.ts +3 -0
  162. package/dist/src/transformers/luxon.js +1 -0
  163. package/dist/src/transformers/moment.d.ts +3 -0
  164. package/dist/src/transformers/moment.js +1 -0
  165. package/dist/src/transformers/{number.transformer.d.ts → number.d.ts} +1 -1
  166. package/dist/src/transformers/public.d.ts +7 -0
  167. package/dist/src/transformers/public.js +1 -0
  168. package/dist/src/transformers/{string.transformer.d.ts → string.d.ts} +1 -1
  169. package/dist/src/transformers/types.d.ts +3 -0
  170. package/dist/src/transformers/types.js +0 -0
  171. package/package.json +7 -7
  172. package/dist/src/collect.d.ts +0 -15
  173. package/dist/src/collect.js +0 -1
  174. package/dist/src/configure.js +0 -1
  175. package/dist/src/create-rule.js +0 -1
  176. package/dist/src/enums.js +0 -1
  177. package/dist/src/functions/check-call-options.js +0 -1
  178. package/dist/src/functions/deserialize.js +0 -1
  179. package/dist/src/functions/serialize.js +0 -1
  180. package/dist/src/functions/validate.js +0 -1
  181. package/dist/src/interfaces.d.ts +0 -32
  182. package/dist/src/meta-access.d.ts +0 -12
  183. package/dist/src/meta-access.js +0 -1
  184. package/dist/src/rule-plan.js +0 -1
  185. package/dist/src/seal/validate-meta.d.ts +0 -13
  186. package/dist/src/seal/validate-meta.js +0 -1
  187. package/dist/src/transformers/date.transformer.js +0 -1
  188. package/dist/src/transformers/luxon.transformer.d.ts +0 -8
  189. package/dist/src/transformers/luxon.transformer.js +0 -1
  190. package/dist/src/transformers/moment.transformer.d.ts +0 -7
  191. package/dist/src/transformers/moment.transformer.js +0 -1
  192. package/dist/src/types.d.ts +0 -177
  193. /package/dist/src/{errors.d.ts → common/errors.d.ts} +0 -0
  194. /package/dist/src/{errors.js → common/errors.js} +0 -0
  195. /package/dist/src/{interfaces.js → common/interfaces.js} +0 -0
  196. /package/dist/src/{types.js → common/types.js} +0 -0
  197. /package/dist/src/{utils.d.ts → common/utils.d.ts} +0 -0
  198. /package/dist/src/{utils.js → common/utils.js} +0 -0
  199. /package/dist/src/transformers/{collection.transformer.js → collection.js} +0 -0
  200. /package/dist/src/transformers/{number.transformer.js → number.js} +0 -0
  201. /package/dist/src/transformers/{string.transformer.js → string.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,89 @@
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
+
3
87
  ## 5.1.0
4
88
 
5
89
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- export { createRule } from './src/create-rule';
2
- export { Field, arrayOf } from './src/decorators/index';
3
- 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';
4
4
  export { Baker } from './src/baker';
5
- export { ExcludeMode, RequiredType } from './src/enums';
6
- export type { BakerIssue, BakerIssueSet } from './src/errors';
7
- export { isBakerIssueSet, BakerError } from './src/errors';
8
- export type { EmittableRule, Transformer, TransformParams } from './src/types';
9
- export type { BakerConfig } from './src/configure';
10
- export type { RuntimeOptions } from './src/interfaces';
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{createRule}from"./src/create-rule.js";export{Field,arrayOf}from"./src/decorators/index.js";export{Baker}from"./src/baker.js";export{ExcludeMode,RequiredType}from"./src/enums.js";export{isBakerIssueSet,BakerError}from"./src/errors.js";
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";
@@ -1,6 +1,5 @@
1
- import type { BakerConfig } from './configure';
2
- import type { BakerIssueSet } from './errors';
3
- import type { RuntimeOptions } from './interfaces';
1
+ import type { BakerIssueSet, ClassCtor, RuntimeOptions } from './common';
2
+ import type { BakerConfig } from './config';
4
3
  /**
5
4
  * A baker — an isolated registration + seal + runtime boundary. Each `new Baker()` owns its own
6
5
  * registry, config, and compiled executors, so multiple bakers in one process (or a bundler-duplicated
@@ -30,12 +29,12 @@ export declare class Baker {
30
29
  readonly Recipe: (value: Function, _context: ClassDecoratorContext) => void;
31
30
  /** Seal every root registered to this baker (and its nested DTOs) with this baker's config. */
32
31
  readonly seal: () => void;
33
- deserialize: <T>(Class: new (...args: never[]) => T, input: unknown, options?: RuntimeOptions) => T | BakerIssueSet | Promise<T | BakerIssueSet>;
34
- deserializeSync: <T>(Class: new (...args: never[]) => T, input: unknown, options?: RuntimeOptions) => T | BakerIssueSet;
35
- deserializeAsync: <T>(Class: new (...args: never[]) => T, input: unknown, options?: RuntimeOptions) => Promise<T | BakerIssueSet>;
36
- validate: <T>(Class: new (...args: never[]) => T, input: unknown, options?: RuntimeOptions) => true | BakerIssueSet | Promise<true | BakerIssueSet>;
37
- validateSync: <T>(Class: new (...args: never[]) => T, input: unknown, options?: RuntimeOptions) => true | BakerIssueSet;
38
- validateAsync: <T>(Class: new (...args: never[]) => T, input: unknown, options?: RuntimeOptions) => Promise<true | BakerIssueSet>;
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>;
39
38
  serialize: <T>(instance: T, options?: RuntimeOptions) => Record<string, unknown> | Promise<Record<string, unknown>>;
40
39
  serializeSync: <T>(instance: T, options?: RuntimeOptions) => Record<string, unknown>;
41
40
  serializeAsync: <T>(instance: T, options?: RuntimeOptions) => Promise<Record<string, unknown>>;
package/dist/src/baker.js CHANGED
@@ -1 +1 @@
1
- export class Baker{#G=new Set;#F=new Map;#H;#I=!1;constructor(j){this.#H=j===void 0?Object.freeze({}):K(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 L(`${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{normalizeConfig as K}from"./configure.js";import{BakerError as L}from"./errors.js";import{runDeserialize as M,runDeserializeSync as N,runDeserializeAsync as P}from"./functions/deserialize.js";import{resolveSerializeClass as I,runSerialize as Q,runSerializeSync as U,runSerializeAsync as W}from"./functions/serialize.js";import{runValidate as X,runValidateSync as Y,runValidateAsync as Z}from"./functions/validate.js";import{sealRegistry as _}from"./seal/seal.js";
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,4 @@
1
+ export interface RuntimeOptions {
2
+ /** Per-request groups — passed at runtime since they may vary per request */
3
+ groups?: string[];
4
+ }
@@ -0,0 +1,2 @@
1
+ /** Generic class constructor — contravariant `never[]` args accept any user constructor */
2
+ export type ClassCtor<T = object> = new (...args: never[]) => T;
@@ -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,3 @@
1
+ export { normalizeConfig } from './config-normalizer';
2
+ export { BAKER_CONFIG_KEYS } from './constants';
3
+ export type { BakerConfig } from './interfaces';
@@ -0,0 +1 @@
1
+ export{normalizeConfig}from"./config-normalizer.js";export{BAKER_CONFIG_KEYS}from"./constants.js";
@@ -1,5 +1,4 @@
1
- import type { SealOptions } from './interfaces';
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,2 @@
1
+ export declare const ARRAY_OF: unique symbol;
2
+ export declare const FIELD_OPTION_KEYS: ReadonlySet<string>;
@@ -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,5 @@
1
+ /** Direction in which a field is excluded. */
2
+ export declare enum ExcludeMode {
3
+ DeserializeOnly = "deserializeOnly",
4
+ SerializeOnly = "serializeOnly"
5
+ }
@@ -0,0 +1 @@
1
+ export var ExcludeMode;((i)=>{i.DeserializeOnly="deserializeOnly";i.SerializeOnly="serializeOnly"})(ExcludeMode||={});
@@ -1,9 +1,6 @@
1
- import type { ClassCtor, EmittableRule, Transformer } from '../types';
2
- import { ExcludeMode } from '../enums';
3
- interface ArrayOfMarker {
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{ensureMeta as L}from"../collect.js";import{Direction as h,ExcludeMode as N}from"../enums.js";import{BakerError as X}from"../errors.js";import{isAsyncFunction as P,isPromiseLike as Y}from"../utils.js";const I=Symbol.for("baker:arrayOf");function arrayOf(...q){return{rules:q,[I]:!0}}function $(q){return typeof q==="object"&&q!==null&&q[I]===!0}const j=new Set(["type","discriminator","keepDiscriminatorProperty","rules","optional","nullable","name","deserializeName","serializeName","exclude","groups","when","transform","message","context","mapValue","setValue"]);function S(q){if(typeof q==="function")return!1;if(typeof q!=="object"||q===null)return!1;if($(q))return!1;const C=Object.keys(q);if(C.length===0)return!0;return C.some((b)=>j.has(b))}function V(q,C,b){const J=b?`${C} ${b}`:C;if(typeof q==="function"){const Q=q;if(typeof Q.emit!=="function"||typeof Q.ruleName!=="string"){const G=Q.name?` Did you forget to call '${Q.name}()'? Factories must be invoked (e.g., '${Q.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.${G} 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 ${q===null?"null":typeof q}. Use createRule() or import a rule from @zipbul/baker/rules. Valid @Field forms: @Field(), @Field(rule, ...), @Field(options), @Field(rule, ..., options).`)}function z(q){if(q.length===0)return{rules:[],options:{}};if(q.length===1&&S(q[0])){const b=q[0];return{rules:b.rules??[],options:b}}const C=q[q.length-1];if(S(C)){const b=C;let J=q.slice(0,-1);if(b.rules)J=[...J,...b.rules];return{rules:J,options:b}}return{rules:q,options:{}}}function D(q,C,b){for(const J of C)if($(J))for(const H of J.rules){const Q={rule:H,each:!0};if(b.groups!==void 0)Q.groups=b.groups;if(b.message!==void 0)Q.message=b.message;if(b.context!==void 0)Q.context=b.context;q.validation.push(Q)}else{const H={rule:J};if(b.groups!==void 0)H.groups=b.groups;if(b.message!==void 0)H.message=b.message;if(b.context!==void 0)H.context=b.context;q.validation.push(H)}}function _(q,C){if(C.name){const b={name:C.name};if(C.groups!==void 0)b.groups=C.groups;q.expose.push(b)}else if(C.deserializeName||C.serializeName){if(C.deserializeName){const b={name:C.deserializeName,deserializeOnly:!0};if(C.groups!==void 0)b.groups=C.groups;q.expose.push(b)}if(C.serializeName){const b={name:C.serializeName,serializeOnly:!0};if(C.groups!==void 0)b.groups=C.groups;q.expose.push(b)}}else if(C.groups)q.expose.push({groups:C.groups});else q.expose.push({})}function w(q,C,b){const J=P(b);return{fn:(Q)=>{const G=b(Q);if(!J&&Y(G))throw new X(`@Field(${q}) ${C} transform returned Promise. Declare the transform with async if it is asynchronous.`);return G},isAsync:J}}function T(q,C,b){if(!b.transform)return;const J=Array.isArray(b.transform)?b.transform:[b.transform];for(const H of J){const Q=w(C,h.Deserialize,H.deserialize),G=w(C,h.Serialize,H.serialize);q.transform.push({fn:Q.fn,isAsync:Q.isAsync,options:{deserializeOnly:!0}},{fn:G.fn,isAsync:G.isAsync,options:{serializeOnly:!0}})}}function Field(...q){return(C,b)=>{if(b.static)throw new X("@Field cannot decorate static fields.");if(b.private)throw new X("@Field cannot decorate private fields.");if(typeof b.name==="symbol")throw new X("@Field: symbol property keys are not supported. Use a string property name.");const J=b.name,H=L(b.metadata,J),{rules:Q,options:G}=z(q);if(G.name&&(G.deserializeName||G.serializeName))throw new X(`@Field on ${J}: 'name' cannot be combined with 'deserializeName'/'serializeName'. Use one or the other.`);for(let U=0;U<Q.length;U++){const W=Q[U];if($(W))for(let Z=0;Z<W.rules.length;Z++)V(W.rules[Z],J,`arrayOf[${Z}]`);else V(W,J)}D(H,Q,G);if(G.context!==void 0)H.context=G.context;if(G.message!==void 0)H.message=G.message;if(G.optional)H.flags.isOptional=!0;if(G.nullable)H.flags.isNullable=!0;if(G.when)H.flags.validateIf=G.when;if(G.type){const U={fn:G.type};if(G.discriminator!==void 0)U.discriminator=G.discriminator;if(G.keepDiscriminatorProperty!==void 0)U.keepDiscriminatorProperty=G.keepDiscriminatorProperty;const W=G.mapValue??G.setValue;if(W!==void 0)U.collectionValue=W;H.type=U}_(H,G);if(G.exclude){if(G.exclude===!0)H.exclude={};else if(G.exclude===N.DeserializeOnly)H.exclude={deserializeOnly:!0};else if(G.exclude===N.SerializeOnly)H.exclude={serializeOnly:!0}}T(H,J,G)}}export{arrayOf,Field};
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 { Field, arrayOf } from './field';
2
- export type { FieldOptions, ArrayOfMarker } from './field';
1
+ export * from './public';
2
+ export { ExcludeMode } from './enums';
@@ -1 +1 @@
1
- export{Field,arrayOf}from"./field.js";
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,2 @@
1
+ export { Field, arrayOf } from './field';
2
+ export type { FieldOptions, ArrayOfMarker } from './interfaces';
@@ -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,5 @@
1
+ /** Collection container type for a nested field. */
2
+ export declare enum CollectionType {
3
+ Map = "Map",
4
+ Set = "Set"
5
+ }
@@ -0,0 +1 @@
1
+ export var CollectionType;((e)=>{e.Map="Map";e.Set="Set"})(CollectionType||={});
@@ -0,0 +1,3 @@
1
+ export type { RawClassMeta, RawPropertyMeta, RuleDef, TransformDef, ExposeDef, TypeDef, MessageArgs, DiscriminatorDef, } from './interfaces';
2
+ export { CollectionType } from './enums';
3
+ export { MetaStore, metaStore } from './meta-store';
@@ -0,0 +1 @@
1
+ export{CollectionType}from"./enums.js";export{MetaStore,metaStore}from"./meta-store.js";
@@ -0,0 +1,90 @@
1
+ import type { ClassCtor } from '../common';
2
+ import type { InternalRule } from '../rules';
3
+ import type { TransformFunction } from '../transformers';
4
+ import type { CollectionType } from './enums';
5
+ /** Arguments for user-defined message callback */
6
+ export interface MessageArgs {
7
+ property: string;
8
+ value: unknown;
9
+ constraints: Record<string, unknown>;
10
+ }
11
+ export interface RuleDef {
12
+ rule: InternalRule;
13
+ each?: boolean;
14
+ groups?: string[];
15
+ /** Value to include in BakerIssue.message on validation failure */
16
+ message?: string | ((args: MessageArgs) => string);
17
+ /** Arbitrary value to include in BakerIssue.context on validation failure */
18
+ context?: unknown;
19
+ }
20
+ export interface TransformDef {
21
+ fn: TransformFunction;
22
+ isAsync?: boolean;
23
+ options?: {
24
+ groups?: string[];
25
+ deserializeOnly?: boolean;
26
+ serializeOnly?: boolean;
27
+ };
28
+ }
29
+ export interface ExposeDef {
30
+ name?: string;
31
+ groups?: string[];
32
+ deserializeOnly?: boolean;
33
+ serializeOnly?: boolean;
34
+ }
35
+ export interface ExcludeDef {
36
+ deserializeOnly?: boolean;
37
+ serializeOnly?: boolean;
38
+ }
39
+ /** A polymorphic discriminator subtype mapping — a class constructor keyed by its wire name. */
40
+ export interface DiscriminatorSubType {
41
+ value: ClassCtor;
42
+ name: string;
43
+ }
44
+ /** Polymorphic discriminator config — shared single source between @Field options and the IR TypeDef. */
45
+ export interface DiscriminatorDef {
46
+ property: string;
47
+ subTypes: DiscriminatorSubType[];
48
+ }
49
+ export interface TypeDef {
50
+ fn: () => ClassCtor | ClassCtor[] | MapConstructor | SetConstructor;
51
+ discriminator?: DiscriminatorDef;
52
+ keepDiscriminatorProperty?: boolean;
53
+ /** seal-time normalization result — true if fn() returns an array */
54
+ isArray?: boolean;
55
+ /** seal-time normalization result — cached class after resolving fn() (DTOs only, excluding primitives) */
56
+ resolvedClass?: ClassCtor;
57
+ /** seal-time normalization result — Map or Set collection type */
58
+ collection?: CollectionType;
59
+ /** Nested DTO class thunk for Map value / Set element */
60
+ collectionValue?: () => ClassCtor;
61
+ /** seal-time normalization result — cached class after resolving collectionValue */
62
+ resolvedCollectionValue?: ClassCtor;
63
+ }
64
+ export interface PropertyFlags {
65
+ /** `@Field({ optional })` — skip all validation when undefined/null */
66
+ isOptional?: boolean;
67
+ /** `@Field({ nullable })` — allow and assign null, reject undefined */
68
+ isNullable?: boolean;
69
+ /** `@Field({ when })` — skip all field validation when the predicate returns false */
70
+ validateIf?: (obj: Record<string, unknown>) => boolean;
71
+ /** Seal-derived — trigger recursive validation for nested `@Field({ type })` DTOs */
72
+ validateNested?: boolean;
73
+ /** Seal-derived — validate nested DTOs per array element */
74
+ validateNestedEach?: boolean;
75
+ }
76
+ export interface RawPropertyMeta {
77
+ validation: RuleDef[];
78
+ transform: TransformDef[];
79
+ expose: ExposeDef[];
80
+ exclude: ExcludeDef | null;
81
+ type: TypeDef | null;
82
+ flags: PropertyFlags;
83
+ /** Field-level message applied to ALL failures of this field (gate/structural/required/conversion/rule) */
84
+ message?: string | ((args: MessageArgs) => string);
85
+ /** Field-level context attached to ALL failures of this field */
86
+ context?: unknown;
87
+ }
88
+ export interface RawClassMeta {
89
+ [propertyKey: string]: RawPropertyMeta;
90
+ }
File without changes
@@ -0,0 +1,49 @@
1
+ import type { RawClassMeta, RawPropertyMeta } from './interfaces';
2
+ import type { MetaObject } from './types';
3
+ import { RAW } from '../symbols';
4
+ /**
5
+ * The single boundary that bridges symbol-keyed decorator metadata to typed RAW metadata. All RAW
6
+ * access goes through this one process-wide `metaStore` instance (and, for the seal pipeline, an
7
+ * injected reference) — no other module touches `Class[Symbol.metadata][this.#rawKey]` directly. The private
8
+ * `#metaOf`/`#ensureOwnMeta` methods are the actual encapsulation; `RAW` itself is process-global
9
+ * (`Symbol.for('baker:raw')`), so the value of consolidating access here is one access protocol, not
10
+ * a "private symbol".
11
+ *
12
+ * RAW lives on the TC39 decorator metadata object (`Class[Symbol.metadata][this.#rawKey]`) — where modern field
13
+ * decorators can write (they receive `context.metadata`, never the class). Sealed executors live in
14
+ * each Baker's own map, never on the class.
15
+ */
16
+ declare class MetaStore {
17
+ #private;
18
+ constructor(rawKey?: typeof RAW);
19
+ get(cls: Function): RawClassMeta | undefined;
20
+ /**
21
+ * Test-only: like {@link get} but throws if the class has no @Field decorators. Specs use it to
22
+ * assert metadata presence; production reads go through {@link get}/{@link hasOwn}.
23
+ */
24
+ require(cls: Function): RawClassMeta;
25
+ /**
26
+ * Test-only: inject RAW metadata directly, bypassing the @Field decorator path. The seal-pipeline
27
+ * specs use this to author DTOs programmatically; production metadata is written via {@link ensure}.
28
+ */
29
+ set(cls: Function, raw: RawClassMeta): void;
30
+ /** Test-only: drop a class's own RAW slot so specs can reset state between cases. */
31
+ delete(cls: Function): void;
32
+ /**
33
+ * True only when cls has its OWN RAW slot. A subclass without its own @Field decorators resolves
34
+ * `Class[Symbol.metadata]` to the parent's metadata via the class prototype chain; the dual own-check
35
+ * keeps inheritance merging from double-counting inherited fields.
36
+ */
37
+ hasOwn(cls: Function): boolean;
38
+ /**
39
+ * The RawPropertyMeta for `key` on a decorator metadata object — creating the RAW slot and the
40
+ * per-key default meta if absent. Called by @Field, which receives `context.metadata`.
41
+ *
42
+ * The own-RAW check is required: a subclass's metadata inherits the parent's RAW via the metadata
43
+ * prototype chain, so a bare assignment would pollute the parent. A fresh own RAW (null prototype)
44
+ * keeps child fields isolated.
45
+ */
46
+ ensure(metadata: MetaObject, key: string): RawPropertyMeta;
47
+ }
48
+ export { MetaStore };
49
+ export declare const metaStore: MetaStore;
@@ -0,0 +1 @@
1
+ class MetaStore{#z;constructor(z=I){this.#z=z}#D(z){return z[Symbol.metadata]??void 0}#G(z){if(!Object.hasOwn(z,Symbol.metadata))Object.defineProperty(z,Symbol.metadata,{value:{},writable:!0,configurable:!0,enumerable:!1});return z[Symbol.metadata]}get(z){return this.#D(z)?.[this.#z]}require(z){const D=this.get(z);if(D===void 0)throw new H(`${z.name||"<anonymous>"}: class has no @Field decorators`);return D}set(z,D){this.#G(z)[this.#z]=D}delete(z){if(Object.hasOwn(z,Symbol.metadata))delete z[Symbol.metadata][this.#z]}hasOwn(z){if(!Object.hasOwn(z,Symbol.metadata))return!1;const D=z[Symbol.metadata];return D!=null&&Object.hasOwn(D,this.#z)}ensure(z,D){if(!Object.hasOwn(z,this.#z))z[this.#z]=Object.create(null);const G=z[this.#z];return G[D]??={validation:[],transform:[],expose:[],exclude:null,type:null,flags:{}}}}import{BakerError as H}from"../common/index.js";import{RAW as I}from"../symbols.js";export{MetaStore};export const metaStore=new MetaStore;