@smartlyio/oats-runtime 4.4.0 → 4.4.1-alpha.18

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/dist/server.js CHANGED
@@ -4,6 +4,7 @@ exports.createHandlerFactory = exports.assertMethod = exports.supportedMethods =
4
4
  const assert_1 = require("./assert");
5
5
  const safe_navigation_1 = require("@smartlyio/safe-navigation");
6
6
  const make_1 = require("./make");
7
+ const serialize_1 = require("./serialize");
7
8
  class RequestValidationError extends Error {
8
9
  constructor(tag, errors) {
9
10
  super('invalid request ' + tag + ' ' + errors.map(make_1.validationErrorPrinter).join('\n'));
@@ -46,30 +47,55 @@ function voidify(value) {
46
47
  }
47
48
  return null;
48
49
  }
49
- function cleanHeaders(maker, headers) {
50
- const normalized = voidify(lowercaseObject(headers));
50
+ function cleanHeaders(mode, maker, headers) {
51
+ // note: we now expect that on client side headers are type conforming so do not need to lowercase those
52
+ // this is slightly breaking change is somebody has subverted the type checker
53
+ const normalized = voidify(mode === 'server' ? lowercaseObject(headers) : headers);
51
54
  const acceptsNull = maker(null);
52
55
  if (acceptsNull.isSuccess()) {
53
56
  return acceptsNull.success();
54
57
  }
55
58
  return maker(normalized, {
56
- unknownField: 'drop'
59
+ unknownField: 'drop',
60
+ convertFromNetwork: mode === 'server'
57
61
  }).success(throwRequestValidationError.bind(null, 'headers'));
58
62
  }
59
- function safe(headers, params, query, body, response, endpoint, { validationOptions = {} } = {}) {
63
+ function serializeWhenClient(mode, value) {
64
+ if (mode === 'client') {
65
+ return (0, serialize_1.serialize)(value);
66
+ }
67
+ return value;
68
+ }
69
+ function getOutBody(mode, value) {
70
+ if (!value) {
71
+ return value;
72
+ }
73
+ return { contentType: value.contentType, value: serializeWhenClient(mode, value.value) };
74
+ }
75
+ function safe(headers, params, query, body, response, endpoint, { validationOptions = {}, mode } = { mode: 'client' }) {
60
76
  return async (ctx) => {
77
+ // note: data coming to client adapter needs to be serialized and data coming from the adapter needs conversion from network
78
+ // note: data coming to server adapter needs to be deserialized and data coming from the adapter needs serialization
79
+ // this is all very clear.
80
+ const internalMakeOptions = { convertFromNetwork: mode === 'server' };
61
81
  const result = await endpoint({
62
82
  path: ctx.path,
63
83
  method: ctx.method,
64
84
  servers: ctx.servers,
65
85
  op: ctx.op,
66
- headers: cleanHeaders(headers, ctx.headers),
67
- params: params(voidify(ctx.params), validationOptions.params).success(throwRequestValidationError.bind(null, 'params')),
68
- query: query(ctx.query || {}, validationOptions.query).success(throwRequestValidationError.bind(null, 'query')),
69
- body: body(voidify(ctx.body)).success(throwRequestValidationError.bind(null, 'body')),
86
+ headers: serializeWhenClient(mode, cleanHeaders(mode, headers, ctx.headers)),
87
+ // note: path params come to the adapter in network format always as those are gleaned from the path definition
88
+ params: params(voidify(ctx.params), Object.assign(Object.assign(Object.assign({}, validationOptions.params), internalMakeOptions), { convertFromNetwork: true })).success(throwRequestValidationError.bind(null, 'params')),
89
+ query: serializeWhenClient(mode, query(ctx.query || {}, Object.assign(Object.assign({}, validationOptions.query), internalMakeOptions)).success(throwRequestValidationError.bind(null, 'query'))),
90
+ body: getOutBody(mode, body(voidify(ctx.body), internalMakeOptions).success(throwRequestValidationError.bind(null, 'body'))),
70
91
  requestContext: ctx.requestContext
71
92
  });
72
- return response(result).success(throwResponseValidationError.bind(null, `body ${ctx.path}`, result.value.value));
93
+ const responseValue = response(result, { convertFromNetwork: mode === 'client' }).success(throwResponseValidationError.bind(null, `body ${ctx.path}`, result.value.value));
94
+ if (mode === 'client' || !responseValue) {
95
+ return responseValue;
96
+ }
97
+ // the response must be serialized for transferring from server to client
98
+ return Object.assign(Object.assign({}, responseValue), { headers: (0, serialize_1.serialize)(responseValue.headers), value: Object.assign(Object.assign({}, responseValue.value), { value: (0, serialize_1.serialize)(responseValue.value.value) }) });
73
99
  };
74
100
  }
75
101
  exports.safe = safe;
@@ -110,7 +136,7 @@ function createHandlerFactory(handlers, opts) {
110
136
  const methodHandler = endpoint[method];
111
137
  const endpointWrapper = (0, safe_navigation_1.default)(tree)[path][method].$;
112
138
  (0, assert_1.assert)(endpointWrapper, 'unknown endpoint ' + method.toUpperCase() + ' ' + path);
113
- adapter(path, endpointWrapper.op, assertMethod(method), endpointWrapper.safeHandler(methodHandler, opts), endpointWrapper.servers);
139
+ adapter(path, endpointWrapper.op, assertMethod(method), endpointWrapper.safeHandler(methodHandler, Object.assign(Object.assign({}, opts), { mode: 'server' })), endpointWrapper.servers);
114
140
  });
115
141
  });
