@hardkas/react 0.5.5-alpha → 0.6.1-alpha

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 (3) hide show
  1. package/dist/index.d.ts +180 -21
  2. package/dist/index.js +614 -90
  3. package/package.json +5 -6
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ interface HardKasReactConfig {
25
25
  readonly devServerUrl?: string;
26
26
  }
27
27
  type SSEStatus = "connecting" | "connected" | "disconnected" | "reconnecting" | "failed";
28
+ type ProjectionStatus = "synced" | "stale" | "syncing";
28
29
  interface RuntimeEvent {
29
30
  type: string;
30
31
  payload?: any;
@@ -36,6 +37,10 @@ interface HardKasContextValue {
36
37
  readonly igraClient: PublicClient;
37
38
  readonly queryClient: QueryClient;
38
39
  readonly sseStatus: SSEStatus;
40
+ readonly projectionStatus: ProjectionStatus;
41
+ readonly generationId: string | null;
42
+ readonly lastSyncedAt: string | null;
43
+ readonly apiFetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
39
44
  readonly lastEvent: RuntimeEvent | null;
40
45
  readonly subscribe: (callback: EventCallback) => () => void;
41
46
  readonly providers: EIP6963ProviderDetail[];
@@ -123,26 +128,25 @@ declare function useIgraBalance(options?: {
123
128
  refetchInterval?: number;
124
129
  }): _tanstack_react_query.UseQueryResult<bigint, Error>;
125
130
 
126
- /**
127
- * WARNING: ALPHA-ONLY LOCAL-RUNTIME COUPLING
128
- *
129
- * This file directly imports `@hardkas/bridge-local` to perform local bridge simulation.
130
- * This puts local simulation logic and heavy CPU simulation directly into the React package,
131
- * meaning it is NOT browser-production safe without polyfills or is intended exclusively
132
- * for the local developer preview context.
133
- *
134
- * TODO: In a future release, decouple this simulation code from the core `@hardkas/react` library.
135
- * The hooks should ideally make dev-server REST/SSE HTTP calls, or be extracted into a separate
136
- * package `@hardkas/react-local` to maintain clean, production-safe boundaries.
137
- */
138
- declare function useBridgeLocalPlan(options: {
139
- amountSompi: bigint;
140
- toIgra?: string;
141
- } | null): _tanstack_react_query.UseQueryResult<any, Error>;
142
- declare function useBridgeLocalSimulation(): _tanstack_react_query.UseMutationResult<any, Error, {
143
- payloadBase: any;
144
- prefix: string;
145
- }, unknown>;
131
+ interface HardKasEventInfo {
132
+ eventId: string;
133
+ kind: string;
134
+ domain: string;
135
+ workflowId: string;
136
+ correlationId: string;
137
+ causationId: string | null;
138
+ txId: string | null;
139
+ artifactId: string | null;
140
+ networkId: string;
141
+ timestamp: string | null;
142
+ payload: any;
143
+ }
144
+ interface UseEventsResponse {
145
+ events: HardKasEventInfo[];
146
+ observabilityDrift?: boolean;
147
+ reason?: string;
148
+ }
149
+ declare function useEvents(kind?: string, txId?: string): _tanstack_react_query.UseQueryResult<UseEventsResponse, Error>;
146
150
 
147
151
  type CcipRequestReturnType = Hex;
148
152
 
@@ -4748,4 +4752,159 @@ declare function useIgraWriteContract(): _tanstack_react_query.UseMutationResult
4748
4752
  }, unknown>;
4749
4753
  declare function useIgraWaitForReceipt(): _tanstack_react_query.UseMutationResult<viem.TransactionReceipt, Error, `0x${string}`, unknown>;
4750
4754
 
4751
- export { type EventCallback, type HardKasContextValue, HardKasProvider, type HardKasReactConfig, type HealthInfo, type KasWareLocalState, type KasWareSessionMatch, type MetaMaskLocalState, type RuntimeEvent, type SSEStatus, type SandboxConnection, type SessionInfo, useBridgeLocalPlan, useBridgeLocalSimulation, useConnectKasWareLocal, useCreateSandboxSession, useDisconnectSandboxSession, useHardKas, useHardKasHealth, useHardKasSession, useIgraAccount, useIgraBalance, useIgraInjectedAccount, useIgraReadContract, useIgraWaitForReceipt, useIgraWallet, useIgraWriteContract, useKasWareLocal, useKasWareSessionMatch, useKaspaBalance, useKaspaWallet, useLocalIgraWalletClient, useMetaMaskLocal, usePairSandboxSession, useSandboxSessions, useSwitchToLocalIgra };
4755
+ interface OverviewStats {
4756
+ projectName: string;
4757
+ network: string;
4758
+ workspaceRoot: string;
4759
+ artifactDir: string;
4760
+ queryStorePath: string;
4761
+ runtimeState: "EMPTY" | "ACTIVE" | "PENDING" | "DEGRADED" | "CORRUPTED" | "VERIFIED";
4762
+ runtimeReason: string;
4763
+ recommendedAction: string;
4764
+ guarantees: {
4765
+ artifactIntegrity: "available" | "failed" | "not_checked";
4766
+ localReplay: "verified" | "failed" | "not_checked";
4767
+ consensusValidated: boolean;
4768
+ networkFinality: boolean;
4769
+ };
4770
+ counts: {
4771
+ artifacts: number;
4772
+ transactions: number;
4773
+ events: number;
4774
+ replays: number;
4775
+ pendingReplays: number;
4776
+ corruptedArtifacts: number;
4777
+ degradedProjections: number;
4778
+ };
4779
+ }
4780
+ declare function useOverview(): _tanstack_react_query.UseQueryResult<OverviewStats | null, Error>;
4781
+
4782
+ interface HardKasAccountInfo {
4783
+ name: string;
4784
+ kind: string;
4785
+ address: string;
4786
+ balance: string;
4787
+ privateKeyEnv?: string;
4788
+ walletId?: string;
4789
+ }
4790
+ interface HardKasAccountsResponse {
4791
+ accounts: HardKasAccountInfo[];
4792
+ provenance?: {
4793
+ authority: string;
4794
+ derivedFrom?: string;
4795
+ originalPath?: string;
4796
+ integrity: "verified" | "corrupted" | "invalid_json" | "unknown";
4797
+ replayScope: "local-only" | "global" | "unknown";
4798
+ consensusValidated: boolean;
4799
+ };
4800
+ }
4801
+ declare function useAccounts(): _tanstack_react_query.UseQueryResult<HardKasAccountsResponse, Error>;
4802
+
4803
+ interface TransactionSummary {
4804
+ id: string;
4805
+ txId?: string;
4806
+ planId?: string;
4807
+ signedId?: string;
4808
+ status: string;
4809
+ from: string;
4810
+ to: string;
4811
+ amountSompi: string;
4812
+ amount: string;
4813
+ feeSompi?: string;
4814
+ timestamp: string;
4815
+ mode: string;
4816
+ networkId: string;
4817
+ layer: "L1" | "L2";
4818
+ }
4819
+ interface LineageNode {
4820
+ id: string;
4821
+ label: string;
4822
+ schema: string;
4823
+ }
4824
+ interface LineageEdge {
4825
+ from: string;
4826
+ to: string;
4827
+ label: string;
4828
+ }
4829
+ interface TransactionDetail {
4830
+ id: string;
4831
+ plan: any;
4832
+ signed: any;
4833
+ receipt: any;
4834
+ trace: any;
4835
+ replay: any;
4836
+ lineage: {
4837
+ nodes: LineageNode[];
4838
+ edges: LineageEdge[];
4839
+ };
4840
+ }
4841
+ declare function useTransactions(): _tanstack_react_query.UseQueryResult<TransactionSummary[], Error>;
4842
+ declare function useTransaction(id: string): _tanstack_react_query.UseQueryResult<TransactionDetail | null, Error>;
4843
+
4844
+ interface ArtifactSummary {
4845
+ artifactId: string;
4846
+ contentHash: string;
4847
+ schema: string;
4848
+ version: string;
4849
+ kind: string;
4850
+ mode: string;
4851
+ networkId: string;
4852
+ txId?: string;
4853
+ createdAt: string;
4854
+ path: string;
4855
+ }
4856
+ declare function useArtifacts(schemaFilter?: string): _tanstack_react_query.UseQueryResult<ArtifactSummary[], Error>;
4857
+ declare function useArtifact(id: string): _tanstack_react_query.UseQueryResult<any, Error>;
4858
+
4859
+ interface ReplaySummary {
4860
+ artifactId: string;
4861
+ txId: string;
4862
+ planOk: boolean;
4863
+ receiptOk: boolean;
4864
+ invariantsOk: boolean;
4865
+ ok: boolean;
4866
+ checks: {
4867
+ workflowDeterministic: string;
4868
+ consensusValidation: string;
4869
+ l2BridgeCorrectness: string;
4870
+ };
4871
+ errors: string[];
4872
+ divergencesCount: number;
4873
+ createdAt: string;
4874
+ }
4875
+ interface UseReplayStatusResponse {
4876
+ replays: ReplaySummary[];
4877
+ pendingReplays: any[];
4878
+ pendingReplay: boolean;
4879
+ reason?: string;
4880
+ }
4881
+ declare function useReplayStatus(): _tanstack_react_query.UseQueryResult<UseReplayStatusResponse, Error>;
4882
+ declare function useReplayDetail(txId: string): _tanstack_react_query.UseQueryResult<any, Error>;
4883
+
4884
+ interface DeploymentSummary {
4885
+ artifactId: string;
4886
+ label: string;
4887
+ networkId: string;
4888
+ status: string;
4889
+ txId?: string;
4890
+ deployedAt: string;
4891
+ deployedAddresses: string[];
4892
+ deployer?: string;
4893
+ notes?: string;
4894
+ }
4895
+ declare function useDeployments(): _tanstack_react_query.UseQueryResult<DeploymentSummary[], Error>;
4896
+
4897
+ interface ActivityEvent {
4898
+ eventId: string;
4899
+ kind: string;
4900
+ domain: string;
4901
+ workflowId: string;
4902
+ txId?: string;
4903
+ artifactId?: string;
4904
+ networkId: string;
4905
+ timestamp: string;
4906
+ payload: any;
4907
+ }
4908
+ declare function useActivity(): _tanstack_react_query.UseQueryResult<ActivityEvent[], Error>;
4909
+
4910
+ export { type ActivityEvent, type ArtifactSummary, type DeploymentSummary, type EventCallback, type HardKasAccountInfo, type HardKasAccountsResponse, type HardKasContextValue, type HardKasEventInfo, HardKasProvider, type HardKasReactConfig, type HealthInfo, type KasWareLocalState, type KasWareSessionMatch, type LineageEdge, type LineageNode, type MetaMaskLocalState, type OverviewStats, type ReplaySummary, type RuntimeEvent, type SSEStatus, type SandboxConnection, type SessionInfo, type TransactionDetail, type TransactionSummary, type UseEventsResponse, type UseReplayStatusResponse, useAccounts, useActivity, useArtifact, useArtifacts, useConnectKasWareLocal, useCreateSandboxSession, useDeployments, useDisconnectSandboxSession, useEvents, useHardKas, useHardKasHealth, useHardKasSession, useIgraAccount, useIgraBalance, useIgraInjectedAccount, useIgraReadContract, useIgraWaitForReceipt, useIgraWallet, useIgraWriteContract, useKasWareLocal, useKasWareSessionMatch, useKaspaBalance, useKaspaWallet, useLocalIgraWalletClient, useMetaMaskLocal, useOverview, usePairSandboxSession, useReplayDetail, useReplayStatus, useSandboxSessions, useSwitchToLocalIgra, useTransaction, useTransactions };
package/dist/index.js CHANGED
@@ -2,11 +2,14 @@
2
2
  import React, { createContext, useContext, useMemo } from "react";
3
3
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
4
  import { createPublicClient, http } from "viem";
5
- import { jsx } from "react/jsx-runtime";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  var HardKasContext = createContext(void 0);
7
7
  function HardKasProvider({ config, children, queryClient: externalQueryClient }) {
8
8
  const queryClient = useMemo(() => externalQueryClient ?? new QueryClient(), [externalQueryClient]);
9
9
  const [sseStatus, setSseStatus] = React.useState("disconnected");
10
+ const [projectionStatus, setProjectionStatus] = React.useState("synced");
11
+ const [generationId, setGenerationId] = React.useState(null);
12
+ const [lastSyncedAt, setLastSyncedAt] = React.useState(null);
10
13
  const [lastEvent, setLastEvent] = React.useState(null);
11
14
  const listeners = React.useRef(/* @__PURE__ */ new Set());
12
15
  const eventSource = React.useRef(null);
@@ -16,10 +19,58 @@ function HardKasProvider({ config, children, queryClient: externalQueryClient })
16
19
  const [activeProvider, setActiveProvider] = React.useState(null);
17
20
  const [walletAddress, setWalletAddress] = React.useState(null);
18
21
  const [walletChainId, setWalletChainId] = React.useState(null);
22
+ const devToken = React.useMemo(() => {
23
+ if (typeof window !== "undefined") {
24
+ if (window.__HARDKAS_DEV_TOKEN__) {
25
+ return window.__HARDKAS_DEV_TOKEN__;
26
+ }
27
+ const params = new URLSearchParams(window.location.search);
28
+ return params.get("token") || "";
29
+ }
30
+ return "";
31
+ }, []);
32
+ const [tokenMissing, setTokenMissing] = React.useState(false);
33
+ React.useEffect(() => {
34
+ if (typeof window !== "undefined") {
35
+ const isTest = typeof globalThis.vi !== "undefined" || window.__MOCK_SSE__ || process.env.NODE_ENV === "test";
36
+ const isDashboard = window.location.pathname.startsWith("/") || window.location.port === "7420" || window.location.port === "5173";
37
+ if (isDashboard && !isTest && !devToken) {
38
+ setTokenMissing(true);
39
+ }
40
+ }
41
+ }, [devToken]);
19
42
  const subscribe = React.useCallback((callback) => {
20
43
  listeners.current.add(callback);
21
44
  return () => listeners.current.delete(callback);
22
45
  }, []);
46
+ const apiFetch = React.useCallback(async (input, init) => {
47
+ const headers = new Headers(init?.headers || {});
48
+ if (devToken) {
49
+ headers.set("Authorization", `Bearer ${devToken}`);
50
+ }
51
+ const method = init?.method?.toUpperCase() || "GET";
52
+ if (["POST", "PUT", "PATCH", "DELETE"].includes(method)) {
53
+ headers.set("X-Hardkas-Request", "true");
54
+ }
55
+ const mergedInit = {
56
+ ...init,
57
+ headers
58
+ };
59
+ const response = await fetch(input, mergedInit);
60
+ const genHeader = response.headers?.get?.("X-Hardkas-Generation");
61
+ if (genHeader) {
62
+ setGenerationId((prev) => {
63
+ if (prev !== genHeader) {
64
+ setTimeout(() => {
65
+ if (queryClient) queryClient.invalidateQueries();
66
+ }, 0);
67
+ return genHeader;
68
+ }
69
+ return prev;
70
+ });
71
+ }
72
+ return response;
73
+ }, [queryClient, devToken]);
23
74
  const connect = React.useCallback(() => {
24
75
  if (typeof window === "undefined") return;
25
76
  if (!config.devServerUrl) {
@@ -35,9 +86,20 @@ function HardKasProvider({ config, children, queryClient: externalQueryClient })
35
86
  }
36
87
  setSseStatus("connecting");
37
88
  const baseUrl = config.devServerUrl;
38
- const url = baseUrl.endsWith("/") ? `${baseUrl}api/stream` : `${baseUrl}/api/stream`;
89
+ let url = baseUrl.endsWith("/") ? `${baseUrl}api/stream` : `${baseUrl}/api/stream`;
90
+ if (devToken) {
91
+ url += (url.includes("?") ? "&" : "?") + `token=${encodeURIComponent(devToken)}`;
92
+ }
39
93
  const es = new EventSource(url);
40
94
  eventSource.current = es;
95
+ if (typeof window !== "undefined") {
96
+ window.__MOCK_SSE_CLOSE__ = () => {
97
+ es.close();
98
+ if (es.onerror) {
99
+ es.onerror.call(es, new Event("error"));
100
+ }
101
+ };
102
+ }
41
103
  es.onopen = () => {
42
104
  setSseStatus("connected");
43
105
  backoffMs.current = 500;
@@ -70,6 +132,9 @@ function HardKasProvider({ config, children, queryClient: externalQueryClient })
70
132
  "sandbox-session-paired",
71
133
  "sandbox-session-expired",
72
134
  "sandbox-session-disconnected",
135
+ "projection-stale",
136
+ "projection-synced",
137
+ "query-synced",
73
138
  "ping",
74
139
  "heartbeat"
75
140
  ];
@@ -82,9 +147,20 @@ function HardKasProvider({ config, children, queryClient: externalQueryClient })
82
147
  };
83
148
  setLastEvent(event);
84
149
  listeners.current.forEach((l) => l(event));
150
+ if (type === "projection-stale") {
151
+ setProjectionStatus("stale");
152
+ }
153
+ if (type === "projection-synced" || type === "query-synced") {
154
+ setProjectionStatus("synced");
155
+ setLastSyncedAt((/* @__PURE__ */ new Date()).toISOString());
156
+ if (event.payload?.generationId) {
157
+ setGenerationId(event.payload.generationId);
158
+ }
159
+ queryClient.invalidateQueries();
160
+ }
85
161
  });
86
162
  });
