@rvoh/psychic 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/cjs/src/bin/index.js +14 -0
- package/dist/cjs/src/cli/index.js +8 -0
- package/dist/cjs/src/error/psychic-application/init-missing-package-manager.js +20 -0
- package/dist/cjs/src/helpers/path/psychicPath.js +2 -0
- package/dist/cjs/src/psychic-application/helpers/PsychicImporter.js +9 -0
- package/dist/cjs/src/psychic-application/helpers/globalServiceKeyFromPath.js +11 -0
- package/dist/cjs/src/psychic-application/helpers/import/importServices.js +43 -0
- package/dist/cjs/src/psychic-application/helpers/lookupClassByGlobalName.js +3 -0
- package/dist/cjs/src/psychic-application/index.js +72 -2
- package/dist/esm/src/bin/index.js +14 -0
- package/dist/esm/src/cli/index.js +8 -0
- package/dist/esm/src/error/psychic-application/init-missing-package-manager.js +17 -0
- package/dist/esm/src/helpers/path/psychicPath.js +2 -0
- package/dist/esm/src/psychic-application/helpers/PsychicImporter.js +9 -0
- package/dist/esm/src/psychic-application/helpers/globalServiceKeyFromPath.js +8 -0
- package/dist/esm/src/psychic-application/helpers/import/importServices.js +37 -0
- package/dist/esm/src/psychic-application/helpers/lookupClassByGlobalName.js +3 -0
- package/dist/esm/src/psychic-application/index.js +71 -2
- package/dist/types/src/bin/index.d.ts +1 -0
- package/dist/types/src/error/psychic-application/init-missing-package-manager.d.ts +4 -0
- package/dist/types/src/helpers/path/psychicPath.d.ts +1 -1
- package/dist/types/src/psychic-application/helpers/PsychicImporter.d.ts +1 -0
- package/dist/types/src/psychic-application/helpers/globalServiceKeyFromPath.d.ts +1 -0
- package/dist/types/src/psychic-application/helpers/import/importServices.d.ts +4 -0
- package/dist/types/src/psychic-application/index.d.ts +30 -5
- package/package.json +6 -7
- package/dist/cjs/src/helpers/sspawn.js +0 -26
- package/dist/esm/src/helpers/sspawn.js +0 -22
- package/dist/types/src/helpers/sspawn.d.ts +0 -2
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
> ATTENTION: we are currently in the process of releasing this code to the world, as of the afternoon of March 10th, 2025. This notice will be removed, and the version of this repo will be bumped to 1.0.0, once all of the repos have been migrated to the new spaces and we can verify that it is all working. This is anticipated to
|
|
1
|
+
> ATTENTION: we are currently in the process of releasing this code to the world, as of the afternoon of March 10th, 2025. This notice will be removed, and the version of this repo will be bumped to 1.0.0, once all of the repos have been migrated to the new spaces and we can verify that it is all working. This is anticipated to be done in early April, 2025.
|
|
2
2
|
|
|
3
3
|
# Psychic
|
|
4
4
|
|
|
5
|
-
Psychic is a typescript first Node framework built on top of [kysely](http://kysely.dev) and heavily inspired by Ruby on Rails. It provides a light-weight routing layer around [expressjs](https://expressjs.com) to create a familiar MVC pattern for those coming from a conventional MVC framework,
|
|
5
|
+
Psychic is a typescript first Node framework built on top of [kysely](http://kysely.dev) and heavily inspired by Ruby on Rails. It provides a light-weight routing layer around [expressjs](https://expressjs.com) to create a familiar MVC pattern for those coming from a conventional MVC framework, and...
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Introducing [dream](https://psychicframework.com/docs/models/overview)!, a robust, type-safe ORM with an incredibly powerful autocomplete API, through associations, polymorphism, single table inheritence, deep OpenAPI integration, and much more.
|
|
8
|
+
|
|
9
|
+
In addition, psychic also provides elegant redis and socket.io bindings for distributed websocket applications, a powerful background job system integrated with bullmq to enable you to easily send any of your application code into redis for background processing, the ability to couple with a front-end framework (like nextjs, react, angular etc...) and write specs that drive through your front end, while still maintaining a back end-centric context from which to compose, and much more!
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
For more comprehensive documentation, please see [The official Psychic guides](https://psychicframework.com). We will also be publishing api docs shortly, but the Psychic guides should be comprehensive enough to provide an understanding of the technology and how to use it.
|
|
10
12
|
|
|
11
13
|
## Questions?
|
|
12
14
|
|
|
@@ -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.')
|
|
@@ -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;
|
|
@@ -12,6 +12,8 @@ function default_1(dreamPathType) {
|
|
|
12
12
|
return psychicApp.paths.controllers;
|
|
13
13
|
case 'controllerSpecs':
|
|
14
14
|
return psychicApp.paths.controllerSpecs;
|
|
15
|
+
case 'services':
|
|
16
|
+
return psychicApp.paths.services;
|
|
15
17
|
default:
|
|
16
18
|
return (0, dream_1.dreamPath)(dreamPathType);
|
|
17
19
|
}
|
|
@@ -8,5 +8,14 @@ class PsychicImporter {
|
|
|
8
8
|
const controllerClasses = (await Promise.all(controllerPaths.map(controllerPath => importCb(controllerPath).then(dreamClass => [controllerPath, dreamClass]))));
|
|
9
9
|
return controllerClasses;
|
|
10
10
|
}
|
|
11
|
+
static async importServices(pathToServices,
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
importCb) {
|
|
14
|
+
const servicePaths = await dream_1.DreamImporter.ls(pathToServices);
|
|
15
|
+
const serviceClasses = (await Promise.all(servicePaths.map(servicePath =>
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
17
|
+
importCb(servicePath).then(serviceClass => [servicePath, serviceClass]))));
|
|
18
|
+
return serviceClasses;
|
|
19
|
+
}
|
|
11
20
|
}
|
|
12
21
|
exports.default = PsychicImporter;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = default_1;
|
|
4
|
+
function default_1(filepath, dirPath) {
|
|
5
|
+
const prefixPath = dirPath;
|
|
6
|
+
return ('services/' +
|
|
7
|
+
filepath
|
|
8
|
+
.replace(prefixPath, '')
|
|
9
|
+
.replace(/\.[jt]s$/, '')
|
|
10
|
+
.replace(/^\//, ''));
|
|
11
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = importServices;
|
|
4
|
+
exports.setCachedServices = setCachedServices;
|
|
5
|
+
exports.getServicesOrFail = getServicesOrFail;
|
|
6
|
+
exports.getServicesOrBlank = getServicesOrBlank;
|
|
7
|
+
const globalServiceKeyFromPath_js_1 = require("../globalServiceKeyFromPath.js");
|
|
8
|
+
const PsychicImporter_js_1 = require("../PsychicImporter.js");
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
let _services;
|
|
11
|
+
async function importServices(servicesPath,
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
serviceImportCb) {
|
|
14
|
+
if (_services)
|
|
15
|
+
return _services;
|
|
16
|
+
_services = {};
|
|
17
|
+
const serviceClasses = await PsychicImporter_js_1.default.importServices(servicesPath, serviceImportCb);
|
|
18
|
+
for (const [servicePath, serviceClass] of serviceClasses) {
|
|
19
|
+
const typedServiceClass = serviceClass;
|
|
20
|
+
const serviceKey = (0, globalServiceKeyFromPath_js_1.default)(servicePath, servicesPath);
|
|
21
|
+
if (typeof typedServiceClass['setGlobalName'] === 'function') {
|
|
22
|
+
typedServiceClass['setGlobalName'](serviceKey);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
typedServiceClass.globalName = serviceKey;
|
|
26
|
+
}
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
28
|
+
_services[serviceKey] = serviceClass;
|
|
29
|
+
}
|
|
30
|
+
return _services;
|
|
31
|
+
}
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
function setCachedServices(services) {
|
|
34
|
+
_services = services;
|
|
35
|
+
}
|
|
36
|
+
function getServicesOrFail() {
|
|
37
|
+
if (!_services)
|
|
38
|
+
throw new Error('Must call loadServices before calling getServicesOrFail');
|
|
39
|
+
return _services;
|
|
40
|
+
}
|
|
41
|
+
function getServicesOrBlank() {
|
|
42
|
+
return _services || {};
|
|
43
|
+
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.default = lookupClassByGlobalName;
|
|
4
4
|
const dream_1 = require("@rvoh/dream");
|
|
5
5
|
const importControllers_js_1 = require("./import/importControllers.js");
|
|
6
|
+
const importServices_js_1 = require("./import/importServices.js");
|
|
6
7
|
function lookupClassByGlobalName(name) {
|
|
7
8
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
8
9
|
const lookup = (0, dream_1.lookupClassByGlobalName)(name);
|
|
@@ -11,6 +12,8 @@ function lookupClassByGlobalName(name) {
|
|
|
11
12
|
return lookup;
|
|
12
13
|
const combinedObj = {
|
|
13
14
|
...(0, importControllers_js_1.getControllersOrFail)(),
|
|
15
|
+
...(0, importServices_js_1.getServicesOrFail)(),
|
|
14
16
|
};
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
15
18
|
return combinedObj[name] || null;
|
|
16
19
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsychicApplicationAllowedPackageManagersEnumValues = void 0;
|
|
3
4
|
const dream_1 = require("@rvoh/dream");
|
|
4
5
|
const init_missing_api_root_js_1 = require("../error/psychic-application/init-missing-api-root.js");
|
|
5
6
|
const init_missing_call_to_load_controllers_js_1 = require("../error/psychic-application/init-missing-call-to-load-controllers.js");
|
|
@@ -9,6 +10,9 @@ const EnvInternal_js_1 = require("../helpers/EnvInternal.js");
|
|
|
9
10
|
const cache_js_1 = require("./cache.js");
|
|
10
11
|
const importControllers_js_1 = require("./helpers/import/importControllers.js");
|
|
11
12
|
const lookupClassByGlobalName_js_1 = require("./helpers/lookupClassByGlobalName.js");
|
|
13
|
+
const init_missing_package_manager_js_1 = require("../error/psychic-application/init-missing-package-manager.js");
|
|
14
|
+
const importServices_js_1 = require("./helpers/import/importServices.js");
|
|
15
|
+
const pascalizeFileName_js_1 = require("../helpers/pascalizeFileName.js");
|
|
12
16
|
class PsychicApplication {
|
|
13
17
|
static async init(cb, dreamCb, opts = {}) {
|
|
14
18
|
let psychicApp;
|
|
@@ -21,11 +25,27 @@ class PsychicApplication {
|
|
|
21
25
|
throw new init_missing_api_root_js_1.default();
|
|
22
26
|
if (!psychicApp.routesCb)
|
|
23
27
|
throw new init_missing_routes_callback_js_1.default();
|
|
28
|
+
if (!exports.PsychicApplicationAllowedPackageManagersEnumValues.includes(psychicApp.packageManager))
|
|
29
|
+
throw new init_missing_package_manager_js_1.default();
|
|
24
30
|
if (psychicApp.encryption?.cookies?.current)
|
|
25
31
|
this.checkKey('cookies', psychicApp.encryption.cookies.current.key, psychicApp.encryption.cookies.current.algorithm);
|
|
26
32
|
await psychicApp.inflections?.();
|
|
27
33
|
dreamApp.set('projectRoot', psychicApp.apiRoot);
|
|
28
34
|
dreamApp.set('logger', psychicApp.logger);
|
|
35
|
+
dreamApp.on('repl:start', context => {
|
|
36
|
+
const psychicApp = PsychicApplication.getOrFail();
|
|
37
|
+
for (const globalName of Object.keys(psychicApp.services)) {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
39
|
+
if (!context[globalName]) {
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
41
|
+
;
|
|
42
|
+
context[(0, pascalizeFileName_js_1.default)(globalName)] = psychicApp.services[globalName];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
for (const plugin of psychicApp.plugins) {
|
|
47
|
+
await plugin(psychicApp);
|
|
48
|
+
}
|
|
29
49
|
(0, cache_js_1.cachePsychicApplication)(psychicApp);
|
|
30
50
|
});
|
|
31
51
|
return psychicApp;
|
|
@@ -34,6 +54,29 @@ class PsychicApplication {
|
|
|
34
54
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
35
55
|
return (0, lookupClassByGlobalName_js_1.default)(name);
|
|
36
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* @internal
|
|
59
|
+
*
|
|
60
|
+
* used to provide the correct package manager syntax for running a script
|
|
61
|
+
* inside of the package.json "scripts" section.
|
|
62
|
+
*/
|
|
63
|
+
packageManagerRunCmd(cmd) {
|
|
64
|
+
switch (this.packageManager) {
|
|
65
|
+
case 'npm':
|
|
66
|
+
return `npm run ${cmd}`;
|
|
67
|
+
default:
|
|
68
|
+
return `${this.packageManager} ${cmd}`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @internal
|
|
73
|
+
*
|
|
74
|
+
* adds the necessary package manager prefix to the psy command provided
|
|
75
|
+
* i.e. `psyCmd('sync')`
|
|
76
|
+
*/
|
|
77
|
+
psyCmd(cmd) {
|
|
78
|
+
return this.packageManagerRunCmd(`psy ${cmd}`);
|
|
79
|
+
}
|
|
37
80
|
static checkKey(encryptionIdentifier, key, algorithm) {
|
|
38
81
|
if (!dream_1.Encrypt.validateKey(key, algorithm))
|
|
39
82
|
console.warn(`
|
|
@@ -108,14 +151,18 @@ Try setting it to something valid, like:
|
|
|
108
151
|
get logger() {
|
|
109
152
|
return this._logger;
|
|
110
153
|
}
|
|
111
|
-
_sslCredentials;
|
|
154
|
+
_sslCredentials = undefined;
|
|
112
155
|
get sslCredentials() {
|
|
113
156
|
return this._sslCredentials;
|
|
114
157
|
}
|
|
115
|
-
_saltRounds;
|
|
158
|
+
_saltRounds = undefined;
|
|
116
159
|
get saltRounds() {
|
|
117
160
|
return this._saltRounds;
|
|
118
161
|
}
|
|
162
|
+
_packageManager;
|
|
163
|
+
get packageManager() {
|
|
164
|
+
return this._packageManager;
|
|
165
|
+
}
|
|
119
166
|
_routesCb;
|
|
120
167
|
get routesCb() {
|
|
121
168
|
return this._routesCb;
|
|
@@ -143,6 +190,7 @@ Try setting it to something valid, like:
|
|
|
143
190
|
_paths = {
|
|
144
191
|
apiRoutes: 'src/conf/routes.ts',
|
|
145
192
|
controllers: 'src/app/controllers',
|
|
193
|
+
services: 'src/app/services',
|
|
146
194
|
controllerSpecs: 'spec/unit/controllers',
|
|
147
195
|
};
|
|
148
196
|
get paths() {
|
|
@@ -183,6 +231,10 @@ Try setting it to something valid, like:
|
|
|
183
231
|
get loadedControllers() {
|
|
184
232
|
return this._loadedControllers;
|
|
185
233
|
}
|
|
234
|
+
_loadedServices = false;
|
|
235
|
+
get loadedServices() {
|
|
236
|
+
return this._loadedServices;
|
|
237
|
+
}
|
|
186
238
|
_baseDefaultResponseHeaders = {
|
|
187
239
|
['cache-control']: 'max-age=0, private, must-revalidate',
|
|
188
240
|
};
|
|
@@ -196,6 +248,13 @@ Try setting it to something valid, like:
|
|
|
196
248
|
get controllers() {
|
|
197
249
|
return (0, importControllers_js_1.getControllersOrFail)();
|
|
198
250
|
}
|
|
251
|
+
get services() {
|
|
252
|
+
return (0, importServices_js_1.getServicesOrFail)();
|
|
253
|
+
}
|
|
254
|
+
_plugins = [];
|
|
255
|
+
get plugins() {
|
|
256
|
+
return this._plugins;
|
|
257
|
+
}
|
|
199
258
|
async load(resourceType, resourcePath,
|
|
200
259
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
201
260
|
importCb) {
|
|
@@ -204,6 +263,10 @@ Try setting it to something valid, like:
|
|
|
204
263
|
await (0, importControllers_js_1.default)(this, resourcePath, importCb);
|
|
205
264
|
this._loadedControllers = true;
|
|
206
265
|
break;
|
|
266
|
+
case 'services':
|
|
267
|
+
await (0, importServices_js_1.default)(resourcePath, importCb);
|
|
268
|
+
this._loadedServices = true;
|
|
269
|
+
break;
|
|
207
270
|
}
|
|
208
271
|
}
|
|
209
272
|
booted = false;
|
|
@@ -226,6 +289,9 @@ Try setting it to something valid, like:
|
|
|
226
289
|
await this.inflections?.();
|
|
227
290
|
this.booted = true;
|
|
228
291
|
}
|
|
292
|
+
plugin(cb) {
|
|
293
|
+
this._plugins.push(cb);
|
|
294
|
+
}
|
|
229
295
|
on(hookEventType, cb) {
|
|
230
296
|
switch (hookEventType) {
|
|
231
297
|
case 'server:error':
|
|
@@ -306,6 +372,9 @@ Try setting it to something valid, like:
|
|
|
306
372
|
case 'port':
|
|
307
373
|
this._port = value;
|
|
308
374
|
break;
|
|
375
|
+
case 'packageManager':
|
|
376
|
+
this._packageManager = value;
|
|
377
|
+
break;
|
|
309
378
|
case 'saltRounds':
|
|
310
379
|
this._saltRounds = value;
|
|
311
380
|
break;
|
|
@@ -344,3 +413,4 @@ Try setting it to something valid, like:
|
|
|
344
413
|
}
|
|
345
414
|
}
|
|
346
415
|
exports.default = PsychicApplication;
|
|
416
|
+
exports.PsychicApplicationAllowedPackageManagersEnumValues = ['yarn', 'npm', 'pnpm'];
|
|
@@ -25,6 +25,20 @@ export default class PsychicBin {
|
|
|
25
25
|
if (!bypassDreamSync)
|
|
26
26
|
await DreamBin.sync(() => { });
|
|
27
27
|
await PsychicBin.syncTypes();
|
|
28
|
+
const psychicApp = PsychicApplication.getOrFail();
|
|
29
|
+
DreamCLI.logger.logStartProgress('running post-sync operations...');
|
|
30
|
+
// call post-sync command in a separate process, so that newly-generated
|
|
31
|
+
// types can be reloaded and brought into all classes.
|
|
32
|
+
await DreamCLI.spawn(psychicApp.psyCmd('post-sync'), {
|
|
33
|
+
onStdout: message => {
|
|
34
|
+
DreamCLI.logger.logContinueProgress(`[post-sync]` + ' ' + message, {
|
|
35
|
+
logPrefixColor: 'cyan',
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
DreamCLI.logger.logEndProgress();
|
|
40
|
+
}
|
|
41
|
+
static async postSync() {
|
|
28
42
|
const psychicApp = PsychicApplication.getOrFail();
|
|
29
43
|
await PsychicBin.syncOpenapiJson();
|
|
30
44
|
if (psychicApp.openapi?.syncEnumsToClient) {
|
|
@@ -48,6 +48,14 @@ export default class PsychicCLI {
|
|
|
48
48
|
await PsychicBin.sync();
|
|
49
49
|
process.exit();
|
|
50
50
|
});
|
|
51
|
+
program
|
|
52
|
+
.command('post-sync')
|
|
53
|
+
.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')
|
|
54
|
+
.action(async () => {
|
|
55
|
+
await initializePsychicApplication();
|
|
56
|
+
await PsychicBin.postSync();
|
|
57
|
+
process.exit();
|
|
58
|
+
});
|
|
51
59
|
program
|
|
52
60
|
.command('sync:routes')
|
|
53
61
|
.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.')
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default class PsychicApplicationInitMissingPackageManager extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super();
|
|
4
|
+
}
|
|
5
|
+
get message() {
|
|
6
|
+
return `
|
|
7
|
+
must set packageManager when initializing a new PsychicApplication.
|
|
8
|
+
|
|
9
|
+
within conf/app.ts, you must have a call to "#set('packageManager', '<YOUR_CHOSEN_PACKAGE_MANAGER>')", i.e.
|
|
10
|
+
|
|
11
|
+
// conf/app.ts
|
|
12
|
+
export default async (app: PsychicApplication) => {
|
|
13
|
+
await app.set('packageManager', 'yarn')
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -6,4 +6,13 @@ export default class PsychicImporter {
|
|
|
6
6
|
const controllerClasses = (await Promise.all(controllerPaths.map(controllerPath => importCb(controllerPath).then(dreamClass => [controllerPath, dreamClass]))));
|
|
7
7
|
return controllerClasses;
|
|
8
8
|
}
|
|
9
|
+
static async importServices(pathToServices,
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
importCb) {
|
|
12
|
+
const servicePaths = await DreamImporter.ls(pathToServices);
|
|
13
|
+
const serviceClasses = (await Promise.all(servicePaths.map(servicePath =>
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
15
|
+
importCb(servicePath).then(serviceClass => [servicePath, serviceClass]))));
|
|
16
|
+
return serviceClasses;
|
|
17
|
+
}
|
|
9
18
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import globalServiceKeyFromPath from '../globalServiceKeyFromPath.js';
|
|
2
|
+
import PsychicImporter from '../PsychicImporter.js';
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
|
+
let _services;
|
|
5
|
+
export default async function importServices(servicesPath,
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
serviceImportCb) {
|
|
8
|
+
if (_services)
|
|
9
|
+
return _services;
|
|
10
|
+
_services = {};
|
|
11
|
+
const serviceClasses = await PsychicImporter.importServices(servicesPath, serviceImportCb);
|
|
12
|
+
for (const [servicePath, serviceClass] of serviceClasses) {
|
|
13
|
+
const typedServiceClass = serviceClass;
|
|
14
|
+
const serviceKey = globalServiceKeyFromPath(servicePath, servicesPath);
|
|
15
|
+
if (typeof typedServiceClass['setGlobalName'] === 'function') {
|
|
16
|
+
typedServiceClass['setGlobalName'](serviceKey);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
typedServiceClass.globalName = serviceKey;
|
|
20
|
+
}
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
22
|
+
_services[serviceKey] = serviceClass;
|
|
23
|
+
}
|
|
24
|
+
return _services;
|
|
25
|
+
}
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
export function setCachedServices(services) {
|
|
28
|
+
_services = services;
|
|
29
|
+
}
|
|
30
|
+
export function getServicesOrFail() {
|
|
31
|
+
if (!_services)
|
|
32
|
+
throw new Error('Must call loadServices before calling getServicesOrFail');
|
|
33
|
+
return _services;
|
|
34
|
+
}
|
|
35
|
+
export function getServicesOrBlank() {
|
|
36
|
+
return _services || {};
|
|
37
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { lookupClassByGlobalName as dreamLookupClassByGlobalName } from '@rvoh/dream';
|
|
2
2
|
import { getControllersOrFail } from './import/importControllers.js';
|
|
3
|
+
import { getServicesOrFail } from './import/importServices.js';
|
|
3
4
|
export default function lookupClassByGlobalName(name) {
|
|
4
5
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
5
6
|
const lookup = dreamLookupClassByGlobalName(name);
|
|
@@ -8,6 +9,8 @@ export default function lookupClassByGlobalName(name) {
|
|
|
8
9
|
return lookup;
|
|
9
10
|
const combinedObj = {
|
|
10
11
|
...getControllersOrFail(),
|
|
12
|
+
...getServicesOrFail(),
|
|
11
13
|
};
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
12
15
|
return combinedObj[name] || null;
|
|
13
16
|
}
|
|
@@ -7,6 +7,9 @@ import EnvInternal from '../helpers/EnvInternal.js';
|
|
|
7
7
|
import { cachePsychicApplication, getCachedPsychicApplicationOrFail } from './cache.js';
|
|
8
8
|
import importControllers, { getControllersOrFail } from './helpers/import/importControllers.js';
|
|
9
9
|
import lookupClassByGlobalName from './helpers/lookupClassByGlobalName.js';
|
|
10
|
+
import PsychicApplicationInitMissingPackageManager from '../error/psychic-application/init-missing-package-manager.js';
|
|
11
|
+
import importServices, { getServicesOrFail } from './helpers/import/importServices.js';
|
|
12
|
+
import pascalizeFileName from '../helpers/pascalizeFileName.js';
|
|
10
13
|
export default class PsychicApplication {
|
|
11
14
|
static async init(cb, dreamCb, opts = {}) {
|
|
12
15
|
let psychicApp;
|
|
@@ -19,11 +22,27 @@ export default class PsychicApplication {
|
|
|
19
22
|
throw new PsychicApplicationInitMissingApiRoot();
|
|
20
23
|
if (!psychicApp.routesCb)
|
|
21
24
|
throw new PsychicApplicationInitMissingRoutesCallback();
|
|
25
|
+
if (!PsychicApplicationAllowedPackageManagersEnumValues.includes(psychicApp.packageManager))
|
|
26
|
+
throw new PsychicApplicationInitMissingPackageManager();
|
|
22
27
|
if (psychicApp.encryption?.cookies?.current)
|
|
23
28
|
this.checkKey('cookies', psychicApp.encryption.cookies.current.key, psychicApp.encryption.cookies.current.algorithm);
|
|
24
29
|
await psychicApp.inflections?.();
|
|
25
30
|
dreamApp.set('projectRoot', psychicApp.apiRoot);
|
|
26
31
|
dreamApp.set('logger', psychicApp.logger);
|
|
32
|
+
dreamApp.on('repl:start', context => {
|
|
33
|
+
const psychicApp = PsychicApplication.getOrFail();
|
|
34
|
+
for (const globalName of Object.keys(psychicApp.services)) {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
36
|
+
if (!context[globalName]) {
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
38
|
+
;
|
|
39
|
+
context[pascalizeFileName(globalName)] = psychicApp.services[globalName];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
for (const plugin of psychicApp.plugins) {
|
|
44
|
+
await plugin(psychicApp);
|
|
45
|
+
}
|
|
27
46
|
cachePsychicApplication(psychicApp);
|
|
28
47
|
});
|
|
29
48
|
return psychicApp;
|
|
@@ -32,6 +51,29 @@ export default class PsychicApplication {
|
|
|
32
51
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
33
52
|
return lookupClassByGlobalName(name);
|
|
34
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* @internal
|
|
56
|
+
*
|
|
57
|
+
* used to provide the correct package manager syntax for running a script
|
|
58
|
+
* inside of the package.json "scripts" section.
|
|
59
|
+
*/
|
|
60
|
+
packageManagerRunCmd(cmd) {
|
|
61
|
+
switch (this.packageManager) {
|
|
62
|
+
case 'npm':
|
|
63
|
+
return `npm run ${cmd}`;
|
|
64
|
+
default:
|
|
65
|
+
return `${this.packageManager} ${cmd}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @internal
|
|
70
|
+
*
|
|
71
|
+
* adds the necessary package manager prefix to the psy command provided
|
|
72
|
+
* i.e. `psyCmd('sync')`
|
|
73
|
+
*/
|
|
74
|
+
psyCmd(cmd) {
|
|
75
|
+
return this.packageManagerRunCmd(`psy ${cmd}`);
|
|
76
|
+
}
|
|
35
77
|
static checkKey(encryptionIdentifier, key, algorithm) {
|
|
36
78
|
if (!Encrypt.validateKey(key, algorithm))
|
|
37
79
|
console.warn(`
|
|
@@ -106,14 +148,18 @@ Try setting it to something valid, like:
|
|
|
106
148
|
get logger() {
|
|
107
149
|
return this._logger;
|
|
108
150
|
}
|
|
109
|
-
_sslCredentials;
|
|
151
|
+
_sslCredentials = undefined;
|
|
110
152
|
get sslCredentials() {
|
|
111
153
|
return this._sslCredentials;
|
|
112
154
|
}
|
|
113
|
-
_saltRounds;
|
|
155
|
+
_saltRounds = undefined;
|
|
114
156
|
get saltRounds() {
|
|
115
157
|
return this._saltRounds;
|
|
116
158
|
}
|
|
159
|
+
_packageManager;
|
|
160
|
+
get packageManager() {
|
|
161
|
+
return this._packageManager;
|
|
162
|
+
}
|
|
117
163
|
_routesCb;
|
|
118
164
|
get routesCb() {
|
|
119
165
|
return this._routesCb;
|
|
@@ -141,6 +187,7 @@ Try setting it to something valid, like:
|
|
|
141
187
|
_paths = {
|
|
142
188
|
apiRoutes: 'src/conf/routes.ts',
|
|
143
189
|
controllers: 'src/app/controllers',
|
|
190
|
+
services: 'src/app/services',
|
|
144
191
|
controllerSpecs: 'spec/unit/controllers',
|
|
145
192
|
};
|
|
146
193
|
get paths() {
|
|
@@ -181,6 +228,10 @@ Try setting it to something valid, like:
|
|
|
181
228
|
get loadedControllers() {
|
|
182
229
|
return this._loadedControllers;
|
|
183
230
|
}
|
|
231
|
+
_loadedServices = false;
|
|
232
|
+
get loadedServices() {
|
|
233
|
+
return this._loadedServices;
|
|
234
|
+
}
|
|
184
235
|
_baseDefaultResponseHeaders = {
|
|
185
236
|
['cache-control']: 'max-age=0, private, must-revalidate',
|
|
186
237
|
};
|
|
@@ -194,6 +245,13 @@ Try setting it to something valid, like:
|
|
|
194
245
|
get controllers() {
|
|
195
246
|
return getControllersOrFail();
|
|
196
247
|
}
|
|
248
|
+
get services() {
|
|
249
|
+
return getServicesOrFail();
|
|
250
|
+
}
|
|
251
|
+
_plugins = [];
|
|
252
|
+
get plugins() {
|
|
253
|
+
return this._plugins;
|
|
254
|
+
}
|
|
197
255
|
async load(resourceType, resourcePath,
|
|
198
256
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
199
257
|
importCb) {
|
|
@@ -202,6 +260,10 @@ Try setting it to something valid, like:
|
|
|
202
260
|
await importControllers(this, resourcePath, importCb);
|
|
203
261
|
this._loadedControllers = true;
|
|
204
262
|
break;
|
|
263
|
+
case 'services':
|
|
264
|
+
await importServices(resourcePath, importCb);
|
|
265
|
+
this._loadedServices = true;
|
|
266
|
+
break;
|
|
205
267
|
}
|
|
206
268
|
}
|
|
207
269
|
booted = false;
|
|
@@ -224,6 +286,9 @@ Try setting it to something valid, like:
|
|
|
224
286
|
await this.inflections?.();
|
|
225
287
|
this.booted = true;
|
|
226
288
|
}
|
|
289
|
+
plugin(cb) {
|
|
290
|
+
this._plugins.push(cb);
|
|
291
|
+
}
|
|
227
292
|
on(hookEventType, cb) {
|
|
228
293
|
switch (hookEventType) {
|
|
229
294
|
case 'server:error':
|
|
@@ -304,6 +369,9 @@ Try setting it to something valid, like:
|
|
|
304
369
|
case 'port':
|
|
305
370
|
this._port = value;
|
|
306
371
|
break;
|
|
372
|
+
case 'packageManager':
|
|
373
|
+
this._packageManager = value;
|
|
374
|
+
break;
|
|
307
375
|
case 'saltRounds':
|
|
308
376
|
this._saltRounds = value;
|
|
309
377
|
break;
|
|
@@ -341,3 +409,4 @@ Try setting it to something valid, like:
|
|
|
341
409
|
}
|
|
342
410
|
}
|
|
343
411
|
}
|
|
412
|
+
export const PsychicApplicationAllowedPackageManagersEnumValues = ['yarn', 'npm', 'pnpm'];
|
|
@@ -5,6 +5,7 @@ export default class PsychicBin {
|
|
|
5
5
|
static sync({ bypassDreamSync }?: {
|
|
6
6
|
bypassDreamSync?: boolean;
|
|
7
7
|
}): Promise<void>;
|
|
8
|
+
static postSync(): Promise<void>;
|
|
8
9
|
static syncTypes(customTypes?: any): Promise<void>;
|
|
9
10
|
static syncOpenapiJson(): Promise<void>;
|
|
10
11
|
static syncRoutes(): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export default function (dreamPathType: PsychicPaths): string;
|
|
2
2
|
type DreamPaths = 'models' | 'modelSpecs' | 'serializers' | 'db' | 'conf' | 'factories';
|
|
3
|
-
export type PsychicPaths = DreamPaths | 'apiRoutes' | 'controllers' | 'controllerSpecs';
|
|
3
|
+
export type PsychicPaths = DreamPaths | 'apiRoutes' | 'controllers' | 'controllerSpecs' | 'services';
|
|
4
4
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import PsychicController from '../../controller/index.js';
|
|
2
2
|
export default class PsychicImporter {
|
|
3
3
|
static importControllers(controllersPath: string, importCb: (path: string) => Promise<any>): Promise<[string, typeof PsychicController][]>;
|
|
4
|
+
static importServices(pathToServices: string, importCb: (path: string) => Promise<any>): Promise<[string, any][]>;
|
|
4
5
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function (filepath: string, dirPath: string): string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export default function importServices(servicesPath: string, serviceImportCb: (path: string) => Promise<any>): Promise<Record<string, any>>;
|
|
2
|
+
export declare function setCachedServices(services: Record<string, any>): void;
|
|
3
|
+
export declare function getServicesOrFail(): Record<string, any>;
|
|
4
|
+
export declare function getServicesOrBlank(): Record<string, any>;
|
|
@@ -11,6 +11,20 @@ import { PsychicHookEventType, PsychicHookLoadEventTypes } from './types.js';
|
|
|
11
11
|
export default class PsychicApplication {
|
|
12
12
|
static init(cb: (app: PsychicApplication) => void | Promise<void>, dreamCb: (app: DreamApplication) => void | Promise<void>, opts?: PsychicApplicationInitOptions): Promise<PsychicApplication>;
|
|
13
13
|
static lookupClassByGlobalName(name: string): any;
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* used to provide the correct package manager syntax for running a script
|
|
18
|
+
* inside of the package.json "scripts" section.
|
|
19
|
+
*/
|
|
20
|
+
packageManagerRunCmd(cmd: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* @internal
|
|
23
|
+
*
|
|
24
|
+
* adds the necessary package manager prefix to the psy command provided
|
|
25
|
+
* i.e. `psyCmd('sync')`
|
|
26
|
+
*/
|
|
27
|
+
psyCmd(cmd: string): string;
|
|
14
28
|
private static checkKey;
|
|
15
29
|
/**
|
|
16
30
|
* Returns the cached psychic application if it has been set.
|
|
@@ -45,10 +59,12 @@ export default class PsychicApplication {
|
|
|
45
59
|
};
|
|
46
60
|
private _logger;
|
|
47
61
|
get logger(): DreamLogger;
|
|
48
|
-
private _sslCredentials
|
|
62
|
+
private _sslCredentials;
|
|
49
63
|
get sslCredentials(): PsychicSslCredentials | undefined;
|
|
50
|
-
private _saltRounds
|
|
64
|
+
private _saltRounds;
|
|
51
65
|
get saltRounds(): number | undefined;
|
|
66
|
+
private _packageManager;
|
|
67
|
+
get packageManager(): "yarn" | "npm" | "pnpm";
|
|
52
68
|
private _routesCb;
|
|
53
69
|
get routesCb(): (r: PsychicRouter) => void | Promise<void>;
|
|
54
70
|
private _openapi;
|
|
@@ -67,22 +83,30 @@ export default class PsychicApplication {
|
|
|
67
83
|
private get overrides();
|
|
68
84
|
private _loadedControllers;
|
|
69
85
|
get loadedControllers(): boolean;
|
|
86
|
+
private _loadedServices;
|
|
87
|
+
get loadedServices(): boolean;
|
|
70
88
|
private _baseDefaultResponseHeaders;
|
|
71
89
|
private _defaultResponseHeaders;
|
|
72
90
|
get defaultResponseHeaders(): {
|
|
73
91
|
[x: string]: string | null;
|
|
74
92
|
};
|
|
75
93
|
get controllers(): Record<string, typeof import("../index.js").PsychicController>;
|
|
76
|
-
|
|
94
|
+
get services(): Record<string, any>;
|
|
95
|
+
private _plugins;
|
|
96
|
+
get plugins(): ((app: PsychicApplication) => void | Promise<void>)[];
|
|
97
|
+
load<RT extends 'controllers' | 'services'>(resourceType: RT, resourcePath: string, importCb: (path: string) => Promise<any>): Promise<void>;
|
|
77
98
|
private booted;
|
|
78
99
|
boot(force?: boolean): Promise<void>;
|
|
100
|
+
plugin(cb: (app: PsychicApplication) => void | Promise<void>): void;
|
|
79
101
|
on<T extends PsychicHookEventType>(hookEventType: T, cb: T extends 'server:error' ? (err: Error, req: Request, res: Response) => void | Promise<void> : T extends 'server:init' ? (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 'sync' ? () => any : (conf: PsychicApplication) => void | Promise<void>): void;
|
|
80
102
|
set(option: 'openapi', name: string, value: NamedPsychicOpenapiOptions): void;
|
|
81
|
-
set<Opt extends PsychicApplicationOption>(option: Opt, value: Opt extends 'appName' ? string : Opt extends 'apiOnly' ? boolean : Opt extends 'defaultResponseHeaders' ? Record<string, string | null> : Opt extends 'encryption' ? PsychicApplicationEncryptionOptions : Opt extends 'cors' ? CorsOptions : Opt extends 'cookie' ? CustomCookieOptions : Opt extends 'apiRoot' ? string : Opt extends 'sessionCookieName' ? string : Opt extends 'clientRoot' ? string : Opt extends 'json' ? bodyParser.Options : Opt extends 'logger' ? PsychicLogger : Opt extends 'client' ? PsychicClientOptions : Opt extends 'ssl' ? PsychicSslCredentials : Opt extends 'openapi' ? DefaultPsychicOpenapiOptions : Opt extends 'paths' ? PsychicPathOptions : Opt extends 'port' ? number : Opt extends 'saltRounds' ? number : Opt extends 'inflections' ? () => void | Promise<void> : Opt extends 'routes' ? (r: PsychicRouter) => void | Promise<void> : never): void;
|
|
103
|
+
set<Opt extends PsychicApplicationOption>(option: Opt, value: Opt extends 'appName' ? string : Opt extends 'apiOnly' ? boolean : Opt extends 'defaultResponseHeaders' ? Record<string, string | null> : Opt extends 'encryption' ? PsychicApplicationEncryptionOptions : Opt extends 'cors' ? CorsOptions : Opt extends 'cookie' ? CustomCookieOptions : Opt extends 'apiRoot' ? string : Opt extends 'sessionCookieName' ? string : Opt extends 'clientRoot' ? string : Opt extends 'json' ? bodyParser.Options : Opt extends 'logger' ? PsychicLogger : Opt extends 'client' ? PsychicClientOptions : Opt extends 'ssl' ? PsychicSslCredentials : Opt extends 'openapi' ? DefaultPsychicOpenapiOptions : Opt extends 'paths' ? PsychicPathOptions : Opt extends 'port' ? number : Opt extends 'saltRounds' ? number : Opt extends 'packageManager' ? PsychicApplicationAllowedPackageManagersEnum : Opt extends 'inflections' ? () => void | Promise<void> : Opt extends 'routes' ? (r: PsychicRouter) => void | Promise<void> : never): void;
|
|
82
104
|
override<Override extends keyof PsychicApplicationOverrides>(override: Override, value: PsychicApplicationOverrides[Override]): void;
|
|
83
105
|
private runHooksFor;
|
|
84
106
|
}
|
|
85
|
-
export type PsychicApplicationOption = 'appName' | 'apiOnly' | 'apiRoot' | 'encryption' | 'sessionCookieName' | 'client' | 'clientRoot' | 'cookie' | 'cors' | 'defaultResponseHeaders' | 'inflections' | 'json' | 'logger' | 'openapi' | 'paths' | 'port' | 'routes' | 'saltRounds' | 'ssl';
|
|
107
|
+
export type PsychicApplicationOption = 'appName' | 'apiOnly' | 'apiRoot' | 'encryption' | 'sessionCookieName' | 'client' | 'clientRoot' | 'cookie' | 'cors' | 'defaultResponseHeaders' | 'inflections' | 'json' | 'logger' | 'openapi' | 'packageManager' | 'paths' | 'port' | 'routes' | 'saltRounds' | 'ssl';
|
|
108
|
+
export declare const PsychicApplicationAllowedPackageManagersEnumValues: readonly ["yarn", "npm", "pnpm"];
|
|
109
|
+
export type PsychicApplicationAllowedPackageManagersEnum = (typeof PsychicApplicationAllowedPackageManagersEnumValues)[number];
|
|
86
110
|
export interface PsychicApplicationSpecialHooks {
|
|
87
111
|
sync: (() => any)[];
|
|
88
112
|
serverInit: ((server: PsychicServer) => void | Promise<void>)[];
|
|
@@ -143,6 +167,7 @@ interface PsychicOpenapiInfo {
|
|
|
143
167
|
}
|
|
144
168
|
interface PsychicPathOptions {
|
|
145
169
|
apiRoutes?: string;
|
|
170
|
+
services?: string;
|
|
146
171
|
controllers?: string;
|
|
147
172
|
controllerSpecs?: string;
|
|
148
173
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@rvoh/psychic",
|
|
4
4
|
"description": "Typescript web framework",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.28.0",
|
|
6
6
|
"author": "RVOHealth",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -24,11 +24,10 @@
|
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
26
|
"client": "yarn --cwd=./client start",
|
|
27
|
-
"psy": "NODE_ENV=${NODE_ENV:-test} yarn psyts",
|
|
27
|
+
"psy": "PSYCHIC_CORE_DEVELOPMENT=1 NODE_ENV=${NODE_ENV:-test} yarn psyts",
|
|
28
28
|
"psyjs": "node ./dist/test-app/src/cli/index.js",
|
|
29
29
|
"psyts": "NODE_ENV=${NODE_ENV:-test} tsx ./test-app/src/cli/index.ts",
|
|
30
|
-
"
|
|
31
|
-
"gpsycore": "PSYCHIC_CORE_DEVELOPMENT=1 tsx ./global-cli/main.ts",
|
|
30
|
+
"gpsy": "PSYCHIC_CORE_DEVELOPMENT=1 tsx ./global-cli/main.ts",
|
|
32
31
|
"build": "echo \"building cjs...\" && rm -rf dist && npx tsc -p ./tsconfig.cjs.build.json && echo \"building esm...\" && npx tsc -p ./tsconfig.esm.build.json",
|
|
33
32
|
"build:test-app": "rm -rf dist && echo \"building test app to esm...\" && npx tsc -p ./tsconfig.esm.build.test-app.json && echo \"building test app to cjs...\" && npx tsc -p ./tsconfig.cjs.build.test-app.json",
|
|
34
33
|
"dev": "PSYCHIC_CORE_DEVELOPMENT=1 NODE_ENV=development tsx ./test-app/main.ts",
|
|
@@ -63,7 +62,7 @@
|
|
|
63
62
|
"devDependencies": {
|
|
64
63
|
"@eslint/js": "^9.19.0",
|
|
65
64
|
"@jest-mock/express": "^3.0.0",
|
|
66
|
-
"@rvoh/dream": "^0.
|
|
65
|
+
"@rvoh/dream": "^0.33.0",
|
|
67
66
|
"@rvoh/dream-spec-helpers": "=0.1.0",
|
|
68
67
|
"@rvoh/psychic-spec-helpers": "=0.2.0",
|
|
69
68
|
"@types/express": "^4.17.21",
|
|
@@ -89,8 +88,8 @@
|
|
|
89
88
|
"typedoc": "^0.26.6",
|
|
90
89
|
"typescript": "^5.5.4",
|
|
91
90
|
"typescript-eslint": "=7.18.0",
|
|
92
|
-
"vitest": "^3.
|
|
91
|
+
"vitest": "^3.1.1",
|
|
93
92
|
"winston": "^3.14.2"
|
|
94
93
|
},
|
|
95
94
|
"packageManager": "yarn@4.7.0"
|
|
96
|
-
}
|
|
95
|
+
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = sspawn;
|
|
4
|
-
exports.ssspawn = ssspawn;
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
function sspawn(command,
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
-
opts = {}) {
|
|
9
|
-
return new Promise((accept, reject) => {
|
|
10
|
-
ssspawn(command, opts).on('close', code => {
|
|
11
|
-
if (code !== 0)
|
|
12
|
-
reject(new Error(code?.toString()));
|
|
13
|
-
accept({});
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
function ssspawn(command,
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
-
opts = {}) {
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
21
|
-
return (0, child_process_1.spawn)(command, {
|
|
22
|
-
stdio: 'inherit',
|
|
23
|
-
shell: true,
|
|
24
|
-
...opts,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
2
|
-
export default function sspawn(command,
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
|
-
opts = {}) {
|
|
5
|
-
return new Promise((accept, reject) => {
|
|
6
|
-
ssspawn(command, opts).on('close', code => {
|
|
7
|
-
if (code !== 0)
|
|
8
|
-
reject(new Error(code?.toString()));
|
|
9
|
-
accept({});
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
export function ssspawn(command,
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
-
opts = {}) {
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
17
|
-
return spawn(command, {
|
|
18
|
-
stdio: 'inherit',
|
|
19
|
-
shell: true,
|
|
20
|
-
...opts,
|
|
21
|
-
});
|
|
22
|
-
}
|