@rvoh/psychic 3.0.0-alpha.8 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/cli/index.js +4 -22
- package/dist/cjs/src/controller/index.js +15 -2
- package/dist/cjs/src/generate/controller.js +3 -3
- package/dist/cjs/src/generate/helpers/generateResourceControllerSpecContent.js +6 -5
- package/dist/cjs/src/openapi-renderer/body-segment.js +0 -1
- package/dist/cjs/src/openapi-renderer/endpoint.js +13 -29
- package/dist/cjs/src/openapi-renderer/helpers/safelyAttachCursorPaginationParamToRequestBodySegment.js +3 -15
- package/dist/cjs/src/openapi-renderer/helpers/safelyAttachPaginationParamsToBodySegment.js +14 -1
- package/dist/cjs/src/router/helpers.js +12 -28
- package/dist/cjs/src/router/index.js +2 -7
- package/dist/cjs/src/server/params.js +71 -89
- package/dist/esm/src/cli/index.js +4 -22
- package/dist/esm/src/controller/index.js +15 -2
- package/dist/esm/src/generate/controller.js +3 -3
- package/dist/esm/src/generate/helpers/generateResourceControllerSpecContent.js +6 -5
- package/dist/esm/src/openapi-renderer/body-segment.js +0 -1
- package/dist/esm/src/openapi-renderer/endpoint.js +13 -29
- package/dist/esm/src/openapi-renderer/helpers/safelyAttachCursorPaginationParamToRequestBodySegment.js +3 -15
- package/dist/esm/src/openapi-renderer/helpers/safelyAttachPaginationParamsToBodySegment.js +14 -1
- package/dist/esm/src/router/helpers.js +12 -28
- package/dist/esm/src/router/index.js +2 -7
- package/dist/esm/src/server/params.js +71 -89
- package/dist/types/src/controller/index.d.ts +12 -1
- package/dist/types/src/openapi-renderer/helpers/safelyAttachCursorPaginationParamToRequestBodySegment.d.ts +1 -1
- package/dist/types/src/openapi-renderer/helpers/safelyAttachPaginationParamsToBodySegment.d.ts +6 -0
- package/dist/types/src/router/helpers.d.ts +3 -0
- package/package.json +30 -20
- package/dist/cjs/src/generate/helpers/zustandBindings/printFinalStepsMessage.js +0 -36
- package/dist/cjs/src/generate/helpers/zustandBindings/promptForOptions.js +0 -55
- package/dist/cjs/src/generate/helpers/zustandBindings/writeApiClientFile.js +0 -50
- package/dist/cjs/src/generate/helpers/zustandBindings/writeInitializer.js +0 -46
- package/dist/cjs/src/generate/openapi/zustandBindings.js +0 -27
- package/dist/esm/src/generate/helpers/zustandBindings/printFinalStepsMessage.js +0 -36
- package/dist/esm/src/generate/helpers/zustandBindings/promptForOptions.js +0 -55
- package/dist/esm/src/generate/helpers/zustandBindings/writeApiClientFile.js +0 -50
- package/dist/esm/src/generate/helpers/zustandBindings/writeInitializer.js +0 -46
- package/dist/esm/src/generate/openapi/zustandBindings.js +0 -27
- package/dist/types/src/generate/helpers/zustandBindings/printFinalStepsMessage.d.ts +0 -2
- package/dist/types/src/generate/helpers/zustandBindings/promptForOptions.d.ts +0 -2
- package/dist/types/src/generate/helpers/zustandBindings/writeApiClientFile.d.ts +0 -5
- package/dist/types/src/generate/helpers/zustandBindings/writeInitializer.d.ts +0 -5
- package/dist/types/src/generate/openapi/zustandBindings.d.ts +0 -21
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": "3.0.
|
|
5
|
+
"version": "3.0.1",
|
|
6
6
|
"author": "RVOHealth",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -47,6 +47,24 @@
|
|
|
47
47
|
"files": [
|
|
48
48
|
"dist/**/*"
|
|
49
49
|
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"client": "cd client && pnpm start",
|
|
52
|
+
"client:fspec": "cd client && VITE_PSYCHIC_ENV=test BROWSER=none pnpm start",
|
|
53
|
+
"psy": "NODE_ENV=${NODE_ENV:-test} pnpm psyts",
|
|
54
|
+
"psyjs": "node ./dist/test-app/src/cli/index.js",
|
|
55
|
+
"psyts": "NODE_ENV=${NODE_ENV:-test} tsx ./test-app/src/cli/index.ts",
|
|
56
|
+
"gpsy": "tsx ./global-cli/main.ts",
|
|
57
|
+
"build": "echo \"building cjs...\" && rm -rf dist && tsc -p ./tsconfig.cjs.build.json && echo \"building esm...\" && tsc -p ./tsconfig.esm.build.json",
|
|
58
|
+
"build:test-app": "rm -rf dist && echo \"building test app to esm...\" && tsc -p ./tsconfig.esm.build.test-app.json && echo \"building test app to cjs...\" && tsc -p ./tsconfig.cjs.build.test-app.json",
|
|
59
|
+
"types:esm:trace": "rm -rf dist && tsc -p ./tsconfig.esm.build.json --generateTrace ./typetrace --diagnostics && pnpm analyze-trace ./typetrace --skipMillis 100 --forceMillis 300",
|
|
60
|
+
"dev": "nodemon --quiet --no-stdin",
|
|
61
|
+
"console": "tsx ./test-app/src/conf/repl.ts",
|
|
62
|
+
"uspec": "vitest --config ./spec/unit/vite.config.ts",
|
|
63
|
+
"fspec": "vitest run --config=./spec/features/vite.config.ts",
|
|
64
|
+
"format": "pnpm prettier . --write",
|
|
65
|
+
"lint": "pnpm eslint --no-warn-ignored \"src/**/*.ts\" \"spec/**/*.ts\" \"test-app/**/*.ts\" && pnpm prettier . --check",
|
|
66
|
+
"prepack": "pnpm build"
|
|
67
|
+
},
|
|
50
68
|
"dependencies": {
|
|
51
69
|
"ajv": "^8.17.1",
|
|
52
70
|
"ajv-formats": "^3.0.1",
|
|
@@ -74,9 +92,9 @@
|
|
|
74
92
|
"@koa/cors": "^5.0.0",
|
|
75
93
|
"@koa/etag": "^5.0.2",
|
|
76
94
|
"@koa/router": "^15.3.1",
|
|
77
|
-
"@rvoh/dream": "^2.4.0
|
|
95
|
+
"@rvoh/dream": "^2.4.0",
|
|
78
96
|
"@rvoh/dream-spec-helpers": "^2.1.1",
|
|
79
|
-
"@rvoh/psychic-spec-helpers": "3.0.0
|
|
97
|
+
"@rvoh/psychic-spec-helpers": "3.0.0",
|
|
80
98
|
"@types/koa": "^3.0.1",
|
|
81
99
|
"@types/koa-bodyparser": "^4.3.12",
|
|
82
100
|
"@types/koa-conditional-get": "^2.0.3",
|
|
@@ -120,21 +138,13 @@
|
|
|
120
138
|
"vitest": "^4.0.9",
|
|
121
139
|
"winston": "^3.14.2"
|
|
122
140
|
},
|
|
123
|
-
"
|
|
124
|
-
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"build:test-app": "rm -rf dist && echo \"building test app to esm...\" && tsc -p ./tsconfig.esm.build.test-app.json && echo \"building test app to cjs...\" && tsc -p ./tsconfig.cjs.build.test-app.json",
|
|
132
|
-
"types:esm:trace": "rm -rf dist && tsc -p ./tsconfig.esm.build.json --generateTrace ./typetrace --diagnostics && pnpm analyze-trace ./typetrace --skipMillis 100 --forceMillis 300",
|
|
133
|
-
"dev": "nodemon --quiet --no-stdin",
|
|
134
|
-
"console": "tsx ./test-app/src/conf/repl.ts",
|
|
135
|
-
"uspec": "vitest --config ./spec/unit/vite.config.ts",
|
|
136
|
-
"fspec": "vitest run --config=./spec/features/vite.config.ts",
|
|
137
|
-
"format": "pnpm prettier . --write",
|
|
138
|
-
"lint": "pnpm eslint --no-warn-ignored \"src/**/*.ts\" \"spec/**/*.ts\" \"test-app/**/*.ts\" && pnpm prettier . --check"
|
|
141
|
+
"packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402",
|
|
142
|
+
"pnpm": {
|
|
143
|
+
"overrides": {
|
|
144
|
+
"diff": ">=8.0.3",
|
|
145
|
+
"minimatch@3": "3.1.3",
|
|
146
|
+
"minimatch@5": "5.1.7",
|
|
147
|
+
"minimatch@9": "9.0.6"
|
|
148
|
+
}
|
|
139
149
|
}
|
|
140
|
-
}
|
|
150
|
+
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { DreamCLI } from '@rvoh/dream/system';
|
|
2
|
-
import { camelize } from '@rvoh/dream/utils';
|
|
3
|
-
import colorize from '../../../cli/helpers/colorize.js';
|
|
4
|
-
export default function printFinalStepsMessage(opts) {
|
|
5
|
-
const clientFile = `${opts.outputDir}/${camelize(opts.exportName)}.ts`;
|
|
6
|
-
const importLine = colorize(`+ import { ${opts.exportName} } from '${clientFile}'`, { color: 'green' });
|
|
7
|
-
DreamCLI.logger.log(`
|
|
8
|
-
Finished generating zustand + openapi-fetch bindings for your application,
|
|
9
|
-
but to wire them into your app, we're going to need your help.
|
|
10
|
-
|
|
11
|
-
First, you will need to be sure to sync, so that the new openapi
|
|
12
|
-
types are sent over to your client application.
|
|
13
|
-
|
|
14
|
-
Next, you can use the generated api client in your zustand stores, i.e.
|
|
15
|
-
|
|
16
|
-
${importLine}
|
|
17
|
-
|
|
18
|
-
${colorize(`+ import { create } from 'zustand'`, { color: 'green' })}
|
|
19
|
-
|
|
20
|
-
interface MyState {
|
|
21
|
-
items: Item[]
|
|
22
|
-
loading: boolean
|
|
23
|
-
fetchItems: () => Promise<void>
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const useMyStore = create<MyState>((set) => ({
|
|
27
|
-
items: [],
|
|
28
|
-
loading: false,
|
|
29
|
-
fetchItems: async () => {
|
|
30
|
-
set({ loading: true })
|
|
31
|
-
const { data } = await ${opts.exportName}.GET('/items')
|
|
32
|
-
set({ items: data ?? [], loading: false })
|
|
33
|
-
},
|
|
34
|
-
}))
|
|
35
|
-
`, { logPrefix: '' });
|
|
36
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { camelize } from '@rvoh/dream/utils';
|
|
2
|
-
import cliPrompt from '../../../cli/helpers/cli-prompt.js';
|
|
3
|
-
import PsychicApp from '../../../psychic-app/index.js';
|
|
4
|
-
export default async function promptForOptions(options) {
|
|
5
|
-
if (!options.schemaFile) {
|
|
6
|
-
const defaultVal = './src/openapi/openapi.json';
|
|
7
|
-
const answer = await cliPrompt(`\
|
|
8
|
-
What would you like the schemaFile to be?
|
|
9
|
-
|
|
10
|
-
The schemaFile is the openapi file that openapi-typescript will read to produce
|
|
11
|
-
all of its type definitions. If not provided, it will default to
|
|
12
|
-
|
|
13
|
-
${defaultVal}
|
|
14
|
-
`);
|
|
15
|
-
options.schemaFile = answer || defaultVal;
|
|
16
|
-
}
|
|
17
|
-
if (!options.exportName) {
|
|
18
|
-
const defaultVal = `${camelize(PsychicApp.getOrFail().appName)}Api`;
|
|
19
|
-
const answer = await cliPrompt(`\
|
|
20
|
-
What would you like the exportName to be?
|
|
21
|
-
|
|
22
|
-
The exportName is used to name the typed openapi-fetch client instance that will
|
|
23
|
-
be generated for use in your zustand stores. We recommend naming it something like
|
|
24
|
-
the name of your app, i.e.
|
|
25
|
-
|
|
26
|
-
${defaultVal}
|
|
27
|
-
`);
|
|
28
|
-
options.exportName = answer || defaultVal;
|
|
29
|
-
}
|
|
30
|
-
if (!options.outputDir) {
|
|
31
|
-
const defaultVal = '../client/src/api';
|
|
32
|
-
const answer = await cliPrompt(`\
|
|
33
|
-
What would you like the outputDir to be?
|
|
34
|
-
|
|
35
|
-
The outputDir is the directory where the generated api client and types files
|
|
36
|
-
will be written. If not provided, it will default to:
|
|
37
|
-
|
|
38
|
-
${defaultVal}
|
|
39
|
-
`);
|
|
40
|
-
options.outputDir = answer || defaultVal;
|
|
41
|
-
}
|
|
42
|
-
if (!options.typesFile) {
|
|
43
|
-
const defaultVal = `${options.outputDir}/${camelize(options.exportName)}.types.d.ts`;
|
|
44
|
-
const answer = await cliPrompt(`\
|
|
45
|
-
What would you like the typesFile to be?
|
|
46
|
-
|
|
47
|
-
The typesFile is the path to the generated openapi TypeScript type definitions
|
|
48
|
-
that will be used by the api client. If not provided, it will default to:
|
|
49
|
-
|
|
50
|
-
${defaultVal}
|
|
51
|
-
`);
|
|
52
|
-
options.typesFile = answer || defaultVal;
|
|
53
|
-
}
|
|
54
|
-
return options;
|
|
55
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { camelize } from '@rvoh/dream/utils';
|
|
4
|
-
export default async function writeApiClientFile({ exportName, outputDir, typesFile, }) {
|
|
5
|
-
const destDir = outputDir;
|
|
6
|
-
const destPath = path.join(destDir, `${camelize(exportName)}.ts`);
|
|
7
|
-
try {
|
|
8
|
-
await fs.access(destPath);
|
|
9
|
-
return; // early return if the file already exists
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
// noop
|
|
13
|
-
}
|
|
14
|
-
try {
|
|
15
|
-
await fs.access(destDir);
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
await fs.mkdir(destDir, { recursive: true });
|
|
19
|
-
}
|
|
20
|
-
// compute the relative import path from the output file to the types file
|
|
21
|
-
const typesRelative = path.relative(destDir, typesFile).replace(/\.d\.ts$/, '.js');
|
|
22
|
-
const typesImportPath = typesRelative.startsWith('.') ? typesRelative : `./${typesRelative}`;
|
|
23
|
-
const contents = `\
|
|
24
|
-
import createClient from 'openapi-fetch'
|
|
25
|
-
import type { paths } from '${typesImportPath}'
|
|
26
|
-
|
|
27
|
-
function baseUrl() {
|
|
28
|
-
// add custom code here for determining your application's baseUrl
|
|
29
|
-
// this would generally be something different, depending on if you
|
|
30
|
-
// are in dev/test/production environments. For dev, you might want
|
|
31
|
-
// http://localhost:7777, while test may be http://localhost:7778, or
|
|
32
|
-
// some other port, depending on how you have your spec hooks configured.
|
|
33
|
-
// for production, it should be the real host for your application, i.e.
|
|
34
|
-
// https://myapi.com
|
|
35
|
-
|
|
36
|
-
return 'http://localhost:7777'
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const ${exportName} = createClient<paths>({
|
|
40
|
-
baseUrl: baseUrl(),
|
|
41
|
-
credentials: 'include',
|
|
42
|
-
|
|
43
|
-
// you may customize headers here, for example to add auth tokens:
|
|
44
|
-
// headers: {
|
|
45
|
-
// Authorization: \`Bearer \${getAuthToken()}\`,
|
|
46
|
-
// },
|
|
47
|
-
})
|
|
48
|
-
`;
|
|
49
|
-
await fs.writeFile(destPath, contents);
|
|
50
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { camelize, pascalize } from '@rvoh/dream/utils';
|
|
2
|
-
import * as fs from 'node:fs/promises';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import psychicPath from '../../../helpers/path/psychicPath.js';
|
|
5
|
-
export default async function writeInitializer({ exportName, schemaFile, typesFile, }) {
|
|
6
|
-
const pascalized = pascalize(exportName);
|
|
7
|
-
const camelized = camelize(exportName);
|
|
8
|
-
const destDir = path.join(psychicPath('conf'), 'initializers', 'openapi');
|
|
9
|
-
const initializerFilename = `${camelized}.ts`;
|
|
10
|
-
const initializerPath = path.join(destDir, initializerFilename);
|
|
11
|
-
try {
|
|
12
|
-
await fs.access(initializerPath);
|
|
13
|
-
return; // early return if the file already exists
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
// noop
|
|
17
|
-
}
|
|
18
|
-
try {
|
|
19
|
-
await fs.access(destDir);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
await fs.mkdir(destDir, { recursive: true });
|
|
23
|
-
}
|
|
24
|
-
const contents = `\
|
|
25
|
-
import { DreamCLI } from '@rvoh/dream/system'
|
|
26
|
-
import { PsychicApp } from '@rvoh/psychic'
|
|
27
|
-
import AppEnv from '../../AppEnv.js'
|
|
28
|
-
|
|
29
|
-
export default function initialize${pascalized}(psy: PsychicApp) {
|
|
30
|
-
psy.on('cli:sync', async () => {
|
|
31
|
-
if (AppEnv.isDevelopmentOrTest) {
|
|
32
|
-
DreamCLI.logger.logStartProgress(\`[${camelized}] syncing openapi types...\`)
|
|
33
|
-
await DreamCLI.spawn('npx openapi-typescript ${schemaFile} -o ${typesFile}', {
|
|
34
|
-
onStdout: message => {
|
|
35
|
-
DreamCLI.logger.logContinueProgress(\`[${camelized}]\` + ' ' + message, {
|
|
36
|
-
logPrefixColor: 'green',
|
|
37
|
-
})
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
DreamCLI.logger.logEndProgress()
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
}\
|
|
44
|
-
`;
|
|
45
|
-
await fs.writeFile(initializerPath, contents);
|
|
46
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { DreamCLI } from '@rvoh/dream/system';
|
|
2
|
-
import PackageManager from '../../cli/helpers/PackageManager.js';
|
|
3
|
-
import printFinalStepsMessage from '../helpers/zustandBindings/printFinalStepsMessage.js';
|
|
4
|
-
import promptForOptions from '../helpers/zustandBindings/promptForOptions.js';
|
|
5
|
-
import writeApiClientFile from '../helpers/zustandBindings/writeApiClientFile.js';
|
|
6
|
-
import writeInitializer from '../helpers/zustandBindings/writeInitializer.js';
|
|
7
|
-
/**
|
|
8
|
-
* @internal
|
|
9
|
-
*
|
|
10
|
-
* used by the psychic CLI to generate boilerplate
|
|
11
|
-
* that can be used to integrate a specific openapi.json
|
|
12
|
-
* file with a client using zustand + openapi-fetch.
|
|
13
|
-
*
|
|
14
|
-
* * generates a typed openapi-fetch client file
|
|
15
|
-
* * generates an initializer, which taps into the sync hooks
|
|
16
|
-
* to automatically run openapi-typescript to keep types in sync
|
|
17
|
-
* * prints a helpful message, instructing devs on the final
|
|
18
|
-
* steps for hooking into the newly-generated api mechanisms
|
|
19
|
-
* within their client application's zustand stores.
|
|
20
|
-
*/
|
|
21
|
-
export default async function generateOpenapiZustandBindings(options = {}) {
|
|
22
|
-
const opts = await promptForOptions(options);
|
|
23
|
-
await writeApiClientFile(opts);
|
|
24
|
-
await writeInitializer(opts);
|
|
25
|
-
await DreamCLI.spawn(PackageManager.add(['openapi-fetch', 'openapi-typescript'], { dev: true }));
|
|
26
|
-
printFinalStepsMessage(opts);
|
|
27
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { DreamCLI } from '@rvoh/dream/system';
|
|
2
|
-
import { camelize } from '@rvoh/dream/utils';
|
|
3
|
-
import colorize from '../../../cli/helpers/colorize.js';
|
|
4
|
-
export default function printFinalStepsMessage(opts) {
|
|
5
|
-
const clientFile = `${opts.outputDir}/${camelize(opts.exportName)}.ts`;
|
|
6
|
-
const importLine = colorize(`+ import { ${opts.exportName} } from '${clientFile}'`, { color: 'green' });
|
|
7
|
-
DreamCLI.logger.log(`
|
|
8
|
-
Finished generating zustand + openapi-fetch bindings for your application,
|
|
9
|
-
but to wire them into your app, we're going to need your help.
|
|
10
|
-
|
|
11
|
-
First, you will need to be sure to sync, so that the new openapi
|
|
12
|
-
types are sent over to your client application.
|
|
13
|
-
|
|
14
|
-
Next, you can use the generated api client in your zustand stores, i.e.
|
|
15
|
-
|
|
16
|
-
${importLine}
|
|
17
|
-
|
|
18
|
-
${colorize(`+ import { create } from 'zustand'`, { color: 'green' })}
|
|
19
|
-
|
|
20
|
-
interface MyState {
|
|
21
|
-
items: Item[]
|
|
22
|
-
loading: boolean
|
|
23
|
-
fetchItems: () => Promise<void>
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const useMyStore = create<MyState>((set) => ({
|
|
27
|
-
items: [],
|
|
28
|
-
loading: false,
|
|
29
|
-
fetchItems: async () => {
|
|
30
|
-
set({ loading: true })
|
|
31
|
-
const { data } = await ${opts.exportName}.GET('/items')
|
|
32
|
-
set({ items: data ?? [], loading: false })
|
|
33
|
-
},
|
|
34
|
-
}))
|
|
35
|
-
`, { logPrefix: '' });
|
|
36
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { camelize } from '@rvoh/dream/utils';
|
|
2
|
-
import cliPrompt from '../../../cli/helpers/cli-prompt.js';
|
|
3
|
-
import PsychicApp from '../../../psychic-app/index.js';
|
|
4
|
-
export default async function promptForOptions(options) {
|
|
5
|
-
if (!options.schemaFile) {
|
|
6
|
-
const defaultVal = './src/openapi/openapi.json';
|
|
7
|
-
const answer = await cliPrompt(`\
|
|
8
|
-
What would you like the schemaFile to be?
|
|
9
|
-
|
|
10
|
-
The schemaFile is the openapi file that openapi-typescript will read to produce
|
|
11
|
-
all of its type definitions. If not provided, it will default to
|
|
12
|
-
|
|
13
|
-
${defaultVal}
|
|
14
|
-
`);
|
|
15
|
-
options.schemaFile = answer || defaultVal;
|
|
16
|
-
}
|
|
17
|
-
if (!options.exportName) {
|
|
18
|
-
const defaultVal = `${camelize(PsychicApp.getOrFail().appName)}Api`;
|
|
19
|
-
const answer = await cliPrompt(`\
|
|
20
|
-
What would you like the exportName to be?
|
|
21
|
-
|
|
22
|
-
The exportName is used to name the typed openapi-fetch client instance that will
|
|
23
|
-
be generated for use in your zustand stores. We recommend naming it something like
|
|
24
|
-
the name of your app, i.e.
|
|
25
|
-
|
|
26
|
-
${defaultVal}
|
|
27
|
-
`);
|
|
28
|
-
options.exportName = answer || defaultVal;
|
|
29
|
-
}
|
|
30
|
-
if (!options.outputDir) {
|
|
31
|
-
const defaultVal = '../client/src/api';
|
|
32
|
-
const answer = await cliPrompt(`\
|
|
33
|
-
What would you like the outputDir to be?
|
|
34
|
-
|
|
35
|
-
The outputDir is the directory where the generated api client and types files
|
|
36
|
-
will be written. If not provided, it will default to:
|
|
37
|
-
|
|
38
|
-
${defaultVal}
|
|
39
|
-
`);
|
|
40
|
-
options.outputDir = answer || defaultVal;
|
|
41
|
-
}
|
|
42
|
-
if (!options.typesFile) {
|
|
43
|
-
const defaultVal = `${options.outputDir}/${camelize(options.exportName)}.types.d.ts`;
|
|
44
|
-
const answer = await cliPrompt(`\
|
|
45
|
-
What would you like the typesFile to be?
|
|
46
|
-
|
|
47
|
-
The typesFile is the path to the generated openapi TypeScript type definitions
|
|
48
|
-
that will be used by the api client. If not provided, it will default to:
|
|
49
|
-
|
|
50
|
-
${defaultVal}
|
|
51
|
-
`);
|
|
52
|
-
options.typesFile = answer || defaultVal;
|
|
53
|
-
}
|
|
54
|
-
return options;
|
|
55
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { camelize } from '@rvoh/dream/utils';
|
|
4
|
-
export default async function writeApiClientFile({ exportName, outputDir, typesFile, }) {
|
|
5
|
-
const destDir = outputDir;
|
|
6
|
-
const destPath = path.join(destDir, `${camelize(exportName)}.ts`);
|
|
7
|
-
try {
|
|
8
|
-
await fs.access(destPath);
|
|
9
|
-
return; // early return if the file already exists
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
// noop
|
|
13
|
-
}
|
|
14
|
-
try {
|
|
15
|
-
await fs.access(destDir);
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
await fs.mkdir(destDir, { recursive: true });
|
|
19
|
-
}
|
|
20
|
-
// compute the relative import path from the output file to the types file
|
|
21
|
-
const typesRelative = path.relative(destDir, typesFile).replace(/\.d\.ts$/, '.js');
|
|
22
|
-
const typesImportPath = typesRelative.startsWith('.') ? typesRelative : `./${typesRelative}`;
|
|
23
|
-
const contents = `\
|
|
24
|
-
import createClient from 'openapi-fetch'
|
|
25
|
-
import type { paths } from '${typesImportPath}'
|
|
26
|
-
|
|
27
|
-
function baseUrl() {
|
|
28
|
-
// add custom code here for determining your application's baseUrl
|
|
29
|
-
// this would generally be something different, depending on if you
|
|
30
|
-
// are in dev/test/production environments. For dev, you might want
|
|
31
|
-
// http://localhost:7777, while test may be http://localhost:7778, or
|
|
32
|
-
// some other port, depending on how you have your spec hooks configured.
|
|
33
|
-
// for production, it should be the real host for your application, i.e.
|
|
34
|
-
// https://myapi.com
|
|
35
|
-
|
|
36
|
-
return 'http://localhost:7777'
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const ${exportName} = createClient<paths>({
|
|
40
|
-
baseUrl: baseUrl(),
|
|
41
|
-
credentials: 'include',
|
|
42
|
-
|
|
43
|
-
// you may customize headers here, for example to add auth tokens:
|
|
44
|
-
// headers: {
|
|
45
|
-
// Authorization: \`Bearer \${getAuthToken()}\`,
|
|
46
|
-
// },
|
|
47
|
-
})
|
|
48
|
-
`;
|
|
49
|
-
await fs.writeFile(destPath, contents);
|
|
50
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { camelize, pascalize } from '@rvoh/dream/utils';
|
|
2
|
-
import * as fs from 'node:fs/promises';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import psychicPath from '../../../helpers/path/psychicPath.js';
|
|
5
|
-
export default async function writeInitializer({ exportName, schemaFile, typesFile, }) {
|
|
6
|
-
const pascalized = pascalize(exportName);
|
|
7
|
-
const camelized = camelize(exportName);
|
|
8
|
-
const destDir = path.join(psychicPath('conf'), 'initializers', 'openapi');
|
|
9
|
-
const initializerFilename = `${camelized}.ts`;
|
|
10
|
-
const initializerPath = path.join(destDir, initializerFilename);
|
|
11
|
-
try {
|
|
12
|
-
await fs.access(initializerPath);
|
|
13
|
-
return; // early return if the file already exists
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
// noop
|
|
17
|
-
}
|
|
18
|
-
try {
|
|
19
|
-
await fs.access(destDir);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
await fs.mkdir(destDir, { recursive: true });
|
|
23
|
-
}
|
|
24
|
-
const contents = `\
|
|
25
|
-
import { DreamCLI } from '@rvoh/dream/system'
|
|
26
|
-
import { PsychicApp } from '@rvoh/psychic'
|
|
27
|
-
import AppEnv from '../../AppEnv.js'
|
|
28
|
-
|
|
29
|
-
export default function initialize${pascalized}(psy: PsychicApp) {
|
|
30
|
-
psy.on('cli:sync', async () => {
|
|
31
|
-
if (AppEnv.isDevelopmentOrTest) {
|
|
32
|
-
DreamCLI.logger.logStartProgress(\`[${camelized}] syncing openapi types...\`)
|
|
33
|
-
await DreamCLI.spawn('npx openapi-typescript ${schemaFile} -o ${typesFile}', {
|
|
34
|
-
onStdout: message => {
|
|
35
|
-
DreamCLI.logger.logContinueProgress(\`[${camelized}]\` + ' ' + message, {
|
|
36
|
-
logPrefixColor: 'green',
|
|
37
|
-
})
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
DreamCLI.logger.logEndProgress()
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
}\
|
|
44
|
-
`;
|
|
45
|
-
await fs.writeFile(initializerPath, contents);
|
|
46
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { DreamCLI } from '@rvoh/dream/system';
|
|
2
|
-
import PackageManager from '../../cli/helpers/PackageManager.js';
|
|
3
|
-
import printFinalStepsMessage from '../helpers/zustandBindings/printFinalStepsMessage.js';
|
|
4
|
-
import promptForOptions from '../helpers/zustandBindings/promptForOptions.js';
|
|
5
|
-
import writeApiClientFile from '../helpers/zustandBindings/writeApiClientFile.js';
|
|
6
|
-
import writeInitializer from '../helpers/zustandBindings/writeInitializer.js';
|
|
7
|
-
/**
|
|
8
|
-
* @internal
|
|
9
|
-
*
|
|
10
|
-
* used by the psychic CLI to generate boilerplate
|
|
11
|
-
* that can be used to integrate a specific openapi.json
|
|
12
|
-
* file with a client using zustand + openapi-fetch.
|
|
13
|
-
*
|
|
14
|
-
* * generates a typed openapi-fetch client file
|
|
15
|
-
* * generates an initializer, which taps into the sync hooks
|
|
16
|
-
* to automatically run openapi-typescript to keep types in sync
|
|
17
|
-
* * prints a helpful message, instructing devs on the final
|
|
18
|
-
* steps for hooking into the newly-generated api mechanisms
|
|
19
|
-
* within their client application's zustand stores.
|
|
20
|
-
*/
|
|
21
|
-
export default async function generateOpenapiZustandBindings(options = {}) {
|
|
22
|
-
const opts = await promptForOptions(options);
|
|
23
|
-
await writeApiClientFile(opts);
|
|
24
|
-
await writeInitializer(opts);
|
|
25
|
-
await DreamCLI.spawn(PackageManager.add(['openapi-fetch', 'openapi-typescript'], { dev: true }));
|
|
26
|
-
printFinalStepsMessage(opts);
|
|
27
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @internal
|
|
3
|
-
*
|
|
4
|
-
* used by the psychic CLI to generate boilerplate
|
|
5
|
-
* that can be used to integrate a specific openapi.json
|
|
6
|
-
* file with a client using zustand + openapi-fetch.
|
|
7
|
-
*
|
|
8
|
-
* * generates a typed openapi-fetch client file
|
|
9
|
-
* * generates an initializer, which taps into the sync hooks
|
|
10
|
-
* to automatically run openapi-typescript to keep types in sync
|
|
11
|
-
* * prints a helpful message, instructing devs on the final
|
|
12
|
-
* steps for hooking into the newly-generated api mechanisms
|
|
13
|
-
* within their client application's zustand stores.
|
|
14
|
-
*/
|
|
15
|
-
export default function generateOpenapiZustandBindings(options?: OpenapiZustandBindingsOptions): Promise<void>;
|
|
16
|
-
export interface OpenapiZustandBindingsOptions {
|
|
17
|
-
exportName?: string;
|
|
18
|
-
schemaFile?: string;
|
|
19
|
-
outputDir?: string;
|
|
20
|
-
typesFile?: string;
|
|
21
|
-
}
|