116
142
  };
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { Type } from './reflection-type';
2
+ export declare function withType<A>(to: A, type: Type[]): A;
3
+ export declare function getTypeSet(value: Record<string, any>): Set<Type> | undefined;
4
+ /** Get reflection type from a made value for serialization.
5
+ * Note that only directly made object values or ValueClasses can be used
6
+ * */
7
+ export declare function getType(value: Record<string, any>): Type[] | undefined;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getType = exports.getTypeSet = exports.withType = void 0;
4
+ const value_class_1 = require("./value-class");
5
+ function withType(to, type) {
6
+ if (type.length === 0) {
7
+ return to;
8
+ }
9
+ // todo: is it possible to confuse typing by re-use of valueclassed values
10
+ // eg. with value 'v: SomeValueClass' passing it to multiple makers
11
+ // -> no? the re-use happens only if the valueclass instance matches so it
12
+ // will not mutate the tagged reflection types
13
+ const previous = getTypeSet(to);
14
+ if (previous) {
15
+ type.forEach(type => previous.add(type));
16
+ return to;
17
+ }
18
+ const newType = new Set(type);
19
+ // tag each constructed object with a hidden type property
20
+ Object.defineProperty(to, reflection, {
21
+ enumerable: false,
22
+ value: newType
23
+ });
24
+ return to;
25
+ }
26
+ exports.withType = withType;
27
+ function getTypeSet(value) {
28
+ if (!value || typeof value !== 'object') {
29
+ return;
30
+ }
31
+ // NOTE: prefer using the added reflection type instead of the contstructor type
32
+ // the added reflection type will have types from eg allOf
33
+ // @ts-ignore
34
+ const t = value[reflection];
35
+ if (t && t.size > 0) {
36
+ return t;
37
+ }
38
+ if (value instanceof value_class_1.ValueClass) {
39
+ // a bit of leap here to trust that all ValueClasses have generated `reflection`
40
+ // @ts-ignore
41
+ const classType = new Set([value.constructor.reflection().definition]);
42
+ // tag each constructed object with a hidden type property
43
+ // this ensures that ValueClasses have a mutable reflection type property
44
+ Object.defineProperty(value, reflection, {
45
+ enumerable: false,
46
+ value: classType
47
+ });
48
+ return classType;
49
+ }
50
+ return;
51
+ }
52
+ exports.getTypeSet = getTypeSet;
53
+ /** Get reflection type from a made value for serialization.
54
+ * Note that only directly made object values or ValueClasses can be used
55
+ * */
56
+ function getType(value) {
57
+ const t = getTypeSet(value);
58
+ if (t) {
59
+ return [...t];
60
+ }
61
+ }
62
+ exports.getType = getType;
63
+ const reflection = Symbol('reflection');
64
+ //# sourceMappingURL=type-tag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-tag.js","sourceRoot":"","sources":["../src/type-tag.ts"],"names":[],"mappings":";;;AACA,+CAA2C;AAE3C,SAAgB,QAAQ,CAAI,EAAK,EAAE,IAAY;IAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QACrB,OAAO,EAAE,CAAC;KACX;IACD,0EAA0E;IAC1E,oEAAoE;IACpE,0EAA0E;IAC1E,8CAA8C;IAC9C,MAAM,QAAQ,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;IAChC,IAAI,QAAQ,EAAE;QACZ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACzC,OAAO,EAAE,CAAC;KACX;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,0DAA0D;IAC1D,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,UAAU,EAAE;QACpC,UAAU,EAAE,KAAK;QACjB,KAAK,EAAE,OAAO;KACf,CAAC,CAAC;IACH,OAAO,EAAE,CAAC;AACZ,CAAC;AApBD,4BAoBC;AAED,SAAgB,UAAU,CAAC,KAA0B;IACnD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QACvC,OAAO;KACR;IAED,gFAAgF;IAChF,0DAA0D;IAC1D,aAAa;IACb,MAAM,CAAC,GAAc,KAAK,CAAC,UAAU,CAAC,CAAC;IACvC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE;QACnB,OAAO,CAAC,CAAC;KACV;IACD,IAAI,KAAK,YAAY,wBAAU,EAAE;QAC/B,gFAAgF;QAChF,aAAa;QACb,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;QACvE,0DAA0D;QAC1D,yEAAyE;QACzE,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE;YACvC,UAAU,EAAE,KAAK;YACjB,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QACH,OAAO,SAAS,CAAC;KAClB;IACD,OAAO;AACT,CAAC;AAzBD,gCAyBC;AAED;;KAEK;AACL,SAAgB,OAAO,CAAC,KAA0B;IAChD,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;KACf;AACH,CAAC;AALD,0BAKC;AAED,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC"}
package/jest.config.js ADDED
@@ -0,0 +1,38 @@
1
+ module.exports = {
2
+ moduleFileExtensions: ['js', 'ts', 'tsx'],
3
+ moduleNameMapper: {
4
+ "@smartlyio/oats$": "<rootDir>/packages/oats/index.ts",
5
+ "@smartlyio/oats-runtime$": "<rootDir>/packages/oats-runtime/src/runtime.ts",
6
+ "@smartlyio/oats-axios-adapter$": "<rootDir>/packages/oats-axios-adapter/index.ts",
7
+ "@smartlyio/oats-koa-adapter$": "<rootDir>/packages/oats-koa-adapter/index.ts",
8
+ "@smartlyio/oats-fast-check$": "<rootDir>/packages/oats-fast-check/src/index.ts"
9
+ },
10
+
11
+ testRegex: '.*\\.spec.(tsx?)$',
12
+
13
+ collectCoverageFrom: [
14
+ 'packages/*/src/**/*.{ts,tsxx}',
15
+ '!packages/*/src/**/*.story.*'
16
+ ],
17
+
18
+ coverageThreshold: {
19
+ global: {
20
+ branches: 20,
21
+ statements: 20,
22
+ },
23
+ },
24
+
25
+ modulePathIgnorePatterns: ['<rootDir>/packages/*/dist'],
26
+
27
+ transform: {
28
+ '^.+\\.(ts|js)x?$': 'ts-jest',
29
+ },
30
+
31
+ globals: {
32
+ 'ts-jest': {
33
+ isolatedModules: true,
34
+ diagnostics: true,
35
+ },
36
+ },
37
+ setupFilesAfterEnv: [],
38
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartlyio/oats-runtime",
3
- "version": "4.4.0",
3
+ "version": "4.4.1-alpha.18+2b1a8cf",
4
4
  "license": "MIT",
5
5
  "description": "Runtime for Oats a Openapi3 based generator for typescript aware servers and clients",
6
6
  "private": false,
@@ -51,5 +51,5 @@
51
51
  "tsconfig-paths": "3.14.1",
52
52
  "typescript": "4.6.3"
53
53
  },
