@64mb/freelens-power 1.6.1 → 2.1.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 +1 -1
- package/out/main/common/power-logs.js.map +1 -1
- package/out/main/common/resource-search-kind-priority.js +23 -0
- package/out/main/common/resource-search-kind-priority.js.map +1 -0
- package/out/main/common/store/logs-preferences-store.js +5 -0
- package/out/main/common/store/logs-preferences-store.js.map +1 -1
- package/out/main/dashboard-views.js +26 -0
- package/out/main/dashboard-views.js.map +1 -1
- package/out/main/power-logs-database.js +5 -0
- package/out/main/power-logs-database.js.map +1 -1
- package/out/main/power-logs-ipc.js +8 -0
- package/out/main/power-logs-ipc.js.map +1 -1
- package/out/main/power-logs-query.js +1 -0
- package/out/main/power-logs-query.js.map +1 -1
- package/out/main/resource-search-database.js +7 -2
- package/out/main/resource-search-database.js.map +1 -1
- package/out/main/resource-search-worker.js +6 -2
- package/out/main/resource-search-worker.js.map +1 -1
- package/out/renderer/_virtual/_global-external_react.js +2 -1
- package/out/renderer/ai-diagnostic-handoff.js +1 -0
- package/out/renderer/ai-diagnostic-handoff.js.map +1 -1
- package/out/renderer/ai-extras.js +7 -2
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai.js +1 -1
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/appearance.js +2 -0
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/dashboard.js +35 -8
- package/out/renderer/dashboard.js.map +1 -1
- package/out/renderer/index.js +2 -1
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/power-log-json-cell.js +82 -0
- package/out/renderer/power-log-json-cell.js.map +1 -0
- package/out/renderer/power-log-json-fields.js +144 -0
- package/out/renderer/power-log-json-fields.js.map +1 -0
- package/out/renderer/power-log-json.js +223 -20
- package/out/renderer/power-log-json.js.map +1 -1
- package/out/renderer/power-log-message-tokens.js +60 -0
- package/out/renderer/power-log-message-tokens.js.map +1 -0
- package/out/renderer/power-log-message.js +26 -0
- package/out/renderer/power-log-message.js.map +1 -0
- package/out/renderer/power-log-sql-editor.js +91 -0
- package/out/renderer/power-log-sql-editor.js.map +1 -0
- package/out/renderer/power-logs-client.js +4 -0
- package/out/renderer/power-logs-client.js.map +1 -1
- package/out/renderer/power-logs-sql-drafts.js +76 -0
- package/out/renderer/power-logs-sql-drafts.js.map +1 -0
- package/out/renderer/power-logs-sql-row.js +5 -13
- package/out/renderer/power-logs-sql-row.js.map +1 -1
- package/out/renderer/power-logs.css.js +39 -6
- package/out/renderer/power-logs.css.js.map +1 -1
- package/out/renderer/power-logs.js +372 -135
- package/out/renderer/power-logs.js.map +1 -1
- package/out/renderer/preferences/logs-preferences.js +159 -38
- package/out/renderer/preferences/logs-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +2 -9
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/preferences/section-navigation.js +44 -0
- package/out/renderer/preferences/section-navigation.js.map +1 -0
- package/out/renderer/resource-search-model.js +2 -1
- package/out/renderer/resource-search-model.js.map +1 -1
- package/out/renderer/resource-search-sync.js +1 -1
- package/out/renderer/resource-search-sync.js.map +1 -1
- package/out/renderer/resource-search.js +84 -81
- package/out/renderer/resource-search.js.map +1 -1
- package/out/renderer/src/common/power-log-field-filters.js +92 -0
- package/out/renderer/src/common/power-log-field-filters.js.map +1 -0
- package/out/renderer/src/common/power-log-json-filter-sql.js +106 -0
- package/out/renderer/src/common/power-log-json-filter-sql.js.map +1 -0
- package/out/renderer/src/common/power-log-json-preset.js +34 -0
- package/out/renderer/src/common/power-log-json-preset.js.map +1 -0
- package/out/renderer/src/common/power-log-level.js +171 -0
- package/out/renderer/src/common/power-log-level.js.map +1 -0
- package/out/renderer/src/common/power-log-parser.js +7 -29
- package/out/renderer/src/common/power-log-parser.js.map +1 -1
- package/out/renderer/src/common/power-log-sql-level.js +25 -0
- package/out/renderer/src/common/power-log-sql-level.js.map +1 -0
- package/out/renderer/src/common/power-logs-ai.js +4 -0
- package/out/renderer/src/common/power-logs-ai.js.map +1 -1
- package/out/renderer/src/common/power-logs.js.map +1 -1
- package/out/renderer/src/common/resource-search-kind-priority.js +28 -0
- package/out/renderer/src/common/resource-search-kind-priority.js.map +1 -0
- package/out/renderer/src/common/store/logs-preferences-store.js +5 -0
- package/out/renderer/src/common/store/logs-preferences-store.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,26 +1,229 @@
|
|
|
1
|
+
const require__global_external_react = require("./_virtual/_global-external_react.js");
|
|
1
2
|
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
3
|
+
const require_power_log_json_fields = require("./power-log-json-fields.js");
|
|
4
|
+
let electron = require("electron");
|
|
2
5
|
//#region src/renderer/power-log-json.tsx
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
6
|
+
var fieldButtonStyle = {
|
|
7
|
+
width: 20,
|
|
8
|
+
height: 20,
|
|
9
|
+
minHeight: 0,
|
|
10
|
+
minWidth: 0,
|
|
11
|
+
padding: 0,
|
|
12
|
+
margin: 0,
|
|
13
|
+
border: 0,
|
|
14
|
+
borderRadius: 0,
|
|
15
|
+
background: "transparent",
|
|
16
|
+
color: "inherit",
|
|
17
|
+
display: "inline-flex",
|
|
18
|
+
alignItems: "center",
|
|
19
|
+
justifyContent: "center",
|
|
20
|
+
cursor: "pointer"
|
|
21
|
+
};
|
|
22
|
+
function LogJson({ text, fieldText, fieldsEnabled = true, Icon, onFilter }) {
|
|
23
|
+
const leaveTimer = require__global_external_react.useRef();
|
|
24
|
+
const hoverTimer = require__global_external_react.useRef();
|
|
25
|
+
const keepOpen = () => {
|
|
26
|
+
clearTimeout(leaveTimer.current);
|
|
27
|
+
clearTimeout(hoverTimer.current);
|
|
28
|
+
};
|
|
29
|
+
require__global_external_react.useEffect(() => () => {
|
|
30
|
+
clearTimeout(leaveTimer.current);
|
|
31
|
+
clearTimeout(hoverTimer.current);
|
|
32
|
+
}, []);
|
|
33
|
+
const identity = fieldText ?? text;
|
|
34
|
+
const interactive = fieldsEnabled && Boolean(Icon || onFilter);
|
|
35
|
+
const fields = require__global_external_react.useMemo(() => interactive ? require_power_log_json_fields.jsonFields(identity) : /* @__PURE__ */ new Map(), [identity, interactive]);
|
|
36
|
+
const [active, setActive] = require__global_external_react.useState();
|
|
37
|
+
const activeField = require__global_external_react.useRef(active);
|
|
38
|
+
activeField.current = active;
|
|
39
|
+
const [feedback, setFeedback] = require__global_external_react.useState("");
|
|
40
|
+
const selected = fieldsEnabled && active?.text === identity ? active : void 0;
|
|
41
|
+
const pre = /* @__PURE__ */ require_jsx_runtime.jsx("pre", {
|
|
22
42
|
className: "power-log-json",
|
|
23
|
-
children:
|
|
43
|
+
children: require__global_external_react.useMemo(() => {
|
|
44
|
+
const tokens = [];
|
|
45
|
+
const pattern = /"(?:\\.|[^"\\])*"(?:\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|[\[{]/g;
|
|
46
|
+
let cursor = 0;
|
|
47
|
+
let count = 0;
|
|
48
|
+
for (const match of text.matchAll(pattern)) {
|
|
49
|
+
if (++count > 2e4) break;
|
|
50
|
+
const start = match.index;
|
|
51
|
+
tokens.push(text.slice(cursor, start));
|
|
52
|
+
const value = match[0];
|
|
53
|
+
const key = value.startsWith("\"") && value.endsWith(":");
|
|
54
|
+
const kind = key ? "key" : value.startsWith("\"") ? "string" : /^(true|false|null)$/.test(value) ? "literal" : /^[\[{]$/.test(value) ? void 0 : "number";
|
|
55
|
+
const field = fields.get(start);
|
|
56
|
+
const activate = (element) => {
|
|
57
|
+
if (!field) return;
|
|
58
|
+
clearTimeout(leaveTimer.current);
|
|
59
|
+
const rect = element.getBoundingClientRect();
|
|
60
|
+
let lineRight = rect.right;
|
|
61
|
+
const pre = element.closest("pre");
|
|
62
|
+
if (pre) {
|
|
63
|
+
const lineEnd = text.indexOf("\n", start);
|
|
64
|
+
let remaining = lineEnd < 0 ? text.length : lineEnd;
|
|
65
|
+
const walker = document.createTreeWalker(pre, NodeFilter.SHOW_TEXT);
|
|
66
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
67
|
+
if (remaining > (node.textContent?.length ?? 0)) {
|
|
68
|
+
remaining -= node.textContent?.length ?? 0;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const range = document.createRange();
|
|
72
|
+
range.setStart(element, 0);
|
|
73
|
+
range.setEnd(node, remaining);
|
|
74
|
+
if (typeof range.getClientRects === "function") {
|
|
75
|
+
for (const fragment of Array.from(range.getClientRects())) if (Math.abs(fragment.top - rect.top) < Math.max(1, rect.height / 2)) lineRight = Math.max(lineRight, fragment.right);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const wrapperRight = element.closest(".power-log-json-fields")?.getBoundingClientRect().right;
|
|
81
|
+
const visibleRight = Math.min(window.innerWidth, wrapperRight && wrapperRight > 0 ? wrapperRight : window.innerWidth);
|
|
82
|
+
setActive({
|
|
83
|
+
field,
|
|
84
|
+
text: identity,
|
|
85
|
+
x: Math.max(4, Math.min(lineRight + 4, visibleRight - 64)),
|
|
86
|
+
y: Math.max(0, Math.min(rect.top, window.innerHeight - 20))
|
|
87
|
+
});
|
|
88
|
+
setFeedback("");
|
|
89
|
+
};
|
|
90
|
+
tokens.push(/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
91
|
+
"data-token": kind,
|
|
92
|
+
tabIndex: field ? 0 : void 0,
|
|
93
|
+
onMouseEnter: field ? (event) => {
|
|
94
|
+
keepOpen();
|
|
95
|
+
const element = event.currentTarget;
|
|
96
|
+
if (activeField.current?.text === identity && activeField.current.field !== field) hoverTimer.current = setTimeout(() => activate(element), 180);
|
|
97
|
+
else activate(element);
|
|
98
|
+
} : void 0,
|
|
99
|
+
onMouseLeave: field ? () => clearTimeout(hoverTimer.current) : void 0,
|
|
100
|
+
onFocus: field ? (event) => {
|
|
101
|
+
keepOpen();
|
|
102
|
+
activate(event.currentTarget);
|
|
103
|
+
} : void 0,
|
|
104
|
+
onKeyDown: field ? (event) => {
|
|
105
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
event.stopPropagation();
|
|
108
|
+
event.currentTarget.closest(".power-log-json-fields")?.querySelector("[role=\"toolbar\"] button")?.focus();
|
|
109
|
+
}
|
|
110
|
+
} : void 0,
|
|
111
|
+
children: key ? value.slice(0, -1) : value
|
|
112
|
+
}, start));
|
|
113
|
+
if (key) tokens.push(":");
|
|
114
|
+
cursor = start + value.length;
|
|
115
|
+
}
|
|
116
|
+
tokens.push(text.slice(cursor));
|
|
117
|
+
return tokens;
|
|
118
|
+
}, [
|
|
119
|
+
text,
|
|
120
|
+
fields,
|
|
121
|
+
identity
|
|
122
|
+
])
|
|
123
|
+
});
|
|
124
|
+
if (!Icon && !onFilter) return pre;
|
|
125
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
126
|
+
className: "power-log-json-fields",
|
|
127
|
+
style: { paddingRight: 76 },
|
|
128
|
+
onMouseEnter: keepOpen,
|
|
129
|
+
onMouseLeave: () => {
|
|
130
|
+
leaveTimer.current = setTimeout(() => setActive(void 0), 150);
|
|
131
|
+
},
|
|
132
|
+
onBlur: (event) => {
|
|
133
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setActive(void 0);
|
|
134
|
+
},
|
|
135
|
+
onKeyDown: (event) => {
|
|
136
|
+
if (event.key === "Escape") {
|
|
137
|
+
event.stopPropagation();
|
|
138
|
+
setActive(void 0);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
children: [pre, selected && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
142
|
+
role: "toolbar",
|
|
143
|
+
"aria-label": "JSON field actions",
|
|
144
|
+
style: {
|
|
145
|
+
position: "fixed",
|
|
146
|
+
left: selected.x,
|
|
147
|
+
top: selected.y,
|
|
148
|
+
zIndex: 1e3,
|
|
149
|
+
display: "flex",
|
|
150
|
+
gap: 2,
|
|
151
|
+
padding: 0,
|
|
152
|
+
background: "transparent",
|
|
153
|
+
border: 0,
|
|
154
|
+
alignItems: "center",
|
|
155
|
+
height: 20
|
|
156
|
+
},
|
|
157
|
+
onMouseEnter: keepOpen,
|
|
158
|
+
onClick: (event) => event.stopPropagation(),
|
|
159
|
+
onKeyDown: (event) => {
|
|
160
|
+
event.stopPropagation();
|
|
161
|
+
if (event.key === "Escape") setActive(void 0);
|
|
162
|
+
},
|
|
163
|
+
children: [
|
|
164
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
165
|
+
type: "button",
|
|
166
|
+
style: fieldButtonStyle,
|
|
167
|
+
title: Icon ? void 0 : "Copy value",
|
|
168
|
+
"aria-label": "Copy value",
|
|
169
|
+
onClick: () => {
|
|
170
|
+
try {
|
|
171
|
+
const value = selected.field.value;
|
|
172
|
+
electron.clipboard.writeText(typeof value === "string" ? value : require_power_log_json_fields.jsonFieldLiteral(identity, selected.field));
|
|
173
|
+
setFeedback("Copied");
|
|
174
|
+
} catch {
|
|
175
|
+
setFeedback("Copy failed");
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
children: Icon ? /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
|
|
179
|
+
material: "content_copy",
|
|
180
|
+
size: 14,
|
|
181
|
+
tooltip: "Copy value"
|
|
182
|
+
}) : "⧉"
|
|
183
|
+
}),
|
|
184
|
+
onFilter && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
185
|
+
type: "button",
|
|
186
|
+
style: fieldButtonStyle,
|
|
187
|
+
title: Icon ? void 0 : "Include value",
|
|
188
|
+
"aria-label": "Include value",
|
|
189
|
+
onClick: () => onFilter({
|
|
190
|
+
path: selected.field.path,
|
|
191
|
+
value: selected.field.value,
|
|
192
|
+
operator: "eq"
|
|
193
|
+
}),
|
|
194
|
+
children: Icon ? /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
|
|
195
|
+
material: "filter_alt",
|
|
196
|
+
size: 14,
|
|
197
|
+
tooltip: "Include value"
|
|
198
|
+
}) : "="
|
|
199
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
200
|
+
type: "button",
|
|
201
|
+
style: fieldButtonStyle,
|
|
202
|
+
title: Icon ? void 0 : "Exclude value",
|
|
203
|
+
"aria-label": "Exclude value",
|
|
204
|
+
onClick: () => onFilter({
|
|
205
|
+
path: selected.field.path,
|
|
206
|
+
value: selected.field.value,
|
|
207
|
+
operator: "neq"
|
|
208
|
+
}),
|
|
209
|
+
children: Icon ? /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
|
|
210
|
+
material: "filter_alt_off",
|
|
211
|
+
size: 14,
|
|
212
|
+
tooltip: "Exclude value"
|
|
213
|
+
}) : "!="
|
|
214
|
+
})] }),
|
|
215
|
+
feedback && /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
216
|
+
role: "status",
|
|
217
|
+
style: {
|
|
218
|
+
position: "absolute",
|
|
219
|
+
top: 22,
|
|
220
|
+
right: 0,
|
|
221
|
+
whiteSpace: "nowrap"
|
|
222
|
+
},
|
|
223
|
+
children: feedback
|
|
224
|
+
})
|
|
225
|
+
]
|
|
226
|
+
})]
|
|
24
227
|
});
|
|
25
228
|
}
|
|
26
229
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-log-json.js","names":[],"sources":["../../src/renderer/power-log-json.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nexport function LogJson({ text }: { text: string }) {\n const tokens: ReactNode[] = [];\n const pattern = /\"(?:\\\\.|[^\"\\\\])*\"(?:\\s*:)?|\\b(?:true|false|null)\\b|-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?/g;\n let cursor = 0;\n for (const match of text.matchAll(pattern)) {\n const start = match.index!;\n tokens.push(text.slice(cursor, start));\n const value = match[0];\n const key = value.startsWith('\"') && value.endsWith(\":\");\n const kind = key\n ? \"key\"\n : value.startsWith('\"')\n ? \"string\"\n : /^(true|false|null)$/.test(value)\n ? \"literal\"\n : \"number\";\n tokens.push(\n <span key={start} data-token={kind}>\n {key ? value.slice(0, -1) : value}\n </span>,\n );\n if (key) tokens.push(\":\");\n cursor = start + value.length;\n }\n tokens.push(text.slice(cursor));\n return <pre className=\"power-log-json\">{tokens}</pre>;\n}\n"],"mappings":";;AAEA,SAAgB,QAAQ,EAAE,QAA0B;CAClD,MAAM,SAAsB,CAAC;CAC7B,MAAM,UAAU;CAChB,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,KAAK,SAAS,OAAO,GAAG;EAC1C,MAAM,QAAQ,MAAM;EACpB,OAAO,KAAK,KAAK,MAAM,QAAQ,KAAK,CAAC;EACrC,MAAM,QAAQ,MAAM;EACpB,MAAM,MAAM,MAAM,WAAW,IAAG,KAAK,MAAM,SAAS,GAAG;EACvD,MAAM,OAAO,MACT,QACA,MAAM,WAAW,IAAG,IAClB,WACA,sBAAsB,KAAK,KAAK,IAC9B,YACA;EACR,OAAO,KACL,oCAAA,IAAC,QAAD;GAAkB,cAAY;aAC3B,MAAM,MAAM,MAAM,GAAG,EAAE,IAAI;EACxB,GAFK,KAEL,CACR;EACA,IAAI,KAAK,OAAO,KAAK,GAAG;EACxB,SAAS,QAAQ,MAAM;CACzB;CACA,OAAO,KAAK,KAAK,MAAM,MAAM,CAAC;CAC9B,OAAO,oCAAA,IAAC,OAAD;EAAK,WAAU;YAAkB;CAAY,CAAA;AACtD"}
|
|
1
|
+
{"version":3,"file":"power-log-json.js","names":[],"sources":["../../src/renderer/power-log-json.tsx"],"sourcesContent":["import { clipboard } from \"electron\";\nimport { type ComponentType, type CSSProperties, type ReactNode, useEffect, useMemo, useRef, useState } from \"react\";\nimport { type JsonField, jsonFieldLiteral, jsonFields, maxJsonTokens } from \"./power-log-json-fields\";\n\nimport type { JsonFieldFilter } from \"../common/power-log-field-filters\";\n\nexport interface LogJsonProps {\n text: string;\n fieldText?: string;\n fieldsEnabled?: boolean;\n Icon?: ComponentType<{ material: string; size?: number; tooltip?: string }>;\n onFilter?: (filter: JsonFieldFilter) => void;\n}\n\nconst fieldButtonStyle: CSSProperties = {\n width: 20,\n height: 20,\n minHeight: 0,\n minWidth: 0,\n padding: 0,\n margin: 0,\n border: 0,\n borderRadius: 0,\n background: \"transparent\",\n color: \"inherit\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n cursor: \"pointer\",\n};\n\nexport function LogJson({ text, fieldText, fieldsEnabled = true, Icon, onFilter }: LogJsonProps) {\n const leaveTimer = useRef<ReturnType<typeof setTimeout>>();\n const hoverTimer = useRef<ReturnType<typeof setTimeout>>();\n const keepOpen = () => {\n clearTimeout(leaveTimer.current);\n clearTimeout(hoverTimer.current);\n };\n useEffect(\n () => () => {\n clearTimeout(leaveTimer.current);\n clearTimeout(hoverTimer.current);\n },\n [],\n );\n const identity = fieldText ?? text;\n const interactive = fieldsEnabled && Boolean(Icon || onFilter);\n const fields = useMemo(\n () => (interactive ? jsonFields(identity) : new Map<number, JsonField>()),\n [identity, interactive],\n );\n const [active, setActive] = useState<{ field: JsonField; text: string; x: number; y: number }>();\n const activeField = useRef(active);\n activeField.current = active;\n const [feedback, setFeedback] = useState(\"\");\n const selected = fieldsEnabled && active?.text === identity ? active : undefined;\n const tokens = useMemo(() => {\n const tokens: ReactNode[] = [];\n const pattern = /\"(?:\\\\.|[^\"\\\\])*\"(?:\\s*:)?|\\b(?:true|false|null)\\b|-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|[\\[{]/g;\n let cursor = 0;\n let count = 0;\n for (const match of text.matchAll(pattern)) {\n if (++count > maxJsonTokens) break;\n const start = match.index!;\n tokens.push(text.slice(cursor, start));\n const value = match[0];\n const key = value.startsWith('\"') && value.endsWith(\":\");\n const kind = key\n ? \"key\"\n : value.startsWith('\"')\n ? \"string\"\n : /^(true|false|null)$/.test(value)\n ? \"literal\"\n : /^[\\[{]$/.test(value)\n ? undefined\n : \"number\";\n const field = fields.get(start);\n const activate = (element: HTMLElement) => {\n if (!field) return;\n clearTimeout(leaveTimer.current);\n const rect = element.getBoundingClientRect();\n let lineRight = rect.right;\n const pre = element.closest(\"pre\");\n if (pre) {\n const lineEnd = text.indexOf(\"\\n\", start);\n let remaining = lineEnd < 0 ? text.length : lineEnd;\n const walker = document.createTreeWalker(pre, NodeFilter.SHOW_TEXT);\n for (let node = walker.nextNode(); node; node = walker.nextNode()) {\n if (remaining > (node.textContent?.length ?? 0)) {\n remaining -= node.textContent?.length ?? 0;\n continue;\n }\n const range = document.createRange();\n range.setStart(element, 0);\n range.setEnd(node, remaining);\n if (typeof range.getClientRects === \"function\") {\n for (const fragment of Array.from(range.getClientRects())) {\n if (Math.abs(fragment.top - rect.top) < Math.max(1, rect.height / 2)) {\n lineRight = Math.max(lineRight, fragment.right);\n }\n }\n }\n break;\n }\n }\n const wrapperRight = element.closest(\".power-log-json-fields\")?.getBoundingClientRect().right;\n const visibleRight = Math.min(\n window.innerWidth,\n wrapperRight && wrapperRight > 0 ? wrapperRight : window.innerWidth,\n );\n setActive({\n field,\n text: identity,\n x: Math.max(4, Math.min(lineRight + 4, visibleRight - 64)),\n y: Math.max(0, Math.min(rect.top, window.innerHeight - 20)),\n });\n setFeedback(\"\");\n };\n tokens.push(\n <span\n key={start}\n data-token={kind}\n tabIndex={field ? 0 : undefined}\n onMouseEnter={\n field\n ? (event) => {\n keepOpen();\n const element = event.currentTarget;\n if (activeField.current?.text === identity && activeField.current.field !== field) {\n hoverTimer.current = setTimeout(() => activate(element), 180);\n } else activate(element);\n }\n : undefined\n }\n onMouseLeave={field ? () => clearTimeout(hoverTimer.current) : undefined}\n onFocus={\n field\n ? (event) => {\n keepOpen();\n activate(event.currentTarget);\n }\n : undefined\n }\n onKeyDown={\n field\n ? (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n event.stopPropagation();\n event.currentTarget\n .closest(\".power-log-json-fields\")\n ?.querySelector<HTMLButtonElement>('[role=\"toolbar\"] button')\n ?.focus();\n }\n }\n : undefined\n }\n >\n {key ? value.slice(0, -1) : value}\n </span>,\n );\n if (key) tokens.push(\":\");\n cursor = start + value.length;\n }\n tokens.push(text.slice(cursor));\n return tokens;\n }, [text, fields, identity]);\n const pre = <pre className=\"power-log-json\">{tokens}</pre>;\n if (!Icon && !onFilter) return pre;\n return (\n <div\n className=\"power-log-json-fields\"\n style={{ paddingRight: 76 }}\n onMouseEnter={keepOpen}\n onMouseLeave={() => {\n leaveTimer.current = setTimeout(() => setActive(undefined), 150);\n }}\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node)) setActive(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n setActive(undefined);\n }\n }}\n >\n {pre}\n {selected && (\n <div\n role=\"toolbar\"\n aria-label=\"JSON field actions\"\n style={{\n position: \"fixed\",\n left: selected.x,\n top: selected.y,\n zIndex: 1000,\n display: \"flex\",\n gap: 2,\n padding: 0,\n background: \"transparent\",\n border: 0,\n alignItems: \"center\",\n height: 20,\n }}\n onMouseEnter={keepOpen}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation();\n if (event.key === \"Escape\") setActive(undefined);\n }}\n >\n <button\n type=\"button\"\n style={fieldButtonStyle}\n title={Icon ? undefined : \"Copy value\"}\n aria-label=\"Copy value\"\n onClick={() => {\n try {\n const value = selected.field.value;\n clipboard.writeText(typeof value === \"string\" ? value : jsonFieldLiteral(identity, selected.field));\n setFeedback(\"Copied\");\n } catch {\n setFeedback(\"Copy failed\");\n }\n }}\n >\n {Icon ? <Icon material=\"content_copy\" size={14} tooltip=\"Copy value\" /> : \"⧉\"}\n </button>\n {onFilter && (\n <>\n <button\n type=\"button\"\n style={fieldButtonStyle}\n title={Icon ? undefined : \"Include value\"}\n aria-label=\"Include value\"\n onClick={() => onFilter({ path: selected.field.path, value: selected.field.value, operator: \"eq\" })}\n >\n {Icon ? <Icon material=\"filter_alt\" size={14} tooltip=\"Include value\" /> : \"=\"}\n </button>\n <button\n type=\"button\"\n style={fieldButtonStyle}\n title={Icon ? undefined : \"Exclude value\"}\n aria-label=\"Exclude value\"\n onClick={() => onFilter({ path: selected.field.path, value: selected.field.value, operator: \"neq\" })}\n >\n {Icon ? <Icon material=\"filter_alt_off\" size={14} tooltip=\"Exclude value\" /> : \"!=\"}\n </button>\n </>\n )}\n {feedback && (\n <span role=\"status\" style={{ position: \"absolute\", top: 22, right: 0, whiteSpace: \"nowrap\" }}>\n {feedback}\n </span>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
let node_net = require("node:net");
|
|
2
|
+
//#region src/renderer/power-log-message-tokens.ts
|
|
3
|
+
var identifier = /[\p{L}\p{N}_]/u;
|
|
4
|
+
var candidate = /(?<![\p{L}\p{N}_.])(?:https?:\/\/[^\s<>"']+|(?:trace_id|traceId|span_id|spanId|traceparent|ip|client_ip|remote_addr|clientIp|remoteAddr|http_status|status_code|statusCode|http\.status_code|http\.response\.status_code)["']?\s*[:=]\s*["']?[^\s,"'<>]+|HTTP\/\d(?:\.\d)?\s+\d+|\[[\da-fA-F:.]+\]:\d+|\d{1,3}(?:\.\d{1,3}){3}:\d{1,5}|[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}|(?:GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS|CONNECT|TRACE)|\d+(?:\.\d+)?(?:ns|us|µs|μs|ms|s))/gu;
|
|
5
|
+
function validAddress(value) {
|
|
6
|
+
const socket = /^(?:\[([^\]]+)\]|(\d+(?:\.\d+){3})):(\d+)$/.exec(value);
|
|
7
|
+
return socket ? (0, node_net.isIP)(socket[1] ?? socket[2]) === (socket[1] ? 6 : 4) && Number(socket[3]) <= 65535 : (0, node_net.isIP)(value) !== 0;
|
|
8
|
+
}
|
|
9
|
+
function messageTokens(text) {
|
|
10
|
+
const tokens = [];
|
|
11
|
+
const limit = Math.min(text.length, 65536);
|
|
12
|
+
const input = text.slice(0, limit);
|
|
13
|
+
for (const match of input.matchAll(candidate)) {
|
|
14
|
+
let value = match[0];
|
|
15
|
+
let start = match.index;
|
|
16
|
+
const end = start + value.length;
|
|
17
|
+
if (identifier.test(text[start - 1] ?? "") || identifier.test(text[end] ?? "")) continue;
|
|
18
|
+
let kind;
|
|
19
|
+
if (value.startsWith("http://") || value.startsWith("https://")) {
|
|
20
|
+
if (value.length <= 4096 && !/[.,;!?)]$/.test(value)) try {
|
|
21
|
+
if (new URL(value).hostname) kind = "url";
|
|
22
|
+
} catch {}
|
|
23
|
+
} else {
|
|
24
|
+
const label = /^(.*?)['"]?\s*[:=]\s*['"]?(.*)$/.exec(value);
|
|
25
|
+
if (label && !value.startsWith("[") && !/^\d/.test(value)) {
|
|
26
|
+
const name = label[1].replace(/["']$/, "");
|
|
27
|
+
value = label[2];
|
|
28
|
+
start = end - value.length;
|
|
29
|
+
if (/^(trace_id|traceId|span_id|spanId)$/.test(name)) {
|
|
30
|
+
const size = name.startsWith("span") ? 16 : 32;
|
|
31
|
+
if (new RegExp(`^[a-fA-F0-9]{${size}}$`).test(value) && /[1-9a-f]/i.test(value)) kind = "trace-id";
|
|
32
|
+
} else if (name === "traceparent") {
|
|
33
|
+
const trace = /^00-([\da-f]{32})-([\da-f]{16})-[\da-f]{2}$/.exec(value);
|
|
34
|
+
if (trace && /[1-9a-f]/.test(trace[1]) && /[1-9a-f]/.test(trace[2])) kind = "trace-id";
|
|
35
|
+
} else if (/^(ip|client_ip|remote_addr|clientIp|remoteAddr)$/.test(name)) {
|
|
36
|
+
if (validAddress(value)) kind = "address";
|
|
37
|
+
} else if (/^[1-5]\d{2}$/.test(value)) kind = "http-status";
|
|
38
|
+
} else if (value.startsWith("HTTP/")) {
|
|
39
|
+
value = /\d+$/.exec(value)[0];
|
|
40
|
+
start = end - value.length;
|
|
41
|
+
if (/^[1-5]\d{2}$/.test(value)) kind = "http-status";
|
|
42
|
+
} else if (validAddress(value)) kind = "address";
|
|
43
|
+
else if (/^[\da-fA-F]{8}-/.test(value)) kind = "uuid";
|
|
44
|
+
else if (/^[A-Z]+$/.test(value)) kind = "http-method";
|
|
45
|
+
else if (/^\d+(?:\.\d+)?(?:ns|us|µs|μs|ms|s)$/.test(value) && Number.parseFloat(value) > 0 && !/[.\-+]/.test(text[start - 1] ?? "")) kind = "duration";
|
|
46
|
+
}
|
|
47
|
+
if (kind) tokens.push({
|
|
48
|
+
start,
|
|
49
|
+
end,
|
|
50
|
+
kind,
|
|
51
|
+
...kind === "http-status" ? { statusClass: value[0] } : {}
|
|
52
|
+
});
|
|
53
|
+
if (tokens.length === 80) break;
|
|
54
|
+
}
|
|
55
|
+
return tokens;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
exports.messageTokens = messageTokens;
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=power-log-message-tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power-log-message-tokens.js","names":[],"sources":["../../src/renderer/power-log-message-tokens.ts"],"sourcesContent":["import { isIP } from \"node:net\";\n\nexport interface LogMessageToken {\n start: number;\n end: number;\n kind: \"http-method\" | \"uuid\" | \"url\" | \"trace-id\" | \"address\" | \"http-status\" | \"duration\";\n statusClass?: string;\n}\n\nconst identifier = /[\\p{L}\\p{N}_]/u;\nconst candidate =\n /(?<![\\p{L}\\p{N}_.])(?:https?:\\/\\/[^\\s<>\"']+|(?:trace_id|traceId|span_id|spanId|traceparent|ip|client_ip|remote_addr|clientIp|remoteAddr|http_status|status_code|statusCode|http\\.status_code|http\\.response\\.status_code)[\"']?\\s*[:=]\\s*[\"']?[^\\s,\"'<>]+|HTTP\\/\\d(?:\\.\\d)?\\s+\\d+|\\[[\\da-fA-F:.]+\\]:\\d+|\\d{1,3}(?:\\.\\d{1,3}){3}:\\d{1,5}|[\\da-fA-F]{8}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{12}|(?:GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS|CONNECT|TRACE)|\\d+(?:\\.\\d+)?(?:ns|us|µs|μs|ms|s))/gu;\n\nfunction validAddress(value: string): boolean {\n const socket = /^(?:\\[([^\\]]+)\\]|(\\d+(?:\\.\\d+){3})):(\\d+)$/.exec(value);\n return socket\n ? isIP(socket[1] ?? socket[2]) === (socket[1] ? 6 : 4) && Number(socket[3]) <= 65535\n : isIP(value) !== 0;\n}\n\nexport function messageTokens(text: string): LogMessageToken[] {\n const tokens: LogMessageToken[] = [];\n // cap scanning as well as output nodes for oversized records\n const limit = Math.min(text.length, 65536);\n const input = text.slice(0, limit);\n for (const match of input.matchAll(candidate)) {\n let value = match[0];\n let start = match.index!;\n const end = start + value.length;\n if (identifier.test(text[start - 1] ?? \"\") || identifier.test(text[end] ?? \"\")) continue;\n let kind: LogMessageToken[\"kind\"] | undefined;\n if (value.startsWith(\"http://\") || value.startsWith(\"https://\")) {\n // URLs remain opaque even when validation fails\n if (value.length <= 4096 && !/[.,;!?)]$/.test(value)) {\n try {\n const url = new URL(value);\n if (url.hostname) kind = \"url\";\n } catch {\n /* invalid URLs stay plain */\n }\n }\n } else {\n const label = /^(.*?)['\"]?\\s*[:=]\\s*['\"]?(.*)$/.exec(value);\n if (label && !value.startsWith(\"[\") && !/^\\d/.test(value)) {\n const name = label[1].replace(/[\"']$/, \"\");\n value = label[2];\n start = end - value.length;\n if (/^(trace_id|traceId|span_id|spanId)$/.test(name)) {\n const size = name.startsWith(\"span\") ? 16 : 32;\n if (new RegExp(`^[a-fA-F0-9]{${size}}$`).test(value) && /[1-9a-f]/i.test(value)) kind = \"trace-id\";\n } else if (name === \"traceparent\") {\n // version [00] follows W3C Trace Context's lowercase and nonzero ID rules\n const trace = /^00-([\\da-f]{32})-([\\da-f]{16})-[\\da-f]{2}$/.exec(value);\n if (trace && /[1-9a-f]/.test(trace[1]) && /[1-9a-f]/.test(trace[2])) kind = \"trace-id\";\n } else if (/^(ip|client_ip|remote_addr|clientIp|remoteAddr)$/.test(name)) {\n if (validAddress(value)) kind = \"address\";\n } else if (/^[1-5]\\d{2}$/.test(value)) kind = \"http-status\";\n } else if (value.startsWith(\"HTTP/\")) {\n value = /\\d+$/.exec(value)![0];\n start = end - value.length;\n if (/^[1-5]\\d{2}$/.test(value)) kind = \"http-status\";\n } else if (validAddress(value)) kind = \"address\";\n else if (/^[\\da-fA-F]{8}-/.test(value)) kind = \"uuid\";\n else if (/^[A-Z]+$/.test(value)) kind = \"http-method\";\n else if (\n /^\\d+(?:\\.\\d+)?(?:ns|us|µs|μs|ms|s)$/.test(value) &&\n Number.parseFloat(value) > 0 &&\n !/[.\\-+]/.test(text[start - 1] ?? \"\")\n )\n kind = \"duration\";\n }\n if (kind) tokens.push({ start, end, kind, ...(kind === \"http-status\" ? { statusClass: value[0] } : {}) });\n if (tokens.length === 80) break;\n }\n return tokens;\n}\n"],"mappings":";;AASA,IAAM,aAAa;AACnB,IAAM,YACJ;AAEF,SAAS,aAAa,OAAwB;CAC5C,MAAM,SAAS,6CAA6C,KAAK,KAAK;CACtE,OAAO,UAAA,GAAA,SAAA,KAAA,CACE,OAAO,MAAM,OAAO,EAAE,OAAO,OAAO,KAAK,IAAI,MAAM,OAAO,OAAO,EAAE,KAAK,SAAA,GAAA,SAAA,KAAA,CACxE,KAAK,MAAM;AACtB;AAEA,SAAgB,cAAc,MAAiC;CAC7D,MAAM,SAA4B,CAAC;CAEnC,MAAM,QAAQ,KAAK,IAAI,KAAK,QAAQ,KAAK;CACzC,MAAM,QAAQ,KAAK,MAAM,GAAG,KAAK;CACjC,KAAK,MAAM,SAAS,MAAM,SAAS,SAAS,GAAG;EAC7C,IAAI,QAAQ,MAAM;EAClB,IAAI,QAAQ,MAAM;EAClB,MAAM,MAAM,QAAQ,MAAM;EAC1B,IAAI,WAAW,KAAK,KAAK,QAAQ,MAAM,EAAE,KAAK,WAAW,KAAK,KAAK,QAAQ,EAAE,GAAG;EAChF,IAAI;EACJ,IAAI,MAAM,WAAW,SAAS,KAAK,MAAM,WAAW,UAAU;OAExD,MAAM,UAAU,QAAQ,CAAC,YAAY,KAAK,KAAK,GACjD,IAAI;IAEF,IAAI,IADY,IAAI,KAChB,CAAA,CAAI,UAAU,OAAO;GAC3B,QAAQ,CAER;SAEG;GACL,MAAM,QAAQ,kCAAkC,KAAK,KAAK;GAC1D,IAAI,SAAS,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,KAAK,KAAK,GAAG;IACzD,MAAM,OAAO,MAAM,EAAE,CAAC,QAAQ,SAAS,EAAE;IACzC,QAAQ,MAAM;IACd,QAAQ,MAAM,MAAM;IACpB,IAAI,sCAAsC,KAAK,IAAI,GAAG;KACpD,MAAM,OAAO,KAAK,WAAW,MAAM,IAAI,KAAK;KAC5C,IAAI,IAAI,OAAO,gBAAgB,KAAK,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,YAAY,KAAK,KAAK,GAAG,OAAO;IAC1F,OAAO,IAAI,SAAS,eAAe;KAEjC,MAAM,QAAQ,8CAA8C,KAAK,KAAK;KACtE,IAAI,SAAS,WAAW,KAAK,MAAM,EAAE,KAAK,WAAW,KAAK,MAAM,EAAE,GAAG,OAAO;IAC9E,OAAO,IAAI,mDAAmD,KAAK,IAAI;SACjE,aAAa,KAAK,GAAG,OAAO;IAAA,OAC3B,IAAI,eAAe,KAAK,KAAK,GAAG,OAAO;GAChD,OAAO,IAAI,MAAM,WAAW,OAAO,GAAG;IACpC,QAAQ,OAAO,KAAK,KAAK,CAAC,CAAE;IAC5B,QAAQ,MAAM,MAAM;IACpB,IAAI,eAAe,KAAK,KAAK,GAAG,OAAO;GACzC,OAAO,IAAI,aAAa,KAAK,GAAG,OAAO;QAClC,IAAI,kBAAkB,KAAK,KAAK,GAAG,OAAO;QAC1C,IAAI,WAAW,KAAK,KAAK,GAAG,OAAO;QACnC,IACH,sCAAsC,KAAK,KAAK,KAChD,OAAO,WAAW,KAAK,IAAI,KAC3B,CAAC,SAAS,KAAK,KAAK,QAAQ,MAAM,EAAE,GAEpC,OAAO;EACX;EACA,IAAI,MAAM,OAAO,KAAK;GAAE;GAAO;GAAK;GAAM,GAAI,SAAS,gBAAgB,EAAE,aAAa,MAAM,GAAG,IAAI,CAAC;EAAG,CAAC;EACxG,IAAI,OAAO,WAAW,IAAI;CAC5B;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const require__global_external_react = require("./_virtual/_global-external_react.js");
|
|
2
|
+
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
3
|
+
const require_power_log_message_tokens = require("./power-log-message-tokens.js");
|
|
4
|
+
//#region src/renderer/power-log-message.tsx
|
|
5
|
+
var LogMessage = require__global_external_react.memo(function LogMessage({ text }) {
|
|
6
|
+
const tokens = [];
|
|
7
|
+
let cursor = 0;
|
|
8
|
+
for (const token of require_power_log_message_tokens.messageTokens(text)) {
|
|
9
|
+
tokens.push(text.slice(cursor, token.start));
|
|
10
|
+
tokens.push(/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
11
|
+
"data-log-token": token.kind,
|
|
12
|
+
"data-status-class": token.statusClass,
|
|
13
|
+
children: text.slice(token.start, token.end)
|
|
14
|
+
}, token.start));
|
|
15
|
+
cursor = token.end;
|
|
16
|
+
}
|
|
17
|
+
tokens.push(text.slice(cursor));
|
|
18
|
+
return /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
19
|
+
className: "power-log-message",
|
|
20
|
+
children: tokens
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
exports.LogMessage = LogMessage;
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=power-log-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power-log-message.js","names":[],"sources":["../../src/renderer/power-log-message.tsx"],"sourcesContent":["import { memo, type ReactNode } from \"react\";\nimport { messageTokens } from \"./power-log-message-tokens\";\n\nexport const LogMessage = memo(function LogMessage({ text }: { text: string }) {\n const tokens: ReactNode[] = [];\n let cursor = 0;\n for (const token of messageTokens(text)) {\n tokens.push(text.slice(cursor, token.start));\n tokens.push(\n <span key={token.start} data-log-token={token.kind} data-status-class={token.statusClass}>\n {text.slice(token.start, token.end)}\n </span>,\n );\n cursor = token.end;\n }\n tokens.push(text.slice(cursor));\n return <span className=\"power-log-message\">{tokens}</span>;\n});\n"],"mappings":";;;;AAGA,IAAa,aAAa,+BAAA,KAAK,SAAS,WAAW,EAAE,QAA0B;CAC7E,MAAM,SAAsB,CAAC;CAC7B,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,iCAAA,cAAc,IAAI,GAAG;EACvC,OAAO,KAAK,KAAK,MAAM,QAAQ,MAAM,KAAK,CAAC;EAC3C,OAAO,KACL,oCAAA,IAAC,QAAD;GAAwB,kBAAgB,MAAM;GAAM,qBAAmB,MAAM;aAC1E,KAAK,MAAM,MAAM,OAAO,MAAM,GAAG;EAC9B,GAFK,MAAM,KAEX,CACR;EACA,SAAS,MAAM;CACjB;CACA,OAAO,KAAK,KAAK,MAAM,MAAM,CAAC;CAC9B,OAAO,oCAAA,IAAC,QAAD;EAAM,WAAU;YAAqB;CAAa,CAAA;AAC3D,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const require__global_external_react = require("./_virtual/_global-external_react.js");
|
|
2
|
+
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
3
|
+
//#region src/renderer/power-log-sql-editor.tsx
|
|
4
|
+
var keywords = new Set("SELECT FROM WHERE AS AND OR NOT NULL IS IN LIKE GLOB BETWEEN EXISTS CASE WHEN THEN ELSE END DISTINCT ALL JOIN LEFT RIGHT INNER OUTER CROSS FULL ON USING GROUP BY HAVING ORDER ASC DESC LIMIT OFFSET UNION INTERSECT EXCEPT WITH RECURSIVE OVER PARTITION ROWS RANGE UNBOUNDED PRECEDING FOLLOWING CURRENT ROW NULLS FIRST LAST TRUE FALSE COLLATE CAST FILTER WINDOW VALUES EXPLAIN QUERY PLAN".split(" "));
|
|
5
|
+
var lexeme = /--[^\n]*(?:\n|$)|\/\*[\s\S]*?(?:\*\/|$)|'(?:''|[^'])*(?:'|$)|"(?:""|[^"])*(?:"|$)|`(?:``|[^`])*(?:`|$)|\[[^\]]*(?:\]|$)|\b(?:0x[\da-f]+|\d+(?:\.\d*)?(?:e[+-]?\d+)?)(?![\w])|\b[a-z_]\w*\b/gi;
|
|
6
|
+
function isFunction(source, offset) {
|
|
7
|
+
while (offset < source.length && /\s/.test(source[offset])) offset++;
|
|
8
|
+
return source[offset] === "(";
|
|
9
|
+
}
|
|
10
|
+
function tokenizeSql(value) {
|
|
11
|
+
const source = value.slice(0, 65536);
|
|
12
|
+
const tokens = [];
|
|
13
|
+
let offset = 0;
|
|
14
|
+
lexeme.lastIndex = 0;
|
|
15
|
+
for (let match = lexeme.exec(source); match && tokens.length < 9998; match = lexeme.exec(source)) {
|
|
16
|
+
if (match.index > offset) tokens.push({ text: source.slice(offset, match.index) });
|
|
17
|
+
const text = match[0];
|
|
18
|
+
const kind = text.startsWith("--") || text.startsWith("/*") ? "comment" : text.startsWith("'") ? "string" : text.startsWith("\"") || text.startsWith("`") || text.startsWith("[") ? "identifier" : /^\d/.test(text) ? "number" : keywords.has(text.toUpperCase()) ? "keyword" : isFunction(source, match.index + text.length) ? "function" : void 0;
|
|
19
|
+
tokens.push({
|
|
20
|
+
text,
|
|
21
|
+
kind
|
|
22
|
+
});
|
|
23
|
+
offset = match.index + text.length;
|
|
24
|
+
}
|
|
25
|
+
if (offset < value.length) tokens.push({ text: value.slice(offset) });
|
|
26
|
+
return tokens;
|
|
27
|
+
}
|
|
28
|
+
var styles = `
|
|
29
|
+
.power-logs .power-logs-sql-editor{position:relative;box-sizing:border-box;width:100%;height:56px;min-height:48px;max-height:120px;flex-shrink:0;resize:vertical;overflow:hidden;border:1px solid var(--borderColor);border-radius:4px;background:var(--inputControlBackground,var(--contentColor));--sql-string:#e6a28c;--sql-number:#98c78e;--sql-function:#81b9e6;--sql-identifier:var(--textColorPrimary)}
|
|
30
|
+
.power-logs .power-logs-sql-editor::-webkit-resizer{border:0;background:linear-gradient(135deg,transparent 45%,var(--borderColor) 46% 53%,transparent 54% 70%,var(--borderColor) 71% 78%,transparent 79%) center/10px 10px no-repeat}
|
|
31
|
+
.theme-light .power-logs-sql-editor{--sql-string:#9e2b1a;--sql-number:#17642d;--sql-function:#005a9e;--sql-identifier:#684c9b}
|
|
32
|
+
.power-logs .power-logs-sql-editor>textarea,.power-logs .power-logs-sql-editor>pre{position:absolute;inset:0;box-sizing:border-box;width:100%;height:100%;min-height:0;max-height:none;margin:0;padding:4px 8px;border:0;border-radius:0;font:12px/1.6 var(--font-monospace,monospace);letter-spacing:normal;white-space:pre-wrap;overflow-wrap:anywhere;tab-size:2;overflow:auto;scrollbar-gutter:stable;resize:none}
|
|
33
|
+
.power-logs .power-logs-sql-editor>pre{pointer-events:none;color:var(--textColorPrimary);background:transparent;user-select:none}
|
|
34
|
+
.power-logs .power-logs-sql-editor>textarea{color:transparent;background:transparent;caret-color:var(--textColorPrimary);-webkit-text-fill-color:transparent}
|
|
35
|
+
.power-logs .power-logs-sql-editor>textarea::selection{background:rgba(100,150,240,.35);color:transparent}
|
|
36
|
+
.power-logs-sql-editor [data-sql-token=keyword]{color:var(--primary)}
|
|
37
|
+
.power-logs-sql-editor [data-sql-token=function]{color:var(--sql-function)}
|
|
38
|
+
.power-logs-sql-editor [data-sql-token=string]{color:var(--sql-string)}
|
|
39
|
+
.power-logs-sql-editor [data-sql-token=identifier]{color:var(--sql-identifier)}
|
|
40
|
+
.power-logs-sql-editor [data-sql-token=number]{color:var(--sql-number)}
|
|
41
|
+
.power-logs-sql-editor [data-sql-token=comment]{color:var(--textColorSecondary)}
|
|
42
|
+
`;
|
|
43
|
+
function SqlEditor({ id, value, onChange }) {
|
|
44
|
+
const textarea = require__global_external_react.useRef(null);
|
|
45
|
+
const highlight = require__global_external_react.useRef(null);
|
|
46
|
+
const tokens = require__global_external_react.useMemo(() => tokenizeSql(value), [value]);
|
|
47
|
+
const syncScroll = () => {
|
|
48
|
+
if (!textarea.current || !highlight.current) return;
|
|
49
|
+
highlight.current.scrollTop = textarea.current.scrollTop;
|
|
50
|
+
highlight.current.scrollLeft = textarea.current.scrollLeft;
|
|
51
|
+
};
|
|
52
|
+
require__global_external_react.useLayoutEffect(syncScroll, [value]);
|
|
53
|
+
const selectQuery = (event) => {
|
|
54
|
+
if (event.detail < 3) return;
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
event.stopPropagation();
|
|
57
|
+
window.getSelection()?.removeAllRanges();
|
|
58
|
+
event.currentTarget.focus();
|
|
59
|
+
event.currentTarget.select();
|
|
60
|
+
};
|
|
61
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
62
|
+
className: "power-logs-sql-editor",
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("style", { children: styles }),
|
|
65
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("pre", {
|
|
66
|
+
ref: highlight,
|
|
67
|
+
"aria-hidden": "true",
|
|
68
|
+
children: [tokens.map((token, index) => /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
69
|
+
"data-sql-token": token.kind,
|
|
70
|
+
children: token.text
|
|
71
|
+
}, index)), value.endsWith("\n") && " "]
|
|
72
|
+
}),
|
|
73
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("textarea", {
|
|
74
|
+
ref: textarea,
|
|
75
|
+
id,
|
|
76
|
+
value,
|
|
77
|
+
maxLength: 65536,
|
|
78
|
+
spellCheck: false,
|
|
79
|
+
onChange: (event) => onChange(event.target.value.slice(0, 65536)),
|
|
80
|
+
onScroll: syncScroll,
|
|
81
|
+
onMouseDown: selectQuery,
|
|
82
|
+
onClick: selectQuery
|
|
83
|
+
})
|
|
84
|
+
]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
exports.SqlEditor = SqlEditor;
|
|
89
|
+
exports.tokenizeSql = tokenizeSql;
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=power-log-sql-editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power-log-sql-editor.js","names":[],"sources":["../../src/renderer/power-log-sql-editor.tsx"],"sourcesContent":["import { useLayoutEffect, useMemo, useRef } from \"react\";\n\nimport type { MouseEvent } from \"react\";\n\ntype SqlToken = { text: string; kind?: \"keyword\" | \"function\" | \"string\" | \"identifier\" | \"number\" | \"comment\" };\nconst keywords = new Set(\n \"SELECT FROM WHERE AS AND OR NOT NULL IS IN LIKE GLOB BETWEEN EXISTS CASE WHEN THEN ELSE END DISTINCT ALL JOIN LEFT RIGHT INNER OUTER CROSS FULL ON USING GROUP BY HAVING ORDER ASC DESC LIMIT OFFSET UNION INTERSECT EXCEPT WITH RECURSIVE OVER PARTITION ROWS RANGE UNBOUNDED PRECEDING FOLLOWING CURRENT ROW NULLS FIRST LAST TRUE FALSE COLLATE CAST FILTER WINDOW VALUES EXPLAIN QUERY PLAN\".split(\n \" \",\n ),\n);\nconst lexeme =\n /--[^\\n]*(?:\\n|$)|\\/\\*[\\s\\S]*?(?:\\*\\/|$)|'(?:''|[^'])*(?:'|$)|\"(?:\"\"|[^\"])*(?:\"|$)|`(?:``|[^`])*(?:`|$)|\\[[^\\]]*(?:\\]|$)|\\b(?:0x[\\da-f]+|\\d+(?:\\.\\d*)?(?:e[+-]?\\d+)?)(?![\\w])|\\b[a-z_]\\w*\\b/gi;\n\nfunction isFunction(source: string, offset: number): boolean {\n while (offset < source.length && /\\s/.test(source[offset])) offset++;\n return source[offset] === \"(\";\n}\n\nexport function tokenizeSql(value: string): SqlToken[] {\n const source = value.slice(0, 65536);\n const tokens: SqlToken[] = [];\n let offset = 0;\n lexeme.lastIndex = 0;\n for (let match = lexeme.exec(source); match && tokens.length < 9998; match = lexeme.exec(source)) {\n if (match.index > offset) tokens.push({ text: source.slice(offset, match.index) });\n const text = match[0];\n const kind =\n text.startsWith(\"--\") || text.startsWith(\"/*\")\n ? \"comment\"\n : text.startsWith(\"'\")\n ? \"string\"\n : text.startsWith('\"') || text.startsWith(\"`\") || text.startsWith(\"[\")\n ? \"identifier\"\n : /^\\d/.test(text)\n ? \"number\"\n : keywords.has(text.toUpperCase())\n ? \"keyword\"\n : isFunction(source, match.index + text.length)\n ? \"function\"\n : undefined;\n tokens.push({ text, kind });\n offset = match.index + text.length;\n }\n if (offset < value.length) tokens.push({ text: value.slice(offset) });\n return tokens;\n}\n\nconst styles = `\n.power-logs .power-logs-sql-editor{position:relative;box-sizing:border-box;width:100%;height:56px;min-height:48px;max-height:120px;flex-shrink:0;resize:vertical;overflow:hidden;border:1px solid var(--borderColor);border-radius:4px;background:var(--inputControlBackground,var(--contentColor));--sql-string:#e6a28c;--sql-number:#98c78e;--sql-function:#81b9e6;--sql-identifier:var(--textColorPrimary)}\n.power-logs .power-logs-sql-editor::-webkit-resizer{border:0;background:linear-gradient(135deg,transparent 45%,var(--borderColor) 46% 53%,transparent 54% 70%,var(--borderColor) 71% 78%,transparent 79%) center/10px 10px no-repeat}\n.theme-light .power-logs-sql-editor{--sql-string:#9e2b1a;--sql-number:#17642d;--sql-function:#005a9e;--sql-identifier:#684c9b}\n.power-logs .power-logs-sql-editor>textarea,.power-logs .power-logs-sql-editor>pre{position:absolute;inset:0;box-sizing:border-box;width:100%;height:100%;min-height:0;max-height:none;margin:0;padding:4px 8px;border:0;border-radius:0;font:12px/1.6 var(--font-monospace,monospace);letter-spacing:normal;white-space:pre-wrap;overflow-wrap:anywhere;tab-size:2;overflow:auto;scrollbar-gutter:stable;resize:none}\n.power-logs .power-logs-sql-editor>pre{pointer-events:none;color:var(--textColorPrimary);background:transparent;user-select:none}\n.power-logs .power-logs-sql-editor>textarea{color:transparent;background:transparent;caret-color:var(--textColorPrimary);-webkit-text-fill-color:transparent}\n.power-logs .power-logs-sql-editor>textarea::selection{background:rgba(100,150,240,.35);color:transparent}\n.power-logs-sql-editor [data-sql-token=keyword]{color:var(--primary)}\n.power-logs-sql-editor [data-sql-token=function]{color:var(--sql-function)}\n.power-logs-sql-editor [data-sql-token=string]{color:var(--sql-string)}\n.power-logs-sql-editor [data-sql-token=identifier]{color:var(--sql-identifier)}\n.power-logs-sql-editor [data-sql-token=number]{color:var(--sql-number)}\n.power-logs-sql-editor [data-sql-token=comment]{color:var(--textColorSecondary)}\n`;\n\nexport function SqlEditor({ id, value, onChange }: { id: string; value: string; onChange(value: string): void }) {\n const textarea = useRef<HTMLTextAreaElement>(null);\n const highlight = useRef<HTMLPreElement>(null);\n const tokens = useMemo(() => tokenizeSql(value), [value]);\n const syncScroll = () => {\n if (!textarea.current || !highlight.current) return;\n highlight.current.scrollTop = textarea.current.scrollTop;\n highlight.current.scrollLeft = textarea.current.scrollLeft;\n };\n useLayoutEffect(syncScroll, [value]);\n const selectQuery = (event: MouseEvent<HTMLTextAreaElement>) => {\n if (event.detail < 3) return;\n event.preventDefault();\n event.stopPropagation();\n window.getSelection()?.removeAllRanges();\n event.currentTarget.focus();\n event.currentTarget.select();\n };\n return (\n <div className=\"power-logs-sql-editor\">\n <style>{styles}</style>\n <pre ref={highlight} aria-hidden=\"true\">\n {tokens.map((token, index) => (\n <span key={index} data-sql-token={token.kind}>\n {token.text}\n </span>\n ))}\n {value.endsWith(\"\\n\") && \" \"}\n </pre>\n <textarea\n ref={textarea}\n id={id}\n value={value}\n maxLength={65536}\n spellCheck={false}\n onChange={(event) => onChange(event.target.value.slice(0, 65536))}\n onScroll={syncScroll}\n onMouseDown={selectQuery}\n onClick={selectQuery}\n />\n </div>\n );\n}\n"],"mappings":";;;AAKA,IAAM,WAAW,IAAI,IACnB,kYAAkY,MAChY,GACF,CACF;AACA,IAAM,SACJ;AAEF,SAAS,WAAW,QAAgB,QAAyB;CAC3D,OAAO,SAAS,OAAO,UAAU,KAAK,KAAK,OAAO,OAAO,GAAG;CAC5D,OAAO,OAAO,YAAY;AAC5B;AAEA,SAAgB,YAAY,OAA2B;CACrD,MAAM,SAAS,MAAM,MAAM,GAAG,KAAK;CACnC,MAAM,SAAqB,CAAC;CAC5B,IAAI,SAAS;CACb,OAAO,YAAY;CACnB,KAAK,IAAI,QAAQ,OAAO,KAAK,MAAM,GAAG,SAAS,OAAO,SAAS,MAAM,QAAQ,OAAO,KAAK,MAAM,GAAG;EAChG,IAAI,MAAM,QAAQ,QAAQ,OAAO,KAAK,EAAE,MAAM,OAAO,MAAM,QAAQ,MAAM,KAAK,EAAE,CAAC;EACjF,MAAM,OAAO,MAAM;EACnB,MAAM,OACJ,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,IAAI,IACzC,YACA,KAAK,WAAW,GAAG,IACjB,WACA,KAAK,WAAW,IAAG,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG,IACjE,eACA,MAAM,KAAK,IAAI,IACb,WACA,SAAS,IAAI,KAAK,YAAY,CAAC,IAC7B,YACA,WAAW,QAAQ,MAAM,QAAQ,KAAK,MAAM,IAC1C,aACA,KAAA;EAChB,OAAO,KAAK;GAAE;GAAM;EAAK,CAAC;EAC1B,SAAS,MAAM,QAAQ,KAAK;CAC9B;CACA,IAAI,SAAS,MAAM,QAAQ,OAAO,KAAK,EAAE,MAAM,MAAM,MAAM,MAAM,EAAE,CAAC;CACpE,OAAO;AACT;AAEA,IAAM,SAAS;;;;;;;;;;;;;;;AAgBf,SAAgB,UAAU,EAAE,IAAI,OAAO,YAA0E;CAC/G,MAAM,WAAW,+BAAA,OAA4B,IAAI;CACjD,MAAM,YAAY,+BAAA,OAAuB,IAAI;CAC7C,MAAM,SAAS,+BAAA,cAAc,YAAY,KAAK,GAAG,CAAC,KAAK,CAAC;CACxD,MAAM,mBAAmB;EACvB,IAAI,CAAC,SAAS,WAAW,CAAC,UAAU,SAAS;EAC7C,UAAU,QAAQ,YAAY,SAAS,QAAQ;EAC/C,UAAU,QAAQ,aAAa,SAAS,QAAQ;CAClD;CACA,+BAAA,gBAAgB,YAAY,CAAC,KAAK,CAAC;CACnC,MAAM,eAAe,UAA2C;EAC9D,IAAI,MAAM,SAAS,GAAG;EACtB,MAAM,eAAe;EACrB,MAAM,gBAAgB;EACtB,OAAO,aAAa,CAAC,EAAE,gBAAgB;EACvC,MAAM,cAAc,MAAM;EAC1B,MAAM,cAAc,OAAO;CAC7B;CACA,OACE,oCAAA,KAAC,OAAD;EAAK,WAAU;YAAf;GACE,oCAAA,IAAC,SAAD,EAAA,UAAQ,OAAc,CAAA;GACtB,oCAAA,KAAC,OAAD;IAAK,KAAK;IAAW,eAAY;cAAjC,CACG,OAAO,KAAK,OAAO,UAClB,oCAAA,IAAC,QAAD;KAAkB,kBAAgB,MAAM;eACrC,MAAM;IACH,GAFK,KAEL,CACP,GACA,MAAM,SAAS,IAAI,KAAK,GACtB;;GACL,oCAAA,IAAC,YAAD;IACE,KAAK;IACD;IACG;IACP,WAAW;IACX,YAAY;IACZ,WAAW,UAAU,SAAS,MAAM,OAAO,MAAM,MAAM,GAAG,KAAK,CAAC;IAChE,UAAU;IACV,aAAa;IACb,SAAS;GACV,CAAA;EACE;;AAET"}
|
|
@@ -20,6 +20,10 @@ function createPowerLogsClient(partition) {
|
|
|
20
20
|
notes
|
|
21
21
|
}),
|
|
22
22
|
delete: (captureId) => invoke("delete", { captureId }),
|
|
23
|
+
saveSql: (captureId, sql) => invoke("save-sql", {
|
|
24
|
+
captureId,
|
|
25
|
+
sql
|
|
26
|
+
}),
|
|
23
27
|
query: (captureId, queryId, sql) => invoke("query", {
|
|
24
28
|
captureId,
|
|
25
29
|
queryId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-logs-client.js","names":[],"sources":["../../src/renderer/power-logs-client.ts"],"sourcesContent":["import { ipcRenderer } from \"electron\";\nimport {\n type LogCapture,\n type LogPartition,\n type LogQueryResult,\n type LogRecord,\n type LogTarget,\n POWER_LOGS_CHANNEL,\n} from \"../common/power-logs\";\n\nexport function createPowerLogsClient(partition: LogPartition) {\n const invoke = <T>(operation: string, fields: object = {}): Promise<T> =>\n ipcRenderer.invoke(POWER_LOGS_CHANNEL, { ...partition, operation, ...fields });\n return {\n list: () => invoke<LogCapture[]>(\"list\"),\n create: (target: LogTarget) => invoke<LogCapture>(\"create\", { target }),\n append: (captureId: string, records: LogRecord[]) => invoke<LogCapture>(\"append\", { captureId, records }),\n finish: (captureId: string, partial: boolean, notes: string[]) =>\n invoke<LogCapture>(\"finish\", { captureId, partial, notes }),\n delete: (captureId: string) => invoke<boolean>(\"delete\", { captureId }),\n query: (captureId: string, queryId: string, sql: string) =>\n invoke<LogQueryResult>(\"query\", { captureId, queryId, sql }),\n cancel: (queryId: string) => invoke<boolean>(\"cancel\", { queryId }),\n export: (captureId: string) => invoke<boolean>(\"export\", { captureId }),\n };\n}\n"],"mappings":";;;AAUA,SAAgB,sBAAsB,WAAyB;CAC7D,MAAM,UAAa,WAAmB,SAAiB,CAAC,MACtD,SAAA,YAAY,OAAO,mBAAA,oBAAoB;EAAE,GAAG;EAAW;EAAW,GAAG;CAAO,CAAC;CAC/E,OAAO;EACL,YAAY,OAAqB,MAAM;EACvC,SAAS,WAAsB,OAAmB,UAAU,EAAE,OAAO,CAAC;EACtE,SAAS,WAAmB,YAAyB,OAAmB,UAAU;GAAE;GAAW;EAAQ,CAAC;EACxG,SAAS,WAAmB,SAAkB,UAC5C,OAAmB,UAAU;GAAE;GAAW;GAAS;EAAM,CAAC;EAC5D,SAAS,cAAsB,OAAgB,UAAU,EAAE,UAAU,CAAC;EACtE,QAAQ,WAAmB,SAAiB,QAC1C,OAAuB,SAAS;GAAE;GAAW;GAAS;EAAI,CAAC;EAC7D,SAAS,YAAoB,OAAgB,UAAU,EAAE,QAAQ,CAAC;EAClE,SAAS,cAAsB,OAAgB,UAAU,EAAE,UAAU,CAAC;CACxE;AACF"}
|
|
1
|
+
{"version":3,"file":"power-logs-client.js","names":[],"sources":["../../src/renderer/power-logs-client.ts"],"sourcesContent":["import { ipcRenderer } from \"electron\";\nimport {\n type LogCapture,\n type LogPartition,\n type LogQueryResult,\n type LogRecord,\n type LogTarget,\n POWER_LOGS_CHANNEL,\n} from \"../common/power-logs\";\n\nexport function createPowerLogsClient(partition: LogPartition) {\n const invoke = <T>(operation: string, fields: object = {}): Promise<T> =>\n ipcRenderer.invoke(POWER_LOGS_CHANNEL, { ...partition, operation, ...fields });\n return {\n list: () => invoke<LogCapture[]>(\"list\"),\n create: (target: LogTarget) => invoke<LogCapture>(\"create\", { target }),\n append: (captureId: string, records: LogRecord[]) => invoke<LogCapture>(\"append\", { captureId, records }),\n finish: (captureId: string, partial: boolean, notes: string[]) =>\n invoke<LogCapture>(\"finish\", { captureId, partial, notes }),\n delete: (captureId: string) => invoke<boolean>(\"delete\", { captureId }),\n saveSql: (captureId: string, sql: string) => invoke<LogCapture>(\"save-sql\", { captureId, sql }),\n query: (captureId: string, queryId: string, sql: string) =>\n invoke<LogQueryResult>(\"query\", { captureId, queryId, sql }),\n cancel: (queryId: string) => invoke<boolean>(\"cancel\", { queryId }),\n export: (captureId: string) => invoke<boolean>(\"export\", { captureId }),\n };\n}\n"],"mappings":";;;AAUA,SAAgB,sBAAsB,WAAyB;CAC7D,MAAM,UAAa,WAAmB,SAAiB,CAAC,MACtD,SAAA,YAAY,OAAO,mBAAA,oBAAoB;EAAE,GAAG;EAAW;EAAW,GAAG;CAAO,CAAC;CAC/E,OAAO;EACL,YAAY,OAAqB,MAAM;EACvC,SAAS,WAAsB,OAAmB,UAAU,EAAE,OAAO,CAAC;EACtE,SAAS,WAAmB,YAAyB,OAAmB,UAAU;GAAE;GAAW;EAAQ,CAAC;EACxG,SAAS,WAAmB,SAAkB,UAC5C,OAAmB,UAAU;GAAE;GAAW;GAAS;EAAM,CAAC;EAC5D,SAAS,cAAsB,OAAgB,UAAU,EAAE,UAAU,CAAC;EACtE,UAAU,WAAmB,QAAgB,OAAmB,YAAY;GAAE;GAAW;EAAI,CAAC;EAC9F,QAAQ,WAAmB,SAAiB,QAC1C,OAAuB,SAAS;GAAE;GAAW;GAAS;EAAI,CAAC;EAC7D,SAAS,YAAoB,OAAgB,UAAU,EAAE,QAAQ,CAAC;EAClE,SAAS,cAAsB,OAAgB,UAAU,EAAE,UAAU,CAAC;CACxE;AACF"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/renderer/power-logs-sql-drafts.ts
|
|
2
|
+
var CaptureSqlDrafts = class {
|
|
3
|
+
save;
|
|
4
|
+
onError;
|
|
5
|
+
local = /* @__PURE__ */ new Map();
|
|
6
|
+
pending = /* @__PURE__ */ new Map();
|
|
7
|
+
writes = /* @__PURE__ */ new Map();
|
|
8
|
+
timer;
|
|
9
|
+
version = 0;
|
|
10
|
+
disposed = false;
|
|
11
|
+
constructor(save, onError) {
|
|
12
|
+
this.save = save;
|
|
13
|
+
this.onError = onError;
|
|
14
|
+
}
|
|
15
|
+
read(capture, fallback) {
|
|
16
|
+
return this.local.get(capture.id)?.sql ?? capture.lastSql ?? fallback;
|
|
17
|
+
}
|
|
18
|
+
set(id, sql) {
|
|
19
|
+
if (this.disposed) return;
|
|
20
|
+
const draft = {
|
|
21
|
+
sql,
|
|
22
|
+
version: ++this.version,
|
|
23
|
+
lifetime: this.local.get(id)?.lifetime ?? {}
|
|
24
|
+
};
|
|
25
|
+
this.local.set(id, draft);
|
|
26
|
+
this.pending.set(id, draft);
|
|
27
|
+
this.clearTimer();
|
|
28
|
+
this.timer = setTimeout(() => {
|
|
29
|
+
this.flush();
|
|
30
|
+
}, 250);
|
|
31
|
+
}
|
|
32
|
+
async flush() {
|
|
33
|
+
this.clearTimer();
|
|
34
|
+
for (;;) {
|
|
35
|
+
const pending = [...this.pending];
|
|
36
|
+
this.pending.clear();
|
|
37
|
+
for (const [id, draft] of pending) this.enqueue(id, draft);
|
|
38
|
+
const writes = [...this.writes.values()];
|
|
39
|
+
if (!writes.length) return;
|
|
40
|
+
await Promise.all(writes);
|
|
41
|
+
if (!this.pending.size && !this.writes.size) return;
|
|
42
|
+
this.clearTimer();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
forget(id) {
|
|
46
|
+
this.local.delete(id);
|
|
47
|
+
this.pending.delete(id);
|
|
48
|
+
}
|
|
49
|
+
dispose() {
|
|
50
|
+
this.disposed = true;
|
|
51
|
+
return this.flush();
|
|
52
|
+
}
|
|
53
|
+
clearTimer() {
|
|
54
|
+
clearTimeout(this.timer);
|
|
55
|
+
this.timer = void 0;
|
|
56
|
+
}
|
|
57
|
+
enqueue(id, draft) {
|
|
58
|
+
const tracked = (this.writes.get(id) ?? Promise.resolve()).then(async () => {
|
|
59
|
+
if (this.local.get(id)?.lifetime !== draft.lifetime) return;
|
|
60
|
+
try {
|
|
61
|
+
await this.save(id, draft.sql);
|
|
62
|
+
} catch {
|
|
63
|
+
if (!this.disposed && this.local.get(id)?.version === draft.version) try {
|
|
64
|
+
this.onError?.(id);
|
|
65
|
+
} catch {}
|
|
66
|
+
}
|
|
67
|
+
}).finally(() => {
|
|
68
|
+
if (this.writes.get(id) === tracked) this.writes.delete(id);
|
|
69
|
+
});
|
|
70
|
+
this.writes.set(id, tracked);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
exports.CaptureSqlDrafts = CaptureSqlDrafts;
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=power-logs-sql-drafts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power-logs-sql-drafts.js","names":[],"sources":["../../src/renderer/power-logs-sql-drafts.ts"],"sourcesContent":["import type { LogCapture } from \"../common/power-logs\";\n\ntype Draft = { sql: string; version: number; lifetime: object };\n\nexport class CaptureSqlDrafts {\n private local = new Map<string, Draft>();\n private pending = new Map<string, Draft>();\n private writes = new Map<string, Promise<void>>();\n private timer: ReturnType<typeof setTimeout> | undefined;\n private version = 0;\n private disposed = false;\n\n constructor(\n private save: (id: string, sql: string) => Promise<unknown>,\n private onError?: (id: string) => void,\n ) {}\n\n read(capture: LogCapture, fallback: string): string {\n return this.local.get(capture.id)?.sql ?? capture.lastSql ?? fallback;\n }\n\n set(id: string, sql: string): void {\n if (this.disposed) return;\n const draft = { sql, version: ++this.version, lifetime: this.local.get(id)?.lifetime ?? {} };\n this.local.set(id, draft);\n this.pending.set(id, draft);\n this.clearTimer();\n this.timer = setTimeout(() => {\n void this.flush();\n }, 250);\n }\n\n async flush(): Promise<void> {\n this.clearTimer();\n for (;;) {\n const pending = [...this.pending];\n this.pending.clear();\n for (const [id, draft] of pending) this.enqueue(id, draft);\n const writes = [...this.writes.values()];\n if (!writes.length) return;\n await Promise.all(writes);\n if (!this.pending.size && !this.writes.size) return;\n this.clearTimer();\n }\n }\n\n forget(id: string): void {\n this.local.delete(id);\n this.pending.delete(id);\n }\n\n dispose(): Promise<void> {\n this.disposed = true;\n return this.flush();\n }\n\n private clearTimer(): void {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n private enqueue(id: string, draft: Draft): void {\n const previous = this.writes.get(id) ?? Promise.resolve();\n const write = previous.then(async () => {\n if (this.local.get(id)?.lifetime !== draft.lifetime) return;\n try {\n await this.save(id, draft.sql);\n } catch {\n // retain the local draft without retrying failed writes or reporting obsolete failures\n if (!this.disposed && this.local.get(id)?.version === draft.version) {\n try {\n this.onError?.(id);\n } catch {\n /* error feedback must not break later saves */\n }\n }\n }\n });\n const tracked = write.finally(() => {\n if (this.writes.get(id) === tracked) this.writes.delete(id);\n });\n this.writes.set(id, tracked);\n }\n}\n"],"mappings":";AAIA,IAAa,mBAAb,MAA8B;CASlB;CACA;CATV,wBAAgB,IAAI,IAAmB;CACvC,0BAAkB,IAAI,IAAmB;CACzC,yBAAiB,IAAI,IAA2B;CAChD;CACA,UAAkB;CAClB,WAAmB;CAEnB,YACE,MACA,SACA;EAFQ,KAAA,OAAA;EACA,KAAA,UAAA;CACP;CAEH,KAAK,SAAqB,UAA0B;EAClD,OAAO,KAAK,MAAM,IAAI,QAAQ,EAAE,CAAC,EAAE,OAAO,QAAQ,WAAW;CAC/D;CAEA,IAAI,IAAY,KAAmB;EACjC,IAAI,KAAK,UAAU;EACnB,MAAM,QAAQ;GAAE;GAAK,SAAS,EAAE,KAAK;GAAS,UAAU,KAAK,MAAM,IAAI,EAAE,CAAC,EAAE,YAAY,CAAC;EAAE;EAC3F,KAAK,MAAM,IAAI,IAAI,KAAK;EACxB,KAAK,QAAQ,IAAI,IAAI,KAAK;EAC1B,KAAK,WAAW;EAChB,KAAK,QAAQ,iBAAiB;GAC5B,KAAU,MAAM;EAClB,GAAG,GAAG;CACR;CAEA,MAAM,QAAuB;EAC3B,KAAK,WAAW;EAChB,SAAS;GACP,MAAM,UAAU,CAAC,GAAG,KAAK,OAAO;GAChC,KAAK,QAAQ,MAAM;GACnB,KAAK,MAAM,CAAC,IAAI,UAAU,SAAS,KAAK,QAAQ,IAAI,KAAK;GACzD,MAAM,SAAS,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC;GACvC,IAAI,CAAC,OAAO,QAAQ;GACpB,MAAM,QAAQ,IAAI,MAAM;GACxB,IAAI,CAAC,KAAK,QAAQ,QAAQ,CAAC,KAAK,OAAO,MAAM;GAC7C,KAAK,WAAW;EAClB;CACF;CAEA,OAAO,IAAkB;EACvB,KAAK,MAAM,OAAO,EAAE;EACpB,KAAK,QAAQ,OAAO,EAAE;CACxB;CAEA,UAAyB;EACvB,KAAK,WAAW;EAChB,OAAO,KAAK,MAAM;CACpB;CAEA,aAA2B;EACzB,aAAa,KAAK,KAAK;EACvB,KAAK,QAAQ,KAAA;CACf;CAEA,QAAgB,IAAY,OAAoB;EAiB9C,MAAM,WAhBW,KAAK,OAAO,IAAI,EAAE,KAAK,QAAQ,QAAQ,EAAA,CACjC,KAAK,YAAY;GACtC,IAAI,KAAK,MAAM,IAAI,EAAE,CAAC,EAAE,aAAa,MAAM,UAAU;GACrD,IAAI;IACF,MAAM,KAAK,KAAK,IAAI,MAAM,GAAG;GAC/B,QAAQ;IAEN,IAAI,CAAC,KAAK,YAAY,KAAK,MAAM,IAAI,EAAE,CAAC,EAAE,YAAY,MAAM,SAC1D,IAAI;KACF,KAAK,UAAU,EAAE;IACnB,QAAQ,CAER;GAEJ;EACF,CACgB,CAAA,CAAM,cAAc;GAClC,IAAI,KAAK,OAAO,IAAI,EAAE,MAAM,SAAS,KAAK,OAAO,OAAO,EAAE;EAC5D,CAAC;EACD,KAAK,OAAO,IAAI,IAAI,OAAO;CAC7B;AACF"}
|