@korajs/devtools 0.6.0 → 1.0.0-beta.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/LICENSE +21 -0
- package/dist/{chunk-N64DMGUU.js → chunk-K3QOD2IY.js} +7 -1
- package/dist/chunk-K3QOD2IY.js.map +1 -0
- package/dist/extension/panel.cjs +6 -0
- package/dist/extension/panel.cjs.map +1 -1
- package/dist/extension/panel.js +6 -0
- package/dist/extension/panel.js.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/overlay.cjs +6 -0
- package/dist/overlay.cjs.map +1 -1
- package/dist/overlay.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-N64DMGUU.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dr. Obed Ehoneah
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -14,6 +14,8 @@ var EVENT_TYPE_CATEGORIES = {
|
|
|
14
14
|
"sync:connected": "sync",
|
|
15
15
|
"sync:disconnected": "sync",
|
|
16
16
|
"sync:auth-failed": "sync",
|
|
17
|
+
"sync:clock-skew": "sync",
|
|
18
|
+
"sync:clock-rebase": "sync",
|
|
17
19
|
"sync:schema-mismatch": "sync",
|
|
18
20
|
"sync:apply-failed": "sync",
|
|
19
21
|
"sync:sent": "sync",
|
|
@@ -299,6 +301,10 @@ function timelineLabel(event) {
|
|
|
299
301
|
return "sync disconnected";
|
|
300
302
|
case "sync:auth-failed":
|
|
301
303
|
return "sync auth failed";
|
|
304
|
+
case "sync:clock-skew":
|
|
305
|
+
return `clock skew ${Math.round(event.skewMs / 1e3)}s (${event.severity})`;
|
|
306
|
+
case "sync:clock-rebase":
|
|
307
|
+
return `clock rebase ${event.rebasedCount} ops (${Math.round(event.maxSkewMs / 1e3)}s ahead)`;
|
|
302
308
|
case "sync:schema-mismatch":
|
|
303
309
|
return `schema mismatch client ${event.clientSchemaVersion} server ${event.serverSchemaVersion}`;
|
|
304
310
|
case "sync:apply-failed":
|
|
@@ -853,4 +859,4 @@ export {
|
|
|
853
859
|
renderDevtoolsPanel,
|
|
854
860
|
mountKoraDevtoolsOverlay
|
|
855
861
|
};
|
|
856
|
-
//# sourceMappingURL=chunk-
|
|
862
|
+
//# sourceMappingURL=chunk-K3QOD2IY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/panel.ts","../src/types.ts","../src/filter/event-filter.ts","../src/stats/event-stats.ts","../src/ui/components.ts","../src/ui/panel-state.ts","../src/ui/panel-styles.ts","../src/overlay/embed-overlay.ts"],"sourcesContent":["import htm from 'htm'\nimport { h, render } from 'preact'\nimport { useCallback, useMemo, useState } from 'preact/hooks'\n\nimport { filterEvents } from '../filter/event-filter'\nimport { computeStatistics } from '../stats/event-stats'\nimport type { EventCategory, TimestampedEvent } from '../types'\nimport { eventTypeToCategory } from '../types'\nimport { formatTime, formatValue, truncate } from './components'\nimport { buildPanelModel } from './panel-state'\nimport type {\n\tConflictItem,\n\tDevtoolsPanelModel,\n\tNetworkStatusModel,\n\tOperationItem,\n\tTimelineItem,\n} from './panel-state'\n\nconst html = htm.bind(h)\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype ActiveTab = 'timeline' | 'conflicts' | 'operations' | 'network'\n\nconst ALL_CATEGORIES: EventCategory[] = ['operation', 'merge', 'sync', 'query', 'connection']\n\n// ============================================================================\n// Main Panel Component\n// ============================================================================\n\nfunction DevToolsPanel({ events }: { events: readonly TimestampedEvent[] }) {\n\tconst [activeTab, setActiveTab] = useState<ActiveTab>('timeline')\n\tconst [search, setSearch] = useState('')\n\tconst [categories, setCategories] = useState<Set<EventCategory>>(() => new Set(ALL_CATEGORIES))\n\tconst [expanded, setExpanded] = useState<Set<number>>(() => new Set())\n\tconst [paused, setPaused] = useState(false)\n\n\tconst toggleCategory = useCallback((cat: EventCategory) => {\n\t\tsetCategories((prev) => {\n\t\t\tconst next = new Set(prev)\n\t\t\tif (next.has(cat)) next.delete(cat)\n\t\t\telse next.add(cat)\n\t\t\treturn next\n\t\t})\n\t}, [])\n\n\tconst toggleExpand = useCallback((id: number) => {\n\t\tsetExpanded((prev) => {\n\t\t\tconst next = new Set(prev)\n\t\t\tif (next.has(id)) next.delete(id)\n\t\t\telse next.add(id)\n\t\t\treturn next\n\t\t})\n\t}, [])\n\n\tconst filtered = useMemo(\n\t\t() => filterEvents(events, { categories: [...categories] }),\n\t\t[events, categories],\n\t)\n\n\tconst model = useMemo(() => buildPanelModel(filtered), [filtered])\n\tconst stats = useMemo(() => computeStatistics(events), [events])\n\n\treturn html`\n\t\t<div class=\"kora-devtools\">\n\t\t\t<${Toolbar}\n\t\t\t\tactiveTab=${activeTab}\n\t\t\t\tonTabChange=${setActiveTab}\n\t\t\t\ttotalEvents=${events.length}\n\t\t\t\tconflicts=${stats.mergeConflicts}\n\t\t\t\tsearch=${search}\n\t\t\t\tonSearchChange=${setSearch}\n\t\t\t\tpaused=${paused}\n\t\t\t\tonPauseToggle=${() => setPaused((p: boolean) => !p)}\n\t\t\t\tonClear=${() => setExpanded(new Set())}\n\t\t\t\tcategories=${categories}\n\t\t\t\tonToggleCategory=${toggleCategory}\n\t\t\t/>\n\t\t\t<div class=\"kora-content\">\n\t\t\t\t${\n\t\t\t\t\tactiveTab === 'timeline' &&\n\t\t\t\t\thtml`<${TimelinePanel}\n\t\t\t\t\titems=${model.timeline}\n\t\t\t\t\tsearch=${search}\n\t\t\t\t\texpanded=${expanded}\n\t\t\t\t\tonToggle=${toggleExpand}\n\t\t\t\t/>`\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\tactiveTab === 'conflicts' &&\n\t\t\t\t\thtml`<${ConflictsPanel}\n\t\t\t\t\titems=${model.conflicts}\n\t\t\t\t\tsearch=${search}\n\t\t\t\t\texpanded=${expanded}\n\t\t\t\t\tonToggle=${toggleExpand}\n\t\t\t\t/>`\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\tactiveTab === 'operations' &&\n\t\t\t\t\thtml`<${OperationsPanel}\n\t\t\t\t\titems=${model.operations}\n\t\t\t\t\tsearch=${search}\n\t\t\t\t\texpanded=${expanded}\n\t\t\t\t\tonToggle=${toggleExpand}\n\t\t\t\t/>`\n\t\t\t\t}\n\t\t\t\t${\n\t\t\t\t\tactiveTab === 'network' &&\n\t\t\t\t\thtml`<${NetworkPanel} network=${model.network} events=${events} />`\n\t\t\t\t}\n\t\t\t</div>\n\t\t</div>\n\t`\n}\n\n// ============================================================================\n// Toolbar\n// ============================================================================\n\nfunction Toolbar({\n\tactiveTab,\n\tonTabChange,\n\ttotalEvents,\n\tconflicts,\n\tsearch,\n\tonSearchChange,\n\tpaused,\n\tonPauseToggle,\n\tonClear,\n\tcategories,\n\tonToggleCategory,\n}: {\n\tactiveTab: ActiveTab\n\tonTabChange: (tab: ActiveTab) => void\n\ttotalEvents: number\n\tconflicts: number\n\tsearch: string\n\tonSearchChange: (s: string) => void\n\tpaused: boolean\n\tonPauseToggle: () => void\n\tonClear: () => void\n\tcategories: Set<EventCategory>\n\tonToggleCategory: (cat: EventCategory) => void\n}) {\n\tconst tabs: Array<{ id: ActiveTab; label: string; badge?: number }> = [\n\t\t{ id: 'timeline', label: 'Timeline', badge: totalEvents },\n\t\t{ id: 'conflicts', label: 'Conflicts', badge: conflicts },\n\t\t{ id: 'operations', label: 'Operations' },\n\t\t{ id: 'network', label: 'Network' },\n\t]\n\n\treturn html`\n\t\t<div class=\"kora-toolbar\">\n\t\t\t<div class=\"kora-tabs\">\n\t\t\t\t${tabs.map(\n\t\t\t\t\t(tab) => html`\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclass=\"kora-tab${activeTab === tab.id ? ' active' : ''}\"\n\t\t\t\t\t\t\tonClick=${() => onTabChange(tab.id)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t${tab.label}\n\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\ttab.badge != null && tab.badge > 0\n\t\t\t\t\t\t\t\t\t? html`<span class=\"kora-badge\">${tab.badge}</span>`\n\t\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t`,\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div class=\"kora-controls\">\n\t\t\t\t<input\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tclass=\"kora-search\"\n\t\t\t\t\tplaceholder=\"Filter...\"\n\t\t\t\t\tvalue=${search}\n\t\t\t\t\tonInput=${(e: Event) => onSearchChange((e.target as HTMLInputElement).value)}\n\t\t\t\t/>\n\t\t\t\t<button class=\"kora-btn\" onClick=${onPauseToggle}>\n\t\t\t\t\t${paused ? 'Resume' : 'Pause'}\n\t\t\t\t</button>\n\t\t\t\t<button class=\"kora-btn\" onClick=${onClear}>Clear</button>\n\t\t\t\t${ALL_CATEGORIES.map(\n\t\t\t\t\t(cat) => html`\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclass=\"kora-cat-toggle${categories.has(cat) ? ' active' : ''}\"\n\t\t\t\t\t\t\tonClick=${() => onToggleCategory(cat)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t${cat}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t`,\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t`\n}\n\n// ============================================================================\n// Timeline Panel\n// ============================================================================\n\nfunction TimelinePanel({\n\titems,\n\tsearch,\n\texpanded,\n\tonToggle,\n}: {\n\titems: TimelineItem[]\n\tsearch: string\n\texpanded: Set<number>\n\tonToggle: (id: number) => void\n}) {\n\tconst lowerSearch = search.toLowerCase()\n\tconst filtered = lowerSearch\n\t\t? items.filter(\n\t\t\t\t(i) =>\n\t\t\t\t\ti.label.toLowerCase().includes(lowerSearch) || i.type.toLowerCase().includes(lowerSearch),\n\t\t\t)\n\t\t: items\n\n\tconst visible = filtered.slice(-200).reverse()\n\n\tif (visible.length === 0) {\n\t\treturn html`<div class=\"kora-panel\"><div class=\"kora-empty\">No events recorded yet.</div></div>`\n\t}\n\n\treturn html`\n\t\t<div class=\"kora-panel kora-timeline\">\n\t\t\t<div class=\"kora-summary\">Showing ${visible.length} of ${items.length} events</div>\n\t\t\t<div class=\"kora-event-list\">\n\t\t\t\t${visible.map(\n\t\t\t\t\t(item) => html`\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey=${item.id}\n\t\t\t\t\t\t\tclass=\"kora-row${expanded.has(item.id) ? ' expanded' : ''}\"\n\t\t\t\t\t\t\tonClick=${() => onToggle(item.id)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span class=\"kora-time\">${formatTime(item.receivedAt)}</span>\n\t\t\t\t\t\t\t<span class=\"kora-dot\" style=\"background:${item.color}\"></span>\n\t\t\t\t\t\t\t<span class=\"kora-type\">${item.type}</span>\n\t\t\t\t\t\t\t<span class=\"kora-label\">${item.label}</span>\n\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\titem.dependsOn.length > 0\n\t\t\t\t\t\t\t\t\t? html`<span class=\"kora-deps\">deps: ${item.dependsOn.length}</span>`\n\t\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\texpanded.has(item.id)\n\t\t\t\t\t\t\t\t? html`\n\t\t\t\t\t\t\t\t\t<div class=\"kora-detail\">\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-detail-label\">Event ID:</span> ${item.id}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-detail-label\">Received:</span>\n\t\t\t\t\t\t\t\t\t\t\t${new Date(item.receivedAt).toISOString()}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\t\t\t\titem.dependsOn.length > 0\n\t\t\t\t\t\t\t\t\t\t\t\t? html`<div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-detail-label\">Causal deps:</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t${item.dependsOn.join(', ')}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t`\n\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t}\n\t\t\t\t\t`,\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t`\n}\n\n// ============================================================================\n// Conflicts Panel\n// ============================================================================\n\nfunction ConflictsPanel({\n\titems,\n\tsearch,\n\texpanded,\n\tonToggle,\n}: {\n\titems: ConflictItem[]\n\tsearch: string\n\texpanded: Set<number>\n\tonToggle: (id: number) => void\n}) {\n\tconst lowerSearch = search.toLowerCase()\n\tconst filtered = lowerSearch\n\t\t? items.filter(\n\t\t\t\t(i) =>\n\t\t\t\t\ti.collection.toLowerCase().includes(lowerSearch) ||\n\t\t\t\t\ti.field.toLowerCase().includes(lowerSearch) ||\n\t\t\t\t\ti.strategy.toLowerCase().includes(lowerSearch),\n\t\t\t)\n\t\t: items\n\n\tconst visible = filtered.slice(-100).reverse()\n\n\tif (visible.length === 0) {\n\t\treturn html`<div class=\"kora-panel\"><div class=\"kora-empty\">No merge conflicts detected.</div></div>`\n\t}\n\n\treturn html`\n\t\t<div class=\"kora-panel kora-conflicts\">\n\t\t\t<div class=\"kora-summary\">\n\t\t\t\t${items.length} conflict${items.length !== 1 ? 's' : ''}\n\t\t\t</div>\n\t\t\t<table class=\"kora-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Time</th>\n\t\t\t\t\t\t<th>Collection</th>\n\t\t\t\t\t\t<th>Field</th>\n\t\t\t\t\t\t<th>Strategy</th>\n\t\t\t\t\t\t<th>Tier</th>\n\t\t\t\t\t\t<th>Result</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t${visible.map(\n\t\t\t\t\t\t(item) => html`\n\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\tkey=${item.id}\n\t\t\t\t\t\t\t\tclass=\"kora-conflict-row${item.constraintViolated ? ' violated' : ''}${expanded.has(item.id) ? ' expanded' : ''}\"\n\t\t\t\t\t\t\t\tonClick=${() => onToggle(item.id)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<td>${formatTime(item.timestamp)}</td>\n\t\t\t\t\t\t\t\t<td>${item.collection}</td>\n\t\t\t\t\t\t\t\t<td class=\"kora-mono\">${item.field}</td>\n\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t<span class=\"kora-strategy tier-${item.tier}\">${item.strategy}</span>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td>${item.tier}</td>\n\t\t\t\t\t\t\t\t<td class=\"kora-mono\">${truncate(String(item.output), 30)}</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\texpanded.has(item.id)\n\t\t\t\t\t\t\t\t\t? html`\n\t\t\t\t\t\t\t\t\t\t<tr class=\"kora-detail-row\">\n\t\t\t\t\t\t\t\t\t\t\t<td colspan=\"6\">\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-conflict-detail\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-comparison\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-value-box\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-value-label\">Input A (local)</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<pre class=\"kora-value-code\">\n${formatValue(item.inputA)}</pre\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-value-box\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-value-label\">Input B (remote)</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<pre class=\"kora-value-code\">\n${formatValue(item.inputB)}</pre\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-value-box result\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-value-label\">Result</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<pre class=\"kora-value-code\">\n${formatValue(item.output)}</pre\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\t\t\t\t\t\t\titem.constraintViolated\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? html`<div class=\"kora-constraint-warning\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tConstraint violated: ${item.constraintViolated}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t`\n\t\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t`,\n\t\t\t\t\t)}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t`\n}\n\n// ============================================================================\n// Operations Panel\n// ============================================================================\n\nfunction OperationsPanel({\n\titems,\n\tsearch,\n\texpanded,\n\tonToggle,\n}: {\n\titems: OperationItem[]\n\tsearch: string\n\texpanded: Set<number>\n\tonToggle: (id: number) => void\n}) {\n\tconst lowerSearch = search.toLowerCase()\n\tconst filtered = lowerSearch\n\t\t? items.filter(\n\t\t\t\t(i) =>\n\t\t\t\t\ti.collection.toLowerCase().includes(lowerSearch) ||\n\t\t\t\t\ti.recordId.toLowerCase().includes(lowerSearch) ||\n\t\t\t\t\ti.operationId.toLowerCase().includes(lowerSearch) ||\n\t\t\t\t\ti.opType.toLowerCase().includes(lowerSearch),\n\t\t\t)\n\t\t: items\n\n\tconst visible = filtered.slice(-200).reverse()\n\n\tif (visible.length === 0) {\n\t\treturn html`<div class=\"kora-panel\"><div class=\"kora-empty\">No operations recorded yet.</div></div>`\n\t}\n\n\treturn html`\n\t\t<div class=\"kora-panel kora-operations\">\n\t\t\t<div class=\"kora-summary\">\n\t\t\t\tShowing ${visible.length} of ${items.length} operations\n\t\t\t</div>\n\t\t\t<div class=\"kora-op-list\">\n\t\t\t\t${visible.map(\n\t\t\t\t\t(item) => html`\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey=${item.id}\n\t\t\t\t\t\t\tclass=\"kora-op-row${expanded.has(item.id) ? ' expanded' : ''}\"\n\t\t\t\t\t\t\tonClick=${() => onToggle(item.id)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span class=\"kora-time\">${formatTime(item.timestamp)}</span>\n\t\t\t\t\t\t\t<span class=\"kora-op-type op-${item.opType}\">${item.opType}</span>\n\t\t\t\t\t\t\t<span class=\"kora-op-collection\">${item.collection}</span>\n\t\t\t\t\t\t\t<span class=\"kora-op-record kora-mono\"\n\t\t\t\t\t\t\t\t>${truncate(item.recordId, 12)}</span\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span class=\"kora-op-node\">node:${truncate(item.nodeId, 8)}</span>\n\t\t\t\t\t\t\t<span class=\"kora-op-seq\">#${item.sequenceNumber}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t${\n\t\t\t\t\t\t\texpanded.has(item.id)\n\t\t\t\t\t\t\t\t? html`\n\t\t\t\t\t\t\t\t\t<div class=\"kora-op-detail\">\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-detail-label\">Operation ID:</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-mono\">${item.operationId}</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-detail-label\">Node:</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-mono\">${item.nodeId}</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-detail-label\">Sequence:</span>\n\t\t\t\t\t\t\t\t\t\t\t${item.sequenceNumber}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\t\t\t\titem.causalDeps.length > 0\n\t\t\t\t\t\t\t\t\t\t\t\t? html`<div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-detail-label\">Causal deps:</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-mono\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>${item.causalDeps.join(', ')}</span\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\t\t\t\titem.data\n\t\t\t\t\t\t\t\t\t\t\t\t? html`<div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"kora-detail-label\">Data:</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<pre class=\"kora-value-code\">\n${JSON.stringify(item.data, null, 2)}</pre\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t`\n\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t}\n\t\t\t\t\t`,\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t`\n}\n\n// ============================================================================\n// Network Panel\n// ============================================================================\n\nfunction NetworkPanel({\n\tnetwork,\n\tevents,\n}: {\n\tnetwork: NetworkStatusModel\n\tevents: readonly TimestampedEvent[]\n}) {\n\tconst statusClass = network.connected ? 'connected' : 'disconnected'\n\tconst statusText = network.connected\n\t\t? network.quality\n\t\t\t? `Connected (${network.quality})`\n\t\t\t: 'Connected'\n\t\t: 'Disconnected'\n\n\tconst syncEvents = events\n\t\t.filter((e) => {\n\t\t\tconst cat = eventTypeToCategory(e.event.type)\n\t\t\treturn cat === 'sync' || cat === 'connection'\n\t\t})\n\t\t.slice(-50)\n\t\t.reverse()\n\n\treturn html`\n\t\t<div class=\"kora-panel kora-network\">\n\t\t\t<div class=\"kora-network-status\">\n\t\t\t\t<div class=\"kora-status-indicator ${statusClass}\">\n\t\t\t\t\t<span class=\"kora-status-dot\"></span>\n\t\t\t\t\t<span class=\"kora-status-text\">${statusText}</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div class=\"kora-stats-grid\">\n\t\t\t\t<${StatCard} label=\"Sent\" value=${String(network.sentOps)} unit=\"ops\" />\n\t\t\t\t<${StatCard} label=\"Received\" value=${String(network.receivedOps)} unit=\"ops\" />\n\t\t\t\t<${StatCard} label=\"Pending\" value=${String(network.pendingAcks)} unit=\"acks\" />\n\t\t\t\t<${StatCard}\n\t\t\t\t\tlabel=\"Last Sync\"\n\t\t\t\t\tvalue=${network.lastSyncAt ? formatTime(network.lastSyncAt) : 'never'}\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t${\n\t\t\t\tnetwork.versionVector.length > 0\n\t\t\t\t\t? html`\n\t\t\t\t\t\t<div class=\"kora-vv-section\">\n\t\t\t\t\t\t\t<h3>Version Vector</h3>\n\t\t\t\t\t\t\t<table class=\"kora-table kora-vv-table\">\n\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t<th>Node</th>\n\t\t\t\t\t\t\t\t\t\t<th>Sequence</th>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t${network.versionVector.map(\n\t\t\t\t\t\t\t\t\t\t(entry) => html`\n\t\t\t\t\t\t\t\t\t\t\t<tr key=${entry.nodeId}>\n\t\t\t\t\t\t\t\t\t\t\t\t<td class=\"kora-mono\">${truncate(entry.nodeId, 16)}</td>\n\t\t\t\t\t\t\t\t\t\t\t\t<td>${entry.sequenceNumber}</td>\n\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t`,\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t\t: null\n\t\t\t}\n\n\t\t\t${\n\t\t\t\tsyncEvents.length > 0\n\t\t\t\t\t? html`\n\t\t\t\t\t\t<div class=\"kora-sync-log\">\n\t\t\t\t\t\t\t<h3>Recent Sync Activity</h3>\n\t\t\t\t\t\t\t<div class=\"kora-event-list\">\n\t\t\t\t\t\t\t\t${syncEvents.map(\n\t\t\t\t\t\t\t\t\t(entry) => html`\n\t\t\t\t\t\t\t\t\t\t<div key=${entry.id} class=\"kora-row compact\">\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-time\"\n\t\t\t\t\t\t\t\t\t\t\t\t>${formatTime(entry.receivedAt)}</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"kora-type\">${entry.event.type}</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t`,\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t\t: null\n\t\t\t}\n\t\t</div>\n\t`\n}\n\nfunction StatCard({\n\tlabel,\n\tvalue,\n\tunit,\n}: {\n\tlabel: string\n\tvalue: string\n\tunit?: string\n}) {\n\treturn html`\n\t\t<div class=\"kora-stat-card\">\n\t\t\t<div class=\"kora-stat-value\">${value}</div>\n\t\t\t<div class=\"kora-stat-label\">${label}${unit ? ` (${unit})` : ''}</div>\n\t\t</div>\n\t`\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Render the DevTools panel into the target element using Preact.\n * Efficient re-renders via virtual DOM diffing.\n */\nexport function renderDevtoolsPanel(\n\ttarget: HTMLElement,\n\tevents: readonly TimestampedEvent[],\n): void {\n\trender(html`<${DevToolsPanel} events=${events} />`, target)\n}\n","import type { KoraEvent, KoraEventType } from '@korajs/core'\n\n/** A KoraEvent wrapped with a reception timestamp and sequential ID */\nexport interface TimestampedEvent {\n\t/** Auto-incrementing sequential ID */\n\tid: number\n\t/** The original framework event */\n\tevent: KoraEvent\n\t/** Date.now() when the instrumenter captured the event */\n\treceivedAt: number\n}\n\n/** Event categories for filtering and grouping */\nexport type EventCategory = 'operation' | 'merge' | 'sync' | 'query' | 'connection'\n\n/** Maps each KoraEventType to its category */\nconst EVENT_TYPE_CATEGORIES: Record<KoraEventType, EventCategory> = {\n\t'operation:created': 'operation',\n\t'operation:applied': 'operation',\n\t'merge:started': 'merge',\n\t'merge:completed': 'merge',\n\t'merge:conflict': 'merge',\n\t'constraint:violated': 'merge',\n\t'sync:connected': 'sync',\n\t'sync:disconnected': 'sync',\n\t'sync:auth-failed': 'sync',\n\t'sync:clock-skew': 'sync',\n\t'sync:clock-rebase': 'sync',\n\t'sync:schema-mismatch': 'sync',\n\t'sync:apply-failed': 'sync',\n\t'sync:sent': 'sync',\n\t'sync:received': 'sync',\n\t'sync:acknowledged': 'sync',\n\t'query:subscribed': 'query',\n\t'query:invalidated': 'query',\n\t'query:executed': 'query',\n\t'connection:quality': 'connection',\n\t'sync:diagnostics': 'sync',\n\t'sync:bandwidth': 'sync',\n\t'sync:initial-sync-progress': 'sync',\n\t'awareness:updated': 'sync',\n\t'state-machine:transition': 'operation',\n\t'state-machine:rejected': 'operation',\n\t'store:persistence-error': 'connection',\n\t'store:quota-exceeded': 'connection',\n\t'replay:completed': 'operation',\n}\n\n/** Look up the category for a given event type */\nexport function eventTypeToCategory(type: KoraEventType): EventCategory {\n\treturn EVENT_TYPE_CATEGORIES[type]\n}\n\n/** All event categories */\nexport const EVENT_CATEGORIES = ['operation', 'merge', 'sync', 'query', 'connection'] as const\n\n/** Configuration for the Instrumenter */\nexport interface DevtoolsConfig {\n\t/** Max events in the ring buffer (default: 10000) */\n\tbufferSize?: number\n\t/** Enable message bridge for DevTools panel communication (default: true) */\n\tbridgeEnabled?: boolean\n\t/** Custom message channel name (default: 'kora-devtools') */\n\tchannelName?: string\n}\n\n/** Filter criteria for querying events */\nexport interface EventFilterCriteria {\n\t/** Filter by event categories */\n\tcategories?: EventCategory[]\n\t/** Filter by specific event types */\n\ttypes?: KoraEventType[]\n\t/** Filter by reception time range */\n\ttimeRange?: { start: number; end: number }\n\t/** Filter by collection name (extracted from operation events) */\n\tcollection?: string\n}\n\n/** Aggregated statistics computed from a set of events */\nexport interface EventStatistics {\n\ttotalEvents: number\n\teventsByCategory: Record<EventCategory, number>\n\teventsByType: Partial<Record<KoraEventType, number>>\n\tmergeConflicts: number\n\tconstraintViolations: number\n\tavgMergeDuration: number | null\n\tavgQueryDuration: number | null\n\tsyncOperationsSent: number\n\tsyncOperationsReceived: number\n}\n","import type { KoraEventType } from '@korajs/core'\nimport type { EventCategory, EventFilterCriteria, TimestampedEvent } from '../types'\nimport { eventTypeToCategory } from '../types'\n\n/**\n * Maps a KoraEventType to its EventCategory.\n * Re-exported from types for public API convenience.\n */\nexport function getEventCategory(type: KoraEventType): EventCategory {\n\treturn eventTypeToCategory(type)\n}\n\n/**\n * Extracts the collection name from an event, if present.\n * Only operation events and query:subscribed carry a collection field directly.\n * For other event types that embed operations, we inspect the nested operation.\n */\nfunction extractCollection(event: TimestampedEvent): string | null {\n\tconst e = event.event\n\tswitch (e.type) {\n\t\tcase 'operation:created':\n\t\tcase 'operation:applied':\n\t\t\treturn e.operation.collection\n\t\tcase 'merge:started':\n\t\t\treturn e.operationA.collection\n\t\tcase 'merge:completed':\n\t\tcase 'merge:conflict':\n\t\t\treturn e.trace.operationA.collection\n\t\tcase 'constraint:violated':\n\t\t\treturn e.trace.operationA.collection\n\t\tcase 'query:subscribed':\n\t\t\treturn e.collection\n\t\tcase 'query:invalidated':\n\t\t\treturn e.trigger.collection\n\t\tcase 'sync:sent':\n\t\t\treturn e.operations[0]?.collection ?? null\n\t\tcase 'sync:received':\n\t\t\treturn e.operations[0]?.collection ?? null\n\t\tdefault:\n\t\t\treturn null\n\t}\n}\n\n/**\n * Filters a list of timestamped events by the given criteria.\n * All criteria are combined with AND logic: an event must match all specified criteria.\n * Returns all events if no criteria are specified.\n */\nexport function filterEvents(\n\tevents: readonly TimestampedEvent[],\n\tcriteria: EventFilterCriteria,\n): readonly TimestampedEvent[] {\n\tconst { categories, types, timeRange, collection } = criteria\n\n\t// Fast path: no filters\n\tif (!categories && !types && !timeRange && !collection) {\n\t\treturn events\n\t}\n\n\t// Pre-compute the set of allowed types from categories for efficient lookup\n\tconst categorySet = categories ? new Set<EventCategory>(categories) : null\n\tconst typeSet = types ? new Set<KoraEventType>(types) : null\n\n\treturn events.filter((event) => {\n\t\t// Category filter\n\t\tif (categorySet) {\n\t\t\tconst cat = eventTypeToCategory(event.event.type)\n\t\t\tif (!categorySet.has(cat)) return false\n\t\t}\n\n\t\t// Specific type filter\n\t\tif (typeSet) {\n\t\t\tif (!typeSet.has(event.event.type)) return false\n\t\t}\n\n\t\t// Time range filter (on receivedAt)\n\t\tif (timeRange) {\n\t\t\tif (event.receivedAt < timeRange.start || event.receivedAt > timeRange.end) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\t// Collection filter\n\t\tif (collection) {\n\t\t\tconst eventCollection = extractCollection(event)\n\t\t\tif (eventCollection !== collection) return false\n\t\t}\n\n\t\treturn true\n\t})\n}\n","import type { KoraEventType } from '@korajs/core'\nimport type { EventCategory, EventStatistics, TimestampedEvent } from '../types'\nimport { EVENT_CATEGORIES, eventTypeToCategory } from '../types'\n\n/**\n * Computes aggregate statistics from a collection of timestamped events.\n * Processes the event list in a single pass for efficiency.\n */\nexport function computeStatistics(events: readonly TimestampedEvent[]): EventStatistics {\n\tconst eventsByCategory: Record<EventCategory, number> = {\n\t\toperation: 0,\n\t\tmerge: 0,\n\t\tsync: 0,\n\t\tquery: 0,\n\t\tconnection: 0,\n\t}\n\tconst eventsByType: Partial<Record<KoraEventType, number>> = {}\n\n\tlet mergeConflicts = 0\n\tlet constraintViolations = 0\n\tlet mergeDurationSum = 0\n\tlet mergeDurationCount = 0\n\tlet queryDurationSum = 0\n\tlet queryDurationCount = 0\n\tlet syncOperationsSent = 0\n\tlet syncOperationsReceived = 0\n\n\tfor (const timestamped of events) {\n\t\tconst e = timestamped.event\n\n\t\t// Category count\n\t\tconst category = eventTypeToCategory(e.type)\n\t\teventsByCategory[category]++\n\n\t\t// Type count\n\t\teventsByType[e.type] = (eventsByType[e.type] ?? 0) + 1\n\n\t\t// Type-specific aggregations\n\t\tswitch (e.type) {\n\t\t\tcase 'merge:completed':\n\t\t\t\tmergeDurationSum += e.trace.duration\n\t\t\t\tmergeDurationCount++\n\t\t\t\tbreak\n\t\t\tcase 'merge:conflict':\n\t\t\t\tmergeConflicts++\n\t\t\t\tmergeDurationSum += e.trace.duration\n\t\t\t\tmergeDurationCount++\n\t\t\t\tbreak\n\t\t\tcase 'constraint:violated':\n\t\t\t\tconstraintViolations++\n\t\t\t\tbreak\n\t\t\tcase 'query:executed':\n\t\t\t\tqueryDurationSum += e.duration\n\t\t\t\tqueryDurationCount++\n\t\t\t\tbreak\n\t\t\tcase 'sync:sent':\n\t\t\t\tsyncOperationsSent += e.batchSize\n\t\t\t\tbreak\n\t\t\tcase 'sync:received':\n\t\t\t\tsyncOperationsReceived += e.batchSize\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\treturn {\n\t\ttotalEvents: events.length,\n\t\teventsByCategory,\n\t\teventsByType,\n\t\tmergeConflicts,\n\t\tconstraintViolations,\n\t\tavgMergeDuration: mergeDurationCount > 0 ? mergeDurationSum / mergeDurationCount : null,\n\t\tavgQueryDuration: queryDurationCount > 0 ? queryDurationSum / queryDurationCount : null,\n\t\tsyncOperationsSent,\n\t\tsyncOperationsReceived,\n\t}\n}\n","/**\n * Shared utility functions for DevTools panel components.\n */\n\nexport function formatTime(timestamp: number): string {\n\tconst d = new Date(timestamp)\n\tconst h = String(d.getHours()).padStart(2, '0')\n\tconst m = String(d.getMinutes()).padStart(2, '0')\n\tconst s = String(d.getSeconds()).padStart(2, '0')\n\tconst ms = String(d.getMilliseconds()).padStart(3, '0')\n\treturn `${h}:${m}:${s}.${ms}`\n}\n\nexport function formatDuration(ms: number): string {\n\tif (ms < 1) return `${(ms * 1000).toFixed(0)}µs`\n\tif (ms < 1000) return `${ms.toFixed(1)}ms`\n\treturn `${(ms / 1000).toFixed(2)}s`\n}\n\nexport function truncate(str: string, max: number): string {\n\tif (str.length <= max) return str\n\treturn `${str.slice(0, max - 1)}…`\n}\n\nexport function formatValue(value: unknown): string {\n\tif (value === null || value === undefined) return 'null'\n\tif (typeof value === 'string') return `\"${value}\"`\n\tif (typeof value === 'object') return JSON.stringify(value, null, 2)\n\treturn String(value)\n}\n","import type { KoraEvent, Operation } from '@korajs/core'\nimport type { TimestampedEvent } from '../types'\n\nexport interface TimelineItem {\n\tid: number\n\ttype: KoraEvent['type']\n\tlabel: string\n\tcolor: string\n\treceivedAt: number\n\tdependsOn: string[]\n}\n\nexport interface ConflictItem {\n\tid: number\n\ttimestamp: number\n\tcollection: string\n\tfield: string\n\tstrategy: string\n\ttier: 1 | 2 | 3\n\tinputA: unknown\n\tinputB: unknown\n\toutput: unknown\n\tconstraintViolated: string | null\n}\n\nexport interface OperationItem {\n\tid: number\n\ttimestamp: number\n\toperationId: string\n\tcollection: string\n\trecordId: string\n\topType: Operation['type']\n\tdata: Record<string, unknown> | null\n\tcausalDeps: string[]\n\tnodeId: string\n\tsequenceNumber: number\n}\n\nexport interface NetworkStatusModel {\n\tconnected: boolean\n\tquality: string | null\n\tpendingAcks: number\n\tlastSyncAt: number | null\n\tsentOps: number\n\treceivedOps: number\n\tversionVector: Array<{ nodeId: string; sequenceNumber: number }>\n}\n\nexport interface DevtoolsPanelModel {\n\ttimeline: TimelineItem[]\n\tconflicts: ConflictItem[]\n\toperations: OperationItem[]\n\tnetwork: NetworkStatusModel\n}\n\nexport function buildPanelModel(events: readonly TimestampedEvent[]): DevtoolsPanelModel {\n\tconst timeline = events.map((entry) => ({\n\t\tid: entry.id,\n\t\ttype: entry.event.type,\n\t\tlabel: timelineLabel(entry.event),\n\t\tcolor: timelineColor(entry.event.type),\n\t\treceivedAt: entry.receivedAt,\n\t\tdependsOn: extractCausalDependencies(entry.event),\n\t}))\n\n\tconst conflicts = events.flatMap((entry) => {\n\t\tif (\n\t\t\tentry.event.type !== 'merge:completed' &&\n\t\t\tentry.event.type !== 'merge:conflict' &&\n\t\t\tentry.event.type !== 'constraint:violated'\n\t\t) {\n\t\t\treturn []\n\t\t}\n\n\t\tconst trace = entry.event.trace\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: entry.id,\n\t\t\t\ttimestamp: entry.receivedAt,\n\t\t\t\tcollection: trace.operationA.collection,\n\t\t\t\tfield: trace.field,\n\t\t\t\tstrategy: trace.strategy,\n\t\t\t\ttier: trace.tier,\n\t\t\t\tinputA: trace.inputA,\n\t\t\t\tinputB: trace.inputB,\n\t\t\t\toutput: trace.output,\n\t\t\t\tconstraintViolated: trace.constraintViolated,\n\t\t\t},\n\t\t]\n\t})\n\n\tconst operations = events\n\t\t.map((entry) => {\n\t\t\tconst operation = extractOperation(entry.event)\n\t\t\tif (!operation) return null\n\n\t\t\treturn {\n\t\t\t\tid: entry.id,\n\t\t\t\ttimestamp: entry.receivedAt,\n\t\t\t\toperationId: operation.id,\n\t\t\t\tcollection: operation.collection,\n\t\t\t\trecordId: operation.recordId,\n\t\t\t\topType: operation.type,\n\t\t\t\tdata: operation.data,\n\t\t\t\tcausalDeps: operation.causalDeps,\n\t\t\t\tnodeId: operation.nodeId,\n\t\t\t\tsequenceNumber: operation.sequenceNumber,\n\t\t\t}\n\t\t})\n\t\t.filter((item): item is OperationItem => item !== null)\n\n\tconst network = buildNetworkStatus(events, operations)\n\n\treturn {\n\t\ttimeline,\n\t\tconflicts,\n\t\toperations,\n\t\tnetwork,\n\t}\n}\n\nfunction buildNetworkStatus(\n\tevents: readonly TimestampedEvent[],\n\toperations: readonly OperationItem[],\n): NetworkStatusModel {\n\tlet connected = false\n\tlet quality: string | null = null\n\tlet pendingAcks = 0\n\tlet lastSyncAt: number | null = null\n\tlet sentOps = 0\n\tlet receivedOps = 0\n\n\tfor (const entry of events) {\n\t\tswitch (entry.event.type) {\n\t\t\tcase 'sync:connected':\n\t\t\t\tconnected = true\n\t\t\t\tlastSyncAt = entry.receivedAt\n\t\t\t\tbreak\n\t\t\tcase 'sync:disconnected':\n\t\t\t\tconnected = false\n\t\t\t\tbreak\n\t\t\tcase 'connection:quality':\n\t\t\t\tquality = entry.event.quality\n\t\t\t\tbreak\n\t\t\tcase 'sync:sent':\n\t\t\t\tsentOps += entry.event.operations.length\n\t\t\t\tpendingAcks += entry.event.operations.length\n\t\t\t\tlastSyncAt = entry.receivedAt\n\t\t\t\tbreak\n\t\t\tcase 'sync:received':\n\t\t\t\treceivedOps += entry.event.operations.length\n\t\t\t\tlastSyncAt = entry.receivedAt\n\t\t\t\tbreak\n\t\t\tcase 'sync:acknowledged':\n\t\t\t\tpendingAcks = Math.max(0, pendingAcks - 1)\n\t\t\t\tlastSyncAt = entry.receivedAt\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tconst vector = new Map<string, number>()\n\tfor (const operation of operations) {\n\t\tconst current = vector.get(operation.nodeId) ?? 0\n\t\tif (operation.sequenceNumber > current) {\n\t\t\tvector.set(operation.nodeId, operation.sequenceNumber)\n\t\t}\n\t}\n\n\treturn {\n\t\tconnected,\n\t\tquality,\n\t\tpendingAcks,\n\t\tlastSyncAt,\n\t\tsentOps,\n\t\treceivedOps,\n\t\tversionVector: [...vector.entries()]\n\t\t\t.map(([nodeId, sequenceNumber]) => ({ nodeId, sequenceNumber }))\n\t\t\t.sort((left, right) => left.nodeId.localeCompare(right.nodeId)),\n\t}\n}\n\nfunction timelineLabel(event: KoraEvent): string {\n\tswitch (event.type) {\n\t\tcase 'operation:created':\n\t\tcase 'operation:applied':\n\t\t\treturn `${event.operation.type} ${event.operation.collection}/${event.operation.recordId}`\n\t\tcase 'merge:started':\n\t\t\treturn `merge start ${event.operationA.collection}`\n\t\tcase 'merge:completed':\n\t\t\treturn `merge complete ${event.trace.field}`\n\t\tcase 'merge:conflict':\n\t\t\treturn `merge conflict ${event.trace.field}`\n\t\tcase 'constraint:violated':\n\t\t\treturn `constraint ${event.constraint}`\n\t\tcase 'sync:connected':\n\t\t\treturn `sync connected ${event.nodeId}`\n\t\tcase 'sync:disconnected':\n\t\t\treturn 'sync disconnected'\n\t\tcase 'sync:auth-failed':\n\t\t\treturn 'sync auth failed'\n\t\tcase 'sync:clock-skew':\n\t\t\treturn `clock skew ${Math.round(event.skewMs / 1000)}s (${event.severity})`\n\t\tcase 'sync:clock-rebase':\n\t\t\treturn `clock rebase ${event.rebasedCount} ops (${Math.round(event.maxSkewMs / 1000)}s ahead)`\n\t\tcase 'sync:schema-mismatch':\n\t\t\treturn `schema mismatch client ${event.clientSchemaVersion} server ${event.serverSchemaVersion}`\n\t\tcase 'sync:apply-failed':\n\t\t\treturn `sync apply failed ${event.operationId}`\n\t\tcase 'sync:sent':\n\t\t\treturn `sync sent ${event.batchSize}`\n\t\tcase 'sync:received':\n\t\t\treturn `sync received ${event.batchSize}`\n\t\tcase 'sync:acknowledged':\n\t\t\treturn `sync ack ${event.sequenceNumber}`\n\t\tcase 'query:subscribed':\n\t\t\treturn `query subscribed ${event.collection}`\n\t\tcase 'query:invalidated':\n\t\t\treturn `query invalidated ${event.queryId}`\n\t\tcase 'query:executed':\n\t\t\treturn `query executed ${event.queryId}`\n\t\tcase 'connection:quality':\n\t\t\treturn `connection ${event.quality}`\n\t\tcase 'sync:diagnostics':\n\t\t\treturn `sync diagnostics ${event.diagnostics.quality}`\n\t\tcase 'sync:bandwidth':\n\t\t\treturn `bandwidth ${event.direction} ${event.bytesPerSecond}B/s`\n\t\tcase 'sync:initial-sync-progress':\n\t\t\treturn `initial sync ${Math.round(event.progress * 100)}%`\n\t\tcase 'awareness:updated':\n\t\t\treturn `awareness ${event.states.size} peers`\n\t\tcase 'state-machine:transition':\n\t\t\treturn `transition ${event.collection} ${event.from} → ${event.to}`\n\t\tcase 'state-machine:rejected':\n\t\t\treturn `transition rejected ${event.collection} ${event.from} → ${event.to}`\n\t\tcase 'store:persistence-error':\n\t\t\treturn `store persistence error ${event.message}`\n\t\tcase 'store:quota-exceeded':\n\t\t\treturn 'store quota exceeded'\n\t\tcase 'replay:completed':\n\t\t\treturn `replay ${event.operationsApplied} ops`\n\t}\n}\n\nfunction timelineColor(type: KoraEvent['type']): string {\n\tif (type.startsWith('operation:')) return '#22c55e'\n\tif (type.startsWith('sync:')) return '#a855f7'\n\tif (type.startsWith('merge:') || type.startsWith('constraint:')) return '#f59e0b'\n\tif (type.startsWith('query:')) return '#0ea5e9'\n\treturn '#64748b'\n}\n\nfunction extractCausalDependencies(event: KoraEvent): string[] {\n\tconst operation = extractOperation(event)\n\treturn operation?.causalDeps ?? []\n}\n\nfunction extractOperation(event: KoraEvent): Operation | null {\n\tswitch (event.type) {\n\t\tcase 'operation:created':\n\t\tcase 'operation:applied':\n\t\t\treturn event.operation\n\t\tcase 'query:invalidated':\n\t\t\treturn event.trigger\n\t\tdefault:\n\t\t\treturn null\n\t}\n}\n","/** DevTools panel styles (shared by extension and embedded overlay). */\nexport const KORA_DEVTOOLS_STYLES =\n\t\"/* ================================================================\\n Kora DevTools — Dark theme optimized for Chrome DevTools panels\\n ================================================================ */\\n\\n :root {\\n --bg-primary: #1e1e2e;\\n --bg-secondary: #181825;\\n --bg-surface: #313244;\\n --bg-hover: #45475a;\\n --bg-selected: #585b70;\\n --text-primary: #cdd6f4;\\n --text-secondary: #a6adc8;\\n --text-muted: #6c7086;\\n --border: #45475a;\\n\\n --green: #a6e3a1;\\n --red: #f38ba8;\\n --yellow: #f9e2af;\\n --blue: #89b4fa;\\n --purple: #cba6f7;\\n --teal: #94e2d5;\\n --peach: #fab387;\\n\\n --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace;\\n --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\\n --font-size: 12px;\\n }\\n\\n * {\\n box-sizing: border-box;\\n margin: 0;\\n padding: 0;\\n }\\n\\n body {\\n background: var(--bg-primary);\\n color: var(--text-primary);\\n font-family: var(--font-sans);\\n font-size: var(--font-size);\\n line-height: 1.5;\\n overflow: hidden;\\n }\\n\\n #kora-devtools-root {\\n width: 100vw;\\n height: 100vh;\\n display: flex;\\n flex-direction: column;\\n }\\n\\n .kora-devtools {\\n display: flex;\\n flex-direction: column;\\n height: 100%;\\n }\\n\\n /* ---- Toolbar ---- */\\n\\n .kora-toolbar {\\n background: var(--bg-secondary);\\n border-bottom: 1px solid var(--border);\\n padding: 4px 8px;\\n display: flex;\\n flex-wrap: wrap;\\n align-items: center;\\n gap: 8px;\\n flex-shrink: 0;\\n }\\n\\n .kora-tabs {\\n display: flex;\\n gap: 2px;\\n }\\n\\n .kora-tab {\\n background: transparent;\\n color: var(--text-secondary);\\n border: none;\\n padding: 4px 12px;\\n border-radius: 4px;\\n cursor: pointer;\\n font-size: var(--font-size);\\n font-family: var(--font-sans);\\n display: flex;\\n align-items: center;\\n gap: 6px;\\n }\\n\\n .kora-tab:hover {\\n background: var(--bg-hover);\\n color: var(--text-primary);\\n }\\n\\n .kora-tab.active {\\n background: var(--bg-surface);\\n color: var(--text-primary);\\n font-weight: 600;\\n }\\n\\n .kora-badge {\\n background: var(--bg-hover);\\n color: var(--text-muted);\\n padding: 0 6px;\\n border-radius: 10px;\\n font-size: 10px;\\n font-weight: 500;\\n }\\n\\n .kora-tab.active .kora-badge {\\n background: var(--blue);\\n color: var(--bg-primary);\\n }\\n\\n .kora-controls {\\n display: flex;\\n gap: 4px;\\n align-items: center;\\n margin-left: auto;\\n flex-wrap: wrap;\\n }\\n\\n .kora-search {\\n background: var(--bg-surface);\\n color: var(--text-primary);\\n border: 1px solid var(--border);\\n padding: 3px 8px;\\n border-radius: 4px;\\n font-size: var(--font-size);\\n font-family: var(--font-sans);\\n width: 140px;\\n outline: none;\\n }\\n\\n .kora-search:focus {\\n border-color: var(--blue);\\n }\\n\\n .kora-btn {\\n background: var(--bg-surface);\\n color: var(--text-secondary);\\n border: 1px solid var(--border);\\n padding: 3px 8px;\\n border-radius: 4px;\\n cursor: pointer;\\n font-size: 11px;\\n font-family: var(--font-sans);\\n }\\n\\n .kora-btn:hover {\\n background: var(--bg-hover);\\n color: var(--text-primary);\\n }\\n\\n .kora-cat-toggle {\\n background: transparent;\\n color: var(--text-muted);\\n border: 1px solid transparent;\\n padding: 2px 6px;\\n border-radius: 3px;\\n cursor: pointer;\\n font-size: 10px;\\n font-family: var(--font-sans);\\n text-transform: uppercase;\\n letter-spacing: 0.5px;\\n }\\n\\n .kora-cat-toggle:hover {\\n color: var(--text-secondary);\\n }\\n\\n .kora-cat-toggle.active {\\n background: var(--bg-surface);\\n color: var(--text-primary);\\n border-color: var(--border);\\n }\\n\\n /* ---- Content ---- */\\n\\n .kora-content {\\n flex: 1;\\n overflow: hidden;\\n display: flex;\\n flex-direction: column;\\n }\\n\\n .kora-panel {\\n flex: 1;\\n overflow-y: auto;\\n padding: 8px;\\n }\\n\\n .kora-empty {\\n color: var(--text-muted);\\n text-align: center;\\n padding: 40px;\\n font-style: italic;\\n }\\n\\n .kora-summary {\\n color: var(--text-muted);\\n font-size: 11px;\\n padding: 4px 0 8px;\\n border-bottom: 1px solid var(--border);\\n margin-bottom: 4px;\\n }\\n\\n /* ---- Event List (Timeline) ---- */\\n\\n .kora-event-list {\\n display: flex;\\n flex-direction: column;\\n }\\n\\n .kora-row {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n padding: 3px 6px;\\n border-radius: 3px;\\n cursor: pointer;\\n min-height: 24px;\\n }\\n\\n .kora-row:hover {\\n background: var(--bg-hover);\\n }\\n\\n .kora-row.expanded,\\n .kora-row.selected {\\n background: var(--bg-surface);\\n }\\n\\n .kora-row.compact {\\n padding: 2px 6px;\\n font-size: 11px;\\n }\\n\\n .kora-time {\\n color: var(--text-muted);\\n font-family: var(--font-mono);\\n font-size: 11px;\\n flex-shrink: 0;\\n width: 90px;\\n }\\n\\n .kora-dot {\\n width: 8px;\\n height: 8px;\\n border-radius: 50%;\\n flex-shrink: 0;\\n }\\n\\n .kora-type {\\n color: var(--text-secondary);\\n font-size: 11px;\\n flex-shrink: 0;\\n width: 140px;\\n }\\n\\n .kora-label {\\n color: var(--text-primary);\\n flex: 1;\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n }\\n\\n .kora-deps {\\n color: var(--peach);\\n font-size: 10px;\\n flex-shrink: 0;\\n }\\n\\n /* ---- Detail / Expandable ---- */\\n\\n .kora-detail,\\n .kora-op-detail {\\n background: var(--bg-secondary);\\n border: 1px solid var(--border);\\n border-radius: 4px;\\n padding: 8px 12px;\\n margin: 2px 0 6px 28px;\\n font-size: 11px;\\n display: flex;\\n flex-direction: column;\\n gap: 4px;\\n }\\n\\n .kora-detail-label {\\n color: var(--text-muted);\\n font-weight: 500;\\n margin-right: 4px;\\n }\\n\\n .kora-mono {\\n font-family: var(--font-mono);\\n font-size: 11px;\\n }\\n\\n /* ---- Conflicts Table ---- */\\n\\n .kora-table {\\n width: 100%;\\n border-collapse: collapse;\\n font-size: 11px;\\n }\\n\\n .kora-table th {\\n text-align: left;\\n color: var(--text-muted);\\n font-weight: 500;\\n padding: 6px 8px;\\n border-bottom: 1px solid var(--border);\\n text-transform: uppercase;\\n font-size: 10px;\\n letter-spacing: 0.5px;\\n }\\n\\n .kora-table td {\\n padding: 5px 8px;\\n border-bottom: 1px solid var(--bg-surface);\\n }\\n\\n .kora-conflict-row {\\n cursor: pointer;\\n }\\n\\n .kora-conflict-row:hover td {\\n background: var(--bg-hover);\\n }\\n\\n .kora-conflict-row.expanded td {\\n background: var(--bg-surface);\\n }\\n\\n .kora-conflict-row.violated td {\\n border-left: 3px solid var(--red);\\n }\\n\\n .kora-strategy {\\n padding: 1px 6px;\\n border-radius: 3px;\\n font-size: 10px;\\n font-weight: 500;\\n }\\n\\n .kora-strategy.tier-1 {\\n background: rgba(166, 227, 161, 0.15);\\n color: var(--green);\\n }\\n\\n .kora-strategy.tier-2 {\\n background: rgba(249, 226, 175, 0.15);\\n color: var(--yellow);\\n }\\n\\n .kora-strategy.tier-3 {\\n background: rgba(203, 166, 247, 0.15);\\n color: var(--purple);\\n }\\n\\n .kora-detail-row td {\\n padding: 0;\\n }\\n\\n .kora-conflict-detail {\\n padding: 12px;\\n }\\n\\n .kora-comparison {\\n display: grid;\\n grid-template-columns: 1fr 1fr 1fr;\\n gap: 8px;\\n }\\n\\n .kora-value-box {\\n background: var(--bg-primary);\\n border: 1px solid var(--border);\\n border-radius: 4px;\\n padding: 8px;\\n }\\n\\n .kora-value-box.result {\\n border-color: var(--green);\\n }\\n\\n .kora-value-label {\\n color: var(--text-muted);\\n font-size: 10px;\\n text-transform: uppercase;\\n letter-spacing: 0.5px;\\n margin-bottom: 4px;\\n }\\n\\n .kora-value-code {\\n font-family: var(--font-mono);\\n font-size: 11px;\\n color: var(--text-primary);\\n white-space: pre-wrap;\\n word-break: break-all;\\n margin: 0;\\n }\\n\\n .kora-constraint-warning {\\n margin-top: 8px;\\n padding: 6px 10px;\\n background: rgba(243, 139, 168, 0.1);\\n border: 1px solid var(--red);\\n border-radius: 4px;\\n color: var(--red);\\n font-size: 11px;\\n }\\n\\n /* ---- Operations ---- */\\n\\n .kora-op-list {\\n display: flex;\\n flex-direction: column;\\n }\\n\\n .kora-op-row {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n padding: 3px 6px;\\n border-radius: 3px;\\n cursor: pointer;\\n }\\n\\n .kora-op-row:hover {\\n background: var(--bg-hover);\\n }\\n\\n .kora-op-row.expanded {\\n background: var(--bg-surface);\\n }\\n\\n .kora-op-type {\\n padding: 1px 6px;\\n border-radius: 3px;\\n font-size: 10px;\\n font-weight: 600;\\n text-transform: uppercase;\\n flex-shrink: 0;\\n }\\n\\n .op-insert {\\n background: rgba(166, 227, 161, 0.15);\\n color: var(--green);\\n }\\n\\n .op-update {\\n background: rgba(137, 180, 250, 0.15);\\n color: var(--blue);\\n }\\n\\n .op-delete {\\n background: rgba(243, 139, 168, 0.15);\\n color: var(--red);\\n }\\n\\n .kora-op-collection {\\n color: var(--teal);\\n font-weight: 500;\\n flex-shrink: 0;\\n }\\n\\n .kora-op-record {\\n color: var(--text-secondary);\\n flex-shrink: 0;\\n }\\n\\n .kora-op-node {\\n color: var(--text-muted);\\n font-size: 10px;\\n flex-shrink: 0;\\n }\\n\\n .kora-op-seq {\\n color: var(--text-muted);\\n font-size: 10px;\\n flex-shrink: 0;\\n }\\n\\n /* ---- Network ---- */\\n\\n .kora-network-status {\\n padding: 12px 0;\\n }\\n\\n .kora-status-indicator {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n font-size: 14px;\\n font-weight: 600;\\n }\\n\\n .kora-status-dot {\\n width: 12px;\\n height: 12px;\\n border-radius: 50%;\\n }\\n\\n .connected .kora-status-dot {\\n background: var(--green);\\n box-shadow: 0 0 8px rgba(166, 227, 161, 0.5);\\n }\\n\\n .disconnected .kora-status-dot {\\n background: var(--red);\\n box-shadow: 0 0 8px rgba(243, 139, 168, 0.3);\\n }\\n\\n .connected .kora-status-text {\\n color: var(--green);\\n }\\n\\n .disconnected .kora-status-text {\\n color: var(--red);\\n }\\n\\n .kora-stats-grid {\\n display: grid;\\n grid-template-columns: repeat(4, 1fr);\\n gap: 8px;\\n margin: 12px 0;\\n }\\n\\n .kora-stat-card {\\n background: var(--bg-surface);\\n border: 1px solid var(--border);\\n border-radius: 6px;\\n padding: 12px;\\n text-align: center;\\n }\\n\\n .kora-stat-value {\\n font-size: 20px;\\n font-weight: 700;\\n color: var(--text-primary);\\n font-family: var(--font-mono);\\n }\\n\\n .kora-stat-label {\\n color: var(--text-muted);\\n font-size: 10px;\\n text-transform: uppercase;\\n letter-spacing: 0.5px;\\n margin-top: 2px;\\n }\\n\\n .kora-vv-section,\\n .kora-sync-log {\\n margin-top: 16px;\\n }\\n\\n .kora-vv-section h3,\\n .kora-sync-log h3 {\\n color: var(--text-secondary);\\n font-size: 12px;\\n font-weight: 600;\\n margin-bottom: 8px;\\n }\\n\\n .kora-vv-table {\\n max-width: 400px;\\n }\\n\\n /* ---- Scrollbar ---- */\\n\\n ::-webkit-scrollbar {\\n width: 8px;\\n }\\n\\n ::-webkit-scrollbar-track {\\n background: var(--bg-primary);\\n }\\n\\n ::-webkit-scrollbar-thumb {\\n background: var(--bg-hover);\\n border-radius: 4px;\\n }\\n\\n ::-webkit-scrollbar-thumb:hover {\\n background: var(--bg-selected);\\n }\"\n","import type { Instrumenter } from '../instrumenter/instrumenter'\nimport { renderDevtoolsPanel } from '../ui/panel'\nimport { KORA_DEVTOOLS_STYLES } from '../ui/panel-styles'\n\nconst OVERLAY_HOST_ID = 'kora-devtools-overlay-host'\n\n/**\n * Mount an in-page DevTools panel toggled with Ctrl+Shift+K (Cmd+Shift+K on macOS).\n *\n * @param instrumenter - Active instrumenter from createApp({ devtools: true })\n * @returns Teardown function (also called when overlay is destroyed)\n */\nexport function mountKoraDevtoolsOverlay(instrumenter: Instrumenter): () => void {\n\tif (typeof document === 'undefined') {\n\t\treturn () => {}\n\t}\n\n\tconst existing = document.getElementById(OVERLAY_HOST_ID)\n\tif (existing) {\n\t\texisting.remove()\n\t}\n\n\tconst host = document.createElement('div')\n\thost.id = OVERLAY_HOST_ID\n\thost.style.cssText =\n\t\t'position:fixed;inset:auto 12px 12px 12px;height:42vh;z-index:2147483646;display:none;'\n\n\tconst shadow = host.attachShadow({ mode: 'open' })\n\n\tconst style = document.createElement('style')\n\tstyle.textContent = KORA_DEVTOOLS_STYLES\n\tshadow.appendChild(style)\n\n\tconst panelRoot = document.createElement('div')\n\tpanelRoot.id = 'kora-devtools-root'\n\tpanelRoot.style.height = '100%'\n\tshadow.appendChild(panelRoot)\n\n\tconst hint = document.createElement('div')\n\thint.textContent = 'Kora DevTools (Ctrl+Shift+K)'\n\thint.style.cssText =\n\t\t'position:fixed;bottom:8px;right:12px;z-index:2147483645;font:11px sans-serif;color:#6c7086;background:#1e1e2e;padding:4px 8px;border-radius:4px;opacity:0.85;'\n\tdocument.body.appendChild(hint)\n\n\tdocument.body.appendChild(host)\n\n\tlet visible = false\n\tconst setVisible = (next: boolean): void => {\n\t\tvisible = next\n\t\thost.style.display = visible ? 'block' : 'none'\n\t\thint.style.display = visible ? 'none' : 'block'\n\t}\n\n\tconst refresh = (): void => {\n\t\tif (!visible) return\n\t\trenderDevtoolsPanel(panelRoot, instrumenter.getBuffer().getAll())\n\t}\n\n\tconst intervalId = window.setInterval(refresh, 300)\n\n\tconst onKeyDown = (event: KeyboardEvent): void => {\n\t\tconst isToggle = event.key === 'K' && event.shiftKey && (event.ctrlKey || event.metaKey)\n\t\tif (!isToggle) return\n\t\tevent.preventDefault()\n\t\tsetVisible(!visible)\n\t\tif (visible) {\n\t\t\trefresh()\n\t\t}\n\t}\n\n\twindow.addEventListener('keydown', onKeyDown)\n\n\treturn () => {\n\t\twindow.clearInterval(intervalId)\n\t\twindow.removeEventListener('keydown', onKeyDown)\n\t\thost.remove()\n\t\thint.remove()\n\t}\n}\n"],"mappings":"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","names":["h"]}
|
package/dist/extension/panel.cjs
CHANGED
|
@@ -430,6 +430,8 @@ var EVENT_TYPE_CATEGORIES = {
|
|
|
430
430
|
"sync:connected": "sync",
|
|
431
431
|
"sync:disconnected": "sync",
|
|
432
432
|
"sync:auth-failed": "sync",
|
|
433
|
+
"sync:clock-skew": "sync",
|
|
434
|
+
"sync:clock-rebase": "sync",
|
|
433
435
|
"sync:schema-mismatch": "sync",
|
|
434
436
|
"sync:apply-failed": "sync",
|
|
435
437
|
"sync:sent": "sync",
|
|
@@ -712,6 +714,10 @@ function timelineLabel(event) {
|
|
|
712
714
|
return "sync disconnected";
|
|
713
715
|
case "sync:auth-failed":
|
|
714
716
|
return "sync auth failed";
|
|
717
|
+
case "sync:clock-skew":
|
|
718
|
+
return `clock skew ${Math.round(event.skewMs / 1e3)}s (${event.severity})`;
|
|
719
|
+
case "sync:clock-rebase":
|
|
720
|
+
return `clock rebase ${event.rebasedCount} ops (${Math.round(event.maxSkewMs / 1e3)}s ahead)`;
|
|
715
721
|
case "sync:schema-mismatch":
|
|
716
722
|
return `schema mismatch client ${event.clientSchemaVersion} server ${event.serverSchemaVersion}`;
|
|
717
723
|
case "sync:apply-failed":
|