Complete proctoring SDK with video recording, S3 upload, and screen discipline for assessments
npm install @skilliq/proctoring-sdkFramework-agnostic SDK for enforcing screen discipline in proctored assessments. Monitors fullscreen mode, tab switching, network connectivity, and external device detection with configurable warnings and auto-submission.
- ✅ Fullscreen enforcement with cross-browser support
- ✅ Tab switching detection
- ✅ Mouse leave detection
- ✅ Network connectivity monitoring
- ✅ External device violation handling
- ✅ Configurable warning system with countdowns
- ✅ Auto-submission on violations
- ✅ Framework adapters for React, Angular, and Vue
- ✅ TypeScript support with full type definitions
- ✅ Event-driven architecture
``bash`
npm install @skilliq/proctoring-sdk
`tsx
import React, { useRef, useEffect } from 'react';
import { useScreenDisciplineSDK } from '@skilliq/proctoring-sdk/adapters/react';
function ExamComponent() {
const iframeRef = useRef
const { state, actions } = useScreenDisciplineSDK({
isEnabled: true,
iframeElement: iframeRef.current,
maxWarnings: 3,
countdowns: [15000, 15000, 15000], // 15s each
networkTimeoutMs: 180000, // 3 minutes
submitAssessment: async (event, reason) => {
await fetch('/api/submit-assessment', {
method: 'POST',
body: JSON.stringify({ event, reason }),
});
},
onViolation: async (source) => {
console.log('Violation detected:', source);
},
});
useEffect(() => {
if (iframeRef.current) {
actions.setIframeElement(iframeRef.current);
}
}, [actions]);
return (
Time remaining: {Math.ceil((state.fullscreenDeadline! - Date.now()) / 1000)}s
Warnings left: {state.warningsLeft}
Reconnecting... {Math.ceil((state.networkDeadline! - Date.now()) / 1000)}s
Submitting...
}$3
`typescript
import { Injectable } from '@angular/core';
import { ScreenDisciplineAngularAdapter } from '@skilliq/proctoring-sdk/adapters/angular';@Injectable({ providedIn: 'root' })
export class ExamService {
private adapter = new ScreenDisciplineAngularAdapter({
isEnabled: true,
maxWarnings: 3,
submitAssessment: async (event, reason) => {
await this.http.post('/api/submit-assessment', { event, reason }).toPromise();
},
});
readonly state$ = this.adapter.state$;
readonly actions = this.adapter.actions;
start() {
this.adapter.start();
}
stop() {
this.adapter.stop();
}
ngOnDestroy() {
this.adapter.destroy();
}
}
``typescript
// In your component
export class ExamComponent implements OnInit {
state$ = this.examService.state$;
actions = this.examService.actions; constructor(private examService: ExamService) {}
ngOnInit() {
this.examService.start();
this.state$.subscribe(state => {
if (state.fullscreenWarningOpen) {
// Show warning modal
}
});
}
}
`$3
`typescript
import { createVueScreenDisciplineAdapter } from '@skilliq/proctoring-sdk/adapters/vue';
import { reactive, toRefs, onMounted, onUnmounted } from 'vue';const useScreenDisciplineSDK = createVueScreenDisciplineAdapter({
reactive,
toRefs,
onMounted,
onUnmounted,
});
export default {
setup() {
const { state, actions } = useScreenDisciplineSDK({
isEnabled: true,
maxWarnings: 3,
submitAssessment: async (event, reason) => {
await fetch('/api/submit-assessment', {
method: 'POST',
body: JSON.stringify({ event, reason }),
});
},
});
return { state, actions };
},
};
``vue
Please return to fullscreen
Time remaining: {{ Math.ceil((state.fullscreenDeadline - Date.now()) / 1000) }}s
`$3
`typescript
import { ScreenDisciplineSDK } from '@skilliq/proctoring-sdk';const sdk = new ScreenDisciplineSDK({
isEnabled: true,
maxWarnings: 3,
submitAssessment: async (event, reason) => {
await fetch('/api/submit-assessment', {
method: 'POST',
body: JSON.stringify({ event, reason }),
});
},
onViolation: async (source) => {
console.log('Violation:', source);
},
onStateChange: (state) => {
console.log('State changed:', state);
},
});
// Start monitoring
sdk.start();
// Listen to events
sdk.on('violation', ({ source }) => {
console.log('Violation event:', source);
});
sdk.on('warning', ({ type }) => {
console.log('Warning shown:', type);
});
sdk.on('autoSubmit', ({ source, event, reason }) => {
console.log('Auto-submitted:', { source, event, reason });
});
// Manual actions
await sdk.enterFullscreen();
await sdk.endManually();
// Stop monitoring
sdk.stop();
`API Reference
$3
| Option | Type | Default | Description |
|--------|------|---------|-------------|
|
isEnabled | boolean | true | Enable/disable screen discipline |
| isPractice | boolean | false | Practice mode (no auto-submit) |
| iframeElement | HTMLElement \| null | null | Iframe element to monitor |
| maxWarnings | number | 3 | Maximum warnings before auto-submit |
| countdowns | [number, number, number] | [15000, 15000, 15000] | Countdown durations in ms |
| networkTimeoutMs | number | 180000 | Network timeout duration (3 min) |
| proctorTimeoutMs | number | 120000 | Proctor warning timeout (2 min) |
| isLocalEnvironment | boolean | false | Skip enforcement in local dev |
| submitAssessment | (event: string, reason: string) => Promise | - | Function to submit assessment |
| onViolation | (source: ViolationSource) => void | - | Callback on violation |
| onStateChange | (state: ScreenDisciplineState) => void | - | Callback on state change |
| onManualEnd | () => void | - | Callback on manual end |$3
| Property | Type | Description |
|----------|------|-------------|
|
isOnline | boolean | Network connectivity status |
| isFullscreen | boolean | Fullscreen mode status |
| warningsLeft | number | Remaining warnings |
| fullscreenWarningOpen | boolean | Fullscreen warning visible |
| fullscreenDeadline | number \| null | Fullscreen warning deadline timestamp |
| networkWarningOpen | boolean | Network warning visible |
| networkDeadline | number \| null | Network warning deadline timestamp |
| proctorWarningOpen | boolean | Proctor warning visible |
| proctorDeadline | number \| null | Proctor warning deadline timestamp |
| endOverlayOpen | boolean | End overlay visible |
| isSubmitting | boolean | Submission in progress |$3
-
"EXIT_FULLSCREEN" - User exited fullscreen
- "TAB_SWITCH" - User switched tabs
- "MOUSE_LEAVE" - Mouse left window
- "EXTERNAL_DEVICE" - External device detected
- "NETWORK_TIMEOUT" - Network connection lost
- "DURATION_TIMEOUT" - Time limit exceeded$3
-
stateChange - Emitted when state changes
- violation - Emitted when violation occurs
- warning - Emitted when warning is shown
- autoSubmit` - Emitted when auto-submit is triggeredMIT