@korajs/devtools 1.0.0-beta.12 → 1.0.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,55 +4,46 @@ Browser DevTools extension for Kora.js. Inspect operations, trace conflict resol
4
4
 
5
5
  ## Install
6
6
 
7
+ `korajs` already depends on `@korajs/devtools`; apps only turn it on. Install it directly to use
8
+ the building blocks (`Instrumenter`, `EventBuffer`, the panel renderer) in your own tooling:
9
+
7
10
  ```bash
8
- pnpm add -D @korajs/devtools
11
+ pnpm add -D @korajs/devtools@beta
9
12
  ```
10
13
 
11
14
  ## Enable
12
15
 
13
- Add `devtools: true` to your app config:
14
-
16
+ <!-- docs-check: standalone -->
15
17
  ```typescript
16
- import { createApp } from 'korajs'
18
+ import { createApp, defineSchema, t } from 'korajs'
19
+
20
+ const schema = defineSchema({ version: 1, collections: { todos: { fields: { title: t.string() } } } })
17
21
 
18
22
  const app = createApp({
19
23
  schema,
20
- devtools: process.env.NODE_ENV === 'development',
24
+ devtools: import.meta.env.DEV,
21
25
  })
22
26
  ```
23
27
 
24
- When enabled, the Kora DevTools panel appears in your browser's developer tools.
25
-
26
- ## Panels
27
-
28
- ### Sync Timeline
29
- Horizontal timeline of operations and sync events, color-coded by type. Click any event to inspect it. Causal arrows show dependencies between operations.
28
+ With `devtools: true`, Kora records instrumentation events and:
30
29
 
31
- ### Conflict Inspector
32
- Table of all merge events, filterable by collection, tier, and strategy. Expand any row to see the full `MergeTrace` -- input values, base value, resolved output, and which tier handled it.
30
+ - mounts an in-page overlay, toggled with `Ctrl+Shift+K` (`Cmd+Shift+K` on macOS);
31
+ - forwards events to the browser extension. The extension is not published to a store yet: build
32
+ it with `pnpm --filter @korajs/devtools build` in the Kora repository and load
33
+ `packages/devtools/dist/extension` as an unpacked extension in a Chromium browser.
33
34
 
34
- ### Operation Log
35
- Searchable list of all operations. Click an operation to view its full payload, causal dependencies, and the database state at that point in time.
36
-
37
- ### Network Status
38
- Real-time connection quality indicator, pending operation count, bandwidth graph, and last sync timestamp.
39
-
40
- ## Instrumentation Events
41
-
42
- The DevTools extension listens for events emitted by the Kora runtime:
43
-
44
- - `operation:created` / `operation:applied`
45
- - `merge:started` / `merge:completed` / `merge:conflict`
46
- - `sync:connected` / `sync:disconnected` / `sync:sent` / `sync:received`
47
- - `query:subscribed` / `query:invalidated` / `query:executed`
48
- - `connection:quality`
35
+ ## Panels
49
36
 
50
- ## Keyboard Shortcut
37
+ - **Timeline**: recorded events in order, color-coded by type.
38
+ - **Conflicts**: every merge conflict with its strategy, tier, inputs and result.
39
+ - **Operations**: operations created on this device and applied from sync, with payload, node,
40
+ sequence number and causal dependencies.
41
+ - **Network**: connection state and quality, operations sent and received, last sync, and a
42
+ version vector derived from the recorded operations.
51
43
 
