resuml 1.20.1 → 2.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.
@@ -1,99 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Simple MCP client, sends one tool/resource/prompt call and prints the result.
4
- *
5
- * Usage:
6
- * node scripts/mcp-call.mjs tool <toolName> '<json args>'
7
- * node scripts/mcp-call.mjs resource <uri>
8
- * node scripts/mcp-call.mjs prompt <name> '<json args>'
9
- * node scripts/mcp-call.mjs list-tools
10
- * node scripts/mcp-call.mjs list-resources
11
- * node scripts/mcp-call.mjs list-prompts
12
- */
13
- import { spawn } from 'child_process';
14
- import { resolve, dirname } from 'path';
15
- import { fileURLToPath } from 'url';
16
- import { existsSync } from 'fs';
17
-
18
- const __dirname = dirname(fileURLToPath(import.meta.url));
19
- const ROOT = resolve(__dirname, '..');
20
- const entry = existsSync(resolve(ROOT, 'dist/index.js'))
21
- ? './dist/index.js' : './dist/index.cjs';
22
-
23
- const [,, cmd, arg1, arg2] = process.argv;
24
-
25
- async function run() {
26
- const proc = spawn('node', [entry, 'mcp'], { cwd: ROOT, stdio: ['pipe', 'pipe', 'pipe'] });
27
- let buf = '';
28
- const pending = new Map();
29
- let msgId = 0;
30
-
31
- proc.stdout.on('data', (chunk) => {
32
- buf += chunk.toString();
33
- let idx;
34
- while ((idx = buf.indexOf('\n')) !== -1) {
35
- const line = buf.slice(0, idx).trim();
36
- buf = buf.slice(idx + 1);
37
- if (!line) continue;
38
- try {
39
- const msg = JSON.parse(line);
40
- if (msg.id != null && pending.has(msg.id)) {
41
- const { resolve } = pending.get(msg.id);
42
- pending.delete(msg.id);
43
- resolve(msg);
44
- }
45
- } catch {}
46
- }
47
- });
48
-
49
- function send(method, params) {
50
- return new Promise((resolve) => {
51
- const id = ++msgId;
52
- pending.set(id, { resolve });
53
- proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
54
- });
55
- }
56
-
57
- // Initialize
58
- await send('initialize', {
59
- protocolVersion: '2025-03-26',
60
- capabilities: {},
61
- clientInfo: { name: 'mcp-call', version: '1.0.0' },
62
- });
63
- proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized', params: {} }) + '\n');
64
-
65
- let result;
66
- switch (cmd) {
67
- case 'tool': {
68
- const args = arg2 ? JSON.parse(arg2) : {};
69
- result = await send('tools/call', { name: arg1, arguments: args });
70
- break;
71
- }
72
- case 'resource':
73
- result = await send('resources/read', { uri: arg1 });
74
- break;
75
- case 'prompt': {
76
- const args = arg2 ? JSON.parse(arg2) : {};
77
- result = await send('prompts/get', { name: arg1, arguments: args });
78
- break;
79
- }
80
- case 'list-tools':
81
- result = await send('tools/list', {});
82
- break;
83
- case 'list-resources':
84
- result = await send('resources/list', {});
85
- break;
86
- case 'list-prompts':
87
- result = await send('prompts/list', {});
88
- break;
89
- default:
90
- console.error('Usage: mcp-call.mjs tool|resource|prompt|list-tools|list-resources|list-prompts');
91
- process.exit(1);
92
- }
93
-
94
- console.log(JSON.stringify(result.result ?? result.error, null, 2));
95
- proc.stdin.end();
96
- proc.kill();
97
- }
98
-
99
- run().catch(e => { console.error(e); process.exit(1); });
@@ -1,129 +0,0 @@
1
- /**
2
- * Quick bundler: takes a theme already in node_modules and bundles it
3
- * for the browser as an ESM module.
4
- *
5
- * Shims Node.js builtins (fs, path, url, crypto) so themes that
6
- * reference them at import time still bundle, the shims are no-ops
7
- * or minimal polyfills that cover what Handlebars-based themes need.
8
- */
9
- const esbuild = require('esbuild');
10
- const fs = require('fs');
11
- const path = require('path');
12
-
13
- const THEMES_DIR = path.resolve(__dirname, '../docs/themes');
14
- const SHIMS_DIR = path.resolve(__dirname, '_shims');
15
- fs.mkdirSync(THEMES_DIR, { recursive: true });
16
- fs.mkdirSync(SHIMS_DIR, { recursive: true });
17
-
18
- // Write browser shims for Node built-ins
19
- fs.writeFileSync(path.join(SHIMS_DIR, 'fs.js'), `
20
- export function readFileSync() { return ''; }
21
- export function writeFileSync() {}
22
- export function existsSync() { return false; }
23
- export function mkdirSync() {}
24
- export default { readFileSync, writeFileSync, existsSync, mkdirSync };
25
- `);
26
- fs.writeFileSync(path.join(SHIMS_DIR, 'path.js'), `
27
- export function join(...p) { return p.join('/'); }
28
- export function resolve(...p) { return p.join('/'); }
29
- export function dirname(p) { return p.replace(/\\/[^/]*$/, ''); }
30
- export function basename(p) { return p.replace(/.*\\//, ''); }
31
- export function extname(p) { const m = p.match(/\\.[^.]+$/); return m ? m[0] : ''; }
32
- export const sep = '/';
33
- export default { join, resolve, dirname, basename, extname, sep };
34
- `);
35
- fs.writeFileSync(path.join(SHIMS_DIR, 'url.js'), `
36
- export function fileURLToPath(u) { return u.replace('file://', ''); }
37
- export function pathToFileURL(p) { return 'file://' + p; }
38
- export class URL { constructor(u) { this.href = u; } }
39
- export default { fileURLToPath, pathToFileURL, URL };
40
- `);
41
- fs.writeFileSync(path.join(SHIMS_DIR, 'crypto.js'), `
42
- export function randomUUID() {
43
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
44
- var r = Math.random() * 16 | 0;
45
- return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
46
- });
47
- }
48
- export function createHash() {
49
- return { update() { return this; }, digest() { return 'stub'; } };
50
- }
51
- export default { randomUUID, createHash };
52
- `);
53
-
54
- // Themes to bundle (must already be in node_modules)
55
- const themes = [
56
- 'stackoverflow',
57
- ];
58
-
59
- async function bundleTheme(name) {
60
- const pkg = `jsonresume-theme-${name}`;
61
- const entryCode = `
62
- var theme = require("${pkg}");
63
- export var render = theme.render;
64
- export var pdfRenderOptions = theme.pdfRenderOptions;
65
- `;
66
-
67
- const entryFile = path.join(THEMES_DIR, `_entry_${name}.js`);
68
- fs.writeFileSync(entryFile, entryCode);
69
-
70
- try {
71
- await esbuild.build({
72
- entryPoints: [entryFile],
73
- bundle: true,
74
- minify: true,
75
- format: 'esm',
76
- target: 'es2022',
77
- platform: 'browser',
78
- outfile: path.join(THEMES_DIR, `${name}.js`),
79
- define: {
80
- 'process.env.NODE_ENV': '"production"',
81
- 'global': 'globalThis',
82
- },
83
- alias: {
84
- 'fs': path.join(SHIMS_DIR, 'fs.js'),
85
- 'path': path.join(SHIMS_DIR, 'path.js'),
86
- 'url': path.join(SHIMS_DIR, 'url.js'),
87
- 'node:crypto': path.join(SHIMS_DIR, 'crypto.js'),
88
- 'crypto': path.join(SHIMS_DIR, 'crypto.js'),
89
- 'node:fs': path.join(SHIMS_DIR, 'fs.js'),
90
- 'node:path': path.join(SHIMS_DIR, 'path.js'),
91
- 'node:url': path.join(SHIMS_DIR, 'url.js'),
92
- },
93
- logLevel: 'warning',
94
- });
95
-
96
- fs.unlinkSync(entryFile);
97
- const size = fs.statSync(path.join(THEMES_DIR, `${name}.js`)).size;
98
- console.log(`OK ${name}: ${(size / 1024).toFixed(0)}KB`);
99
- return { name, ok: true, size };
100
- } catch (err) {
101
- fs.unlinkSync(entryFile);
102
- console.error(`FAIL ${name}: ${err.message}`);
103
- return { name, ok: false };
104
- }
105
- }
106
-
107
- async function main() {
108
- const results = [];
109
- for (const t of themes) {
110
- results.push(await bundleTheme(t));
111
- }
112
-
113
- // Write manifest
114
- const manifest = results.map(r => ({
115
- name: r.name,
116
- displayName: r.name.charAt(0).toUpperCase() + r.name.slice(1).replace(/-/g, ' '),
117
- description: '',
118
- browserCompatible: r.ok,
119
- fileSize: r.size || 0,
120
- }));
121
- fs.writeFileSync(path.join(THEMES_DIR, 'manifest.json'), JSON.stringify(manifest, null, 2));
122
-
123
- // Cleanup shims
124
- fs.rmSync(SHIMS_DIR, { recursive: true, force: true });
125
-
126
- console.log(`\nManifest written with ${results.filter(r => r.ok).length}/${results.length} themes`);
127
- }
128
-
129
- main();
@@ -1,117 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Render a small PNG thumbnail of every theme's `.snapshot.html` so the
4
- * theme picker can show visual previews instead of "Stackoverfl…" ×5.
5
- *
6
- * For each theme in docs/themes/manifest.json that has hasSnapshot=true,
7
- * open the snapshot in Playwright at 1200×1600 (full page), then
8
- * screenshot-scale to ~320×420. Output to docs/themes/<name>.thumb.jpg.
9
- *
10
- * Requires the dev server to be running (defaults to :3010) so relative
11
- * URLs inside the snapshot (fonts, CSS) resolve correctly.
12
- *
13
- * Usage:
14
- * node scripts/render-theme-thumbs.mjs
15
- * node scripts/render-theme-thumbs.mjs --only stackoverflow,even
16
- * node scripts/render-theme-thumbs.mjs --force # re-render existing
17
- */
18
-
19
- import { readFileSync, existsSync, statSync } from 'node:fs';
20
- import { resolve, dirname } from 'node:path';
21
- import { fileURLToPath } from 'node:url';
22
- import { chromium } from 'playwright';
23
-
24
- const __dirname = dirname(fileURLToPath(import.meta.url));
25
- const ROOT = resolve(__dirname, '..');
26
- const THEMES_DIR = resolve(ROOT, 'docs/themes');
27
- const MANIFEST = resolve(THEMES_DIR, 'manifest.json');
28
- const DEV_URL = 'http://localhost:3010';
29
-
30
- const args = process.argv.slice(2);
31
- const onlyArg = args.find((a) => a.startsWith('--only='));
32
- const only = onlyArg ? new Set(onlyArg.split('=')[1].split(',')) : null;
33
- const force = args.includes('--force');
34
-
35
- function pickTargets() {
36
- const manifest = JSON.parse(readFileSync(MANIFEST, 'utf8'));
37
- const out = [];
38
- for (const t of manifest) {
39
- if (only && !only.has(t.name)) continue;
40
- if (!t.hasSnapshot) continue;
41
- const thumbPath = resolve(THEMES_DIR, `${t.name}.thumb.jpg`);
42
- if (!force && existsSync(thumbPath)) {
43
- // Skip if thumb is newer than the snapshot
44
- const snapMtime = statSync(resolve(THEMES_DIR, `${t.name}.snapshot.html`)).mtimeMs;
45
- const thumbMtime = statSync(thumbPath).mtimeMs;
46
- if (thumbMtime >= snapMtime) continue;
47
- }
48
- out.push(t.name);
49
- }
50
- return out;
51
- }
52
-
53
- async function probe() {
54
- try {
55
- const r = await fetch(DEV_URL);
56
- if (!r.ok) throw new Error(`HTTP ${r.status}`);
57
- } catch {
58
- console.error(`Dev server not responding on ${DEV_URL}. Start it with:`);
59
- console.error(' node scripts/dev-server.js --port 3010');
60
- process.exit(1);
61
- }
62
- }
63
-
64
- async function main() {
65
- await probe();
66
- const targets = pickTargets();
67
- if (targets.length === 0) {
68
- console.log('Nothing to render (use --force to regenerate existing).');
69
- return;
70
- }
71
- console.log(`Rendering ${targets.length} thumbnails…`);
72
-
73
- const browser = await chromium.launch();
74
- // Render at ~2x the display size (display ≈ 240×320 in the picker grid)
75
- // so thumbnails still look crisp on retina. JPEG keeps repo size down
76
- // compared to PNG, ~15–25 KB per image vs 150+ for PNG.
77
- const ctx = await browser.newContext({
78
- viewport: { width: 480, height: 640 },
79
- deviceScaleFactor: 1,
80
- });
81
- const page = await ctx.newPage();
82
- page.on('pageerror', () => {});
83
- page.on('console', () => {});
84
-
85
- let done = 0;
86
- let failed = 0;
87
- for (const name of targets) {
88
- try {
89
- await page.goto(`${DEV_URL}/themes/${name}.snapshot.html`, {
90
- waitUntil: 'networkidle',
91
- timeout: 15_000,
92
- });
93
- // Small wait so webfonts/CSS transitions settle.
94
- await page.waitForTimeout(400);
95
- // Clamp to the hero portion, long resumes lose legibility when
96
- // the whole page is scaled down to thumb size.
97
- await page.screenshot({
98
- path: resolve(THEMES_DIR, `${name}.thumb.jpg`),
99
- clip: { x: 0, y: 0, width: 480, height: 640 },
100
- type: 'jpeg',
101
- quality: 72,
102
- });
103
- done++;
104
- if (done % 20 === 0) process.stdout.write(` ${done}/${targets.length}\r`);
105
- } catch (e) {
106
- failed++;
107
- console.log(` ⚠ ${name}: ${e.message?.slice(0, 80) ?? 'failed'}`);
108
- }
109
- }
110
- await browser.close();
111
- console.log(`\n✅ Rendered ${done}/${targets.length} thumbnails (${failed} failed)`);
112
- }
113
-
114
- main().catch((e) => {
115
- console.error(e);
116
- process.exit(1);
117
- });