@rvoh/psychic 0.36.0-beta.2 → 0.37.0-beta.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.
- package/dist/cjs/src/bin/index.js +2 -2
- package/dist/cjs/src/controller/helpers/isPaginatedResult.js +5 -2
- package/dist/cjs/src/controller/index.js +14 -6
- package/dist/cjs/src/error/openapi/AttemptedToDeriveDescendentSerializersFromNonSerializer.js +16 -0
- package/dist/cjs/src/error/openapi/ExpectedSerializerForRendersOneOrManyOption.js +36 -0
- package/dist/cjs/src/error/openapi/NoSerializerFoundForRendersOneAndMany.js +16 -0
- package/dist/cjs/src/error/openapi/NonSerializerPassedToSerializerOpenapiRenderer.js +16 -0
- package/dist/cjs/src/error/openapi/NonSerializerSerializerOverrideProvided.js +23 -0
- package/dist/cjs/src/error/openapi/ObjectSerializerRendersOneAndManyRequireClassType.js +19 -0
- package/dist/cjs/src/helpers/isObject.js +12 -0
- package/dist/cjs/src/helpers/pathifyNestedObject.js +5 -2
- package/dist/cjs/src/openapi-renderer/SerializerOpenapiRenderer.js +362 -0
- package/dist/cjs/src/openapi-renderer/app.js +16 -15
- package/dist/cjs/src/openapi-renderer/body-segment.js +8 -9
- package/dist/cjs/src/openapi-renderer/endpoint.js +57 -103
- package/dist/cjs/src/openapi-renderer/helpers/allSerializersFromHandWrittenOpenapi.js +34 -0
- package/dist/cjs/src/openapi-renderer/helpers/allSerializersToRefsInOpenapi.js +52 -0
- package/dist/cjs/src/openapi-renderer/helpers/dreamAttributeOpenapiShape.js +145 -0
- package/dist/cjs/src/openapi-renderer/helpers/isOpenapiShorthand.js +16 -0
- package/dist/cjs/src/openapi-renderer/helpers/maybeNullOpenapiShorthandToOpenapiShorthand.js +17 -0
- package/dist/cjs/src/openapi-renderer/helpers/openapiShorthandToOpenapi.js +111 -0
- package/dist/cjs/src/openapi-renderer/helpers/primitiveOpenapiStatementToOpenapi.js +5 -2
- package/dist/cjs/src/openapi-renderer/helpers/{suppressResponseEnums.js → suppressResponseEnumsConfig.js} +2 -2
- package/dist/cjs/src/psychic-app/index.js +0 -1
- package/dist/cjs/src/server/index.js +1 -35
- package/dist/cjs/src/server/params.js +4 -4
- package/dist/esm/src/bin/index.js +1 -1
- package/dist/esm/src/controller/helpers/isPaginatedResult.js +1 -1
- package/dist/esm/src/controller/index.js +15 -7
- package/dist/esm/src/error/openapi/AttemptedToDeriveDescendentSerializersFromNonSerializer.js +13 -0
- package/dist/esm/src/error/openapi/ExpectedSerializerForRendersOneOrManyOption.js +33 -0
- package/dist/esm/src/error/openapi/NoSerializerFoundForRendersOneAndMany.js +13 -0
- package/dist/esm/src/error/openapi/NonSerializerPassedToSerializerOpenapiRenderer.js +13 -0
- package/dist/esm/src/error/openapi/NonSerializerSerializerOverrideProvided.js +20 -0
- package/dist/esm/src/error/openapi/ObjectSerializerRendersOneAndManyRequireClassType.js +16 -0
- package/dist/esm/src/helpers/isObject.js +9 -0
- package/dist/esm/src/helpers/pathifyNestedObject.js +1 -1
- package/dist/esm/src/openapi-renderer/SerializerOpenapiRenderer.js +356 -0
- package/dist/esm/src/openapi-renderer/app.js +17 -16
- package/dist/esm/src/openapi-renderer/body-segment.js +6 -7
- package/dist/esm/src/openapi-renderer/endpoint.js +58 -104
- package/dist/esm/src/openapi-renderer/helpers/allSerializersFromHandWrittenOpenapi.js +28 -0
- package/dist/esm/src/openapi-renderer/helpers/allSerializersToRefsInOpenapi.js +46 -0
- package/dist/esm/src/openapi-renderer/helpers/dreamAttributeOpenapiShape.js +136 -0
- package/dist/esm/src/openapi-renderer/helpers/isOpenapiShorthand.js +10 -0
- package/dist/esm/src/openapi-renderer/helpers/maybeNullOpenapiShorthandToOpenapiShorthand.js +14 -0
- package/dist/esm/src/openapi-renderer/helpers/openapiShorthandToOpenapi.js +102 -0
- package/dist/esm/src/openapi-renderer/helpers/primitiveOpenapiStatementToOpenapi.js +1 -1
- package/dist/esm/src/openapi-renderer/helpers/{suppressResponseEnums.js → suppressResponseEnumsConfig.js} +1 -1
- package/dist/esm/src/psychic-app/index.js +0 -1
- package/dist/esm/src/server/index.js +1 -35
- package/dist/esm/src/server/params.js +3 -3
- package/dist/types/src/controller/index.d.ts +3 -1
- package/dist/types/src/error/openapi/AttemptedToDeriveDescendentSerializersFromNonSerializer.d.ts +5 -0
- package/dist/types/src/error/openapi/ExpectedSerializerForRendersOneOrManyOption.d.ts +8 -0
- package/dist/types/src/error/openapi/NoSerializerFoundForRendersOneAndMany.d.ts +5 -0
- package/dist/types/src/error/openapi/NonSerializerPassedToSerializerOpenapiRenderer.d.ts +5 -0
- package/dist/types/src/error/openapi/NonSerializerSerializerOverrideProvided.d.ts +6 -0
- package/dist/types/src/error/openapi/ObjectSerializerRendersOneAndManyRequireClassType.d.ts +5 -0
- package/dist/types/src/helpers/isObject.d.ts +1 -0
- package/dist/types/src/openapi-renderer/SerializerOpenapiRenderer.d.ts +25 -0
- package/dist/types/src/openapi-renderer/body-segment.d.ts +4 -7
- package/dist/types/src/openapi-renderer/endpoint.d.ts +17 -21
- package/dist/types/src/openapi-renderer/helpers/allSerializersFromHandWrittenOpenapi.d.ts +2 -0
- package/dist/types/src/openapi-renderer/helpers/allSerializersToRefsInOpenapi.d.ts +2 -0
- package/dist/types/src/openapi-renderer/helpers/dreamAttributeOpenapiShape.d.ts +780 -0
- package/dist/types/src/openapi-renderer/helpers/isOpenapiShorthand.d.ts +1 -0
- package/dist/types/src/openapi-renderer/helpers/maybeNullOpenapiShorthandToOpenapiShorthand.d.ts +2 -0
- package/dist/types/src/openapi-renderer/helpers/openapiShorthandToOpenapi.d.ts +16 -0
- package/dist/types/src/openapi-renderer/helpers/{schemaDelimiter.d.ts → suppressResponseEnumsConfig.d.ts} +1 -1
- package/dist/types/src/psychic-app/index.d.ts +0 -53
- package/dist/types/src/server/index.d.ts +0 -1
- package/package.json +2 -5
- package/dist/cjs/src/helpers/typechecks.js +0 -20
- package/dist/cjs/src/openapi-renderer/helpers/schemaDelimiter.js +0 -13
- package/dist/esm/src/helpers/typechecks.js +0 -16
- package/dist/esm/src/openapi-renderer/helpers/schemaDelimiter.js +0 -7
- package/dist/types/src/helpers/typechecks.d.ts +0 -2
- package/dist/types/src/openapi-renderer/helpers/suppressResponseEnums.d.ts +0 -4
|
@@ -32,7 +32,7 @@ const path = __importStar(require("node:path"));
|
|
|
32
32
|
const TypesBuilder_js_1 = __importDefault(require("../cli/helpers/TypesBuilder.js"));
|
|
33
33
|
const controller_js_1 = __importDefault(require("../generate/controller.js"));
|
|
34
34
|
const resource_js_1 = __importDefault(require("../generate/resource.js"));
|
|
35
|
-
const
|
|
35
|
+
const isObject_js_1 = __importDefault(require("../helpers/isObject.js"));
|
|
36
36
|
const app_js_1 = __importDefault(require("../openapi-renderer/app.js"));
|
|
37
37
|
const index_js_1 = __importDefault(require("../psychic-app/index.js"));
|
|
38
38
|
const index_js_2 = __importDefault(require("../server/index.js"));
|
|
@@ -75,7 +75,7 @@ class PsychicBin {
|
|
|
75
75
|
for (const hook of psychicApp.specialHooks.cliSync) {
|
|
76
76
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
77
77
|
const res = await hook();
|
|
78
|
-
if ((0,
|
|
78
|
+
if ((0, isObject_js_1.default)(res)) {
|
|
79
79
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
80
80
|
output = { ...output, ...res };
|
|
81
81
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.default = isPaginatedResult;
|
|
4
|
-
const
|
|
7
|
+
const isObject_js_1 = __importDefault(require("../../helpers/isObject.js"));
|
|
5
8
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
9
|
function isPaginatedResult(result) {
|
|
7
|
-
if (!(0,
|
|
10
|
+
if (!(0, isObject_js_1.default)(result))
|
|
8
11
|
return false;
|
|
9
12
|
const paginatedFields = ['currentPage', 'pageCount', 'recordCount', 'results'];
|
|
10
13
|
const keys = Object.keys(result);
|
|
@@ -193,12 +193,20 @@ class PsychicController {
|
|
|
193
193
|
session;
|
|
194
194
|
config;
|
|
195
195
|
action;
|
|
196
|
+
renderOpts;
|
|
196
197
|
constructor(req, res, { config, action, }) {
|
|
197
198
|
this.req = req;
|
|
198
199
|
this.res = res;
|
|
199
200
|
this.config = config;
|
|
200
201
|
this.session = new index_js_1.default(req, res);
|
|
201
202
|
this.action = action;
|
|
203
|
+
// TODO: read casing from Dream app config
|
|
204
|
+
this.renderOpts = {
|
|
205
|
+
casing: 'camel',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
get headers() {
|
|
209
|
+
return this.req.headers;
|
|
202
210
|
}
|
|
203
211
|
get params() {
|
|
204
212
|
const params = {
|
|
@@ -262,6 +270,10 @@ class PsychicController {
|
|
|
262
270
|
return data;
|
|
263
271
|
const dreamApp = dream_1.DreamApp.getOrFail();
|
|
264
272
|
const psychicControllerClass = this.constructor;
|
|
273
|
+
// if we already have a serializer, let's just render it
|
|
274
|
+
if (data instanceof dream_1.DreamSerializerBuilder || data instanceof dream_1.ObjectSerializerBuilder) {
|
|
275
|
+
return data.render(this.defaultSerializerPassthrough, this.renderOpts);
|
|
276
|
+
}
|
|
265
277
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
266
278
|
const lookup = exports.controllerSerializerIndex.lookupModel(this.constructor, data.constructor);
|
|
267
279
|
if (lookup?.length) {
|
|
@@ -276,9 +288,7 @@ class PsychicController {
|
|
|
276
288
|
// does not pass it into the call to DreamSerializer/ObjectSerializer,
|
|
277
289
|
// then it would be lost to serializers rendered via rendersOne/Many, and SerializerRenderer
|
|
278
290
|
// handles passing its passthrough data into those
|
|
279
|
-
this.defaultSerializerPassthrough,
|
|
280
|
-
casing: 'camel',
|
|
281
|
-
});
|
|
291
|
+
this.defaultSerializerPassthrough, this.renderOpts);
|
|
282
292
|
}
|
|
283
293
|
}
|
|
284
294
|
else {
|
|
@@ -299,9 +309,7 @@ class PsychicController {
|
|
|
299
309
|
// does not pass it into the call to DreamSerializer/ObjectSerializer,
|
|
300
310
|
// then it would be lost to serializers rendered via rendersOne/Many, and SerializerRenderer
|
|
301
311
|
// handles passing its passthrough data into those
|
|
302
|
-
this.defaultSerializerPassthrough,
|
|
303
|
-
casing: 'camel',
|
|
304
|
-
});
|
|
312
|
+
this.defaultSerializerPassthrough, this.renderOpts);
|
|
305
313
|
}
|
|
306
314
|
else {
|
|
307
315
|
throw new Error(`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_util_1 = require("node:util");
|
|
4
|
+
class AttemptedToDeriveDescendentSerializersFromNonSerializer extends Error {
|
|
5
|
+
serializer;
|
|
6
|
+
constructor(serializer) {
|
|
7
|
+
super();
|
|
8
|
+
this.serializer = serializer;
|
|
9
|
+
}
|
|
10
|
+
get message() {
|
|
11
|
+
return `
|
|
12
|
+
Attempted to derive descendant serializers from non serializer:
|
|
13
|
+
${(0, node_util_1.inspect)(this.serializer)}`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.default = AttemptedToDeriveDescendentSerializersFromNonSerializer;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class ExpectedSerializerForRendersOneOrManyOption extends Error {
|
|
4
|
+
rendersOneOrMany;
|
|
5
|
+
referencingSerializerName;
|
|
6
|
+
attribute;
|
|
7
|
+
constructor(rendersOneOrMany, referencingSerializerName, attribute) {
|
|
8
|
+
super();
|
|
9
|
+
this.rendersOneOrMany = rendersOneOrMany;
|
|
10
|
+
this.referencingSerializerName = referencingSerializerName;
|
|
11
|
+
this.attribute = attribute;
|
|
12
|
+
}
|
|
13
|
+
get message() {
|
|
14
|
+
const baseString = `
|
|
15
|
+
The \`serializer\` option on the \`${this.attribute.name}\` \`${this.rendersOneOrMany}\`
|
|
16
|
+
on serializer \`${this.referencingSerializerName}\``;
|
|
17
|
+
if (this.attribute.options?.serializer) {
|
|
18
|
+
return `${baseString}
|
|
19
|
+
specifies something other than a serializer.
|
|
20
|
+
It should look something like the following:
|
|
21
|
+
|
|
22
|
+
\`\`\`
|
|
23
|
+
.${this.rendersOneOrMany}('${this.attribute.name}', {
|
|
24
|
+
serializer: MySerializer
|
|
25
|
+
})
|
|
26
|
+
\`\`\`
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return `${baseString}
|
|
31
|
+
is throwing an error.
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.default = ExpectedSerializerForRendersOneOrManyOption;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class NoSerializerFoundForRendersOneAndMany extends Error {
|
|
4
|
+
associationName;
|
|
5
|
+
constructor(associationName) {
|
|
6
|
+
super();
|
|
7
|
+
this.associationName = associationName;
|
|
8
|
+
}
|
|
9
|
+
get message() {
|
|
10
|
+
return `
|
|
11
|
+
Attempted to render \`rendersOne\` / \`rendersMany\`
|
|
12
|
+
\`${this.associationName}\`, but could not locate a
|
|
13
|
+
serializer.`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.default = NoSerializerFoundForRendersOneAndMany;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_util_1 = require("node:util");
|
|
4
|
+
class NonSerializerPassedToSerializerOpenapiRenderer extends Error {
|
|
5
|
+
serializer;
|
|
6
|
+
constructor(serializer) {
|
|
7
|
+
super();
|
|
8
|
+
this.serializer = serializer;
|
|
9
|
+
}
|
|
10
|
+
get message() {
|
|
11
|
+
return `
|
|
12
|
+
Non-serializer passed to SerializerOpenapiRenderer:
|
|
13
|
+
${(0, node_util_1.inspect)(this.serializer)}`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.default = NonSerializerPassedToSerializerOpenapiRenderer;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_util_1 = require("node:util");
|
|
4
|
+
class NonSerializerSerializerOverrideProvided extends Error {
|
|
5
|
+
rendersAssociation;
|
|
6
|
+
serializer;
|
|
7
|
+
constructor(rendersAssociation, serializer) {
|
|
8
|
+
super();
|
|
9
|
+
this.rendersAssociation = rendersAssociation;
|
|
10
|
+
this.serializer = serializer;
|
|
11
|
+
}
|
|
12
|
+
get message() {
|
|
13
|
+
return `
|
|
14
|
+
Non-serializer passed to \`serializerOverride\` of a rendersOne/Many declaration:
|
|
15
|
+
|
|
16
|
+
rendersOne/Many declaration:
|
|
17
|
+
${(0, node_util_1.inspect)(this.rendersAssociation)}
|
|
18
|
+
|
|
19
|
+
non-serializer passed to serializerOverride:
|
|
20
|
+
${(0, node_util_1.inspect)(this.serializer)}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.default = NonSerializerSerializerOverrideProvided;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class ObjectSerializerRendersOneAndManyRequireClassType extends Error {
|
|
4
|
+
associationName;
|
|
5
|
+
constructor(associationName) {
|
|
6
|
+
super();
|
|
7
|
+
this.associationName = associationName;
|
|
8
|
+
}
|
|
9
|
+
get message() {
|
|
10
|
+
return `
|
|
11
|
+
ObjectSerializer \`rendersOne\` and \`rendersMany\`
|
|
12
|
+
options must include \`dreamClass\`, \`viewModelClass\`, or
|
|
13
|
+
\`serializer\`.
|
|
14
|
+
|
|
15
|
+
rendersOne/Many name: ${this.associationName}
|
|
16
|
+
`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.default = ObjectSerializerRendersOneAndManyRequireClassType;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = isObject;
|
|
4
|
+
// also in Dream
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
function isObject(x) {
|
|
7
|
+
if (x === null)
|
|
8
|
+
return false;
|
|
9
|
+
if (Array.isArray(x))
|
|
10
|
+
return false;
|
|
11
|
+
return typeof x === 'object';
|
|
12
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.default = pathifyNestedObject;
|
|
4
|
-
const
|
|
7
|
+
const isObject_js_1 = __importDefault(require("./isObject.js"));
|
|
5
8
|
function pathifyNestedObject(obj, prefix = '') {
|
|
6
9
|
return Object.keys(obj).reduce((acc, k) => {
|
|
7
10
|
const pre = prefix.length ? prefix + '/' : '';
|
|
8
|
-
if ((0,
|
|
11
|
+
if ((0, isObject_js_1.default)(obj[k]))
|
|
9
12
|
Object.assign(acc, pathifyNestedObject(obj[k], pre + k));
|
|
10
13
|
else
|
|
11
14
|
acc[pre + k] = obj[k];
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
10
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
11
|
+
const dream_1 = require("@rvoh/dream");
|
|
12
|
+
const node_util_1 = require("node:util");
|
|
13
|
+
const AttemptedToDeriveDescendentSerializersFromNonSerializer_js_1 = __importDefault(require("../error/openapi/AttemptedToDeriveDescendentSerializersFromNonSerializer.js"));
|
|
14
|
+
const ExpectedSerializerForRendersOneOrManyOption_js_1 = __importDefault(require("../error/openapi/ExpectedSerializerForRendersOneOrManyOption.js"));
|
|
15
|
+
const NonSerializerPassedToSerializerOpenapiRenderer_js_1 = __importDefault(require("../error/openapi/NonSerializerPassedToSerializerOpenapiRenderer.js"));
|
|
16
|
+
const NonSerializerSerializerOverrideProvided_js_1 = __importDefault(require("../error/openapi/NonSerializerSerializerOverrideProvided.js"));
|
|
17
|
+
const NoSerializerFoundForRendersOneAndMany_js_1 = __importDefault(require("../error/openapi/NoSerializerFoundForRendersOneAndMany.js"));
|
|
18
|
+
const ObjectSerializerRendersOneAndManyRequireClassType_js_1 = __importDefault(require("../error/openapi/ObjectSerializerRendersOneAndManyRequireClassType.js"));
|
|
19
|
+
const allSerializersFromHandWrittenOpenapi_js_1 = __importDefault(require("./helpers/allSerializersFromHandWrittenOpenapi.js"));
|
|
20
|
+
const allSerializersToRefsInOpenapi_js_1 = __importDefault(require("./helpers/allSerializersToRefsInOpenapi.js"));
|
|
21
|
+
const dreamAttributeOpenapiShape_js_1 = require("./helpers/dreamAttributeOpenapiShape.js");
|
|
22
|
+
const openapiShorthandToOpenapi_js_1 = __importDefault(require("./helpers/openapiShorthandToOpenapi.js"));
|
|
23
|
+
const NULL_OBJECT_OPENAPI = { type: 'null' };
|
|
24
|
+
class SerializerOpenapiRenderer {
|
|
25
|
+
serializer;
|
|
26
|
+
casing;
|
|
27
|
+
suppressResponseEnums;
|
|
28
|
+
allOfSiblings = [];
|
|
29
|
+
constructor(serializer, { casing = 'camel', suppressResponseEnums = false, } = {}) {
|
|
30
|
+
this.serializer = serializer;
|
|
31
|
+
if (!(0, dream_1.isDreamSerializer)(this.serializer))
|
|
32
|
+
throw new NonSerializerPassedToSerializerOpenapiRenderer_js_1.default(this.serializer);
|
|
33
|
+
this.casing = casing;
|
|
34
|
+
this.suppressResponseEnums = suppressResponseEnums;
|
|
35
|
+
}
|
|
36
|
+
get globalName() {
|
|
37
|
+
return this.serializer.globalName ?? '--unnamed--';
|
|
38
|
+
}
|
|
39
|
+
get openapiName() {
|
|
40
|
+
return this.serializer.openapiName ?? '--unnamed--';
|
|
41
|
+
}
|
|
42
|
+
get serializerRef() {
|
|
43
|
+
return {
|
|
44
|
+
$ref: `#/components/schemas/${this.openapiName}`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
_serializerBuilder;
|
|
48
|
+
get serializerBuilder() {
|
|
49
|
+
if (this._serializerBuilder)
|
|
50
|
+
return this._serializerBuilder;
|
|
51
|
+
this._serializerBuilder = this.serializer(undefined, undefined);
|
|
52
|
+
return this._serializerBuilder;
|
|
53
|
+
}
|
|
54
|
+
renderedOpenapi(alreadyExtractedDescendantSerializers = {}) {
|
|
55
|
+
alreadyExtractedDescendantSerializers[this.serializer.globalName] = true;
|
|
56
|
+
const referencedSerializersAndOpenapiSchemaBodyShorthand = this._renderedOpenapi(alreadyExtractedDescendantSerializers);
|
|
57
|
+
if (this.allOfSiblings.length) {
|
|
58
|
+
const openapi = referencedSerializersAndOpenapiSchemaBodyShorthand.openapi;
|
|
59
|
+
return {
|
|
60
|
+
...referencedSerializersAndOpenapiSchemaBodyShorthand,
|
|
61
|
+
openapi: {
|
|
62
|
+
allOf: [openapi, ...this.allOfSiblings],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
return referencedSerializersAndOpenapiSchemaBodyShorthand;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
_renderedOpenapi(alreadyExtractedDescendantSerializers) {
|
|
71
|
+
const referencedSerializersAndAttributes = this.renderedOpenapiAttributes(alreadyExtractedDescendantSerializers);
|
|
72
|
+
const requiredProperties = (0, dream_1.compact)(this.serializerBuilder['attributes'].map(attribute => {
|
|
73
|
+
const attributeType = attribute.type;
|
|
74
|
+
switch (attributeType) {
|
|
75
|
+
case 'attribute': {
|
|
76
|
+
return attribute.options?.as ?? attribute.name;
|
|
77
|
+
}
|
|
78
|
+
case 'delegatedAttribute': {
|
|
79
|
+
return attribute.options?.as ?? attribute.name;
|
|
80
|
+
}
|
|
81
|
+
case 'customAttribute': {
|
|
82
|
+
return attribute.options.flatten ? null : attribute.name;
|
|
83
|
+
}
|
|
84
|
+
case 'rendersOne': {
|
|
85
|
+
return attribute.options.flatten ? null : (attribute.options?.as ?? attribute.name);
|
|
86
|
+
}
|
|
87
|
+
case 'rendersMany': {
|
|
88
|
+
return attribute.options?.as ?? attribute.name;
|
|
89
|
+
}
|
|
90
|
+
default: {
|
|
91
|
+
// protection so that if a new ValidationType is ever added, this will throw a type error at build time
|
|
92
|
+
const _never = attributeType;
|
|
93
|
+
throw new Error(`Unhandled serializer attribute type: ${_never}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}));
|
|
97
|
+
return {
|
|
98
|
+
referencedSerializers: referencedSerializersAndAttributes.referencedSerializers,
|
|
99
|
+
openapi: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
required: (0, dream_1.sort)((0, dream_1.uniq)(requiredProperties.map(property => this.setCase(property)))),
|
|
102
|
+
properties: (0, dream_1.sortObjectByKey)(referencedSerializersAndAttributes.attributes),
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
renderedOpenapiAttributes(alreadyExtractedDescendantSerializers = {}) {
|
|
107
|
+
const $typeForOpenapi = this.serializerBuilder['$typeForOpenapi'];
|
|
108
|
+
const DataTypeForOpenapi = $typeForOpenapi;
|
|
109
|
+
let referencedSerializers = [];
|
|
110
|
+
let renderedOpenapi = {};
|
|
111
|
+
const openapiRenderingOpts = {
|
|
112
|
+
casing: this.casing,
|
|
113
|
+
suppressResponseEnums: this.suppressResponseEnums,
|
|
114
|
+
};
|
|
115
|
+
renderedOpenapi = this.serializerBuilder['attributes'].reduce((accumulator, attribute) => {
|
|
116
|
+
const attributeType = attribute.type;
|
|
117
|
+
let newlyReferencedSerializers = [];
|
|
118
|
+
accumulator = (() => {
|
|
119
|
+
switch (attributeType) {
|
|
120
|
+
////////////////
|
|
121
|
+
// attributes //
|
|
122
|
+
////////////////
|
|
123
|
+
case 'attribute': {
|
|
124
|
+
const outputAttributeName = this.setCase(attribute.options?.as ?? attribute.name);
|
|
125
|
+
const openapi = attribute.options.openapi;
|
|
126
|
+
newlyReferencedSerializers = (0, allSerializersFromHandWrittenOpenapi_js_1.default)(openapi);
|
|
127
|
+
accumulator[outputAttributeName] = DataTypeForOpenapi?.isDream
|
|
128
|
+
? (0, dreamAttributeOpenapiShape_js_1.dreamColumnOpenapiShape)(DataTypeForOpenapi, attribute.name, openapi, {
|
|
129
|
+
suppressResponseEnums: this.suppressResponseEnums,
|
|
130
|
+
})
|
|
131
|
+
: (0, allSerializersToRefsInOpenapi_js_1.default)((0, openapiShorthandToOpenapi_js_1.default)(openapi));
|
|
132
|
+
return accumulator;
|
|
133
|
+
}
|
|
134
|
+
/////////////////////
|
|
135
|
+
// end: attributes //
|
|
136
|
+
/////////////////////
|
|
137
|
+
///////////////////////
|
|
138
|
+
// custom attributes //
|
|
139
|
+
///////////////////////
|
|
140
|
+
case 'customAttribute': {
|
|
141
|
+
const outputAttributeName = this.setCase(attribute.name);
|
|
142
|
+
const openapi = attribute.options.openapi;
|
|
143
|
+
newlyReferencedSerializers = (0, allSerializersFromHandWrittenOpenapi_js_1.default)(openapi);
|
|
144
|
+
if (attribute.options.flatten) {
|
|
145
|
+
this.allOfSiblings.push((0, allSerializersToRefsInOpenapi_js_1.default)((0, openapiShorthandToOpenapi_js_1.default)(openapi)));
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
accumulator[outputAttributeName] = (0, allSerializersToRefsInOpenapi_js_1.default)((0, openapiShorthandToOpenapi_js_1.default)(openapi));
|
|
149
|
+
}
|
|
150
|
+
return accumulator;
|
|
151
|
+
}
|
|
152
|
+
////////////////////////////
|
|
153
|
+
// end: custom attributes //
|
|
154
|
+
////////////////////////////
|
|
155
|
+
//////////////////////////
|
|
156
|
+
// delegated attributes //
|
|
157
|
+
//////////////////////////
|
|
158
|
+
case 'delegatedAttribute': {
|
|
159
|
+
const outputAttributeName = this.setCase(attribute.options?.as ?? attribute.name);
|
|
160
|
+
const openapi = attribute.options.openapi;
|
|
161
|
+
newlyReferencedSerializers = (0, allSerializersFromHandWrittenOpenapi_js_1.default)(openapi);
|
|
162
|
+
accumulator[outputAttributeName] = (0, allSerializersToRefsInOpenapi_js_1.default)((0, openapiShorthandToOpenapi_js_1.default)(openapi));
|
|
163
|
+
return accumulator;
|
|
164
|
+
}
|
|
165
|
+
///////////////////////////////
|
|
166
|
+
// end: delegated attributes //
|
|
167
|
+
///////////////////////////////
|
|
168
|
+
//////////////////
|
|
169
|
+
// rendersOnes //
|
|
170
|
+
//////////////////
|
|
171
|
+
case 'rendersOne': {
|
|
172
|
+
try {
|
|
173
|
+
const outputAttributeName = this.setCase(attribute.options.as ?? attribute.name);
|
|
174
|
+
const referencedSerializersAndOpenapiSchemaBodyShorthand = associationOpenapi(attribute, DataTypeForOpenapi, alreadyExtractedDescendantSerializers, openapiRenderingOpts);
|
|
175
|
+
newlyReferencedSerializers =
|
|
176
|
+
referencedSerializersAndOpenapiSchemaBodyShorthand.referencedSerializers;
|
|
177
|
+
if (attribute.options.flatten && attribute.options.optional) {
|
|
178
|
+
this.allOfSiblings.push({
|
|
179
|
+
anyOf: [referencedSerializersAndOpenapiSchemaBodyShorthand.openapi, NULL_OBJECT_OPENAPI],
|
|
180
|
+
});
|
|
181
|
+
//
|
|
182
|
+
}
|
|
183
|
+
else if (attribute.options.flatten) {
|
|
184
|
+
this.allOfSiblings.push(referencedSerializersAndOpenapiSchemaBodyShorthand.openapi);
|
|
185
|
+
//
|
|
186
|
+
}
|
|
187
|
+
else if (attribute.options.optional) {
|
|
188
|
+
accumulator[outputAttributeName] = {
|
|
189
|
+
anyOf: [referencedSerializersAndOpenapiSchemaBodyShorthand.openapi, NULL_OBJECT_OPENAPI],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
accumulator[outputAttributeName] = referencedSerializersAndOpenapiSchemaBodyShorthand.openapi;
|
|
194
|
+
}
|
|
195
|
+
return accumulator;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (error instanceof CallingSerializersThrewError)
|
|
199
|
+
return accumulator;
|
|
200
|
+
if (error instanceof AttemptedToDeriveDescendentSerializersFromNonSerializer_js_1.default)
|
|
201
|
+
throw new ExpectedSerializerForRendersOneOrManyOption_js_1.default('rendersOne', this.globalName, attribute);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
///////////////////////
|
|
206
|
+
// end: rendersOnes //
|
|
207
|
+
///////////////////////
|
|
208
|
+
///////////////////
|
|
209
|
+
// rendersManys //
|
|
210
|
+
///////////////////
|
|
211
|
+
case 'rendersMany': {
|
|
212
|
+
try {
|
|
213
|
+
const outputAttributeName = this.setCase(attribute.options.as ?? attribute.name);
|
|
214
|
+
const referencedSerializersAndOpenapiSchemaBodyShorthand = associationOpenapi(attribute, DataTypeForOpenapi, alreadyExtractedDescendantSerializers, openapiRenderingOpts);
|
|
215
|
+
newlyReferencedSerializers =
|
|
216
|
+
referencedSerializersAndOpenapiSchemaBodyShorthand.referencedSerializers;
|
|
217
|
+
accumulator[outputAttributeName] = {
|
|
218
|
+
type: 'array',
|
|
219
|
+
items: referencedSerializersAndOpenapiSchemaBodyShorthand.openapi,
|
|
220
|
+
};
|
|
221
|
+
return accumulator;
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
if (error instanceof CallingSerializersThrewError)
|
|
225
|
+
return accumulator;
|
|
226
|
+
if (error instanceof AttemptedToDeriveDescendentSerializersFromNonSerializer_js_1.default)
|
|
227
|
+
throw new ExpectedSerializerForRendersOneOrManyOption_js_1.default('rendersMany', this.globalName, attribute);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
////////////////////////
|
|
232
|
+
// end: rendersManys //
|
|
233
|
+
////////////////////////
|
|
234
|
+
default: {
|
|
235
|
+
// protection so that if a new ValidationType is ever added, this will throw a type error at build time
|
|
236
|
+
const _never = attributeType;
|
|
237
|
+
throw new Error(`Unhandled serializer attribute type: ${_never}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
})();
|
|
241
|
+
const recursiveNewlyReferencedSerializers = newlyReferencedSerializers.flatMap(serializer => descendantSerializers(serializer, alreadyExtractedDescendantSerializers, openapiRenderingOpts));
|
|
242
|
+
referencedSerializers = [
|
|
243
|
+
...referencedSerializers,
|
|
244
|
+
...newlyReferencedSerializers,
|
|
245
|
+
...recursiveNewlyReferencedSerializers,
|
|
246
|
+
];
|
|
247
|
+
return accumulator;
|
|
248
|
+
}, renderedOpenapi);
|
|
249
|
+
return {
|
|
250
|
+
referencedSerializers: (0, dream_1.uniq)(referencedSerializers, serializer => serializer.globalName),
|
|
251
|
+
attributes: renderedOpenapi,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
setCase(attr) {
|
|
255
|
+
switch (this.casing) {
|
|
256
|
+
case 'camel':
|
|
257
|
+
return attr;
|
|
258
|
+
case 'snake':
|
|
259
|
+
return (0, dream_1.snakeify)(attr);
|
|
260
|
+
default: {
|
|
261
|
+
// protection so that if a new Casing is ever added, this will throw a type error at build time
|
|
262
|
+
const _never = this.casing;
|
|
263
|
+
throw new Error(`Unhandled Casing: ${_never}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
exports.default = SerializerOpenapiRenderer;
|
|
269
|
+
function associationOpenapi(attribute, DataTypeForOpenapi, alreadyExtractedDescendantSerializers, opts) {
|
|
270
|
+
const serializerOverride = attribute.options.serializer;
|
|
271
|
+
if (serializerOverride) {
|
|
272
|
+
try {
|
|
273
|
+
return {
|
|
274
|
+
referencedSerializers: [
|
|
275
|
+
serializerOverride,
|
|
276
|
+
...descendantSerializers(serializerOverride, alreadyExtractedDescendantSerializers, opts),
|
|
277
|
+
],
|
|
278
|
+
openapi: new SerializerOpenapiRenderer(serializerOverride, opts).serializerRef,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
if (error instanceof NonSerializerPassedToSerializerOpenapiRenderer_js_1.default)
|
|
283
|
+
throw new NonSerializerSerializerOverrideProvided_js_1.default(attribute, serializerOverride);
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
let associatedClasses;
|
|
288
|
+
const association = DataTypeForOpenapi?.isDream &&
|
|
289
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
290
|
+
DataTypeForOpenapi['getAssociationMetadata'](attribute.name);
|
|
291
|
+
if (association) {
|
|
292
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
293
|
+
associatedClasses = (0, dream_1.expandStiClasses)(association.modelCB());
|
|
294
|
+
//
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
const associatedClass = attribute.options.dreamClass ?? attribute.options.viewModelClass;
|
|
298
|
+
if (associatedClass === undefined) {
|
|
299
|
+
let serializerCheck;
|
|
300
|
+
try {
|
|
301
|
+
;
|
|
302
|
+
DataTypeForOpenapi?.prototype?.serializers;
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
throw new CallingSerializersThrewError();
|
|
306
|
+
}
|
|
307
|
+
if (serializerCheck)
|
|
308
|
+
throw new ObjectSerializerRendersOneAndManyRequireClassType_js_1.default(attribute.name);
|
|
309
|
+
throw new ObjectSerializerRendersOneAndManyRequireClassType_js_1.default(attribute.name);
|
|
310
|
+
}
|
|
311
|
+
if (associatedClass?.isDream) {
|
|
312
|
+
associatedClasses = (0, dream_1.expandStiClasses)(associatedClass);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
associatedClasses = [associatedClass];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const serializersOpenapi = associatedClasses.flatMap(associatedClass => (0, dream_1.inferSerializersFromDreamClassOrViewModelClass)(associatedClass, attribute.options.serializerKey));
|
|
319
|
+
if (serializersOpenapi.length === 0)
|
|
320
|
+
throw new NoSerializerFoundForRendersOneAndMany_js_1.default(attribute.name);
|
|
321
|
+
if (serializersOpenapi.length === 1) {
|
|
322
|
+
const serializer = serializersOpenapi[0];
|
|
323
|
+
return {
|
|
324
|
+
referencedSerializers: [
|
|
325
|
+
serializer,
|
|
326
|
+
...descendantSerializers(serializer, alreadyExtractedDescendantSerializers, opts),
|
|
327
|
+
],
|
|
328
|
+
openapi: new SerializerOpenapiRenderer(serializer, opts).serializerRef,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
referencedSerializers: [
|
|
333
|
+
...serializersOpenapi,
|
|
334
|
+
...serializersOpenapi.flatMap(serializer => descendantSerializers(serializer, alreadyExtractedDescendantSerializers, opts)),
|
|
335
|
+
],
|
|
336
|
+
openapi: {
|
|
337
|
+
anyOf: (0, dream_1.sortBy)((0, dream_1.uniq)(serializersOpenapi.map(serializer => new SerializerOpenapiRenderer(serializer, opts).serializerRef), ref => ref['$ref']), ref => (ref['$ref'] ? ref['$ref'] : (0, node_util_1.inspect)(ref, { depth: 2 }))),
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function descendantSerializers(serializer, alreadyExtractedDescendantSerializers, opts) {
|
|
342
|
+
// alreadyExtractedDescendantSerializers is used not only to avoid duplicate
|
|
343
|
+
// work (and thereby speed up OpenAPI spec generation), but also to avoid
|
|
344
|
+
// infinite loops (a recursive OpenAPI structure is valid)
|
|
345
|
+
if (alreadyExtractedDescendantSerializers[serializer.globalName])
|
|
346
|
+
return [];
|
|
347
|
+
if (!(0, dream_1.isDreamSerializer)(serializer))
|
|
348
|
+
throw new AttemptedToDeriveDescendentSerializersFromNonSerializer_js_1.default(serializer);
|
|
349
|
+
const immediateDescendantSerializers = new SerializerOpenapiRenderer(serializer, opts).renderedOpenapi(alreadyExtractedDescendantSerializers).referencedSerializers;
|
|
350
|
+
return [
|
|
351
|
+
...immediateDescendantSerializers,
|
|
352
|
+
...immediateDescendantSerializers.flatMap(descendantSerializer => descendantSerializers(descendantSerializer, alreadyExtractedDescendantSerializers, opts)),
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
// When attempting to expand STI children, we might call `.serializers` on
|
|
356
|
+
// an instance that throws an error just by calling `.serializers` (so that
|
|
357
|
+
// they can be sure to define serializers on the STI children, but in this
|
|
358
|
+
// case, there might be STI children that are intermediaries to the intended
|
|
359
|
+
// STI children, so they don't have serializers and calling `.serializers`
|
|
360
|
+
// throws an error)
|
|
361
|
+
class CallingSerializersThrewError extends Error {
|
|
362
|
+
}
|