@narrative.io/jsonforms-provider-protocols 3.0.0-beta.17 → 3.0.0-beta.19
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/core/initFormData.d.ts.map +1 -1
- package/dist/core/initFormData.js +2 -0
- package/dist/core/initFormData.js.map +1 -1
- package/dist/core/projection.d.ts +3 -3
- package/dist/core/projection.d.ts.map +1 -1
- package/dist/core/projection.js +25 -16
- package/dist/core/projection.js.map +1 -1
- package/dist/core/refs.d.ts +16 -0
- package/dist/core/refs.d.ts.map +1 -1
- package/dist/core/refs.js +18 -1
- package/dist/core/refs.js.map +1 -1
- package/dist/core/resolveScope.d.ts +5 -5
- package/dist/core/resolveScope.d.ts.map +1 -1
- package/dist/core/resolveScope.js +31 -11
- package/dist/core/resolveScope.js.map +1 -1
- package/dist/debug/overlay.d.ts +18 -0
- package/dist/debug/overlay.d.ts.map +1 -0
- package/dist/debug/overlay.js +308 -0
- package/dist/debug/overlay.js.map +1 -0
- package/package.json +1 -1
- package/src/core/initFormData.ts +2 -0
- package/src/core/projection.ts +36 -21
- package/src/core/refs.ts +52 -0
- package/src/core/resolveScope.ts +38 -16
- package/src/debug/overlay.ts +358 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-DOM debug overlay for `@narrative.io/jsonforms-provider-protocols`.
|
|
3
|
+
*
|
|
4
|
+
* Renders a fixed-position panel with a scrollable list of structured log
|
|
5
|
+
* entries. Used in place of `console.*` because some host apps strip console
|
|
6
|
+
* output in production. No-op unless explicitly enabled at runtime via
|
|
7
|
+
* either `window.__PP_DEBUG__ = true` or `localStorage.PP_DEBUG = "1"`.
|
|
8
|
+
*
|
|
9
|
+
* Internal API. Not part of the package's public surface.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const RING_SIZE = 250;
|
|
13
|
+
const PANEL_ID = "__pp_debug_panel__";
|
|
14
|
+
const STYLE_ID = "__pp_debug_styles__";
|
|
15
|
+
|
|
16
|
+
interface Entry {
|
|
17
|
+
ts: number;
|
|
18
|
+
label: string;
|
|
19
|
+
json: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let entries: Entry[] = [];
|
|
23
|
+
let panel: HTMLElement | null = null;
|
|
24
|
+
let listEl: HTMLElement | null = null;
|
|
25
|
+
|
|
26
|
+
function isEnabled(): boolean {
|
|
27
|
+
if (typeof window === "undefined") return false;
|
|
28
|
+
const w = window as unknown as { __PP_DEBUG__?: boolean };
|
|
29
|
+
if (w.__PP_DEBUG__ === true) return true;
|
|
30
|
+
try {
|
|
31
|
+
return window.localStorage?.getItem("PP_DEBUG") === "1";
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function safeStringify(value: unknown): string {
|
|
38
|
+
const seen = new WeakSet<object>();
|
|
39
|
+
try {
|
|
40
|
+
const out = JSON.stringify(
|
|
41
|
+
value,
|
|
42
|
+
(_key, v) => {
|
|
43
|
+
if (typeof v === "object" && v !== null) {
|
|
44
|
+
if (seen.has(v as object)) return "[Circular]";
|
|
45
|
+
seen.add(v as object);
|
|
46
|
+
}
|
|
47
|
+
if (typeof v === "function")
|
|
48
|
+
return `[Function ${(v as { name?: string }).name || "anonymous"}]`;
|
|
49
|
+
if (typeof v === "undefined") return "[undefined]";
|
|
50
|
+
return v;
|
|
51
|
+
},
|
|
52
|
+
2,
|
|
53
|
+
);
|
|
54
|
+
return out ?? String(value);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return `[unstringifiable: ${(e as Error).message}]`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function ensureStyles(): void {
|
|
61
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
62
|
+
const style = document.createElement("style");
|
|
63
|
+
style.id = STYLE_ID;
|
|
64
|
+
style.textContent = `
|
|
65
|
+
#${PANEL_ID} {
|
|
66
|
+
position: fixed;
|
|
67
|
+
top: 12px;
|
|
68
|
+
left: 12px;
|
|
69
|
+
width: 460px;
|
|
70
|
+
max-height: 70vh;
|
|
71
|
+
z-index: 2147483647;
|
|
72
|
+
background: #0b0f17;
|
|
73
|
+
color: #d6e1ff;
|
|
74
|
+
border: 1px solid #2a3656;
|
|
75
|
+
border-radius: 6px;
|
|
76
|
+
font: 12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
77
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.45);
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
resize: both;
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
}
|
|
83
|
+
#${PANEL_ID} .pp-header {
|
|
84
|
+
display: flex;
|
|
85
|
+
gap: 6px;
|
|
86
|
+
align-items: center;
|
|
87
|
+
padding: 6px 8px;
|
|
88
|
+
background: #131a2c;
|
|
89
|
+
border-bottom: 1px solid #2a3656;
|
|
90
|
+
cursor: move;
|
|
91
|
+
user-select: none;
|
|
92
|
+
flex-shrink: 0;
|
|
93
|
+
}
|
|
94
|
+
#${PANEL_ID} .pp-title {
|
|
95
|
+
flex: 1;
|
|
96
|
+
font-weight: 600;
|
|
97
|
+
color: #9fb4ff;
|
|
98
|
+
}
|
|
99
|
+
#${PANEL_ID} .pp-count {
|
|
100
|
+
color: #6f7fa6;
|
|
101
|
+
font-weight: 400;
|
|
102
|
+
margin-left: 4px;
|
|
103
|
+
}
|
|
104
|
+
#${PANEL_ID} button {
|
|
105
|
+
background: #1f2a47;
|
|
106
|
+
color: #d6e1ff;
|
|
107
|
+
border: 1px solid #2a3656;
|
|
108
|
+
border-radius: 3px;
|
|
109
|
+
padding: 2px 6px;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
font: inherit;
|
|
112
|
+
line-height: 1.2;
|
|
113
|
+
}
|
|
114
|
+
#${PANEL_ID} button:hover { background: #2a3656; }
|
|
115
|
+
#${PANEL_ID} .pp-list {
|
|
116
|
+
flex: 1;
|
|
117
|
+
overflow: auto;
|
|
118
|
+
padding: 4px 6px;
|
|
119
|
+
}
|
|
120
|
+
#${PANEL_ID}.pp-collapsed { resize: none; max-height: none; }
|
|
121
|
+
#${PANEL_ID}.pp-collapsed .pp-list { display: none; }
|
|
122
|
+
#${PANEL_ID} details {
|
|
123
|
+
margin: 4px 0;
|
|
124
|
+
border: 1px solid #2a3656;
|
|
125
|
+
border-radius: 3px;
|
|
126
|
+
background: #0f1422;
|
|
127
|
+
}
|
|
128
|
+
#${PANEL_ID} summary {
|
|
129
|
+
padding: 4px 6px;
|
|
130
|
+
cursor: pointer;
|
|
131
|
+
display: flex;
|
|
132
|
+
gap: 6px;
|
|
133
|
+
align-items: center;
|
|
134
|
+
list-style: none;
|
|
135
|
+
}
|
|
136
|
+
#${PANEL_ID} summary::-webkit-details-marker { display: none; }
|
|
137
|
+
#${PANEL_ID} summary::before {
|
|
138
|
+
content: "▸";
|
|
139
|
+
color: #6f7fa6;
|
|
140
|
+
width: 10px;
|
|
141
|
+
}
|
|
142
|
+
#${PANEL_ID} details[open] > summary::before { content: "▾"; }
|
|
143
|
+
#${PANEL_ID} .pp-label {
|
|
144
|
+
color: #ffd479;
|
|
145
|
+
font-weight: 600;
|
|
146
|
+
}
|
|
147
|
+
#${PANEL_ID} .pp-ts {
|
|
148
|
+
color: #6f7fa6;
|
|
149
|
+
}
|
|
150
|
+
#${PANEL_ID} summary button { margin-left: auto; }
|
|
151
|
+
#${PANEL_ID} pre {
|
|
152
|
+
margin: 0;
|
|
153
|
+
padding: 6px 8px;
|
|
154
|
+
max-height: 320px;
|
|
155
|
+
overflow: auto;
|
|
156
|
+
background: #050811;
|
|
157
|
+
color: #c9d6ff;
|
|
158
|
+
white-space: pre-wrap;
|
|
159
|
+
word-break: break-word;
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
document.head.appendChild(style);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function makeButton(label: string, onClick: (e: MouseEvent) => void): HTMLButtonElement {
|
|
166
|
+
const btn = document.createElement("button");
|
|
167
|
+
btn.type = "button";
|
|
168
|
+
btn.textContent = label;
|
|
169
|
+
btn.addEventListener("click", onClick);
|
|
170
|
+
return btn;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function flashButton(btn: HTMLButtonElement, msg: string, original: string): void {
|
|
174
|
+
btn.textContent = msg;
|
|
175
|
+
setTimeout(() => {
|
|
176
|
+
btn.textContent = original;
|
|
177
|
+
}, 1200);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function copyToClipboard(text: string, btn: HTMLButtonElement, original: string): void {
|
|
181
|
+
const fallback = () => {
|
|
182
|
+
try {
|
|
183
|
+
const ta = document.createElement("textarea");
|
|
184
|
+
ta.value = text;
|
|
185
|
+
ta.style.position = "fixed";
|
|
186
|
+
ta.style.left = "-9999px";
|
|
187
|
+
document.body.appendChild(ta);
|
|
188
|
+
ta.select();
|
|
189
|
+
document.execCommand("copy");
|
|
190
|
+
document.body.removeChild(ta);
|
|
191
|
+
flashButton(btn, "copied", original);
|
|
192
|
+
} catch {
|
|
193
|
+
flashButton(btn, "fail", original);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
if (navigator.clipboard?.writeText) {
|
|
197
|
+
navigator.clipboard.writeText(text).then(
|
|
198
|
+
() => flashButton(btn, "copied", original),
|
|
199
|
+
fallback,
|
|
200
|
+
);
|
|
201
|
+
} else {
|
|
202
|
+
fallback();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function updateCount(): void {
|
|
207
|
+
if (!panel) return;
|
|
208
|
+
const countEl = panel.querySelector(".pp-count");
|
|
209
|
+
if (countEl) countEl.textContent = `(${entries.length})`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function attachDrag(panelEl: HTMLElement, header: HTMLElement): void {
|
|
213
|
+
let dragging = false;
|
|
214
|
+
let offX = 0;
|
|
215
|
+
let offY = 0;
|
|
216
|
+
header.addEventListener("mousedown", (e) => {
|
|
217
|
+
if ((e.target as HTMLElement).tagName === "BUTTON") return;
|
|
218
|
+
dragging = true;
|
|
219
|
+
const rect = panelEl.getBoundingClientRect();
|
|
220
|
+
offX = e.clientX - rect.left;
|
|
221
|
+
offY = e.clientY - rect.top;
|
|
222
|
+
e.preventDefault();
|
|
223
|
+
});
|
|
224
|
+
window.addEventListener("mousemove", (e) => {
|
|
225
|
+
if (!dragging) return;
|
|
226
|
+
panelEl.style.left = `${e.clientX - offX}px`;
|
|
227
|
+
panelEl.style.top = `${e.clientY - offY}px`;
|
|
228
|
+
});
|
|
229
|
+
window.addEventListener("mouseup", () => {
|
|
230
|
+
dragging = false;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function ensurePanel(): HTMLElement {
|
|
235
|
+
if (panel && document.body.contains(panel)) return panel;
|
|
236
|
+
ensureStyles();
|
|
237
|
+
|
|
238
|
+
panel = document.createElement("div");
|
|
239
|
+
panel.id = PANEL_ID;
|
|
240
|
+
|
|
241
|
+
const header = document.createElement("div");
|
|
242
|
+
header.className = "pp-header";
|
|
243
|
+
|
|
244
|
+
const title = document.createElement("span");
|
|
245
|
+
title.className = "pp-title";
|
|
246
|
+
title.textContent = "[provider-protocols] debug";
|
|
247
|
+
const count = document.createElement("span");
|
|
248
|
+
count.className = "pp-count";
|
|
249
|
+
count.textContent = `(${entries.length})`;
|
|
250
|
+
title.appendChild(count);
|
|
251
|
+
header.appendChild(title);
|
|
252
|
+
|
|
253
|
+
const copyAllBtn = makeButton("copy all", (e) => {
|
|
254
|
+
e.stopPropagation();
|
|
255
|
+
const blob = entries
|
|
256
|
+
.map((en) => `// ${new Date(en.ts).toISOString()} ${en.label}\n${en.json}`)
|
|
257
|
+
.join("\n\n");
|
|
258
|
+
copyToClipboard(blob, copyAllBtn, "copy all");
|
|
259
|
+
});
|
|
260
|
+
header.appendChild(copyAllBtn);
|
|
261
|
+
|
|
262
|
+
const clearBtn = makeButton("clear", (e) => {
|
|
263
|
+
e.stopPropagation();
|
|
264
|
+
entries = [];
|
|
265
|
+
if (listEl) listEl.innerHTML = "";
|
|
266
|
+
updateCount();
|
|
267
|
+
});
|
|
268
|
+
header.appendChild(clearBtn);
|
|
269
|
+
|
|
270
|
+
const collapseBtn = makeButton("−", (e) => {
|
|
271
|
+
e.stopPropagation();
|
|
272
|
+
panel!.classList.toggle("pp-collapsed");
|
|
273
|
+
collapseBtn.textContent = panel!.classList.contains("pp-collapsed") ? "+" : "−";
|
|
274
|
+
});
|
|
275
|
+
header.appendChild(collapseBtn);
|
|
276
|
+
|
|
277
|
+
attachDrag(panel, header);
|
|
278
|
+
panel.appendChild(header);
|
|
279
|
+
|
|
280
|
+
listEl = document.createElement("div");
|
|
281
|
+
listEl.className = "pp-list";
|
|
282
|
+
panel.appendChild(listEl);
|
|
283
|
+
|
|
284
|
+
document.body.appendChild(panel);
|
|
285
|
+
return panel;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function renderEntry(entry: Entry): void {
|
|
289
|
+
if (!listEl) return;
|
|
290
|
+
const det = document.createElement("details");
|
|
291
|
+
det.open = false;
|
|
292
|
+
|
|
293
|
+
const sum = document.createElement("summary");
|
|
294
|
+
const lbl = document.createElement("span");
|
|
295
|
+
lbl.className = "pp-label";
|
|
296
|
+
lbl.textContent = entry.label;
|
|
297
|
+
const ts = document.createElement("span");
|
|
298
|
+
ts.className = "pp-ts";
|
|
299
|
+
ts.textContent = new Date(entry.ts).toISOString().slice(11, 23);
|
|
300
|
+
const copyBtn = makeButton("copy", (e) => {
|
|
301
|
+
e.preventDefault();
|
|
302
|
+
e.stopPropagation();
|
|
303
|
+
copyToClipboard(entry.json, copyBtn, "copy");
|
|
304
|
+
});
|
|
305
|
+
sum.appendChild(lbl);
|
|
306
|
+
sum.appendChild(ts);
|
|
307
|
+
sum.appendChild(copyBtn);
|
|
308
|
+
det.appendChild(sum);
|
|
309
|
+
|
|
310
|
+
const pre = document.createElement("pre");
|
|
311
|
+
pre.textContent = entry.json;
|
|
312
|
+
det.appendChild(pre);
|
|
313
|
+
|
|
314
|
+
listEl.appendChild(det);
|
|
315
|
+
listEl.scrollTop = listEl.scrollHeight;
|
|
316
|
+
|
|
317
|
+
while (listEl.children.length > RING_SIZE) {
|
|
318
|
+
const first = listEl.firstChild;
|
|
319
|
+
if (first) listEl.removeChild(first);
|
|
320
|
+
else break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Append a structured debug entry to the in-DOM overlay. No-op unless the
|
|
326
|
+
* runtime gate is on (`window.__PP_DEBUG__ === true` or
|
|
327
|
+
* `localStorage.PP_DEBUG === "1"`). Stringification is lazy: the payload is
|
|
328
|
+
* not serialized when the gate is off, so this is safe to leave in hot paths.
|
|
329
|
+
*/
|
|
330
|
+
export function debugLog(label: string, payload: unknown): void {
|
|
331
|
+
if (!isEnabled()) return;
|
|
332
|
+
if (typeof document === "undefined") return;
|
|
333
|
+
|
|
334
|
+
const entry: Entry = {
|
|
335
|
+
ts: Date.now(),
|
|
336
|
+
label,
|
|
337
|
+
json: safeStringify(payload),
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
entries.push(entry);
|
|
341
|
+
if (entries.length > RING_SIZE) entries.shift();
|
|
342
|
+
|
|
343
|
+
const append = () => {
|
|
344
|
+
try {
|
|
345
|
+
ensurePanel();
|
|
346
|
+
renderEntry(entry);
|
|
347
|
+
updateCount();
|
|
348
|
+
} catch {
|
|
349
|
+
// never break the host on debug failure
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
if (document.body) {
|
|
354
|
+
append();
|
|
355
|
+
} else {
|
|
356
|
+
document.addEventListener("DOMContentLoaded", append, { once: true });
|
|
357
|
+
}
|
|
358
|
+
}
|