@rvoh/psychic 0.33.0 → 0.34.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/README.md +1 -1
- package/dist/cjs/src/bin/index.js +9 -7
- package/dist/cjs/src/cli/helpers/PackageManager.js +41 -0
- package/dist/cjs/src/cli/helpers/cli-prompt.js +45 -0
- package/dist/cjs/src/cli/helpers/cli-select.js +135 -0
- package/dist/cjs/src/cli/helpers/colorize.js +11 -0
- package/dist/cjs/src/cli/index.js +42 -0
- package/dist/cjs/src/generate/helpers/reduxBindings/printFinalStepsMessage.js +37 -0
- package/dist/cjs/src/generate/helpers/reduxBindings/promptForOptions.js +81 -0
- package/dist/cjs/src/generate/helpers/reduxBindings/writeApiFile.js +93 -0
- package/dist/cjs/src/generate/helpers/reduxBindings/writeInitializer.js +76 -0
- package/dist/cjs/src/generate/helpers/reduxBindings/writeOpenapiJsonFile.js +52 -0
- package/dist/cjs/src/generate/initializer/syncEnums.js +75 -0
- package/dist/cjs/src/generate/openapi/reduxBindings.js +36 -0
- package/dist/cjs/src/helpers/autogeneratedFileDisclaimer.js +0 -38
- package/dist/cjs/src/psychic-app/helpers/PsychicImporter.js +9 -0
- package/dist/cjs/src/psychic-app/helpers/import/importInitializers.js +34 -0
- package/dist/cjs/src/psychic-app/index.js +12 -15
- package/dist/esm/src/bin/index.js +9 -7
- package/dist/esm/src/cli/helpers/PackageManager.js +35 -0
- package/dist/esm/src/cli/helpers/cli-prompt.js +19 -0
- package/dist/esm/src/cli/helpers/cli-select.js +106 -0
- package/dist/esm/src/cli/helpers/colorize.js +5 -0
- package/dist/esm/src/cli/index.js +42 -0
- package/dist/esm/src/generate/helpers/reduxBindings/printFinalStepsMessage.js +31 -0
- package/dist/esm/src/generate/helpers/reduxBindings/promptForOptions.js +75 -0
- package/dist/esm/src/generate/helpers/reduxBindings/writeApiFile.js +67 -0
- package/dist/esm/src/generate/helpers/reduxBindings/writeInitializer.js +47 -0
- package/dist/esm/src/generate/helpers/reduxBindings/writeOpenapiJsonFile.js +23 -0
- package/dist/esm/src/generate/initializer/syncEnums.js +46 -0
- package/dist/esm/src/generate/openapi/reduxBindings.js +30 -0
- package/dist/esm/src/helpers/autogeneratedFileDisclaimer.js +0 -38
- package/dist/esm/src/psychic-app/helpers/PsychicImporter.js +9 -0
- package/dist/esm/src/psychic-app/helpers/import/importInitializers.js +25 -0
- package/dist/esm/src/psychic-app/index.js +12 -15
- package/dist/types/src/bin/index.d.ts +1 -1
- package/dist/types/src/cli/helpers/PackageManager.d.ts +7 -0
- package/dist/types/src/cli/helpers/cli-prompt.d.ts +1 -0
- package/dist/types/src/cli/helpers/cli-select.d.ts +17 -0
- package/dist/types/src/cli/helpers/colorize.d.ts +6 -0
- package/dist/types/src/generate/helpers/reduxBindings/printFinalStepsMessage.d.ts +2 -0
- package/dist/types/src/generate/helpers/reduxBindings/promptForOptions.d.ts +2 -0
- package/dist/types/src/generate/helpers/reduxBindings/writeApiFile.d.ts +4 -0
- package/dist/types/src/generate/helpers/reduxBindings/writeInitializer.d.ts +3 -0
- package/dist/types/src/generate/helpers/reduxBindings/writeOpenapiJsonFile.d.ts +7 -0
- package/dist/types/src/generate/initializer/syncEnums.d.ts +1 -0
- package/dist/types/src/generate/openapi/reduxBindings.d.ts +23 -0
- package/dist/types/src/psychic-app/helpers/PsychicImporter.d.ts +2 -0
- package/dist/types/src/psychic-app/helpers/import/importInitializers.d.ts +5 -0
- package/dist/types/src/psychic-app/index.d.ts +4 -10
- package/dist/types/src/psychic-app/types.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
> ATTENTION: we are currently in the process of releasing this code to the world, as of the afternoon of
|
|
1
|
+
> ATTENTION: we are currently in the process of releasing this code to the world, as of the afternoon of May 2nd, 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 sometime in the next month
|
|
2
2
|
|
|
3
3
|
# Psychic
|
|
4
4
|
|
|
@@ -69,9 +69,6 @@ class PsychicBin {
|
|
|
69
69
|
static async postSync() {
|
|
70
70
|
const psychicApp = index_js_1.default.getOrFail();
|
|
71
71
|
await PsychicBin.syncOpenapiJson();
|
|
72
|
-
if (psychicApp.openapi?.syncEnumsToClient) {
|
|
73
|
-
await this.syncClientEnums();
|
|
74
|
-
}
|
|
75
72
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
73
|
let output = {};
|
|
77
74
|
for (const hook of psychicApp.specialHooks.sync) {
|
|
@@ -105,12 +102,17 @@ class PsychicBin {
|
|
|
105
102
|
await (0, generateRouteTypes_js_1.default)(routes);
|
|
106
103
|
dream_1.DreamCLI.logger.logEndProgress();
|
|
107
104
|
}
|
|
108
|
-
static async syncClientEnums() {
|
|
105
|
+
static async syncClientEnums(outfile) {
|
|
109
106
|
dream_1.DreamCLI.logger.logStartProgress(`syncing client enums...`);
|
|
110
|
-
const psychicApp = index_js_1.default.getOrFail();
|
|
111
|
-
const apiPath = path.join(psychicApp.clientRoot, psychicApp.client.apiPath);
|
|
112
107
|
const enumsStr = await (0, enumsFileStr_js_1.default)();
|
|
113
|
-
|
|
108
|
+
try {
|
|
109
|
+
const dir = path.dirname(outfile);
|
|
110
|
+
await fs.mkdir(dir, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// noop
|
|
114
|
+
}
|
|
115
|
+
await fs.writeFile(outfile, enumsStr, {});
|
|
114
116
|
dream_1.DreamCLI.logger.logEndProgress();
|
|
115
117
|
}
|
|
116
118
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const index_js_1 = __importDefault(require("../../psychic-app/index.js"));
|
|
7
|
+
class PackageManager {
|
|
8
|
+
static get packageManager() {
|
|
9
|
+
return index_js_1.default.getOrFail().packageManager;
|
|
10
|
+
}
|
|
11
|
+
static add(dependencyOrDependencies, { dev } = {}) {
|
|
12
|
+
const dependency = Array.isArray(dependencyOrDependencies)
|
|
13
|
+
? dependencyOrDependencies.join(' ')
|
|
14
|
+
: dependencyOrDependencies;
|
|
15
|
+
if (dev) {
|
|
16
|
+
switch (this.packageManager) {
|
|
17
|
+
case 'npm':
|
|
18
|
+
return `${this.packageManager} install --save-dev ${dependency}`;
|
|
19
|
+
default:
|
|
20
|
+
return `${this.packageManager} add -D ${dependency}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
switch (this.packageManager) {
|
|
25
|
+
case 'npm':
|
|
26
|
+
return `${this.packageManager} install ${dependency}`;
|
|
27
|
+
default:
|
|
28
|
+
return `${this.packageManager} add ${dependency}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
static run(cmd) {
|
|
33
|
+
switch (this.packageManager) {
|
|
34
|
+
case 'npm':
|
|
35
|
+
return `npm run ${cmd}`;
|
|
36
|
+
default:
|
|
37
|
+
return `${this.packageManager} ${cmd}`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.default = PackageManager;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.default = cliPrompt;
|
|
27
|
+
const readline = __importStar(require("node:readline"));
|
|
28
|
+
const input = process.stdin;
|
|
29
|
+
const output = process.stdout;
|
|
30
|
+
async function cliPrompt(question) {
|
|
31
|
+
const rl = readline.createInterface({
|
|
32
|
+
input,
|
|
33
|
+
output,
|
|
34
|
+
});
|
|
35
|
+
return new Promise(resolve => {
|
|
36
|
+
if (process.env.BYPASS_CLI_PROMPT === '1')
|
|
37
|
+
resolve('');
|
|
38
|
+
else {
|
|
39
|
+
rl.question(question, value => {
|
|
40
|
+
rl.close();
|
|
41
|
+
resolve(value);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const yoctocolors_1 = __importDefault(require("yoctocolors"));
|
|
30
|
+
const readline = __importStar(require("readline"));
|
|
31
|
+
const input = process.stdin;
|
|
32
|
+
const output = process.stdout;
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
class CLISelect {
|
|
35
|
+
question;
|
|
36
|
+
selectIndex = 0;
|
|
37
|
+
options;
|
|
38
|
+
selector = '>';
|
|
39
|
+
isFirstTimeShowMenu = true;
|
|
40
|
+
cb = null;
|
|
41
|
+
constructor(question, options) {
|
|
42
|
+
this.question = question;
|
|
43
|
+
this.options = options;
|
|
44
|
+
}
|
|
45
|
+
async run() {
|
|
46
|
+
this.init();
|
|
47
|
+
return new Promise(accept => {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
|
|
49
|
+
this.cb = accept;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
keyPressedHandler(_, key) {
|
|
53
|
+
if (key) {
|
|
54
|
+
const optionLength = this.options.length - 1;
|
|
55
|
+
if (key.name === 'down' && this.selectIndex < optionLength) {
|
|
56
|
+
this.selectIndex += 1;
|
|
57
|
+
this.createOptionMenu();
|
|
58
|
+
}
|
|
59
|
+
else if (key.name === 'up' && this.selectIndex > 0) {
|
|
60
|
+
this.selectIndex -= 1;
|
|
61
|
+
this.createOptionMenu();
|
|
62
|
+
}
|
|
63
|
+
else if (key.name === 'return') {
|
|
64
|
+
this.cb?.(this.options[this.selectIndex]);
|
|
65
|
+
input.removeAllListeners('keypress');
|
|
66
|
+
output.write('\n');
|
|
67
|
+
input.setRawMode(false);
|
|
68
|
+
input.pause();
|
|
69
|
+
}
|
|
70
|
+
else if (key.name === 'escape' || (key.name === 'c' && key.ctrl)) {
|
|
71
|
+
this.close();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
ansiEraseLines(count) {
|
|
76
|
+
//adapted from sindresorhus ansi-escape module
|
|
77
|
+
const ESC = '\u001B[';
|
|
78
|
+
const eraseLine = ESC + '2K';
|
|
79
|
+
const cursorUp = (count = 1) => ESC + count + 'A';
|
|
80
|
+
const cursorLeft = ESC + 'G';
|
|
81
|
+
let clear = '';
|
|
82
|
+
for (let i = 0; i < count; i++) {
|
|
83
|
+
clear += eraseLine + (i < count - 1 ? cursorUp() : '');
|
|
84
|
+
}
|
|
85
|
+
if (count) {
|
|
86
|
+
clear += cursorLeft;
|
|
87
|
+
}
|
|
88
|
+
return clear;
|
|
89
|
+
}
|
|
90
|
+
init() {
|
|
91
|
+
console.log(`\n${this.question}`);
|
|
92
|
+
readline.emitKeypressEvents(input);
|
|
93
|
+
this.start();
|
|
94
|
+
}
|
|
95
|
+
start() {
|
|
96
|
+
//setup the input for reading
|
|
97
|
+
input.setRawMode(true);
|
|
98
|
+
input.resume();
|
|
99
|
+
input.on('keypress', (event, key) => this.keyPressedHandler(event, key));
|
|
100
|
+
if (this.selectIndex >= 0) {
|
|
101
|
+
this.createOptionMenu();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
close = () => {
|
|
105
|
+
input.setRawMode(false);
|
|
106
|
+
input.pause();
|
|
107
|
+
process.exit(0);
|
|
108
|
+
};
|
|
109
|
+
getPadding(num = 10) {
|
|
110
|
+
let text = ' ';
|
|
111
|
+
for (let i = 0; i < num; i++) {
|
|
112
|
+
text += ' ';
|
|
113
|
+
}
|
|
114
|
+
return text;
|
|
115
|
+
}
|
|
116
|
+
createOptionMenu() {
|
|
117
|
+
const optionLength = this.options.length;
|
|
118
|
+
if (this.isFirstTimeShowMenu) {
|
|
119
|
+
this.isFirstTimeShowMenu = false;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
output.write(this.ansiEraseLines(optionLength));
|
|
123
|
+
}
|
|
124
|
+
const padding = this.getPadding(0);
|
|
125
|
+
const cursor = yoctocolors_1.default.magenta(this.selector);
|
|
126
|
+
for (let i = 0; i < optionLength; i++) {
|
|
127
|
+
const selectedOption = i === this.selectIndex
|
|
128
|
+
? `${cursor} ${this.options[i]}`
|
|
129
|
+
: `${cursor.replace(/.*/, ' ')} ${this.options[i]}`;
|
|
130
|
+
const ending = i !== optionLength - 1 ? '\n' : '';
|
|
131
|
+
output.write(padding + selectedOption + ending);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.default = CLISelect;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = colorize;
|
|
7
|
+
const yoctocolors_1 = __importDefault(require("yoctocolors"));
|
|
8
|
+
function colorize(text, { color, bgColor, }) {
|
|
9
|
+
const foregroundApplied = color ? yoctocolors_1.default[color](text) : text;
|
|
10
|
+
return bgColor ? yoctocolors_1.default[bgColor](foregroundApplied) : foregroundApplied;
|
|
11
|
+
}
|
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const dream_1 = require("@rvoh/dream");
|
|
7
7
|
const index_js_1 = __importDefault(require("../bin/index.js"));
|
|
8
|
+
const reduxBindings_js_1 = __importDefault(require("../generate/openapi/reduxBindings.js"));
|
|
9
|
+
const syncEnums_js_1 = __importDefault(require("../generate/initializer/syncEnums.js"));
|
|
8
10
|
class PsychicCLI {
|
|
9
11
|
static provide(program, { initializePsychicApp, seedDb, }) {
|
|
10
12
|
dream_1.DreamCLI.generateDreamCli(program, {
|
|
@@ -37,6 +39,37 @@ class PsychicCLI {
|
|
|
37
39
|
await index_js_1.default.generateController(controllerName, actions);
|
|
38
40
|
process.exit();
|
|
39
41
|
});
|
|
42
|
+
program
|
|
43
|
+
.command('generate:openapi:redux')
|
|
44
|
+
.alias('g:openapi:redux')
|
|
45
|
+
.description('generates openapi redux bindings to connect one of your openapi files to one of your clients')
|
|
46
|
+
.option('--schema-file', 'the path from your api root to the openapi file you wish to use to generate your schema, i.e. ./openapi/openapi.json')
|
|
47
|
+
.option('--api-file', 'the path to the boilerplate api file that will be used to scaffold your backend endpoints together with, i.e. ../client/app/api.ts')
|
|
48
|
+
.option('--api-import', 'the camelCased name of the export from your api module, i.e. emptyBackendApi')
|
|
49
|
+
.option('--output-file', 'the path to the file that will contain your typescript openapi redux bindings, i.e. ../client/app/myBackendApi.ts')
|
|
50
|
+
.option('--export-name', 'the camelCased name to use for your exported api, i.e. myBackendApi')
|
|
51
|
+
.action(async ({ schemaFile, apiFile, apiImport, outputFile, exportName, }) => {
|
|
52
|
+
await initializePsychicApp();
|
|
53
|
+
await (0, reduxBindings_js_1.default)({
|
|
54
|
+
exportName,
|
|
55
|
+
schemaFile,
|
|
56
|
+
apiFile,
|
|
57
|
+
apiImport,
|
|
58
|
+
outputFile,
|
|
59
|
+
});
|
|
60
|
+
process.exit();
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command('generate:initializer:sync-enums')
|
|
64
|
+
.alias('g:initializer:sync-enums')
|
|
65
|
+
.description('generates an initializer in your app for syncing enums to a particular path.')
|
|
66
|
+
.argument('<outfile>', 'the path from your backend directory to the location which you want the enums copied. Should end with .ts, i.e. "../client/src/api/enums.ts"')
|
|
67
|
+
.option('--initializer-filename', 'the name you want the file to be in your initializers folder. defaults to `sync-enums.ts`')
|
|
68
|
+
.action(async (outfile, { initializerName, }) => {
|
|
69
|
+
await initializePsychicApp();
|
|
70
|
+
await (0, syncEnums_js_1.default)(outfile, initializerName);
|
|
71
|
+
process.exit();
|
|
72
|
+
});
|
|
40
73
|
program
|
|
41
74
|
.command('routes')
|
|
42
75
|
.description('examines your current models, building a type-map of the associations so that the ORM can understand your relational setup. This is commited to your repo, and synced to the dream repo for consumption within the underlying library.')
|
|
@@ -75,6 +108,15 @@ class PsychicCLI {
|
|
|
75
108
|
await index_js_1.default.syncOpenapiJson();
|
|
76
109
|
process.exit();
|
|
77
110
|
});
|
|
111
|
+
program
|
|
112
|
+
.command('sync:client:enums')
|
|
113
|
+
.argument('<outfile>', 'the path from your backend directory to the location which you want the enums copied. Should end with .ts, i.e. "../client/src/api/enums.ts"')
|
|
114
|
+
.description('syncs your backend enums to a location of your choosing')
|
|
115
|
+
.action(async (outfile) => {
|
|
116
|
+
await initializePsychicApp();
|
|
117
|
+
await index_js_1.default.syncClientEnums(outfile);
|
|
118
|
+
process.exit();
|
|
119
|
+
});
|
|
78
120
|
}
|
|
79
121
|
}
|
|
80
122
|
exports.default = PsychicCLI;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = printFinalStepsMessage;
|
|
7
|
+
const dream_1 = require("@rvoh/dream");
|
|
8
|
+
const colorize_js_1 = __importDefault(require("../../../cli/helpers/colorize.js"));
|
|
9
|
+
function printFinalStepsMessage(opts) {
|
|
10
|
+
const importLine = (0, colorize_js_1.default)(`+ import { ${opts.exportName} } from '${opts.apiFile}'`, { color: 'green' });
|
|
11
|
+
const reducerLine = (0, colorize_js_1.default)(` + [${opts.exportName}.reducerPath]: ${opts.exportName}.reducer,`, {
|
|
12
|
+
color: 'green',
|
|
13
|
+
});
|
|
14
|
+
const middlewareLine = (0, colorize_js_1.default)(` + middleware: gDM => gDM().concat(${opts.exportName}.middleware),`, {
|
|
15
|
+
color: 'green',
|
|
16
|
+
});
|
|
17
|
+
dream_1.DreamCLI.logger.log(`
|
|
18
|
+
Finished generating redux bindings for your application,
|
|
19
|
+
but to wire them into your app, we're going to need your help.
|
|
20
|
+
Be sure to add the following to your root store to bind your new
|
|
21
|
+
api module into your redux app, i.e.
|
|
22
|
+
|
|
23
|
+
// add this line, fix the import path if necessary
|
|
24
|
+
${importLine}
|
|
25
|
+
|
|
26
|
+
export const store = configureStore({
|
|
27
|
+
reducer: {
|
|
28
|
+
// ...
|
|
29
|
+
// add this line
|
|
30
|
+
${reducerLine}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// add this line
|
|
34
|
+
${middlewareLine}
|
|
35
|
+
})
|
|
36
|
+
`, { logPrefix: '' });
|
|
37
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = promptForOptions;
|
|
7
|
+
const dream_1 = require("@rvoh/dream");
|
|
8
|
+
const cli_prompt_js_1 = __importDefault(require("../../../cli/helpers/cli-prompt.js"));
|
|
9
|
+
const index_js_1 = __importDefault(require("../../../psychic-app/index.js"));
|
|
10
|
+
async function promptForOptions(options) {
|
|
11
|
+
if (!options.schemaFile) {
|
|
12
|
+
const defaultVal = './openapi/openapi.json';
|
|
13
|
+
const answer = await (0, cli_prompt_js_1.default)(`\
|
|
14
|
+
What would you like the schemaFile to be?
|
|
15
|
+
|
|
16
|
+
The schemaFile is the openapi file that @rtk-query/codegen-openapi will read to produce
|
|
17
|
+
all of its redux bindings. If not provided, it will default to
|
|
18
|
+
|
|
19
|
+
${defaultVal}
|
|
20
|
+
`);
|
|
21
|
+
options.schemaFile = answer || defaultVal;
|
|
22
|
+
}
|
|
23
|
+
if (!options.exportName) {
|
|
24
|
+
const defaultVal = `${(0, dream_1.camelize)(index_js_1.default.getOrFail().appName)}Api`;
|
|
25
|
+
const answer = await (0, cli_prompt_js_1.default)(`\
|
|
26
|
+
What would you like the exportName to be?
|
|
27
|
+
|
|
28
|
+
The exportName is used to name the final output of the @rtk-query/codegen-openapi utility.
|
|
29
|
+
It will encase all of the backend endpoints that have been consumed via the specified openapi
|
|
30
|
+
file. We recommend naming it something like the name of your app, i.e.
|
|
31
|
+
|
|
32
|
+
${defaultVal}
|
|
33
|
+
`);
|
|
34
|
+
options.exportName = answer || defaultVal;
|
|
35
|
+
}
|
|
36
|
+
if (!options.outputFile) {
|
|
37
|
+
const defaultVal = `../client/app/api/${(0, dream_1.camelize)(options.exportName)}.ts`;
|
|
38
|
+
const answer = await (0, cli_prompt_js_1.default)(`\
|
|
39
|
+
What would you like the outputFile to be?
|
|
40
|
+
|
|
41
|
+
The outputFile is the path to the generated openapi redux bindings. If not provided,
|
|
42
|
+
it will default to:
|
|
43
|
+
|
|
44
|
+
${defaultVal}
|
|
45
|
+
`);
|
|
46
|
+
options.outputFile = answer || defaultVal;
|
|
47
|
+
}
|
|
48
|
+
if (!options.apiFile) {
|
|
49
|
+
const defaultVal = '../client/app/api/api.ts';
|
|
50
|
+
const answer = await (0, cli_prompt_js_1.default)(`\
|
|
51
|
+
What would you like the path to your apiFile to be?
|
|
52
|
+
|
|
53
|
+
The apiFile option specifies which base api file to use to mix in your backend endpoints with.
|
|
54
|
+
This option is provided by the @rtk-query/codegen-openapi library to enable you to define
|
|
55
|
+
custom api behavior, such as defining a base url, adding header preparation steps, etc...
|
|
56
|
+
|
|
57
|
+
We expect you to provide this path with the api root in mind, so you will need to consider
|
|
58
|
+
how to travel to the desired filepath from within your psychic project, i.e.
|
|
59
|
+
|
|
60
|
+
${defaultVal}
|
|
61
|
+
`);
|
|
62
|
+
options.apiFile = answer || defaultVal;
|
|
63
|
+
}
|
|
64
|
+
if (!options.apiImport) {
|
|
65
|
+
const defaultVal = `empty${(0, dream_1.pascalize)(options.exportName)}`;
|
|
66
|
+
const answer = await (0, cli_prompt_js_1.default)(`\
|
|
67
|
+
What would you like the path to your apiImport to be?
|
|
68
|
+
|
|
69
|
+
The apiImport option specifies the export key for the api module being exported from the
|
|
70
|
+
file found at the apiFile path.
|
|
71
|
+
|
|
72
|
+
This option is provided by the @rtk-query/codegen-openapi library to inform it of which
|
|
73
|
+
named export in your apiFile it should be mixing your backend api with. If not provided,
|
|
74
|
+
it will default to
|
|
75
|
+
|
|
76
|
+
${defaultVal}
|
|
77
|
+
`);
|
|
78
|
+
options.apiImport = answer || defaultVal;
|
|
79
|
+
}
|
|
80
|
+
return options;
|
|
81
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.default = writeApiFile;
|
|
27
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
28
|
+
const path = __importStar(require("node:path"));
|
|
29
|
+
async function writeApiFile({ apiFile, apiImport }) {
|
|
30
|
+
const destDir = path.dirname(apiFile);
|
|
31
|
+
try {
|
|
32
|
+
await fs.access(apiFile);
|
|
33
|
+
return; // early return if the file already exists
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// noop
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await fs.access(destDir);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
const contents = `\
|
|
45
|
+
// Or from '@reduxjs/toolkit/query' if not using the auto-generated hooks
|
|
46
|
+
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
|
47
|
+
import { RootState } from '../store' // update this to the correct path to your app's store
|
|
48
|
+
|
|
49
|
+
function baseUrl() {
|
|
50
|
+
// add custom code here for determining your application's baseUrl
|
|
51
|
+
// this would generally be something different, depending on if you
|
|
52
|
+
// are in dev/test/production environments. For dev, you might want
|
|
53
|
+
// http://localhost:7777, while test may be http://localhost:7778, or
|
|
54
|
+
// some other port, depending on how you have your spec hooks configured.
|
|
55
|
+
// for production, it should be the real host for your application, i.e.
|
|
56
|
+
// https://myapi.com
|
|
57
|
+
|
|
58
|
+
return 'http://localhost:7777'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// initialize an empty api service that we'll inject endpoints into later as needed
|
|
62
|
+
export const ${apiImport} = createApi({
|
|
63
|
+
// forces cache to bust any time a component is mounted
|
|
64
|
+
refetchOnMountOrArgChange: true,
|
|
65
|
+
keepUnusedDataFor: 0,
|
|
66
|
+
|
|
67
|
+
baseQuery: fetchBaseQuery({
|
|
68
|
+
baseUrl: baseURL(),
|
|
69
|
+
credentials: 'include',
|
|
70
|
+
|
|
71
|
+
// we recommend that you use a function like this for preparing
|
|
72
|
+
// headers, so that you can make sure any necessary auth tokens
|
|
73
|
+
// used by your app can be applied to the headers when any requests
|
|
74
|
+
// are made to your backend api.
|
|
75
|
+
// prepareHeaders: (headers, { getState }) => {
|
|
76
|
+
// return new Promise(resolve => {
|
|
77
|
+
// function checkToken() {
|
|
78
|
+
// const token = (getState() as RootState).app.authToken
|
|
79
|
+
// if (token) {
|
|
80
|
+
// headers.set('Authorization', \`Bearer \${token}\`)
|
|
81
|
+
// resolve(headers)
|
|
82
|
+
// } else {
|
|
83
|
+
// setTimeout(checkToken, 500) // try again in 500ms
|
|
84
|
+
// }
|
|
85
|
+
// }
|
|
86
|
+
// checkToken()
|
|
87
|
+
// })
|
|
88
|
+
// },
|
|
89
|
+
}),
|
|
90
|
+
endpoints: () => ({}),
|
|
91
|
+
})`;
|
|
92
|
+
await fs.writeFile(apiFile, contents);
|
|
93
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.default = writeInitializer;
|
|
30
|
+
const dream_1 = require("@rvoh/dream");
|
|
31
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
32
|
+
const path = __importStar(require("node:path"));
|
|
33
|
+
const psychicPath_js_1 = __importDefault(require("../../../helpers/path/psychicPath.js"));
|
|
34
|
+
async function writeInitializer({ exportName }) {
|
|
35
|
+
const pascalized = (0, dream_1.pascalize)(exportName);
|
|
36
|
+
const camelized = (0, dream_1.camelize)(exportName);
|
|
37
|
+
const destDir = path.join((0, psychicPath_js_1.default)('conf'), 'initializers', 'openapi');
|
|
38
|
+
const initializerFilename = `${camelized}.ts`;
|
|
39
|
+
const initializerPath = path.join(destDir, initializerFilename);
|
|
40
|
+
try {
|
|
41
|
+
await fs.access(initializerPath);
|
|
42
|
+
return; // early return if the file already exists
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// noop
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
await fs.access(destDir);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
const filePath = path.join('.', 'src', 'conf', 'openapi', `${camelized}.openapi-codegen.json`);
|
|
54
|
+
const contents = `\
|
|
55
|
+
import { DreamCLI } from '@rvoh/dream'
|
|
56
|
+
import { PsychicApp } from '@rvoh/psychic'
|
|
57
|
+
import AppEnv from '../AppEnv.js'
|
|
58
|
+
|
|
59
|
+
export default function initialize${pascalized}(psy: PsychicApp) {
|
|
60
|
+
psy.on('sync', async () => {
|
|
61
|
+
if (AppEnv.isDevelopmentOrTest) {
|
|
62
|
+
DreamCLI.logger.logStartProgress(\`[${camelized}] syncing...\`)
|
|
63
|
+
await DreamCLI.spawn('npx @rtk-query/codegen-openapi ${filePath}', {
|
|
64
|
+
onStdout: message => {
|
|
65
|
+
DreamCLI.logger.logContinueProgress(\`[${camelized}]\` + ' ' + message, {
|
|
66
|
+
logPrefixColor: 'green',
|
|
67
|
+
})
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
DreamCLI.logger.logEndProgress()
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}\
|
|
74
|
+
`;
|
|
75
|
+
await fs.writeFile(initializerPath, contents);
|
|
76
|
+
}
|