@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/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/make.d.ts +5 -3
- package/dist/make.js +65 -7
- package/dist/make.js.map +1 -1
- package/dist/reflection-type.d.ts +1 -0
- package/dist/reflection-type.js.map +1 -1
- package/dist/runtime.d.ts +6 -0
- package/dist/runtime.js +19 -1
- package/dist/runtime.js.map +1 -1
- package/dist/serialize.d.ts +1 -0
- package/dist/serialize.js +41 -0
- package/dist/serialize.js.map +1 -0
- package/dist/server.d.ts +9 -1
- package/dist/server.js +36 -10
- package/dist/server.js.map +1 -1
- package/dist/type-tag.d.ts +7 -0
- package/dist/type-tag.js +64 -0
- package/dist/type-tag.js.map +1 -0
- package/jest.config.js +38 -0
- package/package.json +2 -2
- package/src/client.ts +2 -1
- package/src/make.ts +79 -8
- package/src/reflection-type.ts +1 -0
- package/src/runtime.ts +21 -0
- package/src/serialize.ts +36 -0
- package/src/server.ts +71 -14
- package/src/type-tag.ts +63 -0
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
|
-
|
|
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
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
};
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;;AAAA,qCAAkC;AAClC,gEAAwD;AACxD,iCAA2F;
|
|
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;
|
package/dist/type-tag.js
ADDED
|
@@ -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.
|
|
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": "
|
|
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 {
|
|
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 (!(
|
|
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[
|
|
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
|
-
|
|
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
|
|
package/src/reflection-type.ts
CHANGED
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>
|
package/src/serialize.ts
ADDED
|
@@ -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
|
+
}
|