@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.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/out/main/common/power-logs.js.map +1 -1
  3. package/out/main/common/resource-search-kind-priority.js +23 -0
  4. package/out/main/common/resource-search-kind-priority.js.map +1 -0
  5. package/out/main/common/store/logs-preferences-store.js +5 -0
  6. package/out/main/common/store/logs-preferences-store.js.map +1 -1
  7. package/out/main/dashboard-views.js +26 -0
  8. package/out/main/dashboard-views.js.map +1 -1
  9. package/out/main/power-logs-database.js +5 -0
  10. package/out/main/power-logs-database.js.map +1 -1
  11. package/out/main/power-logs-ipc.js +8 -0
  12. package/out/main/power-logs-ipc.js.map +1 -1
  13. package/out/main/power-logs-query.js +1 -0
  14. package/out/main/power-logs-query.js.map +1 -1
  15. package/out/main/resource-search-database.js +7 -2
  16. package/out/main/resource-search-database.js.map +1 -1
  17. package/out/main/resource-search-worker.js +6 -2
  18. package/out/main/resource-search-worker.js.map +1 -1
  19. package/out/renderer/_virtual/_global-external_react.js +2 -1
  20. package/out/renderer/ai-diagnostic-handoff.js +1 -0
  21. package/out/renderer/ai-diagnostic-handoff.js.map +1 -1
  22. package/out/renderer/ai-extras.js +7 -2
  23. package/out/renderer/ai-extras.js.map +1 -1
  24. package/out/renderer/ai.js +1 -1
  25. package/out/renderer/ai.js.map +1 -1
  26. package/out/renderer/appearance.js +2 -0
  27. package/out/renderer/appearance.js.map +1 -1
  28. package/out/renderer/dashboard.js +35 -8
  29. package/out/renderer/dashboard.js.map +1 -1
  30. package/out/renderer/index.js +2 -1
  31. package/out/renderer/index.js.map +1 -1
  32. package/out/renderer/power-log-json-cell.js +82 -0
  33. package/out/renderer/power-log-json-cell.js.map +1 -0
  34. package/out/renderer/power-log-json-fields.js +144 -0
  35. package/out/renderer/power-log-json-fields.js.map +1 -0
  36. package/out/renderer/power-log-json.js +223 -20
  37. package/out/renderer/power-log-json.js.map +1 -1
  38. package/out/renderer/power-log-message-tokens.js +60 -0
  39. package/out/renderer/power-log-message-tokens.js.map +1 -0
  40. package/out/renderer/power-log-message.js +26 -0
  41. package/out/renderer/power-log-message.js.map +1 -0
  42. package/out/renderer/power-log-sql-editor.js +91 -0
  43. package/out/renderer/power-log-sql-editor.js.map +1 -0
  44. package/out/renderer/power-logs-client.js +4 -0
  45. package/out/renderer/power-logs-client.js.map +1 -1
  46. package/out/renderer/power-logs-sql-drafts.js +76 -0
  47. package/out/renderer/power-logs-sql-drafts.js.map +1 -0
  48. package/out/renderer/power-logs-sql-row.js +5 -13
  49. package/out/renderer/power-logs-sql-row.js.map +1 -1
  50. package/out/renderer/power-logs.css.js +39 -6
  51. package/out/renderer/power-logs.css.js.map +1 -1
  52. package/out/renderer/power-logs.js +372 -135
  53. package/out/renderer/power-logs.js.map +1 -1
  54. package/out/renderer/preferences/logs-preferences.js +159 -38
  55. package/out/renderer/preferences/logs-preferences.js.map +1 -1
  56. package/out/renderer/preferences/preferences.js +2 -9
  57. package/out/renderer/preferences/preferences.js.map +1 -1
  58. package/out/renderer/preferences/section-navigation.js +44 -0
  59. package/out/renderer/preferences/section-navigation.js.map +1 -0
  60. package/out/renderer/resource-search-model.js +2 -1
  61. package/out/renderer/resource-search-model.js.map +1 -1
  62. package/out/renderer/resource-search-sync.js +1 -1
  63. package/out/renderer/resource-search-sync.js.map +1 -1
  64. package/out/renderer/resource-search.js +84 -81
  65. package/out/renderer/resource-search.js.map +1 -1
  66. package/out/renderer/src/common/power-log-field-filters.js +92 -0
  67. package/out/renderer/src/common/power-log-field-filters.js.map +1 -0
  68. package/out/renderer/src/common/power-log-json-filter-sql.js +106 -0
  69. package/out/renderer/src/common/power-log-json-filter-sql.js.map +1 -0
  70. package/out/renderer/src/common/power-log-json-preset.js +34 -0
  71. package/out/renderer/src/common/power-log-json-preset.js.map +1 -0
  72. package/out/renderer/src/common/power-log-level.js +171 -0
  73. package/out/renderer/src/common/power-log-level.js.map +1 -0
  74. package/out/renderer/src/common/power-log-parser.js +7 -29
  75. package/out/renderer/src/common/power-log-parser.js.map +1 -1
  76. package/out/renderer/src/common/power-log-sql-level.js +25 -0
  77. package/out/renderer/src/common/power-log-sql-level.js.map +1 -0
  78. package/out/renderer/src/common/power-logs-ai.js +4 -0
  79. package/out/renderer/src/common/power-logs-ai.js.map +1 -1
  80. package/out/renderer/src/common/power-logs.js.map +1 -1
  81. package/out/renderer/src/common/resource-search-kind-priority.js +28 -0
  82. package/out/renderer/src/common/resource-search-kind-priority.js.map +1 -0
  83. package/out/renderer/src/common/store/logs-preferences-store.js +5 -0
  84. package/out/renderer/src/common/store/logs-preferences-store.js.map +1 -1
  85. 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
- function LogJson({ text }) {
4
- const tokens = [];
5
- const pattern = /"(?:\\.|[^"\\])*"(?:\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g;
6
- let cursor = 0;
7
- for (const match of text.matchAll(pattern)) {
8
- const start = match.index;
9
- tokens.push(text.slice(cursor, start));
10
- const value = match[0];
11
- const key = value.startsWith("\"") && value.endsWith(":");
12
- const kind = key ? "key" : value.startsWith("\"") ? "string" : /^(true|false|null)$/.test(value) ? "literal" : "number";
13
- tokens.push(/* @__PURE__ */ require_jsx_runtime.jsx("span", {
14
- "data-token": kind,
15
- children: key ? value.slice(0, -1) : value
16
- }, start));
17
- if (key) tokens.push(":");
18
- cursor = start + value.length;
19
- }
20
- tokens.push(text.slice(cursor));
21
- return /* @__PURE__ */ require_jsx_runtime.jsx("pre", {
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: tokens
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"}