87
- }, [config.devServerUrl]);
163
+ }, [config.devServerUrl, queryClient, devToken]);
88
164
  React.useEffect(() => {
89
165
  connect();
90
166
  return () => {
@@ -248,6 +324,10 @@ function HardKasProvider({ config, children, queryClient: externalQueryClient })
248
324
  igraClient,
249
325
  queryClient,
250
326
  sseStatus,
327
+ projectionStatus,
328
+ generationId,
329
+ lastSyncedAt,
330
+ apiFetch,
251
331
  lastEvent,
252
332
  subscribe,
253
333
  providers,
@@ -262,6 +342,10 @@ function HardKasProvider({ config, children, queryClient: externalQueryClient })
262
342
  igraClient,
263
343
  queryClient,
264
344
  sseStatus,
345
+ projectionStatus,
346
+ generationId,
347
+ lastSyncedAt,
348
+ apiFetch,
265
349
  lastEvent,
266
350
  subscribe,
267
351
  providers,
@@ -272,7 +356,30 @@ function HardKasProvider({ config, children, queryClient: externalQueryClient })
272
356
  disconnectWallet,
273
357
  switchChain
274
358
  ]);
275
- return /* @__PURE__ */ jsx(HardKasContext.Provider, { value, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children }) });
359
+ return /* @__PURE__ */ jsx(HardKasContext.Provider, { value, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: tokenMissing ? /* @__PURE__ */ jsx("div", { style: {
360
+ display: "flex",
361
+ flexDirection: "column",
362
+ alignItems: "center",
363
+ justifyContent: "center",
364
+ height: "100vh",
365
+ width: "100vw",
366
+ backgroundColor: "#1a1a1a",
367
+ color: "#f87171",
368
+ fontFamily: "sans-serif",
369
+ textAlign: "center",
370
+ padding: "20px"
371
+ }, children: /* @__PURE__ */ jsxs("div", { style: {
372
+ backgroundColor: "#2d2d2d",
373
+ border: "2px solid #ef4444",
374
+ borderRadius: "8px",
375
+ padding: "30px",
376
+ maxWidth: "450px",
377
+ boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.5)"
378
+ }, children: [
379
+ /* @__PURE__ */ jsx("h2", { style: { color: "#ef4444", marginTop: 0 }, children: "Dashboard authentication token missing." }),
380
+ /* @__PURE__ */ jsx("p", { style: { color: "#d1d5db", lineHeight: "1.5" }, children: "The dev-server is secured against local workstation CSRF and DNS rebinding attacks." }),
381
+ /* @__PURE__ */ jsx("p", { style: { color: "#9ca3af", fontWeight: "bold" }, children: "Restart hardkas dashboard." })
382
+ ] }) }) : children }) });
276
383
  }
