create-openibm 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/templates/base.js +0 -7
- package/dist/templates/basic.d.ts +6 -0
- package/dist/templates/basic.js +44 -1
- package/dist/templates/express.js +336 -51
- package/dist/templates/nestjs.js +438 -107
- package/package.json +11 -12
package/dist/templates/base.js
CHANGED
|
@@ -12,10 +12,6 @@ function schemaIbmi(a) {
|
|
|
12
12
|
` transport = env("IBMI_TRANSPORT")`,
|
|
13
13
|
` system = env("IBMI_SYSTEM")`,
|
|
14
14
|
`}`,
|
|
15
|
-
``,
|
|
16
|
-
`generator client {`,
|
|
17
|
-
` output = "./src/generated/ibmi"`,
|
|
18
|
-
`}`,
|
|
19
15
|
];
|
|
20
16
|
if (a.features.includes('program')) {
|
|
21
17
|
blocks.push(``, `// RPG/COBOL program — JS name maps to actual IBM i program via @map`, `program SimpleCalc @map("SIMPLECALC") {`, ` library = "MYLIB"`, ` input PackedDecimal(15, 0) @in`, ` output PackedDecimal(16, 0) @out`, `}`);
|
|
@@ -48,9 +44,6 @@ function gitignore() {
|
|
|
48
44
|
'.env',
|
|
49
45
|
'*.tsbuildinfo',
|
|
50
46
|
'',
|
|
51
|
-
'# Generated IBM i client — re-create with: npm run generate',
|
|
52
|
-
'src/generated/',
|
|
53
|
-
'',
|
|
54
47
|
'# OS',
|
|
55
48
|
'.DS_Store',
|
|
56
49
|
'Thumbs.db',
|
|
@@ -7,5 +7,11 @@ export declare function clientConfigBlock(a: Answers): string;
|
|
|
7
7
|
* pulling @openibm/* from the globally linked monorepo packages.
|
|
8
8
|
*/
|
|
9
9
|
export declare function linkDevScriptMjs(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Stub for src/generated/ibmi/index.ts — created by `npm run generate`.
|
|
12
|
+
* Allows the server to boot without a real IBM i connection.
|
|
13
|
+
* Replace by running: npm run generate
|
|
14
|
+
*/
|
|
15
|
+
export declare function generatedStub(): string;
|
|
10
16
|
/** Zod-based IBM i data-type validators — shared by basic + express templates */
|
|
11
17
|
export declare function ibmiValidatorsTs(): string;
|
package/dist/templates/basic.js
CHANGED
|
@@ -51,6 +51,10 @@ function tsconfig() {
|
|
|
51
51
|
esModuleInterop: true,
|
|
52
52
|
skipLibCheck: true,
|
|
53
53
|
forceConsistentCasingInFileNames: true,
|
|
54
|
+
baseUrl: '.',
|
|
55
|
+
paths: {
|
|
56
|
+
'openibm': ['./node_modules/openibm/index.ts'],
|
|
57
|
+
},
|
|
54
58
|
},
|
|
55
59
|
include: ['src'],
|
|
56
60
|
exclude: ['node_modules', 'dist'],
|
|
@@ -61,7 +65,7 @@ function indexTs(a) {
|
|
|
61
65
|
const hasProgram = a.features.includes('program');
|
|
62
66
|
const hasTable = a.features.includes('table');
|
|
63
67
|
const imports = [
|
|
64
|
-
`import { createClient } from '
|
|
68
|
+
`import { createClient } from 'openibm';`,
|
|
65
69
|
...(a.transport === 'odbc' ? [`import 'odbc';`] : []),
|
|
66
70
|
];
|
|
67
71
|
const clientConfig = clientConfigBlock(a);
|
|
@@ -235,6 +239,45 @@ export function linkDevScriptMjs() {
|
|
|
235
239
|
``,
|
|
236
240
|
].join('\n');
|
|
237
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Stub for src/generated/ibmi/index.ts — created by `npm run generate`.
|
|
244
|
+
* Allows the server to boot without a real IBM i connection.
|
|
245
|
+
* Replace by running: npm run generate
|
|
246
|
+
*/
|
|
247
|
+
export function generatedStub() {
|
|
248
|
+
return [
|
|
249
|
+
`// AUTO-GENERATED STUB — run \`npm run generate\` to replace with the real IBM i client.`,
|
|
250
|
+
`/* eslint-disable */`,
|
|
251
|
+
``,
|
|
252
|
+
`export function createClient(_config: any): {`,
|
|
253
|
+
` connect(): Promise<void>;`,
|
|
254
|
+
` disconnect(): Promise<void>;`,
|
|
255
|
+
` isConnected(): boolean;`,
|
|
256
|
+
` query: Record<string, any>;`,
|
|
257
|
+
` program: Record<string, any>;`,
|
|
258
|
+
`} {`,
|
|
259
|
+
` const warn = () =>`,
|
|
260
|
+
` console.warn('\\x1b[33m[openibm]\\x1b[0m Stub client — run \`npm run generate\` to connect to IBM i');`,
|
|
261
|
+
``,
|
|
262
|
+
` const tableProxy: any = new Proxy({}, {`,
|
|
263
|
+
` get: (_t, method) => {`,
|
|
264
|
+
` if (method === 'findMany') return async () => { warn(); return []; };`,
|
|
265
|
+
` if (method === 'findFirst') return async () => { warn(); return null; };`,
|
|
266
|
+
` return async () => { warn(); return null; };`,
|
|
267
|
+
` },`,
|
|
268
|
+
` });`,
|
|
269
|
+
``,
|
|
270
|
+
` return {`,
|
|
271
|
+
` connect: async () => { warn(); },`,
|
|
272
|
+
` disconnect: async () => {},`,
|
|
273
|
+
` isConnected: () => false,`,
|
|
274
|
+
` query: new Proxy({}, { get: () => tableProxy }),`,
|
|
275
|
+
` program: new Proxy({}, { get: () => async (..._a: any[]) => { warn(); return {}; } }),`,
|
|
276
|
+
` };`,
|
|
277
|
+
`}`,
|
|
278
|
+
``,
|
|
279
|
+
].join('\n');
|
|
280
|
+
}
|
|
238
281
|
/** Zod-based IBM i data-type validators — shared by basic + express templates */
|
|
239
282
|
export function ibmiValidatorsTs() {
|
|
240
283
|
return [
|
|
@@ -2,14 +2,32 @@ import { clientConfigBlock, ibmiValidatorsTs, linkDevScriptMjs } from './basic.j
|
|
|
2
2
|
export function expressFiles(a) {
|
|
3
3
|
const hasProgram = a.features.includes('program');
|
|
4
4
|
const hasTable = a.features.includes('table');
|
|
5
|
-
|
|
5
|
+
const files = {
|
|
6
6
|
'package.json': packageJson(a),
|
|
7
7
|
'tsconfig.json': tsconfig(),
|
|
8
|
-
'
|
|
8
|
+
'nodemon.json': nodemonJson(),
|
|
9
|
+
'src/index.ts': indexTs(),
|
|
10
|
+
'src/app.ts': appTs(hasProgram, hasTable),
|
|
11
|
+
'src/client.ts': clientTs(a),
|
|
12
|
+
'src/docs.ts': docsTs(a, hasProgram, hasTable),
|
|
13
|
+
'src/routes/index.ts': routesIndex(hasProgram, hasTable),
|
|
14
|
+
'src/routes/health.routes.ts': healthRoutes(),
|
|
15
|
+
'src/controllers/health.controller.ts': healthController(),
|
|
9
16
|
'src/ibmi-validators.ts': ibmiValidatorsTs(),
|
|
10
17
|
'scripts/link-dev.mjs': linkDevScriptMjs(),
|
|
18
|
+
'.vscode/settings.json': vscodeSettings(),
|
|
19
|
+
'.vscode/extensions.json': vscodeExtensions(),
|
|
11
20
|
'README.md': readme(a, hasProgram, hasTable),
|
|
12
21
|
};
|
|
22
|
+
if (hasTable) {
|
|
23
|
+
files['src/routes/customers.routes.ts'] = customersRoutes();
|
|
24
|
+
files['src/controllers/customers.controller.ts'] = customersController();
|
|
25
|
+
}
|
|
26
|
+
if (hasProgram) {
|
|
27
|
+
files['src/routes/calculate.routes.ts'] = calculateRoutes();
|
|
28
|
+
files['src/controllers/calculate.controller.ts'] = calculateController();
|
|
29
|
+
}
|
|
30
|
+
return files;
|
|
13
31
|
}
|
|
14
32
|
// ── package.json ──────────────────────────────────────────────────────────────
|
|
15
33
|
function packageJson(a) {
|
|
@@ -21,13 +39,14 @@ function packageJson(a) {
|
|
|
21
39
|
scripts: {
|
|
22
40
|
generate: 'openibm generate',
|
|
23
41
|
build: 'tsc --project tsconfig.json',
|
|
24
|
-
dev: '
|
|
42
|
+
dev: 'nodemon',
|
|
25
43
|
start: 'node dist/index.js',
|
|
26
44
|
'link:dev': 'node scripts/link-dev.mjs',
|
|
27
45
|
},
|
|
28
46
|
dependencies: {
|
|
29
47
|
'@openibm/driver': '^0.1.0',
|
|
30
48
|
'@openibm/types': '^0.1.0',
|
|
49
|
+
'@scalar/express-api-reference': '^0.4.0',
|
|
31
50
|
express: '^4.21.0',
|
|
32
51
|
morgan: '^1.10.0',
|
|
33
52
|
'swagger-ui-express': '^5.0.0',
|
|
@@ -41,6 +60,7 @@ function packageJson(a) {
|
|
|
41
60
|
'@types/morgan': '^1.9.0',
|
|
42
61
|
'@types/swagger-ui-express': '^4.1.0',
|
|
43
62
|
'@types/node': '^22.0.0',
|
|
63
|
+
nodemon: '^3.1.0',
|
|
44
64
|
tsx: '^4.0.0',
|
|
45
65
|
typescript: '^5.7.0',
|
|
46
66
|
},
|
|
@@ -60,14 +80,80 @@ function tsconfig() {
|
|
|
60
80
|
esModuleInterop: true,
|
|
61
81
|
skipLibCheck: true,
|
|
62
82
|
forceConsistentCasingInFileNames: true,
|
|
83
|
+
baseUrl: '.',
|
|
84
|
+
paths: {
|
|
85
|
+
'openibm': ['./node_modules/openibm/index.ts'],
|
|
86
|
+
},
|
|
63
87
|
},
|
|
64
88
|
include: ['src'],
|
|
65
89
|
exclude: ['node_modules', 'dist'],
|
|
66
90
|
}, null, 4) + '\n';
|
|
67
91
|
}
|
|
92
|
+
// ── nodemon.json ──────────────────────────────────────────────────────────────
|
|
93
|
+
function nodemonJson() {
|
|
94
|
+
return JSON.stringify({
|
|
95
|
+
watch: ['src'],
|
|
96
|
+
ext: 'ts,json',
|
|
97
|
+
exec: 'node --env-file=.env --import tsx/esm src/index.ts',
|
|
98
|
+
}, null, 4) + '\n';
|
|
99
|
+
}
|
|
68
100
|
// ── src/index.ts ──────────────────────────────────────────────────────────────
|
|
69
|
-
function indexTs(
|
|
70
|
-
|
|
101
|
+
function indexTs() {
|
|
102
|
+
return [
|
|
103
|
+
`import { client } from './client.js';`,
|
|
104
|
+
`import app from './app.js';`,
|
|
105
|
+
``,
|
|
106
|
+
`const PORT = Number(process.env.PORT ?? 3000);`,
|
|
107
|
+
``,
|
|
108
|
+
`app.listen(PORT, async () => {`,
|
|
109
|
+
` await client.connect();`,
|
|
110
|
+
` console.log(\`Server: http://localhost:\${PORT}\`);`,
|
|
111
|
+
` console.log(\`Swagger: http://localhost:\${PORT}/api-docs\`);`,
|
|
112
|
+
` console.log(\`Scalar: http://localhost:\${PORT}/scalar\`);`,
|
|
113
|
+
`});`,
|
|
114
|
+
``,
|
|
115
|
+
].join('\n');
|
|
116
|
+
}
|
|
117
|
+
// ── src/app.ts ────────────────────────────────────────────────────────────────
|
|
118
|
+
function appTs(hasProgram, hasTable) {
|
|
119
|
+
return [
|
|
120
|
+
`import express from 'express';`,
|
|
121
|
+
`import morgan from 'morgan';`,
|
|
122
|
+
`import { setupDocs } from './docs.js';`,
|
|
123
|
+
`import router from './routes/index.js';`,
|
|
124
|
+
``,
|
|
125
|
+
`const app = express();`,
|
|
126
|
+
``,
|
|
127
|
+
`// ── Middleware ────────────────────────────────────────────────────────────────`,
|
|
128
|
+
``,
|
|
129
|
+
`app.use(express.json());`,
|
|
130
|
+
`app.use(morgan('dev'));`,
|
|
131
|
+
``,
|
|
132
|
+
`// ── API docs ──────────────────────────────────────────────────────────────────`,
|
|
133
|
+
``,
|
|
134
|
+
`setupDocs(app);`,
|
|
135
|
+
``,
|
|
136
|
+
`// ── Routes ───────────────────────────────────────────────────────────────────`,
|
|
137
|
+
``,
|
|
138
|
+
`app.use('/', router);`,
|
|
139
|
+
``,
|
|
140
|
+
`export default app;`,
|
|
141
|
+
``,
|
|
142
|
+
].join('\n');
|
|
143
|
+
}
|
|
144
|
+
// ── src/client.ts ─────────────────────────────────────────────────────────────
|
|
145
|
+
function clientTs(a) {
|
|
146
|
+
return [
|
|
147
|
+
`import { createClient } from 'openibm';`,
|
|
148
|
+
``,
|
|
149
|
+
clientConfigBlock(a),
|
|
150
|
+
``,
|
|
151
|
+
`export { client };`,
|
|
152
|
+
``,
|
|
153
|
+
].join('\n');
|
|
154
|
+
}
|
|
155
|
+
// ── src/docs.ts ───────────────────────────────────────────────────────────────
|
|
156
|
+
function docsTs(a, hasProgram, hasTable) {
|
|
71
157
|
const swaggerPaths = [
|
|
72
158
|
` '/health': {`,
|
|
73
159
|
` get: {`,
|
|
@@ -85,73 +171,259 @@ function indexTs(a, hasProgram, hasTable) {
|
|
|
85
171
|
if (hasProgram) {
|
|
86
172
|
swaggerPaths.push(` '/calculate': {`, ` post: {`, ` tags: ['Programs'],`, ` summary: 'Call SimpleCalc IBM i program',`, ` requestBody: {`, ` required: true,`, ` content: {`, ` 'application/json': {`, ` schema: {`, ` type: 'object',`, ` required: ['input'],`, ` properties: { input: { type: 'integer', example: 5, description: 'IBM i INTEGER' } },`, ` },`, ` },`, ` },`, ` },`, ` responses: {`, ` '200': { description: 'Calculation result', content: { 'application/json': { schema: { example: { input: 5, output: 500 } } } } },`, ` '400': { description: 'Validation error' },`, ` },`, ` },`, ` },`);
|
|
87
173
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
174
|
+
return [
|
|
175
|
+
`import type { Express } from 'express';`,
|
|
176
|
+
`import swaggerUi from 'swagger-ui-express';`,
|
|
177
|
+
`import { apiReference } from '@scalar/express-api-reference';`,
|
|
91
178
|
``,
|
|
92
|
-
`
|
|
93
|
-
`
|
|
94
|
-
`}
|
|
179
|
+
`export const apiSpec = {`,
|
|
180
|
+
` openapi: '3.0.0',`,
|
|
181
|
+
` info: { title: '${a.projectName}', version: '1.0.0', description: 'IBM i REST API' },`,
|
|
182
|
+
` paths: {`,
|
|
183
|
+
...swaggerPaths,
|
|
184
|
+
` },`,
|
|
185
|
+
`};`,
|
|
186
|
+
``,
|
|
187
|
+
`export function setupDocs(app: Express): void {`,
|
|
188
|
+
` app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiSpec as any));`,
|
|
189
|
+
` app.use(`,
|
|
190
|
+
` '/scalar',`,
|
|
191
|
+
` apiReference({ spec: { content: apiSpec } }) as any,`,
|
|
192
|
+
` );`,
|
|
193
|
+
`}`,
|
|
194
|
+
``,
|
|
195
|
+
].join('\n');
|
|
196
|
+
}
|
|
197
|
+
// ── src/routes/index.ts ───────────────────────────────────────────────────────
|
|
198
|
+
function routesIndex(hasProgram, hasTable) {
|
|
199
|
+
const imports = [
|
|
200
|
+
`import { Router } from 'express';`,
|
|
201
|
+
`import healthRouter from './health.routes.js';`,
|
|
95
202
|
];
|
|
96
|
-
if (hasTable)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
203
|
+
if (hasTable)
|
|
204
|
+
imports.push(`import customersRouter from './customers.routes.js';`);
|
|
205
|
+
if (hasProgram)
|
|
206
|
+
imports.push(`import calculateRouter from './calculate.routes.js';`);
|
|
207
|
+
const mounts = [
|
|
208
|
+
`router.use('/health', healthRouter);`,
|
|
209
|
+
];
|
|
210
|
+
if (hasTable)
|
|
211
|
+
mounts.push(`router.use('/customers', customersRouter);`);
|
|
212
|
+
if (hasProgram)
|
|
213
|
+
mounts.push(`router.use('/calculate', calculateRouter);`);
|
|
105
214
|
return [
|
|
106
|
-
|
|
107
|
-
`import morgan from 'morgan';`,
|
|
108
|
-
`import swaggerUi from 'swagger-ui-express';`,
|
|
109
|
-
`import { createClient } from './generated/ibmi/index.js';`,
|
|
110
|
-
...zodImports,
|
|
215
|
+
...imports,
|
|
111
216
|
``,
|
|
112
|
-
`const
|
|
113
|
-
`const PORT = Number(process.env.PORT ?? 3000);`,
|
|
217
|
+
`const router = Router();`,
|
|
114
218
|
``,
|
|
115
|
-
|
|
219
|
+
...mounts,
|
|
116
220
|
``,
|
|
117
|
-
`
|
|
118
|
-
`app.use(morgan('dev'));`,
|
|
221
|
+
`export default router;`,
|
|
119
222
|
``,
|
|
120
|
-
|
|
223
|
+
].join('\n');
|
|
224
|
+
}
|
|
225
|
+
// ── src/routes/health.routes.ts ───────────────────────────────────────────────
|
|
226
|
+
function healthRoutes() {
|
|
227
|
+
return [
|
|
228
|
+
`import { Router } from 'express';`,
|
|
229
|
+
`import { getHealth } from '../controllers/health.controller.js';`,
|
|
121
230
|
``,
|
|
122
|
-
|
|
231
|
+
`const router = Router();`,
|
|
123
232
|
``,
|
|
124
|
-
|
|
233
|
+
`router.get('/', getHealth);`,
|
|
125
234
|
``,
|
|
126
|
-
`
|
|
127
|
-
` openapi: '3.0.0',`,
|
|
128
|
-
` info: { title: '${a.projectName}', version: '1.0.0', description: 'IBM i REST API' },`,
|
|
129
|
-
` paths: {`,
|
|
130
|
-
...swaggerPaths,
|
|
131
|
-
` },`,
|
|
132
|
-
`};`,
|
|
235
|
+
`export default router;`,
|
|
133
236
|
``,
|
|
134
|
-
|
|
237
|
+
].join('\n');
|
|
238
|
+
}
|
|
239
|
+
// ── src/controllers/health.controller.ts ─────────────────────────────────────
|
|
240
|
+
function healthController() {
|
|
241
|
+
return [
|
|
242
|
+
`import type { Request, Response } from 'express';`,
|
|
243
|
+
`import { client } from '../client.js';`,
|
|
135
244
|
``,
|
|
136
|
-
|
|
245
|
+
`export function getHealth(_req: Request, res: Response): void {`,
|
|
246
|
+
` res.json({ status: 'ok', connected: client.isConnected() });`,
|
|
247
|
+
`}`,
|
|
248
|
+
``,
|
|
249
|
+
].join('\n');
|
|
250
|
+
}
|
|
251
|
+
// ── src/routes/customers.routes.ts ───────────────────────────────────────────
|
|
252
|
+
function customersRoutes() {
|
|
253
|
+
return [
|
|
254
|
+
`import { Router } from 'express';`,
|
|
255
|
+
`import { listCustomers, getCustomer } from '../controllers/customers.controller.js';`,
|
|
137
256
|
``,
|
|
138
|
-
|
|
257
|
+
`const router = Router();`,
|
|
139
258
|
``,
|
|
140
|
-
|
|
259
|
+
`router.get('/', listCustomers);`,
|
|
260
|
+
`router.get('/:id', getCustomer);`,
|
|
141
261
|
``,
|
|
142
|
-
`
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
262
|
+
`export default router;`,
|
|
263
|
+
``,
|
|
264
|
+
].join('\n');
|
|
265
|
+
}
|
|
266
|
+
// ── src/controllers/customers.controller.ts ──────────────────────────────────
|
|
267
|
+
function customersController() {
|
|
268
|
+
return [
|
|
269
|
+
`import type { Request, Response } from 'express';`,
|
|
270
|
+
`import { client } from '../client.js';`,
|
|
271
|
+
``,
|
|
272
|
+
`export async function listCustomers(req: Request, res: Response): Promise<void> {`,
|
|
273
|
+
` const state = typeof req.query.state === 'string' ? req.query.state : 'TX';`,
|
|
274
|
+
` const rows = await client.query.Customer.findMany({`,
|
|
275
|
+
` where: { state },`,
|
|
276
|
+
` orderBy: { customerId: 'asc' },`,
|
|
277
|
+
` take: 20,`,
|
|
278
|
+
` });`,
|
|
279
|
+
` res.json(rows);`,
|
|
280
|
+
`}`,
|
|
281
|
+
``,
|
|
282
|
+
`export async function getCustomer(req: Request, res: Response): Promise<void> {`,
|
|
283
|
+
` const row = await client.query.Customer.findFirst({`,
|
|
284
|
+
` where: { customerId: Number(req.params.id) },`,
|
|
285
|
+
` });`,
|
|
286
|
+
` if (!row) { res.status(404).json({ error: 'Not found' }); return; }`,
|
|
287
|
+
` res.json(row);`,
|
|
288
|
+
`}`,
|
|
289
|
+
``,
|
|
290
|
+
].join('\n');
|
|
291
|
+
}
|
|
292
|
+
// ── src/routes/calculate.routes.ts ───────────────────────────────────────────
|
|
293
|
+
function calculateRoutes() {
|
|
294
|
+
return [
|
|
295
|
+
`import { Router } from 'express';`,
|
|
296
|
+
`import { calculate } from '../controllers/calculate.controller.js';`,
|
|
297
|
+
``,
|
|
298
|
+
`const router = Router();`,
|
|
299
|
+
``,
|
|
300
|
+
`router.post('/', calculate);`,
|
|
301
|
+
``,
|
|
302
|
+
`export default router;`,
|
|
303
|
+
``,
|
|
304
|
+
].join('\n');
|
|
305
|
+
}
|
|
306
|
+
// ── src/controllers/calculate.controller.ts ──────────────────────────────────
|
|
307
|
+
function calculateController() {
|
|
308
|
+
return [
|
|
309
|
+
`import type { Request, Response } from 'express';`,
|
|
310
|
+
`import { z } from 'zod';`,
|
|
311
|
+
`import { ibmiInt } from '../ibmi-validators.js';`,
|
|
312
|
+
`import { client } from '../client.js';`,
|
|
313
|
+
``,
|
|
314
|
+
`const calculateSchema = z.object({ input: ibmiInt });`,
|
|
315
|
+
``,
|
|
316
|
+
`export async function calculate(req: Request, res: Response): Promise<void> {`,
|
|
317
|
+
` const parsed = calculateSchema.safeParse(req.body);`,
|
|
318
|
+
` if (!parsed.success) {`,
|
|
319
|
+
` res.status(400).json({ errors: parsed.error.flatten() });`,
|
|
320
|
+
` return;`,
|
|
321
|
+
` }`,
|
|
322
|
+
` const result = await client.program.SimpleCalc({ input: parsed.data.input });`,
|
|
323
|
+
` res.json({ input: parsed.data.input, output: result.output });`,
|
|
324
|
+
`}`,
|
|
147
325
|
``,
|
|
148
326
|
].join('\n');
|
|
149
327
|
}
|
|
328
|
+
// ── .vscode/settings.json ─────────────────────────────────────────────────────
|
|
329
|
+
function vscodeSettings() {
|
|
330
|
+
return JSON.stringify({
|
|
331
|
+
'editor.formatOnSave': true,
|
|
332
|
+
'editor.defaultFormatter': 'esbenp.prettier-vscode',
|
|
333
|
+
'editor.codeActionsOnSave': {
|
|
334
|
+
'source.fixAll.eslint': 'explicit',
|
|
335
|
+
'source.organizeImports': 'never',
|
|
336
|
+
'source.removeUnusedImports': 'explicit',
|
|
337
|
+
},
|
|
338
|
+
'editor.tabSize': 2,
|
|
339
|
+
'editor.insertSpaces': true,
|
|
340
|
+
'editor.rulers': [80, 120],
|
|
341
|
+
'editor.wordWrap': 'on',
|
|
342
|
+
'editor.bracketPairColorization.enabled': true,
|
|
343
|
+
'editor.guides.bracketPairs': true,
|
|
344
|
+
'editor.inlineSuggest.enabled': true,
|
|
345
|
+
'js/ts.tsdk': 'node_modules/typescript/lib',
|
|
346
|
+
'js/ts.enablePromptUseWorkspaceTsdk': true,
|
|
347
|
+
'js/ts.preferences.importModuleSpecifier': 'non-relative',
|
|
348
|
+
'js/ts.updateImportsOnFileMove.enabled': 'always',
|
|
349
|
+
'js/ts.suggest.autoImports': true,
|
|
350
|
+
'js/ts.preferences.quoteStyle': 'single',
|
|
351
|
+
'files.autoSave': 'onFocusChange',
|
|
352
|
+
'files.trimTrailingWhitespace': true,
|
|
353
|
+
'files.insertFinalNewline': true,
|
|
354
|
+
'files.eol': '\n',
|
|
355
|
+
'files.exclude': {
|
|
356
|
+
'**/.git': true,
|
|
357
|
+
'**/.DS_Store': true,
|
|
358
|
+
'**/dist': true,
|
|
359
|
+
'**/coverage': true,
|
|
360
|
+
},
|
|
361
|
+
'search.exclude': {
|
|
362
|
+
'**/node_modules': true,
|
|
363
|
+
'**/dist': true,
|
|
364
|
+
'**/coverage': true,
|
|
365
|
+
'package-lock.json': true,
|
|
366
|
+
'pnpm-lock.yaml': true,
|
|
367
|
+
},
|
|
368
|
+
'prettier.singleQuote': true,
|
|
369
|
+
'prettier.trailingComma': 'all',
|
|
370
|
+
'prettier.tabWidth': 2,
|
|
371
|
+
'prettier.semi': true,
|
|
372
|
+
'prettier.arrowParens': 'always',
|
|
373
|
+
'prettier.endOfLine': 'lf',
|
|
374
|
+
'git.autofetch': true,
|
|
375
|
+
'git.confirmSync': false,
|
|
376
|
+
'git.enableSmartCommit': true,
|
|
377
|
+
'explorer.confirmDelete': false,
|
|
378
|
+
'explorer.confirmDragAndDrop': false,
|
|
379
|
+
'explorer.fileNesting.enabled': true,
|
|
380
|
+
'explorer.fileNesting.patterns': {
|
|
381
|
+
'*.ts': '${capture}.js, ${capture}.d.ts, ${capture}.spec.ts, ${capture}.test.ts',
|
|
382
|
+
'package.json': 'package-lock.json, pnpm-lock.yaml, yarn.lock, .npmrc, .nvmrc, .node-version',
|
|
383
|
+
'tsconfig.json': 'tsconfig.*.json',
|
|
384
|
+
'.env': '.env.*',
|
|
385
|
+
'.gitignore': '.gitattributes',
|
|
386
|
+
'README.md': 'CHANGELOG.md, LICENSE',
|
|
387
|
+
},
|
|
388
|
+
'[typescript]': { 'editor.defaultFormatter': 'esbenp.prettier-vscode' },
|
|
389
|
+
'[javascript]': { 'editor.defaultFormatter': 'esbenp.prettier-vscode' },
|
|
390
|
+
'[json]': { 'editor.defaultFormatter': 'esbenp.prettier-vscode' },
|
|
391
|
+
'[ibmi]': { 'editor.defaultFormatter': 'openibm.vscode' },
|
|
392
|
+
'rest-client.environmentVariables': {
|
|
393
|
+
'$shared': { baseUrl: 'http://localhost:3000' },
|
|
394
|
+
'local': { baseUrl: 'http://localhost:3000' },
|
|
395
|
+
},
|
|
396
|
+
'todo-tree.general.tags': ['TODO', 'FIXME', 'NOTE', 'BUG'],
|
|
397
|
+
'todo-tree.highlights.defaultHighlight': {
|
|
398
|
+
icon: 'alert', type: 'text', foreground: '#fff', iconColour: '#ffcc00',
|
|
399
|
+
},
|
|
400
|
+
'todo-tree.highlights.customHighlight': {
|
|
401
|
+
'TODO': { icon: 'check', iconColour: '#3498db' },
|
|
402
|
+
'FIXME': { icon: 'flame', iconColour: '#e74c3c' },
|
|
403
|
+
'NOTE': { icon: 'note', iconColour: '#2ecc71' },
|
|
404
|
+
'BUG': { icon: 'bug', iconColour: '#e74c3c' },
|
|
405
|
+
},
|
|
406
|
+
}, null, 2) + '\n';
|
|
407
|
+
}
|
|
408
|
+
// ── .vscode/extensions.json ───────────────────────────────────────────────────
|
|
409
|
+
function vscodeExtensions() {
|
|
410
|
+
return JSON.stringify({
|
|
411
|
+
recommendations: [
|
|
412
|
+
'openibm.vscode',
|
|
413
|
+
'esbenp.prettier-vscode',
|
|
414
|
+
'dbaeumer.vscode-eslint',
|
|
415
|
+
'christian-kohler.path-intellisense',
|
|
416
|
+
'humao.rest-client',
|
|
417
|
+
'gruntfuggly.todo-tree',
|
|
418
|
+
],
|
|
419
|
+
}, null, 2) + '\n';
|
|
420
|
+
}
|
|
150
421
|
// ── README.md ─────────────────────────────────────────────────────────────────
|
|
151
|
-
function readme(a,
|
|
422
|
+
function readme(a, hasProgram, hasTable) {
|
|
152
423
|
const endpoints = [
|
|
153
424
|
`| \`GET /health\` | Connection status |`,
|
|
154
425
|
`| \`GET /api-docs\` | Swagger UI |`,
|
|
426
|
+
`| \`GET /scalar\` | Scalar API reference |`,
|
|
155
427
|
...(hasTable ? [`| \`GET /customers\` | Query DB2 table (filtered by state) |`] : []),
|
|
156
428
|
...(hasTable ? [`| \`GET /customers/:id\`| Find customer by ID |`] : []),
|
|
157
429
|
...(hasProgram ? [`| \`POST /calculate\` | Call IBM i program (\`{ input: number }\`) |`] : []),
|
|
@@ -161,6 +433,19 @@ function readme(a, hasTable, hasProgram) {
|
|
|
161
433
|
``,
|
|
162
434
|
`IBM i Express application generated by [create-openibm](https://www.npmjs.com/package/create-openibm).`,
|
|
163
435
|
``,
|
|
436
|
+
`## Project structure`,
|
|
437
|
+
``,
|
|
438
|
+
`\`\`\``,
|
|
439
|
+
`src/`,
|
|
440
|
+
`├── index.ts # Entry point — listen & connect`,
|
|
441
|
+
`├── app.ts # Express setup, middleware, routes`,
|
|
442
|
+
`├── client.ts # IBM i client singleton`,
|
|
443
|
+
`├── docs.ts # OpenAPI spec + Swagger & Scalar setup`,
|
|
444
|
+
`├── ibmi-validators.ts # Zod schemas for IBM i types`,
|
|
445
|
+
`├── controllers/ # Request handlers`,
|
|
446
|
+
`└── routes/ # Express routers`,
|
|
447
|
+
`\`\`\``,
|
|
448
|
+
``,
|
|
164
449
|
`## Setup`,
|
|
165
450
|
``,
|
|
166
451
|
`\`\`\`bash`,
|
|
@@ -172,7 +457,7 @@ function readme(a, hasTable, hasProgram) {
|
|
|
172
457
|
`## Development`,
|
|
173
458
|
``,
|
|
174
459
|
`\`\`\`bash`,
|
|
175
|
-
`npm run dev`,
|
|
460
|
+
`npm run dev # starts with nodemon — restarts on file save`,
|
|
176
461
|
`\`\`\``,
|
|
177
462
|
``,
|
|
178
463
|
`## Endpoints`,
|