@rvoh/psychic 0.26.3 → 0.27.2
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/README.md +6 -4
- package/dist/cjs/src/bin/helpers/enumsAndTheirValues.js +3 -2
- package/dist/cjs/src/bin/helpers/enumsFileStr.js +1 -1
- package/dist/cjs/src/bin/index.js +14 -0
- package/dist/cjs/src/cli/index.js +8 -0
- package/dist/cjs/src/devtools/helpers/launchDevServer.js +3 -0
- package/dist/cjs/src/error/UnexpectedUndefined.js +14 -0
- package/dist/cjs/src/error/psychic-application/init-missing-package-manager.js +20 -0
- package/dist/cjs/src/generate/controller.js +12 -1
- package/dist/cjs/src/generate/helpers/addResourceToRoutes.js +3 -0
- package/dist/cjs/src/helpers/openapiJsonPath.js +5 -1
- package/dist/cjs/src/i18n/provider.js +4 -1
- package/dist/cjs/src/openapi-renderer/app.js +29 -17
- package/dist/cjs/src/openapi-renderer/body-segment.js +3 -2
- package/dist/cjs/src/openapi-renderer/endpoint.js +30 -15
- package/dist/cjs/src/openapi-renderer/serializer.js +25 -13
- package/dist/cjs/src/psychic-application/index.js +47 -2
- package/dist/esm/src/bin/helpers/enumsAndTheirValues.js +3 -2
- package/dist/esm/src/bin/helpers/enumsFileStr.js +1 -1
- package/dist/esm/src/bin/index.js +14 -0
- package/dist/esm/src/cli/index.js +8 -0
- package/dist/esm/src/devtools/helpers/launchDevServer.js +3 -0
- package/dist/esm/src/error/UnexpectedUndefined.js +11 -0
- package/dist/esm/src/error/psychic-application/init-missing-package-manager.js +17 -0
- package/dist/esm/src/generate/controller.js +12 -1
- package/dist/esm/src/generate/helpers/addResourceToRoutes.js +3 -0
- package/dist/esm/src/helpers/openapiJsonPath.js +5 -1
- package/dist/esm/src/i18n/provider.js +4 -1
- package/dist/esm/src/openapi-renderer/app.js +29 -17
- package/dist/esm/src/openapi-renderer/body-segment.js +3 -2
- package/dist/esm/src/openapi-renderer/endpoint.js +30 -15
- package/dist/esm/src/openapi-renderer/serializer.js +25 -13
- package/dist/esm/src/psychic-application/index.js +46 -2
- package/dist/types/src/bin/index.d.ts +1 -0
- package/dist/types/src/error/UnexpectedUndefined.d.ts +4 -0
- package/dist/types/src/error/psychic-application/init-missing-package-manager.d.ts +4 -0
- package/dist/types/src/generate/helpers/generateControllerContent.d.ts +3 -3
- package/dist/types/src/psychic-application/index.d.ts +25 -4
- package/package.json +5 -6
- package/dist/cjs/src/helpers/sspawn.js +0 -26
- package/dist/esm/src/helpers/sspawn.js +0 -22
- package/dist/types/src/helpers/sspawn.d.ts +0 -2
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
> ATTENTION: we are currently in the process of releasing this code to the world, as of the afternoon of March 10th, 2025. This notice will be removed, and the version of this repo will be bumped to 1.0.0, once all of the repos have been migrated to the new spaces and we can verify that it is all working. This is anticipated to
|
|
1
|
+
> ATTENTION: we are currently in the process of releasing this code to the world, as of the afternoon of March 10th, 2025. This notice will be removed, and the version of this repo will be bumped to 1.0.0, once all of the repos have been migrated to the new spaces and we can verify that it is all working. This is anticipated to be done in early April, 2025.
|
|
2
2
|
|
|
3
3
|
# Psychic
|
|
4
4
|
|
|
5
|
-
Psychic is a typescript first Node framework built on top of [kysely](http://kysely.dev) and heavily inspired by Ruby on Rails. It provides a light-weight routing layer around [expressjs](https://expressjs.com) to create a familiar MVC pattern for those coming from a conventional MVC framework,
|
|
5
|
+
Psychic is a typescript first Node framework built on top of [kysely](http://kysely.dev) and heavily inspired by Ruby on Rails. It provides a light-weight routing layer around [expressjs](https://expressjs.com) to create a familiar MVC pattern for those coming from a conventional MVC framework, and...
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Introducing [dream](https://psychicframework.com/docs/models/overview)!, a robust, type-safe ORM with an incredibly powerful autocomplete API, through associations, polymorphism, single table inheritence, deep OpenAPI integration, and much more.
|
|
8
|
+
|
|
9
|
+
In addition, psychic also provides elegant redis and socket.io bindings for distributed websocket applications, a powerful background job system integrated with bullmq to enable you to easily send any of your application code into redis for background processing, the ability to couple with a front-end framework (like nextjs, react, angular etc...) and write specs that drive through your front end, while still maintaining a back end-centric context from which to compose, and much more!
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
For more comprehensive documentation, please see [The official Psychic guides](https://psychicframework.com). We will also be publishing api docs shortly, but the Psychic guides should be comprehensive enough to provide an understanding of the technology and how to use it.
|
|
10
12
|
|
|
11
13
|
## Questions?
|
|
12
14
|
|
|
@@ -10,8 +10,9 @@ SELECT pg_type.typname AS enum_type, pg_enum.enumlabel AS enum_label FROM pg_typ
|
|
|
10
10
|
`.execute((0, dream_1.db)('primary'));
|
|
11
11
|
const rowData = {};
|
|
12
12
|
rows.forEach(row => {
|
|
13
|
-
|
|
14
|
-
rowData[
|
|
13
|
+
const enumType = row.enumType;
|
|
14
|
+
rowData[enumType] ||= [];
|
|
15
|
+
rowData[enumType].push(row.enumLabel);
|
|
15
16
|
});
|
|
16
17
|
return rowData;
|
|
17
18
|
}
|
|
@@ -9,7 +9,7 @@ async function enumsFileStr() {
|
|
|
9
9
|
let enumsFileStr = (0, autogeneratedFileDisclaimer_js_1.default)();
|
|
10
10
|
Object.keys(enums).forEach(enumName => {
|
|
11
11
|
const exportedTypeName = (0, pascalizeFileName_js_1.default)(enumName) + 'Values';
|
|
12
|
-
const values = enums[enumName];
|
|
12
|
+
const values = enums[enumName] || [];
|
|
13
13
|
enumsFileStr += `\
|
|
14
14
|
export const ${exportedTypeName} = [
|
|
15
15
|
${values.map(val => `'${val}'`).join(',\n ')}
|
|
@@ -27,6 +27,20 @@ class PsychicBin {
|
|
|
27
27
|
if (!bypassDreamSync)
|
|
28
28
|
await dream_1.DreamBin.sync(() => { });
|
|
29
29
|
await PsychicBin.syncTypes();
|
|
30
|
+
const psychicApp = index_js_1.default.getOrFail();
|
|
31
|
+
dream_1.DreamCLI.logger.logStartProgress('running post-sync operations...');
|
|
32
|
+
// call post-sync command in a separate process, so that newly-generated
|
|
33
|
+
// types can be reloaded and brought into all classes.
|
|
34
|
+
await dream_1.DreamCLI.spawn(psychicApp.psyCmd('post-sync'), {
|
|
35
|
+
onStdout: message => {
|
|
36
|
+
dream_1.DreamCLI.logger.logContinueProgress(`[post-sync]` + ' ' + message, {
|
|
37
|
+
logPrefixColor: 'cyan',
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
dream_1.DreamCLI.logger.logEndProgress();
|
|
42
|
+
}
|
|
43
|
+
static async postSync() {
|
|
30
44
|
const psychicApp = index_js_1.default.getOrFail();
|
|
31
45
|
await PsychicBin.syncOpenapiJson();
|
|
32
46
|
if (psychicApp.openapi?.syncEnumsToClient) {
|
|
@@ -50,6 +50,14 @@ class PsychicCLI {
|
|
|
50
50
|
await index_js_1.default.sync();
|
|
51
51
|
process.exit();
|
|
52
52
|
});
|
|
53
|
+
program
|
|
54
|
+
.command('post-sync')
|
|
55
|
+
.description('an internal command that runs as the second stage of the `sync` command, since after types are rebuit, the application needs to be reloaded before autogenerating certain files, since those files will need to leverage the updated types')
|
|
56
|
+
.action(async () => {
|
|
57
|
+
await initializePsychicApplication();
|
|
58
|
+
await index_js_1.default.postSync();
|
|
59
|
+
process.exit();
|
|
60
|
+
});
|
|
53
61
|
program
|
|
54
62
|
.command('sync:routes')
|
|
55
63
|
.description('reads the routes generated by your app and generates a cache file, which is then used to give autocomplete support to the route helper, amoongst other things.')
|
|
@@ -6,6 +6,7 @@ exports.stopDevServers = stopDevServers;
|
|
|
6
6
|
const child_process_1 = require("child_process");
|
|
7
7
|
const net_1 = require("net");
|
|
8
8
|
const sleep_js_1 = require("../../../spec/helpers/sleep.js");
|
|
9
|
+
const UnexpectedUndefined_js_1 = require("../../error/UnexpectedUndefined.js");
|
|
9
10
|
const devServerProcesses = {};
|
|
10
11
|
async function launchDevServer(key, { port = 3000, cmd = 'yarn client', timeout = 5000 } = {}) {
|
|
11
12
|
if (devServerProcesses[key])
|
|
@@ -13,6 +14,8 @@ async function launchDevServer(key, { port = 3000, cmd = 'yarn client', timeout
|
|
|
13
14
|
if (process.env.DEBUG === '1')
|
|
14
15
|
console.log('Starting server...');
|
|
15
16
|
const [_cmd, ...args] = cmd.split(' ');
|
|
17
|
+
if (_cmd === undefined)
|
|
18
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
16
19
|
const proc = (0, child_process_1.spawn)(_cmd, args, {
|
|
17
20
|
detached: true,
|
|
18
21
|
env: {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class UnexpectedUndefined extends Error {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
get message() {
|
|
8
|
+
return `Undefined detected where it should never happen since we are iterating
|
|
9
|
+
over keys of an internal object that should not have undefined values.
|
|
10
|
+
|
|
11
|
+
This was added as part of activating noUncheckedIndexedAccess in tsconfig.`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.default = UnexpectedUndefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class PsychicApplicationInitMissingPackageManager extends Error {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
get message() {
|
|
8
|
+
return `
|
|
9
|
+
must set packageManager when initializing a new PsychicApplication.
|
|
10
|
+
|
|
11
|
+
within conf/app.ts, you must have a call to "#set('packageManager', '<YOUR_CHOSEN_PACKAGE_MANAGER>')", i.e.
|
|
12
|
+
|
|
13
|
+
// conf/app.ts
|
|
14
|
+
export default async (app: PsychicApplication) => {
|
|
15
|
+
await app.set('packageManager', 'yarn')
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.default = PsychicApplicationInitMissingPackageManager;
|
|
@@ -4,6 +4,7 @@ exports.default = generateController;
|
|
|
4
4
|
const dream_1 = require("@rvoh/dream");
|
|
5
5
|
const fs = require("fs/promises");
|
|
6
6
|
const node_fs_1 = require("node:fs");
|
|
7
|
+
const UnexpectedUndefined_js_1 = require("../error/UnexpectedUndefined.js");
|
|
7
8
|
const EnvInternal_js_1 = require("../helpers/EnvInternal.js");
|
|
8
9
|
const psychicFileAndDirPaths_js_1 = require("../helpers/path/psychicFileAndDirPaths.js");
|
|
9
10
|
const psychicPath_js_1 = require("../helpers/path/psychicPath.js");
|
|
@@ -23,6 +24,10 @@ async function generateController({ fullyQualifiedControllerName, fullyQualified
|
|
|
23
24
|
if (controllerNameParts.length > (isAdmin ? 1 : 0)) {
|
|
24
25
|
// Write the ancestor controller
|
|
25
26
|
const [baseAncestorName, baseAncestorImportStatement] = baseAncestorNameAndImport(controllerNameParts, isAdmin, { forBaseController: true });
|
|
27
|
+
if (baseAncestorName === undefined)
|
|
28
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
29
|
+
if (baseAncestorImportStatement === undefined)
|
|
30
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
26
31
|
const baseControllerName = [...controllerNameParts, 'BaseController'].join('/');
|
|
27
32
|
const { absDirPath, absFilePath } = (0, psychicFileAndDirPaths_js_1.default)((0, psychicPath_js_1.default)('controllers'), baseControllerName + `.ts`);
|
|
28
33
|
await fs.mkdir(absDirPath, { recursive: true });
|
|
@@ -35,12 +40,18 @@ async function generateController({ fullyQualifiedControllerName, fullyQualified
|
|
|
35
40
|
}));
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
|
-
|
|
43
|
+
const namedPart = allControllerNameParts[index];
|
|
44
|
+
if (namedPart)
|
|
45
|
+
controllerNameParts.push(namedPart);
|
|
39
46
|
}
|
|
40
47
|
// Write the controller
|
|
41
48
|
const [ancestorName, ancestorImportStatement] = baseAncestorNameAndImport(controllerNameParts, isAdmin, {
|
|
42
49
|
forBaseController: false,
|
|
43
50
|
});
|
|
51
|
+
if (ancestorName === undefined)
|
|
52
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
53
|
+
if (ancestorImportStatement === undefined)
|
|
54
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
44
55
|
const { relFilePath, absDirPath, absFilePath } = (0, psychicFileAndDirPaths_js_1.default)((0, psychicPath_js_1.default)('controllers'), fullyQualifiedControllerName + `.ts`);
|
|
45
56
|
await fs.mkdir(absDirPath, { recursive: true });
|
|
46
57
|
try {
|
|
@@ -4,6 +4,7 @@ exports.default = addResourceToRoutes;
|
|
|
4
4
|
exports.addResourceToRoutes_routeToRegexAndReplacements = addResourceToRoutes_routeToRegexAndReplacements;
|
|
5
5
|
const fs = require("fs/promises");
|
|
6
6
|
const path = require("path");
|
|
7
|
+
const UnexpectedUndefined_js_1 = require("../../error/UnexpectedUndefined.js");
|
|
7
8
|
const psychicPath_js_1 = require("../../helpers/path/psychicPath.js");
|
|
8
9
|
const index_js_1 = require("../../psychic-application/index.js");
|
|
9
10
|
async function addResourceToRoutes(route) {
|
|
@@ -13,6 +14,8 @@ async function addResourceToRoutes(route) {
|
|
|
13
14
|
const matchesAndReplacements = addResourceToRoutes_routeToRegexAndReplacements(route);
|
|
14
15
|
for (let index = 0; index < matchesAndReplacements.length; index++) {
|
|
15
16
|
const matchAndReplacement = matchesAndReplacements[index];
|
|
17
|
+
if (matchAndReplacement === undefined)
|
|
18
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
16
19
|
if (matchAndReplacement.regex.test(routes)) {
|
|
17
20
|
routes = routes.replace(matchAndReplacement.regex, matchAndReplacement.replacement +
|
|
18
21
|
closeBrackets(index, indent(matchesAndReplacements.length - index - 1)) +
|
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = openapiJsonPath;
|
|
4
4
|
const path = require("path");
|
|
5
|
+
const UnexpectedUndefined_js_1 = require("../error/UnexpectedUndefined.js");
|
|
5
6
|
const index_js_1 = require("../psychic-application/index.js");
|
|
6
7
|
function openapiJsonPath(openapiName = 'default') {
|
|
7
8
|
const psychicApp = index_js_1.default.getOrFail();
|
|
8
|
-
|
|
9
|
+
const namedOpenapi = psychicApp.openapi[openapiName];
|
|
10
|
+
if (namedOpenapi === undefined)
|
|
11
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
12
|
+
return path.join(psychicApp.apiRoot, namedOpenapi.outputFilename);
|
|
9
13
|
}
|
|
@@ -51,7 +51,10 @@ function applyInterpolations(i18nPathString, str, interpolations) {
|
|
|
51
51
|
return str;
|
|
52
52
|
}
|
|
53
53
|
function _i18n(i18nHash, i18nPath) {
|
|
54
|
-
const
|
|
54
|
+
const index = i18nPath[0];
|
|
55
|
+
if (index === undefined)
|
|
56
|
+
throw new TranslationMissing();
|
|
57
|
+
const translation = i18nHash[index];
|
|
55
58
|
if (translation === undefined)
|
|
56
59
|
throw new TranslationMissing();
|
|
57
60
|
if (typeof translation === 'string')
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const dream_1 = require("@rvoh/dream");
|
|
3
4
|
const fs = require("fs/promises");
|
|
4
5
|
const lodash_es_1 = require("lodash-es");
|
|
6
|
+
const UnexpectedUndefined_js_1 = require("../error/UnexpectedUndefined.js");
|
|
5
7
|
const EnvInternal_js_1 = require("../helpers/EnvInternal.js");
|
|
6
8
|
const openapiJsonPath_js_1 = require("../helpers/openapiJsonPath.js");
|
|
7
9
|
const index_js_1 = require("../psychic-application/index.js");
|
|
@@ -89,22 +91,30 @@ class OpenapiAppRenderer {
|
|
|
89
91
|
if (EnvInternal_js_1.default.isDebug)
|
|
90
92
|
console.log(`Processing OpenAPI key ${key} for controller ${controllerName}`);
|
|
91
93
|
const renderer = controller.openapi[key];
|
|
94
|
+
if (renderer === undefined)
|
|
95
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
92
96
|
finalOutput.components.schemas = {
|
|
93
97
|
...finalOutput.components.schemas,
|
|
94
98
|
...renderer.toSchemaObject(openapiName, processedSchemas),
|
|
95
99
|
};
|
|
96
100
|
const endpointPayload = renderer.toPathObject(openapiName, processedSchemas, routes);
|
|
101
|
+
if (endpointPayload === undefined)
|
|
102
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
97
103
|
const path = Object.keys(endpointPayload)[0];
|
|
98
|
-
|
|
104
|
+
if (path === undefined)
|
|
105
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
106
|
+
const endpointPayloadPath = endpointPayload[path];
|
|
107
|
+
if (endpointPayloadPath === undefined)
|
|
108
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
109
|
+
const method = Object.keys(endpointPayloadPath).find(key => types_js_1.HttpMethods.includes(key));
|
|
99
110
|
if (!finalOutput.paths[path]) {
|
|
100
111
|
finalOutput.paths[path] = { parameters: [] };
|
|
101
112
|
}
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
...
|
|
107
|
-
...endpointPayload[path].parameters,
|
|
113
|
+
const finalPathObject = finalOutput.paths[path];
|
|
114
|
+
finalPathObject[method] = endpointPayloadPath[method];
|
|
115
|
+
finalPathObject.parameters = this.combineParameters([
|
|
116
|
+
...finalPathObject.parameters,
|
|
117
|
+
...endpointPayloadPath.parameters,
|
|
108
118
|
]);
|
|
109
119
|
}
|
|
110
120
|
}
|
|
@@ -112,24 +122,26 @@ class OpenapiAppRenderer {
|
|
|
112
122
|
}
|
|
113
123
|
static combineParameters(parameters) {
|
|
114
124
|
const groupedParams = (0, lodash_es_1.groupBy)(parameters, 'name');
|
|
115
|
-
|
|
116
|
-
const identicalParams = groupedParams[paramName];
|
|
125
|
+
return (0, dream_1.compact)(Object.keys(groupedParams).map(paramName => {
|
|
126
|
+
const identicalParams = groupedParams[paramName] || [];
|
|
117
127
|
return identicalParams.reduce((compositeParam, param) => {
|
|
128
|
+
if (compositeParam === undefined)
|
|
129
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
118
130
|
compositeParam.description ||= param.description;
|
|
119
|
-
if (
|
|
131
|
+
if (param.allowEmptyValue !== undefined)
|
|
120
132
|
compositeParam.allowEmptyValue = param.allowEmptyValue;
|
|
121
|
-
if (
|
|
133
|
+
if (param.allowReserved !== undefined)
|
|
122
134
|
compositeParam.allowReserved = param.allowReserved;
|
|
123
|
-
if (
|
|
135
|
+
if (param.required !== undefined)
|
|
124
136
|
compositeParam.required = param.required;
|
|
125
137
|
return compositeParam;
|
|
126
138
|
}, identicalParams[0]);
|
|
127
|
-
});
|
|
128
|
-
return result;
|
|
139
|
+
}));
|
|
129
140
|
}
|
|
130
141
|
static sortedSchemaPayload(schema) {
|
|
131
142
|
const sortedPaths = Object.keys(schema.paths).sort();
|
|
132
|
-
const
|
|
143
|
+
const schemas = schema.components.schemas || {};
|
|
144
|
+
const sortedSchemaNames = Object.keys(schemas).sort();
|
|
133
145
|
const sortedSchema = { ...schema };
|
|
134
146
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
135
147
|
sortedSchema.paths = sortedPaths.reduce((agg, path) => {
|
|
@@ -140,9 +152,9 @@ class OpenapiAppRenderer {
|
|
|
140
152
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
141
153
|
}, {});
|
|
142
154
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
143
|
-
sortedSchema.components.schemas =
|
|
155
|
+
sortedSchema.components.schemas = sortedSchemaNames.reduce((agg, key) => {
|
|
144
156
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
145
|
-
agg[key] =
|
|
157
|
+
agg[key] = schemas[key];
|
|
146
158
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
147
159
|
return agg;
|
|
148
160
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -174,8 +174,9 @@ class OpenapiBodySegmentRenderer {
|
|
|
174
174
|
if (bodySegment.nullable) {
|
|
175
175
|
data.nullable = true;
|
|
176
176
|
}
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
const description = bodySegment.description;
|
|
178
|
+
if (description) {
|
|
179
|
+
data.description = description;
|
|
179
180
|
}
|
|
180
181
|
return data;
|
|
181
182
|
}
|
|
@@ -103,19 +103,22 @@ class OpenapiEndpointRenderer {
|
|
|
103
103
|
},
|
|
104
104
|
},
|
|
105
105
|
};
|
|
106
|
+
const outputPath = output[path];
|
|
107
|
+
if (outputPath === undefined)
|
|
108
|
+
throw new Error(`no output for path ${path}`);
|
|
106
109
|
if (this.summary) {
|
|
107
|
-
|
|
110
|
+
outputPath[method].summary = this.summary;
|
|
108
111
|
}
|
|
109
112
|
if (this.description) {
|
|
110
|
-
|
|
113
|
+
outputPath[method].description = this.description;
|
|
111
114
|
}
|
|
112
115
|
if (this.security) {
|
|
113
|
-
|
|
116
|
+
outputPath[method].security = this.security;
|
|
114
117
|
}
|
|
115
118
|
if (requestBody) {
|
|
116
|
-
|
|
119
|
+
outputPath[method]['requestBody'] = requestBody;
|
|
117
120
|
}
|
|
118
|
-
|
|
121
|
+
outputPath[method].responses = responses;
|
|
119
122
|
return output;
|
|
120
123
|
}
|
|
121
124
|
/**
|
|
@@ -263,8 +266,10 @@ class OpenapiEndpointRenderer {
|
|
|
263
266
|
? {}
|
|
264
267
|
: this.openapiOpts(openapiName)?.defaults?.headers || {};
|
|
265
268
|
const headers = { ...defaultHeaders, ...(this.headers || []) };
|
|
266
|
-
return (Object.keys(headers).map((headerName) => {
|
|
269
|
+
return ((0, dream_1.compact)(Object.keys(headers).map((headerName) => {
|
|
267
270
|
const header = headers[headerName];
|
|
271
|
+
if (header === undefined)
|
|
272
|
+
return null;
|
|
268
273
|
const data = {
|
|
269
274
|
in: 'header',
|
|
270
275
|
name: headerName,
|
|
@@ -278,7 +283,7 @@ class OpenapiEndpointRenderer {
|
|
|
278
283
|
data.schema.format = header.format;
|
|
279
284
|
}
|
|
280
285
|
return data;
|
|
281
|
-
}) || []);
|
|
286
|
+
})) || []);
|
|
282
287
|
}
|
|
283
288
|
/**
|
|
284
289
|
* @internal
|
|
@@ -292,20 +297,20 @@ class OpenapiEndpointRenderer {
|
|
|
292
297
|
let output = {
|
|
293
298
|
in: 'query',
|
|
294
299
|
name: queryName,
|
|
295
|
-
description: queryParam
|
|
300
|
+
description: queryParam?.description || queryName,
|
|
296
301
|
allowReserved: true,
|
|
297
302
|
...queryParam,
|
|
298
303
|
schema: {
|
|
299
304
|
type: 'string',
|
|
300
305
|
},
|
|
301
306
|
};
|
|
302
|
-
if (typeof queryParam
|
|
307
|
+
if (typeof queryParam?.allowEmptyValue === 'boolean') {
|
|
303
308
|
output.allowEmptyValue = queryParam.allowEmptyValue;
|
|
304
309
|
}
|
|
305
|
-
if (typeof queryParam
|
|
310
|
+
if (typeof queryParam?.allowReserved === 'boolean') {
|
|
306
311
|
output.allowReserved = queryParam.allowReserved;
|
|
307
312
|
}
|
|
308
|
-
if (queryParam
|
|
313
|
+
if (queryParam?.schema) {
|
|
309
314
|
output = {
|
|
310
315
|
...output,
|
|
311
316
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
@@ -617,10 +622,16 @@ class OpenapiEndpointRenderer {
|
|
|
617
622
|
...defaultResponses,
|
|
618
623
|
});
|
|
619
624
|
Object.keys(psychicAndConfigLevelDefaults).forEach(key => {
|
|
620
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
621
625
|
if (!responseData[key]) {
|
|
622
|
-
|
|
623
|
-
|
|
626
|
+
const data = psychicAndConfigLevelDefaults[key];
|
|
627
|
+
switch (key) {
|
|
628
|
+
case 'summary':
|
|
629
|
+
case 'description':
|
|
630
|
+
responseData[key] = data;
|
|
631
|
+
break;
|
|
632
|
+
default:
|
|
633
|
+
responseData[key] = data;
|
|
634
|
+
}
|
|
624
635
|
}
|
|
625
636
|
});
|
|
626
637
|
return responseData;
|
|
@@ -677,6 +688,8 @@ class OpenapiEndpointRenderer {
|
|
|
677
688
|
*/
|
|
678
689
|
parseSingleEntitySerializerResponseShape() {
|
|
679
690
|
const serializerClass = this.getSerializerClasses()[0];
|
|
691
|
+
if (serializerClass === undefined)
|
|
692
|
+
throw new Error('getSerializerClasses returned no serializer classes');
|
|
680
693
|
const serializerKey = serializerClass.openapiName;
|
|
681
694
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
682
695
|
const serializerObject = this.accountForNullableOption({
|
|
@@ -818,7 +831,9 @@ class OpenapiEndpointRenderer {
|
|
|
818
831
|
const modelClass = dreamOrSerializerOrViewModel;
|
|
819
832
|
const modelPrototype = modelClass.prototype;
|
|
820
833
|
const serializerKey = modelPrototype.serializers[this.serializerKey || 'default'];
|
|
821
|
-
|
|
834
|
+
if (serializerKey === undefined)
|
|
835
|
+
throw new Error(`no serializerKey for ${this.serializerKey || 'default'}`);
|
|
836
|
+
return dreamApp.serializers[serializerKey] ?? null;
|
|
822
837
|
}
|
|
823
838
|
}
|
|
824
839
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const dream_1 = require("@rvoh/dream");
|
|
4
4
|
const CannotFlattenMultiplePolymorphicRendersOneAssociations_js_1 = require("../error/openapi/CannotFlattenMultiplePolymorphicRendersOneAssociations.js");
|
|
5
|
+
const UnexpectedUndefined_js_1 = require("../error/UnexpectedUndefined.js");
|
|
5
6
|
const EnvInternal_js_1 = require("../helpers/EnvInternal.js");
|
|
6
7
|
const index_js_1 = require("../psychic-application/index.js");
|
|
7
8
|
const body_segment_js_1 = require("./body-segment.js");
|
|
@@ -80,6 +81,9 @@ class OpenapiSerializerRenderer {
|
|
|
80
81
|
let finalOutput = { ...serializerPayload };
|
|
81
82
|
let flattenedPolymorphicSchemas = [];
|
|
82
83
|
let flattenedPolymorphicAssociation;
|
|
84
|
+
const finalOutputForSerializerKey = finalOutput[serializerKey];
|
|
85
|
+
if (finalOutputForSerializerKey === undefined)
|
|
86
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
83
87
|
associations.forEach(association => {
|
|
84
88
|
const associatedSerializers = dream_1.DreamSerializer.getAssociatedSerializersForOpenapi(association);
|
|
85
89
|
if (!associatedSerializers)
|
|
@@ -118,10 +122,10 @@ Error: ${this.serializerClass.name} missing explicit serializer definition for $
|
|
|
118
122
|
...finalOutput,
|
|
119
123
|
[serializerKey]: {
|
|
120
124
|
anyOf: [
|
|
121
|
-
{ ...
|
|
125
|
+
{ ...finalOutputForSerializerKey },
|
|
122
126
|
{
|
|
123
127
|
allOf: [
|
|
124
|
-
{ ...
|
|
128
|
+
{ ...finalOutputForSerializerKey },
|
|
125
129
|
{ anyOf: flattenedPolymorphicSchemas.map(schema => ({ $schema: schema })) },
|
|
126
130
|
],
|
|
127
131
|
},
|
|
@@ -134,7 +138,7 @@ Error: ${this.serializerClass.name} missing explicit serializer definition for $
|
|
|
134
138
|
...finalOutput,
|
|
135
139
|
[serializerKey]: {
|
|
136
140
|
allOf: [
|
|
137
|
-
{ ...
|
|
141
|
+
{ ...finalOutputForSerializerKey },
|
|
138
142
|
{ anyOf: flattenedPolymorphicSchemas.map(schema => ({ $schema: schema })) },
|
|
139
143
|
],
|
|
140
144
|
},
|
|
@@ -154,22 +158,27 @@ Error: ${this.serializerClass.name} missing explicit serializer definition for $
|
|
|
154
158
|
*/
|
|
155
159
|
addSingleSerializerAssociationToOutput({ associatedSerializers, finalOutput, serializerKey, association, }) {
|
|
156
160
|
const associatedSerializer = associatedSerializers[0];
|
|
161
|
+
if (associatedSerializer === undefined)
|
|
162
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
157
163
|
const associatedSerializerKey = associatedSerializer.openapiName;
|
|
158
164
|
if (EnvInternal_js_1.default.isDebug)
|
|
159
165
|
index_js_1.default.log(`Processing serializer ${associatedSerializerKey}`);
|
|
160
166
|
let flattenedData;
|
|
167
|
+
const finalOutputForSerializerKey = finalOutput[serializerKey];
|
|
168
|
+
if (finalOutputForSerializerKey === undefined)
|
|
169
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
161
170
|
switch (association.type) {
|
|
162
171
|
case 'RendersMany':
|
|
163
|
-
// eslint-disable-next-line @typescript-eslint/no-
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
164
173
|
;
|
|
165
|
-
|
|
174
|
+
finalOutputForSerializerKey.properties[association.field] = {
|
|
166
175
|
type: 'array',
|
|
167
176
|
items: {
|
|
168
177
|
$ref: `#/components/schemas/${associatedSerializerKey}`,
|
|
169
178
|
},
|
|
170
179
|
};
|
|
171
|
-
|
|
172
|
-
...(
|
|
180
|
+
finalOutputForSerializerKey.required = (0, dream_1.uniq)([
|
|
181
|
+
...(finalOutputForSerializerKey.required || []),
|
|
173
182
|
association.field,
|
|
174
183
|
]);
|
|
175
184
|
break;
|
|
@@ -181,8 +190,8 @@ Error: ${this.serializerClass.name} missing explicit serializer definition for $
|
|
|
181
190
|
});
|
|
182
191
|
finalOutput[serializerKey].properties = flattenedData;
|
|
183
192
|
if (!association.optional) {
|
|
184
|
-
|
|
185
|
-
...(
|
|
193
|
+
finalOutputForSerializerKey.required = (0, dream_1.uniq)([
|
|
194
|
+
...(finalOutputForSerializerKey.required || []),
|
|
186
195
|
...Object.keys(flattenedData),
|
|
187
196
|
]);
|
|
188
197
|
}
|
|
@@ -193,8 +202,8 @@ Error: ${this.serializerClass.name} missing explicit serializer definition for $
|
|
|
193
202
|
finalOutput[serializerKey].properties[association.field] = this.accountForNullableOption({
|
|
194
203
|
$ref: `#/components/schemas/${associatedSerializerKey}`,
|
|
195
204
|
}, association.optional);
|
|
196
|
-
|
|
197
|
-
...(
|
|
205
|
+
finalOutputForSerializerKey.required = (0, dream_1.uniq)([
|
|
206
|
+
...(finalOutputForSerializerKey.required || []),
|
|
198
207
|
association.field,
|
|
199
208
|
]);
|
|
200
209
|
}
|
|
@@ -244,6 +253,9 @@ Error: ${this.serializerClass.name} missing explicit serializer definition for $
|
|
|
244
253
|
* each pointing to its respective target serializer.
|
|
245
254
|
*/
|
|
246
255
|
addMultiSerializerAssociationToOutput({ associatedSerializers, finalOutput, serializerKey, association, flattenedPolymorphicSchemas, flattenedPolymorphicAssociation, }) {
|
|
256
|
+
const finalOutputForSerializerKey = finalOutput[serializerKey];
|
|
257
|
+
if (finalOutputForSerializerKey === undefined)
|
|
258
|
+
throw new UnexpectedUndefined_js_1.default();
|
|
247
259
|
if (association.flatten) {
|
|
248
260
|
if (flattenedPolymorphicSchemas.length)
|
|
249
261
|
throw new CannotFlattenMultiplePolymorphicRendersOneAssociations_js_1.default(this.serializerClass, association.field);
|
|
@@ -271,8 +283,8 @@ Error: ${this.serializerClass.name} missing explicit serializer definition for $
|
|
|
271
283
|
const associatedSerializerKey = associatedSerializer.openapiName;
|
|
272
284
|
if (EnvInternal_js_1.default.isDebug)
|
|
273
285
|
index_js_1.default.log(`Processing serializer ${associatedSerializerKey}`);
|
|
274
|
-
|
|
275
|
-
...(
|
|
286
|
+
finalOutputForSerializerKey.required = (0, dream_1.uniq)([
|
|
287
|
+
...(finalOutputForSerializerKey.required || []),
|
|
276
288
|
association.field,
|
|
277
289
|
]);
|
|
278
290
|
switch (association.type) {
|