@rvoh/psychic 0.36.0-beta.1 → 0.37.0-beta.3

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 (24) hide show
  1. package/dist/cjs/src/controller/index.js +18 -12
  2. package/dist/cjs/src/openapi-renderer/app.js +16 -15
  3. package/dist/cjs/src/openapi-renderer/body-segment.js +3 -6
  4. package/dist/cjs/src/openapi-renderer/endpoint.js +56 -103
  5. package/dist/cjs/src/openapi-renderer/helpers/{suppressResponseEnums.js → suppressResponseEnumsConfig.js} +2 -2
  6. package/dist/cjs/src/psychic-app/index.js +0 -1
  7. package/dist/cjs/src/server/index.js +1 -35
  8. package/dist/esm/src/controller/index.js +19 -13
  9. package/dist/esm/src/openapi-renderer/app.js +17 -16
  10. package/dist/esm/src/openapi-renderer/body-segment.js +3 -6
  11. package/dist/esm/src/openapi-renderer/endpoint.js +57 -104
  12. package/dist/esm/src/openapi-renderer/helpers/{suppressResponseEnums.js → suppressResponseEnumsConfig.js} +1 -1
  13. package/dist/esm/src/psychic-app/index.js +0 -1
  14. package/dist/esm/src/server/index.js +1 -35
  15. package/dist/types/src/controller/index.d.ts +3 -1
  16. package/dist/types/src/openapi-renderer/body-segment.d.ts +4 -7
  17. package/dist/types/src/openapi-renderer/endpoint.d.ts +17 -21
  18. package/dist/types/src/openapi-renderer/helpers/{schemaDelimiter.d.ts → suppressResponseEnumsConfig.d.ts} +1 -1
  19. package/dist/types/src/psychic-app/index.d.ts +0 -53
  20. package/dist/types/src/server/index.d.ts +0 -1
  21. package/package.json +2 -5
  22. package/dist/cjs/src/openapi-renderer/helpers/schemaDelimiter.js +0 -13
  23. package/dist/esm/src/openapi-renderer/helpers/schemaDelimiter.js +0 -7
  24. package/dist/types/src/openapi-renderer/helpers/suppressResponseEnums.d.ts +0 -4
@@ -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,24 +270,25 @@ 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) {
268
280
  const serializer = lookup?.[1];
269
281
  if ((0, dream_1.isDreamSerializer)(serializer)) {
270
- return new dream_1.SerializerRenderer(
271
282
  // passthrough data going into the serializer is the argument that gets
272
283
  // used in the custom attribute callback function
273
- serializer(data, this.defaultSerializerPassthrough),
274
- // passthrough data must be passed both into the serializer and the SerializerRenderer
284
+ return serializer(data, this.defaultSerializerPassthrough).render(
285
+ // passthrough data must be passed both into the serializer and render
275
286
  // because, if the serializer does accept passthrough data, then passing it in is how
276
287
  // it gets into the serializer, but if it does not accept passthrough data, and therefore
277
288
  // does not pass it into the call to DreamSerializer/ObjectSerializer,
278
289
  // then it would be lost to serializers rendered via rendersOne/Many, and SerializerRenderer
279
290
  // handles passing its passthrough data into those
280
- this.defaultSerializerPassthrough, {
281
- casing: 'camel',
282
- }).render();
291
+ this.defaultSerializerPassthrough, this.renderOpts);
283
292
  }
284
293
  }
