@korajs/devtools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/chunk-4ZQ2RTZM.js +203 -0
  2. package/dist/chunk-4ZQ2RTZM.js.map +1 -0
  3. package/dist/chunk-JH2X4T4Z.js +58 -0
  4. package/dist/chunk-JH2X4T4Z.js.map +1 -0
  5. package/dist/extension/background.cjs +65 -0
  6. package/dist/extension/background.cjs.map +1 -0
  7. package/dist/extension/background.d.cts +2 -0
  8. package/dist/extension/background.d.ts +2 -0
  9. package/dist/extension/background.js +13 -0
  10. package/dist/extension/background.js.map +1 -0
  11. package/dist/extension/content-script.cjs +18 -0
  12. package/dist/extension/content-script.cjs.map +1 -0
  13. package/dist/extension/content-script.d.cts +2 -0
  14. package/dist/extension/content-script.d.ts +2 -0
  15. package/dist/extension/content-script.js +16 -0
  16. package/dist/extension/content-script.js.map +1 -0
  17. package/dist/extension/devtools-page.html +45 -0
  18. package/dist/extension/devtools.cjs +9 -0
  19. package/dist/extension/devtools.cjs.map +1 -0
  20. package/dist/extension/devtools.d.cts +2 -0
  21. package/dist/extension/devtools.d.ts +2 -0
  22. package/dist/extension/devtools.js +7 -0
  23. package/dist/extension/devtools.js.map +1 -0
  24. package/dist/extension/manifest.json +20 -0
  25. package/dist/extension/panel.cjs +220 -0
  26. package/dist/extension/panel.cjs.map +1 -0
  27. package/dist/extension/panel.d.cts +2 -0
  28. package/dist/extension/panel.d.ts +2 -0
  29. package/dist/extension/panel.js +24 -0
  30. package/dist/extension/panel.js.map +1 -0
  31. package/dist/index.cjs +662 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.cts +260 -0
  34. package/dist/index.d.ts +260 -0
  35. package/dist/index.js +383 -0
  36. package/dist/index.js.map +1 -0
  37. package/package.json +42 -0
