create-openibm 0.1.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 +154 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +156 -0
- package/dist/scaffold.d.ts +12 -0
- package/dist/scaffold.js +22 -0
- package/dist/templates/base.d.ts +2 -0
- package/dist/templates/base.js +58 -0
- package/dist/templates/basic.d.ts +11 -0
- package/dist/templates/basic.js +323 -0
- package/dist/templates/express.d.ts +2 -0
- package/dist/templates/express.js +199 -0
- package/dist/templates/nestjs.d.ts +2 -0
- package/dist/templates/nestjs.js +477 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# `create-openibm`
|
|
2
|
+
|
|
3
|
+
Scaffold a new IBM i application in seconds — interactive CLI that asks what you need and wires everything up.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create openibm@latest
|
|
7
|
+
# or
|
|
8
|
+
npx create-openibm
|
|
9
|
+
# or pass the project name directly
|
|
10
|
+
npx create-openibm my-ibmi-app
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
◆ create-openibm
|
|
19
|
+
|
|
20
|
+
◇ Project name
|
|
21
|
+
│ my-ibmi-app
|
|
22
|
+
|
|
23
|
+
◇ Framework
|
|
24
|
+
│ Express
|
|
25
|
+
|
|
26
|
+
◇ IBM i transport
|
|
27
|
+
│ HTTP (SSH tunnel — recommended for development)
|
|
28
|
+
|
|
29
|
+
◇ Starter examples (space to toggle, enter to confirm)
|
|
30
|
+
│ ◼ Program call
|
|
31
|
+
│ ◼ Table query
|
|
32
|
+
|
|
33
|
+
◇ Package manager
|
|
34
|
+
│ npm
|
|
35
|
+
|
|
36
|
+
◇ Initialize a git repository?
|
|
37
|
+
│ Yes
|
|
38
|
+
|
|
39
|
+
◇ Install dependencies now?
|
|
40
|
+
│ Yes
|
|
41
|
+
|
|
42
|
+
◆ Done! Next steps:
|
|
43
|
+
|
|
44
|
+
cd my-ibmi-app
|
|
45
|
+
cp .env.example .env
|
|
46
|
+
# fill in IBMI_SYSTEM, IBMI_USER, IBMI_PASS
|
|
47
|
+
npm run generate
|
|
48
|
+
npm run dev
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Options
|
|
54
|
+
|
|
55
|
+
| Question | Choices |
|
|
56
|
+
|---|---|
|
|
57
|
+
| Framework | **None** (plain Node.js), **Express**, **NestJS** |
|
|
58
|
+
| Transport | **HTTP** (SSH tunnel), **HTTPS**, **SSH** (direct), **ODBC** (native DB2) |
|
|
59
|
+
| Starter examples | **Program call** (XMLSERVICE *PGM), **Table query** (DB2 query builder) |
|
|
60
|
+
| Package manager | **npm**, **pnpm**, **Yarn** |
|
|
61
|
+
| Git init | Yes / No |
|
|
62
|
+
| Install deps | Yes / No |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Generated structure
|
|
67
|
+
|
|
68
|
+
### None (plain Node.js)
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
my-ibmi-app/
|
|
72
|
+
├── schema.ibmi ← define your programs and tables here
|
|
73
|
+
├── src/
|
|
74
|
+
│ ├── index.ts ← connect, call, query, disconnect
|
|
75
|
+
│ └── generated/ibmi/ ← created by: npm run generate
|
|
76
|
+
├── .env.example
|
|
77
|
+
├── tsconfig.json
|
|
78
|
+
└── package.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Express
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
my-ibmi-app/
|
|
85
|
+
├── schema.ibmi
|
|
86
|
+
├── src/
|
|
87
|
+
│ ├── index.ts ← Express server with IBM i routes
|
|
88
|
+
│ └── generated/ibmi/
|
|
89
|
+
├── .env.example
|
|
90
|
+
├── tsconfig.json
|
|
91
|
+
└── package.json
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### NestJS
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
my-ibmi-app/
|
|
98
|
+
├── schema.ibmi
|
|
99
|
+
├── src/
|
|
100
|
+
│ ├── main.ts
|
|
101
|
+
│ ├── app.module.ts
|
|
102
|
+
│ ├── ibmi/
|
|
103
|
+
│ │ ├── ibmi.module.ts ← provides IBMiService
|
|
104
|
+
│ │ ├── ibmi.service.ts ← wraps generated client, auto connect/disconnect
|
|
105
|
+
│ │ └── ibmi.controller.ts ← HTTP endpoints
|
|
106
|
+
│ └── generated/ibmi/
|
|
107
|
+
├── .env.example
|
|
108
|
+
├── tsconfig.json
|
|
109
|
+
└── package.json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## After scaffolding
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
cp .env.example .env
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Fill in your IBM i credentials:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
IBMI_TRANSPORT=http
|
|
124
|
+
IBMI_SYSTEM=your-ibmi-host
|
|
125
|
+
IBMI_USER=your-username
|
|
126
|
+
IBMI_PASS=your-password
|
|
127
|
+
IBMI_PORT=57700
|
|
128
|
+
IBMI_DATABASE=*LOCAL
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Then generate the typed client from your schema:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npm run generate
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This reads `schema.ibmi` and writes a fully typed TypeScript client to `src/generated/ibmi/`.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Transport notes
|
|
142
|
+
|
|
143
|
+
| Transport | Notes |
|
|
144
|
+
|---|---|
|
|
145
|
+
| **HTTP** | Recommended for development. Requires an SSH tunnel: `ssh -L 57700:localhost:57700 user@ibmi-host` |
|
|
146
|
+
| **HTTPS** | Same as HTTP but TLS. Default port 47700. |
|
|
147
|
+
| **SSH** | Direct connection. Install the `ssh2` peer dep: `npm install ssh2`. Also install `xmlservice-utils` on IBM i. |
|
|
148
|
+
| **ODBC** | Native DB2 pool. Best for production. Install `odbc` and the IBM i Access ODBC Driver. |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
import { scaffold } from './scaffold.js';
|
|
7
|
+
async function main() {
|
|
8
|
+
// Accept project name as positional arg: npx create-openibm my-app
|
|
9
|
+
const argName = process.argv[2];
|
|
10
|
+
console.log();
|
|
11
|
+
p.intro(' ◆ create-openibm ');
|
|
12
|
+
// ── Project name ──────────────────────────────────────────────────────────
|
|
13
|
+
let projectName;
|
|
14
|
+
if (argName && !argName.startsWith('-')) {
|
|
15
|
+
projectName = argName;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const res = await p.text({
|
|
19
|
+
message: 'Project name',
|
|
20
|
+
placeholder: 'my-ibmi-app',
|
|
21
|
+
validate: v => (v.trim() ? undefined : 'Project name is required'),
|
|
22
|
+
});
|
|
23
|
+
if (p.isCancel(res)) {
|
|
24
|
+
p.cancel('Cancelled.');
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
projectName = res.trim();
|
|
28
|
+
}
|
|
29
|
+
const dir = resolve(process.cwd(), projectName);
|
|
30
|
+
if (existsSync(dir)) {
|
|
31
|
+
const overwrite = await p.confirm({
|
|
32
|
+
message: `Directory "${projectName}" already exists. Continue anyway?`,
|
|
33
|
+
initialValue: false,
|
|
34
|
+
});
|
|
35
|
+
if (p.isCancel(overwrite) || !overwrite) {
|
|
36
|
+
p.cancel('Aborted.');
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// ── Framework ─────────────────────────────────────────────────────────────
|
|
41
|
+
const framework = await p.select({
|
|
42
|
+
message: 'Framework',
|
|
43
|
+
options: [
|
|
44
|
+
{ value: 'basic', label: 'None', hint: 'plain Node.js script' },
|
|
45
|
+
{ value: 'express', label: 'Express', hint: 'lightweight HTTP server' },
|
|
46
|
+
{ value: 'nestjs', label: 'NestJS', hint: 'enterprise-grade framework with DI' },
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
if (p.isCancel(framework)) {
|
|
50
|
+
p.cancel('Cancelled.');
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
// ── Transport ─────────────────────────────────────────────────────────────
|
|
54
|
+
const transport = await p.select({
|
|
55
|
+
message: 'IBM i transport',
|
|
56
|
+
options: [
|
|
57
|
+
{ value: 'http', label: 'HTTP', hint: 'SSH tunnel — recommended for development' },
|
|
58
|
+
{ value: 'https', label: 'HTTPS', hint: 'secure HTTP, port 47700' },
|
|
59
|
+
{ value: 'ssh', label: 'SSH', hint: 'direct connection — install ssh2 peer dep' },
|
|
60
|
+
{ value: 'odbc', label: 'ODBC', hint: 'native DB2 pool — recommended for production' },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
if (p.isCancel(transport)) {
|
|
64
|
+
p.cancel('Cancelled.');
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
// ── Starter examples ──────────────────────────────────────────────────────
|
|
68
|
+
const features = await p.multiselect({
|
|
69
|
+
message: 'Starter examples (space to toggle, enter to confirm)',
|
|
70
|
+
options: [
|
|
71
|
+
{ value: 'program', label: 'Program call', hint: 'XMLSERVICE *PGM example in schema.ibmi' },
|
|
72
|
+
{ value: 'table', label: 'Table query', hint: 'DB2 query builder example in schema.ibmi' },
|
|
73
|
+
],
|
|
74
|
+
required: false,
|
|
75
|
+
});
|
|
76
|
+
if (p.isCancel(features)) {
|
|
77
|
+
p.cancel('Cancelled.');
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
// ── Package manager ───────────────────────────────────────────────────────
|
|
81
|
+
const pkgManager = await p.select({
|
|
82
|
+
message: 'Package manager',
|
|
83
|
+
options: [
|
|
84
|
+
{ value: 'npm', label: 'npm' },
|
|
85
|
+
{ value: 'pnpm', label: 'pnpm' },
|
|
86
|
+
{ value: 'yarn', label: 'Yarn' },
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
if (p.isCancel(pkgManager)) {
|
|
90
|
+
p.cancel('Cancelled.');
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
// ── Extras ────────────────────────────────────────────────────────────────
|
|
94
|
+
const gitInit = await p.confirm({ message: 'Initialize a git repository?' });
|
|
95
|
+
if (p.isCancel(gitInit)) {
|
|
96
|
+
p.cancel('Cancelled.');
|
|
97
|
+
process.exit(0);
|
|
98
|
+
}
|
|
99
|
+
const install = await p.confirm({ message: 'Install dependencies now?' });
|
|
100
|
+
if (p.isCancel(install)) {
|
|
101
|
+
p.cancel('Cancelled.');
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
// ── Scaffold ──────────────────────────────────────────────────────────────
|
|
105
|
+
const answers = {
|
|
106
|
+
projectName,
|
|
107
|
+
framework: framework,
|
|
108
|
+
transport: transport,
|
|
109
|
+
features: features,
|
|
110
|
+
pkgManager: pkgManager,
|
|
111
|
+
gitInit: gitInit,
|
|
112
|
+
install: install,
|
|
113
|
+
};
|
|
114
|
+
const s = p.spinner();
|
|
115
|
+
s.start('Scaffolding project…');
|
|
116
|
+
scaffold(answers, dir);
|
|
117
|
+
s.stop('Files written.');
|
|
118
|
+
// ── Git init ──────────────────────────────────────────────────────────────
|
|
119
|
+
if (answers.gitInit) {
|
|
120
|
+
try {
|
|
121
|
+
execSync('git init', { cwd: dir, stdio: 'ignore' });
|
|
122
|
+
execSync('git add -A', { cwd: dir, stdio: 'ignore' });
|
|
123
|
+
}
|
|
124
|
+
catch { /* git not available */ }
|
|
125
|
+
}
|
|
126
|
+
// ── Install deps ──────────────────────────────────────────────────────────
|
|
127
|
+
if (answers.install) {
|
|
128
|
+
const is = p.spinner();
|
|
129
|
+
is.start(`Installing with ${pkgManager}…`);
|
|
130
|
+
try {
|
|
131
|
+
const cmd = pkgManager === 'yarn' ? 'yarn' : `${pkgManager} install`;
|
|
132
|
+
execSync(cmd, { cwd: dir, stdio: 'pipe' });
|
|
133
|
+
is.stop('Dependencies installed.');
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
is.stop('Install failed — run it manually.');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// ── Done ──────────────────────────────────────────────────────────────────
|
|
140
|
+
const pm = answers.pkgManager;
|
|
141
|
+
const run = pm === 'npm' ? 'npm run' : pm;
|
|
142
|
+
p.outro([
|
|
143
|
+
'Done! Next steps:',
|
|
144
|
+
'',
|
|
145
|
+
` cd ${dir}`,
|
|
146
|
+
` cp .env.example .env`,
|
|
147
|
+
` # fill in IBMI_SYSTEM, IBMI_USER, IBMI_PASS`,
|
|
148
|
+
...(answers.install ? [] : [` ${pm} install`]),
|
|
149
|
+
` ${run} generate`,
|
|
150
|
+
` ${run} dev`,
|
|
151
|
+
].join('\n'));
|
|
152
|
+
}
|
|
153
|
+
main().catch(e => {
|
|
154
|
+
console.error(e);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface Answers {
|
|
2
|
+
projectName: string;
|
|
3
|
+
framework: 'basic' | 'express' | 'nestjs';
|
|
4
|
+
transport: 'http' | 'https' | 'ssh' | 'odbc';
|
|
5
|
+
features: ('program' | 'table')[];
|
|
6
|
+
pkgManager: 'npm' | 'pnpm' | 'yarn';
|
|
7
|
+
gitInit: boolean;
|
|
8
|
+
install: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Relative file path → file content */
|
|
11
|
+
export type FileMap = Record<string, string>;
|
|
12
|
+
export declare function scaffold(answers: Answers, outDir: string): void;
|
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { baseFiles } from './templates/base.js';
|
|
4
|
+
import { basicFiles } from './templates/basic.js';
|
|
5
|
+
import { expressFiles } from './templates/express.js';
|
|
6
|
+
import { nestjsFiles } from './templates/nestjs.js';
|
|
7
|
+
const frameworkFiles = {
|
|
8
|
+
basic: basicFiles,
|
|
9
|
+
express: expressFiles,
|
|
10
|
+
nestjs: nestjsFiles,
|
|
11
|
+
};
|
|
12
|
+
export function scaffold(answers, outDir) {
|
|
13
|
+
const files = {
|
|
14
|
+
...baseFiles(answers),
|
|
15
|
+
...frameworkFiles[answers.framework](answers),
|
|
16
|
+
};
|
|
17
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
18
|
+
const abs = join(outDir, relPath);
|
|
19
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
20
|
+
writeFileSync(abs, content, 'utf8');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function baseFiles(a) {
|
|
2
|
+
return {
|
|
3
|
+
'schema.ibmi': schemaIbmi(a),
|
|
4
|
+
'.env.example': envExample(a),
|
|
5
|
+
'.gitignore': gitignore(),
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
// ── schema.ibmi ───────────────────────────────────────────────────────────────
|
|
9
|
+
function schemaIbmi(a) {
|
|
10
|
+
const blocks = [
|
|
11
|
+
`datasource ibmi {`,
|
|
12
|
+
` transport = env("IBMI_TRANSPORT")`,
|
|
13
|
+
` system = env("IBMI_SYSTEM")`,
|
|
14
|
+
`}`,
|
|
15
|
+
``,
|
|
16
|
+
`generator client {`,
|
|
17
|
+
` output = "./src/generated/ibmi"`,
|
|
18
|
+
`}`,
|
|
19
|
+
];
|
|
20
|
+
if (a.features.includes('program')) {
|
|
21
|
+
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`, `}`);
|
|
22
|
+
}
|
|
23
|
+
if (a.features.includes('table')) {
|
|
24
|
+
blocks.push(``, `// DB2 table — JS field names map to real column names via @map`, `table Customer @map("QIWS.QCUSTCDT") {`, ` customerId Int @id @map("CUSNUM")`, ` lastName Char(8) @map("LSTNAM")`, ` initials Char(3) @map("INIT")`, ` city Char(6) @map("CITY")`, ` state Char(2) @map("STATE")`, ` creditLimit Decimal(6, 0) @map("CDTLMT")`, ` balanceDue Decimal(6, 2) @map("BALDUE")`, `}`);
|
|
25
|
+
}
|
|
26
|
+
return blocks.join('\n') + '\n';
|
|
27
|
+
}
|
|
28
|
+
// ── .env.example ──────────────────────────────────────────────────────────────
|
|
29
|
+
function envExample(a) {
|
|
30
|
+
const lines = [
|
|
31
|
+
`IBMI_TRANSPORT=${a.transport}`,
|
|
32
|
+
`IBMI_SYSTEM=your-ibmi-host`,
|
|
33
|
+
`IBMI_USER=your-username`,
|
|
34
|
+
`IBMI_PASS=your-password`,
|
|
35
|
+
];
|
|
36
|
+
if (a.transport === 'http' || a.transport === 'https') {
|
|
37
|
+
const defaultPort = a.transport === 'https' ? '47700' : '57700';
|
|
38
|
+
lines.push(`IBMI_PORT=${defaultPort}`);
|
|
39
|
+
lines.push(`IBMI_DATABASE=*LOCAL`);
|
|
40
|
+
}
|
|
41
|
+
return lines.join('\n') + '\n';
|
|
42
|
+
}
|
|
43
|
+
// ── .gitignore ────────────────────────────────────────────────────────────────
|
|
44
|
+
function gitignore() {
|
|
45
|
+
return [
|
|
46
|
+
'node_modules/',
|
|
47
|
+
'dist/',
|
|
48
|
+
'.env',
|
|
49
|
+
'*.tsbuildinfo',
|
|
50
|
+
'',
|
|
51
|
+
'# Generated IBM i client — re-create with: npm run generate',
|
|
52
|
+
'src/generated/',
|
|
53
|
+
'',
|
|
54
|
+
'# OS',
|
|
55
|
+
'.DS_Store',
|
|
56
|
+
'Thumbs.db',
|
|
57
|
+
].join('\n') + '\n';
|
|
58
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Answers, FileMap } from '../scaffold.js';
|
|
2
|
+
export declare function basicFiles(a: Answers): FileMap;
|
|
3
|
+
export declare function clientConfigBlock(a: Answers): string;
|
|
4
|
+
/**
|
|
5
|
+
* scripts/link-dev.mjs — shared by all templates.
|
|
6
|
+
* Uses `pnpm add file:...` so pnpm installs ALL deps in one shot while
|
|
7
|
+
* pulling @openibm/* from the globally linked monorepo packages.
|
|
8
|
+
*/
|
|
9
|
+
export declare function linkDevScriptMjs(): string;
|
|
10
|
+
/** Zod-based IBM i data-type validators — shared by basic + express templates */
|
|
11
|
+
export declare function ibmiValidatorsTs(): string;
|