mirror of
https://github.com/morten-olsen/mini-loader.git
synced 2026-02-08 01:36:26 +01:00
init
This commit is contained in:
3
packages/cli/.gitignore
vendored
Normal file
3
packages/cli/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/dist/
|
||||
/node_modules/
|
||||
/coverage/
|
||||
4
packages/cli/bin/index.mjs
Executable file
4
packages/cli/bin/index.mjs
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import 'source-map-support/register.js';
|
||||
import '../dist/esm/index.js';
|
||||
44
packages/cli/package.json
Normal file
44
packages/cli/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@morten-olsen/mini-loader-cli",
|
||||
"version": "1.0.0",
|
||||
"main": "./dist/esm/index.js",
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"bin": {
|
||||
"mini-loader": "./bin/index.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --build"
|
||||
},
|
||||
"type": "module",
|
||||
"files": [
|
||||
"./dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/esm/index.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@rollup/plugin-auto-install": "^3.0.5",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@rollup/plugin-sucrase": "^5.0.2",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"commander": "^11.1.0",
|
||||
"inquirer": "^9.2.12",
|
||||
"ora": "^8.0.1",
|
||||
"rollup": "^4.9.4",
|
||||
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"superjson": "^2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@morten-olsen/mini-loader-configs": "workspace:^",
|
||||
"@morten-olsen/mini-loader-runner": "workspace:^",
|
||||
"@morten-olsen/mini-loader-server": "workspace:^",
|
||||
"@types/inquirer": "^9.0.7",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
42
packages/cli/src/bundler/bundler.ts
Normal file
42
packages/cli/src/bundler/bundler.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { rollup } from 'rollup';
|
||||
import sucrase from '@rollup/plugin-sucrase';
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import json from '@rollup/plugin-json';
|
||||
import auto from '@rollup/plugin-auto-install';
|
||||
import { resolve } from 'path';
|
||||
|
||||
const fix = <T extends { default: any }>(f: T) => f as T['default'];
|
||||
|
||||
type BundleOptions = {
|
||||
entry: string;
|
||||
autoInstall?: boolean;
|
||||
};
|
||||
|
||||
const bundle = async ({ entry, autoInstall }: BundleOptions) => {
|
||||
const entryFile = resolve(entry);
|
||||
const codeBundler = await rollup({
|
||||
plugins: [
|
||||
fix(sucrase)({
|
||||
transforms: ['typescript', 'jsx'],
|
||||
}),
|
||||
...[autoInstall ? fix(auto) : []],
|
||||
nodeResolve({ extensions: ['.js', '.jsx', '.ts', '.tsx'] }),
|
||||
fix(json)(),
|
||||
fix(commonjs)({ include: /node_modules/ }),
|
||||
],
|
||||
input: entryFile,
|
||||
});
|
||||
const { output: codeOutput } = await codeBundler.generate({
|
||||
format: 'cjs',
|
||||
});
|
||||
if (codeOutput.length > 1) {
|
||||
throw new Error('Multiple outputs are not supported');
|
||||
}
|
||||
const [codeResult] = codeOutput;
|
||||
|
||||
const { code } = codeResult;
|
||||
return code;
|
||||
};
|
||||
|
||||
export { bundle };
|
||||
22
packages/cli/src/client/client.ts
Normal file
22
packages/cli/src/client/client.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import superjson from 'superjson';
|
||||
import type { Runtime } from '@morten-olsen/mini-loader-server';
|
||||
import type { RootRouter } from '@morten-olsen/mini-loader-server';
|
||||
|
||||
const createClient = () => {
|
||||
const client = createTRPCProxyClient<RootRouter>({
|
||||
transformer: superjson,
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: 'http://localhost:4500/trpc',
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
return client;
|
||||
};
|
||||
|
||||
type Client = ReturnType<typeof createClient>;
|
||||
|
||||
export type { Client, Runtime };
|
||||
export { createClient };
|
||||
37
packages/cli/src/commands/artifacts/artifacts.list.ts
Normal file
37
packages/cli/src/commands/artifacts/artifacts.list.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const list = new Command('list');
|
||||
|
||||
const toInt = (value?: string) => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return parseInt(value, 10);
|
||||
};
|
||||
|
||||
list
|
||||
.alias('ls')
|
||||
.description('List logs')
|
||||
.option('-r, --run-id <runId>', 'Run ID')
|
||||
.option('-l, --load-id <loadId>', 'Load ID')
|
||||
.option('-o, --offset <offset>', 'Offset')
|
||||
.option('-a, --limit <limit>', 'Limit', '1000')
|
||||
.action(async () => {
|
||||
const { runId, loadId, offset, limit } = list.opts();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
const artifacts = await step('Getting artifacts', async () => {
|
||||
return await client.artifacts.find.query({
|
||||
runId,
|
||||
loadId,
|
||||
offset: toInt(offset),
|
||||
limit: toInt(limit),
|
||||
});
|
||||
});
|
||||
console.table(artifacts);
|
||||
});
|
||||
|
||||
export { list };
|
||||
7
packages/cli/src/commands/artifacts/artifacts.ts
Normal file
7
packages/cli/src/commands/artifacts/artifacts.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { list } from './artifacts.list.js';
|
||||
|
||||
const artifacts = new Command('artifacts');
|
||||
artifacts.addCommand(list);
|
||||
|
||||
export { artifacts };
|
||||
25
packages/cli/src/commands/auth/auth.login.ts
Normal file
25
packages/cli/src/commands/auth/auth.login.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Command } from 'commander';
|
||||
import inquerer from 'inquirer';
|
||||
|
||||
const login = new Command('login');
|
||||
|
||||
login.description('Login to your account');
|
||||
login.action(async () => {
|
||||
const { host, token } = await inquerer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'host',
|
||||
message: 'Enter the host of your server',
|
||||
default: 'http://localhost:4500',
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'token',
|
||||
message: 'Enter your token',
|
||||
},
|
||||
]);
|
||||
|
||||
console.log(host, token);
|
||||
});
|
||||
|
||||
export { login };
|
||||
7
packages/cli/src/commands/auth/auth.ts
Normal file
7
packages/cli/src/commands/auth/auth.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { login } from './auth.login.js';
|
||||
|
||||
const auth = new Command('auth');
|
||||
auth.addCommand(login);
|
||||
|
||||
export { auth };
|
||||
20
packages/cli/src/commands/loads/loads.list.ts
Normal file
20
packages/cli/src/commands/loads/loads.list.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const list = new Command('list');
|
||||
|
||||
list
|
||||
.alias('ls')
|
||||
.description('List loads')
|
||||
.action(async () => {
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
const loads = step('Getting data', async () => {
|
||||
await client.loads.find.query({});
|
||||
});
|
||||
console.table(loads);
|
||||
});
|
||||
|
||||
export { list };
|
||||
39
packages/cli/src/commands/loads/loads.push.ts
Normal file
39
packages/cli/src/commands/loads/loads.push.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Command } from 'commander';
|
||||
import { resolve } from 'path';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { bundle } from '../../bundler/bundler.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const push = new Command('push');
|
||||
|
||||
push
|
||||
.argument('script')
|
||||
.option('-i, --id <id>', 'Load id')
|
||||
.option('-n, --name <name>')
|
||||
.option('-r, --run', 'Run the load')
|
||||
.option('-ai, --auto-install', 'Auto install dependencies', false)
|
||||
.action(async (script) => {
|
||||
const opts = push.opts();
|
||||
const location = resolve(script);
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
const code = await step('Bundling', async () => {
|
||||
return await bundle({ entry: location, autoInstall: opts.autoInstall });
|
||||
});
|
||||
const id = await step('Creating load', async () => {
|
||||
return await client.loads.set.mutate({
|
||||
id: opts.id,
|
||||
name: opts.name,
|
||||
script: code,
|
||||
});
|
||||
});
|
||||
console.log('created load with id', id);
|
||||
if (opts.run) {
|
||||
await step('Creating run', async () => {
|
||||
await client.runs.create.mutate({ loadId: id });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export { push };
|
||||
10
packages/cli/src/commands/loads/loads.ts
Normal file
10
packages/cli/src/commands/loads/loads.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Command } from 'commander';
|
||||
import { push } from './loads.push.js';
|
||||
import { list } from './loads.list.js';
|
||||
|
||||
const loads = new Command('loads');
|
||||
|
||||
loads.addCommand(push);
|
||||
loads.addCommand(list);
|
||||
|
||||
export { loads };
|
||||
35
packages/cli/src/commands/local/local.run.ts
Normal file
35
packages/cli/src/commands/local/local.run.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Command } from 'commander';
|
||||
import { resolve } from 'path';
|
||||
import { run as runLoad } from '@morten-olsen/mini-loader-runner';
|
||||
import { bundle } from '../../bundler/bundler.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const run = new Command('run');
|
||||
|
||||
run
|
||||
.option('-ai, --auto-install', 'Auto install dependencies', false)
|
||||
.argument('script')
|
||||
.action(async (script) => {
|
||||
const location = resolve(script);
|
||||
const { autoInstall } = run.opts();
|
||||
|
||||
const code = await step('Bundling', async () => {
|
||||
return await bundle({ entry: location, autoInstall });
|
||||
});
|
||||
const { promise, emitter } = await runLoad({
|
||||
script: code,
|
||||
});
|
||||
emitter.addListener('message', (message) => {
|
||||
switch (message.type) {
|
||||
case 'log':
|
||||
console.log(message.payload);
|
||||
break;
|
||||
case 'artifact:create':
|
||||
console.log('artifact:create', message.payload.name);
|
||||
break;
|
||||
}
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
export { run };
|
||||
8
packages/cli/src/commands/local/local.ts
Normal file
8
packages/cli/src/commands/local/local.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Command } from 'commander';
|
||||
import { run } from './local.run.js';
|
||||
|
||||
const local = new Command('local');
|
||||
|
||||
local.addCommand(run);
|
||||
|
||||
export { local };
|
||||
41
packages/cli/src/commands/logs/logs.list.ts
Normal file
41
packages/cli/src/commands/logs/logs.list.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const list = new Command('list');
|
||||
|
||||
const toInt = (value?: string) => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return parseInt(value, 10);
|
||||
};
|
||||
|
||||
list
|
||||
.alias('ls')
|
||||
.description('List logs')
|
||||
.option('-r, --run-id <runId>', 'Run ID')
|
||||
.option('-l, --load-id <loadId>', 'Load ID')
|
||||
.option('--severities <severities...>', 'Severities')
|
||||
.option('-o, --offset <offset>', 'Offset')
|
||||
.option('-a, --limit <limit>', 'Limit', '1000')
|
||||
.option('-s, --sort <order>', 'Sort', 'desc')
|
||||
.action(async () => {
|
||||
const { runId, loadId, severities, offset, limit, order } = list.opts();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
const logs = await step('Getting logs', async () => {
|
||||
return await client.logs.find.query({
|
||||
runId,
|
||||
loadId,
|
||||
severities,
|
||||
offset: toInt(offset),
|
||||
limit: toInt(limit),
|
||||
order,
|
||||
});
|
||||
});
|
||||
console.table(logs.reverse());
|
||||
});
|
||||
|
||||
export { list };
|
||||
7
packages/cli/src/commands/logs/logs.ts
Normal file
7
packages/cli/src/commands/logs/logs.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { list } from './logs.list.js';
|
||||
|
||||
const logs = new Command('logs');
|
||||
logs.addCommand(list);
|
||||
|
||||
export { logs };
|
||||
19
packages/cli/src/commands/runs/runs.create.ts
Normal file
19
packages/cli/src/commands/runs/runs.create.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const create = new Command('create');
|
||||
|
||||
create
|
||||
.description('Create a new run')
|
||||
.argument('load-id', 'Load ID')
|
||||
.action(async (loadId) => {
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
await step('Creating run', async () => {
|
||||
await client.runs.create.mutate({ loadId });
|
||||
});
|
||||
});
|
||||
|
||||
export { create };
|
||||
21
packages/cli/src/commands/runs/runs.list.ts
Normal file
21
packages/cli/src/commands/runs/runs.list.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const list = new Command('create');
|
||||
|
||||
list
|
||||
.alias('ls')
|
||||
.description('Find a run')
|
||||
.argument('[load-id]', 'Load ID')
|
||||
.action(async (loadId) => {
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
const runs = await step('Getting runs', async () => {
|
||||
return await client.runs.find.query({ loadId });
|
||||
});
|
||||
console.table(runs);
|
||||
});
|
||||
|
||||
export { list };
|
||||
8
packages/cli/src/commands/runs/runs.ts
Normal file
8
packages/cli/src/commands/runs/runs.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Command } from 'commander';
|
||||
import { create } from './runs.create.js';
|
||||
import { list } from './runs.list.js';
|
||||
|
||||
const runs = new Command('runs');
|
||||
runs.description('Manage runs').addCommand(create).addCommand(list);
|
||||
|
||||
export { runs };
|
||||
33
packages/cli/src/commands/secrets/secrets.list.ts
Normal file
33
packages/cli/src/commands/secrets/secrets.list.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const list = new Command('list');
|
||||
|
||||
const toInt = (value?: string) => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return parseInt(value, 10);
|
||||
};
|
||||
|
||||
list
|
||||
.alias('ls')
|
||||
.description('List logs')
|
||||
.option('-o, --offset <offset>', 'Offset')
|
||||
.option('-a, --limit <limit>', 'Limit', '1000')
|
||||
.action(async () => {
|
||||
const { offset, limit } = list.opts();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
const secrets = await step('Getting secrets', async () => {
|
||||
return await client.secrets.find.query({
|
||||
offset: toInt(offset),
|
||||
limit: toInt(limit),
|
||||
});
|
||||
});
|
||||
console.table(secrets);
|
||||
});
|
||||
|
||||
export { list };
|
||||
21
packages/cli/src/commands/secrets/secrets.remove.ts
Normal file
21
packages/cli/src/commands/secrets/secrets.remove.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const remove = new Command('remove');
|
||||
|
||||
remove
|
||||
.alias('rm')
|
||||
.argument('<id>')
|
||||
.action(async (id) => {
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
await step('Removing', async () => {
|
||||
await client.secrets.remove.mutate({
|
||||
id,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export { remove };
|
||||
22
packages/cli/src/commands/secrets/secrets.set.ts
Normal file
22
packages/cli/src/commands/secrets/secrets.set.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const set = new Command('set');
|
||||
|
||||
set
|
||||
.argument('<id>')
|
||||
.argument('[value]')
|
||||
.action(async (id, value) => {
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
});
|
||||
await step('Setting secret', async () => {
|
||||
await client.secrets.set.mutate({
|
||||
id,
|
||||
value,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export { set };
|
||||
11
packages/cli/src/commands/secrets/secrets.ts
Normal file
11
packages/cli/src/commands/secrets/secrets.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Command } from 'commander';
|
||||
import { list } from './secrets.list.js';
|
||||
import { set } from './secrets.set.js';
|
||||
import { remove } from './secrets.remove.js';
|
||||
|
||||
const secrets = new Command('secrets');
|
||||
secrets.addCommand(list);
|
||||
secrets.addCommand(set);
|
||||
secrets.addCommand(remove);
|
||||
|
||||
export { secrets };
|
||||
18
packages/cli/src/index.ts
Normal file
18
packages/cli/src/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { program } from 'commander';
|
||||
import { loads } from './commands/loads/loads.js';
|
||||
import { runs } from './commands/runs/runs.js';
|
||||
import { logs } from './commands/logs/logs.js';
|
||||
import { artifacts } from './commands/artifacts/artifacts.js';
|
||||
import { secrets } from './commands/secrets/secrets.js';
|
||||
import { local } from './commands/local/local.js';
|
||||
import { auth } from './commands/auth/auth.js';
|
||||
|
||||
program.addCommand(loads);
|
||||
program.addCommand(runs);
|
||||
program.addCommand(logs);
|
||||
program.addCommand(artifacts);
|
||||
program.addCommand(secrets);
|
||||
program.addCommand(local);
|
||||
program.addCommand(auth);
|
||||
|
||||
await program.parseAsync();
|
||||
15
packages/cli/src/utils/step.ts
Normal file
15
packages/cli/src/utils/step.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import ora from 'ora';
|
||||
|
||||
const step = async <T>(message: string, fn: () => Promise<T>): Promise<T> => {
|
||||
const spinner = ora(message).start();
|
||||
try {
|
||||
const result = await fn();
|
||||
spinner.succeed();
|
||||
return result;
|
||||
} catch (err) {
|
||||
spinner.fail();
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export { step };
|
||||
9
packages/cli/tsconfig.json
Normal file
9
packages/cli/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@morten-olsen/mini-loader-configs/tsconfig.esm.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/esm",
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts"
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user