285
294
  else {
@@ -291,19 +300,16 @@ class PsychicController {
291
300
  if (serializerKey && Object.prototype.hasOwnProperty.call(dreamApp.serializers, serializerKey)) {
292
301
  const serializer = dreamApp.serializers[serializerKey];
293
302
  if (serializer && (0, dream_1.isDreamSerializer)(serializer)) {
294
- return new dream_1.SerializerRenderer(
295
303
  // passthrough data going into the serializer is the argument that gets
296
304
  // used in the custom attribute callback function
297
- serializer(data, this.defaultSerializerPassthrough),
298
- // passthrough data must be passed both into the serializer and the SerializerRenderer
305
+ return serializer(data, this.defaultSerializerPassthrough).render(
306
+ // passthrough data must be passed both into the serializer and render
299
307
  // because, if the serializer does accept passthrough data, then passing it in is how
300
308
  // it gets into the serializer, but if it does not accept passthrough data, and therefore
301
309
  // does not pass it into the call to DreamSerializer/ObjectSerializer,
302
310
  // then it would be lost to serializers rendered via rendersOne/Many, and SerializerRenderer
303
311
  // handles passing its passthrough data into those
304
- this.defaultSerializerPassthrough, {
305
- casing: 'camel',
306
- }).render();
312
+ this.defaultSerializerPassthrough, this.renderOpts);
307
313
  }
308
314
  else {
309
315
  throw new Error(`
@@ -27,7 +27,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const dream_1 = require("@rvoh/dream");
30
- const lodash_es_1 = require("lodash-es");
31
30
  const fs = __importStar(require("node:fs/promises"));
32
31
  const node_util_1 = require("node:util");
33
32
  const UnexpectedUndefined_js_1 = __importDefault(require("../error/UnexpectedUndefined.js"));
@@ -36,8 +35,7 @@ const index_js_1 = __importDefault(require("../psychic-app/index.js"));
36
35
  const types_js_1 = require("../router/types.js");
37
36
  const index_js_2 = __importDefault(require("../server/index.js"));
38
37
  const defaults_js_1 = require("./defaults.js");
39
- const schemaDelimiter_js_1 = __importDefault(require("./helpers/schemaDelimiter.js"));
40
- const suppressResponseEnums_js_1 = __importDefault(require("./helpers/suppressResponseEnums.js"));
38
+ const suppressResponseEnumsConfig_js_1 = __importDefault(require("./helpers/suppressResponseEnumsConfig.js"));
41
39
  const debugEnabled = (0, node_util_1.debuglog)('psychic').enabled;
42
40
  class OpenapiAppRenderer {
43
41
  /**
@@ -76,13 +74,12 @@ class OpenapiAppRenderer {
76
74
  return output;
77
75
  }
78
76
  static _toObject(routes, openapiName) {
79
- const opts = {
80
- openapiName,
77
+ const renderOpts = {
81
78
  casing: 'camel',
82
- schemaDelimiter: (0, schemaDelimiter_js_1.default)(openapiName),
83
- suppressResponseEnums: (0, suppressResponseEnums_js_1.default)(openapiName),
79
+ suppressResponseEnums: (0, suppressResponseEnumsConfig_js_1.default)(openapiName),
84
80
  };
85
- let processedSchemas = {};
81
+ const alreadyExtractedDescendantSerializers = {};
82
+ const renderedSchemasOpenapi = {};
86
83
  const psychicApp = index_js_1.default.getOrFail();
87
84
  const controllers = psychicApp.controllers;
88
85
  const openapiConfig = psychicApp.openapi?.[openapiName];
@@ -127,7 +124,10 @@ class OpenapiAppRenderer {
127
124
  const renderer = controller.openapi[key];
128
125
  if (renderer === undefined)
129
126
  throw new UnexpectedUndefined_js_1.default();
130
- const endpointPayloadAndReferencedSerializers = renderer.toPathObject(routes, opts);
127
+ const endpointPayloadAndReferencedSerializers = renderer.toPathObject(routes, {
128
+ openapiName,
129
+ renderOpts,
130
+ });
131
131
  const serializersAppearingInHandWrittenOpenapi = endpointPayloadAndReferencedSerializers.referencedSerializers;
132
132
  const endpointPayload = endpointPayloadAndReferencedSerializers.openapi;
133
133
  if (endpointPayload === undefined)
@@ -148,15 +148,16 @@ class OpenapiAppRenderer {
148
148
  ...finalPathObject.parameters,
149
149
  ...endpointPayloadPath.parameters,
150
150
  ]);
151
- const schemaRenderingResults = renderer.toSchemaObject({
152
- ...opts,
153
- processedSchemas,
151
+ renderer.toSchemaObject({
152
+ openapiName,
153
+ renderOpts,
154
+ renderedSchemasOpenapi,
155
+ alreadyExtractedDescendantSerializers,
154
156
  serializersAppearingInHandWrittenOpenapi,
155
157
  });
156
- processedSchemas = { ...processedSchemas, ...schemaRenderingResults.processedSchemas };
157
158
  finalOutput.components.schemas = {
158
159
  ...finalOutput.components.schemas,
159
- ...schemaRenderingResults.renderedSchemas,
160
+ ...renderedSchemasOpenapi,
160
161
  };
161
162
  }
162
163
  }
@@ -168,7 +169,7 @@ class OpenapiAppRenderer {
168
169
  return finalOutput;
169
170
  }
170
171
  static combineParameters(parameters) {
171
- const groupedParams = (0, lodash_es_1.groupBy)(parameters, 'name');
172
+ const groupedParams = (0, dream_1.groupBy)(parameters, obj => obj.name);
172
173
  return (0, dream_1.compact)(Object.keys(groupedParams).map(paramName => {
173
174
  const identicalParams = groupedParams[paramName] || [];
174
175
  return identicalParams.reduce((compositeParam, param) => {
@@ -12,7 +12,6 @@ const primitiveOpenapiStatementToOpenapi_js_1 = __importDefault(require("./helpe
12
12
  const schemaToRef_js_1 = __importDefault(require("./helpers/schemaToRef.js"));
13
13
  class OpenapiBodySegmentRenderer {
14
14
  bodySegment;
15
- schemaDelimiter;
16
15
  casing;
17
16
  suppressResponseEnums;
18
17
  target;
@@ -23,12 +22,11 @@ class OpenapiBodySegmentRenderer {
23
22
  * Used to recursively parse nested object structures
24
23
  * within nested openapi objects
25
24
  */
26
- constructor(bodySegment, { openapiName, schemaDelimiter, casing, suppressResponseEnums, target }) {
25
+ constructor(bodySegment, { openapiName, renderOpts, target }) {
27
26
  this.openapiName = openapiName;
28
27
  this.bodySegment = bodySegment;
29
- this.schemaDelimiter = schemaDelimiter;
30
- this.casing = casing;
31
- this.suppressResponseEnums = suppressResponseEnums;
28
+ this.casing = renderOpts.casing;
29
+ this.suppressResponseEnums = renderOpts.suppressResponseEnums;
32
30
  this.target = target;
33
31
  }
34
32
  /**
@@ -390,7 +388,6 @@ The following values will be allowed:
390
388
  throw new NonSerializerSuppliedToSerializerBodySegment_js_1.default(this.bodySegment, serializer);
391
389
  const serializerRef = new dream_1.SerializerOpenapiRenderer(serializer, {
392
390
  casing: this.casing,
393
- schemaDelimiter: this.schemaDelimiter,
394
391
  suppressResponseEnums: this.suppressResponseEnums,
395
392
  }).serializerRef;
396
393
  if (serializerRefBodySegment.many) {
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.MissingControllerActionPairingInRoutes = void 0;
7
7
  const dream_1 = require("@rvoh/dream");
8
- const lodash_es_1 = require("lodash-es");
9
8
  const FailedToLookupSerializerForEndpoint_js_1 = __importDefault(require("../error/openapi/FailedToLookupSerializerForEndpoint.js"));
10
9
  const NonSerializerDerivedInOpenapiEndpointRenderer_js_1 = __importDefault(require("../error/openapi/NonSerializerDerivedInOpenapiEndpointRenderer.js"));
11
10
  const NonSerializerDerivedInToSchemaObjects_js_1 = __importDefault(require("../error/openapi/NonSerializerDerivedInToSchemaObjects.js"));
@@ -21,10 +20,6 @@ class OpenapiEndpointRenderer {
21
20
  dreamsOrSerializers;
22
21
  controllerClass;
23
22
  action;
24
- openapiName;
25
- casing;
26
- schemaDelimiter;
27
- suppressResponseEnums;
28
23
  many;
29
24
  paginate;
30
25
  responses;
@@ -96,18 +91,18 @@ class OpenapiEndpointRenderer {
96
91
  * `#toPathObject` specifically builds the `paths` portion of the
97
92
  * final openapi.json output
98
93
  */
99
- toPathObject(routes, { openapiName, casing, schemaDelimiter, suppressResponseEnums, }) {
100
- this.openapiName = openapiName;
101
- this.casing = casing;
102
- this.schemaDelimiter = schemaDelimiter;
103
- this.suppressResponseEnums = suppressResponseEnums;
94
+ toPathObject(routes, { openapiName, renderOpts }) {
104
95
  const path = this.computedPath(routes);
105
96
  const method = this.computedMethod(routes);
106
- const requestBody = this.computedRequestBody(routes);
107
- const responsesAndReferencedSerializers = this.parseResponses();
97
+ const requestBody = this.computedRequestBody(routes, { openapiName, renderOpts });
98
+ const responsesAndReferencedSerializers = this.parseResponses({ openapiName, renderOpts });
108
99
  const output = {
109
100
  [path]: {
110
- parameters: [...this.headersArray(), ...this.pathParamsArray(routes), ...this.queryArray()],
101
+ parameters: [
102
+ ...this.headersArray({ openapiName }),
103
+ ...this.pathParamsArray(routes),
104
+ ...this.queryArray({ openapiName, renderOpts }),
105
+ ],
111
106
  [method]: {
112
107
  tags: this.tags || [],
113
108
  },
@@ -149,18 +144,13 @@ class OpenapiEndpointRenderer {
149
144
  * final openapi.json output, adding any relevant entries that were uncovered
150
145
  * while parsing the responses and provided callback function.
151
146
  */
152
- toSchemaObject({ openapiName, casing, schemaDelimiter, suppressResponseEnums, processedSchemas, serializersAppearingInHandWrittenOpenapi, }) {
153
- this.openapiName = openapiName;
154
- this.casing = casing;
155
- this.schemaDelimiter = schemaDelimiter;
156
- this.suppressResponseEnums = suppressResponseEnums;
147
+ toSchemaObject({ openapiName, renderOpts, alreadyExtractedDescendantSerializers, renderedSchemasOpenapi, serializersAppearingInHandWrittenOpenapi, }) {
157
148
  const serializers = this.getSerializerClasses() ?? [];
158
- return serializersToSchemaObjects(this.controllerClass, this.action, [...serializers, ...serializersAppearingInHandWrittenOpenapi], {
159
- openapiName: this.openapiName,
160
- casing: this.casing,
161
- schemaDelimiter: this.schemaDelimiter,
162
- suppressResponseEnums: this.suppressResponseEnums,
163
- processedSchemas,
149
+ serializersToSchemaObjects(this.controllerClass, this.action, [...serializers, ...serializersAppearingInHandWrittenOpenapi], {
150
+ openapiName,
151
+ renderOpts,
152
+ alreadyExtractedDescendantSerializers,
153
+ renderedSchemasOpenapi,
164
154
  });
165
155
  }
166
156
  /**
@@ -264,10 +254,8 @@ class OpenapiEndpointRenderer {
264
254
  * Generates the header portion of the openapi payload's
265
255
  * "parameters" field for a single endpoint.
266
256
  */
267
- headersArray() {
268
- const defaultHeaders = this.omitDefaultHeaders
269
- ? {}
270
- : (0, openapiOpts_js_1.default)(this.openapiName)?.defaults?.headers || {};
257
+ headersArray({ openapiName }) {
258
+ const defaultHeaders = this.omitDefaultHeaders ? {} : (0, openapiOpts_js_1.default)(openapiName)?.defaults?.headers || {};
271
259
  const headers = { ...defaultHeaders, ...(this.headers || []) };
272
260
  return ((0, dream_1.compact)(Object.keys(headers).map((headerName) => {
273
261
  const header = headers[headerName];
@@ -294,7 +282,7 @@ class OpenapiEndpointRenderer {
294
282
  * Generates the header portion of the openapi payload's
295
283
  * "parameters" field for a single endpoint.
296
284
  */
297
- queryArray() {
285
+ queryArray({ openapiName, renderOpts, }) {
298
286
  const queryParams = Object.keys(this.query || {}).map((queryName) => {
299
287
  const queryParam = this.query[queryName];
300
288
  let output = {
@@ -318,10 +306,8 @@ class OpenapiEndpointRenderer {
318
306
  ...output,
319
307
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
320
308
  schema: new body_segment_js_1.default(queryParam.schema, {
321
- openapiName: this.openapiName,
322
- schemaDelimiter: this.schemaDelimiter,
323
- casing: this.casing,
324
- suppressResponseEnums: this.suppressResponseEnums,
309
+ openapiName,
310
+ renderOpts,
325
311
  target: 'request',
326
312
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
327
313
  }).render().openapi,
@@ -349,7 +335,7 @@ class OpenapiEndpointRenderer {
349
335
  *
350
336
  * Generates the requestBody portion of the endpoint
351
337
  */
352
- computedRequestBody(routes) {
338
+ computedRequestBody(routes, { openapiName, renderOpts, }) {
353
339
  const method = this.computedMethod(routes);
354
340
  if (this.requestBody === null)
355
341
  return this.defaultRequestBody();
@@ -357,13 +343,11 @@ class OpenapiEndpointRenderer {
357
343
  if (!httpMethodsThatAllowBody.includes(method))
358
344
  return this.defaultRequestBody();
359
345
  if (this.shouldAutogenerateBody()) {
360
- return this.generateRequestBodyForModel();
346
+ return this.generateRequestBodyForModel({ openapiName, renderOpts });
361
347
  }
362
348
  let schema = new body_segment_js_1.default(this.requestBody, {
363
- openapiName: this.openapiName,
364
- schemaDelimiter: this.schemaDelimiter,
365
- casing: this.casing,
366
- suppressResponseEnums: this.suppressResponseEnums,
349
+ openapiName,
350
+ renderOpts,
367
351
  target: 'request',
368
352
  }).render().openapi;
369
353
  const bodyPageParam = this.paginate?.body;
@@ -427,7 +411,7 @@ class OpenapiEndpointRenderer {
427
411
  * that model that are safe to ingest will be automatically added to
428
412
  * the request body.
429
413
  */
430
- generateRequestBodyForModel() {
414
+ generateRequestBodyForModel({ openapiName, renderOpts, }) {
431
415
  const forDreamClass = this.requestBody?.for;
432
416
  const dreamClass = forDreamClass || this.getSingleDreamModelClass();
433
417
  if (!dreamClass)
@@ -540,10 +524,8 @@ class OpenapiEndpointRenderer {
540
524
  paramsShape.properties = {
541
525
  ...paramsShape.properties,
542
526
  [columnName]: new body_segment_js_1.default(metadata.type, {
543
- openapiName: this.openapiName,
544
- schemaDelimiter: this.schemaDelimiter,
545
- casing: this.casing,
546
- suppressResponseEnums: this.suppressResponseEnums,
527
+ openapiName,
528
+ renderOpts,
547
529
  target: 'request',
548
530
  }).render().openapi,
549
531
  };
@@ -587,10 +569,8 @@ class OpenapiEndpointRenderer {
587
569
  }
588
570
  }
589
571
  let processedSchema = new body_segment_js_1.default(paramsShape, {
590
- openapiName: this.openapiName,
591
- schemaDelimiter: this.schemaDelimiter,
592
- casing: this.casing,
593
- suppressResponseEnums: this.suppressResponseEnums,
572
+ openapiName,
573
+ renderOpts,
594
574
  target: 'request',
595
575
  }).render().openapi;
596
576
  const bodyPageParam = this.paginate?.body;
@@ -610,13 +590,11 @@ class OpenapiEndpointRenderer {
610
590
  *
611
591
  * Generates the responses portion of the endpoint
612
592
  */
613
- parseResponses() {
593
+ parseResponses({ openapiName, renderOpts, }) {
614
594
  let responseData = {};
615
595
  const rendererOpts = {
616
- openapiName: this.openapiName,
617
- schemaDelimiter: this.schemaDelimiter,
618
- casing: this.casing,
619
- suppressResponseEnums: this.suppressResponseEnums,
596
+ openapiName,
597
+ renderOpts,
620
598
  target: 'response',
621
599
  };
622
600
  const computedStatus = this.status || this.defaultStatus;
@@ -632,7 +610,7 @@ class OpenapiEndpointRenderer {
632
610
  };
633
611
  }
634
612
  else {
635
- const parsingResults = this.parseSerializerResponseShape();
613
+ const parsingResults = this.parseSerializerResponseShape({ renderOpts });
636
614
  serializersAppearingInHandWrittenOpenapi = [
637
615
  ...serializersAppearingInHandWrittenOpenapi,
638
616
  ...parsingResults.referencedSerializers,
@@ -647,7 +625,7 @@ class OpenapiEndpointRenderer {
647
625
  }
648
626
  Object.keys(this.responses || {}).forEach(statusCode => {
649
627
  const statusCodeInt = parseInt(statusCode);
650
- const response = (0, lodash_es_1.cloneDeep)(this.responses[statusCodeInt]);
628
+ const response = (0, dream_1.cloneDeepSafe)(this.responses[statusCodeInt], obj => obj);
651
629
  responseData[statusCodeInt] ||= { description: statusDescription(statusCodeInt) };
652
630
  const statusResponse = responseData[statusCodeInt];
653
631
  const results = new body_segment_js_1.default(response, rendererOpts).render();
@@ -663,13 +641,13 @@ class OpenapiEndpointRenderer {
663
641
  });
664
642
  const defaultResponses = this.omitDefaultResponses
665
643
  ? {}
666
- : (0, openapiOpts_js_1.default)(this.openapiName)?.defaults?.responses || {};
644
+ : (0, openapiOpts_js_1.default)(openapiName)?.defaults?.responses || {};
667
645
  const psychicAndConfigLevelDefaults = this.omitDefaultResponses
668
646
  ? {}
669
- : (0, lodash_es_1.cloneDeep)({
647
+ : (0, dream_1.cloneDeepSafe)({
670
648
  ...defaults_js_1.DEFAULT_OPENAPI_RESPONSES,
671
649
  ...defaultResponses,
672
- });
650
+ }, obj => obj);
673
651
  Object.keys(psychicAndConfigLevelDefaults).forEach(key => {
674
652
  if (!responseData[key]) {
675
653
  const data = psychicAndConfigLevelDefaults[key];
@@ -708,7 +686,7 @@ class OpenapiEndpointRenderer {
708
686
  * returns a ref object for the callback passed to the
709
687
  * Openapi decorator.
710
688
  */
711
- parseSerializerResponseShape() {
689
+ parseSerializerResponseShape({ renderOpts, }) {
712
690
  const serializerClasses = this.getSerializerClasses();
713
691
  if (!serializerClasses)
714
692
  return {
@@ -716,9 +694,9 @@ class OpenapiEndpointRenderer {
716
694
  openapi: { description: 'no content' },
717
695
  };
718
696
  if (serializerClasses.length > 1) {
719
- return this.parseMultiEntitySerializerResponseShape(serializerClasses);
697
+ return this.parseMultiEntitySerializerResponseShape(serializerClasses, { renderOpts });
720
698
  }
721
- return this.parseSingleEntitySerializerResponseShape(serializerClasses[0]);
699
+ return this.parseSingleEntitySerializerResponseShape(serializerClasses[0], { renderOpts });
722
700
  }
723
701
  /**
724
702
  * @internal
@@ -731,16 +709,14 @@ class OpenapiEndpointRenderer {
731
709
  * public show() {...}
732
710
  * ```
733
711
  */
734
- parseSingleEntitySerializerResponseShape(serializer) {
712
+ parseSingleEntitySerializerResponseShape(serializer, { renderOpts, }) {
735
713
  if (serializer === undefined) {
736
714
  throw new FailedToLookupSerializerForEndpoint_js_1.default(this.controllerClass, this.action);
737
715
  }
738
716
  if (!(0, dream_1.isDreamSerializer)(serializer)) {
739
717
  throw new SerializerForEndpointNotAFunction_js_1.default(this.controllerClass, this.action, serializer);
740
718
  }
741
- const serializerOpenapiRenderer = new dream_1.SerializerOpenapiRenderer(serializer, {
742
- schemaDelimiter: this.schemaDelimiter,
743
- });
719
+ const serializerOpenapiRenderer = new dream_1.SerializerOpenapiRenderer(serializer, renderOpts);
744
720
  const finalOutput = {
745
721
  content: {
746
722
  'application/json': {
@@ -807,18 +783,16 @@ class OpenapiEndpointRenderer {
807
783
  * public responses() {...}
808
784
  * ```
809
785
  */
810
- parseMultiEntitySerializerResponseShape(serializers) {
786
+ parseMultiEntitySerializerResponseShape(serializers, { renderOpts, }) {
811
787
  const anyOf = { anyOf: [] };
812
788
  serializers.forEach(serializer => {
813
789
  if (!(0, dream_1.isDreamSerializer)(serializer))
814
790
  throw new NonSerializerDerivedInOpenapiEndpointRenderer_js_1.default(this.controllerClass, this.action, serializer);
815
791
  });
816
- const sortedSerializerClasses = (0, dream_1.sortBy)(serializers, serializer => new dream_1.SerializerOpenapiRenderer(serializer, { schemaDelimiter: this.schemaDelimiter }).openapiName);
792
+ const sortedSerializerClasses = (0, dream_1.sortBy)(serializers, serializer => new dream_1.SerializerOpenapiRenderer(serializer, renderOpts).openapiName);
817
793
  let referencedSerializers = [];
818
794
  sortedSerializerClasses.forEach(serializer => {
819
- const serializerOpenapiRenderer = new dream_1.SerializerOpenapiRenderer(serializer, {
820
- schemaDelimiter: this.schemaDelimiter,
821
- });
795
+ const serializerOpenapiRenderer = new dream_1.SerializerOpenapiRenderer(serializer, renderOpts);
822
796
  anyOf.anyOf.push(serializerOpenapiRenderer.serializerRef);
823
797
  referencedSerializers = [
824
798
  ...referencedSerializers,
@@ -998,55 +972,34 @@ function statusDescription(status) {
998
972
  return `Status ${status}`;
999
973
  }
1000
974
  }
1001
- function serializersToSchemaObjects(controllerClass, actionName, serializers, { casing, schemaDelimiter, suppressResponseEnums, openapiName, processedSchemas, }) {
975
+ function serializersToSchemaObjects(controllerClass, actionName, serializers, { renderOpts, openapiName, alreadyExtractedDescendantSerializers, renderedSchemasOpenapi, }) {
1002
976
  serializers.forEach(serializer => {
1003
977
  if (!(0, dream_1.isDreamSerializer)(serializer))
1004
978
  throw new NonSerializerDerivedInToSchemaObjects_js_1.default(controllerClass, actionName, serializer);
1005
979
  });
1006
- serializers = serializers.filter(serializer => {
1007
- const serializerOpenapiRenderer = new dream_1.SerializerOpenapiRenderer(serializer, {
1008
- casing,
1009
- schemaDelimiter,
1010
- suppressResponseEnums,
1011
- });
1012
- return !processedSchemas[serializerOpenapiRenderer.globalName];
1013
- });
980
+ serializers = serializers.filter(serializer => !renderedSchemasOpenapi[new dream_1.SerializerOpenapiRenderer(serializer, renderOpts).openapiName]);
1014
981
  if (!serializers.length)
1015
- return { processedSchemas, renderedSchemas: {} };
1016
- const renderedSchemas = {};
982
+ return;
1017
983
  let dependentOnSerializers = [];
1018
984
  serializers.forEach(serializer => {
1019
- const renderer = new dream_1.SerializerOpenapiRenderer(serializer, {
1020
- casing,
1021
- schemaDelimiter,
1022
- suppressResponseEnums,
1023
- });
1024
- const globalName = renderer.globalName;
1025
- processedSchemas = { ...processedSchemas, [globalName]: true };
1026
- const results = renderer.renderedOpenapi(processedSchemas);
985
+ const renderer = new dream_1.SerializerOpenapiRenderer(serializer, renderOpts);
986
+ const results = renderer.renderedOpenapi(alreadyExtractedDescendantSerializers);
1027
987
  const segmentRendererResults = new body_segment_js_1.default(results.openapi, {
1028
988
  openapiName,
1029
- casing,
1030
- schemaDelimiter,
1031
- suppressResponseEnums,
989
+ renderOpts,
1032
990
  target: 'response',
1033
991
  }).render();
1034
- renderedSchemas[renderer.openapiName] = segmentRendererResults.openapi;
992
+ renderedSchemasOpenapi[renderer.openapiName] = segmentRendererResults.openapi;
1035
993
  dependentOnSerializers = [
1036
994
  ...dependentOnSerializers,
1037
995
  ...results.referencedSerializers,
1038
- ...segmentRendererResults.referencedSerializers,
996
+ ...segmentRendererResults.referencedSerializers, // should always be empty
1039
997
  ];
1040
998
  });
1041
- const recursiveResults = serializersToSchemaObjects(controllerClass, actionName, dependentOnSerializers, {
1042
- casing,
1043
- suppressResponseEnums,
1044
- schemaDelimiter,
999
+ serializersToSchemaObjects(controllerClass, actionName, dependentOnSerializers, {
1000
+ renderOpts,
1045
1001
  openapiName,
1046
- processedSchemas,
1002
+ alreadyExtractedDescendantSerializers,
1003
+ renderedSchemasOpenapi,
1047
1004
  });
1048
- return {
1049
- processedSchemas: { ...processedSchemas, ...recursiveResults.processedSchemas },
1050
- renderedSchemas: { ...renderedSchemas, ...recursiveResults.renderedSchemas },
1051
- };
1052
1005
  }
@@ -3,11 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.default = suppressResponseEnums;
6
+ exports.default = suppressResponseEnumsConfig;
7
7
  const openapiOpts_js_1 = __importDefault(require("./openapiOpts.js"));
8
8
  /**
9
9
  * returns either the delimiter set in the app config, or else a blank string
10
10
  */
11
- function suppressResponseEnums(openapiName) {
11
+ function suppressResponseEnumsConfig(openapiName) {
12
12
  return !!(0, openapiOpts_js_1.default)(openapiName)?.suppressResponseEnums;
13
13
  }
@@ -190,7 +190,6 @@ Try setting it to something valid, like:
190
190
  _openapi = {
191
191
  default: {
192
192
  outputFilename: 'openapi.json',
193
- schemaDelimiter: '',
194
193
  info: {
195
194
  title: 'untitled openapi spec',
196
195
  version: 'unknown version',
@@ -30,14 +30,10 @@ const dream_1 = require("@rvoh/dream");
30
30
  const cookieParser = __importStar(require("cookie-parser"));
31
31
  const cors = __importStar(require("cors"));
32
32
  const express = __importStar(require("express"));
33
- const OpenApiValidator = __importStar(require("express-openapi-validator"));
34
- const path = __importStar(require("node:path"));
35
- const node_util_1 = require("node:util");
36
- const isOpenapiError_js_1 = __importDefault(require("../helpers/isOpenapiError.js"));
37
33
  const index_js_1 = __importDefault(require("../psychic-app/index.js"));
38
34
  const index_js_2 = __importDefault(require("../router/index.js"));
39
35
  const startPsychicServer_js_1 = __importStar(require("./helpers/startPsychicServer.js"));
40
- const debugEnabled = (0, node_util_1.debuglog)('psychic').enabled;
36
+ // const debugEnabled = debuglog('psychic').enabled
41
37
  class PsychicServer {
42
38
  static async startPsychicServer(opts) {
43
39
  return await (0, startPsychicServer_js_1.default)(opts);
@@ -92,7 +88,6 @@ class PsychicServer {
92
88
  for (const serverInitAfterMiddlewareHook of this.config.specialHooks.serverInitAfterMiddleware) {
93
89
  await serverInitAfterMiddlewareHook(this);
94
90
  }
95
- this.initializeOpenapiValidation();
96
91
  await this.buildRoutes();
97
92
  for (const afterRoutesHook of this.config.specialHooks.serverInitAfterRoutes) {
98
93
  await afterRoutesHook(this);
@@ -171,35 +166,6 @@ class PsychicServer {
171
166
  initializeJSON() {
172
167
  this.expressApp.use(express.json(this.config.jsonOptions));
173
168
  }
174
- initializeOpenapiValidation() {
175
- const psychicApp = index_js_1.default.getOrFail();
176
- for (const openapiName in psychicApp.openapi) {
177
- const openapiOpts = psychicApp.openapi[openapiName];
178
- if (openapiOpts?.validation) {
179
- const opts = openapiOpts.validation;
180
- opts.apiSpec ||= path.join(psychicApp.apiRoot, 'openapi.json');
181
- this.expressApp.use(OpenApiValidator.middleware(opts));
182
- this.expressApp.use((err, req, res, next) => {
183
- if ((0, isOpenapiError_js_1.default)(err)) {
184
- if (debugEnabled) {
185
- index_js_1.default.log((0, node_util_1.inspect)(err));
186
- console.trace();
187
- }
188
- res.status(err.status).json({
189
- message: err.message,
190
- errors: err.errors,
191
- });
192
- }
193
- else {
194
- if (debugEnabled) {
195
- index_js_1.default.logWithLevel('error', err);
196
- }
197
- next();
198
- }
199
- });
200
- }
201
- }
202
- }
203
169
  async buildRoutes() {
204
170
  const r = new index_js_2.default(this.expressApp, this.config);
205
171
  const psychicApp = index_js_1.default.getOrFail();