@cleverbrush/mapper 4.5.0 → 5.1.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/LICENSE +28 -0
- package/README.md +35 -2
- package/dist/MappingRegistry.d.ts +92 -26
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2024 by Andrew Zolotuhkin <andrew_zol@cleverbrush.com>
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
9
|
+
this list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
|
16
|
+
may be used to endorse or promote products derived from this software
|
|
17
|
+
without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://github.com/cleverbrush/framework/actions/workflows/ci.yml)
|
|
4
4
|
[](../../LICENSE)
|
|
5
5
|
<!-- coverage-badge-start -->
|
|
6
|
-

|
|
7
7
|
<!-- coverage-badge-end -->
|
|
8
8
|
|
|
9
9
|
A type-safe, declarative object mapper for converting objects between different `@cleverbrush/schema` representations. Uses PropertyDescriptors as pointers to properties (similar to expressions in C# .NET) and enforces **compile-time completeness** — TypeScript will produce an error if any target property is not mapped, auto-mapped, or explicitly ignored.
|
|
@@ -86,7 +86,7 @@ const dto = await mapFn({
|
|
|
86
86
|
1. **Define schemas** — use `@cleverbrush/schema` to define source and target shapes
|
|
87
87
|
2. **Configure mappings** — use `.for()` to select a target property, then `.from()`, `.compute()`, or `.ignore()` to define how it's populated
|
|
88
88
|
3. **Auto-mapping fills the gaps** — properties with the same name and compatible type are mapped automatically
|
|
89
|
-
4. **Get a mapper function** — `
|
|
89
|
+
4. **Get a mapper function** — use `getMapper(from, to)` for an async function or `getSyncMapper(from, to)` when all steps are synchronous
|
|
90
90
|
5. **TypeScript enforces completeness** — if any target property is unmapped, you get a compile-time error
|
|
91
91
|
|
|
92
92
|
## Compile-Time Safety
|
|
@@ -225,6 +225,32 @@ Every non-auto-mappable target property must be either mapped or explicitly igno
|
|
|
225
225
|
- A **compile-time TypeScript type error** — a type-assignability mismatch that includes the unmapped property names in the type parameters
|
|
226
226
|
- A **runtime `MapperConfigurationError`** if type checks are bypassed
|
|
227
227
|
|
|
228
|
+
## Synchronous mapping
|
|
229
|
+
|
|
230
|
+
Use the same `configure()` API and retrieve a synchronous mapper when every final
|
|
231
|
+
step and nested mapping is synchronous:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
const registry = mapper().configure(Source, Target, m => m
|
|
235
|
+
.for(t => t.label).compute(s => s.name.toUpperCase()));
|
|
236
|
+
const toTarget = registry.getSyncMapper(Source, Target);
|
|
237
|
+
const results = rows.map(toTarget); // ordinary values, not promises
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Completeness checking, `.from()`, ignores, nested objects/arrays and compatible
|
|
241
|
+
auto-mapping still apply. Async computations or nested async mappings reject
|
|
242
|
+
`getSyncMapper()` at compile time; runtime guards cover JavaScript and unsafe
|
|
243
|
+
casts too. Callbacks are never probed during configuration. A function falsely
|
|
244
|
+
typed as synchronous that returns a promise/thenable throws when invoked.
|
|
245
|
+
`getMapper()` still always returns an async function. There is no separate
|
|
246
|
+
`configureSync()` API.
|
|
247
|
+
|
|
248
|
+
Configure once and reuse the same source/target schema instances. Queries can
|
|
249
|
+
provide their projection schema through `.rowSchema`; see the
|
|
250
|
+
[projection-aware read guide](../knex-schema/README.md#projection-aware-reads), including separate
|
|
251
|
+
definition/mapping/service files and explicit polymorphic dispatch. The mapper
|
|
252
|
+
performs no database calls or application enrichment.
|
|
253
|
+
|
|
228
254
|
## API
|
|
229
255
|
|
|
230
256
|
### `mapper()`
|
|
@@ -252,6 +278,13 @@ const mapFn = registry.getMapper(ApiUser, DomainUser);
|
|
|
252
278
|
const result = await mapFn(sourceObject);
|
|
253
279
|
```
|
|
254
280
|
|
|
281
|
+
### `registry.getSyncMapper(fromSchema, toSchema)`
|
|
282
|
+
|
|
283
|
+
Retrieves an ordinary function for a complete, synchronous mapping. It returns
|
|
284
|
+
the target value directly and can be passed to `rows.map(...)`. Async final steps
|
|
285
|
+
or nested async mappings are rejected. See [Synchronous mapping](#synchronous-mapping)
|
|
286
|
+
for runtime guards and schema identity requirements.
|
|
287
|
+
|
|
255
288
|
### `Mapper`
|
|
256
289
|
|
|
257
290
|
A fluent builder for configuring how each target property is populated:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type InferType, ObjectSchemaBuilder, type PropertyDescriptor, type PropertyDescriptorTree, type SchemaBuilder, SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR } from '@cleverbrush/schema';
|
|
2
2
|
/**
|
|
3
3
|
* Internal symbol used to brand target property descriptors with their key.
|
|
4
4
|
* Using a symbol instead of a string property keeps it out of IntelliSense.
|
|
@@ -9,14 +9,49 @@ declare const SYMBOL_TARGET_PROPERTY_KEY: unique symbol;
|
|
|
9
9
|
* unmapped target properties. Using a symbol keeps it out of IntelliSense.
|
|
10
10
|
*/
|
|
11
11
|
declare const SYMBOL_UNMAPPED: unique symbol;
|
|
12
|
+
declare const SYMBOL_STEPS: unique symbol;
|
|
13
|
+
type Step<T, K extends string, Async extends boolean> = Omit<T, K> & Record<K, Async>;
|
|
14
|
+
type IsAsync<T> = 0 extends 1 & T ? true : [Extract<T, PromiseLike<any>>] extends [never] ? false : true;
|
|
15
|
+
type ArraySchemaShape = {
|
|
16
|
+
introspect(): {
|
|
17
|
+
elementSchema: unknown;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
type ObjectSchemaShape = {
|
|
21
|
+
introspect(): {
|
|
22
|
+
properties: unknown;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
type SameProperties<A, B> = [keyof A, keyof B] extends [keyof B, keyof A] ? [A, B] extends [B, A] ? true : false : false;
|
|
26
|
+
type RegisteredMatch<S, T, Registered> = S extends ObjectSchemaShape ? T extends ObjectSchemaShape ? Registered extends [infer From, infer To] ? [InferType<S>, InferType<T>] extends [
|
|
27
|
+
InferType<From>,
|
|
28
|
+
InferType<To>
|
|
29
|
+
] ? [InferType<From>, InferType<To>] extends [
|
|
30
|
+
InferType<S>,
|
|
31
|
+
InferType<T>
|
|
32
|
+
] ? SameProperties<ExtractSchemaProperties<S>, ExtractSchemaProperties<From>> extends true ? SameProperties<ExtractSchemaProperties<T>, ExtractSchemaProperties<To>> : false : false : false : false : false : false;
|
|
33
|
+
type NestedAsync<S, T, Registered> = S extends ArraySchemaShape ? T extends ArraySchemaShape ? NestedAsync<ExtractArrayElementSchema<S>, ExtractArrayElementSchema<T>, Registered> : false : true extends RegisteredMatch<S, T, Registered> ? true : false;
|
|
34
|
+
type AsyncKeys<S, T, Registered, Steps> = {
|
|
35
|
+
[K in keyof ExtractSchemaProperties<T> & string]: K extends keyof Steps ? Steps[K] extends false ? never : K : K extends keyof ExtractSchemaProperties<S> ? NestedAsync<ExtractSchemaProperties<S>[K], ExtractSchemaProperties<T>[K], Registered> extends false ? never : K : never;
|
|
36
|
+
}[keyof ExtractSchemaProperties<T> & string];
|
|
37
|
+
/** Compile-time diagnostics for incomplete or asynchronous mapping steps. */
|
|
38
|
+
type SyncMappingArguments<Unmapped extends string, Async> = [Unmapped] extends [
|
|
39
|
+
never
|
|
40
|
+
] ? [Async] extends [never] ? [] : [error: 'Mapping contains asynchronous steps'] : [error: `Unmapped properties: ${Unmapped}`];
|
|
41
|
+
/** Compile-time diagnostics for registered synchronous mapping pairs. */
|
|
42
|
+
type RegisteredSyncArguments<Pair, Registered, Async> = Pair extends Registered ? Pair extends Async ? [error: 'Mapping contains asynchronous steps'] : [] : [error: 'Mapping is not registered'];
|
|
12
43
|
/**
|
|
13
44
|
* Extracts the properties record from an ObjectSchemaBuilder.
|
|
14
45
|
*/
|
|
15
|
-
type ExtractSchemaProperties<T> = T extends
|
|
46
|
+
type ExtractSchemaProperties<T> = T extends {
|
|
47
|
+
introspect(): {
|
|
48
|
+
properties: infer TProperties;
|
|
49
|
+
};
|
|
50
|
+
} ? TProperties : never;
|
|
16
51
|
/**
|
|
17
52
|
* Gets all top-level property key names of an ObjectSchemaBuilder.
|
|
18
53
|
*/
|
|
19
|
-
type SchemaKeys<T extends
|
|
54
|
+
type SchemaKeys<T extends ObjectSchemaShape> = keyof ExtractSchemaProperties<T> & string;
|
|
20
55
|
/**
|
|
21
56
|
* Branded phantom type that tags a property descriptor with its key name.
|
|
22
57
|
* This allows TypeScript to infer which property was selected in the
|
|
@@ -32,23 +67,27 @@ type TargetPropertyKey<K extends string> = {
|
|
|
32
67
|
* with `TargetPropertyKey<K>` so the key can be inferred from the return
|
|
33
68
|
* type of the selector callback.
|
|
34
69
|
*/
|
|
35
|
-
type TargetPropertyTree<TSchema extends ObjectSchemaBuilder<any, any, any>, TAllowedKeys extends string> = {
|
|
36
|
-
[K in
|
|
70
|
+
type TargetPropertyTree<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TAllowedKeys extends string> = {
|
|
71
|
+
-readonly [K in keyof ExtractSchemaProperties<TSchema> as K extends TAllowedKeys ? K : never]-?: TargetPropertyKey<K & string> & PropertyDescriptor<TSchema, ExtractSchemaProperties<TSchema>[K], any>;
|
|
37
72
|
};
|
|
38
73
|
/**
|
|
39
74
|
* Infers the TypeScript type of a specific property in an ObjectSchemaBuilder
|
|
40
75
|
* by its key name.
|
|
41
76
|
*/
|
|
42
|
-
type SchemaPropertyInferredType<TSchema extends ObjectSchemaBuilder<any, any, any>, K extends string> = K extends keyof ExtractSchemaProperties<TSchema> ? InferType<ExtractSchemaProperties<TSchema>[K]> : never;
|
|
77
|
+
type SchemaPropertyInferredType<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, K extends string> = K extends keyof ExtractSchemaProperties<TSchema> ? InferType<ExtractSchemaProperties<TSchema>[K]> : never;
|
|
43
78
|
/**
|
|
44
79
|
* Extracts the schema (SchemaBuilder) of a specific property in an
|
|
45
80
|
* ObjectSchemaBuilder by its key name.
|
|
46
81
|
*/
|
|
47
|
-
type TargetPropertySchema<TSchema extends ObjectSchemaBuilder<any, any, any>, K extends string> = K extends keyof ExtractSchemaProperties<TSchema> ? ExtractSchemaProperties<TSchema>[K] : never;
|
|
82
|
+
type TargetPropertySchema<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, K extends string> = K extends keyof ExtractSchemaProperties<TSchema> ? ExtractSchemaProperties<TSchema>[K] : never;
|
|
48
83
|
/**
|
|
49
84
|
* Extracts the element schema from an ArraySchemaBuilder.
|
|
50
85
|
*/
|
|
51
|
-
type ExtractArrayElementSchema<T> = T extends
|
|
86
|
+
type ExtractArrayElementSchema<T> = T extends {
|
|
87
|
+
introspect(): {
|
|
88
|
+
elementSchema: infer TElementSchema;
|
|
89
|
+
};
|
|
90
|
+
} ? NonNullable<TElementSchema> : never;
|
|
52
91
|
/**
|
|
53
92
|
* Extracts the property schema from a PropertyDescriptor's `getSchema()`
|
|
54
93
|
* return type. Used by {@link IsFromCompatible} to recover the source
|
|
@@ -72,13 +111,13 @@ type ExtractPropertySchema<T> = T extends {
|
|
|
72
111
|
* → `false` (same-name, compatible primitive types → auto-mappable)
|
|
73
112
|
* - Neither is ObjectSchemaBuilder + incompatible InferType → `true`
|
|
74
113
|
*/
|
|
75
|
-
type NeedsMapping<TSourcePropSchema, TTargetPropSchema, TRegistered> = TSourcePropSchema extends
|
|
114
|
+
type NeedsMapping<TSourcePropSchema, TTargetPropSchema, TRegistered> = TSourcePropSchema extends ArraySchemaShape ? TTargetPropSchema extends ArraySchemaShape ? NeedsMapping<ExtractArrayElementSchema<TSourcePropSchema>, ExtractArrayElementSchema<TTargetPropSchema>, TRegistered> : true : TTargetPropSchema extends ArraySchemaShape ? true : TSourcePropSchema extends ObjectSchemaShape ? TTargetPropSchema extends ObjectSchemaShape ? true extends RegisteredMatch<TSourcePropSchema, TTargetPropSchema, TRegistered> ? false : InferType<TSourcePropSchema> extends InferType<TTargetPropSchema> ? InferType<TTargetPropSchema> extends InferType<TSourcePropSchema> ? false : true : true : true : TTargetPropSchema extends ObjectSchemaShape ? true : InferType<TSourcePropSchema> extends InferType<TTargetPropSchema> ? false : true;
|
|
76
115
|
/**
|
|
77
116
|
* From all keys of the target schema, filter down to only those that
|
|
78
117
|
* require explicit mapping (i.e. `NeedsMapping` is `true`).
|
|
79
118
|
* Keys where `NeedsMapping` is `false` can be auto-mapped via the registry.
|
|
80
119
|
*/
|
|
81
|
-
type KeysNeedingMapping<TFromSchema extends ObjectSchemaBuilder<any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any>, TRegistered> = {
|
|
120
|
+
type KeysNeedingMapping<TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TRegistered> = {
|
|
82
121
|
[K in SchemaKeys<TToSchema>]: K extends SchemaKeys<TFromSchema> ? NeedsMapping<ExtractSchemaProperties<TFromSchema>[K], ExtractSchemaProperties<TToSchema>[K], TRegistered> extends true ? K : never : K;
|
|
83
122
|
}[SchemaKeys<TToSchema>];
|
|
84
123
|
/**
|
|
@@ -90,7 +129,7 @@ type KeysNeedingMapping<TFromSchema extends ObjectSchemaBuilder<any, any, any>,
|
|
|
90
129
|
*/
|
|
91
130
|
type CheckSchemaCompatible<TSourceSchema, TTargetSchema, TRegistered> = [
|
|
92
131
|
InferType<TSourceSchema>
|
|
93
|
-
] extends [InferType<TTargetSchema>] ? true : TSourceSchema extends
|
|
132
|
+
] extends [InferType<TTargetSchema>] ? true : TSourceSchema extends ArraySchemaShape ? TTargetSchema extends ArraySchemaShape ? CheckSchemaCompatible<ExtractArrayElementSchema<TSourceSchema>, ExtractArrayElementSchema<TTargetSchema>, TRegistered> : false : TSourceSchema extends ObjectSchemaShape ? TTargetSchema extends ObjectSchemaShape ? true extends RegisteredMatch<TSourceSchema, TTargetSchema, TRegistered> ? true : false : false : false;
|
|
94
133
|
/**
|
|
95
134
|
* Checks whether the source property selected by `from()` is compatible
|
|
96
135
|
* with the target property, evaluated *after* `TReturn` has been inferred.
|
|
@@ -100,8 +139,11 @@ type CheckSchemaCompatible<TSourceSchema, TTargetSchema, TRegistered> = [
|
|
|
100
139
|
* successfully, and this type performs the actual compatibility check
|
|
101
140
|
* in the `_args` conditional spread.
|
|
102
141
|
*/
|
|
103
|
-
type IsFromCompatible<TReturn, TToSchema extends ObjectSchemaBuilder<any, any, any>, TKey extends string, TRegistered> = [ExtractPropertySchema<TReturn>] extends [never] ? false : CheckSchemaCompatible<ExtractPropertySchema<TReturn>, TargetPropertySchema<TToSchema, TKey>, TRegistered>;
|
|
104
|
-
|
|
142
|
+
type IsFromCompatible<TReturn, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TKey extends string, TRegistered> = [ExtractPropertySchema<TReturn>] extends [never] ? false : CheckSchemaCompatible<ExtractPropertySchema<TReturn>, TargetPropertySchema<TToSchema, TKey>, TRegistered>;
|
|
143
|
+
/** A mapping function that always returns a Promise, including for pure mappings. */
|
|
144
|
+
export type SchemaToSchemaMapperResult<TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>> = (from: InferType<TFromSchema>) => Promise<InferType<TToSchema>>;
|
|
145
|
+
/** A complete synchronous mapping; unexpected thenables throw instead of leaking into DTOs. */
|
|
146
|
+
export type SyncSchemaToSchemaMapperResult<TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>> = (from: InferType<TFromSchema>) => InferType<TToSchema>;
|
|
105
147
|
export declare class MapperConfigurationError extends Error {
|
|
106
148
|
constructor(messageOrUnmappedProperties: string | string[]);
|
|
107
149
|
}
|
|
@@ -112,11 +154,11 @@ export declare class MapperConfigurationError extends Error {
|
|
|
112
154
|
* - `compute()` — compute from the entire source object
|
|
113
155
|
* - `ignore()` — explicitly skip the property
|
|
114
156
|
*/
|
|
115
|
-
export declare class PropertyMappingBuilder<TFromSchema extends ObjectSchemaBuilder<any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any>, TKey extends string, TUnmapped extends string, TRegistered = never> {
|
|
157
|
+
export declare class PropertyMappingBuilder<TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TKey extends string, TUnmapped extends string, TRegistered = never, TAsyncRegistered = never, TSteps = {}> {
|
|
116
158
|
private readonly _mapper;
|
|
117
159
|
private readonly _targetKey;
|
|
118
160
|
/** @internal */
|
|
119
|
-
constructor(mapper: Mapper<TFromSchema, TToSchema, any, TRegistered>, targetKey: TKey);
|
|
161
|
+
constructor(mapper: Mapper<TFromSchema, TToSchema, any, TRegistered, TAsyncRegistered, TSteps>, targetKey: TKey);
|
|
120
162
|
/**
|
|
121
163
|
* Maps the target property from a source property. The selector
|
|
122
164
|
* receives the source schema's PropertyDescriptorTree and supports
|
|
@@ -141,21 +183,21 @@ export declare class PropertyMappingBuilder<TFromSchema extends ObjectSchemaBuil
|
|
|
141
183
|
success: boolean;
|
|
142
184
|
};
|
|
143
185
|
};
|
|
144
|
-
}>(selector: (tree: PropertyDescriptorTree<TFromSchema, TFromSchema,
|
|
186
|
+
}>(selector: (tree: PropertyDescriptorTree<TFromSchema, TFromSchema, any>) => TReturn, ..._args: [TReturn] extends [never] ? [
|
|
145
187
|
error: `Property '${TKey}': source property type is not assignable to the target property type. Use compute() instead.`
|
|
146
188
|
] : IsFromCompatible<TReturn, TToSchema, TKey, TRegistered> extends true ? [] : [
|
|
147
189
|
error: `Property '${TKey}': source property type is not assignable to the target property type. Use compute() instead.`
|
|
148
|
-
]): Mapper<TFromSchema, TToSchema, Exclude<TUnmapped, TKey>, TRegistered
|
|
190
|
+
]): Mapper<TFromSchema, TToSchema, Exclude<TUnmapped, TKey>, TRegistered, TAsyncRegistered, Step<TSteps, TKey, NestedAsync<ExtractPropertySchema<TReturn>, TargetPropertySchema<TToSchema, TKey>, TAsyncRegistered>>>;
|
|
149
191
|
/**
|
|
150
192
|
* Computes the target property value from the entire source object.
|
|
151
193
|
* Supports both sync and async functions.
|
|
152
194
|
*/
|
|
153
|
-
compute
|
|
195
|
+
compute<TResult extends SchemaPropertyInferredType<TToSchema, TKey> | Promise<SchemaPropertyInferredType<TToSchema, TKey>>>(fn: (obj: InferType<TFromSchema>) => TResult): Mapper<TFromSchema, TToSchema, Exclude<TUnmapped, TKey>, TRegistered, TAsyncRegistered, Step<TSteps, TKey, IsAsync<TResult>>>;
|
|
154
196
|
/**
|
|
155
197
|
* Explicitly excludes the target property from mapping.
|
|
156
198
|
* The property will not appear in the output object.
|
|
157
199
|
*/
|
|
158
|
-
ignore(): Mapper<TFromSchema, TToSchema, Exclude<TUnmapped, TKey>, TRegistered
|
|
200
|
+
ignore(): Mapper<TFromSchema, TToSchema, Exclude<TUnmapped, TKey>, TRegistered, TAsyncRegistered, Step<TSteps, TKey, false>>;
|
|
159
201
|
}
|
|
160
202
|
/**
|
|
161
203
|
* A fluent builder for configuring how each target property is populated
|
|
@@ -173,9 +215,11 @@ export declare class PropertyMappingBuilder<TFromSchema extends ObjectSchemaBuil
|
|
|
173
215
|
* @typeParam TToSchema - target ObjectSchemaBuilder
|
|
174
216
|
* @typeParam TUnmapped - union of target property key names not yet mapped
|
|
175
217
|
*/
|
|
176
|
-
export declare class Mapper<TFromSchema extends ObjectSchemaBuilder<any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any>, TUnmapped extends string = SchemaKeys<TToSchema>, TRegistered = never> {
|
|
218
|
+
export declare class Mapper<TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TUnmapped extends string = SchemaKeys<TToSchema>, TRegistered = never, TAsyncRegistered = never, TSteps = {}> {
|
|
177
219
|
/** Phantom property for structural type-checking of TUnmapped. */
|
|
178
220
|
readonly [SYMBOL_UNMAPPED]: TUnmapped;
|
|
221
|
+
/** @internal Tracks final per-property computations without executing them. */
|
|
222
|
+
readonly [SYMBOL_STEPS]: TSteps;
|
|
179
223
|
private readonly _fromSchema;
|
|
180
224
|
private readonly _toSchema;
|
|
181
225
|
private readonly _registry;
|
|
@@ -186,7 +230,7 @@ export declare class Mapper<TFromSchema extends ObjectSchemaBuilder<any, any, an
|
|
|
186
230
|
* @param toSchema - `object` schema to map to
|
|
187
231
|
* @param registry - optional MappingRegistry to auto-register the mapper
|
|
188
232
|
*/
|
|
189
|
-
constructor(fromSchema: TFromSchema, toSchema: TToSchema, registry?: MappingRegistry<any>);
|
|
233
|
+
constructor(fromSchema: TFromSchema, toSchema: TToSchema, registry?: MappingRegistry<any, any>);
|
|
190
234
|
/**
|
|
191
235
|
* Selects a target property to configure. The selector callback
|
|
192
236
|
* receives a tree of all target properties. Navigate by
|
|
@@ -196,7 +240,15 @@ export declare class Mapper<TFromSchema extends ObjectSchemaBuilder<any, any, an
|
|
|
196
240
|
* ObjectSchemaBuilder with a registered mapping) are also available
|
|
197
241
|
* for explicit override.
|
|
198
242
|
*/
|
|
199
|
-
for<TKey extends SchemaKeys<TToSchema>>(selector: (tree: TargetPropertyTree<TToSchema, SchemaKeys<TToSchema>>) => TargetPropertyKey<TKey>): PropertyMappingBuilder<TFromSchema, TToSchema, TKey, TUnmapped, TRegistered>;
|
|
243
|
+
for<TKey extends SchemaKeys<TToSchema>>(selector: (tree: TargetPropertyTree<TToSchema, SchemaKeys<TToSchema>>) => TargetPropertyKey<TKey>): PropertyMappingBuilder<TFromSchema, TToSchema, TKey, TUnmapped, TRegistered, TAsyncRegistered, TSteps>;
|
|
244
|
+
/**
|
|
245
|
+
* Compile a synchronous function when the complete mapping has no async steps.
|
|
246
|
+
* Eligibility is inferred through compute(), from(), nested mappings and overrides.
|
|
247
|
+
* Callbacks are never probed during configuration. A disguised Promise/thenable
|
|
248
|
+
* throws at invocation; known async functions/dependencies throw at compilation.
|
|
249
|
+
* @throws MapperConfigurationError for incomplete or asynchronous mappings.
|
|
250
|
+
*/
|
|
251
|
+
getSyncMapper(..._args: SyncMappingArguments<TUnmapped, AsyncKeys<TFromSchema, TToSchema, TAsyncRegistered, TSteps>>): SyncSchemaToSchemaMapperResult<TFromSchema, TToSchema>;
|
|
200
252
|
/**
|
|
201
253
|
* Returns the configured async mapping function.
|
|
202
254
|
*
|
|
@@ -212,9 +264,9 @@ export declare class Mapper<TFromSchema extends ObjectSchemaBuilder<any, any, an
|
|
|
212
264
|
*/
|
|
213
265
|
getMapper(..._args: [TUnmapped] extends [never] ? [] : [error: `Unmapped properties: ${TUnmapped}`]): SchemaToSchemaMapperResult<TFromSchema, TToSchema>;
|
|
214
266
|
}
|
|
215
|
-
export declare class MappingRegistry<TRegistered = never> {
|
|
267
|
+
export declare class MappingRegistry<TRegistered = never, TAsyncRegistered = never> {
|
|
216
268
|
#private;
|
|
217
|
-
protected readonly _mappers: Map<ObjectSchemaBuilder<any, any, any>, Map<ObjectSchemaBuilder<any, any, any>, SchemaToSchemaMapperResult<any, any>>>;
|
|
269
|
+
protected readonly _mappers: Map<ObjectSchemaBuilder<any, any, any, any, any, any, any>, Map<ObjectSchemaBuilder<any, any, any, any, any, any, any>, SchemaToSchemaMapperResult<any, any>>>;
|
|
218
270
|
/**
|
|
219
271
|
* Defines a mapping between two schemas and returns a new immutable
|
|
220
272
|
* registry containing the mapping. The callback `fn` receives a fresh
|
|
@@ -231,7 +283,21 @@ export declare class MappingRegistry<TRegistered = never> {
|
|
|
231
283
|
* @throws if schemas are invalid, mapping is duplicate, or unmapped
|
|
232
284
|
* properties remain that cannot be auto-mapped
|
|
233
285
|
*/
|
|
234
|
-
configure<TFromSchema extends ObjectSchemaBuilder<any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any
|
|
286
|
+
configure<TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TSteps = {}>(fromSchema: TFromSchema, toSchema: TToSchema, fn: (mapper: Mapper<TFromSchema, TToSchema, KeysNeedingMapping<TFromSchema, TToSchema, TRegistered>, TRegistered, TAsyncRegistered>) => Mapper<TFromSchema, TToSchema, never, TRegistered, TAsyncRegistered, TSteps>): MappingRegistry<TRegistered | [TFromSchema, TToSchema], TAsyncRegistered | ([
|
|
287
|
+
AsyncKeys<TFromSchema, TToSchema, TAsyncRegistered, TSteps>
|
|
288
|
+
] extends [never] ? never : [TFromSchema, TToSchema])>;
|
|
289
|
+
/**
|
|
290
|
+
* Retrieve a registered synchronous mapper inferred from configure().
|
|
291
|
+
* The pair must be registered and its final steps must all be synchronous.
|
|
292
|
+
* Async mappings remain usable with getMapper(); configuration never executes
|
|
293
|
+
* compute callbacks. Unexpected thenables throw when the returned mapper runs.
|
|
294
|
+
* @throws MapperConfigurationError for an asynchronous dependency.
|
|
295
|
+
* @throws Error if the schema pair has not been registered.
|
|
296
|
+
*/
|
|
297
|
+
getSyncMapper<TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(fromSchema: TFromSchema, toSchema: TToSchema, ..._args: RegisteredSyncArguments<[
|
|
298
|
+
TFromSchema,
|
|
299
|
+
TToSchema
|
|
300
|
+
], TRegistered, TAsyncRegistered>): SyncSchemaToSchemaMapperResult<TFromSchema, TToSchema>;
|
|
235
301
|
/**
|
|
236
302
|
* Gets a mapper function that will map from the fromSchema to the toSchema.
|
|
237
303
|
* Throws an error if no mapper is found for the given schemas pair.
|
|
@@ -240,7 +306,7 @@ export declare class MappingRegistry<TRegistered = never> {
|
|
|
240
306
|
* @returns a function that will take a value of the fromSchema type as an argument and
|
|
241
307
|
* return a promise resolving to a value of the toSchema type
|
|
242
308
|
*/
|
|
243
|
-
getMapper<TFromSchema extends ObjectSchemaBuilder<any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any>>(fromSchema: TFromSchema, toSchema: TToSchema): SchemaToSchemaMapperResult<TFromSchema, TToSchema>;
|
|
309
|
+
getMapper<TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(fromSchema: TFromSchema, toSchema: TToSchema): SchemaToSchemaMapperResult<TFromSchema, TToSchema>;
|
|
244
310
|
}
|
|
245
311
|
/**
|
|
246
312
|
* Creates a new empty {@link MappingRegistry}.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { SchemaToSchemaMapperResult } from './MappingRegistry.js';
|
|
1
|
+
export type { SchemaToSchemaMapperResult, SyncSchemaToSchemaMapperResult } from './MappingRegistry.js';
|
|
2
2
|
export { Mapper, MapperConfigurationError, MappingRegistry, mapper, PropertyMappingBuilder } from './MappingRegistry.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ArraySchemaBuilder as
|
|
1
|
+
import{ArraySchemaBuilder as g,ObjectSchemaBuilder as u,SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR as f}from"@cleverbrush/schema";var j=Symbol("targetPropertyKey");var R=new WeakMap;function E(p,e){if(p!=null&&typeof p.then=="function")throw new l(`Synchronous mapping returned a thenable for property "${e}"`);return p}function I(p){let e=R.get(p);if(!e)throw new l("Mapping contains an asynchronous computation or nested mapper");return e}var l=class extends Error{constructor(e){super(typeof e=="string"?e:`Mapper configuration error: the following target properties are not mapped and not ignored: ${e.join(", ")}`),this.name="MapperConfigurationError"}};function O(p,e,t){if(p instanceof u&&e instanceof u){if(t){let a=t._mappers.get(p)?.get(e);if(a)return a}let c=Object.keys(p.introspect().properties||{}).sort(),m=Object.keys(e.introspect().properties||{}).sort();if(c.length===m.length&&c.every((r,a)=>r===m[a])){let r=async a=>a;return R.set(r,a=>a),r}return null}if(p instanceof g&&e instanceof g){let c=p.introspect().elementSchema,m=e.introspect().elementSchema;if(c&&m){let r=O(c,m,t);if(r){let a=async T=>{if(T!=null)return Array.isArray(T)?Promise.all(T.map(r)):T},d=R.get(r);return d&&R.set(a,T=>{if(T!=null)return Array.isArray(T)?T.map(d):T}),a}}return null}if(!(p instanceof u)&&!(e instanceof u)&&!(p instanceof g)&&!(e instanceof g)){let c=typeof p.introspect=="function"?p.introspect():void 0,m=typeof e.introspect=="function"?e.introspect():void 0;if(!c||!m||c.type!==m.type)return null;let r=async a=>a;return R.set(r,a=>a),r}return null}var A=class{_mapper;_targetKey;constructor(e,t){this._mapper=e,this._targetKey=t}from(e,...t){let c=u.getPropertiesFor(this._mapper._fromSchema),r=e(c)[f],a=r.getSchema(),T=(this._mapper._toSchema.introspect().properties||{})[this._targetKey];if(a instanceof u&&T instanceof u&&this._mapper._registry){let i=this._mapper._registry._mappers.get(a)?.get(T);if(i)return this._mapper._mappings.set(this._targetKey,{type:"auto",sourceDescriptorInner:r,autoMapper:i}),this._mapper}if(a instanceof g&&T instanceof g){let y=a.introspect().elementSchema,i=T.introspect().elementSchema;if(y&&i){let h=O(y,i,this._mapper._registry);if(h)return this._mapper._mappings.set(this._targetKey,{type:"autoArray",sourceDescriptorInner:r,elementMapper:h}),this._mapper}}return this._mapper._mappings.set(this._targetKey,{type:"prop",sourceDescriptorInner:r}),this._mapper}compute(e){return this._mapper._mappings.set(this._targetKey,{type:"custom",fn:e}),this._mapper}ignore(){return this._mapper._mappings.set(this._targetKey,{type:"ignore"}),this._mapper}},K=class{_fromSchema;_toSchema;_registry;_mappings=new Map;constructor(e,t,c){this._fromSchema=e,this._toSchema=t,this._registry=c}for(e){let t,c=new Proxy({},{get(a,d){return typeof d=="string"&&(t=d),{[j]:d}}});if(e(c),!t)throw new Error("for selector must access a property on the target tree");let m=this._toSchema.introspect();if(!(m.properties?Object.keys(m.properties):[]).includes(t))throw new l(`Property "${t}" does not exist in the target schema`);return new A(this,t)}getSyncMapper(...e){return I(this.getMapper())}getMapper(...e){let t=this._toSchema.introspect(),c=t.properties?Object.keys(t.properties):[];if(this._registry){let i=this._fromSchema.introspect().properties||{},h=t.properties||{},o=u.getPropertiesFor(this._fromSchema);for(let S of c){if(this._mappings.has(S))continue;let n=i[S],s=h[S];if(!n||!s)continue;let x=o[S];if(x?.[f]){if(n instanceof u&&s instanceof u){let M=this._registry._mappers.get(n)?.get(s);if(M){this._mappings.set(S,{type:"auto",sourceDescriptorInner:x[f],autoMapper:M});continue}let _=Object.keys(n.introspect().properties||{}).sort(),F=Object.keys(s.introspect().properties||{}).sort();_.length===F.length&&_.every((B,v)=>B===F[v])&&this._mappings.set(S,{type:"prop",sourceDescriptorInner:x[f]});continue}if(!(n instanceof u)&&!(s instanceof u)&&!(n instanceof g)&&!(s instanceof g)){this._mappings.set(S,{type:"prop",sourceDescriptorInner:x[f]});continue}if(n instanceof g&&s instanceof g){let P=n.introspect().elementSchema,M=s.introspect().elementSchema;if(P&&M){let _=O(P,M,this._registry);_&&this._mappings.set(S,{type:"autoArray",sourceDescriptorInner:x[f],elementMapper:_})}}}}}let m=c.filter(y=>!this._mappings.has(y));if(m.length>0)throw new l(m);let r=u.getPropertiesFor(this._toSchema),a=new Map(this._mappings),d=async y=>{let i={};for(let[h,o]of a){if(o.type==="ignore")continue;let S;if(o.type==="prop"){let s=o.sourceDescriptorInner.getValue(y);if(s.success)S=s.value;else continue}else if(o.type==="custom")S=await o.fn(y);else if(o.type==="auto"){let s=o.sourceDescriptorInner.getValue(y);if(s.success&&s.value!==void 0)S=await o.autoMapper(s.value);else continue}else if(o.type==="autoArray"){let s=o.sourceDescriptorInner.getValue(y);if(s.success&&s.value!=null){if(!Array.isArray(s.value))throw new l(`Expected array for property "${h}" but got ${typeof s.value}`);S=await Promise.all(s.value.map(o.elementMapper))}else continue}let n=r[h];n?.[f]?n[f].setValue(i,S,{createMissingStructure:!0}):i[h]=S}return i},T=[...a].map(([y,i])=>{let h=i.autoMapper??i.elementMapper,o=h?R.get(h):void 0;return h&&!o||i.fn?.constructor.name==="AsyncFunction"?null:{key:y,entry:i,nestedSync:o}});return T.every(y=>y!==null)&&R.set(d,y=>{let i={};for(let{key:h,entry:o,nestedSync:S}of T){if(o.type==="ignore")continue;let n;if(o.type==="custom")n=E(o.fn(y),h);else{let x=o.sourceDescriptorInner.getValue(y);if(!x.success)continue;if(n=x.value,o.type==="auto"){if(n===void 0)continue;n=E(S(n),h)}else if(o.type==="autoArray"){if(n==null)continue;if(!Array.isArray(n))throw new l(`Expected array for property "${h}" but got ${typeof n}`);n=n.map(P=>E(S(P),h))}}let s=r[h]?.[f];s?s.setValue(i,n,{createMissingStructure:!0}):i[h]=n}return i}),this._registry&&this._registry._mappers.get(this._fromSchema)?.set(this._toSchema,d),d}},b=class p{_mappers=new Map;#e(e,t){return!(!e||!t||!(e instanceof u)||!(t instanceof u))}configure(e,t,c){if(!this.#e(e,t))throw new Error("Both fromSchema and toSchema must be instances of ObjectSchemaBuilder");if(this._mappers.get(e)?.has(t))throw new Error("Duplicate mapping: a mapping for this schemas pair is already registered");let r=new p;for(let[T,y]of this._mappers)r._mappers.set(T,new Map(y));r._mappers.has(e)||r._mappers.set(e,new Map);let a=new K(e,t,r);return c(a).getMapper(),r}getSyncMapper(e,t,...c){return I(this.getMapper(e,t))}getMapper(e,t){if(!this.#e(e,t))throw new Error("Both fromSchema and toSchema must be instances of ObjectSchemaBuilder");let c=this._mappers.get(e);if(!c)throw new Error("No mapper found for the given schemas pair");let m=c.get(t);if(!m)throw new Error("No mapper found for the given schemas pair");return m}};function w(){return new b}export{K as Mapper,l as MapperConfigurationError,b as MappingRegistry,A as PropertyMappingBuilder,w as mapper};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/MappingRegistry.ts"],"sourcesContent":["import {\n ArraySchemaBuilder,\n type InferType,\n ObjectSchemaBuilder,\n type PropertyDescriptor,\n type PropertyDescriptorTree,\n type SchemaBuilder,\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n} from '@cleverbrush/schema';\n\n/**\n * Internal symbol used to brand target property descriptors with their key.\n * Using a symbol instead of a string property keeps it out of IntelliSense.\n */\nconst SYMBOL_TARGET_PROPERTY_KEY: unique symbol = Symbol('targetPropertyKey');\n\n/**\n * Internal symbol used as a phantom property key on Mapper to track\n * unmapped target properties. Using a symbol keeps it out of IntelliSense.\n */\nconst SYMBOL_UNMAPPED: unique symbol = Symbol('unmapped');\n\n// ── Helper Types ──────────────────────────────────────────────────────\n\n/**\n * Extracts the properties record from an ObjectSchemaBuilder.\n */\ntype ExtractSchemaProperties<T> =\n T extends ObjectSchemaBuilder<infer TProperties, any, any>\n ? TProperties\n : never;\n\n/**\n * Gets all top-level property key names of an ObjectSchemaBuilder.\n */\ntype SchemaKeys<T extends ObjectSchemaBuilder<any, any, any>> =\n keyof ExtractSchemaProperties<T> & string;\n\n/**\n * Branded phantom type that tags a property descriptor with its key name.\n * This allows TypeScript to infer which property was selected in the\n * `for` callback, enabling compile-time tracking of mapped vs unmapped\n * properties.\n */\ntype TargetPropertyKey<K extends string> = {\n readonly [SYMBOL_TARGET_PROPERTY_KEY]: K;\n};\n\n/**\n * Creates a tree of selectable target properties, filtered to only show\n * properties whose keys are in `TAllowedKeys`. Each property is branded\n * with `TargetPropertyKey<K>` so the key can be inferred from the return\n * type of the selector callback.\n */\ntype TargetPropertyTree<\n TSchema extends ObjectSchemaBuilder<any, any, any>,\n TAllowedKeys extends string\n> = {\n [K in SchemaKeys<TSchema> & TAllowedKeys]: TargetPropertyKey<K> &\n PropertyDescriptor<TSchema, ExtractSchemaProperties<TSchema>[K], any>;\n};\n\n/**\n * Infers the TypeScript type of a specific property in an ObjectSchemaBuilder\n * by its key name.\n */\ntype SchemaPropertyInferredType<\n TSchema extends ObjectSchemaBuilder<any, any, any>,\n K extends string\n> = K extends keyof ExtractSchemaProperties<TSchema>\n ? InferType<ExtractSchemaProperties<TSchema>[K]>\n : never;\n\n/**\n * Extracts the schema (SchemaBuilder) of a specific property in an\n * ObjectSchemaBuilder by its key name.\n */\ntype TargetPropertySchema<\n TSchema extends ObjectSchemaBuilder<any, any, any>,\n K extends string\n> = K extends keyof ExtractSchemaProperties<TSchema>\n ? ExtractSchemaProperties<TSchema>[K]\n : never;\n\n/**\n * Extracts the element schema from an ArraySchemaBuilder.\n */\ntype ExtractArrayElementSchema<T> =\n T extends ArraySchemaBuilder<infer TElementSchema, any, any>\n ? TElementSchema\n : never;\n\n/**\n * Extracts the property schema from a PropertyDescriptor's `getSchema()`\n * return type. Used by {@link IsFromCompatible} to recover the source\n * property's schema from the inferred `TReturn` of the `from()` selector.\n */\ntype ExtractPropertySchema<T> = T extends {\n [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {\n getSchema(): infer TSchema;\n };\n}\n ? TSchema\n : never;\n\n/**\n * Determines whether a property needs an explicit mapping configuration.\n *\n * - Both are ObjectSchemaBuilder with registered mapping → `false` (auto-mappable)\n * - Both are ObjectSchemaBuilder without registered mapping → `true`\n * - Either is ObjectSchemaBuilder but the other is not → `true`\n * - Both are ArraySchemaBuilder with element schemas that have registered mapping → `false`\n * - Both are ArraySchemaBuilder with same InferType → `false`\n * - Array vs non-array → `true`\n * - Neither is ObjectSchemaBuilder + InferType<source> extends InferType<target>\n * → `false` (same-name, compatible primitive types → auto-mappable)\n * - Neither is ObjectSchemaBuilder + incompatible InferType → `true`\n */\ntype NeedsMapping<TSourcePropSchema, TTargetPropSchema, TRegistered> =\n TSourcePropSchema extends ArraySchemaBuilder<any, any, any>\n ? TTargetPropSchema extends ArraySchemaBuilder<any, any, any>\n ? NeedsMapping<\n ExtractArrayElementSchema<TSourcePropSchema>,\n ExtractArrayElementSchema<TTargetPropSchema>,\n TRegistered\n >\n : true\n : TTargetPropSchema extends ArraySchemaBuilder<any, any, any>\n ? true\n : TSourcePropSchema extends ObjectSchemaBuilder<any, any, any>\n ? TTargetPropSchema extends ObjectSchemaBuilder<any, any, any>\n ? [TSourcePropSchema, TTargetPropSchema] extends TRegistered\n ? false\n : InferType<TSourcePropSchema> extends InferType<TTargetPropSchema>\n ? InferType<TTargetPropSchema> extends InferType<TSourcePropSchema>\n ? false\n : true\n : true\n : true\n : TTargetPropSchema extends ObjectSchemaBuilder<any, any, any>\n ? true\n : InferType<TSourcePropSchema> extends InferType<TTargetPropSchema>\n ? false\n : true;\n\n/**\n * From all keys of the target schema, filter down to only those that\n * require explicit mapping (i.e. `NeedsMapping` is `true`).\n * Keys where `NeedsMapping` is `false` can be auto-mapped via the registry.\n */\ntype KeysNeedingMapping<\n TFromSchema extends ObjectSchemaBuilder<any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any>,\n TRegistered\n> = {\n [K in SchemaKeys<TToSchema>]: K extends SchemaKeys<TFromSchema>\n ? NeedsMapping<\n ExtractSchemaProperties<TFromSchema>[K],\n ExtractSchemaProperties<TToSchema>[K],\n TRegistered\n > extends true\n ? K\n : never\n : K;\n}[SchemaKeys<TToSchema>];\n\n/**\n * Checks whether two property schemas are compatible, considering\n * registered mappings. Mirrors {@link NeedsMapping}'s structure:\n * receives schemas as direct type parameters, uses\n * {@link ExtractArrayElementSchema} for arrays, and checks registration\n * via tuple-extends-union (not inline `infer`).\n */\ntype CheckSchemaCompatible<TSourceSchema, TTargetSchema, TRegistered> =\n // Direct InferType match → compatible\n [InferType<TSourceSchema>] extends [InferType<TTargetSchema>]\n ? true\n : // Both arrays → recurse into element schemas\n TSourceSchema extends ArraySchemaBuilder<any, any, any>\n ? TTargetSchema extends ArraySchemaBuilder<any, any, any>\n ? CheckSchemaCompatible<\n ExtractArrayElementSchema<TSourceSchema>,\n ExtractArrayElementSchema<TTargetSchema>,\n TRegistered\n >\n : false\n : // Both objects → check registration\n TSourceSchema extends ObjectSchemaBuilder<any, any, any>\n ? TTargetSchema extends ObjectSchemaBuilder<any, any, any>\n ? [TSourceSchema, TTargetSchema] extends TRegistered\n ? true\n : false\n : false\n : false;\n\n/**\n * Checks whether the source property selected by `from()` is compatible\n * with the target property, evaluated *after* `TReturn` has been inferred.\n *\n * This decouples inference from validation: the `TReturn` constraint uses\n * `any` for setValue/getValue value types so TypeScript always infers\n * successfully, and this type performs the actual compatibility check\n * in the `_args` conditional spread.\n */\ntype IsFromCompatible<\n TReturn,\n TToSchema extends ObjectSchemaBuilder<any, any, any>,\n TKey extends string,\n TRegistered\n> = [ExtractPropertySchema<TReturn>] extends [never]\n ? false\n : CheckSchemaCompatible<\n ExtractPropertySchema<TReturn>,\n TargetPropertySchema<TToSchema, TKey>,\n TRegistered\n >;\n\n// ── Mapper Result Type ────────────────────────────────────────────────\n\nexport type SchemaToSchemaMapperResult<\n TFromSchema extends ObjectSchemaBuilder<any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any>\n> = (from: InferType<TFromSchema>) => Promise<InferType<TToSchema>>;\n\n// ── Error Class ───────────────────────────────────────────────────────\n\nexport class MapperConfigurationError extends Error {\n constructor(messageOrUnmappedProperties: string | string[]) {\n super(\n typeof messageOrUnmappedProperties === 'string'\n ? messageOrUnmappedProperties\n : `Mapper configuration error: the following target properties are not mapped and not ignored: ${messageOrUnmappedProperties.join(', ')}`\n );\n this.name = 'MapperConfigurationError';\n }\n}\n\n// ── Internal Mapping Entry ────────────────────────────────────────────\n\ntype MappingEntry = {\n type: 'prop' | 'custom' | 'ignore' | 'auto' | 'autoArray';\n sourceDescriptorInner?: ReturnType<\n PropertyDescriptor<\n any,\n any,\n any\n >[typeof SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]['getValue']\n > extends any\n ? any\n : never;\n fn?: (obj: any) => any;\n autoMapper?: SchemaToSchemaMapperResult<any, any>;\n elementMapper?: (element: any) => Promise<any>;\n};\n\n// ── Array Element Mapper Resolution ───────────────────────────────────\n\n/**\n * Resolves an element mapper for array properties.\n * Handles ObjectSchemaBuilder elements (via registry) and\n * primitive elements (direct copy when types match).\n * Returns null if no valid element mapper can be resolved.\n */\nfunction resolveElementMapper(\n sourceElementSchema: SchemaBuilder<any, any, any>,\n targetElementSchema: SchemaBuilder<any, any, any>,\n registry: MappingRegistry<any> | undefined\n): ((element: any) => Promise<any>) | null {\n // Both element schemas are ObjectSchemaBuilder: look up registered mapper\n if (\n sourceElementSchema instanceof ObjectSchemaBuilder &&\n targetElementSchema instanceof ObjectSchemaBuilder\n ) {\n if (registry) {\n const fromSchemaMappers =\n registry['_mappers'].get(sourceElementSchema);\n const autoMapper = fromSchemaMappers?.get(targetElementSchema);\n if (autoMapper) {\n return autoMapper as (element: any) => Promise<any>;\n }\n }\n // Same inferred structure: copy directly\n const fromKeys = Object.keys(\n sourceElementSchema.introspect().properties || {}\n ).sort();\n const toKeys = Object.keys(\n targetElementSchema.introspect().properties || {}\n ).sort();\n if (\n fromKeys.length === toKeys.length &&\n fromKeys.every((k, i) => k === toKeys[i])\n ) {\n return async (element: any) => element;\n }\n return null;\n }\n\n // Both element schemas are ArraySchemaBuilder: recursive element mapping\n if (\n sourceElementSchema instanceof ArraySchemaBuilder &&\n targetElementSchema instanceof ArraySchemaBuilder\n ) {\n const innerSourceElement =\n sourceElementSchema.introspect().elementSchema;\n const innerTargetElement =\n targetElementSchema.introspect().elementSchema;\n if (innerSourceElement && innerTargetElement) {\n const innerMapper = resolveElementMapper(\n innerSourceElement,\n innerTargetElement,\n registry\n );\n if (innerMapper) {\n return async (arr: any) => {\n if (arr == null) return undefined;\n if (!Array.isArray(arr)) return arr;\n return Promise.all(arr.map(innerMapper));\n };\n }\n }\n return null;\n }\n\n // Neither is ObjectSchemaBuilder nor ArraySchemaBuilder:\n // treat as primitive-compatible only when element schema types match.\n if (\n !(sourceElementSchema instanceof ObjectSchemaBuilder) &&\n !(targetElementSchema instanceof ObjectSchemaBuilder) &&\n !(sourceElementSchema instanceof ArraySchemaBuilder) &&\n !(targetElementSchema instanceof ArraySchemaBuilder)\n ) {\n const sourceIntrospection =\n typeof (sourceElementSchema as any).introspect === 'function'\n ? (sourceElementSchema as any).introspect()\n : undefined;\n const targetIntrospection =\n typeof (targetElementSchema as any).introspect === 'function'\n ? (targetElementSchema as any).introspect()\n : undefined;\n\n if (\n !sourceIntrospection ||\n !targetIntrospection ||\n sourceIntrospection.type !== targetIntrospection.type\n ) {\n return null;\n }\n return async (element: any) => element;\n }\n\n return null;\n}\n\n// ── PropertyMappingBuilder ────────────────────────────────────────────\n\n/**\n * Intermediate builder returned by `for()`. Provides three strategies\n * to configure how the selected target property is populated:\n * - `from()` — copy from a source property\n * - `compute()` — compute from the entire source object\n * - `ignore()` — explicitly skip the property\n */\nexport class PropertyMappingBuilder<\n TFromSchema extends ObjectSchemaBuilder<any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any>,\n TKey extends string,\n TUnmapped extends string,\n TRegistered = never\n> {\n private readonly _mapper: Mapper<TFromSchema, TToSchema, any, TRegistered>;\n private readonly _targetKey: TKey;\n\n /** @internal */\n constructor(\n mapper: Mapper<TFromSchema, TToSchema, any, TRegistered>,\n targetKey: TKey\n ) {\n this._mapper = mapper;\n this._targetKey = targetKey;\n }\n\n /**\n * Maps the target property from a source property. The selector\n * receives the source schema's PropertyDescriptorTree and supports\n * nested paths (e.g. `(s) => s.address.city`).\n *\n * Type compatibility is enforced: only source properties whose\n * inferred type is assignable to the target property type will\n * appear in the selector callback.\n *\n * Under `strictFunctionTypes`, the `setValue` and `getValue` constraints\n * provide bidirectional type checking:\n * - `setValue` contravariance rejects source schemas with extra properties\n * - `getValue` covariance rejects source schemas with missing properties\n * - registered mappings widen the constraint via intersection\n */\n public from<\n TReturn extends {\n [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {\n getSchema(): SchemaBuilder<any, any, any>;\n setValue: (obj: any, value: any) => any;\n getValue: (obj: any) => {\n value?: any;\n success: boolean;\n };\n };\n }\n >(\n selector: (\n tree: PropertyDescriptorTree<\n TFromSchema,\n TFromSchema,\n SchemaPropertyInferredType<TToSchema, TKey>\n >\n ) => TReturn,\n ..._args: [TReturn] extends [never]\n ? [\n error: `Property '${TKey}': source property type is not assignable to the target property type. Use compute() instead.`\n ]\n : IsFromCompatible<\n TReturn,\n TToSchema,\n TKey,\n TRegistered\n > extends true\n ? []\n : [\n error: `Property '${TKey}': source property type is not assignable to the target property type. Use compute() instead.`\n ]\n ): Mapper<TFromSchema, TToSchema, Exclude<TUnmapped, TKey>, TRegistered> {\n const sourceTree = ObjectSchemaBuilder.getPropertiesFor(\n this._mapper['_fromSchema']\n );\n const sourceDescriptor = selector(sourceTree as any);\n const inner = sourceDescriptor[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR];\n\n // Check if both source and target property schemas are ObjectSchemaBuilder;\n // if so, look up the registered mapper and use it at runtime.\n const sourceSchema = inner.getSchema();\n const toProperties = (this._mapper['_toSchema'].introspect()\n .properties || {}) as Record<string, any>;\n const targetPropSchema = toProperties[this._targetKey];\n\n if (\n sourceSchema instanceof ObjectSchemaBuilder &&\n targetPropSchema instanceof ObjectSchemaBuilder &&\n this._mapper['_registry']\n ) {\n const fromSchemaMappers =\n this._mapper['_registry']['_mappers'].get(sourceSchema);\n const autoMapper = fromSchemaMappers?.get(targetPropSchema);\n if (autoMapper) {\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'auto',\n sourceDescriptorInner: inner,\n autoMapper\n });\n return this._mapper as any;\n }\n }\n\n // Check if both source and target are ArraySchemaBuilder;\n // if so, resolve the element mapper and use element-wise mapping.\n if (\n sourceSchema instanceof ArraySchemaBuilder &&\n targetPropSchema instanceof ArraySchemaBuilder\n ) {\n const sourceElementSchema = (\n sourceSchema as ArraySchemaBuilder<any, any, any>\n ).introspect().elementSchema;\n const targetElementSchema = (\n targetPropSchema as ArraySchemaBuilder<any, any, any>\n ).introspect().elementSchema;\n\n if (sourceElementSchema && targetElementSchema) {\n const elementMapper = resolveElementMapper(\n sourceElementSchema,\n targetElementSchema,\n this._mapper['_registry']\n );\n if (elementMapper) {\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'autoArray',\n sourceDescriptorInner: inner,\n elementMapper\n });\n return this._mapper as any;\n }\n }\n }\n\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'prop',\n sourceDescriptorInner: inner\n });\n\n return this._mapper as any;\n }\n\n /**\n * Computes the target property value from the entire source object.\n * Supports both sync and async functions.\n */\n public compute(\n fn:\n | ((\n obj: InferType<TFromSchema>\n ) => SchemaPropertyInferredType<TToSchema, TKey>)\n | ((\n obj: InferType<TFromSchema>\n ) => Promise<SchemaPropertyInferredType<TToSchema, TKey>>)\n ): Mapper<TFromSchema, TToSchema, Exclude<TUnmapped, TKey>, TRegistered> {\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'custom',\n fn: fn as any\n });\n\n return this._mapper as any;\n }\n\n /**\n * Explicitly excludes the target property from mapping.\n * The property will not appear in the output object.\n */\n public ignore(): Mapper<\n TFromSchema,\n TToSchema,\n Exclude<TUnmapped, TKey>,\n TRegistered\n > {\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'ignore'\n });\n\n return this._mapper as any;\n }\n}\n\n// ── Mapper ────────────────────────────────────────────────────────────\n\n/**\n * A fluent builder for configuring how each target property is populated\n * from a source schema. Uses PropertyDescriptors as pointers to properties\n * (similar to expressions in C# .NET).\n *\n * The `TUnmapped` type parameter tracks which target properties have not\n * yet been mapped or ignored. `getMapper()` is only callable (without\n * arguments) when `TUnmapped` is `never` — i.e. all properties have been\n * accounted for. If any property is missing, TypeScript will produce a\n * compile-time type error (a type-assignability mismatch that includes\n * the unmapped property names in its type parameters).\n *\n * @typeParam TFromSchema - source ObjectSchemaBuilder\n * @typeParam TToSchema - target ObjectSchemaBuilder\n * @typeParam TUnmapped - union of target property key names not yet mapped\n */\nexport class Mapper<\n TFromSchema extends ObjectSchemaBuilder<any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any>,\n TUnmapped extends string = SchemaKeys<TToSchema>,\n TRegistered = never\n> {\n /** Phantom property for structural type-checking of TUnmapped. */\n declare readonly [SYMBOL_UNMAPPED]: TUnmapped;\n\n private readonly _fromSchema: TFromSchema;\n private readonly _toSchema: TToSchema;\n private readonly _registry: MappingRegistry<any> | undefined;\n private readonly _mappings: Map<string, MappingEntry> = new Map();\n\n /**\n * Creates a new instance of the Mapper class.\n * @param fromSchema - `object` schema to map from\n * @param toSchema - `object` schema to map to\n * @param registry - optional MappingRegistry to auto-register the mapper\n */\n public constructor(\n fromSchema: TFromSchema,\n toSchema: TToSchema,\n registry?: MappingRegistry<any>\n ) {\n this._fromSchema = fromSchema;\n this._toSchema = toSchema;\n this._registry = registry;\n }\n\n /**\n * Selects a target property to configure. The selector callback\n * receives a tree of all target properties. Navigate by\n * property name: `(t) => t.cityName`.\n *\n * Auto-mappable properties (same name and compatible type, or\n * ObjectSchemaBuilder with a registered mapping) are also available\n * for explicit override.\n */\n public for<TKey extends SchemaKeys<TToSchema>>(\n selector: (\n tree: TargetPropertyTree<TToSchema, SchemaKeys<TToSchema>>\n ) => TargetPropertyKey<TKey>\n ): PropertyMappingBuilder<\n TFromSchema,\n TToSchema,\n TKey,\n TUnmapped,\n TRegistered\n > {\n // At runtime, use a Proxy to detect which property was accessed\n let capturedKey: string | undefined;\n const proxy = new Proxy({} as any, {\n get(_target, prop) {\n if (typeof prop === 'string') {\n capturedKey = prop;\n }\n return { [SYMBOL_TARGET_PROPERTY_KEY]: prop };\n }\n });\n\n selector(proxy);\n\n if (!capturedKey) {\n throw new Error(\n 'for selector must access a property on the target tree'\n );\n }\n\n // Validate that the captured key exists in the target schema\n const toIntrospection = this._toSchema.introspect();\n const targetProperties = toIntrospection.properties\n ? Object.keys(toIntrospection.properties)\n : [];\n if (!targetProperties.includes(capturedKey)) {\n throw new MapperConfigurationError(\n `Property \"${capturedKey}\" does not exist in the target schema`\n );\n }\n\n return new PropertyMappingBuilder(this, capturedKey as TKey);\n }\n\n /**\n * Returns the configured async mapping function.\n *\n * **Compile-time safety:** This method is only callable without\n * arguments when all target properties have been mapped or explicitly\n * ignored. If any property is unmapped, TypeScript will require\n * a string argument describing the unmapped properties, producing\n * a clear compile-time error.\n *\n * **Runtime safety:** Even if TypeScript checks are bypassed (e.g.\n * via `as any`), a `MapperConfigurationError` is thrown at runtime\n * listing the unmapped properties.\n */\n public getMapper(\n ..._args: [TUnmapped] extends [never]\n ? []\n : [error: `Unmapped properties: ${TUnmapped}`]\n ): SchemaToSchemaMapperResult<TFromSchema, TToSchema> {\n // Runtime validation: ensure all target properties are covered\n const toIntrospection = this._toSchema.introspect();\n const targetProperties = toIntrospection.properties\n ? Object.keys(toIntrospection.properties)\n : [];\n\n // Auto-mapping: fill in unmapped properties using registered nested mappers\n if (this._registry) {\n const fromIntrospection = this._fromSchema.introspect();\n const fromProperties = (fromIntrospection.properties ||\n {}) as Record<string, any>;\n const toProperties = (toIntrospection.properties || {}) as Record<\n string,\n any\n >;\n const fromTree = ObjectSchemaBuilder.getPropertiesFor(\n this._fromSchema\n );\n\n for (const key of targetProperties) {\n if (this._mappings.has(key)) continue;\n\n const fromPropSchema = fromProperties[key];\n const toPropSchema = toProperties[key];\n\n if (!fromPropSchema || !toPropSchema) continue;\n\n const sourceDescriptor = (fromTree as any)[key];\n if (!sourceDescriptor?.[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR])\n continue;\n\n // ObjectSchemaBuilder → ObjectSchemaBuilder: use registered mapper\n if (\n fromPropSchema instanceof ObjectSchemaBuilder &&\n toPropSchema instanceof ObjectSchemaBuilder\n ) {\n const fromSchemaMappers =\n this._registry['_mappers'].get(fromPropSchema);\n const autoMapper = fromSchemaMappers?.get(toPropSchema);\n if (autoMapper) {\n this._mappings.set(key, {\n type: 'auto',\n sourceDescriptorInner:\n sourceDescriptor[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ],\n autoMapper\n });\n continue;\n }\n\n // Same-name ObjectSchemaBuilder with identical property keys:\n // copy directly (full type safety ensured at compile time\n // via bidirectional InferType check)\n const fromKeys = Object.keys(\n fromPropSchema.introspect().properties || {}\n ).sort();\n const toKeys = Object.keys(\n toPropSchema.introspect().properties || {}\n ).sort();\n if (\n fromKeys.length === toKeys.length &&\n fromKeys.every((k, i) => k === toKeys[i])\n ) {\n this._mappings.set(key, {\n type: 'prop',\n sourceDescriptorInner:\n sourceDescriptor[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ]\n });\n }\n continue;\n }\n\n // Primitive → Primitive: auto-map same-name props\n // (type compatibility is ensured at the type level by\n // KeysNeedingMapping / NeedsMapping)\n if (\n !(fromPropSchema instanceof ObjectSchemaBuilder) &&\n !(toPropSchema instanceof ObjectSchemaBuilder) &&\n !(fromPropSchema instanceof ArraySchemaBuilder) &&\n !(toPropSchema instanceof ArraySchemaBuilder)\n ) {\n this._mappings.set(key, {\n type: 'prop',\n sourceDescriptorInner:\n sourceDescriptor[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]\n });\n continue;\n }\n\n // ArraySchemaBuilder → ArraySchemaBuilder: use element mapper\n if (\n fromPropSchema instanceof ArraySchemaBuilder &&\n toPropSchema instanceof ArraySchemaBuilder\n ) {\n const sourceElementSchema =\n fromPropSchema.introspect().elementSchema;\n const targetElementSchema =\n toPropSchema.introspect().elementSchema;\n if (sourceElementSchema && targetElementSchema) {\n const elementMapper = resolveElementMapper(\n sourceElementSchema,\n targetElementSchema,\n this._registry\n );\n if (elementMapper) {\n this._mappings.set(key, {\n type: 'autoArray',\n sourceDescriptorInner:\n sourceDescriptor[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ],\n elementMapper\n });\n }\n }\n }\n }\n }\n\n const unmapped = targetProperties.filter(\n key => !this._mappings.has(key)\n );\n\n if (unmapped.length > 0) {\n throw new MapperConfigurationError(unmapped);\n }\n\n const targetTree = ObjectSchemaBuilder.getPropertiesFor(this._toSchema);\n\n const mappings = new Map(this._mappings);\n\n const mapperFn = async (\n source: InferType<TFromSchema>\n ): Promise<InferType<TToSchema>> => {\n const result = {} as any;\n\n for (const [key, entry] of mappings) {\n if (entry.type === 'ignore') {\n continue;\n }\n\n let value: any;\n\n if (entry.type === 'prop') {\n const getResult =\n entry.sourceDescriptorInner.getValue(source);\n if (getResult.success) {\n value = getResult.value;\n } else {\n continue;\n }\n } else if (entry.type === 'custom') {\n value = await entry.fn!(source);\n } else if (entry.type === 'auto') {\n const getResult =\n entry.sourceDescriptorInner.getValue(source);\n if (getResult.success && getResult.value !== undefined) {\n value = await entry.autoMapper!(getResult.value);\n } else {\n continue;\n }\n } else if (entry.type === 'autoArray') {\n const getResult =\n entry.sourceDescriptorInner.getValue(source);\n // null/undefined → skip (spec §6); non-array → error\n if (getResult.success && getResult.value != null) {\n if (!Array.isArray(getResult.value)) {\n throw new MapperConfigurationError(\n `Expected array for property \"${key}\" but got ${typeof getResult.value}`\n );\n }\n value = await Promise.all(\n getResult.value.map(entry.elementMapper!)\n );\n } else {\n continue;\n }\n }\n\n const targetDescriptor = (targetTree as any)[key];\n if (targetDescriptor?.[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]) {\n targetDescriptor[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ].setValue(result, value, {\n createMissingStructure: true\n });\n } else {\n result[key] = value;\n }\n }\n\n return result;\n };\n\n // Auto-register in the registry if available\n if (this._registry) {\n this._registry['_mappers']\n .get(this._fromSchema)\n ?.set(this._toSchema, mapperFn);\n }\n\n return mapperFn;\n }\n}\n\n// ── MappingRegistry ───────────────────────────────────────────────────\n\nexport class MappingRegistry<TRegistered = never> {\n protected readonly _mappers: Map<\n ObjectSchemaBuilder<any, any, any>,\n Map<\n ObjectSchemaBuilder<any, any, any>,\n SchemaToSchemaMapperResult<any, any>\n >\n > = new Map();\n\n #ensureObjectSchemas(\n fromSchema: ObjectSchemaBuilder<any, any, any>,\n toSchema: ObjectSchemaBuilder<any, any, any>\n ): boolean {\n return !(\n !fromSchema ||\n !toSchema ||\n fromSchema instanceof ObjectSchemaBuilder === false ||\n toSchema instanceof ObjectSchemaBuilder === false\n );\n }\n\n /**\n * Defines a mapping between two schemas and returns a new immutable\n * registry containing the mapping. The callback `fn` receives a fresh\n * `Mapper` and must return it after configuring property mappings.\n * The mapper is automatically finalized and registered. Properties\n * not explicitly mapped or ignored may be auto-mapped if a matching\n * nested mapping is already registered in the registry.\n *\n * @param fromSchema - source ObjectSchemaBuilder\n * @param toSchema - target ObjectSchemaBuilder\n * @param fn - callback that configures property mappings on the mapper\n * @returns a new MappingRegistry containing all previous mappings plus\n * the newly configured one\n * @throws if schemas are invalid, mapping is duplicate, or unmapped\n * properties remain that cannot be auto-mapped\n */\n public configure<\n TFromSchema extends ObjectSchemaBuilder<any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any>\n >(\n fromSchema: TFromSchema,\n toSchema: TToSchema,\n fn: (\n mapper: Mapper<\n TFromSchema,\n TToSchema,\n KeysNeedingMapping<TFromSchema, TToSchema, TRegistered>,\n TRegistered\n >\n ) => Mapper<TFromSchema, TToSchema, never, TRegistered>\n ): MappingRegistry<TRegistered | [TFromSchema, TToSchema]> {\n if (!this.#ensureObjectSchemas(fromSchema, toSchema)) {\n throw new Error(\n 'Both fromSchema and toSchema must be instances of ObjectSchemaBuilder'\n );\n }\n\n // Check for duplicate mappings\n const existingFromMappers = this._mappers.get(fromSchema);\n if (existingFromMappers?.has(toSchema)) {\n throw new Error(\n 'Duplicate mapping: a mapping for this schemas pair is already registered'\n );\n }\n\n // Create new immutable registry with cloned mappings\n const newRegistry = new MappingRegistry<\n TRegistered | [TFromSchema, TToSchema]\n >();\n for (const [from, toMap] of this._mappers) {\n newRegistry._mappers.set(from, new Map(toMap));\n }\n\n // Ensure the from→to map slot exists so getMapper can register\n if (!newRegistry._mappers.has(fromSchema)) {\n newRegistry._mappers.set(fromSchema, new Map());\n }\n\n // Create mapper with new registry reference\n const mapper = new Mapper<\n TFromSchema,\n TToSchema,\n KeysNeedingMapping<TFromSchema, TToSchema, TRegistered>,\n TRegistered\n >(fromSchema, toSchema, newRegistry);\n\n // Configure via user callback\n const configuredMapper = fn(mapper);\n\n // Implicit finalization: auto-map + validate + register\n (\n configuredMapper as Mapper<\n TFromSchema,\n TToSchema,\n never,\n TRegistered\n >\n ).getMapper();\n\n return newRegistry;\n }\n\n /**\n * Gets a mapper function that will map from the fromSchema to the toSchema.\n * Throws an error if no mapper is found for the given schemas pair.\n * @param fromSchema a schema to map from\n * @param toSchema a schema to map to\n * @returns a function that will take a value of the fromSchema type as an argument and\n * return a promise resolving to a value of the toSchema type\n */\n public getMapper<\n TFromSchema extends ObjectSchemaBuilder<any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any>\n >(\n fromSchema: TFromSchema,\n toSchema: TToSchema\n ): SchemaToSchemaMapperResult<TFromSchema, TToSchema> {\n if (!this.#ensureObjectSchemas(fromSchema, toSchema)) {\n throw new Error(\n 'Both fromSchema and toSchema must be instances of ObjectSchemaBuilder'\n );\n }\n\n const mappers = this._mappers.get(fromSchema);\n if (!mappers) {\n throw new Error('No mapper found for the given schemas pair');\n }\n const mapper = mappers.get(toSchema);\n if (!mapper) {\n throw new Error('No mapper found for the given schemas pair');\n }\n\n return mapper;\n }\n}\n\n/**\n * Creates a new empty {@link MappingRegistry}.\n *\n * This is a convenience factory function — an alternative to\n * `new MappingRegistry()` that reads better in a fluent chain:\n *\n * ```ts\n * const registry = mapper()\n * .configure(A, B, m => ...)\n * .configure(C, D, m => ...);\n * ```\n */\nexport function mapper(): MappingRegistry {\n return new MappingRegistry();\n}\n"],"mappings":"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","names":["ArraySchemaBuilder","ObjectSchemaBuilder","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","SYMBOL_TARGET_PROPERTY_KEY","MapperConfigurationError","messageOrUnmappedProperties","resolveElementMapper","sourceElementSchema","targetElementSchema","registry","ObjectSchemaBuilder","autoMapper","fromKeys","toKeys","k","i","element","ArraySchemaBuilder","innerSourceElement","innerTargetElement","innerMapper","arr","sourceIntrospection","targetIntrospection","PropertyMappingBuilder","mapper","targetKey","selector","_args","sourceTree","inner","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","sourceSchema","targetPropSchema","elementMapper","fn","Mapper","fromSchema","toSchema","capturedKey","proxy","_target","prop","SYMBOL_TARGET_PROPERTY_KEY","toIntrospection","targetProperties","fromProperties","toProperties","fromTree","key","fromPropSchema","toPropSchema","sourceDescriptor","unmapped","targetTree","mappings","mapperFn","source","result","entry","value","getResult","targetDescriptor","MappingRegistry","_MappingRegistry","#ensureObjectSchemas","newRegistry","from","toMap","mappers"]}
|
|
1
|
+
{"version":3,"sources":["../src/MappingRegistry.ts"],"sourcesContent":["import {\n ArraySchemaBuilder,\n type InferType,\n ObjectSchemaBuilder,\n type PropertyDescriptor,\n type PropertyDescriptorTree,\n type SchemaBuilder,\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n} from '@cleverbrush/schema';\n\n/**\n * Internal symbol used to brand target property descriptors with their key.\n * Using a symbol instead of a string property keeps it out of IntelliSense.\n */\nconst SYMBOL_TARGET_PROPERTY_KEY: unique symbol = Symbol('targetPropertyKey');\n\n/**\n * Internal symbol used as a phantom property key on Mapper to track\n * unmapped target properties. Using a symbol keeps it out of IntelliSense.\n */\nconst SYMBOL_UNMAPPED: unique symbol = Symbol('unmapped');\nconst SYMBOL_STEPS: unique symbol = Symbol('mappingSteps');\n\ntype Step<T, K extends string, Async extends boolean> = Omit<T, K> &\n Record<K, Async>;\ntype IsAsync<T> = 0 extends 1 & T\n ? true\n : [Extract<T, PromiseLike<any>>] extends [never]\n ? false\n : true;\n// Inspect metadata instead of recursively comparing the entire fluent builder.\ntype ArraySchemaShape = { introspect(): { elementSchema: unknown } };\ntype ObjectSchemaShape = { introspect(): { properties: unknown } };\ntype SameProperties<A, B> = [keyof A, keyof B] extends [keyof B, keyof A]\n ? [A, B] extends [B, A]\n ? true\n : false\n : false;\n// Runtime registration still uses schema identity. At compile time compare the\n// declared fields, not every recursive builder/validation method. Exact key sets\n// prevent an empty or smaller schema from matching an unrelated registration.\ntype RegisteredMatch<S, T, Registered> = S extends ObjectSchemaShape\n ? T extends ObjectSchemaShape\n ? Registered extends [infer From, infer To]\n ? [InferType<S>, InferType<T>] extends [\n InferType<From>,\n InferType<To>\n ]\n ? [InferType<From>, InferType<To>] extends [\n InferType<S>,\n InferType<T>\n ]\n ? SameProperties<\n ExtractSchemaProperties<S>,\n ExtractSchemaProperties<From>\n > extends true\n ? SameProperties<\n ExtractSchemaProperties<T>,\n ExtractSchemaProperties<To>\n >\n : false\n : false\n : false\n : false\n : false\n : false;\ntype NestedAsync<S, T, Registered> = S extends ArraySchemaShape\n ? T extends ArraySchemaShape\n ? NestedAsync<\n ExtractArrayElementSchema<S>,\n ExtractArrayElementSchema<T>,\n Registered\n >\n : false\n : true extends RegisteredMatch<S, T, Registered>\n ? true\n : false;\ntype AsyncKeys<S, T, Registered, Steps> = {\n [K in keyof ExtractSchemaProperties<T> & string]: K extends keyof Steps\n ? Steps[K] extends false\n ? never\n : K\n : K extends keyof ExtractSchemaProperties<S>\n ? NestedAsync<\n ExtractSchemaProperties<S>[K],\n ExtractSchemaProperties<T>[K],\n Registered\n > extends false\n ? never\n : K\n : never;\n}[keyof ExtractSchemaProperties<T> & string];\n\n/** Compile-time diagnostics for incomplete or asynchronous mapping steps. */\ntype SyncMappingArguments<Unmapped extends string, Async> = [Unmapped] extends [\n never\n]\n ? [Async] extends [never]\n ? []\n : [error: 'Mapping contains asynchronous steps']\n : [error: `Unmapped properties: ${Unmapped}`];\n\n/** Compile-time diagnostics for registered synchronous mapping pairs. */\ntype RegisteredSyncArguments<Pair, Registered, Async> = Pair extends Registered\n ? Pair extends Async\n ? [error: 'Mapping contains asynchronous steps']\n : []\n : [error: 'Mapping is not registered'];\n\nconst syncImplementations = new WeakMap<Function, (source: any) => any>();\n\nfunction assertSync(value: any, key: string): any {\n if (value != null && typeof value.then === 'function') {\n throw new MapperConfigurationError(\n `Synchronous mapping returned a thenable for property \"${key}\"`\n );\n }\n return value;\n}\n\nfunction synchronous(fn: Function): (source: any) => any {\n const result = syncImplementations.get(fn);\n if (!result)\n throw new MapperConfigurationError(\n 'Mapping contains an asynchronous computation or nested mapper'\n );\n return result;\n}\n\n// ── Helper Types ──────────────────────────────────────────────────────\n\n/**\n * Extracts the properties record from an ObjectSchemaBuilder.\n */\ntype ExtractSchemaProperties<T> = T extends {\n introspect(): { properties: infer TProperties };\n}\n ? TProperties\n : never;\n\n/**\n * Gets all top-level property key names of an ObjectSchemaBuilder.\n */\ntype SchemaKeys<T extends ObjectSchemaShape> =\n keyof ExtractSchemaProperties<T> & string;\n\n/**\n * Branded phantom type that tags a property descriptor with its key name.\n * This allows TypeScript to infer which property was selected in the\n * `for` callback, enabling compile-time tracking of mapped vs unmapped\n * properties.\n */\ntype TargetPropertyKey<K extends string> = {\n readonly [SYMBOL_TARGET_PROPERTY_KEY]: K;\n};\n\n/**\n * Creates a tree of selectable target properties, filtered to only show\n * properties whose keys are in `TAllowedKeys`. Each property is branded\n * with `TargetPropertyKey<K>` so the key can be inferred from the return\n * type of the selector callback.\n */\ntype TargetPropertyTree<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TAllowedKeys extends string\n> = {\n // Filter source properties without detaching the target key's declaration.\n -readonly [K in keyof ExtractSchemaProperties<TSchema> as K extends TAllowedKeys\n ? K\n : never]-?: TargetPropertyKey<K & string> &\n PropertyDescriptor<TSchema, ExtractSchemaProperties<TSchema>[K], any>;\n};\n\n/**\n * Infers the TypeScript type of a specific property in an ObjectSchemaBuilder\n * by its key name.\n */\ntype SchemaPropertyInferredType<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n K extends string\n> = K extends keyof ExtractSchemaProperties<TSchema>\n ? InferType<ExtractSchemaProperties<TSchema>[K]>\n : never;\n\n/**\n * Extracts the schema (SchemaBuilder) of a specific property in an\n * ObjectSchemaBuilder by its key name.\n */\ntype TargetPropertySchema<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n K extends string\n> = K extends keyof ExtractSchemaProperties<TSchema>\n ? ExtractSchemaProperties<TSchema>[K]\n : never;\n\n/**\n * Extracts the element schema from an ArraySchemaBuilder.\n */\ntype ExtractArrayElementSchema<T> = T extends {\n introspect(): { elementSchema: infer TElementSchema };\n}\n ? NonNullable<TElementSchema>\n : never;\n\n/**\n * Extracts the property schema from a PropertyDescriptor's `getSchema()`\n * return type. Used by {@link IsFromCompatible} to recover the source\n * property's schema from the inferred `TReturn` of the `from()` selector.\n */\ntype ExtractPropertySchema<T> = T extends {\n [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {\n getSchema(): infer TSchema;\n };\n}\n ? TSchema\n : never;\n\n/**\n * Determines whether a property needs an explicit mapping configuration.\n *\n * - Both are ObjectSchemaBuilder with registered mapping → `false` (auto-mappable)\n * - Both are ObjectSchemaBuilder without registered mapping → `true`\n * - Either is ObjectSchemaBuilder but the other is not → `true`\n * - Both are ArraySchemaBuilder with element schemas that have registered mapping → `false`\n * - Both are ArraySchemaBuilder with same InferType → `false`\n * - Array vs non-array → `true`\n * - Neither is ObjectSchemaBuilder + InferType<source> extends InferType<target>\n * → `false` (same-name, compatible primitive types → auto-mappable)\n * - Neither is ObjectSchemaBuilder + incompatible InferType → `true`\n */\ntype NeedsMapping<TSourcePropSchema, TTargetPropSchema, TRegistered> =\n TSourcePropSchema extends ArraySchemaShape\n ? TTargetPropSchema extends ArraySchemaShape\n ? NeedsMapping<\n ExtractArrayElementSchema<TSourcePropSchema>,\n ExtractArrayElementSchema<TTargetPropSchema>,\n TRegistered\n >\n : true\n : TTargetPropSchema extends ArraySchemaShape\n ? true\n : TSourcePropSchema extends ObjectSchemaShape\n ? TTargetPropSchema extends ObjectSchemaShape\n ? true extends RegisteredMatch<\n TSourcePropSchema,\n TTargetPropSchema,\n TRegistered\n >\n ? false\n : InferType<TSourcePropSchema> extends InferType<TTargetPropSchema>\n ? InferType<TTargetPropSchema> extends InferType<TSourcePropSchema>\n ? false\n : true\n : true\n : true\n : TTargetPropSchema extends ObjectSchemaShape\n ? true\n : InferType<TSourcePropSchema> extends InferType<TTargetPropSchema>\n ? false\n : true;\n\n/**\n * From all keys of the target schema, filter down to only those that\n * require explicit mapping (i.e. `NeedsMapping` is `true`).\n * Keys where `NeedsMapping` is `false` can be auto-mapped via the registry.\n */\ntype KeysNeedingMapping<\n TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TRegistered\n> = {\n [K in SchemaKeys<TToSchema>]: K extends SchemaKeys<TFromSchema>\n ? NeedsMapping<\n ExtractSchemaProperties<TFromSchema>[K],\n ExtractSchemaProperties<TToSchema>[K],\n TRegistered\n > extends true\n ? K\n : never\n : K;\n}[SchemaKeys<TToSchema>];\n\n/**\n * Checks whether two property schemas are compatible, considering\n * registered mappings. Mirrors {@link NeedsMapping}'s structure:\n * receives schemas as direct type parameters, uses\n * {@link ExtractArrayElementSchema} for arrays, and checks registration\n * via tuple-extends-union (not inline `infer`).\n */\ntype CheckSchemaCompatible<TSourceSchema, TTargetSchema, TRegistered> =\n // Direct InferType match → compatible\n [InferType<TSourceSchema>] extends [InferType<TTargetSchema>]\n ? true\n : // Both arrays → recurse into element schemas\n TSourceSchema extends ArraySchemaShape\n ? TTargetSchema extends ArraySchemaShape\n ? CheckSchemaCompatible<\n ExtractArrayElementSchema<TSourceSchema>,\n ExtractArrayElementSchema<TTargetSchema>,\n TRegistered\n >\n : false\n : // Both objects → check registration\n TSourceSchema extends ObjectSchemaShape\n ? TTargetSchema extends ObjectSchemaShape\n ? true extends RegisteredMatch<\n TSourceSchema,\n TTargetSchema,\n TRegistered\n >\n ? true\n : false\n : false\n : false;\n\n/**\n * Checks whether the source property selected by `from()` is compatible\n * with the target property, evaluated *after* `TReturn` has been inferred.\n *\n * This decouples inference from validation: the `TReturn` constraint uses\n * `any` for setValue/getValue value types so TypeScript always infers\n * successfully, and this type performs the actual compatibility check\n * in the `_args` conditional spread.\n */\ntype IsFromCompatible<\n TReturn,\n TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TKey extends string,\n TRegistered\n> = [ExtractPropertySchema<TReturn>] extends [never]\n ? false\n : CheckSchemaCompatible<\n ExtractPropertySchema<TReturn>,\n TargetPropertySchema<TToSchema, TKey>,\n TRegistered\n >;\n\n// ── Mapper Result Type ────────────────────────────────────────────────\n\n/** A mapping function that always returns a Promise, including for pure mappings. */\nexport type SchemaToSchemaMapperResult<\n TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n> = (from: InferType<TFromSchema>) => Promise<InferType<TToSchema>>;\n\n/** A complete synchronous mapping; unexpected thenables throw instead of leaking into DTOs. */\nexport type SyncSchemaToSchemaMapperResult<\n TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n> = (from: InferType<TFromSchema>) => InferType<TToSchema>;\n\n// ── Error Class ───────────────────────────────────────────────────────\n\nexport class MapperConfigurationError extends Error {\n constructor(messageOrUnmappedProperties: string | string[]) {\n super(\n typeof messageOrUnmappedProperties === 'string'\n ? messageOrUnmappedProperties\n : `Mapper configuration error: the following target properties are not mapped and not ignored: ${messageOrUnmappedProperties.join(', ')}`\n );\n this.name = 'MapperConfigurationError';\n }\n}\n\n// ── Internal Mapping Entry ────────────────────────────────────────────\n\ntype MappingEntry = {\n type: 'prop' | 'custom' | 'ignore' | 'auto' | 'autoArray';\n sourceDescriptorInner?: ReturnType<\n PropertyDescriptor<\n any,\n any,\n any\n >[typeof SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]['getValue']\n > extends any\n ? any\n : never;\n fn?: (obj: any) => any;\n autoMapper?: SchemaToSchemaMapperResult<any, any>;\n elementMapper?: (element: any) => Promise<any>;\n};\n\n// ── Array Element Mapper Resolution ───────────────────────────────────\n\n/**\n * Resolves an element mapper for array properties.\n * Handles ObjectSchemaBuilder elements (via registry) and\n * primitive elements (direct copy when types match).\n * Returns null if no valid element mapper can be resolved.\n */\nfunction resolveElementMapper(\n sourceElementSchema: SchemaBuilder<any, any, any>,\n targetElementSchema: SchemaBuilder<any, any, any>,\n registry: MappingRegistry<any, any> | undefined\n): ((element: any) => Promise<any>) | null {\n // Both element schemas are ObjectSchemaBuilder: look up registered mapper\n if (\n sourceElementSchema instanceof ObjectSchemaBuilder &&\n targetElementSchema instanceof ObjectSchemaBuilder\n ) {\n if (registry) {\n const fromSchemaMappers =\n registry['_mappers'].get(sourceElementSchema);\n const autoMapper = fromSchemaMappers?.get(targetElementSchema);\n if (autoMapper) {\n return autoMapper as (element: any) => Promise<any>;\n }\n }\n // Same inferred structure: copy directly\n const fromKeys = Object.keys(\n sourceElementSchema.introspect().properties || {}\n ).sort();\n const toKeys = Object.keys(\n targetElementSchema.introspect().properties || {}\n ).sort();\n if (\n fromKeys.length === toKeys.length &&\n fromKeys.every((k, i) => k === toKeys[i])\n ) {\n const copy = async (element: any) => element;\n syncImplementations.set(copy, element => element);\n return copy;\n }\n return null;\n }\n\n // Both element schemas are ArraySchemaBuilder: recursive element mapping\n if (\n sourceElementSchema instanceof ArraySchemaBuilder &&\n targetElementSchema instanceof ArraySchemaBuilder\n ) {\n const innerSourceElement =\n sourceElementSchema.introspect().elementSchema;\n const innerTargetElement =\n targetElementSchema.introspect().elementSchema;\n if (innerSourceElement && innerTargetElement) {\n const innerMapper = resolveElementMapper(\n innerSourceElement,\n innerTargetElement,\n registry\n );\n if (innerMapper) {\n const mapArray = async (arr: any) => {\n if (arr == null) return undefined;\n if (!Array.isArray(arr)) return arr;\n return Promise.all(arr.map(innerMapper));\n };\n const sync = syncImplementations.get(innerMapper);\n if (sync)\n syncImplementations.set(mapArray, arr => {\n if (arr == null) return undefined;\n if (!Array.isArray(arr)) return arr;\n return arr.map(sync);\n });\n return mapArray;\n }\n }\n return null;\n }\n\n // Neither is ObjectSchemaBuilder nor ArraySchemaBuilder:\n // treat as primitive-compatible only when element schema types match.\n if (\n !(sourceElementSchema instanceof ObjectSchemaBuilder) &&\n !(targetElementSchema instanceof ObjectSchemaBuilder) &&\n !(sourceElementSchema instanceof ArraySchemaBuilder) &&\n !(targetElementSchema instanceof ArraySchemaBuilder)\n ) {\n const sourceIntrospection =\n typeof (sourceElementSchema as any).introspect === 'function'\n ? (sourceElementSchema as any).introspect()\n : undefined;\n const targetIntrospection =\n typeof (targetElementSchema as any).introspect === 'function'\n ? (targetElementSchema as any).introspect()\n : undefined;\n\n if (\n !sourceIntrospection ||\n !targetIntrospection ||\n sourceIntrospection.type !== targetIntrospection.type\n ) {\n return null;\n }\n const copy = async (element: any) => element;\n syncImplementations.set(copy, element => element);\n return copy;\n }\n\n return null;\n}\n\n// ── PropertyMappingBuilder ────────────────────────────────────────────\n\n/**\n * Intermediate builder returned by `for()`. Provides three strategies\n * to configure how the selected target property is populated:\n * - `from()` — copy from a source property\n * - `compute()` — compute from the entire source object\n * - `ignore()` — explicitly skip the property\n */\nexport class PropertyMappingBuilder<\n TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TKey extends string,\n TUnmapped extends string,\n TRegistered = never,\n TAsyncRegistered = never,\n TSteps = {}\n> {\n private readonly _mapper: Mapper<\n TFromSchema,\n TToSchema,\n any,\n TRegistered,\n TAsyncRegistered,\n TSteps\n >;\n private readonly _targetKey: TKey;\n\n /** @internal */\n constructor(\n mapper: Mapper<\n TFromSchema,\n TToSchema,\n any,\n TRegistered,\n TAsyncRegistered,\n TSteps\n >,\n targetKey: TKey\n ) {\n this._mapper = mapper;\n this._targetKey = targetKey;\n }\n\n /**\n * Maps the target property from a source property. The selector\n * receives the source schema's PropertyDescriptorTree and supports\n * nested paths (e.g. `(s) => s.address.city`).\n *\n * Type compatibility is enforced: only source properties whose\n * inferred type is assignable to the target property type will\n * appear in the selector callback.\n *\n * Under `strictFunctionTypes`, the `setValue` and `getValue` constraints\n * provide bidirectional type checking:\n * - `setValue` contravariance rejects source schemas with extra properties\n * - `getValue` covariance rejects source schemas with missing properties\n * - registered mappings widen the constraint via intersection\n */\n public from<\n TReturn extends {\n [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {\n getSchema(): SchemaBuilder<any, any, any>;\n setValue: (obj: any, value: any) => any;\n getValue: (obj: any) => {\n value?: any;\n success: boolean;\n };\n };\n }\n >(\n selector: (\n tree: PropertyDescriptorTree<TFromSchema, TFromSchema, any>\n ) => TReturn,\n ..._args: [TReturn] extends [never]\n ? [\n error: `Property '${TKey}': source property type is not assignable to the target property type. Use compute() instead.`\n ]\n : IsFromCompatible<\n TReturn,\n TToSchema,\n TKey,\n TRegistered\n > extends true\n ? []\n : [\n error: `Property '${TKey}': source property type is not assignable to the target property type. Use compute() instead.`\n ]\n ): Mapper<\n TFromSchema,\n TToSchema,\n Exclude<TUnmapped, TKey>,\n TRegistered,\n TAsyncRegistered,\n Step<\n TSteps,\n TKey,\n NestedAsync<\n ExtractPropertySchema<TReturn>,\n TargetPropertySchema<TToSchema, TKey>,\n TAsyncRegistered\n >\n >\n > {\n const sourceTree = ObjectSchemaBuilder.getPropertiesFor(\n this._mapper['_fromSchema']\n );\n const sourceDescriptor = selector(sourceTree as any);\n const inner = sourceDescriptor[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR];\n\n // Check if both source and target property schemas are ObjectSchemaBuilder;\n // if so, look up the registered mapper and use it at runtime.\n const sourceSchema = inner.getSchema();\n const toProperties = (this._mapper['_toSchema'].introspect()\n .properties || {}) as Record<string, any>;\n const targetPropSchema = toProperties[this._targetKey];\n\n if (\n sourceSchema instanceof ObjectSchemaBuilder &&\n targetPropSchema instanceof ObjectSchemaBuilder &&\n this._mapper['_registry']\n ) {\n const fromSchemaMappers =\n this._mapper['_registry']['_mappers'].get(sourceSchema);\n const autoMapper = fromSchemaMappers?.get(targetPropSchema);\n if (autoMapper) {\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'auto',\n sourceDescriptorInner: inner,\n autoMapper\n });\n return this._mapper as any;\n }\n }\n\n // Check if both source and target are ArraySchemaBuilder;\n // if so, resolve the element mapper and use element-wise mapping.\n if (\n sourceSchema instanceof ArraySchemaBuilder &&\n targetPropSchema instanceof ArraySchemaBuilder\n ) {\n const sourceElementSchema = (\n sourceSchema as ArraySchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n ).introspect().elementSchema;\n const targetElementSchema = (\n targetPropSchema as ArraySchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n ).introspect().elementSchema;\n\n if (sourceElementSchema && targetElementSchema) {\n const elementMapper = resolveElementMapper(\n sourceElementSchema,\n targetElementSchema,\n this._mapper['_registry']\n );\n if (elementMapper) {\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'autoArray',\n sourceDescriptorInner: inner,\n elementMapper\n });\n return this._mapper as any;\n }\n }\n }\n\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'prop',\n sourceDescriptorInner: inner\n });\n\n return this._mapper as any;\n }\n\n /**\n * Computes the target property value from the entire source object.\n * Supports both sync and async functions.\n */\n public compute<\n TResult extends\n | SchemaPropertyInferredType<TToSchema, TKey>\n | Promise<SchemaPropertyInferredType<TToSchema, TKey>>\n >(\n fn: (obj: InferType<TFromSchema>) => TResult\n ): Mapper<\n TFromSchema,\n TToSchema,\n Exclude<TUnmapped, TKey>,\n TRegistered,\n TAsyncRegistered,\n Step<TSteps, TKey, IsAsync<TResult>>\n > {\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'custom',\n fn: fn as any\n });\n\n return this._mapper as any;\n }\n\n /**\n * Explicitly excludes the target property from mapping.\n * The property will not appear in the output object.\n */\n public ignore(): Mapper<\n TFromSchema,\n TToSchema,\n Exclude<TUnmapped, TKey>,\n TRegistered,\n TAsyncRegistered,\n Step<TSteps, TKey, false>\n > {\n this._mapper['_mappings'].set(this._targetKey, {\n type: 'ignore'\n });\n\n return this._mapper as any;\n }\n}\n\n// ── Mapper ────────────────────────────────────────────────────────────\n\n/**\n * A fluent builder for configuring how each target property is populated\n * from a source schema. Uses PropertyDescriptors as pointers to properties\n * (similar to expressions in C# .NET).\n *\n * The `TUnmapped` type parameter tracks which target properties have not\n * yet been mapped or ignored. `getMapper()` is only callable (without\n * arguments) when `TUnmapped` is `never` — i.e. all properties have been\n * accounted for. If any property is missing, TypeScript will produce a\n * compile-time type error (a type-assignability mismatch that includes\n * the unmapped property names in its type parameters).\n *\n * @typeParam TFromSchema - source ObjectSchemaBuilder\n * @typeParam TToSchema - target ObjectSchemaBuilder\n * @typeParam TUnmapped - union of target property key names not yet mapped\n */\nexport class Mapper<\n TFromSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n TUnmapped extends string = SchemaKeys<TToSchema>,\n TRegistered = never,\n TAsyncRegistered = never,\n TSteps = {}\n> {\n /** Phantom property for structural type-checking of TUnmapped. */\n declare readonly [SYMBOL_UNMAPPED]: TUnmapped;\n /** @internal Tracks final per-property computations without executing them. */\n declare readonly [SYMBOL_STEPS]: TSteps;\n\n private readonly _fromSchema: TFromSchema;\n private readonly _toSchema: TToSchema;\n private readonly _registry: MappingRegistry<any, any> | undefined;\n private readonly _mappings: Map<string, MappingEntry> = new Map();\n\n /**\n * Creates a new instance of the Mapper class.\n * @param fromSchema - `object` schema to map from\n * @param toSchema - `object` schema to map to\n * @param registry - optional MappingRegistry to auto-register the mapper\n */\n public constructor(\n fromSchema: TFromSchema,\n toSchema: TToSchema,\n registry?: MappingRegistry<any, any>\n ) {\n this._fromSchema = fromSchema;\n this._toSchema = toSchema;\n this._registry = registry;\n }\n\n /**\n * Selects a target property to configure. The selector callback\n * receives a tree of all target properties. Navigate by\n * property name: `(t) => t.cityName`.\n *\n * Auto-mappable properties (same name and compatible type, or\n * ObjectSchemaBuilder with a registered mapping) are also available\n * for explicit override.\n */\n public for<TKey extends SchemaKeys<TToSchema>>(\n selector: (\n tree: TargetPropertyTree<TToSchema, SchemaKeys<TToSchema>>\n ) => TargetPropertyKey<TKey>\n ): PropertyMappingBuilder<\n TFromSchema,\n TToSchema,\n TKey,\n TUnmapped,\n TRegistered,\n TAsyncRegistered,\n TSteps\n > {\n // At runtime, use a Proxy to detect which property was accessed\n let capturedKey: string | undefined;\n const proxy = new Proxy({} as any, {\n get(_target, prop) {\n if (typeof prop === 'string') {\n capturedKey = prop;\n }\n return { [SYMBOL_TARGET_PROPERTY_KEY]: prop };\n }\n });\n\n selector(proxy);\n\n if (!capturedKey) {\n throw new Error(\n 'for selector must access a property on the target tree'\n );\n }\n\n // Validate that the captured key exists in the target schema\n const toIntrospection = this._toSchema.introspect();\n const targetProperties = toIntrospection.properties\n ? Object.keys(toIntrospection.properties)\n : [];\n if (!targetProperties.includes(capturedKey)) {\n throw new MapperConfigurationError(\n `Property \"${capturedKey}\" does not exist in the target schema`\n );\n }\n\n return new PropertyMappingBuilder(this, capturedKey as TKey);\n }\n\n /**\n * Compile a synchronous function when the complete mapping has no async steps.\n * Eligibility is inferred through compute(), from(), nested mappings and overrides.\n * Callbacks are never probed during configuration. A disguised Promise/thenable\n * throws at invocation; known async functions/dependencies throw at compilation.\n * @throws MapperConfigurationError for incomplete or asynchronous mappings.\n */\n public getSyncMapper(\n ..._args: SyncMappingArguments<\n TUnmapped,\n AsyncKeys<TFromSchema, TToSchema, TAsyncRegistered, TSteps>\n >\n ): SyncSchemaToSchemaMapperResult<TFromSchema, TToSchema> {\n return synchronous(\n (\n this.getMapper as () => SchemaToSchemaMapperResult<\n TFromSchema,\n TToSchema\n >\n )()\n );\n }\n\n /**\n * Returns the configured async mapping function.\n *\n * **Compile-time safety:** This method is only callable without\n * arguments when all target properties have been mapped or explicitly\n * ignored. If any property is unmapped, TypeScript will require\n * a string argument describing the unmapped properties, producing\n * a clear compile-time error.\n *\n * **Runtime safety:** Even if TypeScript checks are bypassed (e.g.\n * via `as any`), a `MapperConfigurationError` is thrown at runtime\n * listing the unmapped properties.\n */\n public getMapper(\n ..._args: [TUnmapped] extends [never]\n ? []\n : [error: `Unmapped properties: ${TUnmapped}`]\n ): SchemaToSchemaMapperResult<TFromSchema, TToSchema> {\n // Runtime validation: ensure all target properties are covered\n const toIntrospection = this._toSchema.introspect();\n const targetProperties = toIntrospection.properties\n ? Object.keys(toIntrospection.properties)\n : [];\n\n // Auto-mapping: fill in unmapped properties using registered nested mappers\n if (this._registry) {\n const fromIntrospection = this._fromSchema.introspect();\n const fromProperties = (fromIntrospection.properties ||\n {}) as Record<string, any>;\n const toProperties = (toIntrospection.properties || {}) as Record<\n string,\n any\n >;\n const fromTree = ObjectSchemaBuilder.getPropertiesFor(\n this._fromSchema\n );\n\n for (const key of targetProperties) {\n if (this._mappings.has(key)) continue;\n\n const fromPropSchema = fromProperties[key];\n const toPropSchema = toProperties[key];\n\n if (!fromPropSchema || !toPropSchema) continue;\n\n const sourceDescriptor = (fromTree as any)[key];\n if (!sourceDescriptor?.[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR])\n continue;\n\n // ObjectSchemaBuilder → ObjectSchemaBuilder: use registered mapper\n if (\n fromPropSchema instanceof ObjectSchemaBuilder &&\n toPropSchema instanceof ObjectSchemaBuilder\n ) {\n const fromSchemaMappers =\n this._registry['_mappers'].get(fromPropSchema);\n const autoMapper = fromSchemaMappers?.get(toPropSchema);\n if (autoMapper) {\n this._mappings.set(key, {\n type: 'auto',\n sourceDescriptorInner:\n sourceDescriptor[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ],\n autoMapper\n });\n continue;\n }\n\n // Same-name ObjectSchemaBuilder with identical property keys:\n // copy directly (full type safety ensured at compile time\n // via bidirectional InferType check)\n const fromKeys = Object.keys(\n fromPropSchema.introspect().properties || {}\n ).sort();\n const toKeys = Object.keys(\n toPropSchema.introspect().properties || {}\n ).sort();\n if (\n fromKeys.length === toKeys.length &&\n fromKeys.every((k, i) => k === toKeys[i])\n ) {\n this._mappings.set(key, {\n type: 'prop',\n sourceDescriptorInner:\n sourceDescriptor[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ]\n });\n }\n continue;\n }\n\n // Primitive → Primitive: auto-map same-name props\n // (type compatibility is ensured at the type level by\n // KeysNeedingMapping / NeedsMapping)\n if (\n !(fromPropSchema instanceof ObjectSchemaBuilder) &&\n !(toPropSchema instanceof ObjectSchemaBuilder) &&\n !(fromPropSchema instanceof ArraySchemaBuilder) &&\n !(toPropSchema instanceof ArraySchemaBuilder)\n ) {\n this._mappings.set(key, {\n type: 'prop',\n sourceDescriptorInner:\n sourceDescriptor[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]\n });\n continue;\n }\n\n // ArraySchemaBuilder → ArraySchemaBuilder: use element mapper\n if (\n fromPropSchema instanceof ArraySchemaBuilder &&\n toPropSchema instanceof ArraySchemaBuilder\n ) {\n const sourceElementSchema =\n fromPropSchema.introspect().elementSchema;\n const targetElementSchema =\n toPropSchema.introspect().elementSchema;\n if (sourceElementSchema && targetElementSchema) {\n const elementMapper = resolveElementMapper(\n sourceElementSchema,\n targetElementSchema,\n this._registry\n );\n if (elementMapper) {\n this._mappings.set(key, {\n type: 'autoArray',\n sourceDescriptorInner:\n sourceDescriptor[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ],\n elementMapper\n });\n }\n }\n }\n }\n }\n\n const unmapped = targetProperties.filter(\n key => !this._mappings.has(key)\n );\n\n if (unmapped.length > 0) {\n throw new MapperConfigurationError(unmapped);\n }\n\n const targetTree = ObjectSchemaBuilder.getPropertiesFor(this._toSchema);\n\n const mappings = new Map(this._mappings);\n\n const mapperFn = async (\n source: InferType<TFromSchema>\n ): Promise<InferType<TToSchema>> => {\n const result = {} as any;\n\n for (const [key, entry] of mappings) {\n if (entry.type === 'ignore') {\n continue;\n }\n\n let value: any;\n\n if (entry.type === 'prop') {\n const getResult =\n entry.sourceDescriptorInner.getValue(source);\n if (getResult.success) {\n value = getResult.value;\n } else {\n continue;\n }\n } else if (entry.type === 'custom') {\n value = await entry.fn!(source);\n } else if (entry.type === 'auto') {\n const getResult =\n entry.sourceDescriptorInner.getValue(source);\n if (getResult.success && getResult.value !== undefined) {\n value = await entry.autoMapper!(getResult.value);\n } else {\n continue;\n }\n } else if (entry.type === 'autoArray') {\n const getResult =\n entry.sourceDescriptorInner.getValue(source);\n // null/undefined → skip (spec §6); non-array → error\n if (getResult.success && getResult.value != null) {\n if (!Array.isArray(getResult.value)) {\n throw new MapperConfigurationError(\n `Expected array for property \"${key}\" but got ${typeof getResult.value}`\n );\n }\n value = await Promise.all(\n getResult.value.map(entry.elementMapper!)\n );\n } else {\n continue;\n }\n }\n\n const targetDescriptor = (targetTree as any)[key];\n if (targetDescriptor?.[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]) {\n targetDescriptor[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ].setValue(result, value, {\n createMissingStructure: true\n });\n } else {\n result[key] = value;\n }\n }\n\n return result;\n };\n\n // The same finalized entries drive the sync executor. Only execution differs;\n // descriptor selection, automatic mapping and completeness are shared above.\n const syncEntries = [...mappings].map(([key, entry]) => {\n const nested = entry.autoMapper ?? entry.elementMapper;\n const nestedSync = nested\n ? syncImplementations.get(nested)\n : undefined;\n if (\n (nested && !nestedSync) ||\n entry.fn?.constructor.name === 'AsyncFunction'\n )\n return null;\n return { key, entry, nestedSync };\n });\n if (syncEntries.every(entry => entry !== null)) {\n syncImplementations.set(mapperFn, source => {\n const result: Record<string, any> = {};\n for (const { key, entry, nestedSync } of syncEntries) {\n if (entry.type === 'ignore') continue;\n let value: any;\n if (entry.type === 'custom')\n value = assertSync(entry.fn!(source), key);\n else {\n const read =\n entry.sourceDescriptorInner.getValue(source);\n if (!read.success) continue;\n value = read.value;\n if (entry.type === 'auto') {\n if (value === undefined) continue;\n value = assertSync(nestedSync!(value), key);\n } else if (entry.type === 'autoArray') {\n if (value == null) continue;\n if (!Array.isArray(value))\n throw new MapperConfigurationError(\n `Expected array for property \"${key}\" but got ${typeof value}`\n );\n value = value.map(element =>\n assertSync(nestedSync!(element), key)\n );\n }\n }\n const target = (targetTree as any)[key]?.[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ];\n if (target)\n target.setValue(result, value, {\n createMissingStructure: true\n });\n else result[key] = value;\n }\n return result;\n });\n }\n\n // Auto-register in the registry if available\n if (this._registry) {\n this._registry['_mappers']\n .get(this._fromSchema)\n ?.set(this._toSchema, mapperFn);\n }\n\n return mapperFn;\n }\n}\n\n// ── MappingRegistry ───────────────────────────────────────────────────\n\nexport class MappingRegistry<TRegistered = never, TAsyncRegistered = never> {\n protected readonly _mappers: Map<\n ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n Map<\n ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n SchemaToSchemaMapperResult<any, any>\n >\n > = new Map();\n\n #ensureObjectSchemas(\n fromSchema: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n toSchema: ObjectSchemaBuilder<any, any, any, any, any, any, any>\n ): boolean {\n return !(\n !fromSchema ||\n !toSchema ||\n fromSchema instanceof ObjectSchemaBuilder === false ||\n toSchema instanceof ObjectSchemaBuilder === false\n );\n }\n\n /**\n * Defines a mapping between two schemas and returns a new immutable\n * registry containing the mapping. The callback `fn` receives a fresh\n * `Mapper` and must return it after configuring property mappings.\n * The mapper is automatically finalized and registered. Properties\n * not explicitly mapped or ignored may be auto-mapped if a matching\n * nested mapping is already registered in the registry.\n *\n * @param fromSchema - source ObjectSchemaBuilder\n * @param toSchema - target ObjectSchemaBuilder\n * @param fn - callback that configures property mappings on the mapper\n * @returns a new MappingRegistry containing all previous mappings plus\n * the newly configured one\n * @throws if schemas are invalid, mapping is duplicate, or unmapped\n * properties remain that cannot be auto-mapped\n */\n public configure<\n TFromSchema extends ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >,\n TToSchema extends ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >,\n TSteps = {}\n >(\n fromSchema: TFromSchema,\n toSchema: TToSchema,\n fn: (\n mapper: Mapper<\n TFromSchema,\n TToSchema,\n KeysNeedingMapping<TFromSchema, TToSchema, TRegistered>,\n TRegistered,\n TAsyncRegistered\n >\n ) => Mapper<\n TFromSchema,\n TToSchema,\n never,\n TRegistered,\n TAsyncRegistered,\n TSteps\n >\n ): MappingRegistry<\n TRegistered | [TFromSchema, TToSchema],\n | TAsyncRegistered\n | ([\n AsyncKeys<TFromSchema, TToSchema, TAsyncRegistered, TSteps>\n ] extends [never]\n ? never\n : [TFromSchema, TToSchema])\n > {\n if (!this.#ensureObjectSchemas(fromSchema, toSchema)) {\n throw new Error(\n 'Both fromSchema and toSchema must be instances of ObjectSchemaBuilder'\n );\n }\n\n // Check for duplicate mappings\n const existingFromMappers = this._mappers.get(fromSchema);\n if (existingFromMappers?.has(toSchema)) {\n throw new Error(\n 'Duplicate mapping: a mapping for this schemas pair is already registered'\n );\n }\n\n // Create new immutable registry with cloned mappings\n const newRegistry = new MappingRegistry<\n TRegistered | [TFromSchema, TToSchema],\n | TAsyncRegistered\n | ([\n AsyncKeys<TFromSchema, TToSchema, TAsyncRegistered, TSteps>\n ] extends [never]\n ? never\n : [TFromSchema, TToSchema])\n >();\n for (const [from, toMap] of this._mappers) {\n newRegistry._mappers.set(from, new Map(toMap));\n }\n\n // Ensure the from→to map slot exists so getMapper can register\n if (!newRegistry._mappers.has(fromSchema)) {\n newRegistry._mappers.set(fromSchema, new Map());\n }\n\n // Create mapper with new registry reference\n const mapper = new Mapper<\n TFromSchema,\n TToSchema,\n KeysNeedingMapping<TFromSchema, TToSchema, TRegistered>,\n TRegistered,\n TAsyncRegistered\n >(fromSchema, toSchema, newRegistry);\n\n // Configure via user callback\n const configuredMapper = fn(mapper);\n\n // Implicit finalization: auto-map + validate + register\n configuredMapper.getMapper();\n\n return newRegistry;\n }\n\n /**\n * Retrieve a registered synchronous mapper inferred from configure().\n * The pair must be registered and its final steps must all be synchronous.\n * Async mappings remain usable with getMapper(); configuration never executes\n * compute callbacks. Unexpected thenables throw when the returned mapper runs.\n * @throws MapperConfigurationError for an asynchronous dependency.\n * @throws Error if the schema pair has not been registered.\n */\n public getSyncMapper<\n TFromSchema extends ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >,\n TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n fromSchema: TFromSchema,\n toSchema: TToSchema,\n ..._args: RegisteredSyncArguments<\n [TFromSchema, TToSchema],\n TRegistered,\n TAsyncRegistered\n >\n ): SyncSchemaToSchemaMapperResult<TFromSchema, TToSchema> {\n return synchronous(this.getMapper(fromSchema, toSchema));\n }\n\n /**\n * Gets a mapper function that will map from the fromSchema to the toSchema.\n * Throws an error if no mapper is found for the given schemas pair.\n * @param fromSchema a schema to map from\n * @param toSchema a schema to map to\n * @returns a function that will take a value of the fromSchema type as an argument and\n * return a promise resolving to a value of the toSchema type\n */\n public getMapper<\n TFromSchema extends ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >,\n TToSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n fromSchema: TFromSchema,\n toSchema: TToSchema\n ): SchemaToSchemaMapperResult<TFromSchema, TToSchema> {\n if (!this.#ensureObjectSchemas(fromSchema, toSchema)) {\n throw new Error(\n 'Both fromSchema and toSchema must be instances of ObjectSchemaBuilder'\n );\n }\n\n const mappers = this._mappers.get(fromSchema);\n if (!mappers) {\n throw new Error('No mapper found for the given schemas pair');\n }\n const mapper = mappers.get(toSchema);\n if (!mapper) {\n throw new Error('No mapper found for the given schemas pair');\n }\n\n return mapper;\n }\n}\n\n/**\n * Creates a new empty {@link MappingRegistry}.\n *\n * This is a convenience factory function — an alternative to\n * `new MappingRegistry()` that reads better in a fluent chain:\n *\n * ```ts\n * const registry = mapper()\n * .configure(A, B, m => ...)\n * .configure(C, D, m => ...);\n * ```\n */\nexport function mapper(): MappingRegistry {\n return new MappingRegistry();\n}\n"],"mappings":"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","names":["ArraySchemaBuilder","ObjectSchemaBuilder","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","SYMBOL_TARGET_PROPERTY_KEY","syncImplementations","assertSync","value","key","MapperConfigurationError","synchronous","fn","result","messageOrUnmappedProperties","resolveElementMapper","sourceElementSchema","targetElementSchema","registry","ObjectSchemaBuilder","autoMapper","fromKeys","toKeys","k","i","copy","element","ArraySchemaBuilder","innerSourceElement","innerTargetElement","innerMapper","mapArray","arr","sync","sourceIntrospection","targetIntrospection","PropertyMappingBuilder","mapper","targetKey","selector","_args","sourceTree","inner","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","sourceSchema","targetPropSchema","elementMapper","Mapper","fromSchema","toSchema","capturedKey","proxy","_target","prop","SYMBOL_TARGET_PROPERTY_KEY","toIntrospection","targetProperties","fromProperties","toProperties","fromTree","fromPropSchema","toPropSchema","sourceDescriptor","unmapped","targetTree","mappings","mapperFn","source","entry","getResult","targetDescriptor","syncEntries","nested","nestedSync","read","target","MappingRegistry","_MappingRegistry","#ensureObjectSchemas","newRegistry","from","toMap","mappers"]}
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"email": "andrew_zol@cleverbrush.com"
|
|
6
6
|
},
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@cleverbrush/schema": "^
|
|
8
|
+
"@cleverbrush/schema": "^5.1.0"
|
|
9
9
|
},
|
|
10
10
|
"description": "Type-safe, schema-driven object mapper for @cleverbrush/schema — compile-time completeness, selector-based field mapping, auto-mapping",
|
|
11
11
|
"files": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"data transformation",
|
|
21
21
|
"cleverbrush"
|
|
22
22
|
],
|
|
23
|
-
"license": "BSD
|
|
23
|
+
"license": "BSD-3-Clause",
|
|
24
24
|
"main": "./dist/index.js",
|
|
25
25
|
"exports": {
|
|
26
26
|
".": {
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"sideEffects": false,
|
|
32
32
|
"name": "@cleverbrush/mapper",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=24"
|
|
35
|
+
},
|
|
33
36
|
"readme": "https://github.com/cleverbrush/framework/tree/master/libs/mapper#readme",
|
|
34
37
|
"repository": {
|
|
35
38
|
"type": "git",
|
|
@@ -42,5 +45,5 @@
|
|
|
42
45
|
},
|
|
43
46
|
"type": "module",
|
|
44
47
|
"types": "./dist/index.d.ts",
|
|
45
|
-
"version": "
|
|
48
|
+
"version": "5.1.0"
|
|
46
49
|
}
|