@@ -0,0 +1,203 @@
1
+ // src/ui/panel-state.ts
2
+ function buildPanelModel(events) {
3
+ const timeline = events.map((entry) => ({
4
+ id: entry.id,
5
+ type: entry.event.type,
6
+ label: timelineLabel(entry.event),
7
+ color: timelineColor(entry.event.type),
8
+ receivedAt: entry.receivedAt,
9
+ dependsOn: extractCausalDependencies(entry.event)
10
+ }));
11
+ const conflicts = events.flatMap((entry) => {
12
+ if (entry.event.type !== "merge:completed" && entry.event.type !== "merge:conflict") {
13
+ return [];
14
+ }
15
+ const trace = entry.event.trace;
16
+ return [
17
+ {
18
+ id: entry.id,
19
+ timestamp: entry.receivedAt,
20
+ collection: trace.operationA.collection,
21
+ field: trace.field,
22
+ strategy: trace.strategy,
23
+ tier: trace.tier,
24
+ inputA: trace.inputA,
25
+ inputB: trace.inputB,
26
+ output: trace.output,
27
+ constraintViolated: trace.constraintViolated
28
+ }
29
+ ];
30
+ });
31
+ const operations = events.map((entry) => {
32
+ const operation = extractOperation(entry.event);
33
+ if (!operation) return null;
34
+ return {
35
+ id: entry.id,
36
+ timestamp: entry.receivedAt,
37
+ operationId: operation.id,
38
+ collection: operation.collection,
39
+ recordId: operation.recordId,
40
+ opType: operation.type,
41
+ data: operation.data,
42
+ causalDeps: operation.causalDeps,
43
+ nodeId: operation.nodeId,
44
+ sequenceNumber: operation.sequenceNumber
45
+ };
46
+ }).filter((item) => item !== null);
47
+ const network = buildNetworkStatus(events, operations);
48
+ return {
49
+ timeline,
50
+ conflicts,
51
+ operations,
52
+ network
53
+ };
54
+ }
55
+ function buildNetworkStatus(events, operations) {
56
+ let connected = false;
57
+ let quality = null;
58
+ let pendingAcks = 0;
59
+ let lastSyncAt = null;
60
+ let sentOps = 0;
61
+ let receivedOps = 0;
62
+ for (const entry of events) {
63
+ switch (entry.event.type) {
64
+ case "sync:connected":
65
+ connected = true;
66
+ lastSyncAt = entry.receivedAt;
67
+ break;
68
+ case "sync:disconnected":
69
+ connected = false;
70
+ break;
71
+ case "connection:quality":
72
+ quality = entry.event.quality;
73
+ break;
74
+ case "sync:sent":
75
+ sentOps += entry.event.operations.length;
76
+ pendingAcks += entry.event.operations.length;
77
+ lastSyncAt = entry.receivedAt;
78
+ break;
79
+ case "sync:received":
80
+ receivedOps += entry.event.operations.length;
81
+ lastSyncAt = entry.receivedAt;
82
+ break;
83
+ case "sync:acknowledged":
84
+ pendingAcks = Math.max(0, pendingAcks - 1);
85
+ lastSyncAt = entry.receivedAt;
86
+ break;
87
+ }
88
+ }
89
+ const vector = /* @__PURE__ */ new Map();
90
+ for (const operation of operations) {
91
+ const current = vector.get(operation.nodeId) ?? 0;
92
+ if (operation.sequenceNumber > current) {
93
+ vector.set(operation.nodeId, operation.sequenceNumber);
94
+ }
95
+ }
96
+ return {
97
+ connected,
98
+ quality,
99
+ pendingAcks,
100
+ lastSyncAt,
101
+ sentOps,
102
+ receivedOps,
103
+ versionVector: [...vector.entries()].map(([nodeId, sequenceNumber]) => ({ nodeId, sequenceNumber })).sort((left, right) => left.nodeId.localeCompare(right.nodeId))
104
+ };
105
+ }
106
+ function timelineLabel(event) {
107
+ switch (event.type) {
108
+ case "operation:created":
109
+ case "operation:applied":
110
+ return `${event.operation.type} ${event.operation.collection}/${event.operation.recordId}`;
111
+ case "merge:started":
112
+ return `merge start ${event.operationA.collection}`;
113
+ case "merge:completed":
114
+ return `merge complete ${event.trace.field}`;
115
+ case "merge:conflict":
116
+ return `merge conflict ${event.trace.field}`;
117
+ case "constraint:violated":
118
+ return `constraint ${event.constraint}`;
119
+ case "sync:connected":
120
+ return `sync connected ${event.nodeId}`;
121
+ case "sync:disconnected":
122
+ return `sync disconnected`;
123
+ case "sync:sent":
124
+ return `sync sent ${event.batchSize}`;
125
+ case "sync:received":
126
+ return `sync received ${event.batchSize}`;
127
+ case "sync:acknowledged":
128
+ return `sync ack ${event.sequenceNumber}`;
129
+ case "query:subscribed":
130
+ return `query subscribed ${event.collection}`;
131
+ case "query:invalidated":
132
+ return `query invalidated ${event.queryId}`;
133
+ case "query:executed":
134
+ return `query executed ${event.queryId}`;
135
+ case "connection:quality":
136
+ return `connection ${event.quality}`;
137
+ }
138
+ }
139
+ function timelineColor(type) {
140
+ if (type.startsWith("operation:")) return "#22c55e";
141
+ if (type.startsWith("sync:")) return "#a855f7";
142
+ if (type.startsWith("merge:") || type.startsWith("constraint:")) return "#f59e0b";
143
+ if (type.startsWith("query:")) return "#0ea5e9";
144
+ return "#64748b";
145
+ }
146
+ function extractCausalDependencies(event) {
147
+ const operation = extractOperation(event);
148
+ return operation?.causalDeps ?? [];
149
+ }
150
+ function extractOperation(event) {
151
+ switch (event.type) {
152
+ case "operation:created":
153
+ case "operation:applied":
154
+ return event.operation;
155
+ case "query:invalidated":
156
+ return event.trigger;
157
+ default:
158
+ return null;
159
+ }
160
+ }
161
+
162
+ // src/ui/panel.ts
163
+ function renderDevtoolsPanel(target, events) {
164
+ const model = buildPanelModel(events);
165
+ target.innerHTML = [
166
+ '<section data-panel="timeline"><h2>Sync Timeline</h2>',
167
+ `<p>Total events: ${model.timeline.length}</p>`,
168
+ "<ul>",
169
+ ...model.timeline.slice(-20).map(
170
+ (item) => `<li><span style="color:${item.color}">${item.type}</span> \xB7 ${escapeHtml(item.label)}</li>`
171
+ ),
172
+ "</ul></section>",
173
+ '<section data-panel="conflicts"><h2>Conflict Inspector</h2>',
174
+ `<p>Conflicts: ${model.conflicts.length}</p>`,
175
+ "<ul>",
176
+ ...model.conflicts.slice(-20).map(
177
+ (item) => `<li>${escapeHtml(item.collection)}.${escapeHtml(item.field)} \xB7 ${escapeHtml(item.strategy)} \xB7 tier ${item.tier}</li>`
178
+ ),
179
+ "</ul></section>",
180
+ '<section data-panel="operations"><h2>Operation Log</h2>',
181
+ `<p>Operations: ${model.operations.length}</p>`,
182
+ "<ul>",
183
+ ...model.operations.slice(-20).map(
184
+ (item) => `<li>${escapeHtml(item.opType)} ${escapeHtml(item.collection)}/${escapeHtml(item.recordId)} (${escapeHtml(item.operationId)})</li>`
185
+ ),
186
+ "</ul></section>",
187
+ '<section data-panel="network"><h2>Network Status</h2>',
188
+ `<p>Connected: ${model.network.connected ? "yes" : "no"}</p>`,
189
+ `<p>Pending ACKs: ${model.network.pendingAcks}</p>`,
190
+ `<p>Sent ops: ${model.network.sentOps}</p>`,
191
+ `<p>Received ops: ${model.network.receivedOps}</p>`,
192
+ "</section>"
193
+ ].join("");
194
+ }
195
+ function escapeHtml(value) {
196
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
197
+ }
198
+
199
+ export {
200
+ buildPanelModel,
201
+ renderDevtoolsPanel
202
+ };
203
+ //# sourceMappingURL=chunk-4ZQ2RTZM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/panel-state.ts","../src/ui/panel.ts"],"sourcesContent":["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\n\t\t.flatMap((entry) => {\n\t\t\tif (entry.event.type !== 'merge:completed' && entry.event.type !== 'merge:conflict') {\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\tconst trace = entry.event.trace\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tid: entry.id,\n\t\t\t\t\ttimestamp: entry.receivedAt,\n\t\t\t\t\tcollection: trace.operationA.collection,\n\t\t\t\t\tfield: trace.field,\n\t\t\t\t\tstrategy: trace.strategy,\n\t\t\t\t\ttier: trace.tier,\n\t\t\t\t\tinputA: trace.inputA,\n\t\t\t\t\tinputB: trace.inputB,\n\t\t\t\t\toutput: trace.output,\n\t\t\t\t\tconstraintViolated: trace.constraintViolated,\n\t\t\t\t},\n\t\t\t]\n\t\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: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}\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","import type { TimestampedEvent } from '../types'\nimport { buildPanelModel } from './panel-state'\n\nexport function renderDevtoolsPanel(target: HTMLElement, events: readonly TimestampedEvent[]): void {\n\tconst model = buildPanelModel(events)\n\n\ttarget.innerHTML = [\n\t\t'<section data-panel=\"timeline\"><h2>Sync Timeline</h2>',\n\t\t`<p>Total events: ${model.timeline.length}</p>`,\n\t\t'<ul>',\n\t\t...model.timeline.slice(-20).map(\n\t\t\t(item) =>\n\t\t\t\t`<li><span style=\"color:${item.color}\">${item.type}</span> · ${escapeHtml(item.label)}</li>`,\n\t\t),\n\t\t'</ul></section>',\n\t\t'<section data-panel=\"conflicts\"><h2>Conflict Inspector</h2>',\n\t\t`<p>Conflicts: ${model.conflicts.length}</p>`,\n\t\t'<ul>',\n\t\t...model.conflicts\n\t\t\t.slice(-20)\n\t\t\t.map(\n\t\t\t\t(item) =>\n\t\t\t\t\t`<li>${escapeHtml(item.collection)}.${escapeHtml(item.field)} · ${escapeHtml(item.strategy)} · tier ${item.tier}</li>`,\n\t\t\t),\n\t\t'</ul></section>',\n\t\t'<section data-panel=\"operations\"><h2>Operation Log</h2>',\n\t\t`<p>Operations: ${model.operations.length}</p>`,\n\t\t'<ul>',\n\t\t...model.operations.slice(-20).map(\n\t\t\t(item) =>\n\t\t\t\t`<li>${escapeHtml(item.opType)} ${escapeHtml(item.collection)}/${escapeHtml(item.recordId)} (${escapeHtml(item.operationId)})</li>`,\n\t\t),\n\t\t'</ul></section>',\n\t\t'<section data-panel=\"network\"><h2>Network Status</h2>',\n\t\t`<p>Connected: ${model.network.connected ? 'yes' : 'no'}</p>`,\n\t\t`<p>Pending ACKs: ${model.network.pendingAcks}</p>`,\n\t\t`<p>Sent ops: ${model.network.sentOps}</p>`,\n\t\t`<p>Received ops: ${model.network.receivedOps}</p>`,\n\t\t'</section>',\n\t].join('')\n}\n\nfunction escapeHtml(value: string): string {\n\treturn value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;')\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,58 @@
1
+ // src/extension/port-router.ts
2
+ var PortRouter = class {
3
+ panelClients = /* @__PURE__ */ new Map();
4
+ contentClients = /* @__PURE__ */ new Map();
5
+ handleConnection(port) {
6
+ if (port.name === "kora-panel") {
7
+ this.attachPanel(port);
8
+ return;
9
+ }
10
+ if (port.name === "kora-content") {
11
+ this.attachContent(port);
12
+ }
13
+ }
14
+ attachPanel(port) {
15
+ port.onMessage.addListener((message) => {
16
+ if (!isPanelInitMessage(message)) return;
17
+ this.panelClients.set(message.tabId, { tabId: message.tabId, port });
18
+ });
19
+ port.onDisconnect.addListener(() => {
20
+ for (const [tabId, client] of this.panelClients) {
21
+ if (client.port === port) {
22
+ this.panelClients.delete(tabId);
23
+ }
24
+ }
25
+ });
26
+ }
27
+ attachContent(port) {
28
+ const tabId = port.sender?.tab?.id;
29
+ if (typeof tabId !== "number") {
30
+ return;
31
+ }
32
+ this.contentClients.set(tabId, port);
33
+ port.onMessage.addListener((message) => {
34
+ if (!isContentEventMessage(message)) return;
35
+ const panel = this.panelClients.get(tabId);
36
+ if (!panel) return;
37
+ panel.port.postMessage({ type: "kora-event", payload: message.payload });
38
+ });
39
+ port.onDisconnect.addListener(() => {
40
+ this.contentClients.delete(tabId);
41
+ });
42
+ }
43
+ };
44
+ function isPanelInitMessage(value) {
45
+ if (typeof value !== "object" || value === null) return false;
46
+ const record = value;
47
+ return record.type === "panel-init" && typeof record.tabId === "number";
48
+ }
49
+ function isContentEventMessage(value) {
50
+ if (typeof value !== "object" || value === null) return false;
51
+ const record = value;
52
+ return record.type === "kora-event" && "payload" in record;
53
+ }
54
+
55
+ export {
56
+ PortRouter
57
+ };
58
+ //# sourceMappingURL=chunk-JH2X4T4Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/extension/port-router.ts"],"sourcesContent":["export interface ExtensionPort {\n\tname: string\n\tonMessage: {\n\t\taddListener(callback: (message: unknown) => void): void\n\t}\n\tonDisconnect: {\n\t\taddListener(callback: () => void): void\n\t}\n\tpostMessage(message: unknown): void\n\tsender?: { tab?: { id?: number } }\n}\n\ninterface PanelClient {\n\tport: ExtensionPort\n\ttabId: number\n}\n\n/**\n * Routes content-script events to the matching DevTools panel by tab.\n */\nexport class PortRouter {\n\tprivate readonly panelClients = new Map<number, PanelClient>()\n\tprivate readonly contentClients = new Map<number, ExtensionPort>()\n\n\thandleConnection(port: ExtensionPort): void {\n\t\tif (port.name === 'kora-panel') {\n\t\t\tthis.attachPanel(port)\n\t\t\treturn\n\t\t}\n\n\t\tif (port.name === 'kora-content') {\n\t\t\tthis.attachContent(port)\n\t\t}\n\t}\n\n\tprivate attachPanel(port: ExtensionPort): void {\n\t\tport.onMessage.addListener((message) => {\n\t\t\tif (!isPanelInitMessage(message)) return\n\n\t\t\tthis.panelClients.set(message.tabId, { tabId: message.tabId, port })\n\t\t})\n\n\t\tport.onDisconnect.addListener(() => {\n\t\t\tfor (const [tabId, client] of this.panelClients) {\n\t\t\t\tif (client.port === port) {\n\t\t\t\t\tthis.panelClients.delete(tabId)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate attachContent(port: ExtensionPort): void {\n\t\tconst tabId = port.sender?.tab?.id\n\t\tif (typeof tabId !== 'number') {\n\t\t\treturn\n\t\t}\n\n\t\tthis.contentClients.set(tabId, port)\n\n\t\tport.onMessage.addListener((message) => {\n\t\t\tif (!isContentEventMessage(message)) return\n\n\t\t\tconst panel = this.panelClients.get(tabId)\n\t\t\tif (!panel) return\n\n\t\t\tpanel.port.postMessage({ type: 'kora-event', payload: message.payload })\n\t\t})\n\n\t\tport.onDisconnect.addListener(() => {\n\t\t\tthis.contentClients.delete(tabId)\n\t\t})\n\t}\n}\n\nfunction isPanelInitMessage(value: unknown): value is { type: 'panel-init'; tabId: number } {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst record = value as Record<string, unknown>\n\treturn record.type === 'panel-init' && typeof record.tabId === 'number'\n}\n\nfunction isContentEventMessage(value: unknown): value is { type: 'kora-event'; payload: unknown } {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst record = value as Record<string, unknown>\n\treturn record.type === 'kora-event' && 'payload' in record\n}\n"],"mappings":";AAoBO,IAAM,aAAN,MAAiB;AAAA,EACN,eAAe,oBAAI,IAAyB;AAAA,EAC5C,iBAAiB,oBAAI,IAA2B;AAAA,EAEjE,iBAAiB,MAA2B;AAC3C,QAAI,KAAK,SAAS,cAAc;AAC/B,WAAK,YAAY,IAAI;AACrB;AAAA,IACD;AAEA,QAAI,KAAK,SAAS,gBAAgB;AACjC,WAAK,cAAc,IAAI;AAAA,IACxB;AAAA,EACD;AAAA,EAEQ,YAAY,MAA2B;AAC9C,SAAK,UAAU,YAAY,CAAC,YAAY;AACvC,UAAI,CAAC,mBAAmB,OAAO,EAAG;AAElC,WAAK,aAAa,IAAI,QAAQ,OAAO,EAAE,OAAO,QAAQ,OAAO,KAAK,CAAC;AAAA,IACpE,CAAC;AAED,SAAK,aAAa,YAAY,MAAM;AACnC,iBAAW,CAAC,OAAO,MAAM,KAAK,KAAK,cAAc;AAChD,YAAI,OAAO,SAAS,MAAM;AACzB,eAAK,aAAa,OAAO,KAAK;AAAA,QAC/B;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAEQ,cAAc,MAA2B;AAChD,UAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,QAAI,OAAO,UAAU,UAAU;AAC9B;AAAA,IACD;AAEA,SAAK,eAAe,IAAI,OAAO,IAAI;AAEnC,SAAK,UAAU,YAAY,CAAC,YAAY;AACvC,UAAI,CAAC,sBAAsB,OAAO,EAAG;AAErC,YAAM,QAAQ,KAAK,aAAa,IAAI,KAAK;AACzC,UAAI,CAAC,MAAO;AAEZ,YAAM,KAAK,YAAY,EAAE,MAAM,cAAc,SAAS,QAAQ,QAAQ,CAAC;AAAA,IACxE,CAAC;AAED,SAAK,aAAa,YAAY,MAAM;AACnC,WAAK,eAAe,OAAO,KAAK;AAAA,IACjC,CAAC;AAAA,EACF;AACD;AAEA,SAAS,mBAAmB,OAAgE;AAC3F,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,SAAS;AACf,SAAO,OAAO,SAAS,gBAAgB,OAAO,OAAO,UAAU;AAChE;AAEA,SAAS,sBAAsB,OAAmE;AACjG,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,SAAS;AACf,SAAO,OAAO,SAAS,gBAAgB,aAAa;AACrD;","names":[]}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ // src/extension/port-router.ts
4
+ var PortRouter = class {
5
+ panelClients = /* @__PURE__ */ new Map();
6
+ contentClients = /* @__PURE__ */ new Map();
7
+ handleConnection(port) {
8
+ if (port.name === "kora-panel") {
9
+ this.attachPanel(port);
10
+ return;
11
+ }
12
+ if (port.name === "kora-content") {
13
+ this.attachContent(port);
14
+ }
15
+ }
16
+ attachPanel(port) {
17
+ port.onMessage.addListener((message) => {
18
+ if (!isPanelInitMessage(message)) return;
19
+ this.panelClients.set(message.tabId, { tabId: message.tabId, port });
20
+ });
21
+ port.onDisconnect.addListener(() => {
22
+ for (const [tabId, client] of this.panelClients) {
23
+ if (client.port === port) {
24
+ this.panelClients.delete(tabId);
25
+ }
26
+ }
27
+ });
28
+ }
29
+ attachContent(port) {
30
+ const tabId = port.sender?.tab?.id;
31
+ if (typeof tabId !== "number") {
32
+ return;
33
+ }
34
+ this.contentClients.set(tabId, port);
35
+ port.onMessage.addListener((message) => {
36
+ if (!isContentEventMessage(message)) return;
37
+ const panel = this.panelClients.get(tabId);
38
+ if (!panel) return;
39
+ panel.port.postMessage({ type: "kora-event", payload: message.payload });
40
+ });
41
+ port.onDisconnect.addListener(() => {
42
+ this.contentClients.delete(tabId);
43
+ });
44
+ }
45
+ };
46
+ function isPanelInitMessage(value) {
47
+ if (typeof value !== "object" || value === null) return false;
48
+ const record = value;
49
+ return record.type === "panel-init" && typeof record.tabId === "number";
50
+ }
51
+ function isContentEventMessage(value) {
52
+ if (typeof value !== "object" || value === null) return false;
53
+ const record = value;
54
+ return record.type === "kora-event" && "payload" in record;
55
+ }
56
+
57
+ // src/extension/background.ts
58
+ var runtime = globalThis.chrome?.runtime;
59
+ if (runtime?.onConnect) {
60
+ const router = new PortRouter();
61
+ runtime.onConnect.addListener((port) => {
62
+ router.handleConnection(port);
63
+ });
64
+ }
65
+ //# sourceMappingURL=background.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/extension/port-router.ts","../../src/extension/background.ts"],"sourcesContent":["export interface ExtensionPort {\n\tname: string\n\tonMessage: {\n\t\taddListener(callback: (message: unknown) => void): void\n\t}\n\tonDisconnect: {\n\t\taddListener(callback: () => void): void\n\t}\n\tpostMessage(message: unknown): void\n\tsender?: { tab?: { id?: number } }\n}\n\ninterface PanelClient {\n\tport: ExtensionPort\n\ttabId: number\n}\n\n/**\n * Routes content-script events to the matching DevTools panel by tab.\n */\nexport class PortRouter {\n\tprivate readonly panelClients = new Map<number, PanelClient>()\n\tprivate readonly contentClients = new Map<number, ExtensionPort>()\n\n\thandleConnection(port: ExtensionPort): void {\n\t\tif (port.name === 'kora-panel') {\n\t\t\tthis.attachPanel(port)\n\t\t\treturn\n\t\t}\n\n\t\tif (port.name === 'kora-content') {\n\t\t\tthis.attachContent(port)\n\t\t}\n\t}\n\n\tprivate attachPanel(port: ExtensionPort): void {\n\t\tport.onMessage.addListener((message) => {\n\t\t\tif (!isPanelInitMessage(message)) return\n\n\t\t\tthis.panelClients.set(message.tabId, { tabId: message.tabId, port })\n\t\t})\n\n\t\tport.onDisconnect.addListener(() => {\n\t\t\tfor (const [tabId, client] of this.panelClients) {\n\t\t\t\tif (client.port === port) {\n\t\t\t\t\tthis.panelClients.delete(tabId)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate attachContent(port: ExtensionPort): void {\n\t\tconst tabId = port.sender?.tab?.id\n\t\tif (typeof tabId !== 'number') {\n\t\t\treturn\n\t\t}\n\n\t\tthis.contentClients.set(tabId, port)\n\n\t\tport.onMessage.addListener((message) => {\n\t\t\tif (!isContentEventMessage(message)) return\n\n\t\t\tconst panel = this.panelClients.get(tabId)\n\t\t\tif (!panel) return\n\n\t\t\tpanel.port.postMessage({ type: 'kora-event', payload: message.payload })\n\t\t})\n\n\t\tport.onDisconnect.addListener(() => {\n\t\t\tthis.contentClients.delete(tabId)\n\t\t})\n\t}\n}\n\nfunction isPanelInitMessage(value: unknown): value is { type: 'panel-init'; tabId: number } {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst record = value as Record<string, unknown>\n\treturn record.type === 'panel-init' && typeof record.tabId === 'number'\n}\n\nfunction isContentEventMessage(value: unknown): value is { type: 'kora-event'; payload: unknown } {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst record = value as Record<string, unknown>\n\treturn record.type === 'kora-event' && 'payload' in record\n}\n","import { PortRouter, type ExtensionPort } from './port-router'\n\ninterface RuntimeLike {\n\tonConnect?: {\n\t\taddListener(callback: (port: ExtensionPort) => void): void\n\t}\n}\n\nconst runtime = (globalThis as { chrome?: { runtime?: RuntimeLike } }).chrome?.runtime\n\nif (runtime?.onConnect) {\n\tconst router = new PortRouter()\n\truntime.onConnect.addListener((port) => {\n\t\trouter.handleConnection(port)\n\t})\n}\n"],"mappings":";;;AAoBO,IAAM,aAAN,MAAiB;AAAA,EACN,eAAe,oBAAI,IAAyB;AAAA,EAC5C,iBAAiB,oBAAI,IAA2B;AAAA,EAEjE,iBAAiB,MAA2B;AAC3C,QAAI,KAAK,SAAS,cAAc;AAC/B,WAAK,YAAY,IAAI;AACrB;AAAA,IACD;AAEA,QAAI,KAAK,SAAS,gBAAgB;AACjC,WAAK,cAAc,IAAI;AAAA,IACxB;AAAA,EACD;AAAA,EAEQ,YAAY,MAA2B;AAC9C,SAAK,UAAU,YAAY,CAAC,YAAY;AACvC,UAAI,CAAC,mBAAmB,OAAO,EAAG;AAElC,WAAK,aAAa,IAAI,QAAQ,OAAO,EAAE,OAAO,QAAQ,OAAO,KAAK,CAAC;AAAA,IACpE,CAAC;AAED,SAAK,aAAa,YAAY,MAAM;AACnC,iBAAW,CAAC,OAAO,MAAM,KAAK,KAAK,cAAc;AAChD,YAAI,OAAO,SAAS,MAAM;AACzB,eAAK,aAAa,OAAO,KAAK;AAAA,QAC/B;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAEQ,cAAc,MAA2B;AAChD,UAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,QAAI,OAAO,UAAU,UAAU;AAC9B;AAAA,IACD;AAEA,SAAK,eAAe,IAAI,OAAO,IAAI;AAEnC,SAAK,UAAU,YAAY,CAAC,YAAY;AACvC,UAAI,CAAC,sBAAsB,OAAO,EAAG;AAErC,YAAM,QAAQ,KAAK,aAAa,IAAI,KAAK;AACzC,UAAI,CAAC,MAAO;AAEZ,YAAM,KAAK,YAAY,EAAE,MAAM,cAAc,SAAS,QAAQ,QAAQ,CAAC;AAAA,IACxE,CAAC;AAED,SAAK,aAAa,YAAY,MAAM;AACnC,WAAK,eAAe,OAAO,KAAK;AAAA,IACjC,CAAC;AAAA,EACF;AACD;AAEA,SAAS,mBAAmB,OAAgE;AAC3F,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,SAAS;AACf,SAAO,OAAO,SAAS,gBAAgB,OAAO,OAAO,UAAU;AAChE;AAEA,SAAS,sBAAsB,OAAmE;AACjG,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,SAAS;AACf,SAAO,OAAO,SAAS,gBAAgB,aAAa;AACrD;;;AC5EA,IAAM,UAAW,WAAsD,QAAQ;AAE/E,IAAI,SAAS,WAAW;AACvB,QAAM,SAAS,IAAI,WAAW;AAC9B,UAAQ,UAAU,YAAY,CAAC,SAAS;AACvC,WAAO,iBAAiB,IAAI;AAAA,EAC7B,CAAC;AACF;","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,13 @@
1
+ import {
2
+ PortRouter
3
+ } from "../chunk-JH2X4T4Z.js";
4
+
5
+ // src/extension/background.ts
6
+ var runtime = globalThis.chrome?.runtime;
7
+ if (runtime?.onConnect) {
8
+ const router = new PortRouter();
9
+ runtime.onConnect.addListener((port) => {
10
+ router.handleConnection(port);
11
+ });
12
+ }
13
+ //# sourceMappingURL=background.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/extension/background.ts"],"sourcesContent":["import { PortRouter, type ExtensionPort } from './port-router'\n\ninterface RuntimeLike {\n\tonConnect?: {\n\t\taddListener(callback: (port: ExtensionPort) => void): void\n\t}\n}\n\nconst runtime = (globalThis as { chrome?: { runtime?: RuntimeLike } }).chrome?.runtime\n\nif (runtime?.onConnect) {\n\tconst router = new PortRouter()\n\truntime.onConnect.addListener((port) => {\n\t\trouter.handleConnection(port)\n\t})\n}\n"],"mappings":";;;;;AAQA,IAAM,UAAW,WAAsD,QAAQ;AAE/E,IAAI,SAAS,WAAW;AACvB,QAAM,SAAS,IAAI,WAAW;AAC9B,UAAQ,UAAU,YAAY,CAAC,SAAS;AACvC,WAAO,iBAAiB,IAAI;AAAA,EAC7B,CAAC;AACF;","names":[]}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ // src/extension/content-script.ts
4
+ var runtime = globalThis.chrome?.runtime;
5
+ if (runtime) {
6
+ const port = runtime.connect({ name: "kora-content" });
7
+ window.addEventListener("message", (event) => {
8
+ const data = event.data;
9
+ if (!data || data.source !== "kora-devtools" || !data.payload) {
10
+ return;
11
+ }
12
+ port.postMessage({
13
+ type: "kora-event",
14
+ payload: data.payload
15
+ });
16
+ });
17
+ }
18
+ //# sourceMappingURL=content-script.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/extension/content-script.ts"],"sourcesContent":["import type { TimestampedEvent } from '../types'\n\ninterface RuntimePort {\n\tpostMessage(message: unknown): void\n}\n\ninterface RuntimeLike {\n\tconnect(info: { name: string }): RuntimePort\n}\n\nconst runtime = (globalThis as { chrome?: { runtime?: RuntimeLike } }).chrome?.runtime\n\nif (runtime) {\n\tconst port = runtime.connect({ name: 'kora-content' })\n\n\twindow.addEventListener('message', (event: MessageEvent) => {\n\t\tconst data = event.data as { source?: string; payload?: TimestampedEvent } | undefined\n\t\tif (!data || data.source !== 'kora-devtools' || !data.payload) {\n\t\t\treturn\n\t\t}\n\n\t\tport.postMessage({\n\t\t\ttype: 'kora-event',\n\t\t\tpayload: data.payload,\n\t\t})\n\t})\n}\n"],"mappings":";;;AAUA,IAAM,UAAW,WAAsD,QAAQ;AAE/E,IAAI,SAAS;AACZ,QAAM,OAAO,QAAQ,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD,SAAO,iBAAiB,WAAW,CAAC,UAAwB;AAC3D,UAAM,OAAO,MAAM;AACnB,QAAI,CAAC,QAAQ,KAAK,WAAW,mBAAmB,CAAC,KAAK,SAAS;AAC9D;AAAA,IACD;AAEA,SAAK,YAAY;AAAA,MAChB,MAAM;AAAA,MACN,SAAS,KAAK;AAAA,IACf,CAAC;AAAA,EACF,CAAC;AACF;","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,16 @@
1
+ // src/extension/content-script.ts
2
+ var runtime = globalThis.chrome?.runtime;
3
+ if (runtime) {
4
+ const port = runtime.connect({ name: "kora-content" });
5
+ window.addEventListener("message", (event) => {
6
+ const data = event.data;
7
+ if (!data || data.source !== "kora-devtools" || !data.payload) {
8
+ return;
9
+ }
10
+ port.postMessage({
11
+ type: "kora-event",
12
+ payload: data.payload
13
+ });
14
+ });
15
+ }
16
+ //# sourceMappingURL=content-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/extension/content-script.ts"],"sourcesContent":["import type { TimestampedEvent } from '../types'\n\ninterface RuntimePort {\n\tpostMessage(message: unknown): void\n}\n\ninterface RuntimeLike {\n\tconnect(info: { name: string }): RuntimePort\n}\n\nconst runtime = (globalThis as { chrome?: { runtime?: RuntimeLike } }).chrome?.runtime\n\nif (runtime) {\n\tconst port = runtime.connect({ name: 'kora-content' })\n\n\twindow.addEventListener('message', (event: MessageEvent) => {\n\t\tconst data = event.data as { source?: string; payload?: TimestampedEvent } | undefined\n\t\tif (!data || data.source !== 'kora-devtools' || !data.payload) {\n\t\t\treturn\n\t\t}\n\n\t\tport.postMessage({\n\t\t\ttype: 'kora-event',\n\t\t\tpayload: data.payload,\n\t\t})\n\t})\n}\n"],"mappings":";AAUA,IAAM,UAAW,WAAsD,QAAQ;AAE/E,IAAI,SAAS;AACZ,QAAM,OAAO,QAAQ,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD,SAAO,iBAAiB,WAAW,CAAC,UAAwB;AAC3D,UAAM,OAAO,MAAM;AACnB,QAAI,CAAC,QAAQ,KAAK,WAAW,mBAAmB,CAAC,KAAK,SAAS;AAC9D;AAAA,IACD;AAEA,SAAK,YAAY;AAAA,MAChB,MAAM;AAAA,MACN,SAAS,KAAK;AAAA,IACf,CAAC;AAAA,EACF,CAAC;AACF;","names":[]}
@@ -0,0 +1,45 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Kora DevTools</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
11
+ background: #0f172a;
12
+ color: #e2e8f0;
13
+ }
14
+ main {
15
+ display: grid;
16
+ grid-template-columns: 1fr 1fr;
17
+ gap: 12px;
18
+ padding: 12px;
19
+ }
20
+ section {
21
+ border: 1px solid #1e293b;
22
+ background: #111827;
23
+ border-radius: 8px;
24
+ padding: 10px;
25
+ }
26
+ h2 {
27
+ margin: 0 0 8px;
28
+ font-size: 14px;
29
+ }
30
+ p, li {
31
+ font-size: 12px;
32
+ }
33
+ ul {
34
+ margin: 6px 0 0;
35
+ padding-left: 18px;
36
+ max-height: 200px;
37
+ overflow: auto;
38
+ }
39
+ </style>
40
+ </head>
41
+ <body>
42
+ <main id="kora-devtools-root"></main>
43
+ <script type="module" src="./panel.js"></script>
44
+ </body>
45
+ </html>
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+
3
+ // src/extension/devtools.ts
4
+ var devtools = globalThis.chrome?.devtools;
5
+ if (devtools?.panels) {
6
+ devtools.panels.create("Kora", "", "devtools-page.html", () => {
7
+ });
8
+ }
9
+ //# sourceMappingURL=devtools.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/extension/devtools.ts"],"sourcesContent":["interface DevtoolsLike {\n\tpanels: {\n\t\tcreate(\n\t\t\ttitle: string,\n\t\t\ticonPath: string,\n\t\t\tpagePath: string,\n\t\t\tcallback: () => void,\n\t\t): void\n\t}\n}\n\nconst devtools = (globalThis as { chrome?: { devtools?: DevtoolsLike } }).chrome?.devtools\n\nif (devtools?.panels) {\n\tdevtools.panels.create('Kora', '', 'devtools-page.html', () => {})\n}\n"],"mappings":";;;AAWA,IAAM,WAAY,WAAwD,QAAQ;AAElF,IAAI,UAAU,QAAQ;AACrB,WAAS,OAAO,OAAO,QAAQ,IAAI,sBAAsB,MAAM;AAAA,EAAC,CAAC;AAClE;","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,7 @@
1
+ // src/extension/devtools.ts
2
+ var devtools = globalThis.chrome?.devtools;
3
+ if (devtools?.panels) {
4
+ devtools.panels.create("Kora", "", "devtools-page.html", () => {
5
+ });
6
+ }
7
+ //# sourceMappingURL=devtools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/extension/devtools.ts"],"sourcesContent":["interface DevtoolsLike {\n\tpanels: {\n\t\tcreate(\n\t\t\ttitle: string,\n\t\t\ticonPath: string,\n\t\t\tpagePath: string,\n\t\t\tcallback: () => void,\n\t\t): void\n\t}\n}\n\nconst devtools = (globalThis as { chrome?: { devtools?: DevtoolsLike } }).chrome?.devtools\n\nif (devtools?.panels) {\n\tdevtools.panels.create('Kora', '', 'devtools-page.html', () => {})\n}\n"],"mappings":";AAWA,IAAM,WAAY,WAAwD,QAAQ;AAElF,IAAI,UAAU,QAAQ;AACrB,WAAS,OAAO,OAAO,QAAQ,IAAI,sBAAsB,MAAM;AAAA,EAAC,CAAC;AAClE;","names":[]}
@@ -0,0 +1,20 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "name": "Kora DevTools",
4
+ "version": "0.0.0",
5
+ "description": "DevTools panel for visualizing Kora sync, merges, and operations.",
6
+ "permissions": [],
7
+ "host_permissions": ["<all_urls>"],
8
+ "background": {
9
+ "service_worker": "background.js",
10
+ "type": "module"
11
+ },
12
+ "devtools_page": "devtools.js",
13
+ "content_scripts": [
14
+ {
15
+ "matches": ["<all_urls>"],
16
+ "js": ["content-script.js"],
17
+ "run_at": "document_start"
18
+ }
19
+ ]
20
+ }