54
- "gitHead": "743a31718a2487b0cf7157ac1b6bc8872af256c1"
54
+ "gitHead": "2b1a8cf408f64c88102224b5e608961a4b65cbe1"
55
55
  }
package/src/client.ts CHANGED
@@ -184,7 +184,8 @@ function makeMethod(adapter: ClientAdapter, handler: server.Handler, pathParams:
184
184
  handler.body,
185
185
  handler.response,
186
186
  ctx =>
187
- adapter({ ...ctx, path: fillInPathParams(params, handler.path), servers: handler.servers })
187
+ adapter({ ...ctx, path: fillInPathParams(params, handler.path), servers: handler.servers }),
188
+ { mode: 'client' }
188
189
  );
189
190
  return (ctx: ClientArg<any, any, any>) =>
190
191
  call({
package/src/make.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { assert, fail } from './assert';
2
2
  import safe from '@smartlyio/safe-navigation';
3
3
  import * as _ from 'lodash';
4
+ import { isEqual, uniq } from 'lodash';
4
5
  import { ValueClass } from './value-class';
5
6
  import { NamedTypeDefinition, ObjectType, Type } from './reflection-type';
6
7
  import { discriminateUnion } from './union-discriminator';
7
- import { isEqual, uniq } from 'lodash';
8
+ import { getType, getTypeSet, withType } from './type-tag';
8
9
 
9
10
  export class MakeError extends Error {
10
11
  constructor(readonly errors: ValidationError[]) {
@@ -122,6 +123,9 @@ export interface MakeOptions {
122
123
  * (if query parameter is not repeated, it will not be an array on server side).
123
124
  */
124
125
  allowConvertForArrayType?: boolean;
126
+
127
+ /** If true convert property names from network to ts format while parsing objects */
128
+ convertFromNetwork?: boolean;
125
129
  }
126
130
 
127
131
  export type Maker<Shape, V> = (value: Shape, opts?: MakeOptions) => Make<V>;
@@ -363,16 +367,18 @@ export function makeOneOf(...options: any[]) {
363
367
  };
364
368
  }
365
369
 
366
- export function makeAllOf(...all: any[]) {
370
+ export function makeAllOf(...all: Maker<any, any>[]) {
367
371
  return (value: any, opts?: MakeOptions) => {
372
+ const types = [];
368
373
  for (const make of all) {
369
374
  const result: Make<any> = make(value, opts);
370
375
  if (result.isError()) {
371
376
  return result.errorPath('(allOf)');
372
377
  }
373
378
  value = result.success();
379
+ types.push(...(getTypeSet(value) ?? []));
374
380
  }
375
- return Make.ok(value);
381
+ return Make.ok(withType(value, types));
376
382
  };
377
383
  }
378
384
 
@@ -380,9 +386,45 @@ function isMagic(field: string) {
380
386
  return ['__proto__', 'constructor'].indexOf(field) >= 0;
381
387
  }
382
388
 
389
+ function getInputPropName(args: {
390
+ value: any;
391
+ index: string;
392
+ fromNetwork: Record<string, string>;
393
+ opts?: MakeOptions;
394
+ }) {
395
+ if (!args.opts?.convertFromNetwork) {
396
+ return args.index;
397
+ }
398
+ // we need to look at the types already used for making the value to detect cases
399
+ // where the property has already been mapped and we should use the ts side
400
+ // property name
401
+ const types = getType(args.value) ?? [];
402
+ const networkProp = args.fromNetwork?.[args.index] ?? args.index;
403
+ for (const type of types) {
404
+ if (type.type !== 'object') {
405
+ // only objects can have network mappings
406
+ continue;
407
+ }
408
+ const mapped = type.properties[args.index];
409
+ if (mapped) {
410
+ // if the object was made already then network mapping has been done for listed properties
411
+ // and we should use the ts side property
412
+ assert(
413
+ networkProp == mapped.networkName,
414
+ `Network names for properties with the same name need to be equal when a object has multiple types. Mismatch in ${networkProp} and ${mapped.networkName}.`
415
+ );
416
+ return args.index;
417
+ }
418
+ }
419
+ return networkProp;
420
+ }
421
+
383
422
  export function makeObject<
384
423
  P extends { [key: string]: Maker<any, any> | { optional: Maker<any, any> } }
385
- >(props: P, additionalProp?: any, comparisorOrder?: string[]) {
424
+ >(props: P, additionalProp?: any, comparisorOrder?: string[], type?: ObjectType) {
425
+ const fromNetwork = fromNetworkMap(type);
426
+ const listedInputPropNames = new Set(Object.keys(props).map(prop => fromNetwork[prop] ?? prop));
427
+
386
428
  return (value: any, opts?: MakeOptions) => {
387
429
  if (typeof value !== 'object' || value == null || Array.isArray(value)) {
388
430
  return getErrorWithValueMsg('expected an object', value);
@@ -393,26 +435,32 @@ export function makeObject<
393
435
  if (isMagic(index)) {
394
436
  return error(`Using ${index} as field of an object is not allowed`);
395
437
  }
438
+ const inputPropName = getInputPropName({ opts, index, fromNetwork, value });
396
439
  let maker: any = props[index];
397
440
  if (!maker) {
398
441
  return error(`Prop maker not found for key ${index}. This is likely a bug in oats`);
399
442
  }
400
443
  if (maker.optional) {
401
- if (!(index in value) || value[index] === undefined) {
444
+ if (!(inputPropName in value) || value[inputPropName] === undefined) {
402
445
  continue;
403
446
  }
404
447
  maker = maker.optional;
405
448
  }
406
- const propResult: Make<any> = maker(value[index], opts);
449
+ const propResult: Make<any> = maker(value[inputPropName], opts);
407
450
  if (propResult.isError()) {
408
451
  return propResult.errorPath(index);
409
452
  }
410
453
  result[index] = propResult.success();
411
454
  }
412
455
  for (const index of Object.keys(value)) {
456
+ // do not consider props that got mapped
457
+ if (opts?.convertFromNetwork && listedInputPropNames.has(index)) {
458
+ continue;
459
+ }
413
460
  if (isMagic(index)) {
414
461
  return error(`Using ${index} as objects additional field is not allowed.`);
415
462
  }
463
+ // do not allow overriding mapped props
416
464
  if (props[index]) {
417
465
  continue;
418
466
  }
@@ -422,13 +470,21 @@ export function makeObject<
422
470
  }
423
471
  return error('unexpected property').errorPath(index);
424
472
  }
473
+ // If the input value A has been made already with network prop name mapping
474
+ // this runs the property value through the current additionalProps maker to validate it
475
+ // nb. the property key *has* already been mapped when making A and we just use it here
476
+ // this works unless we start mapping additionalProp property names.
425
477
  const propResult: Make<any> = additionalProp(value[index], opts);
426
478
  if (propResult.isError()) {
427
479
  return propResult.errorPath(index);
428
480
  }
429
481
  result[index] = propResult.success();
430
482
  }
431
- return Make.ok(result);
483
+ const oldType = getType(value);
484
+ if (oldType) {
485
+ withType(result, oldType);
486
+ }
487
+ return Make.ok(withType(result, type ? [type] : []));
432
488
  };
433
489
  }
434
490
 
@@ -542,12 +598,26 @@ function priority(v: ObjectType['properties'][string]) {
542
598
  return Priority.NonScalar;
543
599
  }
544
600
 
601
+ function fromNetworkMap(type?: ObjectType) {
602
+ if (!type) {
603
+ return {};
604
+ }
605
+ const fromNetwork = Object.keys(type.properties).reduce<Record<string, string>>((memo, key) => {
606
+ const mapped = type.properties[key].networkName;
607
+ if (mapped) {
608
+ memo[key] = mapped;
609
+ }
610
+ return memo;
611
+ }, {});
612
+ return fromNetwork;
613
+ }
545
614
  function fromObjectReflection(type: ObjectType): Maker<any, any> {
546
615
  const comparisonOrder = Object.keys(type.properties).sort((aKey, bKey) => {
547
616
  const a = type.properties[aKey]!;
548
617
  const b = type.properties[bKey]!;
549
618
  return priority(a) - priority(b);
550
619
  });
620
+
551
621
  return makeObject(
552
622
  Object.entries(type.properties).reduce(
553
623
  (memo, [key, prop]) => ({
@@ -561,7 +631,8 @@ function fromObjectReflection(type: ObjectType): Maker<any, any> {
561
631
  ? makeAny()
562
632
  : fromReflection(type.additionalProperties)
563
633
  : undefined,
564
- comparisonOrder
634
+ comparisonOrder,
635
+ type
565
636
  );
566
637
  }
567
638
 
@@ -96,6 +96,7 @@ export interface NamedType {
96
96
  export interface PropType {
97
97
  required: boolean;
98
98
  value: Type;
99
+ networkName?: string;
99
100
  }
100
101
 
101
102
  export interface Props {
package/src/runtime.ts CHANGED
@@ -4,12 +4,16 @@ import * as make from './make';
4
4
  import { fromReflection } from './make';
5
5
  import * as valueClass from './value-class';
6
6
  import * as reflection from './reflection-type';
7
+ import * as typeTag from './type-tag';
8
+ import { ValueClass } from './value-class';
7
9
 
8
10
  export { redirect } from './redirect';
9
11
 
10
12
  export { make, fromReflection, server, client, valueClass, reflection };
11
13
 
12
14
  export const noContentContentType = 'oatsNoContent' as const;
15
+ export { serialize } from './serialize';
16
+ export { getType, withType } from './type-tag';
13
17
 
14
18
  type Scalar = number | string | boolean;
15
19
 
@@ -18,6 +22,23 @@ const tagKey = Symbol();
18
22
 
19
23
  type ScalarWithoutBrand<V> = V extends ShapedClass<infer S> ? ScalarWithoutBrand<S> : V;
20
24
 
25
+ // helper to set necessary metadata for valueclass instances
26
+ export function instanceAssign(
27
+ to: ValueClass,
28
+ value: Record<string, unknown>,
29
+ opts: make.MakeOptions | { unSafeSet: boolean } | undefined,
30
+ builder: make.Maker<any, any>
31
+ ) {
32
+ Object.assign(to, opts && 'unSafeSet' in opts ? value : builder(value, opts).success());
33
+ // when we create an instance of a ValueClass we need to propagate the type information from 'value' to the newly
34
+ // constructed instance
35
+ const existingType = typeTag.getType(value);
36
+ if (existingType) {
37
+ return typeTag.withType(to, existingType);
38
+ }
39
+ return to;
40
+ }
41
+
21
42
  export type ShapeOf<A> = A extends Scalar
22
43
  ? ScalarWithoutBrand<A>
23
44
  : A extends ShapedClass<infer S>
@@ -0,0 +1,36 @@
1
+ import { getType } from './type-tag';
2
+
3
+ export function serialize(value: any): any {
4
+ if (!value || typeof value !== 'object') {
5
+ return value;
6
+ }
7
+ if (Array.isArray(value)) {
8
+ return value.map(v => serialize(v));
9
+ }
10
+ const types = getType(value);
11
+ if (!types) {
12
+ return value;
13
+ }
14
+ // get mapping from all types ts property names to network names
15
+ const jointMap = types
16
+ .flatMap(type => (type.type === 'object' ? [type] : []))
17
+ .reduce<Map<string, string>>((memo, type) => {
18
+ for (const key in type.properties) {
19
+ const mapped = type.properties[key].networkName;
20
+ if (mapped != null) {
21
+ memo.set(key, mapped);
22
+ }
23
+ }
24
+ return memo;
25
+ }, new Map());
26
+ // in case there are no network mapping done we can return immediately to avoid wasting a loop
27
+ if (jointMap.size === 0) {
28
+ return value;
29
+ }
30
+ // map value using jointMap
31
+ return Object.entries(value).reduce<Record<string, any>>((memo, [key, prop]) => {
32
+ const to = jointMap.get(key) ?? key;
33
+ memo[to] = serialize(prop);
34
+ return memo;
35
+ }, {});
36
+ }