@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,308 @@
|
|
|
1
|
+
const RING_SIZE = 250;
|
|
2
|
+
const PANEL_ID = "__pp_debug_panel__";
|
|
3
|
+
const STYLE_ID = "__pp_debug_styles__";
|
|
4
|
+
let entries = [];
|
|
5
|
+
let panel = null;
|
|
6
|
+
let listEl = null;
|
|
7
|
+
function isEnabled() {
|
|
8
|
+
if (typeof window === "undefined") return false;
|
|
9
|
+
const w = window;
|
|
10
|
+
if (w.__PP_DEBUG__ === true) return true;
|
|
11
|
+
try {
|
|
12
|
+
return window.localStorage?.getItem("PP_DEBUG") === "1";
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function safeStringify(value) {
|
|
18
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
19
|
+
try {
|
|
20
|
+
const out = JSON.stringify(
|
|
21
|
+
value,
|
|
22
|
+
(_key, v) => {
|
|
23
|
+
if (typeof v === "object" && v !== null) {
|
|
24
|
+
if (seen.has(v)) return "[Circular]";
|
|
25
|
+
seen.add(v);
|
|
26
|
+
}
|
|
27
|
+
if (typeof v === "function")
|
|
28
|
+
return `[Function ${v.name || "anonymous"}]`;
|
|
29
|
+
if (typeof v === "undefined") return "[undefined]";
|
|
30
|
+
return v;
|
|
31
|
+
},
|
|
32
|
+
2
|
|
33
|
+
);
|
|
34
|
+
return out ?? String(value);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return `[unstringifiable: ${e.message}]`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function ensureStyles() {
|
|
40
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
41
|
+
const style = document.createElement("style");
|
|
42
|
+
style.id = STYLE_ID;
|
|
43
|
+
style.textContent = `
|
|
44
|
+
#${PANEL_ID} {
|
|
45
|
+
position: fixed;
|
|
46
|
+
top: 12px;
|
|
47
|
+
left: 12px;
|
|
48
|
+
width: 460px;
|
|
49
|
+
max-height: 70vh;
|
|
50
|
+
z-index: 2147483647;
|
|
51
|
+
background: #0b0f17;
|
|
52
|
+
color: #d6e1ff;
|
|
53
|
+
border: 1px solid #2a3656;
|
|
54
|
+
border-radius: 6px;
|
|
55
|
+
font: 12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
56
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.45);
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
resize: both;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
}
|
|
62
|
+
#${PANEL_ID} .pp-header {
|
|
63
|
+
display: flex;
|
|
64
|
+
gap: 6px;
|
|
65
|
+
align-items: center;
|
|
66
|
+
padding: 6px 8px;
|
|
67
|
+
background: #131a2c;
|
|
68
|
+
border-bottom: 1px solid #2a3656;
|
|
69
|
+
cursor: move;
|
|
70
|
+
user-select: none;
|
|
71
|
+
flex-shrink: 0;
|
|
72
|
+
}
|
|
73
|
+
#${PANEL_ID} .pp-title {
|
|
74
|
+
flex: 1;
|
|
75
|
+
font-weight: 600;
|
|
76
|
+
color: #9fb4ff;
|
|
77
|
+
}
|
|
78
|
+
#${PANEL_ID} .pp-count {
|
|
79
|
+
color: #6f7fa6;
|
|
80
|
+
font-weight: 400;
|
|
81
|
+
margin-left: 4px;
|
|
82
|
+
}
|
|
83
|
+
#${PANEL_ID} button {
|
|
84
|
+
background: #1f2a47;
|
|
85
|
+
color: #d6e1ff;
|
|
86
|
+
border: 1px solid #2a3656;
|
|
87
|
+
border-radius: 3px;
|
|
88
|
+
padding: 2px 6px;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
font: inherit;
|
|
91
|
+
line-height: 1.2;
|
|
92
|
+
}
|
|
93
|
+
#${PANEL_ID} button:hover { background: #2a3656; }
|
|
94
|
+
#${PANEL_ID} .pp-list {
|
|
95
|
+
flex: 1;
|
|
96
|
+
overflow: auto;
|
|
97
|
+
padding: 4px 6px;
|
|
98
|
+
}
|
|
99
|
+
#${PANEL_ID}.pp-collapsed { resize: none; max-height: none; }
|
|
100
|
+
#${PANEL_ID}.pp-collapsed .pp-list { display: none; }
|
|
101
|
+
#${PANEL_ID} details {
|
|
102
|
+
margin: 4px 0;
|
|
103
|
+
border: 1px solid #2a3656;
|
|
104
|
+
border-radius: 3px;
|
|
105
|
+
background: #0f1422;
|
|
106
|
+
}
|
|
107
|
+
#${PANEL_ID} summary {
|
|
108
|
+
padding: 4px 6px;
|
|
109
|
+
cursor: pointer;
|
|
110
|
+
display: flex;
|
|
111
|
+
gap: 6px;
|
|
112
|
+
align-items: center;
|
|
113
|
+
list-style: none;
|
|
114
|
+
}
|
|
115
|
+
#${PANEL_ID} summary::-webkit-details-marker { display: none; }
|
|
116
|
+
#${PANEL_ID} summary::before {
|
|
117
|
+
content: "▸";
|
|
118
|
+
color: #6f7fa6;
|
|
119
|
+
width: 10px;
|
|
120
|
+
}
|
|
121
|
+
#${PANEL_ID} details[open] > summary::before { content: "▾"; }
|
|
122
|
+
#${PANEL_ID} .pp-label {
|
|
123
|
+
color: #ffd479;
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
}
|
|
126
|
+
#${PANEL_ID} .pp-ts {
|
|
127
|
+
color: #6f7fa6;
|
|
128
|
+
}
|
|
129
|
+
#${PANEL_ID} summary button { margin-left: auto; }
|
|
130
|
+
#${PANEL_ID} pre {
|
|
131
|
+
margin: 0;
|
|
132
|
+
padding: 6px 8px;
|
|
133
|
+
max-height: 320px;
|
|
134
|
+
overflow: auto;
|
|
135
|
+
background: #050811;
|
|
136
|
+
color: #c9d6ff;
|
|
137
|
+
white-space: pre-wrap;
|
|
138
|
+
word-break: break-word;
|
|
139
|
+
}
|
|
140
|
+
`;
|
|
141
|
+
document.head.appendChild(style);
|
|
142
|
+
}
|
|
143
|
+
function makeButton(label, onClick) {
|
|
144
|
+
const btn = document.createElement("button");
|
|
145
|
+
btn.type = "button";
|
|
146
|
+
btn.textContent = label;
|
|
147
|
+
btn.addEventListener("click", onClick);
|
|
148
|
+
return btn;
|
|
149
|
+
}
|
|
150
|
+
function flashButton(btn, msg, original) {
|
|
151
|
+
btn.textContent = msg;
|
|
152
|
+
setTimeout(() => {
|
|
153
|
+
btn.textContent = original;
|
|
154
|
+
}, 1200);
|
|
155
|
+
}
|
|
156
|
+
function copyToClipboard(text, btn, original) {
|
|
157
|
+
const fallback = () => {
|
|
158
|
+
try {
|
|
159
|
+
const ta = document.createElement("textarea");
|
|
160
|
+
ta.value = text;
|
|
161
|
+
ta.style.position = "fixed";
|
|
162
|
+
ta.style.left = "-9999px";
|
|
163
|
+
document.body.appendChild(ta);
|
|
164
|
+
ta.select();
|
|
165
|
+
document.execCommand("copy");
|
|
166
|
+
document.body.removeChild(ta);
|
|
167
|
+
flashButton(btn, "copied", original);
|
|
168
|
+
} catch {
|
|
169
|
+
flashButton(btn, "fail", original);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
if (navigator.clipboard?.writeText) {
|
|
173
|
+
navigator.clipboard.writeText(text).then(
|
|
174
|
+
() => flashButton(btn, "copied", original),
|
|
175
|
+
fallback
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
fallback();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function updateCount() {
|
|
182
|
+
if (!panel) return;
|
|
183
|
+
const countEl = panel.querySelector(".pp-count");
|
|
184
|
+
if (countEl) countEl.textContent = `(${entries.length})`;
|
|
185
|
+
}
|
|
186
|
+
function attachDrag(panelEl, header) {
|
|
187
|
+
let dragging = false;
|
|
188
|
+
let offX = 0;
|
|
189
|
+
let offY = 0;
|
|
190
|
+
header.addEventListener("mousedown", (e) => {
|
|
191
|
+
if (e.target.tagName === "BUTTON") return;
|
|
192
|
+
dragging = true;
|
|
193
|
+
const rect = panelEl.getBoundingClientRect();
|
|
194
|
+
offX = e.clientX - rect.left;
|
|
195
|
+
offY = e.clientY - rect.top;
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
});
|
|
198
|
+
window.addEventListener("mousemove", (e) => {
|
|
199
|
+
if (!dragging) return;
|
|
200
|
+
panelEl.style.left = `${e.clientX - offX}px`;
|
|
201
|
+
panelEl.style.top = `${e.clientY - offY}px`;
|
|
202
|
+
});
|
|
203
|
+
window.addEventListener("mouseup", () => {
|
|
204
|
+
dragging = false;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function ensurePanel() {
|
|
208
|
+
if (panel && document.body.contains(panel)) return panel;
|
|
209
|
+
ensureStyles();
|
|
210
|
+
panel = document.createElement("div");
|
|
211
|
+
panel.id = PANEL_ID;
|
|
212
|
+
const header = document.createElement("div");
|
|
213
|
+
header.className = "pp-header";
|
|
214
|
+
const title = document.createElement("span");
|
|
215
|
+
title.className = "pp-title";
|
|
216
|
+
title.textContent = "[provider-protocols] debug";
|
|
217
|
+
const count = document.createElement("span");
|
|
218
|
+
count.className = "pp-count";
|
|
219
|
+
count.textContent = `(${entries.length})`;
|
|
220
|
+
title.appendChild(count);
|
|
221
|
+
header.appendChild(title);
|
|
222
|
+
const copyAllBtn = makeButton("copy all", (e) => {
|
|
223
|
+
e.stopPropagation();
|
|
224
|
+
const blob = entries.map((en) => `// ${new Date(en.ts).toISOString()} ${en.label}
|
|
225
|
+
${en.json}`).join("\n\n");
|
|
226
|
+
copyToClipboard(blob, copyAllBtn, "copy all");
|
|
227
|
+
});
|
|
228
|
+
header.appendChild(copyAllBtn);
|
|
229
|
+
const clearBtn = makeButton("clear", (e) => {
|
|
230
|
+
e.stopPropagation();
|
|
231
|
+
entries = [];
|
|
232
|
+
if (listEl) listEl.innerHTML = "";
|
|
233
|
+
updateCount();
|
|
234
|
+
});
|
|
235
|
+
header.appendChild(clearBtn);
|
|
236
|
+
const collapseBtn = makeButton("−", (e) => {
|
|
237
|
+
e.stopPropagation();
|
|
238
|
+
panel.classList.toggle("pp-collapsed");
|
|
239
|
+
collapseBtn.textContent = panel.classList.contains("pp-collapsed") ? "+" : "−";
|
|
240
|
+
});
|
|
241
|
+
header.appendChild(collapseBtn);
|
|
242
|
+
attachDrag(panel, header);
|
|
243
|
+
panel.appendChild(header);
|
|
244
|
+
listEl = document.createElement("div");
|
|
245
|
+
listEl.className = "pp-list";
|
|
246
|
+
panel.appendChild(listEl);
|
|
247
|
+
document.body.appendChild(panel);
|
|
248
|
+
return panel;
|
|
249
|
+
}
|
|
250
|
+
function renderEntry(entry) {
|
|
251
|
+
if (!listEl) return;
|
|
252
|
+
const det = document.createElement("details");
|
|
253
|
+
det.open = false;
|
|
254
|
+
const sum = document.createElement("summary");
|
|
255
|
+
const lbl = document.createElement("span");
|
|
256
|
+
lbl.className = "pp-label";
|
|
257
|
+
lbl.textContent = entry.label;
|
|
258
|
+
const ts = document.createElement("span");
|
|
259
|
+
ts.className = "pp-ts";
|
|
260
|
+
ts.textContent = new Date(entry.ts).toISOString().slice(11, 23);
|
|
261
|
+
const copyBtn = makeButton("copy", (e) => {
|
|
262
|
+
e.preventDefault();
|
|
263
|
+
e.stopPropagation();
|
|
264
|
+
copyToClipboard(entry.json, copyBtn, "copy");
|
|
265
|
+
});
|
|
266
|
+
sum.appendChild(lbl);
|
|
267
|
+
sum.appendChild(ts);
|
|
268
|
+
sum.appendChild(copyBtn);
|
|
269
|
+
det.appendChild(sum);
|
|
270
|
+
const pre = document.createElement("pre");
|
|
271
|
+
pre.textContent = entry.json;
|
|
272
|
+
det.appendChild(pre);
|
|
273
|
+
listEl.appendChild(det);
|
|
274
|
+
listEl.scrollTop = listEl.scrollHeight;
|
|
275
|
+
while (listEl.children.length > RING_SIZE) {
|
|
276
|
+
const first = listEl.firstChild;
|
|
277
|
+
if (first) listEl.removeChild(first);
|
|
278
|
+
else break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function debugLog(label, payload) {
|
|
282
|
+
if (!isEnabled()) return;
|
|
283
|
+
if (typeof document === "undefined") return;
|
|
284
|
+
const entry = {
|
|
285
|
+
ts: Date.now(),
|
|
286
|
+
label,
|
|
287
|
+
json: safeStringify(payload)
|
|
288
|
+
};
|
|
289
|
+
entries.push(entry);
|
|
290
|
+
if (entries.length > RING_SIZE) entries.shift();
|
|
291
|
+
const append = () => {
|
|
292
|
+
try {
|
|
293
|
+
ensurePanel();
|
|
294
|
+
renderEntry(entry);
|
|
295
|
+
updateCount();
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
if (document.body) {
|
|
300
|
+
append();
|
|
301
|
+
} else {
|
|
302
|
+
document.addEventListener("DOMContentLoaded", append, { once: true });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
export {
|
|
306
|
+
debugLog
|
|
307
|
+
};
|
|
308
|
+
//# sourceMappingURL=overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.js","sources":["../../src/debug/overlay.ts"],"sourcesContent":["/**\n * In-DOM debug overlay for `@narrative.io/jsonforms-provider-protocols`.\n *\n * Renders a fixed-position panel with a scrollable list of structured log\n * entries. Used in place of `console.*` because some host apps strip console\n * output in production. No-op unless explicitly enabled at runtime via\n * either `window.__PP_DEBUG__ = true` or `localStorage.PP_DEBUG = \"1\"`.\n *\n * Internal API. Not part of the package's public surface.\n */\n\nconst RING_SIZE = 250;\nconst PANEL_ID = \"__pp_debug_panel__\";\nconst STYLE_ID = \"__pp_debug_styles__\";\n\ninterface Entry {\n ts: number;\n label: string;\n json: string;\n}\n\nlet entries: Entry[] = [];\nlet panel: HTMLElement | null = null;\nlet listEl: HTMLElement | null = null;\n\nfunction isEnabled(): boolean {\n if (typeof window === \"undefined\") return false;\n const w = window as unknown as { __PP_DEBUG__?: boolean };\n if (w.__PP_DEBUG__ === true) return true;\n try {\n return window.localStorage?.getItem(\"PP_DEBUG\") === \"1\";\n } catch {\n return false;\n }\n}\n\nfunction safeStringify(value: unknown): string {\n const seen = new WeakSet<object>();\n try {\n const out = JSON.stringify(\n value,\n (_key, v) => {\n if (typeof v === \"object\" && v !== null) {\n if (seen.has(v as object)) return \"[Circular]\";\n seen.add(v as object);\n }\n if (typeof v === \"function\")\n return `[Function ${(v as { name?: string }).name || \"anonymous\"}]`;\n if (typeof v === \"undefined\") return \"[undefined]\";\n return v;\n },\n 2,\n );\n return out ?? String(value);\n } catch (e) {\n return `[unstringifiable: ${(e as Error).message}]`;\n }\n}\n\nfunction ensureStyles(): void {\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = `\n#${PANEL_ID} {\n position: fixed;\n top: 12px;\n left: 12px;\n width: 460px;\n max-height: 70vh;\n z-index: 2147483647;\n background: #0b0f17;\n color: #d6e1ff;\n border: 1px solid #2a3656;\n border-radius: 6px;\n font: 12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;\n box-shadow: 0 8px 32px rgba(0,0,0,0.45);\n display: flex;\n flex-direction: column;\n resize: both;\n overflow: hidden;\n}\n#${PANEL_ID} .pp-header {\n display: flex;\n gap: 6px;\n align-items: center;\n padding: 6px 8px;\n background: #131a2c;\n border-bottom: 1px solid #2a3656;\n cursor: move;\n user-select: none;\n flex-shrink: 0;\n}\n#${PANEL_ID} .pp-title {\n flex: 1;\n font-weight: 600;\n color: #9fb4ff;\n}\n#${PANEL_ID} .pp-count {\n color: #6f7fa6;\n font-weight: 400;\n margin-left: 4px;\n}\n#${PANEL_ID} button {\n background: #1f2a47;\n color: #d6e1ff;\n border: 1px solid #2a3656;\n border-radius: 3px;\n padding: 2px 6px;\n cursor: pointer;\n font: inherit;\n line-height: 1.2;\n}\n#${PANEL_ID} button:hover { background: #2a3656; }\n#${PANEL_ID} .pp-list {\n flex: 1;\n overflow: auto;\n padding: 4px 6px;\n}\n#${PANEL_ID}.pp-collapsed { resize: none; max-height: none; }\n#${PANEL_ID}.pp-collapsed .pp-list { display: none; }\n#${PANEL_ID} details {\n margin: 4px 0;\n border: 1px solid #2a3656;\n border-radius: 3px;\n background: #0f1422;\n}\n#${PANEL_ID} summary {\n padding: 4px 6px;\n cursor: pointer;\n display: flex;\n gap: 6px;\n align-items: center;\n list-style: none;\n}\n#${PANEL_ID} summary::-webkit-details-marker { display: none; }\n#${PANEL_ID} summary::before {\n content: \"▸\";\n color: #6f7fa6;\n width: 10px;\n}\n#${PANEL_ID} details[open] > summary::before { content: \"▾\"; }\n#${PANEL_ID} .pp-label {\n color: #ffd479;\n font-weight: 600;\n}\n#${PANEL_ID} .pp-ts {\n color: #6f7fa6;\n}\n#${PANEL_ID} summary button { margin-left: auto; }\n#${PANEL_ID} pre {\n margin: 0;\n padding: 6px 8px;\n max-height: 320px;\n overflow: auto;\n background: #050811;\n color: #c9d6ff;\n white-space: pre-wrap;\n word-break: break-word;\n}\n`;\n document.head.appendChild(style);\n}\n\nfunction makeButton(label: string, onClick: (e: MouseEvent) => void): HTMLButtonElement {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.textContent = label;\n btn.addEventListener(\"click\", onClick);\n return btn;\n}\n\nfunction flashButton(btn: HTMLButtonElement, msg: string, original: string): void {\n btn.textContent = msg;\n setTimeout(() => {\n btn.textContent = original;\n }, 1200);\n}\n\nfunction copyToClipboard(text: string, btn: HTMLButtonElement, original: string): void {\n const fallback = () => {\n try {\n const ta = document.createElement(\"textarea\");\n ta.value = text;\n ta.style.position = \"fixed\";\n ta.style.left = \"-9999px\";\n document.body.appendChild(ta);\n ta.select();\n document.execCommand(\"copy\");\n document.body.removeChild(ta);\n flashButton(btn, \"copied\", original);\n } catch {\n flashButton(btn, \"fail\", original);\n }\n };\n if (navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).then(\n () => flashButton(btn, \"copied\", original),\n fallback,\n );\n } else {\n fallback();\n }\n}\n\nfunction updateCount(): void {\n if (!panel) return;\n const countEl = panel.querySelector(\".pp-count\");\n if (countEl) countEl.textContent = `(${entries.length})`;\n}\n\nfunction attachDrag(panelEl: HTMLElement, header: HTMLElement): void {\n let dragging = false;\n let offX = 0;\n let offY = 0;\n header.addEventListener(\"mousedown\", (e) => {\n if ((e.target as HTMLElement).tagName === \"BUTTON\") return;\n dragging = true;\n const rect = panelEl.getBoundingClientRect();\n offX = e.clientX - rect.left;\n offY = e.clientY - rect.top;\n e.preventDefault();\n });\n window.addEventListener(\"mousemove\", (e) => {\n if (!dragging) return;\n panelEl.style.left = `${e.clientX - offX}px`;\n panelEl.style.top = `${e.clientY - offY}px`;\n });\n window.addEventListener(\"mouseup\", () => {\n dragging = false;\n });\n}\n\nfunction ensurePanel(): HTMLElement {\n if (panel && document.body.contains(panel)) return panel;\n ensureStyles();\n\n panel = document.createElement(\"div\");\n panel.id = PANEL_ID;\n\n const header = document.createElement(\"div\");\n header.className = \"pp-header\";\n\n const title = document.createElement(\"span\");\n title.className = \"pp-title\";\n title.textContent = \"[provider-protocols] debug\";\n const count = document.createElement(\"span\");\n count.className = \"pp-count\";\n count.textContent = `(${entries.length})`;\n title.appendChild(count);\n header.appendChild(title);\n\n const copyAllBtn = makeButton(\"copy all\", (e) => {\n e.stopPropagation();\n const blob = entries\n .map((en) => `// ${new Date(en.ts).toISOString()} ${en.label}\\n${en.json}`)\n .join(\"\\n\\n\");\n copyToClipboard(blob, copyAllBtn, \"copy all\");\n });\n header.appendChild(copyAllBtn);\n\n const clearBtn = makeButton(\"clear\", (e) => {\n e.stopPropagation();\n entries = [];\n if (listEl) listEl.innerHTML = \"\";\n updateCount();\n });\n header.appendChild(clearBtn);\n\n const collapseBtn = makeButton(\"−\", (e) => {\n e.stopPropagation();\n panel!.classList.toggle(\"pp-collapsed\");\n collapseBtn.textContent = panel!.classList.contains(\"pp-collapsed\") ? \"+\" : \"−\";\n });\n header.appendChild(collapseBtn);\n\n attachDrag(panel, header);\n panel.appendChild(header);\n\n listEl = document.createElement(\"div\");\n listEl.className = \"pp-list\";\n panel.appendChild(listEl);\n\n document.body.appendChild(panel);\n return panel;\n}\n\nfunction renderEntry(entry: Entry): void {\n if (!listEl) return;\n const det = document.createElement(\"details\");\n det.open = false;\n\n const sum = document.createElement(\"summary\");\n const lbl = document.createElement(\"span\");\n lbl.className = \"pp-label\";\n lbl.textContent = entry.label;\n const ts = document.createElement(\"span\");\n ts.className = \"pp-ts\";\n ts.textContent = new Date(entry.ts).toISOString().slice(11, 23);\n const copyBtn = makeButton(\"copy\", (e) => {\n e.preventDefault();\n e.stopPropagation();\n copyToClipboard(entry.json, copyBtn, \"copy\");\n });\n sum.appendChild(lbl);\n sum.appendChild(ts);\n sum.appendChild(copyBtn);\n det.appendChild(sum);\n\n const pre = document.createElement(\"pre\");\n pre.textContent = entry.json;\n det.appendChild(pre);\n\n listEl.appendChild(det);\n listEl.scrollTop = listEl.scrollHeight;\n\n while (listEl.children.length > RING_SIZE) {\n const first = listEl.firstChild;\n if (first) listEl.removeChild(first);\n else break;\n }\n}\n\n/**\n * Append a structured debug entry to the in-DOM overlay. No-op unless the\n * runtime gate is on (`window.__PP_DEBUG__ === true` or\n * `localStorage.PP_DEBUG === \"1\"`). Stringification is lazy: the payload is\n * not serialized when the gate is off, so this is safe to leave in hot paths.\n */\nexport function debugLog(label: string, payload: unknown): void {\n if (!isEnabled()) return;\n if (typeof document === \"undefined\") return;\n\n const entry: Entry = {\n ts: Date.now(),\n label,\n json: safeStringify(payload),\n };\n\n entries.push(entry);\n if (entries.length > RING_SIZE) entries.shift();\n\n const append = () => {\n try {\n ensurePanel();\n renderEntry(entry);\n updateCount();\n } catch {\n // never break the host on debug failure\n }\n };\n\n if (document.body) {\n append();\n } else {\n document.addEventListener(\"DOMContentLoaded\", append, { once: true });\n }\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/core/initFormData.ts
CHANGED
|
@@ -41,10 +41,12 @@ export function initFormDataFromSchema(
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
debugLog("[initFormData] initFormDataFromSchema", { schema, seed, output: base });
|
|
44
45
|
return base;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
import { resolveRef } from "./refs";
|
|
49
|
+
import { debugLog } from "../debug/overlay";
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
52
|
* Initialize a single property value based on its schema definition.
|
package/src/core/projection.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
deref as derefSchema,
|
|
3
|
+
tryCombinatorBranches,
|
|
4
|
+
} from "./refs";
|
|
5
|
+
import { debugLog } from "../debug/overlay";
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Projection utilities for navigating complex data structures
|
|
@@ -100,9 +104,9 @@ function setAtPath(
|
|
|
100
104
|
* Numeric segments traverse into `items` (array item schema).
|
|
101
105
|
* String segments traverse into `properties[segment]`.
|
|
102
106
|
*
|
|
103
|
-
* `$ref` nodes
|
|
104
|
-
*
|
|
105
|
-
*
|
|
107
|
+
* Dereferences `$ref` nodes transparently at every step, and falls through
|
|
108
|
+
* to `oneOf` / `anyOf` / `allOf` branches when a segment can't resolve
|
|
109
|
+
* directly — picks the first branch that satisfies the navigation.
|
|
106
110
|
*/
|
|
107
111
|
export function getProjectedSchema(
|
|
108
112
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -116,29 +120,40 @@ export function getProjectedSchema(
|
|
|
116
120
|
|
|
117
121
|
for (const seg of segments) {
|
|
118
122
|
current = derefSchema(current, schema);
|
|
119
|
-
if (!current)
|
|
123
|
+
if (!current) {
|
|
124
|
+
debugLog("[projection] empty (deref miss)", { path, segment: seg, rootSchema: schema });
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
120
127
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
const navigate = (
|
|
129
|
+
node: Record<string, unknown>,
|
|
130
|
+
): Record<string, unknown> | undefined => {
|
|
131
|
+
if (typeof seg === "number") {
|
|
132
|
+
const items = (node as { items?: unknown }).items;
|
|
133
|
+
return items && typeof items === "object"
|
|
134
|
+
? (items as Record<string, unknown>)
|
|
135
|
+
: undefined;
|
|
128
136
|
}
|
|
129
|
-
|
|
130
|
-
// Object key → traverse into properties[key]
|
|
131
|
-
const properties = current.properties as
|
|
137
|
+
const properties = (node as { properties?: unknown }).properties as
|
|
132
138
|
| Record<string, Record<string, unknown>>
|
|
133
139
|
| undefined;
|
|
134
|
-
if (properties && properties[seg])
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
140
|
+
if (properties && properties[seg]) return properties[seg];
|
|
141
|
+
return undefined;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
let next = navigate(current);
|
|
145
|
+
if (next === undefined) {
|
|
146
|
+
next = tryCombinatorBranches(current, schema, navigate);
|
|
147
|
+
}
|
|
148
|
+
if (!next) {
|
|
149
|
+
debugLog("[projection] empty (segment unresolved)", { path, segment: seg, current, rootSchema: schema });
|
|
150
|
+
return {};
|
|
139
151
|
}
|
|
152
|
+
current = next;
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
const resolved = derefSchema(current, schema);
|
|
143
|
-
|
|
156
|
+
const out = resolved ?? {};
|
|
157
|
+
debugLog("[projection] getProjectedSchema", { path, output: out, rootSchema: schema });
|
|
158
|
+
return out;
|
|
144
159
|
}
|
package/src/core/refs.ts
CHANGED
|
@@ -74,6 +74,58 @@ export function deref(
|
|
|
74
74
|
return resolveRef(node, root) as Record<string, unknown> | undefined;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Depth limit for combinator (oneOf/anyOf/allOf) branch descent. Schemas
|
|
79
|
+
* rarely nest combinators beyond one or two levels; this guard protects
|
|
80
|
+
* against pathological nesting and cycles (e.g. `oneOf: [$ref back to self]`).
|
|
81
|
+
*/
|
|
82
|
+
const COMBINATOR_DEPTH_LIMIT = 8;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Try to navigate a segment through a schema node's combinator branches
|
|
86
|
+
* (`oneOf` / `anyOf` / `allOf`) when direct navigation has failed.
|
|
87
|
+
*
|
|
88
|
+
* Semantics: for walker purposes (renderer-tester matching), we only need
|
|
89
|
+
* ONE concrete schema that satisfies the next navigation step. First-match
|
|
90
|
+
* by structural shape wins, same convention as `initOneOf` uses for seeding.
|
|
91
|
+
*
|
|
92
|
+
* @param node the schema node to search (already dereffed by caller)
|
|
93
|
+
* @param root the root schema, for dereferencing branch `$ref`s
|
|
94
|
+
* @param tryFn predicate that attempts navigation on a candidate branch
|
|
95
|
+
* and returns the navigated value, or `undefined` if the
|
|
96
|
+
* branch doesn't have what the caller's looking for
|
|
97
|
+
* @param depth recursion depth (capped at `COMBINATOR_DEPTH_LIMIT`)
|
|
98
|
+
*/
|
|
99
|
+
export function tryCombinatorBranches<T>(
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
+
node: Record<string, any> | undefined,
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
+
root: Record<string, any>,
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
+
tryFn: (candidate: Record<string, any>) => T | undefined,
|
|
106
|
+
depth = 0,
|
|
107
|
+
): T | undefined {
|
|
108
|
+
if (depth > COMBINATOR_DEPTH_LIMIT) return undefined;
|
|
109
|
+
if (!node || typeof node !== "object") return undefined;
|
|
110
|
+
|
|
111
|
+
const branches = (node.oneOf || node.anyOf || node.allOf) as
|
|
112
|
+
| Record<string, unknown>[]
|
|
113
|
+
| undefined;
|
|
114
|
+
if (!Array.isArray(branches)) return undefined;
|
|
115
|
+
|
|
116
|
+
for (const raw of branches) {
|
|
117
|
+
const branch = deref(raw as Record<string, unknown>, root);
|
|
118
|
+
if (!branch || typeof branch !== "object") continue;
|
|
119
|
+
|
|
120
|
+
const direct = tryFn(branch);
|
|
121
|
+
if (direct !== undefined) return direct;
|
|
122
|
+
|
|
123
|
+
const nested = tryCombinatorBranches(branch, root, tryFn, depth + 1);
|
|
124
|
+
if (nested !== undefined) return nested;
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
77
129
|
/**
|
|
78
130
|
* Recursively dereference every `$ref` in a schema subtree, producing a
|
|
79
131
|
* concrete schema with no remaining refs. Cycles along any single chain are
|
package/src/core/resolveScope.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { deepDeref, deref } from "./refs";
|
|
1
|
+
import { deepDeref, deref, tryCombinatorBranches } from "./refs";
|
|
2
|
+
import { debugLog } from "../debug/overlay";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Resolve a JSON Forms scope path to its schema within a root schema.
|
|
@@ -9,11 +10,11 @@ import { deepDeref, deref } from "./refs";
|
|
|
9
10
|
* - "items" segments navigate into array `.items`
|
|
10
11
|
* - all other segments index directly into the current object
|
|
11
12
|
*
|
|
12
|
-
* `$ref` nodes
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* deep-dereferenced so downstream walkers
|
|
16
|
-
*
|
|
13
|
+
* Dereferences `$ref` nodes transparently at every step, and falls through
|
|
14
|
+
* to `oneOf` / `anyOf` / `allOf` branches when a segment can't resolve
|
|
15
|
+
* directly — picks the first branch that satisfies the navigation. The
|
|
16
|
+
* returned schema is deep-dereferenced so downstream walkers can operate on
|
|
17
|
+
* a self-contained sub-schema without needing the original root.
|
|
17
18
|
*/
|
|
18
19
|
export function resolveScopeSchema(
|
|
19
20
|
scope: string,
|
|
@@ -21,11 +22,18 @@ export function resolveScopeSchema(
|
|
|
21
22
|
rootSchema: Record<string, any>,
|
|
22
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
24
|
): Record<string, any> | undefined {
|
|
24
|
-
if (!scope || !rootSchema)
|
|
25
|
+
if (!scope || !rootSchema) {
|
|
26
|
+
debugLog("[resolveScope] undefined (empty input)", { scope, rootSchema });
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
25
29
|
|
|
26
30
|
// Remove the leading "#/" and split into segments
|
|
27
31
|
const path = scope.replace(/^#\/?/, "");
|
|
28
|
-
if (!path)
|
|
32
|
+
if (!path) {
|
|
33
|
+
const out = deepDeref(rootSchema, rootSchema);
|
|
34
|
+
debugLog("[resolveScope] root deepDeref", { scope, output: out, rootSchema });
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
29
37
|
|
|
30
38
|
const segments = path.split("/");
|
|
31
39
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -33,16 +41,30 @@ export function resolveScopeSchema(
|
|
|
33
41
|
|
|
34
42
|
for (const segment of segments) {
|
|
35
43
|
current = deref(current, rootSchema);
|
|
36
|
-
if (!current || typeof current !== "object")
|
|
44
|
+
if (!current || typeof current !== "object") {
|
|
45
|
+
debugLog("[resolveScope] undefined (deref miss)", { scope, segment, rootSchema });
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
const navigate = (node: Record<string, any>): unknown => {
|
|
51
|
+
if (segment === "properties") return node.properties;
|
|
52
|
+
if (segment === "items") return node.items;
|
|
53
|
+
return node[segment];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
let next = navigate(current);
|
|
57
|
+
if (next === undefined) {
|
|
58
|
+
next = tryCombinatorBranches(current, rootSchema, navigate);
|
|
59
|
+
}
|
|
60
|
+
if (next === undefined) {
|
|
61
|
+
debugLog("[resolveScope] undefined (segment unresolved)", { scope, segment, current, rootSchema });
|
|
62
|
+
return undefined;
|
|
44
63
|
}
|
|
64
|
+
current = next;
|
|
45
65
|
}
|
|
46
66
|
|
|
47
|
-
|
|
67
|
+
const out = deepDeref(current, rootSchema);
|
|
68
|
+
debugLog("[resolveScope] resolveScopeSchema", { scope, output: out, rootSchema });
|
|
69
|
+
return out;
|
|
48
70
|
}
|