52
- In development mode, press `Ctrl+Shift+K` (or `Cmd+Shift+K` on macOS) to toggle the embedded DevTools overlay.
44
+ The panels show events recorded since the page loaded. The complete event catalog is in the
45
+ [DevTools API reference](https://korajs.dev/api/devtools#events).
53
46
 
54
47
  ## License
55
48
 
56
49
  MIT
57
-
58
- See the [full documentation](https://github.com/ehoneahobed/kora) for guides, API reference, and examples.
@@ -16,8 +16,14 @@ var EVENT_TYPE_CATEGORIES = {
16
16
  "sync:auth-failed": "sync",
17
17
  "sync:suspended": "sync",
18
18
  "sync:clock-skew": "sync",
19
+ "encryption:status": "sync",
20
+ "sync:node-id-rotated": "sync",
21
+ "sync:local-node": "sync",
19
22
  "sync:clock-rebase": "sync",
20
23
  "sync:schema-mismatch": "sync",
24
+ "sync:protocol-deprecated": "sync",
25
+ "sync:unverified-legacy-operation": "sync",
26
+ "sync:forged-duplicate": "sync",
21
27
  "sync:apply-failed": "sync",
22
28
  "sync:apply-blocked": "sync",
23
29
  "sync:apply-retrying": "sync",
@@ -31,20 +37,30 @@ var EVENT_TYPE_CATEGORIES = {
31
37
  "query:subscribed": "query",
32
38
  "query:invalidated": "query",
33
39
  "query:executed": "query",
40
+ "query:error": "query",
41
+ "storage:persistence": "connection",
34
42
  "connection:quality": "connection",
35
43
  "sync:diagnostics": "sync",
36
44
  "sync:bandwidth": "sync",
37
45
  "sync:initial-sync-progress": "sync",
38
46
  "sync:delivery-gap": "sync",
39
47
  "sync:delivery-stalled": "sync",
48
+ "sync:durability-degraded": "connection",
49
+ "sync:durability-restored": "connection",
40
50
  "awareness:updated": "sync",
41
51
  "state-machine:transition": "operation",
42
52
  "state-machine:rejected": "operation",
43
53
  "store:persistence-error": "connection",
44
54
  "store:quota-exceeded": "connection",
55
+ "store:log-integrity": "operation",
56
+ "store:rematerialized": "operation",
45
57
  "store:storage-fallback": "connection",
46
58
  "store:opfs-unavailable": "connection",
59
+ "store:durability-lost": "connection",
47
60
  "store:db-name-collision": "connection",
61
+ "store:storage-blocked": "connection",
62
+ "store:storage-migrated": "connection",
63
+ "store:schema-ahead": "connection",
48
64
  "replay:completed": "operation"
49
65
  };
50
66
  function eventTypeToCategory(type) {
@@ -315,12 +331,24 @@ function timelineLabel(event) {
315
331
  return "sync auth failed";
316
332
  case "sync:suspended":
317
333
  return `sync suspended (${event.reason})`;
334
+ case "encryption:status":
335
+ return `encryption ${event.status.state}${event.status.code ? ` (${event.status.code})` : ""}`;
318
336
  case "sync:clock-skew":
319
337
  return `clock skew ${Math.round(event.skewMs / 1e3)}s (${event.severity})`;
338
+ case "sync:node-id-rotated":
339
+ return `node id rotated (${event.reenqueuedCount} ops re-queued)`;
340
+ case "sync:local-node":
341
+ return `local node ${event.nodeId} ${event.action}`;
320
342
  case "sync:clock-rebase":
321
343
  return `clock rebase ${event.rebasedCount} ops (${Math.round(event.maxSkewMs / 1e3)}s ahead)`;
322
344
  case "sync:schema-mismatch":
323
345
  return `schema mismatch client ${event.clientSchemaVersion} server ${event.serverSchemaVersion}`;
346
+ case "sync:protocol-deprecated":
347
+ return `protocol v${event.clientProtocolVersion} deprecated (node ${event.nodeId})`;
348
+ case "sync:unverified-legacy-operation":
349
+ return `unverified legacy op ${event.operationId} (node ${event.nodeId})`;
350
+ case "sync:forged-duplicate":
351
+ return `forged duplicate refused ${event.operationId} (node ${event.nodeId})`;
324
352
  case "sync:apply-failed":
325
353
  return `sync apply failed ${event.operationId}`;
326
354
  case "sync:apply-blocked":
@@ -347,6 +375,10 @@ function timelineLabel(event) {
347
375
  return `query invalidated ${event.queryId}`;
348
376
  case "query:executed":
349
377
  return `query executed ${event.queryId}`;
378
+ case "query:error":
379
+ return `query error ${event.collection} (${event.phase}): ${event.message}`;
380
+ case "storage:persistence":
381
+ return `storage persistence ${event.state}: ${event.persisted ? "persisted" : "best-effort"}`;
350
382
  case "connection:quality":
351
383
  return `connection ${event.quality}`;
352
384
  case "sync:diagnostics":
@@ -359,6 +391,10 @@ function timelineLabel(event) {
359
391
  return `delivery gap expected ${event.expectedBase} received ${event.receivedBase}`;
360
392
  case "sync:delivery-stalled":
361
393
  return `delivery unacknowledged ${event.watermark} \u2192 ${event.outstandingMaxDeliverySequence} (${event.repeatCount} repeats)`;
394
+ case "sync:durability-degraded":
395
+ return `local durability degraded after ${event.failedAttempts} failed barriers: ${event.message}`;
396
+ case "sync:durability-restored":
397
+ return "local durability restored";
362
398
  case "awareness:updated":
363
399
  return `awareness ${event.states.size} peers`;
364
400
  case "state-machine:transition":
@@ -369,12 +405,24 @@ function timelineLabel(event) {
369
405
  return `store persistence error ${event.message}`;
370
406
  case "store:quota-exceeded":
371
407
  return "store quota exceeded";
408
+ case "store:log-integrity":
409
+ return `log integrity: ${event.repaired} repaired, ${event.quarantined} quarantined`;
410
+ case "store:rematerialized":
411
+ return `re-materialized ${event.records} records (${event.mode}), ${event.changedRows} changed`;
372
412
  case "store:storage-fallback":
373
413
  return `store fallback ${event.from} \u2192 ${event.to}`;
374
414
  case "store:opfs-unavailable":
375
415
  return `store OPFS unavailable (${event.reason}), running in memory`;
416
+ case "store:durability-lost":
417
+ return `store durability lost (${event.reason} during ${event.phase}), writes refused`;
376
418
  case "store:db-name-collision":
377
419
  return `store db-name collision ${event.dbName}`;
420
+ case "store:storage-blocked":
421
+ return `store ${event.resource} ${event.state === "waiting" ? "blocked by another holder" : "unblocked"}`;
422
+ case "store:storage-migrated":
423
+ return `store migrated ${event.from} \u2192 ${event.to}`;
424
+ case "store:schema-ahead":
425
+ return `store refused: database at schema v${event.storedVersion}, code at v${event.codeVersion}`;
378
426
  case "replay:completed":
379
427
  return `replay ${event.operationsApplied} ops`;
380
428
  }
@@ -948,4 +996,4 @@ export {
948
996
  renderDevtoolsPanel,
949
997
  mountKoraDevtoolsOverlay
950
998
  };
951
- //# sourceMappingURL=chunk-JTHYT3EM.js.map
999
+ //# sourceMappingURL=chunk-LQFL4TWM.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:suspended': 'sync',\n\t'sync:clock-skew': 'sync',\n\t'encryption:status': 'sync',\n\t'sync:node-id-rotated': 'sync',\n\t'sync:local-node': 'sync',\n\t'sync:clock-rebase': 'sync',\n\t'sync:schema-mismatch': 'sync',\n\t'sync:protocol-deprecated': 'sync',\n\t'sync:unverified-legacy-operation': 'sync',\n\t'sync:forged-duplicate': 'sync',\n\t'sync:apply-failed': 'sync',\n\t'sync:apply-blocked': 'sync',\n\t'sync:apply-retrying': 'sync',\n\t'sync:apply-recovered': 'sync',\n\t'sync:apply-abandoned': 'sync',\n\t'sync:operation-rejected': 'sync',\n\t'sync:sent': 'sync',\n\t'sync:received': 'sync',\n\t'sync:acknowledged': 'sync',\n\t'sync:scope-retracted': 'sync',\n\t'query:subscribed': 'query',\n\t'query:invalidated': 'query',\n\t'query:executed': 'query',\n\t'query:error': 'query',\n\t'storage:persistence': 'connection',\n\t'connection:quality': 'connection',\n\t'sync:diagnostics': 'sync',\n\t'sync:bandwidth': 'sync',\n\t'sync:initial-sync-progress': 'sync',\n\t'sync:delivery-gap': 'sync',\n\t'sync:delivery-stalled': 'sync',\n\t'sync:durability-degraded': 'connection',\n\t'sync:durability-restored': 'connection',\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'store:log-integrity': 'operation',\n\t'store:rematerialized': 'operation',\n\t'store:storage-fallback': 'connection',\n\t'store:opfs-unavailable': 'connection',\n\t'store:durability-lost': 'connection',\n\t'store:db-name-collision': 'connection',\n\t'store:storage-blocked': 'connection',\n\t'store:storage-migrated': 'connection',\n\t'store:schema-ahead': '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:suspended':\n\t\t\treturn `sync suspended (${event.reason})`\n\t\tcase 'encryption:status':\n\t\t\treturn `encryption ${event.status.state}${event.status.code ? ` (${event.status.code})` : ''}`\n\t\tcase 'sync:clock-skew':\n\t\t\treturn `clock skew ${Math.round(event.skewMs / 1000)}s (${event.severity})`\n\t\tcase 'sync:node-id-rotated':\n\t\t\treturn `node id rotated (${event.reenqueuedCount} ops re-queued)`\n\t\tcase 'sync:local-node':\n\t\t\treturn `local node ${event.nodeId} ${event.action}`\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:protocol-deprecated':\n\t\t\treturn `protocol v${event.clientProtocolVersion} deprecated (node ${event.nodeId})`\n\t\tcase 'sync:unverified-legacy-operation':\n\t\t\treturn `unverified legacy op ${event.operationId} (node ${event.nodeId})`\n\t\tcase 'sync:forged-duplicate':\n\t\t\treturn `forged duplicate refused ${event.operationId} (node ${event.nodeId})`\n\t\tcase 'sync:apply-failed':\n\t\t\treturn `sync apply failed ${event.operationId}`\n\t\tcase 'sync:apply-blocked':\n\t\t\treturn `sync apply blocked ${event.failure.collection}/${event.failure.recordId}`\n\t\tcase 'sync:apply-retrying':\n\t\t\treturn `sync apply retry ${event.failure.collection}/${event.failure.recordId}`\n\t\tcase 'sync:apply-recovered':\n\t\t\treturn `sync apply recovered ${event.failure.collection}/${event.failure.recordId}`\n\t\tcase 'sync:apply-abandoned':\n\t\t\treturn `sync apply abandoned ${event.failure.collection}/${event.failure.recordId}`\n\t\tcase 'sync:operation-rejected':\n\t\t\treturn `sync op rejected ${event.collection}/${event.recordId} (${event.code})`\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 'sync:scope-retracted':\n\t\t\treturn `scope retracted ${event.collection}/${event.recordId}`\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 'query:error':\n\t\t\treturn `query error ${event.collection} (${event.phase}): ${event.message}`\n\t\tcase 'storage:persistence':\n\t\t\treturn `storage persistence ${event.state}: ${event.persisted ? 'persisted' : 'best-effort'}`\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 'sync:delivery-gap':\n\t\t\treturn `delivery gap expected ${event.expectedBase} received ${event.receivedBase}`\n\t\tcase 'sync:delivery-stalled':\n\t\t\treturn `delivery unacknowledged ${event.watermark} → ${event.outstandingMaxDeliverySequence} (${event.repeatCount} repeats)`\n\t\tcase 'sync:durability-degraded':\n\t\t\treturn `local durability degraded after ${event.failedAttempts} failed barriers: ${event.message}`\n\t\tcase 'sync:durability-restored':\n\t\t\treturn 'local durability restored'\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 'store:log-integrity':\n\t\t\treturn `log integrity: ${event.repaired} repaired, ${event.quarantined} quarantined`\n\t\tcase 'store:rematerialized':\n\t\t\treturn `re-materialized ${event.records} records (${event.mode}), ${event.changedRows} changed`\n\t\tcase 'store:storage-fallback':\n\t\t\treturn `store fallback ${event.from} → ${event.to}`\n\t\tcase 'store:opfs-unavailable':\n\t\t\treturn `store OPFS unavailable (${event.reason}), running in memory`\n\t\tcase 'store:durability-lost':\n\t\t\treturn `store durability lost (${event.reason} during ${event.phase}), writes refused`\n\t\tcase 'store:db-name-collision':\n\t\t\treturn `store db-name collision ${event.dbName}`\n\t\tcase 'store:storage-blocked':\n\t\t\treturn `store ${event.resource} ${event.state === 'waiting' ? 'blocked by another holder' : 'unblocked'}`\n\t\tcase 'store:storage-migrated':\n\t\t\treturn `store migrated ${event.from} → ${event.to}`\n\t\tcase 'store:schema-ahead':\n\t\t\treturn `store refused: database at schema v${event.storedVersion}, code at v${event.codeVersion}`\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 :host,\\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 :host,\\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: 100%;\\n height: 100%;\\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\t\t:host {\n\t\t\tcolor-scheme: dark;\n\t\t\tbackground: #1e1e2e;\n\t\t\tcolor: #cdd6f4;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n\t\t\toverflow: hidden;\n\t\t\tborder: 1px solid #45475a;\n\t\t\tbox-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);\n\t\t}\n\t\t.kora-overlay-close {\n\t\t\tposition: absolute;\n\t\t\ttop: 6px;\n\t\t\tright: 8px;\n\t\t\tz-index: 1;\n\t\t\tbackground: #313244;\n\t\t\tcolor: #cdd6f4;\n\t\t\tborder: 1px solid #45475a;\n\t\t\tborder-radius: 4px;\n\t\t\tcursor: pointer;\n\t\t\tfont: 12px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n\t\t\tline-height: 1;\n\t\t\tpadding: 4px 8px;\n\t\t}\n\t\t.kora-overlay-close:hover {\n\t\t\tbackground: #45475a;\n\t\t}\n\t`\n\tshadow.appendChild(style)\n\n\tconst closeButton = document.createElement('button')\n\tcloseButton.type = 'button'\n\tcloseButton.className = 'kora-overlay-close'\n\tcloseButton.textContent = 'Close'\n\tcloseButton.setAttribute('aria-label', 'Close Kora DevTools')\n\tshadow.appendChild(closeButton)\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.textContent = visible ? 'Hide Kora DevTools (Esc)' : 'Kora DevTools (Ctrl+Shift+K)'\n\t\thint.style.display = '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) {\n\t\t\tevent.preventDefault()\n\t\t\tsetVisible(!visible)\n\t\t\tif (visible) {\n\t\t\t\trefresh()\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (event.key === 'Escape' && visible) {\n\t\t\tevent.preventDefault()\n\t\t\tsetVisible(false)\n\t\t}\n\t}\n\n\tconst onHintClick = (): void => {\n\t\tsetVisible(!visible)\n\t\tif (visible) {\n\t\t\trefresh()\n\t\t}\n\t}\n\n\tconst onCloseClick = (): void => {\n\t\tsetVisible(false)\n\t}\n\n\twindow.addEventListener('keydown', onKeyDown)\n\thint.addEventListener('click', onHintClick)\n\tcloseButton.addEventListener('click', onCloseClick)\n\n\treturn () => {\n\t\twindow.clearInterval(intervalId)\n\t\twindow.removeEventListener('keydown', onKeyDown)\n\t\thint.removeEventListener('click', onHintClick)\n\t\tcloseButton.removeEventListener('click', onCloseClick)\n\t\thost.remove()\n\t\thint.remove()\n\t}\n}\n"],"mappings":"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","names":["h"]}
@@ -432,8 +432,14 @@ var EVENT_TYPE_CATEGORIES = {
432
432
  "sync:auth-failed": "sync",
433
433
  "sync:suspended": "sync",
434
434
  "sync:clock-skew": "sync",
435
+ "encryption:status": "sync",
436
+ "sync:node-id-rotated": "sync",
437
+ "sync:local-node": "sync",
435
438
  "sync:clock-rebase": "sync",
436
439
  "sync:schema-mismatch": "sync",
440
+ "sync:protocol-deprecated": "sync",
441
+ "sync:unverified-legacy-operation": "sync",
442
+ "sync:forged-duplicate": "sync",
437
443
  "sync:apply-failed": "sync",
438
444
  "sync:apply-blocked": "sync",
439
445
  "sync:apply-retrying": "sync",
@@ -447,20 +453,30 @@ var EVENT_TYPE_CATEGORIES = {
447
453
  "query:subscribed": "query",
448
454
  "query:invalidated": "query",
449
455
  "query:executed": "query",
456
+ "query:error": "query",
457
+ "storage:persistence": "connection",
450
458
  "connection:quality": "connection",
451
459
  "sync:diagnostics": "sync",
452
460
  "sync:bandwidth": "sync",
453
461
  "sync:initial-sync-progress": "sync",
454
462
  "sync:delivery-gap": "sync",
455
463
  "sync:delivery-stalled": "sync",
464
+ "sync:durability-degraded": "connection",
465
+ "sync:durability-restored": "connection",
456
466
  "awareness:updated": "sync",
457
467
  "state-machine:transition": "operation",
458
468
  "state-machine:rejected": "operation",
459
469
  "store:persistence-error": "connection",
460
470
  "store:quota-exceeded": "connection",
471
+ "store:log-integrity": "operation",
472
+ "store:rematerialized": "operation",
461
473
  "store:storage-fallback": "connection",
462
474
  "store:opfs-unavailable": "connection",
475
+ "store:durability-lost": "connection",
463
476
  "store:db-name-collision": "connection",
477
+ "store:storage-blocked": "connection",
478
+ "store:storage-migrated": "connection",
479
+ "store:schema-ahead": "connection",
464
480
  "replay:completed": "operation"
465
481
  };
466
482
  function eventTypeToCategory(type) {
@@ -728,12 +744,24 @@ function timelineLabel(event) {
728
744
  return "sync auth failed";
729
745
  case "sync:suspended":
730
746
  return `sync suspended (${event.reason})`;
747
+ case "encryption:status":
748
+ return `encryption ${event.status.state}${event.status.code ? ` (${event.status.code})` : ""}`;
731
749
  case "sync:clock-skew":
732
750
  return `clock skew ${Math.round(event.skewMs / 1e3)}s (${event.severity})`;
751
+ case "sync:node-id-rotated":
752
+ return `node id rotated (${event.reenqueuedCount} ops re-queued)`;
753
+ case "sync:local-node":
754
+ return `local node ${event.nodeId} ${event.action}`;
733
755
  case "sync:clock-rebase":
734
756
  return `clock rebase ${event.rebasedCount} ops (${Math.round(event.maxSkewMs / 1e3)}s ahead)`;
735
757
  case "sync:schema-mismatch":
736
758
  return `schema mismatch client ${event.clientSchemaVersion} server ${event.serverSchemaVersion}`;
759
+ case "sync:protocol-deprecated":
760
+ return `protocol v${event.clientProtocolVersion} deprecated (node ${event.nodeId})`;
761
+ case "sync:unverified-legacy-operation":
762
+ return `unverified legacy op ${event.operationId} (node ${event.nodeId})`;
763
+ case "sync:forged-duplicate":
764
+ return `forged duplicate refused ${event.operationId} (node ${event.nodeId})`;
737
765
  case "sync:apply-failed":
738
766
  return `sync apply failed ${event.operationId}`;
739
767
  case "sync:apply-blocked":
@@ -760,6 +788,10 @@ function timelineLabel(event) {
760
788
  return `query invalidated ${event.queryId}`;
761
789
  case "query:executed":
762
790
  return `query executed ${event.queryId}`;
791
+ case "query:error":
792
+ return `query error ${event.collection} (${event.phase}): ${event.message}`;
793
+ case "storage:persistence":
794
+ return `storage persistence ${event.state}: ${event.persisted ? "persisted" : "best-effort"}`;
763
795
  case "connection:quality":
764
796
  return `connection ${event.quality}`;
765
797
  case "sync:diagnostics":
@@ -772,6 +804,10 @@ function timelineLabel(event) {
772
804
  return `delivery gap expected ${event.expectedBase} received ${event.receivedBase}`;
773
805
  case "sync:delivery-stalled":
774
806
  return `delivery unacknowledged ${event.watermark} \u2192 ${event.outstandingMaxDeliverySequence} (${event.repeatCount} repeats)`;
807
+ case "sync:durability-degraded":
808
+ return `local durability degraded after ${event.failedAttempts} failed barriers: ${event.message}`;
809
+ case "sync:durability-restored":
810
+ return "local durability restored";
775
811
  case "awareness:updated":
776
812
  return `awareness ${event.states.size} peers`;
777
813
  case "state-machine:transition":
@@ -782,12 +818,24 @@ function timelineLabel(event) {
782
818
  return `store persistence error ${event.message}`;
783
819
  case "store:quota-exceeded":
784
820
  return "store quota exceeded";
821
+ case "store:log-integrity":
822
+ return `log integrity: ${event.repaired} repaired, ${event.quarantined} quarantined`;
823
+ case "store:rematerialized":
824
+ return `re-materialized ${event.records} records (${event.mode}), ${event.changedRows} changed`;
785
825
  case "store:storage-fallback":
786
826
  return `store fallback ${event.from} \u2192 ${event.to}`;
787
827
  case "store:opfs-unavailable":
788
828
  return `store OPFS unavailable (${event.reason}), running in memory`;
829
+ case "store:durability-lost":
830
+ return `store durability lost (${event.reason} during ${event.phase}), writes refused`;
789
831
  case "store:db-name-collision":
790
832
  return `store db-name collision ${event.dbName}`;
833
+ case "store:storage-blocked":
834
+ return `store ${event.resource} ${event.state === "waiting" ? "blocked by another holder" : "unblocked"}`;
835
+ case "store:storage-migrated":
836
+ return `store migrated ${event.from} \u2192 ${event.to}`;
837
+ case "store:schema-ahead":
838
+ return `store refused: database at schema v${event.storedVersion}, code at v${event.codeVersion}`;
791
839
  case "replay:completed":
792
840
  return `replay ${event.operationsApplied} ops`;
793
841
  }