@zendir/ui 0.1.9 → 0.1.10

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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"useSpacecraftPosition.js","sources":["../../../src/react/hooks/useSpacecraftPosition.ts"],"sourcesContent":["/**\n * @zendir/ui - useSpacecraftPosition Hook\n * \n * React hook for fetching and tracking spacecraft positions.\n * \n * OPTIONAL DEPENDENCY: Requires @zendir/sdk client instance.\n * The client must be passed as a prop (allows using any compatible client).\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type { SpacecraftPosition } from '../types';\n\n// Re-export the canonical type for consumers\nexport type { SpacecraftPosition };\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Compact ground track point for real-time orbit visualization\n * Uses abbreviated property names for performance in streaming contexts\n * Note: For full ground track data, use GroundTrackPoint from '../types'\n */\nexport interface CompactGroundTrackPoint {\n /** Latitude in degrees */\n lat: number;\n /** Longitude in degrees */\n lon: number;\n /** Timestamp (epoch ms or ISO string) */\n t: number;\n}\n\n/** Client interface (compatible with @zendir/sdk ZendirClient) */\nexport interface PositionClientInterface {\n fetchSpacecraftPositions: () => Promise<SpacecraftPosition[]>;\n}\n\n// ============================================================================\n// Hook Types\n// ============================================================================\n\nexport interface UseSpacecraftPositionOptions {\n /** Zendir client instance (or any compatible client) */\n client: PositionClientInterface;\n /** Polling interval in ms (default: 1000) */\n interval?: number;\n /** Enable polling */\n enabled?: boolean;\n /** Track ground track history */\n trackHistory?: boolean;\n /** Maximum history points */\n maxHistory?: number;\n}\n\nexport interface UseSpacecraftPositionResult {\n /** All spacecraft positions */\n spacecraft: SpacecraftPosition[];\n /** Ground track history per spacecraft */\n groundTracks: Map<string, CompactGroundTrackPoint[]>;\n /** Loading state */\n isLoading: boolean;\n /** Error message */\n error: string | null;\n /** Last update timestamp */\n lastUpdate: number | null;\n /** Get specific spacecraft */\n getSpacecraft: (id: string) => SpacecraftPosition | undefined;\n /** Manually refresh positions */\n refresh: () => Promise<void>;\n}\n\nconst DEFAULT_MAX_HISTORY = 500;\n\nexport function useSpacecraftPosition(\n options: UseSpacecraftPositionOptions\n): UseSpacecraftPositionResult {\n const {\n client,\n interval = 1000,\n enabled = true,\n trackHistory = true,\n maxHistory = DEFAULT_MAX_HISTORY,\n } = options;\n\n const [spacecraft, setSpacecraft] = useState<SpacecraftPosition[]>([]);\n const [groundTracks, setGroundTracks] = useState<Map<string, CompactGroundTrackPoint[]>>(new Map());\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [lastUpdate, setLastUpdate] = useState<number | null>(null);\n\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const startTimeRef = useRef<number>(Date.now());\n\n const fetchPositions = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n const positions = await client.fetchSpacecraftPositions();\n setSpacecraft(positions);\n setLastUpdate(Date.now());\n\n // Update ground tracks\n if (trackHistory) {\n const elapsed = (Date.now() - startTimeRef.current) / 1000;\n\n setGroundTracks(prev => {\n const newTracks = new Map(prev);\n\n for (const sc of positions) {\n // Skip if no id\n if (!sc.id) continue;\n \n const point: CompactGroundTrackPoint = {\n lat: sc.latitude,\n lon: sc.longitude,\n t: elapsed,\n };\n\n const existing = newTracks.get(sc.id) || [];\n const updated = [...existing, point];\n\n if (updated.length > maxHistory) {\n newTracks.set(sc.id, updated.slice(-maxHistory));\n } else {\n newTracks.set(sc.id, updated);\n }\n }\n\n return newTracks;\n });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to fetch positions';\n setError(message);\n } finally {\n setIsLoading(false);\n }\n }, [client, trackHistory, maxHistory]);\n\n // Set up polling\n useEffect(() => {\n if (!enabled) {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n return;\n }\n\n startTimeRef.current = Date.now();\n\n // Initial fetch\n fetchPositions();\n\n // Set up polling\n intervalRef.current = setInterval(fetchPositions, interval);\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [enabled, interval, fetchPositions]);\n\n const getSpacecraft = useCallback(\n (id: string) => spacecraft.find(sc => sc.id === id),\n [spacecraft]\n );\n\n return {\n spacecraft,\n groundTracks,\n isLoading,\n error,\n lastUpdate,\n getSpacecraft,\n refresh: fetchPositions,\n };\n}\n\nexport default useSpacecraftPosition;\n\n\n\n\n"],"names":[],"mappings":";AAwEA,MAAM,sBAAsB;AAErB,SAAS,sBACd,SAC6B;AAC7B,QAAM;AAAA,IACJ;AAAA,IACA,WAAW;AAAA,IACX,UAAU;AAAA,IACV,eAAe;AAAA,IACf,aAAa;AAAA,EAAA,IACX;AAEJ,QAAM,CAAC,YAAY,aAAa,IAAI,SAA+B,CAAA,CAAE;AACrE,QAAM,CAAC,cAAc,eAAe,IAAI,SAAiD,oBAAI,KAAK;AAClG,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAEhE,QAAM,cAAc,OAA8C,IAAI;AACtE,QAAM,eAAe,OAAe,KAAK,IAAA,CAAK;AAE9C,QAAM,iBAAiB,YAAY,YAAY;AAC7C,iBAAa,IAAI;AACjB,aAAS,IAAI;AAEb,QAAI;AACF,YAAM,YAAY,MAAM,OAAO,yBAAA;AAC/B,oBAAc,SAAS;AACvB,oBAAc,KAAK,KAAK;AAGxB,UAAI,cAAc;AAChB,cAAM,WAAW,KAAK,IAAA,IAAQ,aAAa,WAAW;AAEtD,wBAAgB,CAAA,SAAQ;AACtB,gBAAM,YAAY,IAAI,IAAI,IAAI;AAE9B,qBAAW,MAAM,WAAW;AAE1B,gBAAI,CAAC,GAAG,GAAI;AAEZ,kBAAM,QAAiC;AAAA,cACrC,KAAK,GAAG;AAAA,cACR,KAAK,GAAG;AAAA,cACR,GAAG;AAAA,YAAA;AAGL,kBAAM,WAAW,UAAU,IAAI,GAAG,EAAE,KAAK,CAAA;AACzC,kBAAM,UAAU,CAAC,GAAG,UAAU,KAAK;AAEnC,gBAAI,QAAQ,SAAS,YAAY;AAC/B,wBAAU,IAAI,GAAG,IAAI,QAAQ,MAAM,CAAC,UAAU,CAAC;AAAA,YACjD,OAAO;AACL,wBAAU,IAAI,GAAG,IAAI,OAAO;AAAA,YAC9B;AAAA,UACF;AAEA,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,eAAS,OAAO;AAAA,IAClB,UAAA;AACE,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,cAAc,UAAU,CAAC;AAGrC,YAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,UAAI,YAAY,SAAS;AACvB,sBAAc,YAAY,OAAO;AACjC,oBAAY,UAAU;AAAA,MACxB;AACA;AAAA,IACF;AAEA,iBAAa,UAAU,KAAK,IAAA;AAG5B,mBAAA;AAGA,gBAAY,UAAU,YAAY,gBAAgB,QAAQ;AAE1D,WAAO,MAAM;AACX,UAAI,YAAY,SAAS;AACvB,sBAAc,YAAY,OAAO;AACjC,oBAAY,UAAU;AAAA,MACxB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,UAAU,cAAc,CAAC;AAEtC,QAAM,gBAAgB;AAAA,IACpB,CAAC,OAAe,WAAW,KAAK,CAAA,OAAM,GAAG,OAAO,EAAE;AAAA,IAClD,CAAC,UAAU;AAAA,EAAA;AAGb,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;"}
@@ -1,73 +0,0 @@
1
- import { useState, useRef, useCallback, useEffect } from "react";
2
- const MAX_HISTORY = 100;
3
- function useTelemetry(options) {
4
- const { client, spacecraftId, interval = 2e3, enabled = true } = options;
5
- const [telemetry, setTelemetry] = useState(null);
6
- const [attitude, setAttitude] = useState(null);
7
- const [history, setHistory] = useState([]);
8
- const [isLoading, setIsLoading] = useState(false);
9
- const [error, setError] = useState(null);
10
- const [lastUpdate, setLastUpdate] = useState(null);
11
- const intervalRef = useRef(null);
12
- const fetchTelemetry = useCallback(async () => {
13
- if (!spacecraftId) return;
14
- setIsLoading(true);
15
- setError(null);
16
- try {
17
- const [telemetryData, attitudeData] = await Promise.all([
18
- client.getTelemetry(spacecraftId),
19
- client.getAttitude(spacecraftId)
20
- ]);
21
- setTelemetry(telemetryData);
22
- setAttitude(attitudeData);
23
- setLastUpdate(Date.now());
24
- setHistory((prev) => {
25
- const newHistory = [...prev, telemetryData];
26
- if (newHistory.length > MAX_HISTORY) {
27
- return newHistory.slice(-MAX_HISTORY);
28
- }
29
- return newHistory;
30
- });
31
- } catch (err) {
32
- const message = err instanceof Error ? err.message : "Failed to fetch telemetry";
33
- setError(message);
34
- } finally {
35
- setIsLoading(false);
36
- }
37
- }, [client, spacecraftId]);
38
- useEffect(() => {
39
- if (!enabled || !spacecraftId) {
40
- if (intervalRef.current) {
41
- clearInterval(intervalRef.current);
42
- intervalRef.current = null;
43
- }
44
- return;
45
- }
46
- fetchTelemetry();
47
- intervalRef.current = setInterval(fetchTelemetry, interval);
48
- return () => {
49
- if (intervalRef.current) {
50
- clearInterval(intervalRef.current);
51
- intervalRef.current = null;
52
- }
53
- };
54
- }, [enabled, spacecraftId, interval, fetchTelemetry]);
55
- useEffect(() => {
56
- setHistory([]);
57
- setTelemetry(null);
58
- setAttitude(null);
59
- }, [spacecraftId]);
60
- return {
61
- telemetry,
62
- attitude,
63
- history,
64
- isLoading,
65
- error,
66
- lastUpdate,
67
- refresh: fetchTelemetry
68
- };
69
- }
70
- export {
71
- useTelemetry
72
- };
73
- //# sourceMappingURL=useTelemetry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useTelemetry.js","sources":["../../../src/react/hooks/useTelemetry.ts"],"sourcesContent":["/**\n * @zendir/ui - useTelemetry Hook\n * \n * React hook for fetching and updating spacecraft telemetry data.\n * \n * OPTIONAL DEPENDENCY: Requires @zendir/sdk client instance.\n * The client must be passed as a prop (allows using any compatible client).\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type { TelemetryData } from '../types';\n\n// ============================================================================\n// Types (defined locally to avoid hard dependency on @zendir/sdk)\n// ============================================================================\n\n/** Attitude data from spacecraft */\nexport interface AttitudeData {\n quaternion?: { x: number; y: number; z: number; w: number };\n euler?: { roll: number; pitch: number; yaw: number };\n angularVelocity?: { x: number; y: number; z: number };\n timestamp?: string;\n}\n\n/** Client interface (compatible with @zendir/sdk ZendirClient) */\nexport interface TelemetryClientInterface {\n getTelemetry: (spacecraftId: string) => Promise<unknown>;\n getAttitude: (spacecraftId: string) => Promise<AttitudeData>;\n}\n\n// ============================================================================\n// Hook Types\n// ============================================================================\n\nexport interface UseTelemetryOptions {\n /** Zendir client instance (or any compatible client) */\n client: TelemetryClientInterface;\n /** Spacecraft ID to fetch telemetry for */\n spacecraftId: string;\n /** Polling interval in ms (default: 2000) */\n interval?: number;\n /** Enable polling */\n enabled?: boolean;\n}\n\nexport interface UseTelemetryResult {\n /** Latest telemetry data */\n telemetry: TelemetryData | null;\n /** Latest attitude data */\n attitude: AttitudeData | null;\n /** Telemetry history */\n history: TelemetryData[];\n /** Loading state */\n isLoading: boolean;\n /** Error message */\n error: string | null;\n /** Last update timestamp */\n lastUpdate: number | null;\n /** Manually refresh telemetry */\n refresh: () => Promise<void>;\n}\n\nconst MAX_HISTORY = 100;\n\nexport function useTelemetry(options: UseTelemetryOptions): UseTelemetryResult {\n const { client, spacecraftId, interval = 2000, enabled = true } = options;\n\n const [telemetry, setTelemetry] = useState<TelemetryData | null>(null);\n const [attitude, setAttitude] = useState<AttitudeData | null>(null);\n const [history, setHistory] = useState<TelemetryData[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [lastUpdate, setLastUpdate] = useState<number | null>(null);\n\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const fetchTelemetry = useCallback(async () => {\n if (!spacecraftId) return;\n\n setIsLoading(true);\n setError(null);\n\n try {\n const [telemetryData, attitudeData] = await Promise.all([\n client.getTelemetry(spacecraftId),\n client.getAttitude(spacecraftId),\n ]);\n\n setTelemetry(telemetryData as unknown as TelemetryData);\n setAttitude(attitudeData);\n setLastUpdate(Date.now());\n\n // Add to history\n setHistory(prev => {\n const newHistory = [...prev, telemetryData as unknown as TelemetryData];\n if (newHistory.length > MAX_HISTORY) {\n return newHistory.slice(-MAX_HISTORY);\n }\n return newHistory;\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to fetch telemetry';\n setError(message);\n } finally {\n setIsLoading(false);\n }\n }, [client, spacecraftId]);\n\n // Set up polling\n useEffect(() => {\n if (!enabled || !spacecraftId) {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n return;\n }\n\n // Initial fetch\n fetchTelemetry();\n\n // Set up polling\n intervalRef.current = setInterval(fetchTelemetry, interval);\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [enabled, spacecraftId, interval, fetchTelemetry]);\n\n // Clear history when spacecraft changes\n useEffect(() => {\n setHistory([]);\n setTelemetry(null);\n setAttitude(null);\n }, [spacecraftId]);\n\n return {\n telemetry,\n attitude,\n history,\n isLoading,\n error,\n lastUpdate,\n refresh: fetchTelemetry,\n };\n}\n\nexport default useTelemetry;\n\n\n\n\n"],"names":[],"mappings":";AA8DA,MAAM,cAAc;AAEb,SAAS,aAAa,SAAkD;AAC7E,QAAM,EAAE,QAAQ,cAAc,WAAW,KAAM,UAAU,SAAS;AAElE,QAAM,CAAC,WAAW,YAAY,IAAI,SAA+B,IAAI;AACrE,QAAM,CAAC,UAAU,WAAW,IAAI,SAA8B,IAAI;AAClE,QAAM,CAAC,SAAS,UAAU,IAAI,SAA0B,CAAA,CAAE;AAC1D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAEhE,QAAM,cAAc,OAA8C,IAAI;AAEtE,QAAM,iBAAiB,YAAY,YAAY;AAC7C,QAAI,CAAC,aAAc;AAEnB,iBAAa,IAAI;AACjB,aAAS,IAAI;AAEb,QAAI;AACF,YAAM,CAAC,eAAe,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,QACtD,OAAO,aAAa,YAAY;AAAA,QAChC,OAAO,YAAY,YAAY;AAAA,MAAA,CAChC;AAED,mBAAa,aAAyC;AACtD,kBAAY,YAAY;AACxB,oBAAc,KAAK,KAAK;AAGxB,iBAAW,CAAA,SAAQ;AACjB,cAAM,aAAa,CAAC,GAAG,MAAM,aAAyC;AACtE,YAAI,WAAW,SAAS,aAAa;AACnC,iBAAO,WAAW,MAAM,CAAC,WAAW;AAAA,QACtC;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,eAAS,OAAO;AAAA,IAClB,UAAA;AACE,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,YAAY,CAAC;AAGzB,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,cAAc;AAC7B,UAAI,YAAY,SAAS;AACvB,sBAAc,YAAY,OAAO;AACjC,oBAAY,UAAU;AAAA,MACxB;AACA;AAAA,IACF;AAGA,mBAAA;AAGA,gBAAY,UAAU,YAAY,gBAAgB,QAAQ;AAE1D,WAAO,MAAM;AACX,UAAI,YAAY,SAAS;AACvB,sBAAc,YAAY,OAAO;AACjC,oBAAY,UAAU;AAAA,MACxB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,cAAc,UAAU,cAAc,CAAC;AAGpD,YAAU,MAAM;AACd,eAAW,CAAA,CAAE;AACb,iBAAa,IAAI;AACjB,gBAAY,IAAI;AAAA,EAClB,GAAG,CAAC,YAAY,CAAC;AAEjB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;"}
@@ -1,148 +0,0 @@
1
- import { useState, useRef, useEffect, useCallback } from "react";
2
- let loadState = "pending";
3
- let sdkModule = null;
4
- const subscribers = /* @__PURE__ */ new Set();
5
- function subscribe(callback) {
6
- subscribers.add(callback);
7
- return () => subscribers.delete(callback);
8
- }
9
- function notifySubscribers() {
10
- subscribers.forEach((cb) => cb());
11
- }
12
- async function loadZendirSDK() {
13
- if (loadState === "loaded") {
14
- return sdkModule;
15
- }
16
- if (loadState === "failed") {
17
- return null;
18
- }
19
- if (loadState === "loading") {
20
- return new Promise((resolve) => {
21
- const unsub = subscribe(() => {
22
- unsub();
23
- resolve(sdkModule);
24
- });
25
- });
26
- }
27
- loadState = "loading";
28
- try {
29
- const mod = await import(
30
- /* @vite-ignore */
31
- "@zendir/sdk"
32
- );
33
- sdkModule = { getZendirClient: mod.getZendirClient };
34
- loadState = "loaded";
35
- notifySubscribers();
36
- return sdkModule;
37
- } catch (err) {
38
- loadState = "failed";
39
- notifySubscribers();
40
- return null;
41
- }
42
- }
43
- function useZendirSession(options = {}) {
44
- const { apiKey, baseUrl, autoConnect = false, debug = false } = options;
45
- const [sdkLoaded, setSdkLoaded] = useState(loadState === "loaded");
46
- const [sdkFailed, setSdkFailed] = useState(loadState === "failed");
47
- const clientRef = useRef(null);
48
- const [session, setSession] = useState(null);
49
- const [simulation, setSimulation] = useState(null);
50
- const [isLoading, setIsLoading] = useState(false);
51
- const [error, setError] = useState(null);
52
- useEffect(() => {
53
- let mounted = true;
54
- const unsubscribe = subscribe(() => {
55
- if (mounted) {
56
- setSdkLoaded(loadState === "loaded");
57
- setSdkFailed(loadState === "failed");
58
- }
59
- });
60
- if (loadState === "pending") {
61
- loadZendirSDK().then((mod) => {
62
- if (mounted && mod) {
63
- clientRef.current = mod.getZendirClient({ apiKey, baseUrl, debug });
64
- }
65
- });
66
- } else if (loadState === "loaded" && sdkModule) {
67
- clientRef.current = sdkModule.getZendirClient({ apiKey, baseUrl, debug });
68
- }
69
- return () => {
70
- mounted = false;
71
- unsubscribe();
72
- };
73
- }, [apiKey, baseUrl, debug]);
74
- useEffect(() => {
75
- if (sdkFailed) {
76
- setError("@zendir/sdk is not installed. Install with: npm install @zendir/sdk");
77
- }
78
- }, [sdkFailed]);
79
- const connect = useCallback(async () => {
80
- if (!clientRef.current) {
81
- setError("@zendir/sdk is not available");
82
- return;
83
- }
84
- setIsLoading(true);
85
- setError(null);
86
- try {
87
- const sessionInfo = await clientRef.current.createSession();
88
- setSession(sessionInfo);
89
- } catch (err) {
90
- const message = err instanceof Error ? err.message : "Failed to connect";
91
- setError(message);
92
- throw err;
93
- } finally {
94
- setIsLoading(false);
95
- }
96
- }, []);
97
- const createSimulation = useCallback(async (params) => {
98
- if (!clientRef.current) {
99
- setError("@zendir/sdk is not available");
100
- return;
101
- }
102
- setIsLoading(true);
103
- setError(null);
104
- try {
105
- const simInfo = await clientRef.current.createSimulation({
106
- name: params.name,
107
- spacecraft: params.spacecraft
108
- });
109
- setSimulation(simInfo);
110
- } catch (err) {
111
- const message = err instanceof Error ? err.message : "Failed to create simulation";
112
- setError(message);
113
- throw err;
114
- } finally {
115
- setIsLoading(false);
116
- }
117
- }, []);
118
- const disconnect = useCallback(() => {
119
- if (clientRef.current) {
120
- clientRef.current.clearSession();
121
- }
122
- setSession(null);
123
- setSimulation(null);
124
- setError(null);
125
- }, []);
126
- useEffect(() => {
127
- if (autoConnect && apiKey && sdkLoaded && clientRef.current) {
128
- connect().catch(() => {
129
- });
130
- }
131
- }, [autoConnect, apiKey, sdkLoaded, connect]);
132
- return {
133
- session,
134
- simulation,
135
- isLoading,
136
- error,
137
- isConnected: !!session && session.status === "active",
138
- isSdkAvailable: sdkLoaded && !sdkFailed,
139
- client: clientRef.current,
140
- connect,
141
- createSimulation,
142
- disconnect
143
- };
144
- }
145
- export {
146
- useZendirSession
147
- };
148
- //# sourceMappingURL=useZendirSession.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useZendirSession.js","sources":["../../../src/react/hooks/useZendirSession.ts"],"sourcesContent":["/**\n * @zendir/ui - useZendirSession Hook\n * \n * React hook for managing Zendir API session lifecycle.\n * \n * OPTIONAL DEPENDENCY: Requires @zendir/sdk to be installed.\n * If not installed, the hook returns an error state.\n * \n * @example\n * ```tsx\n * import { useZendirSession } from '@zendir/ui/react';\n * \n * function App() {\n * const { session, connect, isLoading, error } = useZendirSession({\n * apiKey: 'your-api-key',\n * autoConnect: true,\n * });\n * \n * if (error) return <div>Error: {error}</div>;\n * if (isLoading) return <div>Connecting...</div>;\n * if (!session) return <button onClick={connect}>Connect</button>;\n * \n * return <div>Connected: {session.id}</div>;\n * }\n * ```\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\n\n// ============================================================================\n// Types (defined locally to avoid hard dependency on @zendir/sdk)\n// ============================================================================\n\n/** Session info from Zendir API */\nexport interface SessionInfo {\n id: string;\n status: 'active' | 'expired' | 'error';\n createdAt?: string;\n expiresAt?: string;\n}\n\n/** Simulation info from Zendir API */\nexport interface SimulationInfo {\n id: string;\n name?: string;\n status: 'running' | 'paused' | 'stopped';\n spacecraftCount?: number;\n}\n\n/** Zendir client interface (matches @zendir/sdk) */\nexport interface ZendirClientInterface {\n createSession: () => Promise<SessionInfo>;\n createSimulation: (params: {\n name?: string;\n spacecraft: Array<{\n name: string;\n tle?: { line1: string; line2: string };\n catalogNumber?: number;\n }>;\n }) => Promise<SimulationInfo>;\n clearSession: () => void;\n}\n\n// ============================================================================\n// Dynamic SDK Loading\n// ============================================================================\n\ntype LoadState = 'pending' | 'loading' | 'loaded' | 'failed';\nlet loadState: LoadState = 'pending';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet sdkModule: { getZendirClient: (options?: any) => any } | null = null;\nconst subscribers = new Set<() => void>();\n\nfunction subscribe(callback: () => void) {\n subscribers.add(callback);\n return () => subscribers.delete(callback);\n}\n\nfunction notifySubscribers() {\n subscribers.forEach(cb => cb());\n}\n\n/**\n * Dynamically load @zendir/sdk\n * Returns null if not available\n */\nasync function loadZendirSDK(): Promise<typeof sdkModule> {\n if (loadState === 'loaded') {\n return sdkModule;\n }\n \n if (loadState === 'failed') {\n return null;\n }\n \n if (loadState === 'loading') {\n // Wait for the existing load to complete\n return new Promise(resolve => {\n const unsub = subscribe(() => {\n unsub();\n resolve(sdkModule);\n });\n });\n }\n \n loadState = 'loading';\n \n try {\n // @vite-ignore: optional peer — do not resolve at build time when SDK is not installed\n const mod = await import(/* @vite-ignore */ '@zendir/sdk');\n sdkModule = { getZendirClient: mod.getZendirClient };\n loadState = 'loaded';\n notifySubscribers();\n return sdkModule;\n } catch (err) {\n loadState = 'failed';\n notifySubscribers();\n return null;\n }\n}\n\n// ============================================================================\n// Hook Types\n// ============================================================================\n\nexport interface UseZendirSessionOptions {\n /** API key (optional - can be set via client) */\n apiKey?: string;\n /** Base URL override */\n baseUrl?: string;\n /** Auto-connect on mount */\n autoConnect?: boolean;\n /** Enable debug logging */\n debug?: boolean;\n}\n\nexport interface UseZendirSessionResult {\n /** Current session info */\n session: SessionInfo | null;\n /** Current simulation info */\n simulation: SimulationInfo | null;\n /** Loading state */\n isLoading: boolean;\n /** Error message */\n error: string | null;\n /** Connected status */\n isConnected: boolean;\n /** Whether SDK is available */\n isSdkAvailable: boolean;\n /** Zendir client instance (null if SDK not loaded) */\n client: ZendirClientInterface | null;\n /** Connect/create session */\n connect: () => Promise<void>;\n /** Create simulation */\n createSimulation: (params: {\n name?: string;\n spacecraft: Array<{\n name: string;\n tle?: { line1: string; line2: string };\n catalogNumber?: number;\n }>;\n }) => Promise<void>;\n /** Disconnect/clear session */\n disconnect: () => void;\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\nexport function useZendirSession(\n options: UseZendirSessionOptions = {}\n): UseZendirSessionResult {\n const { apiKey, baseUrl, autoConnect = false, debug = false } = options;\n\n const [sdkLoaded, setSdkLoaded] = useState(loadState === 'loaded');\n const [sdkFailed, setSdkFailed] = useState(loadState === 'failed');\n const clientRef = useRef<ZendirClientInterface | null>(null);\n \n const [session, setSession] = useState<SessionInfo | null>(null);\n const [simulation, setSimulation] = useState<SimulationInfo | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Load SDK on mount\n useEffect(() => {\n let mounted = true;\n \n const unsubscribe = subscribe(() => {\n if (mounted) {\n setSdkLoaded(loadState === 'loaded');\n setSdkFailed(loadState === 'failed');\n }\n });\n \n if (loadState === 'pending') {\n loadZendirSDK().then((mod) => {\n if (mounted && mod) {\n clientRef.current = mod.getZendirClient({ apiKey, baseUrl, debug });\n }\n });\n } else if (loadState === 'loaded' && sdkModule) {\n clientRef.current = sdkModule.getZendirClient({ apiKey, baseUrl, debug });\n }\n \n return () => {\n mounted = false;\n unsubscribe();\n };\n }, [apiKey, baseUrl, debug]);\n\n // Update error if SDK failed to load\n useEffect(() => {\n if (sdkFailed) {\n setError('@zendir/sdk is not installed. Install with: npm install @zendir/sdk');\n }\n }, [sdkFailed]);\n\n const connect = useCallback(async () => {\n if (!clientRef.current) {\n setError('@zendir/sdk is not available');\n return;\n }\n \n setIsLoading(true);\n setError(null);\n\n try {\n const sessionInfo = await clientRef.current.createSession();\n setSession(sessionInfo);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to connect';\n setError(message);\n throw err;\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n const createSimulation = useCallback(async (params: {\n name?: string;\n spacecraft: Array<{\n name: string;\n tle?: { line1: string; line2: string };\n catalogNumber?: number;\n }>;\n }) => {\n if (!clientRef.current) {\n setError('@zendir/sdk is not available');\n return;\n }\n \n setIsLoading(true);\n setError(null);\n\n try {\n const simInfo = await clientRef.current.createSimulation({\n name: params.name,\n spacecraft: params.spacecraft,\n });\n setSimulation(simInfo);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to create simulation';\n setError(message);\n throw err;\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n const disconnect = useCallback(() => {\n if (clientRef.current) {\n clientRef.current.clearSession();\n }\n setSession(null);\n setSimulation(null);\n setError(null);\n }, []);\n\n // Auto-connect on mount if enabled and SDK is loaded\n useEffect(() => {\n if (autoConnect && apiKey && sdkLoaded && clientRef.current) {\n connect().catch(() => {\n // Error already set in state\n });\n }\n }, [autoConnect, apiKey, sdkLoaded, connect]);\n\n return {\n session,\n simulation,\n isLoading,\n error,\n isConnected: !!session && session.status === 'active',\n isSdkAvailable: sdkLoaded && !sdkFailed,\n client: clientRef.current,\n connect,\n createSimulation,\n disconnect,\n };\n}\n\n/**\n * Check if @zendir/sdk is available\n */\nexport function isZendirSdkAvailable(): boolean {\n return loadState === 'loaded' && sdkModule !== null;\n}\n\n/**\n * Preload the @zendir/sdk module\n * Call this early in your app to ensure SDK is ready\n */\nexport async function preloadZendirSdk(): Promise<boolean> {\n const mod = await loadZendirSDK();\n return mod !== null;\n}\n\nexport default useZendirSession;\n"],"names":[],"mappings":"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"}