@jordanalec/dtk 1.0.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/LICENSE +21 -0
- package/README.md +730 -0
- package/dist/add.js +89 -0
- package/dist/cli.js +12 -0
- package/dist/init.js +20 -0
- package/dist/utils/patch.js +8 -0
- package/package.json +52 -0
- package/templates/init/.env.template +2 -0
- package/templates/init/GUIDE.md +543 -0
- package/templates/init/README.md +59 -0
- package/templates/init/jest.config.ts +19 -0
- package/templates/init/package.json +22 -0
- package/templates/init/src/lib/auth.test.ts +48 -0
- package/templates/init/src/lib/basic-auth.ts +6 -0
- package/templates/init/src/lib/bearer-token.ts +5 -0
- package/templates/init/src/lib/http.test.ts +197 -0
- package/templates/init/src/lib/http.ts +81 -0
- package/templates/init/src/lib/oauth.test.ts +61 -0
- package/templates/init/src/lib/oauth.ts +15 -0
- package/templates/init/src/lib/token.ts +5 -0
- package/templates/init/src/load-env.ts +4 -0
- package/templates/init/src/runbooks/example.ts +33 -0
- package/templates/init/src/suite.test.ts +94 -0
- package/templates/init/src/suite.ts +70 -0
- package/templates/init/src/types/http.ts +12 -0
- package/templates/init/src/types/oauth.ts +13 -0
- package/templates/init/src/types/suite.ts +37 -0
- package/templates/init/tsconfig.json +14 -0
- package/templates/init/tsconfig.test.json +8 -0
- package/templates/plugins/aws-dynamo/env.txt +2 -0
- package/templates/plugins/aws-dynamo/example.ts +75 -0
- package/templates/plugins/aws-dynamo/plugin.json +38 -0
- package/templates/plugins/aws-dynamo/service.test.ts +180 -0
- package/templates/plugins/aws-dynamo/service.ts +73 -0
- package/templates/plugins/aws-dynamo/types.ts +29 -0
- package/templates/plugins/aws-s3/env.txt +2 -0
- package/templates/plugins/aws-s3/example.ts +41 -0
- package/templates/plugins/aws-s3/plugin.json +38 -0
- package/templates/plugins/aws-s3/service.test.ts +150 -0
- package/templates/plugins/aws-s3/service.ts +43 -0
- package/templates/plugins/aws-s3/types.ts +28 -0
- package/templates/plugins/aws-sns/env.txt +2 -0
- package/templates/plugins/aws-sns/example.ts +18 -0
- package/templates/plugins/aws-sns/plugin.json +37 -0
- package/templates/plugins/aws-sns/service.test.ts +79 -0
- package/templates/plugins/aws-sns/service.ts +28 -0
- package/templates/plugins/aws-sns/types.ts +8 -0
- package/templates/plugins/aws-sqs/env.txt +2 -0
- package/templates/plugins/aws-sqs/example.ts +16 -0
- package/templates/plugins/aws-sqs/plugin.json +37 -0
- package/templates/plugins/aws-sqs/service.test.ts +63 -0
- package/templates/plugins/aws-sqs/service.ts +27 -0
- package/templates/plugins/aws-sqs/types.ts +8 -0
- package/templates/plugins/open-ai/env.txt +1 -0
- package/templates/plugins/open-ai/example.ts +27 -0
- package/templates/plugins/open-ai/plugin.json +36 -0
- package/templates/plugins/open-ai/service.test.ts +55 -0
- package/templates/plugins/open-ai/service.ts +26 -0
- package/templates/plugins/open-ai/types.ts +61 -0
- package/templates/plugins/tsconfig.json +11 -0
package/dist/add.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { readFile, writeFile, mkdir, access } from 'fs/promises';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { injectAtSentinel } from './utils/patch.js';
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const TEMPLATES_DIR = join(__dirname, '../templates');
|
|
9
|
+
const PLUGIN_MAP = {
|
|
10
|
+
'aws-sqs': 'aws-sqs',
|
|
11
|
+
'aws-sns': 'aws-sns',
|
|
12
|
+
'aws-dynamo': 'aws-dynamo',
|
|
13
|
+
'aws-s3': 'aws-s3',
|
|
14
|
+
'open-ai': 'open-ai',
|
|
15
|
+
};
|
|
16
|
+
export const addCommand = new Command('add')
|
|
17
|
+
.description('Add a service plugin to the project')
|
|
18
|
+
.argument('<plugin>', `Plugin to add. Available: ${Object.keys(PLUGIN_MAP).join(', ')}`)
|
|
19
|
+
.action(async (plugin) => {
|
|
20
|
+
const pluginKey = PLUGIN_MAP[plugin];
|
|
21
|
+
if (!pluginKey) {
|
|
22
|
+
console.error(`Unknown plugin: "${plugin}". Available: ${Object.keys(PLUGIN_MAP).join(', ')}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const pluginDir = join(TEMPLATES_DIR, 'plugins', pluginKey);
|
|
26
|
+
const manifest = JSON.parse(await readFile(join(pluginDir, 'plugin.json'), 'utf8'));
|
|
27
|
+
const destDir = process.cwd();
|
|
28
|
+
for (const file of manifest.files) {
|
|
29
|
+
const dest = join(destDir, file.dest);
|
|
30
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
31
|
+
let content = await readFile(join(pluginDir, file.src), 'utf8');
|
|
32
|
+
for (const { from, to } of manifest.transforms?.[file.src] ?? []) {
|
|
33
|
+
content = content.replaceAll(from, to);
|
|
34
|
+
}
|
|
35
|
+
await writeFile(dest, content, 'utf8');
|
|
36
|
+
console.log(` created ${file.dest}`);
|
|
37
|
+
}
|
|
38
|
+
for (const [targetFile, sentinels] of Object.entries(manifest.patches)) {
|
|
39
|
+
const filePath = join(destDir, targetFile);
|
|
40
|
+
let content = await readFile(filePath, 'utf8');
|
|
41
|
+
for (const [sentinel, lines] of Object.entries(sentinels)) {
|
|
42
|
+
for (const line of Array.isArray(lines) ? lines : [lines]) {
|
|
43
|
+
content = injectAtSentinel(content, sentinel, line);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
await writeFile(filePath, content, 'utf8');
|
|
47
|
+
console.log(` patched ${targetFile}`);
|
|
48
|
+
}
|
|
49
|
+
if (manifest.env) {
|
|
50
|
+
const fragment = (await readFile(join(pluginDir, manifest.env), 'utf8')).trim();
|
|
51
|
+
const envPath = join(destDir, '.env.template');
|
|
52
|
+
const envExists = await access(envPath).then(() => true).catch(() => false);
|
|
53
|
+
const envContent = envExists ? await readFile(envPath, 'utf8') : '';
|
|
54
|
+
const missingLines = fragment.split('\n').map(l => l.trim()).filter(l => l && !envContent.includes(l));
|
|
55
|
+
if (missingLines.length > 0) {
|
|
56
|
+
const base = envContent.trimEnd();
|
|
57
|
+
await writeFile(envPath, (base ? base + '\n' : '') + missingLines.join('\n') + '\n', 'utf8');
|
|
58
|
+
console.log(` ${envExists ? 'updated' : 'created'} .env.template`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (manifest.example) {
|
|
62
|
+
const runbookDest = join(destDir, 'src', 'runbooks', `${plugin}.ts`);
|
|
63
|
+
const alreadyExists = await access(runbookDest).then(() => true).catch(() => false);
|
|
64
|
+
if (!alreadyExists) {
|
|
65
|
+
await mkdir(dirname(runbookDest), { recursive: true });
|
|
66
|
+
await writeFile(runbookDest, await readFile(join(pluginDir, manifest.example), 'utf8'), 'utf8');
|
|
67
|
+
console.log(` created src/runbooks/${plugin}.ts`);
|
|
68
|
+
const pkgPath = join(destDir, 'package.json');
|
|
69
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
|
|
70
|
+
const scriptKey = `runbook:${plugin}`;
|
|
71
|
+
if (!pkg.scripts[scriptKey]) {
|
|
72
|
+
pkg.scripts[scriptKey] = `tsx src/runbooks/${plugin}.ts`;
|
|
73
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
74
|
+
console.log(` updated package.json (added ${scriptKey})`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.log(` skipped src/runbooks/${plugin}.ts (already exists)`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
console.log(`\nPlugin "${plugin}" added.`);
|
|
82
|
+
if (manifest.dependencies) {
|
|
83
|
+
const deps = Object.entries(manifest.dependencies)
|
|
84
|
+
.map(([k, v]) => `${k}@${v}`)
|
|
85
|
+
.join(' ');
|
|
86
|
+
console.log(`Installing dependencies: npm install ${deps}`);
|
|
87
|
+
execSync(`npm install ${deps}`, { cwd: destDir, stdio: 'inherit' });
|
|
88
|
+
}
|
|
89
|
+
});
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { initCommand } from './init.js';
|
|
4
|
+
import { addCommand } from './add.js';
|
|
5
|
+
const program = new Command();
|
|
6
|
+
program
|
|
7
|
+
.name('dtk')
|
|
8
|
+
.description('Developer toolkit scaffolder')
|
|
9
|
+
.version('0.0.1');
|
|
10
|
+
program.addCommand(initCommand);
|
|
11
|
+
program.addCommand(addCommand);
|
|
12
|
+
program.parse();
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { cp } from 'fs/promises';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const TEMPLATES_DIR = join(__dirname, '../templates');
|
|
8
|
+
const ROOT_FILES = ['.env.template', '.gitignore', 'tsconfig.json', 'tsconfig.test.json', 'jest.config.ts', 'package.json', 'README.md', 'GUIDE.md'];
|
|
9
|
+
export const initCommand = new Command('init')
|
|
10
|
+
.description('Scaffold a new dtk project in the current directory')
|
|
11
|
+
.action(async () => {
|
|
12
|
+
const initDir = join(TEMPLATES_DIR, 'init');
|
|
13
|
+
const dest = process.cwd();
|
|
14
|
+
await cp(join(initDir, 'src'), join(dest, 'src'), { recursive: true });
|
|
15
|
+
for (const file of ROOT_FILES) {
|
|
16
|
+
await cp(join(initDir, file), join(dest, file));
|
|
17
|
+
}
|
|
18
|
+
console.log('Project scaffolded. Installing dependencies...');
|
|
19
|
+
execSync('npm install', { cwd: dest, stdio: 'inherit' });
|
|
20
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function injectAtSentinel(content, sentinel, line) {
|
|
2
|
+
const marker = `// dtk:${sentinel}`;
|
|
3
|
+
if (!content.includes(marker))
|
|
4
|
+
throw new Error(`Sentinel not found: ${marker}`);
|
|
5
|
+
if (content.includes(line.trim()))
|
|
6
|
+
return content;
|
|
7
|
+
return content.replace(marker, `${line}\n${marker}`);
|
|
8
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jordanalec/dtk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI scaffolding tool for generating self-contained TypeScript runbook projects",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"runbook",
|
|
8
|
+
"typescript",
|
|
9
|
+
"scaffolding",
|
|
10
|
+
"devtools"
|
|
11
|
+
],
|
|
12
|
+
"author": "JordanAlec",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/JordanAlec/dtk.git"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/JordanAlec/dtk#readme",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"bin": {
|
|
21
|
+
"dtk": "./dist/cli.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"templates",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"test": "node node_modules/jest/bin/jest.js"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"commander": "^14.0.3"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@aws-sdk/client-dynamodb": "^3.1041.0",
|
|
38
|
+
"@aws-sdk/client-s3": "^3.1041.0",
|
|
39
|
+
"@aws-sdk/client-sns": "^3.1040.0",
|
|
40
|
+
"@aws-sdk/client-sqs": "^3.1040.0",
|
|
41
|
+
"@aws-sdk/s3-request-presigner": "^3.1041.0",
|
|
42
|
+
"@aws-sdk/util-dynamodb": "^3.996.2",
|
|
43
|
+
"@types/jest": "^30.0.0",
|
|
44
|
+
"@types/node": "^20.0.0",
|
|
45
|
+
"axios": "^1.6.0",
|
|
46
|
+
"dotenv": "^16.4.0",
|
|
47
|
+
"jest": "^30.3.0",
|
|
48
|
+
"ts-jest": "^29.4.9",
|
|
49
|
+
"tsx": "^4.0.0",
|
|
50
|
+
"typescript": "^5.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|