@hivecommons/promptargs 0.6.0 → 0.8.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 +139 -14
- package/dist/autodetect.d.ts +2 -0
- package/dist/autodetect.js +72 -7
- package/dist/autodetect.js.map +1 -1
- package/dist/cli.js +50 -141
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +130 -0
- package/dist/commands.js.map +1 -0
- package/dist/flags.d.ts +14 -0
- package/dist/flags.js +57 -0
- package/dist/flags.js.map +1 -0
- package/dist/iterate.d.ts +8 -0
- package/dist/iterate.js +28 -0
- package/dist/iterate.js.map +1 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +64 -11
- package/dist/loader.js.map +1 -1
- package/dist/log.d.ts +19 -0
- package/dist/log.js +41 -0
- package/dist/log.js.map +1 -0
- package/dist/mustache.mjs +764 -0
- package/dist/parser.d.ts +1 -0
- package/dist/parser.js +48 -10
- package/dist/parser.js.map +1 -1
- package/dist/resolver.d.ts +2 -0
- package/dist/resolver.js +110 -66
- package/dist/resolver.js.map +1 -1
- package/dist/sanitize.d.ts +10 -0
- package/dist/sanitize.js +21 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/status.d.ts +1 -1
- package/dist/status.js +9 -5
- package/dist/status.js.map +1 -1
- package/dist/ui.d.ts +13 -1
- package/dist/ui.html +46 -60
- package/dist/ui.js +89 -21
- package/dist/ui.js.map +1 -1
- package/package.json +16 -6
- package/dist/parser.test.d.ts +0 -1
- package/dist/parser.test.js +0 -56
- package/dist/parser.test.js.map +0 -1
package/dist/ui.html
CHANGED
|
@@ -649,9 +649,19 @@
|
|
|
649
649
|
</div>
|
|
650
650
|
</div>
|
|
651
651
|
|
|
652
|
-
<script>
|
|
652
|
+
<script type="importmap">
|
|
653
|
+
{ "imports": { "mustache": "./mustache.mjs" } }
|
|
654
|
+
</script>
|
|
655
|
+
<script type="module">
|
|
656
|
+
// Import the canonical {{var}} grammar and iteration logic from the same
|
|
657
|
+
// compiled modules the CLI runs, instead of hand-reimplementing them here
|
|
658
|
+
// (see issue #69). Only splitValues/esc/escHtml stay UI-local: they encode
|
|
659
|
+
// browser-only concerns (no glob/file access, HTML escaping) with no CLI
|
|
660
|
+
// counterpart to drift from.
|
|
661
|
+
import { parseVars, expand as sharedExpand, VAR_PATTERN } from './parser.js';
|
|
662
|
+
import { cartesian as sharedCartesian, zip as sharedZip } from './iterate.js';
|
|
663
|
+
|
|
653
664
|
(function () {
|
|
654
|
-
const VAR_RE = /\{\{(\w+)(?:=([^}]*))?\}\}/g;
|
|
655
665
|
const DEBOUNCE_MS = 120;
|
|
656
666
|
|
|
657
667
|
const PRESETS = [
|
|
@@ -726,20 +736,6 @@
|
|
|
726
736
|
const copyBtn = document.getElementById('copy-btn');
|
|
727
737
|
const modeDesc = document.getElementById('mode-desc');
|
|
728
738
|
|
|
729
|
-
function parseVarsFromTemplate(text) {
|
|
730
|
-
const seen = new Set();
|
|
731
|
-
const vars = [];
|
|
732
|
-
let m;
|
|
733
|
-
VAR_RE.lastIndex = 0;
|
|
734
|
-
while ((m = VAR_RE.exec(text)) !== null) {
|
|
735
|
-
if (!seen.has(m[1])) {
|
|
736
|
-
seen.add(m[1]);
|
|
737
|
-
vars.push({ name: m[1], defaultValue: m[2] });
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
return vars;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
739
|
function getRowData() {
|
|
744
740
|
const rows = [];
|
|
745
741
|
for (const tr of varBody.querySelectorAll('tr')) {
|
|
@@ -753,7 +749,12 @@
|
|
|
753
749
|
|
|
754
750
|
function syncVarsFromTemplate() {
|
|
755
751
|
const text = tmpl.value;
|
|
756
|
-
|
|
752
|
+
let parsed;
|
|
753
|
+
try {
|
|
754
|
+
parsed = parseVars(text);
|
|
755
|
+
} catch {
|
|
756
|
+
return; // update() reports the parse error in the preview
|
|
757
|
+
}
|
|
757
758
|
const existing = getRowData();
|
|
758
759
|
const existingNames = new Set(existing.map(r => r.name));
|
|
759
760
|
|
|
@@ -814,41 +815,9 @@
|
|
|
814
815
|
return raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
815
816
|
}
|
|
816
817
|
|
|
817
|
-
function cartesian(arrays) {
|
|
818
|
-
if (arrays.length === 0) return [[]];
|
|
819
|
-
const first = arrays[0];
|
|
820
|
-
const rest = cartesian(arrays.slice(1));
|
|
821
|
-
const out = [];
|
|
822
|
-
for (const v of first) {
|
|
823
|
-
for (const r of rest) {
|
|
824
|
-
out.push([v, ...r]);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
return out;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
function zip(arrays) {
|
|
831
|
-
const maxLen = Math.max(...arrays.map(a => a.length), 1);
|
|
832
|
-
const out = [];
|
|
833
|
-
for (let i = 0; i < maxLen; i++) {
|
|
834
|
-
out.push(arrays.map(a => a[Math.min(i, a.length - 1)]));
|
|
835
|
-
}
|
|
836
|
-
return out;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
function expand(template, values) {
|
|
840
|
-
let result = template;
|
|
841
|
-
VAR_RE.lastIndex = 0;
|
|
842
|
-
result = result.replace(VAR_RE, (match, name, def) => {
|
|
843
|
-
if (name in values) return values[name];
|
|
844
|
-
if (def !== undefined) return def;
|
|
845
|
-
return match;
|
|
846
|
-
});
|
|
847
|
-
return result;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
818
|
function highlightUnfilled(html) {
|
|
851
|
-
|
|
819
|
+
VAR_PATTERN.lastIndex = 0;
|
|
820
|
+
return html.replace(VAR_PATTERN, '<span class="unfilled">$&</span>');
|
|
852
821
|
}
|
|
853
822
|
|
|
854
823
|
function update() {
|
|
@@ -879,16 +848,24 @@
|
|
|
879
848
|
if (arrayVars.length === 0) {
|
|
880
849
|
combos = [[]];
|
|
881
850
|
} else if (mode === 'cross') {
|
|
882
|
-
combos =
|
|
851
|
+
combos = sharedCartesian(arrayVars.map(v => v.values));
|
|
883
852
|
} else {
|
|
884
|
-
combos =
|
|
853
|
+
combos = sharedZip(arrayVars.map(v => v.values));
|
|
885
854
|
}
|
|
886
855
|
|
|
887
856
|
const results = [];
|
|
888
|
-
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
857
|
+
try {
|
|
858
|
+
for (const combo of combos) {
|
|
859
|
+
const vals = { ...scalarValues };
|
|
860
|
+
arrayVars.forEach((v, i) => { vals[v.name] = combo[i]; });
|
|
861
|
+
results.push({ values: { ...vals }, expanded: sharedExpand(text, vals) });
|
|
862
|
+
}
|
|
863
|
+
} catch (err) {
|
|
864
|
+
previewBody.innerHTML = `<span class="preview-empty">${escHtml(err instanceof Error ? err.message : String(err))}</span>`;
|
|
865
|
+
previewCount.textContent = 'error';
|
|
866
|
+
previewCount.className = 'preview-count empty';
|
|
867
|
+
cliText.innerHTML = '<span class="dollar">$</span>promptargs';
|
|
868
|
+
return;
|
|
892
869
|
}
|
|
893
870
|
|
|
894
871
|
const count = results.length;
|
|
@@ -908,16 +885,25 @@
|
|
|
908
885
|
}
|
|
909
886
|
previewBody.innerHTML = html || '<span class="preview-empty">No output</span>';
|
|
910
887
|
|
|
911
|
-
// Build CLI command
|
|
888
|
+
// Build CLI command. Every value must survive a POSIX shell unchanged:
|
|
889
|
+
// a bare word is left as-is, anything else is single-quoted (the only
|
|
890
|
+
// quoting under which $, backtick, backslash and whitespace are all
|
|
891
|
+
// literal). The inlined template stays double-quoted but also escapes
|
|
892
|
+
// the characters the shell expands inside double quotes.
|
|
893
|
+
const bareWord = /^[A-Za-z0-9_@%+=:,.\/-]+$/;
|
|
894
|
+
const shellQuote = s => bareWord.test(s) ? s : `'${s.replace(/'/g, `'\\''`)}'`;
|
|
895
|
+
const dquote = s => `"${s.replace(/[\\"$`]/g, '\\$&')}"`;
|
|
912
896
|
let cli = 'promptargs';
|
|
913
897
|
const hasInline = !text.includes('\n') && text.length < 200;
|
|
914
898
|
if (hasInline) {
|
|
915
|
-
cli += `
|
|
899
|
+
cli += ` ${dquote(text)}`;
|
|
916
900
|
}
|
|
917
901
|
for (const row of rows) {
|
|
918
902
|
if (!row.values) continue;
|
|
919
903
|
const prefix = row.source === 'file' ? '@' : '';
|
|
920
|
-
|
|
904
|
+
// Globs keep their double quotes so the CLI, not the shell, expands them;
|
|
905
|
+
// the same escaping as the template keeps the pattern itself literal.
|
|
906
|
+
const val = row.source === 'glob' ? dquote(row.values) : shellQuote(`${prefix}${row.values}`);
|
|
921
907
|
cli += ` --${row.name}=${val}`;
|
|
922
908
|
}
|
|
923
909
|
if (mode === 'cross' && arrayVars.length > 1) {
|
package/dist/ui.js
CHANGED
|
@@ -2,7 +2,8 @@ import { createServer } from 'node:http';
|
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { join, dirname } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { autodetect, AUTODETECT_VARS } from './autodetect.js';
|
|
5
|
+
import { autodetect, isSensitiveEnvName, isSensitiveEnvValue, AUTODETECT_VARS } from './autodetect.js';
|
|
6
|
+
import { diag, classifyError, jsonLogEnabled, logRequest } from './log.js';
|
|
6
7
|
const DEFAULT_PORT = 3700;
|
|
7
8
|
const OPEN_DELAY_MS = 500;
|
|
8
9
|
const DIFF_PREVIEW_MAX_CHARS = 60;
|
|
@@ -18,15 +19,15 @@ const SKIP_ENV_EXACT = new Set([
|
|
|
18
19
|
'TERM_PROGRAM_VERSION', 'TERM_SESSION_ID', 'Apple_PubSub_Socket_Render',
|
|
19
20
|
'MallocNanoZone', 'ORIGINAL_XDG_CURRENT_DESKTOP', 'GIT_ASKPASS',
|
|
20
21
|
]);
|
|
21
|
-
function shouldSkipEnv(key) {
|
|
22
|
+
export function shouldSkipEnv(key) {
|
|
22
23
|
if (SKIP_ENV_EXACT.has(key))
|
|
23
24
|
return true;
|
|
24
25
|
return SKIP_ENV_PREFIXES.some(p => key.startsWith(p));
|
|
25
26
|
}
|
|
26
|
-
function truncateValue(val, max) {
|
|
27
|
+
export function truncateValue(val, max) {
|
|
27
28
|
return val.length > max ? val.slice(0, max) + '...' : val;
|
|
28
29
|
}
|
|
29
|
-
function collectEnvVars() {
|
|
30
|
+
export function collectEnvVars() {
|
|
30
31
|
const git = {};
|
|
31
32
|
for (const name of AUTODETECT_VARS) {
|
|
32
33
|
const value = autodetect(name);
|
|
@@ -38,13 +39,31 @@ function collectEnvVars() {
|
|
|
38
39
|
}
|
|
39
40
|
const terminal = {};
|
|
40
41
|
for (const [key, val] of Object.entries(process.env)) {
|
|
41
|
-
|
|
42
|
+
// Never embed credential-looking names or credential-shaped values in the
|
|
43
|
+
// served page.
|
|
44
|
+
if (!val || shouldSkipEnv(key) || isSensitiveEnvName(key) || isSensitiveEnvValue(val))
|
|
42
45
|
continue;
|
|
43
46
|
terminal[key] = truncateValue(val, ENV_VAL_PREVIEW_MAX_CHARS);
|
|
44
47
|
}
|
|
45
48
|
return { git, terminal };
|
|
46
49
|
}
|
|
47
|
-
export function
|
|
50
|
+
export function buildUIHtml(rawHtml, envVars) {
|
|
51
|
+
// Escape "<" so env values cannot break out of the script tag (e.g. "</script>").
|
|
52
|
+
const envJson = JSON.stringify(envVars).replace(/</g, '\\u003c');
|
|
53
|
+
const envScript = `<script>window.__PROMPTARGS_ENV__ = ${envJson};</script>`;
|
|
54
|
+
// Function replacer: a replacement *string* would expand `$&`, "$`" and "$'"
|
|
55
|
+
// found in env values into surrounding raw HTML, undoing the escaping above.
|
|
56
|
+
return rawHtml.replace(/<script\b/, (match) => `${envScript}\n${match}`);
|
|
57
|
+
}
|
|
58
|
+
// Browser-facing modules the builder UI imports directly instead of
|
|
59
|
+
// hand-reimplementing their logic (see buildUIHtml / ui.html). Serving the
|
|
60
|
+
// exact compiled files means the browser runs the same code the CLI does.
|
|
61
|
+
const STATIC_JS_ROUTES = {
|
|
62
|
+
'/parser.js': 'parser.js',
|
|
63
|
+
'/iterate.js': 'iterate.js',
|
|
64
|
+
'/mustache.mjs': 'mustache.mjs',
|
|
65
|
+
};
|
|
66
|
+
export function startUI(port = DEFAULT_PORT, options = {}) {
|
|
48
67
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
49
68
|
const htmlPath = join(__dirname, 'ui.html');
|
|
50
69
|
let rawHtml;
|
|
@@ -52,34 +71,83 @@ export function startUI(port = DEFAULT_PORT) {
|
|
|
52
71
|
rawHtml = readFileSync(htmlPath, 'utf-8');
|
|
53
72
|
}
|
|
54
73
|
catch {
|
|
55
|
-
|
|
74
|
+
diag('ui_start_error', { error_class: 'not_found' }, 'UI file not found. Reinstall @hivecommons/promptargs.');
|
|
56
75
|
process.exit(1);
|
|
57
76
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
77
|
+
const html = buildUIHtml(rawHtml, collectEnvVars());
|
|
78
|
+
// Filled once the server is bound, because port 0 means the OS picks one.
|
|
79
|
+
const ALLOWED_HOSTS = new Set();
|
|
80
|
+
let rejectedHosts = 0;
|
|
81
|
+
const server = createServer((req, res) => {
|
|
82
|
+
if (jsonLogEnabled()) {
|
|
83
|
+
const startedAt = process.hrtime.bigint();
|
|
84
|
+
res.on('finish', () => logRequest(res.statusCode, startedAt));
|
|
85
|
+
}
|
|
86
|
+
// Reject non-local Host headers to block DNS-rebinding reads of env data.
|
|
87
|
+
if (!req.headers.host || !ALLOWED_HOSTS.has(req.headers.host)) {
|
|
88
|
+
// Never log the Host header or URL: both are attacker-controlled.
|
|
89
|
+
if (rejectedHosts++ === 0) {
|
|
90
|
+
diag('ui_host_rejected', { status: 403 }, 'promptargs: rejected request with unexpected Host header (further rejections suppressed)');
|
|
91
|
+
}
|
|
92
|
+
res.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
93
|
+
res.end('Forbidden');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const file = req.url ? STATIC_JS_ROUTES[req.url] : undefined;
|
|
97
|
+
if (file) {
|
|
98
|
+
try {
|
|
99
|
+
const js = readFileSync(join(__dirname, file), 'utf-8');
|
|
100
|
+
res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8' });
|
|
101
|
+
res.end(js);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
diag('ui_asset_error', { status: 404, error_class: classifyError(err) }, `promptargs: failed to load static asset ${req.url} (${err.code ?? 'unknown'})`);
|
|
105
|
+
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
106
|
+
res.end('Not found');
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
62
110
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
63
111
|
res.end(html);
|
|
64
112
|
});
|
|
65
|
-
|
|
66
|
-
|
|
113
|
+
// Bind loopback only: the page embeds environment variables and must never
|
|
114
|
+
// be reachable from other machines on the network.
|
|
115
|
+
server.listen(port, '127.0.0.1', () => {
|
|
116
|
+
const boundPort = server.address().port;
|
|
117
|
+
ALLOWED_HOSTS.add(`localhost:${boundPort}`);
|
|
118
|
+
ALLOWED_HOSTS.add(`127.0.0.1:${boundPort}`);
|
|
119
|
+
ALLOWED_HOSTS.add(`[::1]:${boundPort}`);
|
|
120
|
+
const url = `http://localhost:${boundPort}`;
|
|
67
121
|
console.log(`promptargs builder running at ${url}`);
|
|
68
122
|
console.log('Press Ctrl+C to stop.\n');
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
process.platform === '
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
123
|
+
if (options.openBrowser ?? true) {
|
|
124
|
+
setTimeout(() => {
|
|
125
|
+
import('node:child_process').then(({ exec }) => {
|
|
126
|
+
const cmd = process.platform === 'darwin' ? 'open' :
|
|
127
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
128
|
+
exec(`${cmd} ${url}`, err => {
|
|
129
|
+
if (!err)
|
|
130
|
+
return;
|
|
131
|
+
// Through a shell, a missing opener surfaces as exit status 127 rather than ENOENT.
|
|
132
|
+
const why = err.code === 127 ? 'not found' : String(err.code ?? err.message);
|
|
133
|
+
diag('ui_browser_open_failed', { error_class: classifyError(err) }, `promptargs: could not open a browser (${cmd}: ${why}); open ${url} manually`);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}, OPEN_DELAY_MS);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
server.on('close', () => {
|
|
140
|
+
if (rejectedHosts > 1) {
|
|
141
|
+
diag('ui_host_rejected_total', { count: rejectedHosts }, `promptargs: rejected ${rejectedHosts} requests with unexpected Host headers in total`);
|
|
142
|
+
}
|
|
76
143
|
});
|
|
77
144
|
server.on('error', (err) => {
|
|
78
145
|
if (err.code === 'EADDRINUSE') {
|
|
79
|
-
|
|
146
|
+
diag('ui_start_error', { error_class: 'port_in_use' }, `Port ${port} is in use. Try: promptargs ui --port=${port + 1}`);
|
|
80
147
|
process.exit(1);
|
|
81
148
|
}
|
|
82
149
|
throw err;
|
|
83
150
|
});
|
|
151
|
+
return server;
|
|
84
152
|
}
|
|
85
153
|
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hivecommons/promptargs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Template arguments for AI prompts — variable substitution for Claude Code, Copilot, and more",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Template arguments for AI prompts — variable substitution for Claude Code, Copilot, Goose, Bob, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"promptargs": "dist/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc && cp src/ui.html dist/ui.html",
|
|
11
|
+
"build": "tsc && cp src/ui.html dist/ui.html && cp node_modules/mustache/mustache.mjs dist/mustache.mjs",
|
|
12
12
|
"dev": "tsc --watch",
|
|
13
|
-
"test": "node
|
|
13
|
+
"test": "node scripts/run-tests.mjs",
|
|
14
|
+
"test:coverage": "node scripts/run-tests.mjs --coverage",
|
|
14
15
|
"lint": "tsc --noEmit"
|
|
15
16
|
},
|
|
16
17
|
"keywords": [
|
|
@@ -20,7 +21,12 @@
|
|
|
20
21
|
"claude",
|
|
21
22
|
"copilot",
|
|
22
23
|
"args",
|
|
23
|
-
"cli"
|
|
24
|
+
"cli",
|
|
25
|
+
"claude-code",
|
|
26
|
+
"goose",
|
|
27
|
+
"bob",
|
|
28
|
+
"agent-skills",
|
|
29
|
+
"prompt-templates"
|
|
24
30
|
],
|
|
25
31
|
"author": "hivecommons",
|
|
26
32
|
"license": "Apache-2.0",
|
|
@@ -33,10 +39,14 @@
|
|
|
33
39
|
},
|
|
34
40
|
"homepage": "https://github.com/hivecommons/promptargs#readme",
|
|
35
41
|
"engines": {
|
|
36
|
-
"node": ">=
|
|
42
|
+
"node": ">=22"
|
|
37
43
|
},
|
|
38
44
|
"files": [
|
|
39
45
|
"dist",
|
|
46
|
+
"!dist/**/*.test.js",
|
|
47
|
+
"!dist/**/*.test.js.map",
|
|
48
|
+
"!dist/**/*.test.d.ts",
|
|
49
|
+
"!dist/ui-harness.*",
|
|
40
50
|
"README.md",
|
|
41
51
|
"LICENSE"
|
|
42
52
|
],
|
package/dist/parser.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/parser.test.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test';
|
|
2
|
-
import assert from 'node:assert';
|
|
3
|
-
import { parseVars, expand } from './parser.js';
|
|
4
|
-
describe('parseVars', () => {
|
|
5
|
-
it('finds simple variables', () => {
|
|
6
|
-
const vars = parseVars('Hello {{name}}, welcome to {{place}}');
|
|
7
|
-
assert.strictEqual(vars.length, 2);
|
|
8
|
-
assert.strictEqual(vars[0].name, 'name');
|
|
9
|
-
assert.strictEqual(vars[1].name, 'place');
|
|
10
|
-
});
|
|
11
|
-
it('finds variables with defaults', () => {
|
|
12
|
-
const vars = parseVars('Be {{tone=concise}} please');
|
|
13
|
-
assert.strictEqual(vars.length, 1);
|
|
14
|
-
assert.strictEqual(vars[0].name, 'tone');
|
|
15
|
-
assert.strictEqual(vars[0].defaultValue, 'concise');
|
|
16
|
-
});
|
|
17
|
-
it('deduplicates variables', () => {
|
|
18
|
-
const vars = parseVars('{{name}} said hi to {{name}}');
|
|
19
|
-
assert.strictEqual(vars.length, 1);
|
|
20
|
-
});
|
|
21
|
-
it('returns empty for no variables', () => {
|
|
22
|
-
const vars = parseVars('No variables here');
|
|
23
|
-
assert.strictEqual(vars.length, 0);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe('expand', () => {
|
|
27
|
-
it('substitutes values', () => {
|
|
28
|
-
const result = expand('Hello {{name}}!', { name: 'World' });
|
|
29
|
-
assert.strictEqual(result, 'Hello World!');
|
|
30
|
-
});
|
|
31
|
-
it('uses defaults when no value provided', () => {
|
|
32
|
-
const result = expand('Be {{tone=concise}}', {});
|
|
33
|
-
assert.strictEqual(result, 'Be concise');
|
|
34
|
-
});
|
|
35
|
-
it('overrides defaults with provided values', () => {
|
|
36
|
-
const result = expand('Be {{tone=concise}}', { tone: 'verbose' });
|
|
37
|
-
assert.strictEqual(result, 'Be verbose');
|
|
38
|
-
});
|
|
39
|
-
it('leaves unfilled vars as-is', () => {
|
|
40
|
-
const result = expand('Hello {{name}}', {});
|
|
41
|
-
assert.strictEqual(result, 'Hello {{name}}');
|
|
42
|
-
});
|
|
43
|
-
it('handles multiple vars', () => {
|
|
44
|
-
const result = expand('Review {{file}} for {{focus=correctness}}. Be {{tone=concise}}.', { file: 'main.go' });
|
|
45
|
-
assert.strictEqual(result, 'Review main.go for correctness. Be concise.');
|
|
46
|
-
});
|
|
47
|
-
it('handles arrays in comma-separated values', () => {
|
|
48
|
-
const result = expand('Check {{file}}', { file: 'a.go' });
|
|
49
|
-
assert.strictEqual(result, 'Check a.go');
|
|
50
|
-
});
|
|
51
|
-
it('does not HTML-escape values', () => {
|
|
52
|
-
const result = expand('{{code}}', { code: '<div class="test">' });
|
|
53
|
-
assert.strictEqual(result, '<div class="test">');
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
//# sourceMappingURL=parser.test.js.map
|
package/dist/parser.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../src/parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEhD,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,IAAI,GAAG,SAAS,CAAC,sCAAsC,CAAC,CAAC;QAC/D,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,SAAS,CAAC,4BAA4B,CAAC,CAAC;QACrD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,IAAI,GAAG,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACvD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,IAAI,GAAG,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACtB,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;QACjD,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,MAAM,GAAG,MAAM,CACnB,iEAAiE,EACjE,EAAE,IAAI,EAAE,SAAS,EAAE,CACpB,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,6CAA6C,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1D,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|