@rvoh/psychic 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/cjs/src/bin/helpers/printRoutes.js +1 -1
- package/dist/cjs/src/controller/decorators.js +3 -1
- package/dist/cjs/src/generate/helpers/reduxBindings/printFinalStepsMessage.js +6 -2
- package/dist/cjs/src/generate/helpers/reduxBindings/writeInitializer.js +2 -2
- package/dist/cjs/src/generate/helpers/syncOpenapiTypescript/generateInitializer.js +1 -1
- package/dist/cjs/src/router/helpers.js +10 -0
- package/dist/cjs/src/router/index.js +13 -0
- package/dist/esm/src/bin/helpers/printRoutes.js +1 -1
- package/dist/esm/src/controller/decorators.js +3 -1
- package/dist/esm/src/generate/helpers/reduxBindings/printFinalStepsMessage.js +6 -2
- package/dist/esm/src/generate/helpers/reduxBindings/writeInitializer.js +2 -2
- package/dist/esm/src/generate/helpers/syncOpenapiTypescript/generateInitializer.js +1 -1
- package/dist/esm/src/router/helpers.js +9 -0
- package/dist/esm/src/router/index.js +14 -1
- package/dist/types/src/router/helpers.d.ts +5 -0
- package/dist/types/src/router/index.d.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -31,7 +31,7 @@ function buildExpressions(routes) {
|
|
|
31
31
|
const beginningOfExpression = `${route.httpMethod.toUpperCase()}${' '.repeat(numMethodSpaces)}${formattedPath}`;
|
|
32
32
|
const controllerRouteConf = route;
|
|
33
33
|
const endOfExpression = controllerRouteConf.controller
|
|
34
|
-
? controllerRouteConf.controller.controllerActionPath(
|
|
34
|
+
? controllerRouteConf.controller.controllerActionPath(controllerRouteConf.action)
|
|
35
35
|
: 'middleware';
|
|
36
36
|
return [beginningOfExpression, endOfExpression];
|
|
37
37
|
});
|
|
@@ -18,7 +18,9 @@ function BeforeAction(opts = {}) {
|
|
|
18
18
|
}
|
|
19
19
|
if (!Object.getOwnPropertyDescriptor(psychicControllerClass, 'controllerHooks'))
|
|
20
20
|
psychicControllerClass.controllerHooks = [...psychicControllerClass.controllerHooks];
|
|
21
|
-
psychicControllerClass.controllerHooks.
|
|
21
|
+
if (!psychicControllerClass.controllerHooks.find(hook => hook.methodName === methodName)) {
|
|
22
|
+
psychicControllerClass.controllerHooks.push(new hooks_js_1.ControllerHook(psychicControllerClass.name, methodName.toString(), opts));
|
|
23
|
+
}
|
|
22
24
|
});
|
|
23
25
|
};
|
|
24
26
|
}
|
|
@@ -7,7 +7,7 @@ exports.default = printFinalStepsMessage;
|
|
|
7
7
|
const dream_1 = require("@rvoh/dream");
|
|
8
8
|
const colorize_js_1 = __importDefault(require("../../../cli/helpers/colorize.js"));
|
|
9
9
|
function printFinalStepsMessage(opts) {
|
|
10
|
-
const importLine = (0, colorize_js_1.default)(`+ import { ${opts.exportName} } from '${opts.
|
|
10
|
+
const importLine = (0, colorize_js_1.default)(`+ import { ${opts.exportName} } from '${opts.outputFile}'`, { color: 'green' });
|
|
11
11
|
const reducerLine = (0, colorize_js_1.default)(` + [${opts.exportName}.reducerPath]: ${opts.exportName}.reducer,`, {
|
|
12
12
|
color: 'green',
|
|
13
13
|
});
|
|
@@ -17,7 +17,11 @@ function printFinalStepsMessage(opts) {
|
|
|
17
17
|
dream_1.DreamCLI.logger.log(`
|
|
18
18
|
Finished generating redux bindings for your application,
|
|
19
19
|
but to wire them into your app, we're going to need your help.
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
First, you will need to be sure to sync, so that the new openapi
|
|
22
|
+
types are sent over to your client application.
|
|
23
|
+
|
|
24
|
+
Next, be sure to add the following to your root store to bind your new
|
|
21
25
|
api module into your redux app, i.e.
|
|
22
26
|
|
|
23
27
|
// add this line, fix the import path if necessary
|
|
@@ -54,10 +54,10 @@ async function writeInitializer({ exportName }) {
|
|
|
54
54
|
const contents = `\
|
|
55
55
|
import { DreamCLI } from '@rvoh/dream'
|
|
56
56
|
import { PsychicApp } from '@rvoh/psychic'
|
|
57
|
-
import AppEnv from '
|
|
57
|
+
import AppEnv from '../../AppEnv.js'
|
|
58
58
|
|
|
59
59
|
export default function initialize${pascalized}(psy: PsychicApp) {
|
|
60
|
-
psy.on('sync', async () => {
|
|
60
|
+
psy.on('cli:sync', async () => {
|
|
61
61
|
if (AppEnv.isDevelopmentOrTest) {
|
|
62
62
|
DreamCLI.logger.logStartProgress(\`[${camelized}] syncing...\`)
|
|
63
63
|
await DreamCLI.spawn('npx @rtk-query/codegen-openapi ${filePath}', {
|
|
@@ -50,7 +50,7 @@ import { PsychicApp } from "@rvoh/psychic"
|
|
|
50
50
|
import AppEnv from '../AppEnv.js'
|
|
51
51
|
|
|
52
52
|
export default (psy: PsychicApp) => {
|
|
53
|
-
psy.on('sync', async () => {
|
|
53
|
+
psy.on('cli:sync', async () => {
|
|
54
54
|
if (AppEnv.isDevelopmentOrTest) {
|
|
55
55
|
DreamCLI.logger.logStartProgress(\`[${hyphenized}] extracting types from ${openapiFilepath} to ${outfile}...\`)
|
|
56
56
|
await DreamCLI.spawn('npx openapi-typescript ${openapiFilepath} -o ${outfile}')
|
|
@@ -12,6 +12,7 @@ exports.namespacedControllerActionString = namespacedControllerActionString;
|
|
|
12
12
|
exports.lookupControllerOrFail = lookupControllerOrFail;
|
|
13
13
|
exports.applyResourcesAction = applyResourcesAction;
|
|
14
14
|
exports.applyResourceAction = applyResourceAction;
|
|
15
|
+
exports.convertRouteParams = convertRouteParams;
|
|
15
16
|
const dream_1 = require("@rvoh/dream");
|
|
16
17
|
const cannot_find_inferred_controller_from_provided_namespace_js_1 = __importDefault(require("../error/router/cannot-find-inferred-controller-from-provided-namespace.js"));
|
|
17
18
|
const cannot_infer_controller_from_top_level_route_js_1 = __importDefault(require("../error/router/cannot-infer-controller-from-top-level-route.js"));
|
|
@@ -123,6 +124,15 @@ function applyResourceAction(path, action, routingMechanism, options) {
|
|
|
123
124
|
throw new Error(`unsupported resource method type: ${action}`);
|
|
124
125
|
}
|
|
125
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Converts OpenAPI-style route parameters to Express.js-style parameters
|
|
129
|
+
* Example: users/{userId}/abc/{id} -> users/:userId/abc/:id
|
|
130
|
+
*/
|
|
131
|
+
function convertRouteParams(path) {
|
|
132
|
+
// Use a more specific regex to avoid polynomial time complexity
|
|
133
|
+
// Only match valid parameter names (alphanumeric + underscore, 1-50 chars)
|
|
134
|
+
return path.replace(/\{([a-zA-Z_][a-zA-Z0-9_]{0,49})\}/g, ':$1');
|
|
135
|
+
}
|
|
126
136
|
function httpMethodFromResourcefulAction(action) {
|
|
127
137
|
switch (action) {
|
|
128
138
|
case 'index':
|
|
@@ -74,6 +74,7 @@ class PsychicRouter {
|
|
|
74
74
|
return '/' + this.currentNamespacePaths.join('/') + '/' + str;
|
|
75
75
|
}
|
|
76
76
|
crud(httpMethod, path, controllerOrMiddleware, action) {
|
|
77
|
+
this.checkPathForInvalidChars(path);
|
|
77
78
|
const isMiddleware = typeof controllerOrMiddleware === 'function' &&
|
|
78
79
|
!controllerOrMiddleware?.isPsychicController;
|
|
79
80
|
// devs can provide custom express middleware which bypasses
|
|
@@ -98,6 +99,18 @@ class PsychicRouter {
|
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
}
|
|
102
|
+
checkPathForInvalidChars(path) {
|
|
103
|
+
if (path.includes('{'))
|
|
104
|
+
throw new Error(`
|
|
105
|
+
The provided route "${path}" contains characters that are not supported.
|
|
106
|
+
If you are trying to write a uri param, you will need to use expressjs
|
|
107
|
+
param syntax, which is a prefixing colon, rather than using brackets
|
|
108
|
+
to surround the param.
|
|
109
|
+
|
|
110
|
+
provided route: "${path}"
|
|
111
|
+
suggested fix: "${(0, helpers_js_1.convertRouteParams)(path)}"
|
|
112
|
+
`);
|
|
113
|
+
}
|
|
101
114
|
namespace(namespace, cb) {
|
|
102
115
|
const nestedRouter = new PsychicNestedRouter(this.app, this.config, this.routeManager, {
|
|
103
116
|
namespaces: this.currentNamespaces,
|
|
@@ -25,7 +25,7 @@ function buildExpressions(routes) {
|
|
|
25
25
|
const beginningOfExpression = `${route.httpMethod.toUpperCase()}${' '.repeat(numMethodSpaces)}${formattedPath}`;
|
|
26
26
|
const controllerRouteConf = route;
|
|
27
27
|
const endOfExpression = controllerRouteConf.controller
|
|
28
|
-
? controllerRouteConf.controller.controllerActionPath(
|
|
28
|
+
? controllerRouteConf.controller.controllerActionPath(controllerRouteConf.action)
|
|
29
29
|
: 'middleware';
|
|
30
30
|
return [beginningOfExpression, endOfExpression];
|
|
31
31
|
});
|
|
@@ -11,7 +11,9 @@ export function BeforeAction(opts = {}) {
|
|
|
11
11
|
}
|
|
12
12
|
if (!Object.getOwnPropertyDescriptor(psychicControllerClass, 'controllerHooks'))
|
|
13
13
|
psychicControllerClass.controllerHooks = [...psychicControllerClass.controllerHooks];
|
|
14
|
-
psychicControllerClass.controllerHooks.
|
|
14
|
+
if (!psychicControllerClass.controllerHooks.find(hook => hook.methodName === methodName)) {
|
|
15
|
+
psychicControllerClass.controllerHooks.push(new ControllerHook(psychicControllerClass.name, methodName.toString(), opts));
|
|
16
|
+
}
|
|
15
17
|
});
|
|
16
18
|
};
|
|
17
19
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DreamCLI } from '@rvoh/dream';
|
|
2
2
|
import colorize from '../../../cli/helpers/colorize.js';
|
|
3
3
|
export default function printFinalStepsMessage(opts) {
|
|
4
|
-
const importLine = colorize(`+ import { ${opts.exportName} } from '${opts.
|
|
4
|
+
const importLine = colorize(`+ import { ${opts.exportName} } from '${opts.outputFile}'`, { color: 'green' });
|
|
5
5
|
const reducerLine = colorize(` + [${opts.exportName}.reducerPath]: ${opts.exportName}.reducer,`, {
|
|
6
6
|
color: 'green',
|
|
7
7
|
});
|
|
@@ -11,7 +11,11 @@ export default function printFinalStepsMessage(opts) {
|
|
|
11
11
|
DreamCLI.logger.log(`
|
|
12
12
|
Finished generating redux bindings for your application,
|
|
13
13
|
but to wire them into your app, we're going to need your help.
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
First, you will need to be sure to sync, so that the new openapi
|
|
16
|
+
types are sent over to your client application.
|
|
17
|
+
|
|
18
|
+
Next, be sure to add the following to your root store to bind your new
|
|
15
19
|
api module into your redux app, i.e.
|
|
16
20
|
|
|
17
21
|
// add this line, fix the import path if necessary
|
|
@@ -25,10 +25,10 @@ export default async function writeInitializer({ exportName }) {
|
|
|
25
25
|
const contents = `\
|
|
26
26
|
import { DreamCLI } from '@rvoh/dream'
|
|
27
27
|
import { PsychicApp } from '@rvoh/psychic'
|
|
28
|
-
import AppEnv from '
|
|
28
|
+
import AppEnv from '../../AppEnv.js'
|
|
29
29
|
|
|
30
30
|
export default function initialize${pascalized}(psy: PsychicApp) {
|
|
31
|
-
psy.on('sync', async () => {
|
|
31
|
+
psy.on('cli:sync', async () => {
|
|
32
32
|
if (AppEnv.isDevelopmentOrTest) {
|
|
33
33
|
DreamCLI.logger.logStartProgress(\`[${camelized}] syncing...\`)
|
|
34
34
|
await DreamCLI.spawn('npx @rtk-query/codegen-openapi ${filePath}', {
|
|
@@ -21,7 +21,7 @@ import { PsychicApp } from "@rvoh/psychic"
|
|
|
21
21
|
import AppEnv from '../AppEnv.js'
|
|
22
22
|
|
|
23
23
|
export default (psy: PsychicApp) => {
|
|
24
|
-
psy.on('sync', async () => {
|
|
24
|
+
psy.on('cli:sync', async () => {
|
|
25
25
|
if (AppEnv.isDevelopmentOrTest) {
|
|
26
26
|
DreamCLI.logger.logStartProgress(\`[${hyphenized}] extracting types from ${openapiFilepath} to ${outfile}...\`)
|
|
27
27
|
await DreamCLI.spawn('npx openapi-typescript ${openapiFilepath} -o ${outfile}')
|
|
@@ -109,6 +109,15 @@ export function applyResourceAction(path, action, routingMechanism, options) {
|
|
|
109
109
|
throw new Error(`unsupported resource method type: ${action}`);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Converts OpenAPI-style route parameters to Express.js-style parameters
|
|
114
|
+
* Example: users/{userId}/abc/{id} -> users/:userId/abc/:id
|
|
115
|
+
*/
|
|
116
|
+
export function convertRouteParams(path) {
|
|
117
|
+
// Use a more specific regex to avoid polynomial time complexity
|
|
118
|
+
// Only match valid parameter names (alphanumeric + underscore, 1-50 chars)
|
|
119
|
+
return path.replace(/\{([a-zA-Z_][a-zA-Z0-9_]{0,49})\}/g, ':$1');
|
|
120
|
+
}
|
|
112
121
|
function httpMethodFromResourcefulAction(action) {
|
|
113
122
|
switch (action) {
|
|
114
123
|
case 'index':
|
|
@@ -6,7 +6,7 @@ import ParamValidationErrors from '../error/controller/ParamValidationErrors.js'
|
|
|
6
6
|
import EnvInternal from '../helpers/EnvInternal.js';
|
|
7
7
|
import errorIsRescuableHttpError from '../helpers/error/errorIsRescuableHttpError.js';
|
|
8
8
|
import PsychicApp from '../psychic-app/index.js';
|
|
9
|
-
import { applyResourceAction, applyResourcesAction, lookupControllerOrFail, routePath, } from '../router/helpers.js';
|
|
9
|
+
import { applyResourceAction, applyResourcesAction, convertRouteParams, lookupControllerOrFail, routePath, } from '../router/helpers.js';
|
|
10
10
|
import RouteManager from './route-manager.js';
|
|
11
11
|
import { ResourceMethods, ResourcesMethods, } from './types.js';
|
|
12
12
|
export default class PsychicRouter {
|
|
@@ -68,6 +68,7 @@ export default class PsychicRouter {
|
|
|
68
68
|
return '/' + this.currentNamespacePaths.join('/') + '/' + str;
|
|
69
69
|
}
|
|
70
70
|
crud(httpMethod, path, controllerOrMiddleware, action) {
|
|
71
|
+
this.checkPathForInvalidChars(path);
|
|
71
72
|
const isMiddleware = typeof controllerOrMiddleware === 'function' &&
|
|
72
73
|
!controllerOrMiddleware?.isPsychicController;
|
|
73
74
|
// devs can provide custom express middleware which bypasses
|
|
@@ -92,6 +93,18 @@ export default class PsychicRouter {
|
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
95
|
}
|
|
96
|
+
checkPathForInvalidChars(path) {
|
|
97
|
+
if (path.includes('{'))
|
|
98
|
+
throw new Error(`
|
|
99
|
+
The provided route "${path}" contains characters that are not supported.
|
|
100
|
+
If you are trying to write a uri param, you will need to use expressjs
|
|
101
|
+
param syntax, which is a prefixing colon, rather than using brackets
|
|
102
|
+
to surround the param.
|
|
103
|
+
|
|
104
|
+
provided route: "${path}"
|
|
105
|
+
suggested fix: "${convertRouteParams(path)}"
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
95
108
|
namespace(namespace, cb) {
|
|
96
109
|
const nestedRouter = new PsychicNestedRouter(this.app, this.config, this.routeManager, {
|
|
97
110
|
namespaces: this.currentNamespaces,
|
|
@@ -23,4 +23,9 @@ export interface NamespaceConfig {
|
|
|
23
23
|
}
|
|
24
24
|
export declare function applyResourcesAction(path: string, action: ResourcesMethodType, routingMechanism: RoutingMechanism, options?: ResourcesOptions): void;
|
|
25
25
|
export declare function applyResourceAction(path: string, action: ResourcesMethodType, routingMechanism: PsychicRouter | PsychicNestedRouter, options?: ResourcesOptions): void;
|
|
26
|
+
/**
|
|
27
|
+
* Converts OpenAPI-style route parameters to Express.js-style parameters
|
|
28
|
+
* Example: users/{userId}/abc/{id} -> users/:userId/abc/:id
|
|
29
|
+
*/
|
|
30
|
+
export declare function convertRouteParams(path: string): string;
|
|
26
31
|
export {};
|
|
@@ -36,6 +36,7 @@ export default class PsychicRouter {
|
|
|
36
36
|
crud(httpMethod: HttpMethod, path: string): void;
|
|
37
37
|
crud(httpMethod: HttpMethod, path: string, middleware: RequestHandler): void;
|
|
38
38
|
crud(httpMethod: HttpMethod, path: string, controller: typeof PsychicController, action: string): void;
|
|
39
|
+
private checkPathForInvalidChars;
|
|
39
40
|
namespace(namespace: string, cb: (router: PsychicNestedRouter) => void): void;
|
|
40
41
|
scope(scope: string, cb: (router: PsychicNestedRouter) => void): void;
|
|
41
42
|
resources(path: string, optionsOrCb?: ResourcesOptions | ((router: PsychicNestedRouter) => void), cb?: (router: PsychicNestedRouter) => void): void;
|