@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.
Files changed (42) hide show
  1. package/README.md +6 -4
  2. package/dist/cjs/src/bin/helpers/enumsAndTheirValues.js +3 -2
  3. package/dist/cjs/src/bin/helpers/enumsFileStr.js +1 -1
  4. package/dist/cjs/src/bin/index.js +14 -0
  5. package/dist/cjs/src/cli/index.js +8 -0
  6. package/dist/cjs/src/devtools/helpers/launchDevServer.js +3 -0
  7. package/dist/cjs/src/error/UnexpectedUndefined.js +14 -0
  8. package/dist/cjs/src/error/psychic-application/init-missing-package-manager.js +20 -0
  9. package/dist/cjs/src/generate/controller.js +12 -1
  10. package/dist/cjs/src/generate/helpers/addResourceToRoutes.js +3 -0
  11. package/dist/cjs/src/helpers/openapiJsonPath.js +5 -1
  12. package/dist/cjs/src/i18n/provider.js +4 -1
  13. package/dist/cjs/src/openapi-renderer/app.js +29 -17
  14. package/dist/cjs/src/openapi-renderer/body-segment.js +3 -2
  15. package/dist/cjs/src/openapi-renderer/endpoint.js +30 -15
  16. package/dist/cjs/src/openapi-renderer/serializer.js +25 -13
  17. package/dist/cjs/src/psychic-application/index.js +47 -2
  18. package/dist/esm/src/bin/helpers/enumsAndTheirValues.js +3 -2
  19. package/dist/esm/src/bin/helpers/enumsFileStr.js +1 -1
  20. package/dist/esm/src/bin/index.js +14 -0
  21. package/dist/esm/src/cli/index.js +8 -0
  22. package/dist/esm/src/devtools/helpers/launchDevServer.js +3 -0
  23. package/dist/esm/src/error/UnexpectedUndefined.js +11 -0
  24. package/dist/esm/src/error/psychic-application/init-missing-package-manager.js +17 -0
  25. package/dist/esm/src/generate/controller.js +12 -1
  26. package/dist/esm/src/generate/helpers/addResourceToRoutes.js +3 -0
  27. package/dist/esm/src/helpers/openapiJsonPath.js +5 -1
  28. package/dist/esm/src/i18n/provider.js +4 -1
  29. package/dist/esm/src/openapi-renderer/app.js +29 -17
  30. package/dist/esm/src/openapi-renderer/body-segment.js +3 -2
  31. package/dist/esm/src/openapi-renderer/endpoint.js +30 -15
  32. package/dist/esm/src/openapi-renderer/serializer.js +25 -13
  33. package/dist/esm/src/psychic-application/index.js +46 -2
  34. package/dist/types/src/bin/index.d.ts +1 -0
  35. package/dist/types/src/error/UnexpectedUndefined.d.ts +4 -0
  36. package/dist/types/src/error/psychic-application/init-missing-package-manager.d.ts +4 -0
  37. package/dist/types/src/generate/helpers/generateControllerContent.d.ts +3 -3
  38. package/dist/types/src/psychic-application/index.d.ts +25 -4
  39. package/package.json +5 -6
  40. package/dist/cjs/src/helpers/sspawn.js +0 -26
  41. package/dist/esm/src/helpers/sspawn.js +0 -22
  42. 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 take 1 day.
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, a type-safe ORM with an incredibly powerful autocomplete API, elegant redis and socket.io bindings for distributed websocket applications, the ability to couple with a front-end framework (like react, angular, etc...) and write specs that drive through your front end, while still maintaining a back end-centric context from which to write specs.
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
- 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.
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
- NOTE: doing so will create the new app in the psychic folder, so once done testing remember to remove it.
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
- rowData[row.enumType] ||= [];
14
- rowData[row.enumType].push(row.enumLabel);
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
- controllerNameParts.push(allControllerNameParts[index]);
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
- return path.join(psychicApp.apiRoot, psychicApp.openapi[openapiName].outputFilename);
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 translation = i18nHash[i18nPath[0]];
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
- const method = Object.keys(endpointPayload[path]).find(key => types_js_1.HttpMethods.includes(key));
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 pathObj = finalOutput.paths[path];
103
- const otherPathObj = endpointPayload[path];
104
- pathObj[method] = otherPathObj[method];
105
- pathObj.parameters = this.combineParameters([
106
- ...pathObj.parameters,
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
- const result = Object.keys(groupedParams).map(paramName => {
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 (compositeParam.allowEmptyValue !== undefined)
131
+ if (param.allowEmptyValue !== undefined)
120
132
  compositeParam.allowEmptyValue = param.allowEmptyValue;
121
- if (compositeParam.allowReserved !== undefined)
133
+ if (param.allowReserved !== undefined)
122
134
  compositeParam.allowReserved = param.allowReserved;
123
- if (compositeParam.required !== undefined)
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 sortedSchemas = Object.keys(schema.components.schemas).sort();
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 = sortedSchemas.reduce((agg, key) => {
155
+ sortedSchema.components.schemas = sortedSchemaNames.reduce((agg, key) => {
144
156
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
145
- agg[key] = schema.components.schemas[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
- if (bodySegment.description) {
178
- data.description = bodySegment.description;
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
- output[path][method].summary = this.summary;
110
+ outputPath[method].summary = this.summary;
108
111
  }
109
112
  if (this.description) {
110
- output[path][method].description = this.description;
113
+ outputPath[method].description = this.description;
111
114
  }
112
115
  if (this.security) {
113
- output[path][method].security = this.security;
116
+ outputPath[method].security = this.security;
114
117
  }
115
118
  if (requestBody) {
116
- output[path][method]['requestBody'] = requestBody;
119
+ outputPath[method]['requestBody'] = requestBody;
117
120
  }
118
- output[path][method].responses = responses;
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.description || queryName,
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.allowEmptyValue === 'boolean') {
307
+ if (typeof queryParam?.allowEmptyValue === 'boolean') {
303
308
  output.allowEmptyValue = queryParam.allowEmptyValue;
304
309
  }
305
- if (typeof queryParam.allowReserved === 'boolean') {
310
+ if (typeof queryParam?.allowReserved === 'boolean') {
306
311
  output.allowReserved = queryParam.allowReserved;
307
312
  }
308
- if (queryParam.schema) {
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
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
623
- responseData[key] = psychicAndConfigLevelDefaults[key];
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
- return dreamApp.serializers[serializerKey] || null;
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
- { ...finalOutput[serializerKey] },
125
+ { ...finalOutputForSerializerKey },
122
126
  {
123
127
  allOf: [
124
- { ...finalOutput[serializerKey] },
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
- { ...finalOutput[serializerKey] },
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-unsafe-member-access, @typescript-eslint/no-explicit-any
172
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
164
173
  ;
165
- finalOutput[serializerKey].properties[association.field] = {
174
+ finalOutputForSerializerKey.properties[association.field] = {
166
175
  type: 'array',
167
176
  items: {
168
177
  $ref: `#/components/schemas/${associatedSerializerKey}`,
169
178
  },
170
179
  };
171
- finalOutput[serializerKey].required = (0, dream_1.uniq)([
172
- ...(finalOutput[serializerKey].required || []),
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
- finalOutput[serializerKey].required = (0, dream_1.uniq)([
185
- ...(finalOutput[serializerKey].required || []),
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
- finalOutput[serializerKey].required = (0, dream_1.uniq)([
197
- ...(finalOutput[serializerKey].required || []),
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
- finalOutput[serializerKey].required = (0, dream_1.uniq)([
275
- ...(finalOutput[serializerKey].required || []),
286
+ finalOutputForSerializerKey.required = (0, dream_1.uniq)([
287
+ ...(finalOutputForSerializerKey.required || []),
276
288
  association.field,
277
289
  ]);
278
290
  switch (association.type) {