277
384
  function useHardKas() {
278
385
  const context = useContext(HardKasContext);
@@ -286,7 +393,7 @@ function useHardKas() {
286
393
  import { useQuery, useQueryClient } from "@tanstack/react-query";
287
394
  import { useEffect } from "react";
288
395
  function useHardKasSession(name) {
289
- const { config, subscribe } = useHardKas();
396
+ const { config, subscribe, apiFetch } = useHardKas();
290
397
  const queryClient = useQueryClient();
291
398
  const sessionToResolve = name || config.sessionName;
292
399
  useEffect(() => {
@@ -303,7 +410,7 @@ function useHardKasSession(name) {
303
410
  try {
304
411
  const baseUrl = config.devServerUrl || "";
305
412
  const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/session` : `${baseUrl}/api/session` : "/api/session";
306
- const response = await fetch(url);
413
+ const response = await apiFetch(url);
307
414
  const json = await response.json();
308
415
  const active = json.active;
309
416
  if (!active) return null;
@@ -329,7 +436,7 @@ function useHardKasSession(name) {
329
436
  import { useQuery as useQuery2, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
330
437
  import { useEffect as useEffect2 } from "react";
331
438
  function useHardKasHealth() {
332
- const { config, subscribe } = useHardKas();
439
+ const { config, subscribe, apiFetch } = useHardKas();
333
440
  const queryClient = useQueryClient2();
334
441
  useEffect2(() => {
335
442
  const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "health"] });
@@ -345,7 +452,7 @@ function useHardKasHealth() {
345
452
  try {
346
453
  const baseUrl = config.devServerUrl || "";
347
454
  const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/health` : `${baseUrl}/api/health` : "/api/health";
348
- const res = await fetch(url);
455
+ const res = await apiFetch(url);
349
456
  if (!res || !res.ok) {
350
457
  throw new Error("Failed to fetch health from dev server");
351
458
  }
@@ -362,7 +469,8 @@ function useHardKasHealth() {
362
469
  }
363
470
 
364
471
  // src/hooks/kaspa.ts
365
- import { useQuery as useQuery3 } from "@tanstack/react-query";
472
+ import { useQuery as useQuery3, useQueryClient as useQueryClient3 } from "@tanstack/react-query";
473
+ import { useEffect as useEffect3 } from "react";
366
474
  function useKaspaWallet() {
367
475
  const { data: session } = useHardKasSession();
368
476
  return {
@@ -372,9 +480,18 @@ function useKaspaWallet() {
372
480
  };
373
481
  }
374
482
  function useKaspaBalance(options = {}) {
375
- const { config } = useHardKas();
483
+ const { config, subscribe, apiFetch } = useHardKas();
484
+ const queryClient = useQueryClient3();
376
485
  const { address, name } = useKaspaWallet();
377
486
  const { data: health } = useHardKasHealth();
487
+ useEffect3(() => {
488
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["kaspa", "balance"] });
489
+ return subscribe((event) => {
490
+ if (["query-synced", "session-changed"].includes(event.type)) {
491
+ sync();
492
+ }
493
+ });
494
+ }, [queryClient, subscribe]);
378
495
  const l1Status = health?.kaspa?.status || health?.l1?.status;
379
496
  const isL1Online = l1Status === "healthy" || l1Status === "simulated-mode" || l1Status === "ok" || l1Status === "running" || l1Status === "online" || !!address && address.startsWith("kaspa:sim_");
380
497
  return useQuery3({
@@ -382,7 +499,21 @@ function useKaspaBalance(options = {}) {
382
499
  queryFn: async () => {
383
500
  if (!address) return 0n;
384
501
  if (address.startsWith("kaspa:sim_") || l1Status === "simulated-mode") {
385
- return 100000000000n;
502
+ try {
503
+ const baseUrl = config.devServerUrl || "";
504
+ const fetchUrl = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/accounts` : `${baseUrl}/api/accounts` : "/api/accounts";
505
+ const res = await apiFetch(fetchUrl);
506
+ if (res.ok) {
507
+ const data = await res.json();
508
+ const match = (data.accounts || []).find((a) => a.address === address);
509
+ if (match) {
510
+ return BigInt(match.balanceSompi || match.balance || "0");
511
+ }
512
+ }
513
+ } catch (e) {
514
+ console.warn("Failed to fetch derived simulated balance, falling back to 0:", e);
515
+ }
516
+ return 0n;
386
517
  }
387
518
  let url = config.kaspaRpcUrl || "http://127.0.0.1:16110";
388
519
  if (url.includes("127.0.0.1:16110") || url.includes("localhost:16110")) {
@@ -433,7 +564,7 @@ function useKaspaBalance(options = {}) {
433
564
  }
434
565
  }
435
566
  try {
436
- const response = await fetch(url, {
567
+ const response = await apiFetch(url, {
437
568
  method: "POST",
438
569
  headers: { "Content-Type": "application/json" },
439
570
  body: JSON.stringify({
@@ -455,10 +586,11 @@ function useKaspaBalance(options = {}) {
455
586
  }
456
587
 
457
588
  // src/hooks/igra.ts
458
- import { useQuery as useQuery4 } from "@tanstack/react-query";
589
+ import { useQuery as useQuery4, useQueryClient as useQueryClient4 } from "@tanstack/react-query";
590
+ import { useEffect as useEffect4 } from "react";
459
591
  function useIgraAccount() {
460
592
  const { data: session } = useHardKasSession();
461
- const { walletAddress } = useHardKas();
593
+ const { walletAddress, apiFetch } = useHardKas();
462
594
  const address = walletAddress || session?.l2.address;
463
595
  return {
464
596
  name: walletAddress ? "Browser Wallet" : session?.l2.account,
@@ -475,7 +607,8 @@ function useIgraWallet() {
475
607
  walletChainId,
476
608
  connectWallet,
477
609
  disconnectWallet,
478
- switchChain
610
+ switchChain,
611
+ apiFetch
479
612
  } = useHardKas();
480
613
  return {
481
614
  providers,
@@ -489,10 +622,18 @@ function useIgraWallet() {
489
622
  };
490
623
  }
491
624
  function useIgraBalance(options = {}) {
492
- const { igraClient } = useHardKas();
625
+ const { igraClient, config, subscribe, apiFetch } = useHardKas();
626
+ const queryClient = useQueryClient4();
493
627
  const { address } = useIgraAccount();
628
+ useEffect4(() => {
629
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["igra", "balance"] });
630
+ return subscribe((event) => {
631
+ if (["query-synced", "session-changed"].includes(event.type)) {
632
+ sync();
633
+ }
634
+ });
635
+ }, [queryClient, subscribe]);
494
636
  const { data: session } = useHardKasSession();
495
- const { config } = useHardKas();
496
637
  const { data: health } = useHardKasHealth();
497
638
  const l2Status = health?.igra?.status || health?.l2?.status;
498
639
  const isL2Online = l2Status === "healthy" || l2Status === "ok" || l2Status === "running" || l2Status === "online" || !!address && (address.startsWith("0xsim_") || address.startsWith("kaspa:sim_") || !address.startsWith("0x"));
@@ -501,7 +642,23 @@ function useIgraBalance(options = {}) {
501
642
  queryFn: async () => {
502
643
  if (!address) return 0n;
503
644
  if (address.startsWith("0xsim_") || !address.startsWith("0x") || l2Status === "simulated-mode") {
504
- return 500000000000000000000n;
645
+ try {
646
+ const baseUrl = config.devServerUrl || "";
647
+ const fetchUrl = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/accounts` : `${baseUrl}/api/accounts` : "/api/accounts";
648
+ const res = await apiFetch(fetchUrl);
649
+ if (res.ok) {
650
+ const data = await res.json();
651
+ const match = (data.accounts || []).find(
652
+ (a) => a.address.toLowerCase() === address.toLowerCase() || a.name.toLowerCase() === address.toLowerCase()
653
+ );
654
+ if (match) {
655
+ return BigInt(match.balanceSompi || match.balance || "0");
656
+ }
657
+ }
658
+ } catch (e) {
659
+ console.warn("Failed to fetch derived simulated L2 balance, falling back to 0:", e);
660
+ }
661
+ return 0n;
505
662
  }
506
663
  try {
507
664
  return await igraClient.getBalance({ address });
@@ -514,48 +671,56 @@ function useIgraBalance(options = {}) {
514
671
  });
515
672
  }
516
673
 
517
- // src/hooks/bridge.ts
518
- import { useQuery as useQuery5, useMutation } from "@tanstack/react-query";
519
- import { planBridgeEntry, simulatePrefixMining } from "@hardkas/bridge-local";
520
- function useBridgeLocalPlan(options) {
521
- const { data: session } = useHardKasSession();
522
- const { config } = useHardKas();
674
+ // src/hooks/events.ts
675
+ import { useQuery as useQuery5 } from "@tanstack/react-query";
676
+ import { useEffect as useEffect5 } from "react";
677
+ import { useQueryClient as useQueryClient5 } from "@tanstack/react-query";
678
+ function useEvents(kind, txId) {
679
+ const { config, subscribe, apiFetch } = useHardKas();
680
+ const queryClient = useQueryClient5();
681
+ const queryKey = ["hardkas", "events", kind, txId];
682
+ useEffect5(() => {
683
+ return subscribe((event) => {
684
+ queryClient.invalidateQueries({ queryKey: ["hardkas", "events"] });
685
+ });
686
+ }, [queryClient, subscribe]);
523
687
  return useQuery5({
524
- queryKey: ["bridge", "plan", session?.name, options?.amountSompi.toString(), options?.toIgra, config.localOnly],
688
+ queryKey,
525
689
  queryFn: async () => {
526
- if (!session || !options) return null;
527
- const targetAddress = options.toIgra || session.l2.address;
528
- if (!targetAddress) throw new Error("Target Igra address is required.");
529
- return planBridgeEntry({
530
- fromAddress: session.l1.address,
531
- targetEvmAddress: targetAddress,
532
- amountSompi: options.amountSompi,
533
- networkId: "simnet",
534
- // Fixed for local simulation
535
- availableUtxos: [
536
- {
537
- outpoint: { transactionId: "mock-utxo", index: 0 },
538
- address: session.l1.address,
539
- amountSompi: options.amountSompi * 2n,
540
- scriptPublicKey: "mock-script"
541
- }
542
- ]
543
- });
690
+ try {
691
+ const baseUrl = config.devServerUrl || "";
692
+ const queryParams = new URLSearchParams();
693
+ if (kind) queryParams.append("kind", kind);
694
+ if (txId) queryParams.append("txId", txId);
695
+ const queryStr = queryParams.toString() ? `?${queryParams.toString()}` : "";
696
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/events${queryStr}` : `${baseUrl}/api/events${queryStr}` : `/api/events${queryStr}`;
697
+ const response = await apiFetch(url);
698
+ if (!response.ok) return { events: [] };
699
+ const data = await response.json();
700
+ return {
701
+ events: data.events || [],
702
+ observabilityDrift: data.observabilityDrift,
703
+ reason: data.reason
704
+ };
705
+ } catch (e) {
706
+ console.error("Failed to fetch events from dev server:", e);
707
+ return { events: [] };
708
+ }
544
709
  },
545
- enabled: !!session && !!options
546
- });
547
- }
548
- function useBridgeLocalSimulation() {
549
- return useMutation({
550
- mutationFn: async (params) => {
551
- return simulatePrefixMining(params.payloadBase, params.prefix, { timeoutMs: 1e4 });
552
- }
710
+ staleTime: 5e3
553
711
  });
554
712
  }
555
713
 
556
714
  // src/hooks/metamask.ts
557
- import { useState, useEffect as useEffect3, useCallback } from "react";
715
+ import { useState, useEffect as useEffect6, useCallback } from "react";
558
716
  import { createWalletClient, custom } from "viem";
717
+ var getMetaMaskProvider = () => {
718
+ if (typeof window === "undefined" || !window.ethereum) return null;
719
+ if (window.ethereum.providers) {
720
+ return window.ethereum.providers.find((p) => p.isMetaMask) || window.ethereum;
721
+ }
722
+ return window.ethereum;
723
+ };
559
724
  function useMetaMaskLocal() {
560
725
  const [state, setState] = useState({
561
726
  installed: false,
@@ -565,12 +730,12 @@ function useMetaMaskLocal() {
565
730
  errors: []
566
731
  });
567
732
  const checkStatus = useCallback(async () => {
568
- if (typeof window === "undefined" || !window.ethereum) {
733
+ const provider = getMetaMaskProvider();
734
+ if (!provider) {
569
735
  setState((s) => ({ ...s, installed: false }));
570
736
  return;
571
737
  }
572
738
  try {
573
- const provider = window.ethereum;
574
739
  const chainIdHex = await provider.request({ method: "eth_chainId" });
575
740
  const chainId = parseInt(chainIdHex, 16);
576
741
  const accounts = await provider.request({ method: "eth_accounts" });
@@ -588,17 +753,18 @@ function useMetaMaskLocal() {
588
753
  }
589
754
  }, []);
590
755
  const connect = useCallback(async () => {
591
- if (typeof window === "undefined" || !window.ethereum) return;
756
+ const provider = getMetaMaskProvider();
757
+ if (!provider) return;
592
758
  try {
593
- await window.ethereum.request({ method: "eth_requestAccounts" });
759
+ await provider.request({ method: "eth_requestAccounts" });
594
760
  await checkStatus();
595
761
  } catch (e) {
596
762
  setState((s) => ({ ...s, errors: [...s.errors, e.message] }));
597
763
  }
598
764
  }, [checkStatus]);
599
- useEffect3(() => {
600
- if (typeof window === "undefined" || !window.ethereum) return;
601
- const provider = window.ethereum;
765
+ useEffect6(() => {
766
+ const provider = getMetaMaskProvider();
767
+ if (!provider) return;
602
768
  checkStatus();
603
769
  const handleChange = () => checkStatus();
604
770
  provider.on("accountsChanged", handleChange);
@@ -616,16 +782,17 @@ function useMetaMaskLocal() {
616
782
  }
617
783
  function useSwitchToLocalIgra() {
618
784
  const switchChain = async () => {
619
- if (typeof window === "undefined" || !window.ethereum) return;
785
+ const provider = getMetaMaskProvider();
786
+ if (!provider) return;
620
787
  try {
621
- await window.ethereum.request({
788
+ await provider.request({
622
789
  method: "wallet_switchEthereumChain",
623
790
  params: [{ chainId: "0x4bd8" }]
624
791
  // 19416
625
792
  });
626
793
  } catch (e) {
627
794
  if (e.code === 4902) {
628
- await window.ethereum.request({
795
+ await provider.request({
629
796
  method: "wallet_addEthereumChain",
630
797
  params: [{
631
798
  chainId: "0x4bd8",
@@ -651,17 +818,18 @@ function useIgraInjectedAccount(sessionL2Address) {
651
818
  function useLocalIgraWalletClient() {
652
819
  const { state } = useMetaMaskLocal();
653
820
  const getClient = useCallback(() => {
654
- if (!state.connected || !window.ethereum) return null;
821
+ const provider = getMetaMaskProvider();
822
+ if (!state.connected || !provider) return null;
655
823
  return createWalletClient({
656
824
  account: state.account,
657
- transport: custom(window.ethereum)
825
+ transport: custom(provider)
658
826
  });
659
827
  }, [state.connected, state.account]);
660
828
  return { getClient };
661
829
  }
662
830
 
663
831
  // src/hooks/kasware.ts
664
- import { useState as useState2, useEffect as useEffect4, useCallback as useCallback2 } from "react";
832
+ import { useState as useState2, useEffect as useEffect7, useCallback as useCallback2 } from "react";
665
833
  function useKasWareLocal() {
666
834
  const [state, setState] = useState2({
667
835
  installed: false,
@@ -693,7 +861,7 @@ function useKasWareLocal() {
693
861
  setState((s) => ({ ...s, errors: [e.message] }));
694
862
  }
695
863
  }, []);
696
- useEffect4(() => {
864
+ useEffect7(() => {
697
865
  if (typeof window === "undefined" || !window.kasware) return;
698
866
  const provider = window.kasware;
699
867
  checkStatus();
@@ -753,12 +921,12 @@ function useKasWareSessionMatch(sessionL1Address) {
753
921
  }
754
922
 
755
923
  // src/hooks/sandbox.ts
756
- import { useQuery as useQuery6, useMutation as useMutation2, useQueryClient as useQueryClient3 } from "@tanstack/react-query";
757
- import { useEffect as useEffect5 } from "react";
924
+ import { useQuery as useQuery6, useMutation, useQueryClient as useQueryClient6 } from "@tanstack/react-query";
925
+ import { useEffect as useEffect8 } from "react";
758
926
  function useSandboxSessions() {
759
- const { config, subscribe } = useHardKas();
760
- const queryClient = useQueryClient3();
761
- useEffect5(() => {
927
+ const { config, subscribe, apiFetch } = useHardKas();
928
+ const queryClient = useQueryClient6();
929
+ useEffect8(() => {
762
930
  const sync = () => queryClient.invalidateQueries({ queryKey: ["sandbox", "sessions"] });
763
931
  return subscribe((event) => {
764
932
  if ([
@@ -776,20 +944,20 @@ function useSandboxSessions() {
776
944
  queryFn: async () => {
777
945
  const baseUrl = config.devServerUrl || "";
778
946
  const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/walletconnect/sandbox/sessions` : `${baseUrl}/api/walletconnect/sandbox/sessions` : "/api/walletconnect/sandbox/sessions";
779
- const res = await fetch(url);
947
+ const res = await apiFetch(url);
780
948
  const json = await res.json();
781
949
  return json.sessions;
782
950
  }
783
951
  });
784
952
  }
785
953
  function useCreateSandboxSession() {
786
- const { config } = useHardKas();
787
- const queryClient = useQueryClient3();
788
- return useMutation2({
954
+ const { config, apiFetch } = useHardKas();
955
+ const queryClient = useQueryClient6();
956
+ return useMutation({
789
957
  mutationFn: async () => {
790
958
  const baseUrl = config.devServerUrl || "";
791
959
  const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/walletconnect/sandbox/create` : `${baseUrl}/api/walletconnect/sandbox/create` : "/api/walletconnect/sandbox/create";
792
- const res = await fetch(url, { method: "POST" });
960
+ const res = await apiFetch(url, { method: "POST" });
793
961
  return await res.json();
794
962
  },
795
963
  onSuccess: () => {
@@ -798,13 +966,13 @@ function useCreateSandboxSession() {
798
966
  });
799
967
  }
800
968
  function usePairSandboxSession() {
801
- const { config } = useHardKas();
802
- const queryClient = useQueryClient3();
803
- return useMutation2({
969
+ const { config, apiFetch } = useHardKas();
970
+ const queryClient = useQueryClient6();
971
+ return useMutation({
804
972
  mutationFn: async (id) => {
805
973
  const baseUrl = config.devServerUrl || "";
806
974
  const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/walletconnect/sandbox/pair` : `${baseUrl}/api/walletconnect/sandbox/pair` : "/api/walletconnect/sandbox/pair";
807
- const res = await fetch(url, {
975
+ const res = await apiFetch(url, {
808
976
  method: "POST",
809
977
  headers: { "Content-Type": "application/json" },
810
978
  body: JSON.stringify({ id })
@@ -817,13 +985,13 @@ function usePairSandboxSession() {
817
985
  });
818
986
  }
819
987
  function useDisconnectSandboxSession() {
820
- const { config } = useHardKas();
821
- const queryClient = useQueryClient3();
822
- return useMutation2({
988
+ const { config, apiFetch } = useHardKas();
989
+ const queryClient = useQueryClient6();
990
+ return useMutation({
823
991
  mutationFn: async (id) => {
824
992
  const baseUrl = config.devServerUrl || "";
825
993
  const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/walletconnect/sandbox/disconnect` : `${baseUrl}/api/walletconnect/sandbox/disconnect` : "/api/walletconnect/sandbox/disconnect";
826
- const res = await fetch(url, {
994
+ const res = await apiFetch(url, {
827
995
  method: "POST",
828
996
  headers: { "Content-Type": "application/json" },
829
997
  body: JSON.stringify({ id })
@@ -837,7 +1005,7 @@ function useDisconnectSandboxSession() {
837
1005
  }
838
1006
 
839
1007
  // src/hooks/contracts.ts
840
- import { useQuery as useQuery7, useMutation as useMutation3 } from "@tanstack/react-query";
1008
+ import { useQuery as useQuery7, useMutation as useMutation2 } from "@tanstack/react-query";
841
1009
  import { createWalletClient as createWalletClient2, custom as custom2 } from "viem";
842
1010
  function useIgraReadContract(options) {
843
1011
  const { igraClient, config } = useHardKas();
@@ -858,7 +1026,7 @@ function useIgraReadContract(options) {
858
1026
  }
859
1027
  function useIgraWriteContract() {
860
1028
  const { activeProvider, walletAddress, igraClient } = useHardKas();
861
- return useMutation3({
1029
+ return useMutation2({
862
1030
  mutationFn: async (params) => {
863
1031
  let client = params.walletClient;
864
1032
  if (!client) {
@@ -885,19 +1053,370 @@ function useIgraWriteContract() {
885
1053
  }
886
1054
  function useIgraWaitForReceipt() {
887
1055
  const { igraClient } = useHardKas();
888
- return useMutation3({
1056
+ return useMutation2({
889
1057
  mutationFn: async (hash) => {
890
1058
  return await igraClient.waitForTransactionReceipt({ hash });
891
1059
  }
892
1060
  });
893
1061
  }
1062
+
1063
+ // src/hooks/overview.ts
1064
+ import { useQuery as useQuery8, useQueryClient as useQueryClient7 } from "@tanstack/react-query";
1065
+ import { useEffect as useEffect9 } from "react";
1066
+ function useOverview() {
1067
+ const { config, subscribe, apiFetch } = useHardKas();
1068
+ const queryClient = useQueryClient7();
1069
+ useEffect9(() => {
1070
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "overview"] });
1071
+ return subscribe((event) => {
1072
+ if (["query-synced", "session-changed"].includes(event.type)) {
1073
+ sync();
1074
+ }
1075
+ });
1076
+ }, [queryClient, subscribe]);
1077
+ return useQuery8({
1078
+ queryKey: ["hardkas", "overview"],
1079
+ queryFn: async () => {
1080
+ try {
1081
+ const baseUrl = config.devServerUrl || "";
1082
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/overview` : `${baseUrl}/api/overview` : "/api/overview";
1083
+ const response = await apiFetch(url);
1084
+ if (!response.ok) return null;
1085
+ return await response.json();
1086
+ } catch (e) {
1087
+ console.error("Failed to fetch overview stats from dev server:", e);
1088
+ return null;
1089
+ }
1090
+ },
1091
+ staleTime: 6e4
1092
+ });
1093
+ }
1094
+
1095
+ // src/hooks/accounts.ts
1096
+ import { useQuery as useQuery9, useQueryClient as useQueryClient8 } from "@tanstack/react-query";
1097
+ import { useEffect as useEffect10 } from "react";
1098
+ function useAccounts() {
1099
+ const { config, subscribe, apiFetch } = useHardKas();
1100
+ const queryClient = useQueryClient8();
1101
+ useEffect10(() => {
1102
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "accounts"] });
1103
+ return subscribe((event) => {
1104
+ if (["query-synced", "session-changed"].includes(event.type)) {
1105
+ sync();
1106
+ }
1107
+ });
1108
+ }, [queryClient, subscribe]);
1109
+ return useQuery9({
1110
+ queryKey: ["hardkas", "accounts"],
1111
+ queryFn: async () => {
1112
+ try {
1113
+ const baseUrl = config.devServerUrl || "";
1114
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/accounts` : `${baseUrl}/api/accounts` : "/api/accounts";
1115
+ const response = await apiFetch(url);
1116
+ if (!response.ok) return { accounts: [] };
1117
+ const data = await response.json();
1118
+ return data || { accounts: [] };
1119
+ } catch (e) {
1120
+ console.error("Failed to fetch accounts from dev server:", e);
1121
+ return { accounts: [] };
1122
+ }
1123
+ },
1124
+ staleTime: 3e4
1125
+ });
1126
+ }
1127
+
1128
+ // src/hooks/transactions.ts
1129
+ import { useQuery as useQuery10, useQueryClient as useQueryClient9 } from "@tanstack/react-query";
1130
+ import { useEffect as useEffect11 } from "react";
1131
+ function useTransactions() {
1132
+ const { config, subscribe, apiFetch } = useHardKas();
1133
+ const queryClient = useQueryClient9();
1134
+ useEffect11(() => {
1135
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "transactions"] });
1136
+ return subscribe((event) => {
1137
+ if (["query-synced", "session-changed"].includes(event.type)) {
1138
+ sync();
1139
+ }
1140
+ });
1141
+ }, [queryClient, subscribe]);
1142
+ return useQuery10({
1143
+ queryKey: ["hardkas", "transactions"],
1144
+ queryFn: async () => {
1145
+ try {
1146
+ const baseUrl = config.devServerUrl || "";
1147
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/transactions` : `${baseUrl}/api/transactions` : "/api/transactions";
1148
+ const response = await apiFetch(url);
1149
+ if (!response.ok) return [];
1150
+ const data = await response.json();
1151
+ return data.transactions || [];
1152
+ } catch (e) {
1153
+ console.error("Failed to fetch transactions from dev server:", e);
1154
+ return [];
1155
+ }
1156
+ },
1157
+ staleTime: 1e4
1158
+ });
1159
+ }
1160
+ function useTransaction(id) {
1161
+ const { config, subscribe, apiFetch } = useHardKas();
1162
+ const queryClient = useQueryClient9();
1163
+ useEffect11(() => {
1164
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "transaction", id] });
1165
+ return subscribe((event) => {
1166
+ if (["query-synced", "session-changed"].includes(event.type)) {
1167
+ sync();
1168
+ }
1169
+ });
1170
+ }, [id, queryClient, subscribe]);
1171
+ return useQuery10({
1172
+ queryKey: ["hardkas", "transaction", id],
1173
+ queryFn: async () => {
1174
+ if (!id) return null;
1175
+ try {
1176
+ const baseUrl = config.devServerUrl || "";
1177
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/transactions/${id}` : `${baseUrl}/api/transactions/${id}` : `/api/transactions/${id}`;
1178
+ const response = await apiFetch(url);
1179
+ if (!response.ok) return null;
1180
+ return await response.json();
1181
+ } catch (e) {
1182
+ console.error(`Failed to fetch transaction detail for '${id}':`, e);
1183
+ return null;
1184
+ }
1185
+ },
1186
+ enabled: !!id,
1187
+ staleTime: 1e4
1188
+ });
1189
+ }
1190
+
1191
+ // src/hooks/artifacts.ts
1192
+ import { useQuery as useQuery11, useQueryClient as useQueryClient10 } from "@tanstack/react-query";
1193
+ import { useEffect as useEffect12 } from "react";
1194
+ function useArtifacts(schemaFilter) {
1195
+ const { config, subscribe, apiFetch } = useHardKas();
1196
+ const queryClient = useQueryClient10();
1197
+ useEffect12(() => {
1198
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "artifacts", schemaFilter || "all"] });
1199
+ return subscribe((event) => {
1200
+ if (["query-synced", "session-changed"].includes(event.type)) {
1201
+ sync();
1202
+ }
1203
+ });
1204
+ }, [schemaFilter, queryClient, subscribe]);
1205
+ return useQuery11({
1206
+ queryKey: ["hardkas", "artifacts", schemaFilter || "all"],
1207
+ queryFn: async () => {
1208
+ try {
1209
+ const baseUrl = config.devServerUrl || "";
1210
+ const queryParams = schemaFilter ? `?schema=${schemaFilter}` : "";
1211
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/artifacts${queryParams}` : `${baseUrl}/api/artifacts${queryParams}` : `/api/artifacts${queryParams}`;
1212
+ const response = await apiFetch(url);
1213
+ if (!response.ok) return [];
1214
+ const data = await response.json();
1215
+ return data.artifacts || [];
1216
+ } catch (e) {
1217
+ console.error("Failed to fetch artifacts from dev server:", e);
1218
+ return [];
1219
+ }
1220
+ },
1221
+ staleTime: 3e4
1222
+ });
1223
+ }
1224
+ function useArtifact(id) {
1225
+ const { config, subscribe, apiFetch } = useHardKas();
1226
+ const queryClient = useQueryClient10();
1227
+ useEffect12(() => {
1228
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "artifact", id] });
1229
+ return subscribe((event) => {
1230
+ if (["query-synced", "session-changed"].includes(event.type)) {
1231
+ sync();
1232
+ }
1233
+ });
1234
+ }, [id, queryClient, subscribe]);
1235
+ return useQuery11({
1236
+ queryKey: ["hardkas", "artifact", id],
1237
+ queryFn: async () => {
1238
+ if (!id) return null;
1239
+ try {
1240
+ const baseUrl = config.devServerUrl || "";
1241
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/artifacts/${id}` : `${baseUrl}/api/artifacts/${id}` : `/api/artifacts/${id}`;
1242
+ const response = await apiFetch(url);
1243
+ if (!response.ok) return null;
1244
+ const data = await response.json();
1245
+ return data.artifact || null;
1246
+ } catch (e) {
1247
+ console.error(`Failed to fetch artifact detail for '${id}':`, e);
1248
+ return null;
1249
+ }
1250
+ },
1251
+ enabled: !!id,
1252
+ staleTime: 3e4
1253
+ });
1254
+ }
1255
+
1256
+ // src/hooks/replay.ts
1257
+ import { useQuery as useQuery12, useQueryClient as useQueryClient11 } from "@tanstack/react-query";
1258
+ import { useEffect as useEffect13 } from "react";
1259
+ function useReplayStatus() {
1260
+ const { config, subscribe, apiFetch } = useHardKas();
1261
+ const queryClient = useQueryClient11();
1262
+ useEffect13(() => {
1263
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "replay"] });
1264
+ return subscribe((event) => {
1265
+ if (["query-synced", "session-changed"].includes(event.type)) {
1266
+ sync();
1267
+ }
1268
+ });
1269
+ }, [queryClient, subscribe]);
1270
+ return useQuery12({
1271
+ queryKey: ["hardkas", "replay"],
1272
+ queryFn: async () => {
1273
+ try {
1274
+ const baseUrl = config.devServerUrl || "";
1275
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/replay` : `${baseUrl}/api/replay` : "/api/replay";
1276
+ const response = await apiFetch(url);
1277
+ if (!response.ok) return { replays: [], pendingReplays: [], pendingReplay: false };
1278
+ const data = await response.json();
1279
+ const formatReplay = (r) => {
1280
+ return {
1281
+ artifactId: r.artifactId,
1282
+ txId: r.payload?.txId || r.txId,
1283
+ planOk: r.payload?.planOk ?? false,
1284
+ receiptOk: r.payload?.receiptOk ?? false,
1285
+ invariantsOk: r.payload?.invariantsOk ?? false,
1286
+ ok: r.payload?.planOk && r.payload?.receiptOk && r.payload?.invariantsOk ? true : false,
1287
+ checks: r.payload?.checks || {
1288
+ workflowDeterministic: "unknown",
1289
+ consensusValidation: "unknown",
1290
+ l2BridgeCorrectness: "unknown"
1291
+ },
1292
+ errors: r.payload?.errors || [],
1293
+ divergencesCount: r.payload?.divergencesCount || 0,
1294
+ createdAt: r.createdAt || r.timestamp || (/* @__PURE__ */ new Date()).toISOString()
1295
+ };
1296
+ };
1297
+ return {
1298
+ replays: (data.replays || []).map(formatReplay),
1299
+ pendingReplays: data.pendingReplays || [],
1300
+ pendingReplay: data.pendingReplay || false,
1301
+ reason: data.reason
1302
+ };
1303
+ } catch (e) {
1304
+ console.error("Failed to fetch replay status from dev server:", e);
1305
+ return { replays: [], pendingReplays: [], pendingReplay: false };
1306
+ }
1307
+ },
1308
+ staleTime: 3e4
1309
+ });
1310
+ }
1311
+ function useReplayDetail(txId) {
1312
+ const { config, subscribe, apiFetch } = useHardKas();
1313
+ const queryClient = useQueryClient11();
1314
+ useEffect13(() => {
1315
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "replay", txId] });
1316
+ return subscribe((event) => {
1317
+ if (["query-synced", "session-changed"].includes(event.type)) {
1318
+ sync();
1319
+ }
1320
+ });
1321
+ }, [txId, queryClient, subscribe]);
1322
+ return useQuery12({
1323
+ queryKey: ["hardkas", "replay", txId],
1324
+ queryFn: async () => {
1325
+ if (!txId) return null;
1326
+ try {
1327
+ const baseUrl = config.devServerUrl || "";
1328
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/replay/${txId}` : `${baseUrl}/api/replay/${txId}` : `/api/replay/${txId}`;
1329
+ const response = await apiFetch(url);
1330
+ if (!response.ok) return null;
1331
+ const data = await response.json();
1332
+ return data.replay || null;
1333
+ } catch (e) {
1334
+ console.error(`Failed to fetch replay details for transaction '${txId}':`, e);
1335
+ return null;
1336
+ }
1337
+ },
1338
+ enabled: !!txId,
1339
+ staleTime: 3e4
1340
+ });
1341
+ }
1342
+
1343
+ // src/hooks/deployments.ts
1344
+ import { useQuery as useQuery13, useQueryClient as useQueryClient12 } from "@tanstack/react-query";
1345
+ import { useEffect as useEffect14 } from "react";
1346
+ function useDeployments() {
1347
+ const { config, subscribe, apiFetch } = useHardKas();
1348
+ const queryClient = useQueryClient12();
1349
+ useEffect14(() => {
1350
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "deployments"] });
1351
+ return subscribe((event) => {
1352
+ if (["query-synced", "session-changed"].includes(event.type)) {
1353
+ sync();
1354
+ }
1355
+ });
1356
+ }, [queryClient, subscribe]);
1357
+ return useQuery13({
1358
+ queryKey: ["hardkas", "deployments"],
1359
+ queryFn: async () => {
1360
+ try {
1361
+ const baseUrl = config.devServerUrl || "";
1362
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/deployments` : `${baseUrl}/api/deployments` : "/api/deployments";
1363
+ const response = await apiFetch(url);
1364
+ if (!response.ok) return [];
1365
+ const data = await response.json();
1366
+ return data.deployments || [];
1367
+ } catch (e) {
1368
+ console.error("Failed to fetch deployments from dev server:", e);
1369
+ return [];
1370
+ }
1371
+ },
1372
+ staleTime: 3e4
1373
+ });
1374
+ }
1375
+
1376
+ // src/hooks/activity.ts
1377
+ import { useQuery as useQuery14, useQueryClient as useQueryClient13 } from "@tanstack/react-query";
1378
+ import { useEffect as useEffect15 } from "react";
1379
+ function useActivity() {
1380
+ const { config, subscribe, apiFetch } = useHardKas();
1381
+ const queryClient = useQueryClient13();
1382
+ useEffect15(() => {
1383
+ const sync = () => queryClient.invalidateQueries({ queryKey: ["hardkas", "activity"] });
1384
+ return subscribe((event) => {
1385
+ if (["query-synced", "session-changed"].includes(event.type)) {
1386
+ sync();
1387
+ }
1388
+ });
1389
+ }, [queryClient, subscribe]);
1390
+ return useQuery14({
1391
+ queryKey: ["hardkas", "activity"],
1392
+ queryFn: async () => {
1393
+ try {
1394
+ const baseUrl = config.devServerUrl || "";
1395
+ const url = baseUrl ? baseUrl.endsWith("/") ? `${baseUrl}api/activity` : `${baseUrl}/api/activity` : "/api/activity";
1396
+ const response = await apiFetch(url);
1397
+ if (!response.ok) return [];
1398
+ const data = await response.json();
1399
+ return data.activity || [];
1400
+ } catch (e) {
1401
+ console.error("Failed to fetch activity from dev server:", e);
1402
+ return [];
1403
+ }
1404
+ },
1405
+ staleTime: 5e3
1406
+ // Frequent updates safe because it's local dev
1407
+ });
1408
+ }
894
1409
  export {
895
1410
  HardKasProvider,
896
- useBridgeLocalPlan,
897
- useBridgeLocalSimulation,
1411
+ useAccounts,
1412
+ useActivity,
1413
+ useArtifact,
1414
+ useArtifacts,
898
1415
  useConnectKasWareLocal,
899
1416
  useCreateSandboxSession,
1417
+ useDeployments,
900
1418
  useDisconnectSandboxSession,
1419
+ useEvents,
901
1420
  useHardKas,
902
1421
  useHardKasHealth,
903
1422
  useHardKasSession,
@@ -914,7 +1433,12 @@ export {
914
1433
  useKaspaWallet,
915
1434
  useLocalIgraWalletClient,
916
1435
  useMetaMaskLocal,
1436
+ useOverview,
917
1437
  usePairSandboxSession,
1438
+ useReplayDetail,
1439
+ useReplayStatus,
918
1440
  useSandboxSessions,
919
- useSwitchToLocalIgra
1441
+ useSwitchToLocalIgra,
1442
+ useTransaction,
1443
+ useTransactions
920
1444
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/react",
3
- "version": "0.5.5-alpha",
3
+ "version": "0.6.1-alpha",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -14,11 +14,10 @@
14
14
  "dependencies": {
15
15
  "@tanstack/react-query": "^5.61.5",
16
16
  "viem": "^2.21.51",
17
- "@hardkas/bridge-local": "0.5.5-alpha",
18
- "@hardkas/kaspa-rpc": "0.5.5-alpha",
19
- "@hardkas/core": "0.5.5-alpha",
20
- "@hardkas/sessions": "0.5.5-alpha",
21
- "@hardkas/l2": "0.5.5-alpha"
17
+ "@hardkas/core": "0.6.1-alpha",
18
+ "@hardkas/sessions": "0.6.1-alpha",
19
+ "@hardkas/kaspa-rpc": "0.6.1-alpha",
20
+ "@hardkas/l2": "0.6.1-alpha"
22
21
  },
23
22
  "devDependencies": {
24
23
  "@testing-library/dom": "^10.4.1",