vg-x07df 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.
- package/.azure-pipelines/publish-public.yml +37 -0
- package/.azure-pipelines/publish.yml +39 -0
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/AUTO_JOIN_GUIDE.md +411 -0
- package/README.md +215 -0
- package/Screenshot 2025-09-24 at 14.34.48.png +0 -0
- package/Screenshot 2025-10-04 at 12.58.54.png +0 -0
- package/biome.json +48 -0
- package/examples/demo/.env.example +19 -0
- package/examples/demo/CHANGELOG.md +22 -0
- package/examples/demo/README.md +72 -0
- package/examples/demo/eslint.config.js +23 -0
- package/examples/demo/index.html +13 -0
- package/examples/demo/package.json +34 -0
- package/examples/demo/pnpm-lock.yaml +2098 -0
- package/examples/demo/pnpm-workspace.yaml +1 -0
- package/examples/demo/public/vite.svg +1 -0
- package/examples/demo/src/App.css +52 -0
- package/examples/demo/src/App.tsx +176 -0
- package/examples/demo/src/assets/react.svg +1 -0
- package/examples/demo/src/components/auth/LoginForm.css +144 -0
- package/examples/demo/src/components/auth/LoginForm.tsx +80 -0
- package/examples/demo/src/components/calling/AutoJoinSettings.tsx +213 -0
- package/examples/demo/src/components/calling/AutoJoinStatus.tsx +72 -0
- package/examples/demo/src/components/calling/CallInitiator.css +258 -0
- package/examples/demo/src/components/calling/CallInitiator.tsx +142 -0
- package/examples/demo/src/components/calling/CallNotifications.css +119 -0
- package/examples/demo/src/components/calling/CallNotifications.tsx +108 -0
- package/examples/demo/src/components/calling/IncomingCallModal.css +192 -0
- package/examples/demo/src/components/calling/IncomingCallModal.tsx +78 -0
- package/examples/demo/src/components/calling/MinimizedCall.css +156 -0
- package/examples/demo/src/components/calling/MinimizedCall.tsx +78 -0
- package/examples/demo/src/components/conference/ConferenceHeader.css +265 -0
- package/examples/demo/src/components/conference/ConferenceHeader.tsx +78 -0
- package/examples/demo/src/components/conference/EnhancedControlBar.css +356 -0
- package/examples/demo/src/components/conference/EnhancedControlBar.tsx +262 -0
- package/examples/demo/src/components/conference/PaginationControls.css +67 -0
- package/examples/demo/src/components/conference/PaginationControls.tsx +64 -0
- package/examples/demo/src/components/conference/ParticipantGrid.css +153 -0
- package/examples/demo/src/components/conference/ParticipantGrid.tsx +87 -0
- package/examples/demo/src/components/conference/ParticipantTile.css +210 -0
- package/examples/demo/src/components/conference/ParticipantTile.tsx +114 -0
- package/examples/demo/src/components/conference/VideoConference.css +214 -0
- package/examples/demo/src/components/conference/VideoConference.tsx +93 -0
- package/examples/demo/src/contexts/AuthContext.tsx +105 -0
- package/examples/demo/src/hooks/useAuth.ts +5 -0
- package/examples/demo/src/hooks/useCallTimer.ts +42 -0
- package/examples/demo/src/index.css +68 -0
- package/examples/demo/src/main.tsx +10 -0
- package/examples/demo/src/services/auth.service.ts +153 -0
- package/examples/demo/src/types/auth.types.ts +31 -0
- package/examples/demo/tsconfig.app.json +28 -0
- package/examples/demo/tsconfig.json +7 -0
- package/examples/demo/tsconfig.node.json +26 -0
- package/examples/demo/vite.config.ts +15 -0
- package/images/callpad-without-ai.png +0 -0
- package/package.json +28 -0
- package/packages/sdk/CHANGELOG.md +33 -0
- package/packages/sdk/LICENSE +21 -0
- package/packages/sdk/README.md +97 -0
- package/packages/sdk/documentation.md +1132 -0
- package/packages/sdk/openapi-ts.config.ts +7 -0
- package/packages/sdk/package.json +88 -0
- package/packages/sdk/src/core/auth.manager.ts +52 -0
- package/packages/sdk/src/core/events/event-bus.ts +301 -0
- package/packages/sdk/src/core/events/index.ts +8 -0
- package/packages/sdk/src/core/events/types.ts +165 -0
- package/packages/sdk/src/core/index.ts +3 -0
- package/packages/sdk/src/core/signal/api.config.ts +49 -0
- package/packages/sdk/src/core/signal/index.ts +16 -0
- package/packages/sdk/src/core/signal/signal.client.ts +101 -0
- package/packages/sdk/src/core/signal/types.ts +110 -0
- package/packages/sdk/src/core/socketio/handlers/base.handler.ts +212 -0
- package/packages/sdk/src/core/socketio/handlers/call-accepted.handler.ts +34 -0
- package/packages/sdk/src/core/socketio/handlers/call-canceled.handler.ts +34 -0
- package/packages/sdk/src/core/socketio/handlers/call-declined.handler.ts +29 -0
- package/packages/sdk/src/core/socketio/handlers/call-ended.handler.ts +40 -0
- package/packages/sdk/src/core/socketio/handlers/call-incoming.handler.ts +72 -0
- package/packages/sdk/src/core/socketio/handlers/call-join-info.handler.ts +181 -0
- package/packages/sdk/src/core/socketio/handlers/call-participant-joined.handler.ts +42 -0
- package/packages/sdk/src/core/socketio/handlers/call-participant-joining.handler.ts +42 -0
- package/packages/sdk/src/core/socketio/handlers/call-timeout.handler.ts +31 -0
- package/packages/sdk/src/core/socketio/handlers/handler.registry.ts +62 -0
- package/packages/sdk/src/core/socketio/handlers/index.ts +21 -0
- package/packages/sdk/src/core/socketio/handlers/participant-left.handler.ts +37 -0
- package/packages/sdk/src/core/socketio/handlers/schema.ts +130 -0
- package/packages/sdk/src/core/socketio/index.ts +5 -0
- package/packages/sdk/src/core/socketio/socket.manager.ts +187 -0
- package/packages/sdk/src/core/socketio/types.ts +14 -0
- package/packages/sdk/src/core/types.ts +23 -0
- package/packages/sdk/src/generated/api/core/ApiError.ts +21 -0
- package/packages/sdk/src/generated/api/core/ApiRequestOptions.ts +13 -0
- package/packages/sdk/src/generated/api/core/ApiResult.ts +7 -0
- package/packages/sdk/src/generated/api/core/CancelablePromise.ts +126 -0
- package/packages/sdk/src/generated/api/core/OpenAPI.ts +55 -0
- package/packages/sdk/src/generated/api/core/request.ts +339 -0
- package/packages/sdk/src/generated/api/index.ts +5 -0
- package/packages/sdk/src/generated/api/models.ts +219 -0
- package/packages/sdk/src/generated/api/services.ts +225 -0
- package/packages/sdk/src/hooks/index.ts +21 -0
- package/packages/sdk/src/hooks/useAutoJoin.ts +66 -0
- package/packages/sdk/src/hooks/useCallActions.ts +28 -0
- package/packages/sdk/src/hooks/useCallQuality.ts +416 -0
- package/packages/sdk/src/hooks/useCallState.ts +23 -0
- package/packages/sdk/src/hooks/useConnection.ts +15 -0
- package/packages/sdk/src/hooks/useDevices.ts +296 -0
- package/packages/sdk/src/hooks/useErrorRecovery.ts +299 -0
- package/packages/sdk/src/hooks/useErrors.ts +84 -0
- package/packages/sdk/src/hooks/useEvent.ts +188 -0
- package/packages/sdk/src/hooks/useMediaControls.ts +215 -0
- package/packages/sdk/src/hooks/useParticipantStatus.ts +318 -0
- package/packages/sdk/src/hooks/useParticipants.ts +111 -0
- package/packages/sdk/src/index.ts +66 -0
- package/packages/sdk/src/livekit/constants.ts +76 -0
- package/packages/sdk/src/livekit/device.manager.ts +172 -0
- package/packages/sdk/src/livekit/error-classifier.ts +155 -0
- package/packages/sdk/src/livekit/events/eventBridge.ts +371 -0
- package/packages/sdk/src/livekit/events/trackRegistry.ts +114 -0
- package/packages/sdk/src/livekit/index.ts +49 -0
- package/packages/sdk/src/livekit/livekit.service.ts +110 -0
- package/packages/sdk/src/livekit/media.controls.ts +315 -0
- package/packages/sdk/src/livekit/room.manager.ts +79 -0
- package/packages/sdk/src/livekit/track.utils.ts +230 -0
- package/packages/sdk/src/livekit/types.ts +135 -0
- package/packages/sdk/src/provider/RtcProvider.tsx +78 -0
- package/packages/sdk/src/services/call-actions.ts +260 -0
- package/packages/sdk/src/services/error-recovery.ts +461 -0
- package/packages/sdk/src/services/index.ts +2 -0
- package/packages/sdk/src/services/sdk-builder.ts +104 -0
- package/packages/sdk/src/state/errors.ts +163 -0
- package/packages/sdk/src/state/selectors.ts +28 -0
- package/packages/sdk/src/state/store.ts +36 -0
- package/packages/sdk/src/state/types.ts +151 -0
- package/packages/sdk/src/utils/logger.ts +183 -0
- package/packages/sdk/tsconfig.json +49 -0
- package/packages/sdk/tsup.config.ts +51 -0
- package/pnpm-workspace.yaml +4 -0
- package/tsconfig.base.json +19 -0
- package/turbo.json +34 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { LogLevel } from "../utils/logger";
|
|
2
|
+
import { rtcStore } from "./store";
|
|
3
|
+
import type { RtcError } from "./types";
|
|
4
|
+
|
|
5
|
+
// Re-export RtcError type for external consumption
|
|
6
|
+
export type { RtcError } from "./types";
|
|
7
|
+
|
|
8
|
+
export type ErrorCode =
|
|
9
|
+
| "NETWORK"
|
|
10
|
+
| "SOCKET_PAYLOAD"
|
|
11
|
+
| "JOIN_FLOW"
|
|
12
|
+
| "LIVEKIT_CONNECT"
|
|
13
|
+
| "LIVEKIT_MEDIA"
|
|
14
|
+
| "MEDIA_PERMISSION"
|
|
15
|
+
| "DEVICE_SWITCH"
|
|
16
|
+
| "API_ERROR"
|
|
17
|
+
| "UNEXPECTED";
|
|
18
|
+
|
|
19
|
+
export function pushError(
|
|
20
|
+
code: ErrorCode,
|
|
21
|
+
message: string,
|
|
22
|
+
context?: any,
|
|
23
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
24
|
+
): void {
|
|
25
|
+
const error: RtcError = {
|
|
26
|
+
code,
|
|
27
|
+
message,
|
|
28
|
+
timestamp: Date.now(),
|
|
29
|
+
context,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Log the error if logger is provided
|
|
33
|
+
logger?.("error", message, { code, context });
|
|
34
|
+
rtcStore.getState().addError(error);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Clear errors from the store
|
|
39
|
+
* @param predicate Optional filter function - if provided, only matching errors are removed
|
|
40
|
+
*/
|
|
41
|
+
export function clearErrors(predicate?: (error: RtcError) => boolean): void {
|
|
42
|
+
rtcStore.getState().patch((state) => {
|
|
43
|
+
if (predicate) {
|
|
44
|
+
// Remove only errors matching the predicate
|
|
45
|
+
state.errors = state.errors.filter((error) => !predicate(error));
|
|
46
|
+
} else {
|
|
47
|
+
// Clear all errors
|
|
48
|
+
state.errors = [];
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Convenience helpers for common error scenarios
|
|
54
|
+
export function pushSocketValidationError(
|
|
55
|
+
eventType: string,
|
|
56
|
+
issues: any,
|
|
57
|
+
payload?: any,
|
|
58
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
59
|
+
): void {
|
|
60
|
+
pushError(
|
|
61
|
+
"SOCKET_PAYLOAD",
|
|
62
|
+
`Invalid ${eventType} event payload`,
|
|
63
|
+
{ eventType, issues, payload },
|
|
64
|
+
logger
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function pushIdentityGuardError(
|
|
69
|
+
reason: string,
|
|
70
|
+
expected?: string,
|
|
71
|
+
received?: string,
|
|
72
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
73
|
+
): void {
|
|
74
|
+
pushError(
|
|
75
|
+
"JOIN_FLOW", // Use new error code
|
|
76
|
+
`Identity guard failed: ${reason}`,
|
|
77
|
+
{ expected, received },
|
|
78
|
+
logger
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function pushLiveKitConnectError(
|
|
83
|
+
message: string,
|
|
84
|
+
error: unknown,
|
|
85
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
86
|
+
): void {
|
|
87
|
+
pushError(
|
|
88
|
+
"LIVEKIT_CONNECT",
|
|
89
|
+
`LiveKit connection failed: ${message}`,
|
|
90
|
+
{ originalError: error },
|
|
91
|
+
logger
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function pushStaleEventError(
|
|
96
|
+
eventType: string,
|
|
97
|
+
reason: string,
|
|
98
|
+
context?: any,
|
|
99
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
100
|
+
): void {
|
|
101
|
+
pushError(
|
|
102
|
+
"JOIN_FLOW", // Use new error code
|
|
103
|
+
`Ignored stale ${eventType} event: ${reason}`,
|
|
104
|
+
context,
|
|
105
|
+
logger
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function pushApiError(
|
|
110
|
+
operation: string,
|
|
111
|
+
error: unknown,
|
|
112
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
113
|
+
): void {
|
|
114
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
115
|
+
pushError(
|
|
116
|
+
"API_ERROR",
|
|
117
|
+
`API ${operation} failed: ${errorMessage}`,
|
|
118
|
+
{ operation, originalError: error },
|
|
119
|
+
logger
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function pushNetworkError(
|
|
124
|
+
operation: string,
|
|
125
|
+
error: unknown,
|
|
126
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
127
|
+
): void {
|
|
128
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
129
|
+
pushError(
|
|
130
|
+
"NETWORK",
|
|
131
|
+
`Network error during ${operation}: ${errorMessage}`,
|
|
132
|
+
{ operation, originalError: error },
|
|
133
|
+
logger
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function pushMediaPermissionError(
|
|
138
|
+
device: string,
|
|
139
|
+
error?: unknown,
|
|
140
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
141
|
+
): void {
|
|
142
|
+
pushError(
|
|
143
|
+
"MEDIA_PERMISSION",
|
|
144
|
+
`${device} permission denied`,
|
|
145
|
+
{ device, originalError: error },
|
|
146
|
+
logger
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function pushDeviceError(
|
|
151
|
+
operation: string,
|
|
152
|
+
device: string,
|
|
153
|
+
error: unknown,
|
|
154
|
+
logger?: (level: LogLevel, message: string, meta?: any) => void
|
|
155
|
+
): void {
|
|
156
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
157
|
+
pushError(
|
|
158
|
+
"DEVICE_SWITCH",
|
|
159
|
+
`Failed to ${operation} ${device}: ${errorMessage}`,
|
|
160
|
+
{ operation, device, originalError: error },
|
|
161
|
+
logger
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useRtcStore } from "./store";
|
|
2
|
+
import type { Participant } from "./types";
|
|
3
|
+
|
|
4
|
+
export function useParticipants(): Participant[] {
|
|
5
|
+
return useRtcStore((state) => Object.values(state.room.participants));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function useParticipant(id: string): Participant | undefined {
|
|
9
|
+
return useRtcStore((state) => state.room.participants[id]);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useRingingParticipants(): Participant[] {
|
|
13
|
+
return useRtcStore((state) =>
|
|
14
|
+
Object.values(state.room.participants).filter(
|
|
15
|
+
(p) => p.callState === "RINGING"
|
|
16
|
+
)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function useLocalParticipant(): Participant | undefined {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useSpeakingParticipants(): Participant[] {
|
|
25
|
+
return useRtcStore((state) =>
|
|
26
|
+
Object.values(state.room.participants).filter((p) => p.isSpeaking)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { create } from "zustand";
|
|
2
|
+
import { immer } from "zustand/middleware/immer";
|
|
3
|
+
import type { RtcError, RtcState } from "./types";
|
|
4
|
+
import { defaultState } from "./types";
|
|
5
|
+
|
|
6
|
+
type Actions = {
|
|
7
|
+
patch: (fn: (draft: RtcState) => void) => void;
|
|
8
|
+
reset: () => void;
|
|
9
|
+
addError: (error: RtcError) => void;
|
|
10
|
+
clearErrors: () => void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const useRtcStore = create<RtcState & Actions>()(
|
|
14
|
+
immer((set) => ({
|
|
15
|
+
...defaultState,
|
|
16
|
+
|
|
17
|
+
patch: (fn) =>
|
|
18
|
+
set((state) => {
|
|
19
|
+
fn(state);
|
|
20
|
+
}),
|
|
21
|
+
|
|
22
|
+
reset: () => set(() => defaultState),
|
|
23
|
+
|
|
24
|
+
addError: (error) =>
|
|
25
|
+
set((state) => {
|
|
26
|
+
state.errors.push(error);
|
|
27
|
+
}),
|
|
28
|
+
|
|
29
|
+
clearErrors: () =>
|
|
30
|
+
set((state) => {
|
|
31
|
+
state.errors = [];
|
|
32
|
+
}),
|
|
33
|
+
}))
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export const rtcStore = useRtcStore;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
export type SessionStatus =
|
|
2
|
+
| "IDLE"
|
|
3
|
+
| "CALLING" // Caller initiated, waiting for acceptance
|
|
4
|
+
| "RINGING" // Incoming call (callee perspective)
|
|
5
|
+
| "ACCEPTED" // Call accepted but not yet joined media
|
|
6
|
+
| "AWAITING_JOIN_INFO" // Waiting for join credentials
|
|
7
|
+
| "READY_TO_JOIN" // Has join-info but not connected to media
|
|
8
|
+
| "CONNECTING" // Joining LiveKit room
|
|
9
|
+
| "ACTIVE" // Successfully connected to media session
|
|
10
|
+
| "ENDED";
|
|
11
|
+
|
|
12
|
+
// Unified Participant interface - combines all participant data
|
|
13
|
+
export interface Participant {
|
|
14
|
+
id: string;
|
|
15
|
+
// Profile data
|
|
16
|
+
firstName?: string;
|
|
17
|
+
lastName?: string;
|
|
18
|
+
avatarUrl?: string;
|
|
19
|
+
// Call state
|
|
20
|
+
role: "CALLER" | "CALLEE" | "HOST" | "MEMBER";
|
|
21
|
+
callState: "INVITED" | "RINGING" | "JOINED" | "LEFT";
|
|
22
|
+
// Media state
|
|
23
|
+
audioEnabled: boolean;
|
|
24
|
+
videoEnabled: boolean;
|
|
25
|
+
isSpeaking: boolean;
|
|
26
|
+
connectionQuality?: "excellent" | "good" | "poor" | "lost" | "unknown";
|
|
27
|
+
// Timestamps
|
|
28
|
+
invitedAt?: number;
|
|
29
|
+
joinedAt?: number;
|
|
30
|
+
leftAt?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type PermissionStatus = "granted" | "denied" | "prompt" | "unknown";
|
|
34
|
+
|
|
35
|
+
export interface DeviceState {
|
|
36
|
+
mics: MediaDeviceInfo[];
|
|
37
|
+
cams: MediaDeviceInfo[];
|
|
38
|
+
speakers: MediaDeviceInfo[];
|
|
39
|
+
selected: {
|
|
40
|
+
micId: string | undefined;
|
|
41
|
+
camId: string | undefined;
|
|
42
|
+
speakerId: string | undefined;
|
|
43
|
+
};
|
|
44
|
+
permissions: {
|
|
45
|
+
camera: PermissionStatus;
|
|
46
|
+
microphone: PermissionStatus;
|
|
47
|
+
};
|
|
48
|
+
isEnumerating: boolean;
|
|
49
|
+
lastEnumeratedAt: number | undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface IncomingCallInfo {
|
|
53
|
+
callId: string;
|
|
54
|
+
caller: {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
avatarUrl: string | undefined;
|
|
58
|
+
};
|
|
59
|
+
type: "AUDIO" | "VIDEO";
|
|
60
|
+
timestamp: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface LiveKitJoinInfo {
|
|
64
|
+
token: string;
|
|
65
|
+
roomName: string;
|
|
66
|
+
callId: string;
|
|
67
|
+
url?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface RtcError {
|
|
71
|
+
code: string;
|
|
72
|
+
message: string;
|
|
73
|
+
timestamp: number;
|
|
74
|
+
context?: any;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type AutoJoinStatus = "idle" | "pending" | "retrying" | "succeeded" | "failed";
|
|
78
|
+
|
|
79
|
+
export interface AutoJoinState {
|
|
80
|
+
status: AutoJoinStatus;
|
|
81
|
+
attempt: number;
|
|
82
|
+
maxAttempts: number;
|
|
83
|
+
lastError?: string;
|
|
84
|
+
startedAt?: number;
|
|
85
|
+
completedAt?: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface RtcState {
|
|
89
|
+
session: {
|
|
90
|
+
id?: string;
|
|
91
|
+
status: SessionStatus;
|
|
92
|
+
mode?: "AUDIO" | "VIDEO";
|
|
93
|
+
livekitInfo?: LiveKitJoinInfo;
|
|
94
|
+
// Identity context: how did I get into this call?
|
|
95
|
+
myRole?: "CALLER" | "CALLEE";
|
|
96
|
+
initiatedByMe: boolean;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
room: {
|
|
100
|
+
participants: Record<string, Participant>; // Single unified record
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
local: {
|
|
104
|
+
audioEnabled: boolean;
|
|
105
|
+
videoEnabled: boolean;
|
|
106
|
+
screenEnabled: boolean;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
connection: {
|
|
110
|
+
connected: boolean;
|
|
111
|
+
reconnecting: boolean;
|
|
112
|
+
quality?: "excellent" | "good" | "poor" | "lost";
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
autoJoin: AutoJoinState;
|
|
116
|
+
devices: DeviceState;
|
|
117
|
+
errors: RtcError[];
|
|
118
|
+
incomingCall: IncomingCallInfo | undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const defaultState: RtcState = {
|
|
122
|
+
session: { status: "IDLE", initiatedByMe: false },
|
|
123
|
+
room: {
|
|
124
|
+
participants: {},
|
|
125
|
+
},
|
|
126
|
+
local: { audioEnabled: false, videoEnabled: false, screenEnabled: false },
|
|
127
|
+
connection: { connected: false, reconnecting: false },
|
|
128
|
+
autoJoin: {
|
|
129
|
+
status: "idle",
|
|
130
|
+
attempt: 0,
|
|
131
|
+
maxAttempts: 0,
|
|
132
|
+
},
|
|
133
|
+
devices: {
|
|
134
|
+
mics: [],
|
|
135
|
+
cams: [],
|
|
136
|
+
speakers: [],
|
|
137
|
+
selected: {
|
|
138
|
+
micId: undefined,
|
|
139
|
+
camId: undefined,
|
|
140
|
+
speakerId: undefined,
|
|
141
|
+
},
|
|
142
|
+
permissions: {
|
|
143
|
+
camera: "unknown",
|
|
144
|
+
microphone: "unknown",
|
|
145
|
+
},
|
|
146
|
+
isEnumerating: false,
|
|
147
|
+
lastEnumeratedAt: undefined,
|
|
148
|
+
},
|
|
149
|
+
errors: [],
|
|
150
|
+
incomingCall: undefined,
|
|
151
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive logging system for the Callpad Web SDK
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Log level filtering (debug, info, warn, error)
|
|
6
|
+
* - Environment variable configuration (DEBUG, CALLPAD_LOG_LEVEL)
|
|
7
|
+
* - Hierarchical namespacing (callpad:socket:connection)
|
|
8
|
+
* - Custom logger integration
|
|
9
|
+
* - Zero-cost when disabled
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
13
|
+
|
|
14
|
+
export interface LoggerOptions {
|
|
15
|
+
level?: LogLevel;
|
|
16
|
+
enableDebug?: boolean;
|
|
17
|
+
customLogger?: (level: LogLevel, message: string, meta?: any) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CallpadLogger {
|
|
21
|
+
debug(message: string, meta?: any): void;
|
|
22
|
+
info(message: string, meta?: any): void;
|
|
23
|
+
warn(message: string, meta?: any): void;
|
|
24
|
+
error(message: string, meta?: any): void;
|
|
25
|
+
child(namespace: string): CallpadLogger;
|
|
26
|
+
setLevel(level: LogLevel): void;
|
|
27
|
+
isLevelEnabled(level: LogLevel): boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
31
|
+
debug: 0,
|
|
32
|
+
info: 1,
|
|
33
|
+
warn: 2,
|
|
34
|
+
error: 3,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
class CallpadLoggerImpl implements CallpadLogger {
|
|
38
|
+
private namespace: string;
|
|
39
|
+
private level: LogLevel;
|
|
40
|
+
private enableDebug: boolean;
|
|
41
|
+
private customLogger?: (level: LogLevel, message: string, meta?: any) => void;
|
|
42
|
+
|
|
43
|
+
constructor(namespace = "callpad", options: LoggerOptions = {}) {
|
|
44
|
+
this.namespace = namespace;
|
|
45
|
+
this.level = options.level ?? this.getDefaultLevel();
|
|
46
|
+
this.enableDebug = options.enableDebug ?? this.shouldEnableDebug();
|
|
47
|
+
if (options.customLogger) {
|
|
48
|
+
this.customLogger = options.customLogger;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private getDefaultLevel(): LogLevel {
|
|
53
|
+
const envLevel =
|
|
54
|
+
typeof window !== "undefined"
|
|
55
|
+
? (window as any).__CALLPAD_LOG_LEVEL__
|
|
56
|
+
: typeof globalThis !== "undefined" &&
|
|
57
|
+
(globalThis as any).process?.env?.CALLPAD_LOG_LEVEL;
|
|
58
|
+
|
|
59
|
+
if (envLevel && this.isValidLogLevel(envLevel)) {
|
|
60
|
+
return envLevel as LogLevel;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const isProduction =
|
|
64
|
+
typeof globalThis !== "undefined" &&
|
|
65
|
+
(globalThis as any).process?.env?.NODE_ENV === "production";
|
|
66
|
+
|
|
67
|
+
return isProduction ? "warn" : "info";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private shouldEnableDebug(): boolean {
|
|
71
|
+
const debugEnv =
|
|
72
|
+
typeof window !== "undefined"
|
|
73
|
+
? (window as any).__DEBUG__
|
|
74
|
+
: typeof globalThis !== "undefined" &&
|
|
75
|
+
(globalThis as any).process?.env?.DEBUG;
|
|
76
|
+
|
|
77
|
+
if (!debugEnv) return false;
|
|
78
|
+
|
|
79
|
+
const debugPatterns = debugEnv.split(/[\s,]+/);
|
|
80
|
+
return debugPatterns.some((pattern: string) => {
|
|
81
|
+
if (pattern === "*") return true;
|
|
82
|
+
if (pattern.endsWith("*")) {
|
|
83
|
+
const prefix = pattern.slice(0, -1);
|
|
84
|
+
return this.namespace.startsWith(prefix);
|
|
85
|
+
}
|
|
86
|
+
return this.namespace === pattern;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private isValidLogLevel(level: string): boolean {
|
|
91
|
+
return Object.keys(LOG_LEVELS).includes(level);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private shouldLog(level: LogLevel): boolean {
|
|
95
|
+
if (level === "debug" && !this.enableDebug) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[this.level];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private formatMessage(level: LogLevel, message: string, meta?: any): void {
|
|
102
|
+
if (!this.shouldLog(level)) return;
|
|
103
|
+
|
|
104
|
+
const timestamp = new Date().toISOString();
|
|
105
|
+
const prefix = `[${timestamp}] [${this.namespace}] [${level.toUpperCase()}]`;
|
|
106
|
+
|
|
107
|
+
if (this.customLogger) {
|
|
108
|
+
this.customLogger(level, message, meta);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const logMethod =
|
|
113
|
+
level === "error"
|
|
114
|
+
? console.error
|
|
115
|
+
: level === "warn"
|
|
116
|
+
? console.warn
|
|
117
|
+
: level === "info"
|
|
118
|
+
? console.info
|
|
119
|
+
: console.log;
|
|
120
|
+
|
|
121
|
+
if (meta !== undefined) {
|
|
122
|
+
logMethod(`${prefix} ${message}`, meta);
|
|
123
|
+
} else {
|
|
124
|
+
logMethod(`${prefix} ${message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
debug(message: string, meta?: any): void {
|
|
129
|
+
this.formatMessage("debug", message, meta);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
info(message: string, meta?: any): void {
|
|
133
|
+
this.formatMessage("info", message, meta);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
warn(message: string, meta?: any): void {
|
|
137
|
+
this.formatMessage("warn", message, meta);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
error(message: string, meta?: any): void {
|
|
141
|
+
this.formatMessage("error", message, meta);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
child(namespace: string): CallpadLogger {
|
|
145
|
+
const childNamespace = `${this.namespace}:${namespace}`;
|
|
146
|
+
const childOptions: LoggerOptions = {
|
|
147
|
+
level: this.level,
|
|
148
|
+
enableDebug: this.enableDebug,
|
|
149
|
+
};
|
|
150
|
+
if (this.customLogger) {
|
|
151
|
+
childOptions.customLogger = this.customLogger;
|
|
152
|
+
}
|
|
153
|
+
return new CallpadLoggerImpl(childNamespace, childOptions);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
setLevel(level: LogLevel): void {
|
|
157
|
+
this.level = level;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
isLevelEnabled(level: LogLevel): boolean {
|
|
161
|
+
return this.shouldLog(level);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let rootLogger: CallpadLogger | null = null;
|
|
166
|
+
|
|
167
|
+
export function createLogger(
|
|
168
|
+
namespace?: string,
|
|
169
|
+
options?: LoggerOptions
|
|
170
|
+
): CallpadLogger {
|
|
171
|
+
if (!namespace) {
|
|
172
|
+
if (!rootLogger) {
|
|
173
|
+
rootLogger = new CallpadLoggerImpl("callpad", options);
|
|
174
|
+
}
|
|
175
|
+
return rootLogger;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return new CallpadLoggerImpl(`callpad:${namespace}`, options);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function setGlobalLoggerOptions(options: LoggerOptions): void {
|
|
182
|
+
rootLogger = new CallpadLoggerImpl("callpad", options);
|
|
183
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Base configuration (standalone, no dependency on monorepo)
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
|
6
|
+
"allowJs": true,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"noFallthroughCasesInSwitch": true,
|
|
13
|
+
"module": "esnext",
|
|
14
|
+
"moduleResolution": "bundler",
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"isolatedModules": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
|
|
19
|
+
// Library-specific configuration
|
|
20
|
+
"declaration": true,
|
|
21
|
+
"declarationMap": true,
|
|
22
|
+
"sourceMap": true,
|
|
23
|
+
"outDir": "./dist/types",
|
|
24
|
+
|
|
25
|
+
// Additional strictness for library
|
|
26
|
+
"exactOptionalPropertyTypes": true,
|
|
27
|
+
"noImplicitReturns": true,
|
|
28
|
+
"noUncheckedIndexedAccess": true,
|
|
29
|
+
"noImplicitOverride": true,
|
|
30
|
+
|
|
31
|
+
// Library output settings
|
|
32
|
+
"removeComments": false,
|
|
33
|
+
"preserveConstEnums": true,
|
|
34
|
+
"inlineSources": true
|
|
35
|
+
},
|
|
36
|
+
"include": [
|
|
37
|
+
"src/**/*"
|
|
38
|
+
],
|
|
39
|
+
"exclude": [
|
|
40
|
+
"node_modules",
|
|
41
|
+
"dist",
|
|
42
|
+
"**/*.test.ts",
|
|
43
|
+
"**/*.test.tsx",
|
|
44
|
+
"**/*.spec.ts",
|
|
45
|
+
"**/*.spec.tsx",
|
|
46
|
+
"**/*.stories.ts",
|
|
47
|
+
"**/*.stories.tsx"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: {
|
|
5
|
+
index: "src/index.ts",
|
|
6
|
+
"livekit/index": "src/livekit/index.ts",
|
|
7
|
+
},
|
|
8
|
+
format: ["cjs", "esm"],
|
|
9
|
+
sourcemap: true,
|
|
10
|
+
clean: true,
|
|
11
|
+
splitting: false,
|
|
12
|
+
treeshake: true,
|
|
13
|
+
minify: false, // Keep readable for debugging
|
|
14
|
+
external: [
|
|
15
|
+
"react",
|
|
16
|
+
"react-dom",
|
|
17
|
+
"livekit-client",
|
|
18
|
+
"socket.io-client",
|
|
19
|
+
"axios",
|
|
20
|
+
"jwt-decode",
|
|
21
|
+
"zustand",
|
|
22
|
+
"immer",
|
|
23
|
+
"zod",
|
|
24
|
+
],
|
|
25
|
+
target: "es2020",
|
|
26
|
+
platform: "browser",
|
|
27
|
+
outExtension({ format }) {
|
|
28
|
+
return {
|
|
29
|
+
js: format === "esm" ? ".mjs" : ".cjs",
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
// Production optimizations
|
|
33
|
+
bundle: true,
|
|
34
|
+
skipNodeModulesBundle: true,
|
|
35
|
+
noExternal: [],
|
|
36
|
+
// Library specific settings
|
|
37
|
+
esbuildOptions(options) {
|
|
38
|
+
options.banner = {
|
|
39
|
+
js: '"use client";', // Next.js client component support
|
|
40
|
+
};
|
|
41
|
+
options.jsx = "automatic";
|
|
42
|
+
options.jsxImportSource = "react";
|
|
43
|
+
},
|
|
44
|
+
// Type generation
|
|
45
|
+
dts: {
|
|
46
|
+
resolve: true,
|
|
47
|
+
compilerOptions: {
|
|
48
|
+
moduleResolution: "bundler",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"noFallthroughCasesInSwitch": true,
|
|
12
|
+
"module": "esnext",
|
|
13
|
+
"moduleResolution": "bundler",
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx"
|
|
18
|
+
}
|
|
19
|
+
}
|