@metricinsights/pp-dev 1.5.0-beta.29 → 1.5.0-beta.30
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/dist/cjs/cli.js +1 -1
- package/dist/cjs/plugin.js +1 -1
- package/dist/cjs/request-capture.middleware-Bihrs8O-.js +2 -0
- package/dist/cjs/request-capture.middleware-Bihrs8O-.js.map +1 -0
- package/dist/client/variables-editor.js +118 -63
- package/dist/client/variables-editor.js.map +1 -1
- package/dist/esm/cli.js +1 -1
- package/dist/esm/plugin.js +1 -1
- package/dist/esm/{request-capture.middleware-DMhZwnVA.js → request-capture.middleware-CYtDXIxP.js} +2 -2
- package/dist/esm/request-capture.middleware-CYtDXIxP.js.map +1 -0
- package/package.json +1 -1
- package/dist/cjs/request-capture.middleware-JOjYxq-k.js +0 -2
- package/dist/cjs/request-capture.middleware-JOjYxq-k.js.map +0 -1
- package/dist/esm/request-capture.middleware-DMhZwnVA.js.map +0 -1
|
@@ -155,18 +155,23 @@ function importPageGroupHtml(title, pages) {
|
|
|
155
155
|
return '';
|
|
156
156
|
}
|
|
157
157
|
const rows = pages
|
|
158
|
-
.map((p) =>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
'
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
158
|
+
.map((p) => {
|
|
159
|
+
// A page without a name on MI goes by its internal name (as an attribute, `null` was
|
|
160
|
+
// dropped, and the review step named the page "undefined").
|
|
161
|
+
const name = p.name || p.internal_name;
|
|
162
|
+
return ('<button type="button" class="ve-import-page-row" data-search="' +
|
|
163
|
+
escapeHtml((name + ' ' + p.internal_name).toLowerCase()) +
|
|
164
|
+
'"' +
|
|
165
|
+
actionAttrs('select-import-page', { pageId: p.id, pageName: name }) +
|
|
166
|
+
'>' +
|
|
167
|
+
'<span class="ve-import-page-name">' +
|
|
168
|
+
escapeHtml(name) +
|
|
169
|
+
'</span>' +
|
|
170
|
+
'<span class="ve-import-page-slug">' +
|
|
171
|
+
escapeHtml(p.internal_name) +
|
|
172
|
+
'</span>' +
|
|
173
|
+
'</button>');
|
|
174
|
+
})
|
|
170
175
|
.join('');
|
|
171
176
|
return ('<div class="ve-import-page-group">' +
|
|
172
177
|
'<div class="ve-import-section-title">' +
|
|
@@ -356,6 +361,9 @@ function openOverlay(html) {
|
|
|
356
361
|
return overlay;
|
|
357
362
|
}
|
|
358
363
|
const find = (root, selector) => root.querySelector(selector);
|
|
364
|
+
// Enter on a focused button is that button's own click (Cancel, ✕, the diff's ←/→), not the
|
|
365
|
+
// dialog's confirmation: as the latter, it saved or discarded on a Cancel reached by Shift+Tab.
|
|
366
|
+
const isButton = (target) => target instanceof HTMLButtonElement;
|
|
359
367
|
// Asks for a value (in place of window.prompt()); `onDone` gets `null` when the user cancels.
|
|
360
368
|
function showCustomValueModal(title, initialValue, onDone) {
|
|
361
369
|
const overlay = openOverlay('<div class="ve-modal">' +
|
|
@@ -422,7 +430,7 @@ function showConfirmModal(message, onConfirm) {
|
|
|
422
430
|
}
|
|
423
431
|
});
|
|
424
432
|
overlay.addEventListener('keydown', (ev) => {
|
|
425
|
-
if (ev.key === 'Enter') {
|
|
433
|
+
if (ev.key === 'Enter' && !isButton(ev.target)) {
|
|
426
434
|
ev.preventDefault();
|
|
427
435
|
close(true);
|
|
428
436
|
}
|
|
@@ -640,7 +648,7 @@ function showJsonDiffModal(beforeText, afterText, onConfirm) {
|
|
|
640
648
|
}
|
|
641
649
|
return;
|
|
642
650
|
}
|
|
643
|
-
if (ev.key === 'Enter') {
|
|
651
|
+
if (ev.key === 'Enter' && !isButton(ev.target)) {
|
|
644
652
|
ev.preventDefault();
|
|
645
653
|
close(true);
|
|
646
654
|
}
|
|
@@ -1692,6 +1700,7 @@ function renderValuesTabHtml(state, rows, rawMode, selectedIndex) {
|
|
|
1692
1700
|
<div class="ve-values-layout">
|
|
1693
1701
|
<div class="ve-values-nav">
|
|
1694
1702
|
<div class="ve-values-nav-list">${navHtml}</div>
|
|
1703
|
+
<button type="button" class="ve-values-nav-item ve-values-nav-add"${actionAttrs('add-value-row')}>+ Add item</button>
|
|
1695
1704
|
</div>
|
|
1696
1705
|
<div class="ve-values-detail">${detailHtml}</div>
|
|
1697
1706
|
</div>
|
|
@@ -1830,7 +1839,11 @@ function startVariablesEditor({ templateLess }) {
|
|
|
1830
1839
|
banner = null;
|
|
1831
1840
|
}
|
|
1832
1841
|
// ── Data loading ─────────────────────────────────────────────────────────────
|
|
1833
|
-
|
|
1842
|
+
// Both loaders resolve to whether the tab took the data. Edits made while a load ran (the tab
|
|
1843
|
+
// paints what it has first, see scheduleAutoLoad) stay as they are: only what counts as saved
|
|
1844
|
+
// takes the current content, and the caller leaves the tab unpainted, since a repaint would
|
|
1845
|
+
// move the caret.
|
|
1846
|
+
async function loadSchema(seq, editSeqAtStart) {
|
|
1834
1847
|
let r;
|
|
1835
1848
|
let data;
|
|
1836
1849
|
try {
|
|
@@ -1839,13 +1852,13 @@ function startVariablesEditor({ templateLess }) {
|
|
|
1839
1852
|
}
|
|
1840
1853
|
catch (e) {
|
|
1841
1854
|
if (seq !== schemaSeq) {
|
|
1842
|
-
return;
|
|
1855
|
+
return false;
|
|
1843
1856
|
}
|
|
1844
1857
|
showBanner('error', 'Failed to load the schema: ' + escapeHtml(errorMessage(e)));
|
|
1845
|
-
return;
|
|
1858
|
+
return false;
|
|
1846
1859
|
}
|
|
1847
1860
|
if (seq !== schemaSeq) {
|
|
1848
|
-
return;
|
|
1861
|
+
return false;
|
|
1849
1862
|
}
|
|
1850
1863
|
// An error answer (e.g. 503 with no dist service) is not a schema: kept as one, it rendered as
|
|
1851
1864
|
// "No local __template_variables.json found" and offered to create a file over the real one.
|
|
@@ -1853,39 +1866,46 @@ function startVariablesEditor({ templateLess }) {
|
|
|
1853
1866
|
schemaState = null;
|
|
1854
1867
|
schemaRows = [];
|
|
1855
1868
|
showBanner('error', 'Failed to load the schema: ' + escapeHtml((data && data.error) || 'HTTP ' + r.status));
|
|
1856
|
-
return;
|
|
1869
|
+
return false;
|
|
1857
1870
|
}
|
|
1871
|
+
const freshRows = data.schema && Array.isArray(data.schema.tags) ? data.schema.tags.map((t) => Object.assign({}, t)) : [];
|
|
1872
|
+
const freshRawMode = data.exists && !data.schema;
|
|
1873
|
+
const edited = editSeq !== editSeqAtStart;
|
|
1858
1874
|
schemaState = data;
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1875
|
+
if (!edited) {
|
|
1876
|
+
schemaRows = freshRows;
|
|
1877
|
+
rawMode = freshRawMode;
|
|
1878
|
+
}
|
|
1879
|
+
// In raw mode the file's text, not the textarea's (which may hold edits): the diff shown
|
|
1880
|
+
// before a save compares with it.
|
|
1881
|
+
jsonBaseline.schema = freshRawMode ? data.raw || '' : schemaJsonOf(data, freshRows);
|
|
1882
|
+
return !edited;
|
|
1863
1883
|
}
|
|
1864
|
-
async function loadValues(seq) {
|
|
1884
|
+
async function loadValues(seq, editSeqAtStart) {
|
|
1865
1885
|
let r;
|
|
1866
1886
|
try {
|
|
1867
1887
|
r = await fetch('/@api/variables/values');
|
|
1868
1888
|
}
|
|
1869
1889
|
catch (e) {
|
|
1870
1890
|
if (seq !== valuesSeq) {
|
|
1871
|
-
return;
|
|
1891
|
+
return false;
|
|
1872
1892
|
}
|
|
1873
1893
|
valuesState = null;
|
|
1874
1894
|
valueRows = [];
|
|
1875
1895
|
showBanner('error', 'Failed to load values: ' + escapeHtml(errorMessage(e)));
|
|
1876
|
-
return;
|
|
1896
|
+
return false;
|
|
1877
1897
|
}
|
|
1878
1898
|
if (!r.ok) {
|
|
1879
1899
|
const err = (await r.json().catch(() => ({})));
|
|
1880
1900
|
if (seq !== valuesSeq) {
|
|
1881
|
-
return;
|
|
1901
|
+
return false;
|
|
1882
1902
|
}
|
|
1883
1903
|
// A save is a full replacement. Keep the state explicitly unavailable after a failed
|
|
1884
1904
|
// read so the user cannot accidentally submit an empty replacement set.
|
|
1885
1905
|
valuesState = null;
|
|
1886
1906
|
valueRows = [];
|
|
1887
1907
|
showBanner('error', escapeHtml(err.error || 'Failed to load values.'));
|
|
1888
|
-
return;
|
|
1908
|
+
return false;
|
|
1889
1909
|
}
|
|
1890
1910
|
let data;
|
|
1891
1911
|
try {
|
|
@@ -1893,36 +1913,35 @@ function startVariablesEditor({ templateLess }) {
|
|
|
1893
1913
|
}
|
|
1894
1914
|
catch (e) {
|
|
1895
1915
|
if (seq !== valuesSeq) {
|
|
1896
|
-
return;
|
|
1916
|
+
return false;
|
|
1897
1917
|
}
|
|
1898
1918
|
valuesState = null;
|
|
1899
1919
|
valueRows = [];
|
|
1900
1920
|
showBanner('error', 'Failed to parse the values response: ' + escapeHtml(errorMessage(e)));
|
|
1901
|
-
return;
|
|
1921
|
+
return false;
|
|
1902
1922
|
}
|
|
1903
1923
|
if (seq !== valuesSeq) {
|
|
1904
|
-
return;
|
|
1924
|
+
return false;
|
|
1905
1925
|
}
|
|
1926
|
+
const freshRows = (data.combined || []).map((e) => Object.assign({}, e));
|
|
1927
|
+
const edited = editSeq !== editSeqAtStart;
|
|
1906
1928
|
valuesState = data;
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
}
|
|
1910
|
-
function snapshotSchemaJson() {
|
|
1911
|
-
if (!schemaState) {
|
|
1912
|
-
return '';
|
|
1913
|
-
}
|
|
1914
|
-
if (rawMode) {
|
|
1915
|
-
const editorEl = byId('raw-editor');
|
|
1916
|
-
return editorEl ? editorEl.value : schemaState.raw || '';
|
|
1929
|
+
if (!edited) {
|
|
1930
|
+
valueRows = freshRows;
|
|
1917
1931
|
}
|
|
1918
|
-
|
|
1919
|
-
|
|
1932
|
+
jsonBaseline.values = snapshotValuesJson(freshRows);
|
|
1933
|
+
return !edited;
|
|
1934
|
+
}
|
|
1935
|
+
/** The schema as the file's JSON text would be with `rows` as its tags. */
|
|
1936
|
+
function schemaJsonOf(state, rows) {
|
|
1937
|
+
if (state.raw && !state.schema) {
|
|
1938
|
+
return state.raw;
|
|
1920
1939
|
}
|
|
1921
1940
|
try {
|
|
1922
|
-
return JSON.stringify(Object.assign({},
|
|
1941
|
+
return JSON.stringify(Object.assign({}, state.schema || {}, { tags: rows }), null, 2);
|
|
1923
1942
|
}
|
|
1924
1943
|
catch {
|
|
1925
|
-
return
|
|
1944
|
+
return state.raw || '';
|
|
1926
1945
|
}
|
|
1927
1946
|
}
|
|
1928
1947
|
function snapshotValuesJson(rows) {
|
|
@@ -1936,25 +1955,38 @@ function startVariablesEditor({ templateLess }) {
|
|
|
1936
1955
|
// Fetches fresh data for one tab, tracking a per-tab "loading" flag and sequence number so a
|
|
1937
1956
|
// response that's superseded by a newer load for the SAME tab (started before this one
|
|
1938
1957
|
// resolved) gets dropped instead of clobbering fresher data. Toggles the tab's own loading
|
|
1939
|
-
// flag but deliberately doesn't call render() itself — callers decide when to repaint
|
|
1958
|
+
// flag but deliberately doesn't call render() itself — callers decide when to repaint, which
|
|
1959
|
+
// they do only when the tab took the data (see loadSchema).
|
|
1940
1960
|
function loadTabData(tab) {
|
|
1961
|
+
const editSeqAtStart = editSeq;
|
|
1941
1962
|
if (tab === 'schema') {
|
|
1942
1963
|
const seq = ++schemaSeq;
|
|
1943
1964
|
schemaLoading = true;
|
|
1944
|
-
return loadSchema(seq).then(() => {
|
|
1965
|
+
return loadSchema(seq, editSeqAtStart).then((taken) => {
|
|
1945
1966
|
if (seq === schemaSeq) {
|
|
1946
1967
|
schemaLoading = false;
|
|
1947
1968
|
}
|
|
1969
|
+
return taken;
|
|
1948
1970
|
});
|
|
1949
1971
|
}
|
|
1950
1972
|
const seq = ++valuesSeq;
|
|
1951
1973
|
valuesLoading = true;
|
|
1952
|
-
return loadValues(seq).then(() => {
|
|
1974
|
+
return loadValues(seq, editSeqAtStart).then((taken) => {
|
|
1953
1975
|
if (seq === valuesSeq) {
|
|
1954
1976
|
valuesLoading = false;
|
|
1955
1977
|
}
|
|
1978
|
+
return taken;
|
|
1956
1979
|
});
|
|
1957
1980
|
}
|
|
1981
|
+
/** Repaints the tab once a load is over, or, with edits made meanwhile kept, only the "Refreshing…" dot. */
|
|
1982
|
+
function paintLoaded(taken) {
|
|
1983
|
+
if (taken) {
|
|
1984
|
+
render();
|
|
1985
|
+
}
|
|
1986
|
+
else {
|
|
1987
|
+
updateLoadingIndicator();
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1958
1990
|
// The manual "↻ Refresh" button — deliberately NOT debounced, so it's always the fast path
|
|
1959
1991
|
// for "I just changed something in MI and want it now" (see scheduleAutoLoad for the
|
|
1960
1992
|
// debounced, tab-switch-driven path).
|
|
@@ -1965,8 +1997,7 @@ function startVariablesEditor({ templateLess }) {
|
|
|
1965
1997
|
// render() has to run AFTER that call starts (not before) for the indicator to reflect it.
|
|
1966
1998
|
const pending = loadTabData(activeTab);
|
|
1967
1999
|
render();
|
|
1968
|
-
await pending;
|
|
1969
|
-
render();
|
|
2000
|
+
paintLoaded(await pending);
|
|
1970
2001
|
}
|
|
1971
2002
|
function refresh() {
|
|
1972
2003
|
if (dirty) {
|
|
@@ -1990,9 +2021,9 @@ function startVariablesEditor({ templateLess }) {
|
|
|
1990
2021
|
if (activeTab === tab) {
|
|
1991
2022
|
render();
|
|
1992
2023
|
}
|
|
1993
|
-
void pending.then(() => {
|
|
2024
|
+
void pending.then((taken) => {
|
|
1994
2025
|
if (activeTab === tab) {
|
|
1995
|
-
|
|
2026
|
+
paintLoaded(taken);
|
|
1996
2027
|
}
|
|
1997
2028
|
});
|
|
1998
2029
|
}, AUTO_LOAD_DEBOUNCE_MS);
|
|
@@ -2488,6 +2519,22 @@ function startVariablesEditor({ templateLess }) {
|
|
|
2488
2519
|
showSaveError(new Error(message));
|
|
2489
2520
|
return null;
|
|
2490
2521
|
}
|
|
2522
|
+
// The tab's reload after a save, which the save's own banner outlives: a reload that fails is
|
|
2523
|
+
// not a failed save, and says so next to it.
|
|
2524
|
+
async function reloadAfterSave(tab) {
|
|
2525
|
+
const saved = banner;
|
|
2526
|
+
await loadTabData(tab);
|
|
2527
|
+
if (saved && banner !== saved && banner?.type === 'error') {
|
|
2528
|
+
banner = {
|
|
2529
|
+
type: 'warning',
|
|
2530
|
+
html: saved.html +
|
|
2531
|
+
'<div>The save went through, but reloading afterwards did not: ' +
|
|
2532
|
+
banner.html +
|
|
2533
|
+
' Use ↻ Refresh.</div>',
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
render();
|
|
2537
|
+
}
|
|
2491
2538
|
async function commitSchemaSave(raw) {
|
|
2492
2539
|
const saveEditSeq = editSeq;
|
|
2493
2540
|
const data = await putSave('/@api/variables/schema', { raw });
|
|
@@ -2506,8 +2553,7 @@ function startVariablesEditor({ templateLess }) {
|
|
|
2506
2553
|
setDirty(false);
|
|
2507
2554
|
showBanner(warnings.length ? 'warning' : 'success', message);
|
|
2508
2555
|
jsonBaseline.schema = raw;
|
|
2509
|
-
await
|
|
2510
|
-
render();
|
|
2556
|
+
await reloadAfterSave('schema');
|
|
2511
2557
|
}
|
|
2512
2558
|
// ── Values tab ───────────────────────────────────────────────────────────────
|
|
2513
2559
|
function onComboFocus(i) {
|
|
@@ -3045,10 +3091,16 @@ function startVariablesEditor({ templateLess }) {
|
|
|
3045
3091
|
showBanner('success', 'Imported ' + applied + ' of ' + total + ' variable(s) from "' + escapeHtml(pageName) + '". Save to apply.');
|
|
3046
3092
|
}
|
|
3047
3093
|
function addValueRow() {
|
|
3048
|
-
showCustomValueModal('Variable name', '', (
|
|
3094
|
+
showCustomValueModal('Variable name', '', (entered) => {
|
|
3095
|
+
const name = (entered || '').trim();
|
|
3049
3096
|
if (!name) {
|
|
3050
3097
|
return;
|
|
3051
3098
|
}
|
|
3099
|
+
// MI keeps one value per name: a second row of that name would lose one of the two on save.
|
|
3100
|
+
if (valueRows.some((row) => row.name === name)) {
|
|
3101
|
+
showBanner('error', 'A variable named "' + escapeHtml(name) + '" is in the list already.');
|
|
3102
|
+
return;
|
|
3103
|
+
}
|
|
3052
3104
|
valueRows.push({ name, value: '' });
|
|
3053
3105
|
selectedValueIndex = valueRows.length - 1;
|
|
3054
3106
|
setDirty(true);
|
|
@@ -3125,8 +3177,7 @@ function startVariablesEditor({ templateLess }) {
|
|
|
3125
3177
|
setDirty(false);
|
|
3126
3178
|
showBanner(warnings.length ? 'warning' : 'success', message);
|
|
3127
3179
|
jsonBaseline.values = afterText;
|
|
3128
|
-
await
|
|
3129
|
-
render();
|
|
3180
|
+
await reloadAfterSave('values');
|
|
3130
3181
|
}
|
|
3131
3182
|
function save() {
|
|
3132
3183
|
(activeTab === 'schema' ? saveSchema() : saveValues()).catch(showSaveError);
|
|
@@ -3220,14 +3271,18 @@ function startVariablesEditor({ templateLess }) {
|
|
|
3220
3271
|
if (activeTab === 'values' && valuesRawMode) {
|
|
3221
3272
|
refreshValuesJsonIssues();
|
|
3222
3273
|
}
|
|
3274
|
+
updateLoadingIndicator();
|
|
3275
|
+
}
|
|
3276
|
+
// The "Refreshing…" dot, shown only over cached content already on screen underneath it —
|
|
3277
|
+
// when there is none (state is still null), renderSkeleton() is the loading signal.
|
|
3278
|
+
function updateLoadingIndicator() {
|
|
3223
3279
|
const loadingEl = byId('loading-indicator');
|
|
3224
|
-
if (loadingEl) {
|
|
3225
|
-
|
|
3226
|
-
// when there isn't (state is still null), renderSkeleton() above is the loading signal.
|
|
3227
|
-
const stateForTab = activeTab === 'schema' ? schemaState : valuesState;
|
|
3228
|
-
const isLoading = activeTab === 'schema' ? schemaLoading : valuesLoading;
|
|
3229
|
-
loadingEl.style.display = isLoading && stateForTab ? '' : 'none';
|
|
3280
|
+
if (!loadingEl) {
|
|
3281
|
+
return;
|
|
3230
3282
|
}
|
|
3283
|
+
const stateForTab = activeTab === 'schema' ? schemaState : valuesState;
|
|
3284
|
+
const isLoading = activeTab === 'schema' ? schemaLoading : valuesLoading;
|
|
3285
|
+
loadingEl.style.display = isLoading && stateForTab ? '' : 'none';
|
|
3231
3286
|
}
|
|
3232
3287
|
// ── Events ───────────────────────────────────────────────────────────────────
|
|
3233
3288
|
// One listener per event type for the whole page — the toolbar, the tab's content and the
|