@rvoh/psychic 2.3.8 → 3.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/cli/index.js +4 -0
- package/dist/cjs/src/controller/helpers/logIfDevelopment.js +5 -5
- package/dist/cjs/src/controller/index.js +119 -40
- package/dist/cjs/src/devtools/helpers/launchDevServer.js +15 -1
- package/dist/cjs/src/error/openapi/UnrecognizedDbTypeFoundWhileComputingOpenapiAttribute.js +44 -0
- package/dist/cjs/src/error/router/cannot-commit-routes-without-koa-app.js +12 -0
- package/dist/cjs/src/helpers/toJson.js +2 -8
- package/dist/cjs/src/helpers/validateOpenApiSchema.js +1 -1
- package/dist/cjs/src/openapi-renderer/SerializerOpenapiRenderer.js +2 -2
- package/dist/cjs/src/openapi-renderer/endpoint.js +2 -2
- package/dist/cjs/src/openapi-renderer/helpers/OpenapiPayloadValidator.js +75 -9
- package/dist/cjs/src/openapi-renderer/helpers/{dreamAttributeOpenapiShape.js → dreamColumnOpenapiShape.js} +19 -6
- package/dist/cjs/src/openapi-renderer/helpers/stringify-cache.js +55 -0
- package/dist/cjs/src/openapi-renderer/helpers/validator-cache.js +52 -0
- package/dist/cjs/src/psychic-app/helpers/import/importControllers.js +1 -1
- package/dist/cjs/src/psychic-app/index.js +3 -10
- package/dist/cjs/src/router/index.js +31 -25
- package/dist/cjs/src/server/helpers/startPsychicServer.js +6 -2
- package/dist/cjs/src/server/index.js +32 -35
- package/dist/cjs/src/server/params.js +56 -3
- package/dist/cjs/src/session/index.js +9 -12
- package/dist/esm/src/cli/index.js +4 -0
- package/dist/esm/src/controller/helpers/logIfDevelopment.js +5 -5
- package/dist/esm/src/controller/index.js +119 -40
- package/dist/esm/src/devtools/helpers/launchDevServer.js +15 -1
- package/dist/esm/src/error/openapi/UnrecognizedDbTypeFoundWhileComputingOpenapiAttribute.js +44 -0
- package/dist/esm/src/error/router/cannot-commit-routes-without-koa-app.js +12 -0
- package/dist/esm/src/helpers/toJson.js +2 -8
- package/dist/esm/src/helpers/validateOpenApiSchema.js +1 -1
- package/dist/esm/src/openapi-renderer/SerializerOpenapiRenderer.js +2 -2
- package/dist/esm/src/openapi-renderer/endpoint.js +2 -2
- package/dist/esm/src/openapi-renderer/helpers/OpenapiPayloadValidator.js +75 -9
- package/dist/esm/src/openapi-renderer/helpers/{dreamAttributeOpenapiShape.js → dreamColumnOpenapiShape.js} +19 -6
- package/dist/esm/src/openapi-renderer/helpers/stringify-cache.js +55 -0
- package/dist/esm/src/openapi-renderer/helpers/validator-cache.js +52 -0
- package/dist/esm/src/psychic-app/helpers/import/importControllers.js +1 -1
- package/dist/esm/src/psychic-app/index.js +3 -10
- package/dist/esm/src/router/index.js +31 -25
- package/dist/esm/src/server/helpers/startPsychicServer.js +6 -2
- package/dist/esm/src/server/index.js +32 -35
- package/dist/esm/src/server/params.js +56 -3
- package/dist/esm/src/session/index.js +9 -12
- package/dist/types/src/controller/helpers/logIfDevelopment.d.ts +3 -4
- package/dist/types/src/controller/index.d.ts +19 -8
- package/dist/types/src/devtools/helpers/launchDevServer.d.ts +2 -1
- package/dist/types/src/error/openapi/UnrecognizedDbTypeFoundWhileComputingOpenapiAttribute.d.ts +7 -0
- package/dist/types/src/error/router/cannot-commit-routes-without-koa-app.d.ts +3 -0
- package/dist/types/src/helpers/cookieMaxAgeFromCookieOpts.d.ts +1 -1
- package/dist/types/src/helpers/toJson.d.ts +1 -1
- package/dist/types/src/helpers/validateOpenApiSchema.d.ts +5 -1
- package/dist/types/src/openapi-renderer/helpers/OpenapiPayloadValidator.d.ts +41 -0
- package/dist/types/src/openapi-renderer/helpers/{dreamAttributeOpenapiShape.d.ts → dreamColumnOpenapiShape.d.ts} +1 -1
- package/dist/types/src/openapi-renderer/helpers/stringify-cache.d.ts +34 -0
- package/dist/types/src/openapi-renderer/helpers/validator-cache.d.ts +35 -0
- package/dist/types/src/psychic-app/index.d.ts +11 -14
- package/dist/types/src/router/index.d.ts +17 -17
- package/dist/types/src/router/route-manager.d.ts +4 -3
- package/dist/types/src/server/helpers/startPsychicServer.d.ts +3 -3
- package/dist/types/src/server/index.d.ts +3 -3
- package/dist/types/src/server/params.d.ts +2 -2
- package/dist/types/src/session/index.d.ts +13 -5
- package/package.json +30 -19
- package/dist/cjs/src/error/router/cannot-commit-routes-without-express-app.js +0 -12
- package/dist/esm/src/error/router/cannot-commit-routes-without-express-app.js +0 -12
- package/dist/types/src/error/router/cannot-commit-routes-without-express-app.d.ts +0 -3
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { closeAllDbConnections } from '@rvoh/dream/db';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import cors from '@koa/cors';
|
|
3
|
+
import Koa from 'koa';
|
|
4
|
+
import koaBodyparser from 'koa-bodyparser';
|
|
5
|
+
import conditional from 'koa-conditional-get';
|
|
6
|
+
import etag from 'koa-etag';
|
|
5
7
|
import logIfDevelopment from '../controller/helpers/logIfDevelopment.js';
|
|
6
8
|
import EnvInternal from '../helpers/EnvInternal.js';
|
|
7
9
|
import PsychicApp from '../psychic-app/index.js';
|
|
@@ -15,14 +17,14 @@ export default class PsychicServer {
|
|
|
15
17
|
static createPsychicHttpInstance(app, sslCredentials) {
|
|
16
18
|
return createPsychicHttpInstance(app, sslCredentials);
|
|
17
19
|
}
|
|
18
|
-
|
|
20
|
+
koaApp;
|
|
19
21
|
httpServer;
|
|
20
22
|
booted = false;
|
|
21
23
|
constructor() {
|
|
22
24
|
this.buildApp();
|
|
23
25
|
}
|
|
24
26
|
async routes() {
|
|
25
|
-
const r = new PsychicRouter(this.
|
|
27
|
+
const r = new PsychicRouter(this.koaApp);
|
|
26
28
|
await PsychicApp.getOrFail().routesCb(r);
|
|
27
29
|
return r.routes;
|
|
28
30
|
}
|
|
@@ -31,16 +33,19 @@ export default class PsychicServer {
|
|
|
31
33
|
return;
|
|
32
34
|
const psychicApp = PsychicApp.getOrFail();
|
|
33
35
|
this.setSecureDefaultHeaders();
|
|
34
|
-
this.
|
|
36
|
+
this.koaApp.use(async (ctx, next) => {
|
|
35
37
|
Object.keys(psychicApp.defaultResponseHeaders).forEach(key => {
|
|
36
|
-
|
|
38
|
+
ctx.set(key, psychicApp.defaultResponseHeaders[key]);
|
|
37
39
|
});
|
|
38
|
-
next();
|
|
40
|
+
await next();
|
|
39
41
|
});
|
|
40
42
|
for (const serverInitBeforeMiddlewareHook of PsychicApp.getOrFail().specialHooks
|
|
41
43
|
.serverInitBeforeMiddleware) {
|
|
42
44
|
await serverInitBeforeMiddlewareHook(this);
|
|
43
45
|
}
|
|
46
|
+
// ETag support (Express has this built-in, Koa needs middleware)
|
|
47
|
+
this.koaApp.use(conditional());
|
|
48
|
+
this.koaApp.use(etag());
|
|
44
49
|
this.initializeCors();
|
|
45
50
|
this.initializeJSON();
|
|
46
51
|
try {
|
|
@@ -69,29 +74,23 @@ export default class PsychicServer {
|
|
|
69
74
|
applyNotFoundMiddleware() {
|
|
70
75
|
if (!EnvInternal.isDevelopment)
|
|
71
76
|
return;
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// pick up the correct status code.
|
|
80
|
-
if (res.statusCode === 200)
|
|
81
|
-
res.status(404);
|
|
82
|
-
logIfDevelopment({ req, res, startTime: Date.now(), fallbackStatusCode: 404 });
|
|
83
|
-
// call next to let express handle sending the 404
|
|
84
|
-
next();
|
|
77
|
+
this.koaApp.use(async (ctx, next) => {
|
|
78
|
+
await next();
|
|
79
|
+
// Koa defaults to 404 for unmatched routes. If nothing set the body,
|
|
80
|
+
// log the 404 in development.
|
|
81
|
+
if (ctx.status === 404 && !ctx.body) {
|
|
82
|
+
logIfDevelopment({ ctx, startTime: Date.now(), fallbackStatusCode: 404 });
|
|
83
|
+
}
|
|
85
84
|
});
|
|
86
85
|
}
|
|
87
86
|
setSecureDefaultHeaders() {
|
|
88
|
-
|
|
89
|
-
this.
|
|
90
|
-
|
|
87
|
+
// Koa doesn't send x-powered-by by default, no need to disable it.
|
|
88
|
+
this.koaApp.use(async (ctx, next) => {
|
|
89
|
+
ctx.set('X-Content-Type-Options', 'nosniff');
|
|
91
90
|
if (EnvInternal.isProduction) {
|
|
92
|
-
|
|
91
|
+
ctx.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
93
92
|
}
|
|
94
|
-
next();
|
|
93
|
+
await next();
|
|
95
94
|
});
|
|
96
95
|
}
|
|
97
96
|
// TODO: use config helper for fetching default port
|
|
@@ -104,7 +103,7 @@ export default class PsychicServer {
|
|
|
104
103
|
}
|
|
105
104
|
else {
|
|
106
105
|
const httpServer = await startPsychicServer({
|
|
107
|
-
app: this.
|
|
106
|
+
app: this.koaApp,
|
|
108
107
|
port: port || psychicApp.port,
|
|
109
108
|
sslCredentials: PsychicApp.getOrFail().sslCredentials,
|
|
110
109
|
});
|
|
@@ -146,26 +145,24 @@ export default class PsychicServer {
|
|
|
146
145
|
await this.boot();
|
|
147
146
|
let server;
|
|
148
147
|
await new Promise(accept => {
|
|
149
|
-
server = this.
|
|
148
|
+
server = this.koaApp.listen(port, () => accept({}));
|
|
150
149
|
});
|
|
151
150
|
await block();
|
|
152
151
|
server.close();
|
|
153
152
|
return true;
|
|
154
153
|
}
|
|
155
154
|
buildApp() {
|
|
156
|
-
this.
|
|
157
|
-
this.expressApp.use(cookieParser.default());
|
|
155
|
+
this.koaApp = new Koa();
|
|
158
156
|
}
|
|
159
157
|
initializeCors() {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
cors.default(PsychicApp.getOrFail().corsOptions));
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
159
|
+
this.koaApp.use(cors(PsychicApp.getOrFail().corsOptions));
|
|
163
160
|
}
|
|
164
161
|
initializeJSON() {
|
|
165
|
-
this.
|
|
162
|
+
this.koaApp.use(koaBodyparser(PsychicApp.getOrFail().jsonOptions));
|
|
166
163
|
}
|
|
167
164
|
async buildRoutes() {
|
|
168
|
-
const r = new PsychicRouter(this.
|
|
165
|
+
const r = new PsychicRouter(this.koaApp);
|
|
169
166
|
await PsychicApp.getOrFail().routesCb(r);
|
|
170
167
|
r.commit();
|
|
171
168
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CalendarDate, DateTime } from '@rvoh/dream';
|
|
1
|
+
import { CalendarDate, ClockTime, ClockTimeTz, DateTime } from '@rvoh/dream';
|
|
2
2
|
import { camelize, compact, snakeify } from '@rvoh/dream/utils';
|
|
3
3
|
import ParamValidationError from '../error/controller/ParamValidationError.js';
|
|
4
4
|
import ParamValidationErrors from '../error/controller/ParamValidationErrors.js';
|
|
@@ -85,6 +85,22 @@ export default class Params {
|
|
|
85
85
|
case 'timestamp without time zone[]':
|
|
86
86
|
returnObj[columnName] = this.cast(params, columnName.toString(), 'datetime[]', { allowNull: columnMetadata.allowNull });
|
|
87
87
|
break;
|
|
88
|
+
case 'time':
|
|
89
|
+
case 'time without time zone':
|
|
90
|
+
returnObj[columnName] = this.cast(params, columnName.toString(), 'time', { allowNull: columnMetadata.allowNull });
|
|
91
|
+
break;
|
|
92
|
+
case 'time[]':
|
|
93
|
+
case 'time without time zone[]':
|
|
94
|
+
returnObj[columnName] = this.cast(params, columnName.toString(), 'time[]', { allowNull: columnMetadata.allowNull });
|
|
95
|
+
break;
|
|
96
|
+
case 'timetz':
|
|
97
|
+
case 'time with time zone':
|
|
98
|
+
returnObj[columnName] = this.cast(params, columnName.toString(), 'timetz', { allowNull: columnMetadata.allowNull });
|
|
99
|
+
break;
|
|
100
|
+
case 'timetz[]':
|
|
101
|
+
case 'time with time zone[]':
|
|
102
|
+
returnObj[columnName] = this.cast(params, columnName.toString(), 'timetz[]', { allowNull: columnMetadata.allowNull });
|
|
103
|
+
break;
|
|
88
104
|
case 'jsonb':
|
|
89
105
|
returnObj[columnName] = this.cast(params, columnName.toString(), 'json', { allowNull: columnMetadata.allowNull });
|
|
90
106
|
break;
|
|
@@ -203,7 +219,6 @@ export default class Params {
|
|
|
203
219
|
}
|
|
204
220
|
return paramValue;
|
|
205
221
|
}
|
|
206
|
-
let dateClass;
|
|
207
222
|
const integerRegexp = /^-?\d+$/;
|
|
208
223
|
switch (expectedType) {
|
|
209
224
|
case 'string':
|
|
@@ -227,7 +242,8 @@ export default class Params {
|
|
|
227
242
|
return false;
|
|
228
243
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
229
244
|
case 'datetime':
|
|
230
|
-
case 'date':
|
|
245
|
+
case 'date': {
|
|
246
|
+
let dateClass;
|
|
231
247
|
switch (expectedType) {
|
|
232
248
|
case 'datetime':
|
|
233
249
|
dateClass = DateTime;
|
|
@@ -252,6 +268,35 @@ export default class Params {
|
|
|
252
268
|
}
|
|
253
269
|
}
|
|
254
270
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
271
|
+
}
|
|
272
|
+
case 'time':
|
|
273
|
+
case 'timetz': {
|
|
274
|
+
let timeClass;
|
|
275
|
+
switch (expectedType) {
|
|
276
|
+
case 'time':
|
|
277
|
+
timeClass = ClockTime;
|
|
278
|
+
break;
|
|
279
|
+
case 'timetz':
|
|
280
|
+
timeClass = ClockTimeTz;
|
|
281
|
+
break;
|
|
282
|
+
default:
|
|
283
|
+
if (typeof expectedType === 'string')
|
|
284
|
+
throw Error(`${expectedType} must be "time" or "timetz"`);
|
|
285
|
+
else
|
|
286
|
+
throw Error(`expectedType is not a string`);
|
|
287
|
+
}
|
|
288
|
+
if (paramValue instanceof ClockTime || paramValue instanceof ClockTimeTz)
|
|
289
|
+
return paramValue;
|
|
290
|
+
if (typeof paramValue === 'string') {
|
|
291
|
+
try {
|
|
292
|
+
return timeClass.fromISO(paramValue);
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
299
|
+
}
|
|
255
300
|
case 'integer':
|
|
256
301
|
if (typeof paramValue !== 'string' && typeof paramValue !== 'number')
|
|
257
302
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
@@ -287,6 +332,8 @@ export default class Params {
|
|
|
287
332
|
case 'json[]':
|
|
288
333
|
case 'number[]':
|
|
289
334
|
case 'string[]':
|
|
335
|
+
case 'time[]':
|
|
336
|
+
case 'timetz[]':
|
|
290
337
|
case 'uuid[]':
|
|
291
338
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
292
339
|
if (!Array.isArray(paramValue))
|
|
@@ -365,6 +412,8 @@ const typeToErrorMap = {
|
|
|
365
412
|
null: 'expecting null',
|
|
366
413
|
number: 'expected number or string number',
|
|
367
414
|
string: 'expected string',
|
|
415
|
+
time: 'expected ISO time string',
|
|
416
|
+
timetz: 'expected ISO timetz string',
|
|
368
417
|
uuid: 'expected uuid',
|
|
369
418
|
'bigint[]': 'expected bigint array',
|
|
370
419
|
'boolean[]': 'expected boolean array',
|
|
@@ -375,6 +424,8 @@ const typeToErrorMap = {
|
|
|
375
424
|
'null[]': 'expecting null array',
|
|
376
425
|
'number[]': 'expected number or string number array',
|
|
377
426
|
'string[]': 'expected string array',
|
|
427
|
+
'time[]': 'expected ISO time string array',
|
|
428
|
+
'timetz[]': 'expected ISO timetz string array',
|
|
378
429
|
'uuid[]': 'expected uuid array',
|
|
379
430
|
};
|
|
380
431
|
function typeToError(param) {
|
|
@@ -393,6 +444,8 @@ const arrayTypeToNonArrayTypeMap = {
|
|
|
393
444
|
'null[]': 'null',
|
|
394
445
|
'number[]': 'number',
|
|
395
446
|
'string[]': 'string',
|
|
447
|
+
'time[]': 'time',
|
|
448
|
+
'timetz[]': 'timetz',
|
|
396
449
|
'uuid[]': 'uuid',
|
|
397
450
|
};
|
|
398
451
|
function arrayTypeToNonArrayType(param) {
|
|
@@ -3,31 +3,28 @@ import cookieMaxAgeFromCookieOpts from '../helpers/cookieMaxAgeFromCookieOpts.js
|
|
|
3
3
|
import EnvInternal from '../helpers/EnvInternal.js';
|
|
4
4
|
import PsychicApp from '../psychic-app/index.js';
|
|
5
5
|
export default class Session {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
this.req = req;
|
|
10
|
-
this.res = res;
|
|
6
|
+
ctx;
|
|
7
|
+
constructor(ctx) {
|
|
8
|
+
this.ctx = ctx;
|
|
11
9
|
}
|
|
12
10
|
getCookie(name) {
|
|
13
|
-
const
|
|
14
|
-
const value = cookies[name];
|
|
11
|
+
const value = this.ctx.cookies.get(name);
|
|
15
12
|
if (value)
|
|
16
13
|
return InternalEncrypt.decryptCookie(value);
|
|
17
14
|
return null;
|
|
18
15
|
}
|
|
19
16
|
setCookie(name, data, opts = {}) {
|
|
20
|
-
this.
|
|
21
|
-
secure: EnvInternal.isProduction,
|
|
22
|
-
httpOnly: true,
|
|
17
|
+
this.ctx.cookies.set(name, InternalEncrypt.encryptCookie(data), {
|
|
23
18
|
...opts,
|
|
19
|
+
secure: opts.secure ?? EnvInternal.isProduction,
|
|
20
|
+
httpOnly: opts.httpOnly ?? true,
|
|
24
21
|
maxAge: opts.maxAge
|
|
25
22
|
? cookieMaxAgeFromCookieOpts(opts.maxAge)
|
|
26
|
-
: PsychicApp.getOrFail().cookieOptions?.maxAge,
|
|
23
|
+
: (PsychicApp.getOrFail().cookieOptions?.maxAge ?? cookieMaxAgeFromCookieOpts()),
|
|
27
24
|
});
|
|
28
25
|
}
|
|
29
26
|
clearCookie(name) {
|
|
30
|
-
this.
|
|
27
|
+
this.ctx.cookies.set(name, '', { maxAge: 0 });
|
|
31
28
|
}
|
|
32
29
|
daysToMilliseconds(numDays) {
|
|
33
30
|
return numDays * 60 * 60 * 24 * 1000;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default function logIfDevelopment({
|
|
3
|
-
|
|
4
|
-
res: Response;
|
|
1
|
+
import Koa from 'koa';
|
|
2
|
+
export default function logIfDevelopment({ ctx, startTime, fallbackStatusCode, }: {
|
|
3
|
+
ctx: Koa.Context;
|
|
5
4
|
startTime: number;
|
|
6
5
|
fallbackStatusCode?: number;
|
|
7
6
|
}): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Dream } from '@rvoh/dream';
|
|
2
2
|
import { OpenapiSchemaBody } from '@rvoh/dream/openapi';
|
|
3
3
|
import { DreamParamSafeAttributes, DreamParamSafeColumnNames, SerializerRendererOpts, StrictInterface } from '@rvoh/dream/types';
|
|
4
|
-
import
|
|
4
|
+
import Koa from 'koa';
|
|
5
5
|
import { ControllerHook } from '../controller/hooks.js';
|
|
6
6
|
import { HttpStatusCodeInt, HttpStatusSymbol } from '../error/http/status-codes.js';
|
|
7
7
|
import OpenapiEndpointRenderer from '../openapi-renderer/endpoint.js';
|
|
@@ -14,7 +14,7 @@ export type ControllerActionMetadata = Record<string, {
|
|
|
14
14
|
serializerKey?: string;
|
|
15
15
|
}>;
|
|
16
16
|
export type PsychicParamsPrimitive = string | number | boolean | null | undefined | PsychicParamsPrimitive[];
|
|
17
|
-
export declare const PsychicParamsPrimitiveLiterals: readonly ["bigint", "bigint[]", "boolean", "boolean[]", "date", "date[]", "datetime", "datetime[]", "integer", "integer[]", "json", "json[]", "null", "null[]", "number", "number[]", "string", "string[]", "uuid", "uuid[]"];
|
|
17
|
+
export declare const PsychicParamsPrimitiveLiterals: readonly ["bigint", "bigint[]", "boolean", "boolean[]", "date", "date[]", "datetime", "datetime[]", "integer", "integer[]", "json", "json[]", "null", "null[]", "number", "number[]", "string", "string[]", "time", "time[]", "timetz", "timetz[]", "uuid", "uuid[]"];
|
|
18
18
|
export type PsychicParamsPrimitiveLiteral = (typeof PsychicParamsPrimitiveLiterals)[number];
|
|
19
19
|
export interface PsychicParamsDictionary {
|
|
20
20
|
[key: string]: PsychicParamsPrimitive | PsychicParamsDictionary | PsychicParamsDictionary[];
|
|
@@ -101,13 +101,13 @@ export default class PsychicController {
|
|
|
101
101
|
* and non-controllers
|
|
102
102
|
*/
|
|
103
103
|
get isPsychicControllerInstance(): boolean;
|
|
104
|
-
|
|
105
|
-
res: Response;
|
|
104
|
+
ctx: Koa.Context;
|
|
106
105
|
session: Session;
|
|
107
106
|
action: string;
|
|
108
107
|
renderOpts: SerializerRendererOpts;
|
|
109
108
|
private startTime;
|
|
110
|
-
|
|
109
|
+
private _responseSent;
|
|
110
|
+
constructor(ctx: Koa.Context, { action, }: {
|
|
111
111
|
action: string;
|
|
112
112
|
});
|
|
113
113
|
/**
|
|
@@ -384,9 +384,20 @@ export default class PsychicController {
|
|
|
384
384
|
* @param data - the data to validate and render
|
|
385
385
|
*/
|
|
386
386
|
private validateAndRenderJsonResponse;
|
|
387
|
-
private
|
|
388
|
-
|
|
389
|
-
|
|
387
|
+
private koaSendJson;
|
|
388
|
+
/**
|
|
389
|
+
* @internal
|
|
390
|
+
*
|
|
391
|
+
* Attempts to retrieve or create a cached fast-json-stringify function
|
|
392
|
+
* for the current endpoint and status code. Returns undefined if no
|
|
393
|
+
* OpenAPI schema exists or if the controller's globalName is not set.
|
|
394
|
+
*
|
|
395
|
+
* @param statusCode - the HTTP status code
|
|
396
|
+
* @returns A stringify function, or undefined if no schema exists
|
|
397
|
+
*/
|
|
398
|
+
private getFastJsonStringifyFunction;
|
|
399
|
+
private koaSendStatus;
|
|
400
|
+
private koaRedirect;
|
|
390
401
|
private logIfDevelopment;
|
|
391
402
|
protected defaultSerializerPassthrough: SerializerResult;
|
|
392
403
|
/**
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export declare function launchDevServer(key: string, { port, cmd, timeout }?: LaunchDevServerOpts): Promise<void>;
|
|
1
|
+
export declare function launchDevServer(key: string, { port, cmd, timeout, onStdOut }?: LaunchDevServerOpts): Promise<void>;
|
|
2
2
|
export declare function stopDevServer(key: string): void;
|
|
3
3
|
export declare function stopDevServers(): void;
|
|
4
4
|
export interface LaunchDevServerOpts {
|
|
5
5
|
port?: number;
|
|
6
6
|
cmd?: string;
|
|
7
7
|
timeout?: number;
|
|
8
|
+
onStdOut?: (message: string) => void;
|
|
8
9
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { CustomCookieMaxAgeOptions } from '../psychic-app/index.js';
|
|
2
|
-
export default function cookieMaxAgeFromCookieOpts(cookieOpts
|
|
2
|
+
export default function cookieMaxAgeFromCookieOpts(cookieOpts?: CustomCookieMaxAgeOptions): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function toJson<T>(data: T
|
|
1
|
+
export default function toJson<T>(data: T): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ajv, type JSONSchemaType, type ValidateFunction } from 'ajv';
|
|
1
|
+
import { Ajv, type ErrorObject, type JSONSchemaType, type ValidateFunction } from 'ajv';
|
|
2
2
|
/**
|
|
3
3
|
* @internal
|
|
4
4
|
*
|
|
@@ -56,6 +56,10 @@ export declare function validateObject<T = unknown>(data: unknown, schema: JSONS
|
|
|
56
56
|
* @returns A validator function that can be reused for multiple validations
|
|
57
57
|
*/
|
|
58
58
|
export declare function createValidator<T = unknown>(schema: JSONSchemaType<T> | object, options?: ValidateOpenapiSchemaOptions): ValidateFunction<T>;
|
|
59
|
+
/**
|
|
60
|
+
* Formats AJV errors into a more readable format
|
|
61
|
+
*/
|
|
62
|
+
export declare function formatAjvErrors(ajvErrors: ErrorObject[]): ValidationError[];
|
|
59
63
|
export interface ValidationResult<T = unknown> {
|
|
60
64
|
isValid: boolean;
|
|
61
65
|
data?: T;
|
|
@@ -98,6 +98,27 @@ export default class OpenapiPayloadValidator {
|
|
|
98
98
|
* @param statusCode - the status code used to render
|
|
99
99
|
*/
|
|
100
100
|
validateOpenapiResponseBody(data: any, statusCode: number): void;
|
|
101
|
+
/**
|
|
102
|
+
* @internal
|
|
103
|
+
*
|
|
104
|
+
* Retrieves the OpenAPI response schema for a given status code.
|
|
105
|
+
* Returns undefined if no schema is defined for this endpoint/status code.
|
|
106
|
+
*
|
|
107
|
+
* @param statusCode - the HTTP status code
|
|
108
|
+
* @returns The response schema, or undefined if not found
|
|
109
|
+
*/
|
|
110
|
+
getResponseSchema(statusCode: number): object | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* @internal
|
|
113
|
+
*
|
|
114
|
+
* Retrieves the OpenAPI response schema for a given status code with
|
|
115
|
+
* all components merged in. This is the schema format needed by
|
|
116
|
+
* fast-json-stringify and AJV validators.
|
|
117
|
+
*
|
|
118
|
+
* @param statusCode - the HTTP status code
|
|
119
|
+
* @returns The response schema with components, or undefined if not found
|
|
120
|
+
*/
|
|
121
|
+
getResponseSchemaWithComponents(statusCode: number): object | undefined;
|
|
101
122
|
/**
|
|
102
123
|
* @internal
|
|
103
124
|
*
|
|
@@ -137,6 +158,26 @@ export default class OpenapiPayloadValidator {
|
|
|
137
158
|
* @param headers - the request headers
|
|
138
159
|
*/
|
|
139
160
|
private validateOrFail;
|
|
161
|
+
/**
|
|
162
|
+
* @internal
|
|
163
|
+
*
|
|
164
|
+
* Generates a cache key for the validator based on controller, action, openapiName, and target.
|
|
165
|
+
*
|
|
166
|
+
* @param target - the validation target (one of: 'requestBody', 'query', 'headers', 'responseBody')
|
|
167
|
+
* @returns cache key string
|
|
168
|
+
*/
|
|
169
|
+
private getCacheKey;
|
|
170
|
+
/**
|
|
171
|
+
* @internal
|
|
172
|
+
*
|
|
173
|
+
* Compiles a validator using AJV and caches it for future use.
|
|
174
|
+
*
|
|
175
|
+
* @param cacheKey - the cache key for this validator
|
|
176
|
+
* @param schema - the schema to compile
|
|
177
|
+
* @param options - AJV options
|
|
178
|
+
* @returns compiled validator function
|
|
179
|
+
*/
|
|
180
|
+
private compileAndCacheValidator;
|
|
140
181
|
/**
|
|
141
182
|
* @internal
|
|
142
183
|
*
|
|
@@ -5,7 +5,7 @@ export interface VirtualAttributeStatement {
|
|
|
5
5
|
type: OpenapiShorthandPrimitiveTypes | OpenapiSchemaBodyShorthand | undefined;
|
|
6
6
|
}
|
|
7
7
|
type DreamClassColumnNames<DreamClass extends typeof Dream, DreamInstance extends InstanceType<DreamClass> = InstanceType<DreamClass>, DB = DreamInstance['DB'], TableName extends keyof DB = DreamInstance['table'] & keyof DB, Table extends DB[keyof DB] = DB[TableName]> = keyof Table & string;
|
|
8
|
-
export declare function dreamColumnOpenapiShape<DreamClass extends typeof Dream>(dreamClass: DreamClass, column: DreamClassColumnNames<DreamClass>, openapi?: OpenapiDescription | OpenapiSchemaBodyShorthand | OpenapiShorthandPrimitiveTypes | undefined, { suppressResponseEnums, allowGenericJson, }?: {
|
|
8
|
+
export declare function dreamColumnOpenapiShape<DreamClass extends typeof Dream>(source: string, dreamClass: DreamClass, column: DreamClassColumnNames<DreamClass>, openapi?: OpenapiDescription | OpenapiSchemaBodyShorthand | OpenapiShorthandPrimitiveTypes | undefined, { suppressResponseEnums, allowGenericJson, }?: {
|
|
9
9
|
suppressResponseEnums?: boolean;
|
|
10
10
|
allowGenericJson?: boolean;
|
|
11
11
|
}): OpenapiSchemaBody;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
*
|
|
4
|
+
* Retrieves a cached stringify function if it exists.
|
|
5
|
+
*
|
|
6
|
+
* @param cacheKey - The cache key identifying the stringify function
|
|
7
|
+
* @returns The cached stringify function, or undefined if not found
|
|
8
|
+
*/
|
|
9
|
+
export declare function getCachedStringify(cacheKey: string): ((data: any) => string) | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* @internal
|
|
12
|
+
*
|
|
13
|
+
* Stores a compiled stringify function in the cache.
|
|
14
|
+
*
|
|
15
|
+
* @param cacheKey - The cache key identifying the stringify function
|
|
16
|
+
* @param stringifyFn - The compiled fast-json-stringify function to cache
|
|
17
|
+
*/
|
|
18
|
+
export declare function cacheStringify(cacheKey: string, stringifyFn: (data: any) => string): void;
|
|
19
|
+
/**
|
|
20
|
+
* @internal
|
|
21
|
+
*
|
|
22
|
+
* Clears a specific stringify function from the cache.
|
|
23
|
+
* Used in test environments to ensure test isolation.
|
|
24
|
+
*
|
|
25
|
+
* @param cacheKey - The cache key identifying the stringify function to clear
|
|
26
|
+
*/
|
|
27
|
+
export declare function _testOnlyClearStringify(cacheKey: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* @internal
|
|
30
|
+
*
|
|
31
|
+
* Clears all stringify functions from the cache.
|
|
32
|
+
* Used in test environments to ensure test isolation.
|
|
33
|
+
*/
|
|
34
|
+
export declare function _testOnlyClearStringifyCache(): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ValidateFunction } from 'ajv';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* Retrieves a cached validator function if it exists.
|
|
6
|
+
*
|
|
7
|
+
* @param cacheKey - The cache key identifying the validator
|
|
8
|
+
* @returns The cached validator function, or undefined if not found
|
|
9
|
+
*/
|
|
10
|
+
export declare function getCachedValidator(cacheKey: string): ValidateFunction | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* @internal
|
|
13
|
+
*
|
|
14
|
+
* Stores a compiled validator function in the cache.
|
|
15
|
+
*
|
|
16
|
+
* @param cacheKey - The cache key identifying the validator
|
|
17
|
+
* @param validator - The compiled AJV validator function to cache
|
|
18
|
+
*/
|
|
19
|
+
export declare function cacheValidator(cacheKey: string, validator: ValidateFunction): void;
|
|
20
|
+
/**
|
|
21
|
+
* @internal
|
|
22
|
+
*
|
|
23
|
+
* Clears a specific validator from the cache.
|
|
24
|
+
* Used in test environments to ensure test isolation.
|
|
25
|
+
*
|
|
26
|
+
* @param cacheKey - The cache key identifying the validator to clear
|
|
27
|
+
*/
|
|
28
|
+
export declare function _testOnlyClearValidator(cacheKey: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* @internal
|
|
31
|
+
*
|
|
32
|
+
* Clears all validators from the cache.
|
|
33
|
+
* Used in test environments to ensure test isolation.
|
|
34
|
+
*/
|
|
35
|
+
export declare function _testOnlyClearValidatorCache(): void;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import cors from '@koa/cors';
|
|
1
2
|
import { DreamApp } from '@rvoh/dream';
|
|
2
3
|
import { OpenapiSchemaBody } from '@rvoh/dream/openapi';
|
|
3
4
|
import { DreamAppAllowedPackageManagersEnum } from '@rvoh/dream/system';
|
|
4
5
|
import { DreamAppInitOptions, DreamLogLevel, DreamLogger, EncryptOptions } from '@rvoh/dream/types';
|
|
5
|
-
import * as bodyParser from 'body-parser';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import Koa from 'koa';
|
|
8
|
+
import bodyParser from 'koa-bodyparser';
|
|
9
9
|
import * as http from 'node:http';
|
|
10
10
|
import * as https from 'node:https';
|
|
11
11
|
import { OpenapiValidateTarget } from '../openapi-renderer/defaults.js';
|
|
@@ -35,7 +35,7 @@ export default class PsychicApp {
|
|
|
35
35
|
/**
|
|
36
36
|
* @internal
|
|
37
37
|
*/
|
|
38
|
-
static getPsychicHttpInstance(app:
|
|
38
|
+
static getPsychicHttpInstance(app: Koa, sslCredentials: PsychicSslCredentials | undefined): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse> | https.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
39
39
|
/**
|
|
40
40
|
* Builds the routes cache if it does not already
|
|
41
41
|
* exist. This is called during PsychicApp.init,
|
|
@@ -143,7 +143,7 @@ export default class PsychicApp {
|
|
|
143
143
|
private _httpServerOptions;
|
|
144
144
|
get httpServerOptions(): http.ServerOptions<typeof http.IncomingMessage, typeof http.ServerResponse> | https.ServerOptions<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
145
145
|
private _corsOptions;
|
|
146
|
-
get corsOptions():
|
|
146
|
+
get corsOptions(): cors.Options;
|
|
147
147
|
private _jsonOptions;
|
|
148
148
|
get jsonOptions(): bodyParser.Options;
|
|
149
149
|
private _cookieOptions;
|
|
@@ -156,8 +156,6 @@ export default class PsychicApp {
|
|
|
156
156
|
get sslCredentials(): PsychicSslCredentials | undefined;
|
|
157
157
|
private _saltRounds;
|
|
158
158
|
get saltRounds(): number | undefined;
|
|
159
|
-
private _sanitizeResponseJson;
|
|
160
|
-
get sanitizeResponseJson(): boolean;
|
|
161
159
|
private _packageManager;
|
|
162
160
|
get packageManager(): "pnpm" | "yarn" | "npm";
|
|
163
161
|
private _importExtension;
|
|
@@ -219,16 +217,15 @@ export default class PsychicApp {
|
|
|
219
217
|
load<RT extends 'controllers' | 'services' | 'initializers'>(resourceType: RT, resourcePath: string, importCb: (path: string) => Promise<any>): Promise<void>;
|
|
220
218
|
private booted;
|
|
221
219
|
boot(force?: boolean): Promise<void>;
|
|
222
|
-
use(on: PsychicUseEventType, handler:
|
|
223
|
-
use(handler:
|
|
224
|
-
use(handler: () => void): void;
|
|
220
|
+
use(on: PsychicUseEventType, handler: Koa.Middleware): void;
|
|
221
|
+
use(handler: Koa.Middleware): void;
|
|
225
222
|
plugin(cb: (app: PsychicApp) => void | Promise<void>): void;
|
|
226
|
-
on<T extends PsychicHookEventType>(hookEventType: T, cb: T extends 'server:error' ? (err: Error,
|
|
223
|
+
on<T extends PsychicHookEventType>(hookEventType: T, cb: T extends 'server:error' ? (err: Error, ctx: Koa.Context) => void | Promise<void> : T extends 'server:init:before-middleware' ? (psychicServer: PsychicServer) => void | Promise<void> : T extends 'server:init:after-middleware' ? (psychicServer: PsychicServer) => void | Promise<void> : T extends 'server:start' ? (psychicServer: PsychicServer) => void | Promise<void> : T extends 'server:shutdown' ? (psychicServer: PsychicServer) => void | Promise<void> : T extends 'server:init:after-routes' ? (psychicServer: PsychicServer) => void | Promise<void> : T extends 'cli:start' ? (program: Command) => void | Promise<void> : T extends 'cli:sync' ? () => any : (conf: PsychicApp) => void | Promise<void>): void;
|
|
227
224
|
set(option: 'openapi', name: string, value: NamedPsychicOpenapiOptions): void;
|
|
228
|
-
set<Opt extends PsychicAppOption>(option: Opt, value: Opt extends 'appName' ? string : Opt extends 'apiOnly' ? boolean : Opt extends 'defaultResponseHeaders' ? Record<string, string | null> : Opt extends 'httpServerOptions' ? http.ServerOptions | https.ServerOptions : Opt extends 'encryption' ? PsychicAppEncryptionOptions : Opt extends 'cors' ?
|
|
225
|
+
set<Opt extends PsychicAppOption>(option: Opt, value: Opt extends 'appName' ? string : Opt extends 'apiOnly' ? boolean : Opt extends 'defaultResponseHeaders' ? Record<string, string | null> : Opt extends 'httpServerOptions' ? http.ServerOptions | https.ServerOptions : Opt extends 'encryption' ? PsychicAppEncryptionOptions : Opt extends 'cors' ? cors.Options : Opt extends 'cookie' ? CustomCookieOptions : Opt extends 'apiRoot' ? string : Opt extends 'importExtension' ? GeneratorImportStyle : Opt extends 'sessionCookieName' ? string : Opt extends 'json' ? bodyParser.Options : Opt extends 'logger' ? PsychicLogger : Opt extends 'ssl' ? PsychicSslCredentials : Opt extends 'openapi' ? DefaultPsychicOpenapiOptions : Opt extends 'paths' ? PsychicPathOptions : Opt extends 'port' ? number : Opt extends 'saltRounds' ? number : Opt extends 'packageManager' ? DreamAppAllowedPackageManagersEnum : Opt extends 'inflections' ? () => void | Promise<void> : Opt extends 'routes' ? (r: PsychicRouter) => void | Promise<void> : never): void;
|
|
229
226
|
override<Override extends keyof PsychicAppOverrides>(override: Override, value: PsychicAppOverrides[Override]): void;
|
|
230
227
|
}
|
|
231
|
-
export type PsychicAppOption = 'appName' | 'apiOnly' | 'apiRoot' | 'httpServerOptions' | 'importExtension' | 'encryption' | 'sessionCookieName' | 'cookie' | 'cors' | 'defaultResponseHeaders' | 'inflections' | 'json' | 'logger' | 'openapi' | 'packageManager' | 'paths' | 'port' | 'routes' | 'saltRounds' | '
|
|
228
|
+
export type PsychicAppOption = 'appName' | 'apiOnly' | 'apiRoot' | 'httpServerOptions' | 'importExtension' | 'encryption' | 'sessionCookieName' | 'cookie' | 'cors' | 'defaultResponseHeaders' | 'inflections' | 'json' | 'logger' | 'openapi' | 'packageManager' | 'paths' | 'port' | 'routes' | 'saltRounds' | 'ssl';
|
|
232
229
|
export interface PsychicAppSpecialHooks {
|
|
233
230
|
cliSync: (() => any)[];
|
|
234
231
|
serverInitBeforeMiddleware: ((server: PsychicServer) => void | Promise<void>)[];
|
|
@@ -236,7 +233,7 @@ export interface PsychicAppSpecialHooks {
|
|
|
236
233
|
serverInitAfterRoutes: ((server: PsychicServer) => void | Promise<void>)[];
|
|
237
234
|
serverStart: ((server: PsychicServer) => void | Promise<void>)[];
|
|
238
235
|
serverShutdown: ((server: PsychicServer) => void | Promise<void>)[];
|
|
239
|
-
serverError: ((err: Error,
|
|
236
|
+
serverError: ((err: Error, ctx: Koa.Context) => void | Promise<void>)[];
|
|
240
237
|
}
|
|
241
238
|
export interface PsychicAppOverrides {
|
|
242
239
|
['server:start']: ((psychicServer: PsychicServer, opts: PsychicServerStartProviderOptions) => http.Server | Promise<http.Server>) | null;
|