@tsed/json-mapper 8.44.3 → 8.44.4

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.
@@ -14,6 +14,22 @@ import { getJsonMapperTypes } from "./JsonMapperTypesContainer.js";
14
14
  function hasConversionMethod(mapper, name) {
15
15
  return isFunction(mapper[name]);
16
16
  }
17
+ /**
18
+ * The registered function reads the mapper methods at call time, so a replaced method needs no registration.
19
+ */
20
+ function createTypeMapperEntry(mapper, model, name) {
21
+ const hasConversion = hasConversionMethod(mapper, name);
22
+ const fn = hasConversion
23
+ ? (value, options) => mapper[name](value, options)
24
+ : (value, options) => mapper.deserialize(value, { ...options, type: model });
25
+ return { mapper, name, hasConversion, fn };
26
+ }
27
+ /**
28
+ * The function must be built again when the mapper gained or lost the method named after the type.
29
+ */
30
+ function hasTypeMapperChanged(entry, mapper) {
31
+ return entry.hasConversion !== hasConversionMethod(mapper, entry.name);
32
+ }
17
33
  function isDeserializable(obj, options) {
18
34
  if ((!!options.collectionType && isNil(obj)) || obj === undefined) {
19
35
  return false;
@@ -45,11 +61,6 @@ export class JsonDeserializer extends JsonMapperCompiler {
45
61
  * @protected
46
62
  */
47
63
  this.constructors = {};
48
- /**
49
- * The type mappers registered from the types container, with the function each one was registered with
50
- * @protected
51
- */
52
- this.typeMappers = new Map();
53
64
  this.addGlobal("newInstanceOf", this.newInstanceOf.bind(this));
54
65
  this.addGlobal("alterBeforeDeserialize", this.alterBeforeDeserialize.bind(this));
55
66
  this.addGlobal("alterAfterDeserialize", this.alterAfterDeserialize.bind(this));
@@ -210,43 +221,12 @@ export class JsonDeserializer extends JsonMapperCompiler {
210
221
  }
211
222
  return (writer) => writer.callMapper(nestedMapper.id, varKey(key), formatOpts);
212
223
  }
213
- /**
214
- * A type mapper must be registered again when it was never registered, when the mapper changed, when the mapper
215
- * gained or lost the method named after the type, or when its entry was replaced or removed from the cache.
216
- * The registered function reads the mapper methods at call time, so a replaced method needs no registration.
217
- */
218
- isTypeMapperOutdated(model, mapper) {
219
- const registered = this.typeMappers.get(model);
220
- if (!registered) {
221
- return true;
222
- }
223
- const mapperChanged = registered.mapper !== mapper;
224
- const hasConversion = hasConversionMethod(mapper, registered.name);
225
- const conversionChanged = registered.hasConversion !== hasConversion;
226
- // two models with the same name (Date and "Date") share the mapper table entry: the last registered one wins
227
- const entryReplaced = this.cache.get(model)?.get("typeMapper")?.fn !== registered.fn || this.mappers[registered.name] !== registered.fn;
228
- return mapperChanged || conversionChanged || entryReplaced;
229
- }
230
- registerTypeMapper(model, mapper) {
231
- const name = nameOf(model);
232
- const hasConversion = hasConversionMethod(mapper, name);
233
- const fn = hasConversion
234
- ? (value, options) => mapper[name](value, options)
235
- : (value, options) => mapper.deserialize(value, { ...options, type: model });
236
- this.addTypeMapper(model, fn);
237
- this.typeMappers.set(model, { mapper, name, hasConversion, fn });
238
- }
239
224
  mapOptions({ groups, useAlias = true, types, ...options }) {
240
225
  if (options.store instanceof JsonParameterStore) {
241
226
  return this.mapOptions(mapParamStoreOptions(options.store, options));
242
227
  }
243
228
  const customMappers = {};
244
- types = types || getJsonMapperTypes();
245
- types.forEach((mapper, model) => {
246
- if (model !== Array && model !== Set && model !== Map && this.isTypeMapperOutdated(model, mapper)) {
247
- this.registerTypeMapper(model, mapper);
248
- }
249
- });
229
+ this.syncTypeMappers(types || getJsonMapperTypes(), createTypeMapperEntry, hasTypeMapperChanged);
250
230
  const strictGroups = options.strictGroups ?? JsonMapperSettings.strictGroups;
251
231
  return {
252
232
  ...options,
@@ -17,6 +17,11 @@ export class JsonMapperCompiler {
17
17
  * @protected
18
18
  */
19
19
  this.types = new Map();
20
+ /**
21
+ * The type mappers registered from the types container, with the function each one was registered with
22
+ * @protected
23
+ */
24
+ this.typeMappers = new Map();
20
25
  /**
21
26
  * Cached executable mappers by his id
22
27
  * @protected
@@ -124,6 +129,38 @@ export class JsonMapperCompiler {
124
129
  }
125
130
  return storeGroups.get(groupsId);
126
131
  }
132
+ /**
133
+ * Register the type mappers of the types container (Array, Set and Map excepted: the compiler maps them itself).
134
+ * A type mapper is registered again only when it is outdated, so `map()` does not rebuild them on every call.
135
+ *
136
+ * @param types The type mappers by model.
137
+ * @param create Build the entry, and the function, a type mapper is registered with.
138
+ * @param hasChanged Tell whether the mapper changed since its entry was built, beyond being replaced by another mapper.
139
+ */
140
+ syncTypeMappers(types, create, hasChanged) {
141
+ types.forEach((mapper, model) => {
142
+ if (model !== Array && model !== Set && model !== Map && this.isTypeMapperOutdated(model, mapper, hasChanged)) {
143
+ const entry = create(mapper, model, nameOf(model));
144
+ this.addTypeMapper(model, entry.fn);
145
+ this.typeMappers.set(model, entry);
146
+ }
147
+ });
148
+ }
149
+ /**
150
+ * A type mapper must be registered again when it was never registered, when the mapper was replaced or changed,
151
+ * or when its entry was replaced or removed from the cache.
152
+ */
153
+ isTypeMapperOutdated(model, mapper, hasChanged) {
154
+ const registered = this.typeMappers.get(model);
155
+ if (!registered) {
156
+ return true;
157
+ }
158
+ return (registered.mapper !== mapper ||
159
+ hasChanged(registered, mapper) ||
160
+ // two models with the same name (Date and "Date") share the mapper table entry: the last registered one wins
161
+ this.cache.get(model)?.get("typeMapper")?.fn !== registered.fn ||
162
+ this.mappers[registered.name] !== registered.fn);
163
+ }
127
164
  execMapper(id, value, options) {
128
165
  if (isObjectID(value)) {
129
166
  return value.toString();
@@ -55,6 +55,21 @@ function createTypeMapper(mapper, model, name) {
55
55
  type: model
56
56
  });
57
57
  }
58
+ function createTypeMapperEntry(mapper, model, name) {
59
+ return {
60
+ mapper,
61
+ name,
62
+ serialize: mapper.serialize,
63
+ conversion: mapper[name],
64
+ fn: createTypeMapper(mapper, model, name)
65
+ };
66
+ }
67
+ /**
68
+ * The function of a type mapper is built from its `serialize` and conversion methods: it must be built again when one changed.
69
+ */
70
+ function hasTypeMapperChanged(entry, mapper) {
71
+ return entry.serialize !== mapper.serialize || entry.conversion !== mapper[entry.name];
72
+ }
58
73
  /**
59
74
  * Serializes data structures according to the metadata captured by `@tsed/schema`.
60
75
  *
@@ -64,11 +79,6 @@ function createTypeMapper(mapper, model, name) {
64
79
  export class JsonSerializer extends JsonMapperCompiler {
65
80
  constructor() {
66
81
  super();
67
- /**
68
- * The type mappers registered from the types container, with the function each one was registered with
69
- * @protected
70
- */
71
- this.typeMappers = new Map();
72
82
  this.addTypeMapper(Object, this.mapObject.bind(this));
73
83
  this.addTypeMapper(Array, this.mapArray.bind(this));
74
84
  this.addTypeMapper(Map, this.mapMap.bind(this));
@@ -117,32 +127,9 @@ export class JsonSerializer extends JsonMapperCompiler {
117
127
  writer.add(this.mapAdditionalProperties(entity, properties));
118
128
  return writer.return("obj").root().toString();
119
129
  }
120
- /**
121
- * A type mapper must be registered again when it was never registered, when the mapper or one of the
122
- * methods its function was built from changed, or when its entry was replaced or removed from the cache.
123
- */
124
- isTypeMapperOutdated(model, mapper) {
125
- const registered = this.typeMappers.get(model);
126
- if (!registered) {
127
- return true;
128
- }
129
- const mapperChanged = registered.mapper !== mapper;
130
- const methodsChanged = registered.serialize !== mapper.serialize || registered.conversion !== mapper[registered.name];
131
- // two models with the same name (Date and "Date") share the mapper table entry: the last registered one wins
132
- const entryReplaced = this.cache.get(model)?.get("typeMapper")?.fn !== registered.fn || this.mappers[registered.name] !== registered.fn;
133
- return mapperChanged || methodsChanged || entryReplaced;
134
- }
135
130
  mapOptions({ groups, useAlias = true, types, ...options }) {
136
131
  const customMappers = {};
137
- types = types || getJsonMapperTypes();
138
- types.forEach((mapper, model) => {
139
- if (![Array, Set, Map].includes(model) && this.isTypeMapperOutdated(model, mapper)) {
140
- const name = nameOf(model);
141
- const fn = createTypeMapper(mapper, model, name);
142
- this.addTypeMapper(model, fn);
143
- this.typeMappers.set(model, { mapper, name, serialize: mapper.serialize, conversion: mapper[name], fn });
144
- }
145
- });
132
+ this.syncTypeMappers(types || getJsonMapperTypes(), createTypeMapperEntry, hasTypeMapperChanged);
146
133
  const strictGroups = options.strictGroups ?? JsonMapperSettings.strictGroups;
147
134
  return {
148
135
  ...options,
@@ -1,17 +1,6 @@
1
- import { CachedGroupsJsonMapper, CachedJsonMapper, JsonMapperCallback, JsonMapperCompiler } from "./JsonMapperCompiler.js";
1
+ import { CachedGroupsJsonMapper, CachedJsonMapper, JsonMapperCompiler } from "./JsonMapperCompiler.js";
2
2
  import { Type } from "@tsed/core";
3
- import { JsonMapperMethods } from "../interfaces/JsonMapperMethods.js";
4
3
  import { JsonDeserializerOptions } from "./JsonDeserializerOptions.js";
5
- /**
6
- * A registered type mapper. `hasConversion` tells whether the mapper had a method named after the type
7
- * (e.g. `PrimitiveMapper.String`) when it was registered: the function calls that method, otherwise `deserialize()`.
8
- */
9
- type TypeMapperEntry = {
10
- mapper: JsonMapperMethods;
11
- name: string;
12
- hasConversion: boolean;
13
- fn: JsonMapperCallback<any>;
14
- };
15
4
  /**
16
5
  * Deserializes raw JSON data into class instances based on schema metadata,
17
6
  * handling collections, discriminators, hooks, and registered type mappers.
@@ -22,11 +11,6 @@ export declare class JsonDeserializer extends JsonMapperCompiler<JsonDeserialize
22
11
  * @protected
23
12
  */
24
13
  protected constructors: Record<string, Type<any>>;
25
- /**
26
- * The type mappers registered from the types container, with the function each one was registered with
27
- * @protected
28
- */
29
- protected typeMappers: Map<any, TypeMapperEntry>;
30
14
  constructor();
31
15
  map<Model = any>(input: any, options?: JsonDeserializerOptions): Model;
32
16
  compile(model: Type<any>, groups: false | string[]): CachedJsonMapper<JsonDeserializerOptions>;
@@ -42,13 +26,6 @@ export declare class JsonDeserializer extends JsonMapperCompiler<JsonDeserialize
42
26
  private mapDiscriminator;
43
27
  private mapProperty;
44
28
  private getPropertyFiller;
45
- /**
46
- * A type mapper must be registered again when it was never registered, when the mapper changed, when the mapper
47
- * gained or lost the method named after the type, or when its entry was replaced or removed from the cache.
48
- * The registered function reads the mapper methods at call time, so a replaced method needs no registration.
49
- */
50
- private isTypeMapperOutdated;
51
- private registerTypeMapper;
52
29
  private mapOptions;
53
30
  private mapAdditionalProperties;
54
31
  private mapObject;
@@ -59,4 +36,3 @@ export declare class JsonDeserializer extends JsonMapperCompiler<JsonDeserialize
59
36
  private alterAfterDeserialize;
60
37
  private mapGenericsOptions;
61
38
  }
62
- export {};
@@ -1,5 +1,6 @@
1
1
  import { Type } from "@tsed/core";
2
2
  import { JsonSchema } from "@tsed/schema";
3
+ import type { JsonMapperMethods } from "../interfaces/JsonMapperMethods.js";
3
4
  /**
4
5
  * Signature exposed by compiled mappers. They accept an input value plus contextual options
5
6
  * and return the serialized/deserialized result.
@@ -16,6 +17,15 @@ export type CachedJsonMapper<Options> = {
16
17
  * Cached mapper registry keyed by the groups fingerprint generated for a schema.
17
18
  */
18
19
  export type CachedGroupsJsonMapper<Options> = Map<string, CachedJsonMapper<Options>>;
20
+ /**
21
+ * A type mapper registered from the types container: the mapper, the name of its model and the function it was
22
+ * registered with. Subclasses add the values their outdated check compares.
23
+ */
24
+ export type TypeMapperEntry = {
25
+ mapper: JsonMapperMethods;
26
+ name: string;
27
+ fn: JsonMapperCallback<any>;
28
+ };
19
29
  /**
20
30
  * Base compiler that turns schema metadata into executable (de)serialization functions.
21
31
  * Subclasses supply `map`, `alterValue`, and `createMapper` implementations to specialize
@@ -33,6 +43,11 @@ export declare abstract class JsonMapperCompiler<Options extends Record<string,
33
43
  * @protected
34
44
  */
35
45
  protected types: Map<Type<any>, Type<any>>;
46
+ /**
47
+ * The type mappers registered from the types container, with the function each one was registered with
48
+ * @protected
49
+ */
50
+ protected typeMappers: Map<any, TypeMapperEntry>;
36
51
  /**
37
52
  * Cached executable mappers by his id
38
53
  * @protected
@@ -69,6 +84,20 @@ export declare abstract class JsonMapperCompiler<Options extends Record<string,
69
84
  compile(model: Type<any> | string, groups: false | string[], opts?: {
70
85
  mapper?: any;
71
86
  }): CachedJsonMapper<Options>;
87
+ /**
88
+ * Register the type mappers of the types container (Array, Set and Map excepted: the compiler maps them itself).
89
+ * A type mapper is registered again only when it is outdated, so `map()` does not rebuild them on every call.
90
+ *
91
+ * @param types The type mappers by model.
92
+ * @param create Build the entry, and the function, a type mapper is registered with.
93
+ * @param hasChanged Tell whether the mapper changed since its entry was built, beyond being replaced by another mapper.
94
+ */
95
+ protected syncTypeMappers<Entry extends TypeMapperEntry>(types: Map<Type | Symbol | string, JsonMapperMethods>, create: (mapper: JsonMapperMethods, model: any, name: string) => Entry, hasChanged: (entry: Entry, mapper: JsonMapperMethods) => boolean): void;
96
+ /**
97
+ * A type mapper must be registered again when it was never registered, when the mapper was replaced or changed,
98
+ * or when its entry was replaced or removed from the cache.
99
+ */
100
+ private isTypeMapperOutdated;
72
101
  protected execMapper(id: string, value: any, options: Options): any;
73
102
  protected abstract map(input: any, options: Options): any;
74
103
  protected abstract alterValue(schemaId: string, value: any, options: Options): any;
@@ -1,14 +1,6 @@
1
1
  import { Type } from "@tsed/core";
2
- import { JsonMapperMethods } from "../interfaces/JsonMapperMethods.js";
3
- import { JsonMapperCompiler, JsonMapperCallback } from "./JsonMapperCompiler.js";
2
+ import { JsonMapperCompiler } from "./JsonMapperCompiler.js";
4
3
  import { JsonSerializerOptions } from "./JsonSerializerOptions.js";
5
- type TypeMapperEntry = {
6
- mapper: JsonMapperMethods;
7
- name: string;
8
- serialize: any;
9
- conversion: any;
10
- fn: JsonMapperCallback<any>;
11
- };
12
4
  /**
13
5
  * Serializes data structures according to the metadata captured by `@tsed/schema`.
14
6
  *
@@ -16,20 +8,10 @@ type TypeMapperEntry = {
16
8
  * registered type mappers (e.g., components in `src/components`).
17
9
  */
18
10
  export declare class JsonSerializer extends JsonMapperCompiler<JsonSerializerOptions> {
19
- /**
20
- * The type mappers registered from the types container, with the function each one was registered with
21
- * @protected
22
- */
23
- protected typeMappers: Map<any, TypeMapperEntry>;
24
11
  constructor();
25
12
  map(input: any, options?: JsonSerializerOptions): any;
26
13
  protected alterValue(schemaId: string, value: any, options: JsonSerializerOptions): any;
27
14
  protected createMapper(model: Type<any>, id: string, groups: false | string[]): string;
28
- /**
29
- * A type mapper must be registered again when it was never registered, when the mapper or one of the
30
- * methods its function was built from changed, or when its entry was replaced or removed from the cache.
31
- */
32
- private isTypeMapperOutdated;
33
15
  private mapOptions;
34
16
  private mapProperty;
35
17
  private getPropertyFiller;
@@ -44,4 +26,3 @@ export declare class JsonSerializer extends JsonMapperCompiler<JsonSerializerOpt
44
26
  private mapItem;
45
27
  private mapJSON;
46
28
  }
47
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsed/json-mapper",
3
- "version": "8.44.3",
3
+ "version": "8.44.4",
4
4
  "description": "Json mapper module for Ts.ED Framework",
5
5
  "keywords": [
6
6
  "TypeScript",
@@ -41,10 +41,10 @@
41
41
  "tslib": ">=2.7.0"
42
42
  },
43
43
  "devDependencies": {
44
- "@tsed/barrels": "8.44.3",
45
- "@tsed/core": "8.44.3",
46
- "@tsed/schema": "8.44.3",
47
- "@tsed/typescript": "8.44.3",
44
+ "@tsed/barrels": "8.44.4",
45
+ "@tsed/core": "8.44.4",
46
+ "@tsed/schema": "8.44.4",
47
+ "@tsed/typescript": "8.44.4",
48
48
  "temporal-polyfill": "1.0.1",
49
49
  "typescript": "6.0.2",
50
50
  "vitest": "4.1.4"