@openmdm/core 0.2.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/src/types.ts ADDED
@@ -0,0 +1,1161 @@
1
+ /**
2
+ * OpenMDM Core Types
3
+ *
4
+ * These types define the core data structures for the MDM system.
5
+ * Designed to be database-agnostic and framework-agnostic.
6
+ */
7
+
8
+ // ============================================
9
+ // Device Types
10
+ // ============================================
11
+
12
+ export type DeviceStatus = 'pending' | 'enrolled' | 'unenrolled' | 'blocked';
13
+
14
+ export interface Device {
15
+ id: string;
16
+ externalId?: string | null;
17
+ enrollmentId: string;
18
+ status: DeviceStatus;
19
+
20
+ // Device Info
21
+ model?: string | null;
22
+ manufacturer?: string | null;
23
+ osVersion?: string | null;
24
+ serialNumber?: string | null;
25
+ imei?: string | null;
26
+ macAddress?: string | null;
27
+ androidId?: string | null;
28
+
29
+ // MDM State
30
+ policyId?: string | null;
31
+ lastHeartbeat?: Date | null;
32
+ lastSync?: Date | null;
33
+
34
+ // Telemetry
35
+ batteryLevel?: number | null;
36
+ storageUsed?: number | null;
37
+ storageTotal?: number | null;
38
+ location?: DeviceLocation | null;
39
+ installedApps?: InstalledApp[] | null;
40
+
41
+ // Metadata
42
+ tags?: Record<string, string> | null;
43
+ metadata?: Record<string, unknown> | null;
44
+
45
+ // Timestamps
46
+ createdAt: Date;
47
+ updatedAt: Date;
48
+ }
49
+
50
+ export interface DeviceLocation {
51
+ latitude: number;
52
+ longitude: number;
53
+ accuracy?: number;
54
+ timestamp: Date;
55
+ }
56
+
57
+ export interface InstalledApp {
58
+ packageName: string;
59
+ version: string;
60
+ versionCode?: number;
61
+ installedAt?: Date;
62
+ }
63
+
64
+ export interface CreateDeviceInput {
65
+ enrollmentId: string;
66
+ externalId?: string;
67
+ model?: string;
68
+ manufacturer?: string;
69
+ osVersion?: string;
70
+ serialNumber?: string;
71
+ imei?: string;
72
+ macAddress?: string;
73
+ androidId?: string;
74
+ policyId?: string;
75
+ tags?: Record<string, string>;
76
+ metadata?: Record<string, unknown>;
77
+ }
78
+
79
+ export interface UpdateDeviceInput {
80
+ externalId?: string | null;
81
+ status?: DeviceStatus;
82
+ policyId?: string | null;
83
+ model?: string;
84
+ manufacturer?: string;
85
+ osVersion?: string;
86
+ batteryLevel?: number | null;
87
+ storageUsed?: number | null;
88
+ storageTotal?: number | null;
89
+ lastHeartbeat?: Date;
90
+ lastSync?: Date;
91
+ installedApps?: InstalledApp[];
92
+ location?: DeviceLocation;
93
+ tags?: Record<string, string>;
94
+ metadata?: Record<string, unknown>;
95
+ }
96
+
97
+ export interface DeviceFilter {
98
+ status?: DeviceStatus | DeviceStatus[];
99
+ policyId?: string;
100
+ groupId?: string;
101
+ search?: string;
102
+ tags?: Record<string, string>;
103
+ limit?: number;
104
+ offset?: number;
105
+ }
106
+
107
+ export interface DeviceListResult {
108
+ devices: Device[];
109
+ total: number;
110
+ limit: number;
111
+ offset: number;
112
+ }
113
+
114
+ // ============================================
115
+ // Policy Types
116
+ // ============================================
117
+
118
+ export interface Policy {
119
+ id: string;
120
+ name: string;
121
+ description?: string | null;
122
+ isDefault: boolean;
123
+ settings: PolicySettings;
124
+ createdAt: Date;
125
+ updatedAt: Date;
126
+ }
127
+
128
+ export interface PolicySettings {
129
+ // Kiosk Mode
130
+ kioskMode?: boolean;
131
+ mainApp?: string;
132
+ allowedApps?: string[];
133
+ kioskExitPassword?: string;
134
+
135
+ // Lock Features
136
+ lockStatusBar?: boolean;
137
+ lockNavigationBar?: boolean;
138
+ lockSettings?: boolean;
139
+ lockPowerButton?: boolean;
140
+ blockInstall?: boolean;
141
+ blockUninstall?: boolean;
142
+
143
+ // Hardware Controls
144
+ bluetooth?: HardwareControl;
145
+ wifi?: HardwareControl;
146
+ gps?: HardwareControl;
147
+ mobileData?: HardwareControl;
148
+ camera?: HardwareControl;
149
+ microphone?: HardwareControl;
150
+ usb?: HardwareControl;
151
+ nfc?: HardwareControl;
152
+
153
+ // Update Settings
154
+ systemUpdatePolicy?: SystemUpdatePolicy;
155
+ updateWindow?: TimeWindow;
156
+
157
+ // Security
158
+ passwordPolicy?: PasswordPolicy;
159
+ encryptionRequired?: boolean;
160
+ factoryResetProtection?: boolean;
161
+ safeBootDisabled?: boolean;
162
+
163
+ // Telemetry
164
+ heartbeatInterval?: number;
165
+ locationReportInterval?: number;
166
+ locationEnabled?: boolean;
167
+
168
+ // Network
169
+ wifiConfigs?: WifiConfig[];
170
+ vpnConfig?: VpnConfig;
171
+
172
+ // Applications
173
+ applications?: PolicyApplication[];
174
+
175
+ // Custom settings (for plugins)
176
+ custom?: Record<string, unknown>;
177
+ }
178
+
179
+ export type HardwareControl = 'on' | 'off' | 'user';
180
+ export type SystemUpdatePolicy = 'auto' | 'windowed' | 'postpone' | 'manual';
181
+
182
+ export interface TimeWindow {
183
+ start: string; // "HH:MM"
184
+ end: string; // "HH:MM"
185
+ }
186
+
187
+ export interface PasswordPolicy {
188
+ required: boolean;
189
+ minLength?: number;
190
+ complexity?: 'none' | 'numeric' | 'alphanumeric' | 'complex';
191
+ maxFailedAttempts?: number;
192
+ expirationDays?: number;
193
+ historyLength?: number;
194
+ }
195
+
196
+ export interface WifiConfig {
197
+ ssid: string;
198
+ securityType: 'none' | 'wep' | 'wpa' | 'wpa2' | 'wpa3';
199
+ password?: string;
200
+ hidden?: boolean;
201
+ autoConnect?: boolean;
202
+ }
203
+
204
+ export interface VpnConfig {
205
+ type: 'pptp' | 'l2tp' | 'ipsec' | 'openvpn' | 'wireguard';
206
+ server: string;
207
+ username?: string;
208
+ password?: string;
209
+ certificate?: string;
210
+ config?: Record<string, unknown>;
211
+ }
212
+
213
+ export interface PolicyApplication {
214
+ packageName: string;
215
+ action: 'install' | 'update' | 'uninstall';
216
+ version?: string;
217
+ required?: boolean;
218
+ autoUpdate?: boolean;
219
+ }
220
+
221
+ export interface CreatePolicyInput {
222
+ name: string;
223
+ description?: string;
224
+ isDefault?: boolean;
225
+ settings: PolicySettings;
226
+ }
227
+
228
+ export interface UpdatePolicyInput {
229
+ name?: string;
230
+ description?: string | null;
231
+ isDefault?: boolean;
232
+ settings?: PolicySettings;
233
+ }
234
+
235
+ // ============================================
236
+ // Application Types
237
+ // ============================================
238
+
239
+ export interface Application {
240
+ id: string;
241
+ name: string;
242
+ packageName: string;
243
+ version: string;
244
+ versionCode: number;
245
+ url: string;
246
+ hash?: string | null;
247
+ size?: number | null;
248
+ minSdkVersion?: number | null;
249
+
250
+ // Deployment settings
251
+ showIcon: boolean;
252
+ runAfterInstall: boolean;
253
+ runAtBoot: boolean;
254
+ isSystem: boolean;
255
+
256
+ // State
257
+ isActive: boolean;
258
+
259
+ // Metadata
260
+ metadata?: Record<string, unknown> | null;
261
+ createdAt: Date;
262
+ updatedAt: Date;
263
+ }
264
+
265
+ export interface CreateApplicationInput {
266
+ name: string;
267
+ packageName: string;
268
+ version: string;
269
+ versionCode: number;
270
+ url: string;
271
+ hash?: string;
272
+ size?: number;
273
+ minSdkVersion?: number;
274
+ showIcon?: boolean;
275
+ runAfterInstall?: boolean;
276
+ runAtBoot?: boolean;
277
+ isSystem?: boolean;
278
+ metadata?: Record<string, unknown>;
279
+ }
280
+
281
+ export interface UpdateApplicationInput {
282
+ name?: string;
283
+ version?: string;
284
+ versionCode?: number;
285
+ url?: string;
286
+ hash?: string | null;
287
+ size?: number | null;
288
+ minSdkVersion?: number | null;
289
+ showIcon?: boolean;
290
+ runAfterInstall?: boolean;
291
+ runAtBoot?: boolean;
292
+ isActive?: boolean;
293
+ metadata?: Record<string, unknown> | null;
294
+ }
295
+
296
+ export interface DeployTarget {
297
+ devices?: string[];
298
+ policies?: string[];
299
+ groups?: string[];
300
+ }
301
+
302
+ // ============================================
303
+ // App Version & Rollback Types
304
+ // ============================================
305
+
306
+ export interface AppVersion {
307
+ id: string;
308
+ applicationId: string;
309
+ packageName: string;
310
+ version: string;
311
+ versionCode: number;
312
+ url: string;
313
+ hash?: string | null;
314
+ size?: number | null;
315
+ releaseNotes?: string | null;
316
+ isMinimumVersion: boolean;
317
+ createdAt: Date;
318
+ }
319
+
320
+ export interface AppRollback {
321
+ id: string;
322
+ deviceId: string;
323
+ packageName: string;
324
+ fromVersion: string;
325
+ fromVersionCode: number;
326
+ toVersion: string;
327
+ toVersionCode: number;
328
+ reason?: string | null;
329
+ status: 'pending' | 'in_progress' | 'completed' | 'failed';
330
+ error?: string | null;
331
+ initiatedBy?: string | null;
332
+ createdAt: Date;
333
+ completedAt?: Date | null;
334
+ }
335
+
336
+ export interface CreateAppRollbackInput {
337
+ deviceId: string;
338
+ packageName: string;
339
+ toVersionCode: number;
340
+ reason?: string;
341
+ initiatedBy?: string;
342
+ }
343
+
344
+ // ============================================
345
+ // Command Types
346
+ // ============================================
347
+
348
+ export type CommandType =
349
+ | 'reboot'
350
+ | 'shutdown'
351
+ | 'sync'
352
+ | 'lock'
353
+ | 'unlock'
354
+ | 'wipe'
355
+ | 'factoryReset'
356
+ | 'installApp'
357
+ | 'uninstallApp'
358
+ | 'updateApp'
359
+ | 'runApp'
360
+ | 'clearAppData'
361
+ | 'clearAppCache'
362
+ | 'shell'
363
+ | 'setPolicy'
364
+ | 'grantPermissions'
365
+ | 'exitKiosk'
366
+ | 'enterKiosk'
367
+ | 'setWifi'
368
+ | 'screenshot'
369
+ | 'getLocation'
370
+ | 'setVolume'
371
+ | 'sendNotification'
372
+ | 'whitelistBattery' // Whitelist app from battery optimization (Doze)
373
+ | 'enablePermissiveMode' // Enable permissive mode for debugging
374
+ | 'setTimeZone' // Set device timezone
375
+ | 'enableAdb' // Enable/disable ADB debugging
376
+ | 'rollbackApp' // Rollback to previous app version
377
+ | 'custom';
378
+
379
+ export type CommandStatus =
380
+ | 'pending'
381
+ | 'sent'
382
+ | 'acknowledged'
383
+ | 'completed'
384
+ | 'failed'
385
+ | 'cancelled';
386
+
387
+ export interface Command {
388
+ id: string;
389
+ deviceId: string;
390
+ type: CommandType;
391
+ payload?: Record<string, unknown> | null;
392
+ status: CommandStatus;
393
+ result?: CommandResult | null;
394
+ error?: string | null;
395
+ createdAt: Date;
396
+ sentAt?: Date | null;
397
+ acknowledgedAt?: Date | null;
398
+ completedAt?: Date | null;
399
+ }
400
+
401
+ export interface CommandResult {
402
+ success: boolean;
403
+ message?: string;
404
+ data?: unknown;
405
+ }
406
+
407
+ export interface SendCommandInput {
408
+ deviceId: string;
409
+ type: CommandType;
410
+ payload?: Record<string, unknown>;
411
+ }
412
+
413
+ export interface CommandFilter {
414
+ deviceId?: string;
415
+ status?: CommandStatus | CommandStatus[];
416
+ type?: CommandType | CommandType[];
417
+ limit?: number;
418
+ offset?: number;
419
+ }
420
+
421
+ // ============================================
422
+ // Event Types
423
+ // ============================================
424
+
425
+ export type EventType =
426
+ | 'device.enrolled'
427
+ | 'device.unenrolled'
428
+ | 'device.blocked'
429
+ | 'device.heartbeat'
430
+ | 'device.locationUpdated'
431
+ | 'device.statusChanged'
432
+ | 'device.policyChanged'
433
+ | 'app.installed'
434
+ | 'app.uninstalled'
435
+ | 'app.updated'
436
+ | 'app.crashed'
437
+ | 'app.started'
438
+ | 'app.stopped'
439
+ | 'policy.applied'
440
+ | 'policy.failed'
441
+ | 'command.received'
442
+ | 'command.acknowledged'
443
+ | 'command.completed'
444
+ | 'command.failed'
445
+ | 'security.tamper'
446
+ | 'security.rootDetected'
447
+ | 'security.screenLocked'
448
+ | 'security.screenUnlocked'
449
+ | 'custom';
450
+
451
+ export interface MDMEvent<T = unknown> {
452
+ id: string;
453
+ deviceId: string;
454
+ type: EventType;
455
+ payload: T;
456
+ createdAt: Date;
457
+ }
458
+
459
+ export interface EventFilter {
460
+ deviceId?: string;
461
+ type?: EventType | EventType[];
462
+ startDate?: Date;
463
+ endDate?: Date;
464
+ limit?: number;
465
+ offset?: number;
466
+ }
467
+
468
+ // ============================================
469
+ // Group Types
470
+ // ============================================
471
+
472
+ export interface Group {
473
+ id: string;
474
+ name: string;
475
+ description?: string | null;
476
+ policyId?: string | null;
477
+ parentId?: string | null;
478
+ metadata?: Record<string, unknown> | null;
479
+ createdAt: Date;
480
+ updatedAt: Date;
481
+ }
482
+
483
+ export interface CreateGroupInput {
484
+ name: string;
485
+ description?: string;
486
+ policyId?: string;
487
+ parentId?: string;
488
+ metadata?: Record<string, unknown>;
489
+ }
490
+
491
+ export interface UpdateGroupInput {
492
+ name?: string;
493
+ description?: string | null;
494
+ policyId?: string | null;
495
+ parentId?: string | null;
496
+ metadata?: Record<string, unknown> | null;
497
+ }
498
+
499
+ // ============================================
500
+ // Enrollment Types
501
+ // ============================================
502
+
503
+ export type EnrollmentMethod =
504
+ | 'qr'
505
+ | 'nfc'
506
+ | 'zero-touch'
507
+ | 'knox'
508
+ | 'manual'
509
+ | 'app-only'
510
+ | 'adb';
511
+
512
+ export interface EnrollmentRequest {
513
+ // Device identifiers (at least one required)
514
+ macAddress?: string;
515
+ serialNumber?: string;
516
+ imei?: string;
517
+ androidId?: string;
518
+
519
+ // Device info
520
+ model: string;
521
+ manufacturer: string;
522
+ osVersion: string;
523
+ sdkVersion?: number;
524
+
525
+ // Agent info
526
+ agentVersion?: string;
527
+ agentPackage?: string;
528
+
529
+ // Enrollment details
530
+ method: EnrollmentMethod;
531
+ timestamp: string;
532
+ signature: string;
533
+
534
+ // Optional pre-assigned policy/group
535
+ policyId?: string;
536
+ groupId?: string;
537
+ }
538
+
539
+ export interface EnrollmentResponse {
540
+ deviceId: string;
541
+ enrollmentId: string;
542
+ policyId?: string;
543
+ policy?: Policy;
544
+ serverUrl: string;
545
+ pushConfig: PushConfig;
546
+ token: string;
547
+ refreshToken?: string;
548
+ tokenExpiresAt?: Date;
549
+ }
550
+
551
+ export interface PushConfig {
552
+ provider: 'fcm' | 'mqtt' | 'websocket' | 'polling';
553
+ fcmSenderId?: string;
554
+ mqttUrl?: string;
555
+ mqttTopic?: string;
556
+ mqttUsername?: string;
557
+ mqttPassword?: string;
558
+ wsUrl?: string;
559
+ pollingInterval?: number;
560
+ }
561
+
562
+ // ============================================
563
+ // Telemetry Types
564
+ // ============================================
565
+
566
+ export interface Heartbeat {
567
+ deviceId: string;
568
+ timestamp: Date;
569
+
570
+ // Battery
571
+ batteryLevel: number;
572
+ isCharging: boolean;
573
+ batteryHealth?: 'good' | 'overheat' | 'dead' | 'cold' | 'unknown';
574
+
575
+ // Storage
576
+ storageUsed: number;
577
+ storageTotal: number;
578
+
579
+ // Memory
580
+ memoryUsed: number;
581
+ memoryTotal: number;
582
+
583
+ // Network
584
+ networkType?: 'wifi' | 'cellular' | 'ethernet' | 'none';
585
+ networkName?: string; // SSID or carrier
586
+ signalStrength?: number;
587
+ ipAddress?: string;
588
+
589
+ // Location
590
+ location?: DeviceLocation;
591
+
592
+ // Apps
593
+ installedApps: InstalledApp[];
594
+ runningApps?: string[];
595
+
596
+ // Security
597
+ isRooted?: boolean;
598
+ isEncrypted?: boolean;
599
+ screenLockEnabled?: boolean;
600
+
601
+ // Agent status
602
+ agentVersion?: string;
603
+ policyVersion?: string;
604
+ lastPolicySync?: Date;
605
+ }
606
+
607
+ // ============================================
608
+ // Push Token Types
609
+ // ============================================
610
+
611
+ export interface PushToken {
612
+ id: string;
613
+ deviceId: string;
614
+ provider: 'fcm' | 'mqtt' | 'websocket';
615
+ token: string;
616
+ isActive: boolean;
617
+ createdAt: Date;
618
+ updatedAt: Date;
619
+ }
620
+
621
+ export interface RegisterPushTokenInput {
622
+ deviceId: string;
623
+ provider: 'fcm' | 'mqtt' | 'websocket';
624
+ token: string;
625
+ }
626
+
627
+ // ============================================
628
+ // Configuration Types
629
+ // ============================================
630
+
631
+ export interface MDMConfig {
632
+ /** Database adapter for persistence */
633
+ database: DatabaseAdapter;
634
+
635
+ /** Authentication/authorization configuration */
636
+ auth?: AuthConfig;
637
+
638
+ /** Push notification provider configuration */
639
+ push?: PushProviderConfig;
640
+
641
+ /** Device enrollment configuration */
642
+ enrollment?: EnrollmentConfig;
643
+
644
+ /** Server URL (used in enrollment responses) */
645
+ serverUrl?: string;
646
+
647
+ /** APK/file storage configuration */
648
+ storage?: StorageConfig;
649
+
650
+ /** Outbound webhook configuration */
651
+ webhooks?: WebhookConfig;
652
+
653
+ /** Plugins to extend functionality */
654
+ plugins?: MDMPlugin[];
655
+
656
+ /** Event handlers */
657
+ onDeviceEnrolled?: (device: Device) => Promise<void>;
658
+ onDeviceUnenrolled?: (device: Device) => Promise<void>;
659
+ onDeviceBlocked?: (device: Device) => Promise<void>;
660
+ onHeartbeat?: (device: Device, heartbeat: Heartbeat) => Promise<void>;
661
+ onCommand?: (command: Command) => Promise<void>;
662
+ onEvent?: (event: MDMEvent) => Promise<void>;
663
+ }
664
+
665
+ export interface StorageConfig {
666
+ /** Storage provider (s3, local, custom) */
667
+ provider: 's3' | 'local' | 'custom';
668
+
669
+ /** S3 configuration */
670
+ s3?: {
671
+ bucket: string;
672
+ region: string;
673
+ accessKeyId?: string;
674
+ secretAccessKey?: string;
675
+ endpoint?: string; // For S3-compatible services
676
+ presignedUrlExpiry?: number; // Seconds, default 3600
677
+ };
678
+
679
+ /** Local storage path */
680
+ localPath?: string;
681
+
682
+ /** Custom storage adapter */
683
+ customAdapter?: {
684
+ upload: (file: Buffer, key: string) => Promise<string>;
685
+ getUrl: (key: string) => Promise<string>;
686
+ delete: (key: string) => Promise<void>;
687
+ };
688
+ }
689
+
690
+ export interface WebhookConfig {
691
+ /** Webhook endpoints to notify */
692
+ endpoints?: WebhookEndpoint[];
693
+
694
+ /** Retry configuration */
695
+ retry?: {
696
+ maxRetries?: number;
697
+ initialDelay?: number;
698
+ maxDelay?: number;
699
+ };
700
+
701
+ /** Sign webhooks with HMAC secret */
702
+ signingSecret?: string;
703
+ }
704
+
705
+ export interface WebhookEndpoint {
706
+ /** Unique identifier */
707
+ id: string;
708
+ /** Webhook URL */
709
+ url: string;
710
+ /** Events to trigger this webhook */
711
+ events: (EventType | '*')[];
712
+ /** Custom headers */
713
+ headers?: Record<string, string>;
714
+ /** Whether endpoint is active */
715
+ enabled: boolean;
716
+ }
717
+
718
+ export interface AuthConfig {
719
+ /** Get current user from request context */
720
+ getUser: <T = unknown>(context: unknown) => Promise<T | null>;
721
+ /** Check if user has admin privileges */
722
+ isAdmin?: (user: unknown) => Promise<boolean>;
723
+ /** Check if user can access specific device */
724
+ canAccessDevice?: (user: unknown, deviceId: string) => Promise<boolean>;
725
+ /** Device JWT secret (for device auth tokens) */
726
+ deviceTokenSecret?: string;
727
+ /** Device token expiration in seconds (default: 365 days) */
728
+ deviceTokenExpiration?: number;
729
+ }
730
+
731
+ export interface PushProviderConfig {
732
+ provider: 'fcm' | 'mqtt' | 'websocket' | 'polling';
733
+
734
+ // FCM configuration
735
+ fcmCredentials?: string | Record<string, unknown>;
736
+ fcmProjectId?: string;
737
+
738
+ // MQTT configuration
739
+ mqttUrl?: string;
740
+ mqttUsername?: string;
741
+ mqttPassword?: string;
742
+ mqttTopicPrefix?: string;
743
+
744
+ // WebSocket configuration
745
+ wsPath?: string;
746
+ wsPingInterval?: number;
747
+
748
+ // Polling fallback
749
+ pollingInterval?: number;
750
+ }
751
+
752
+ export interface EnrollmentConfig {
753
+ /** Auto-enroll devices with valid signature */
754
+ autoEnroll?: boolean;
755
+ /** HMAC secret for device signature verification */
756
+ deviceSecret: string;
757
+ /** Allowed enrollment methods */
758
+ allowedMethods?: EnrollmentMethod[];
759
+ /** Default policy for new devices */
760
+ defaultPolicyId?: string;
761
+ /** Default group for new devices */
762
+ defaultGroupId?: string;
763
+ /** Require manual approval for enrollment */
764
+ requireApproval?: boolean;
765
+ /** Custom enrollment validation */
766
+ validate?: (request: EnrollmentRequest) => Promise<boolean>;
767
+ }
768
+
769
+ // ============================================
770
+ // Adapter Interfaces
771
+ // ============================================
772
+
773
+ export interface DatabaseAdapter {
774
+ // Devices
775
+ findDevice(id: string): Promise<Device | null>;
776
+ findDeviceByEnrollmentId(enrollmentId: string): Promise<Device | null>;
777
+ listDevices(filter?: DeviceFilter): Promise<DeviceListResult>;
778
+ createDevice(data: CreateDeviceInput): Promise<Device>;
779
+ updateDevice(id: string, data: UpdateDeviceInput): Promise<Device>;
780
+ deleteDevice(id: string): Promise<void>;
781
+ countDevices(filter?: DeviceFilter): Promise<number>;
782
+
783
+ // Policies
784
+ findPolicy(id: string): Promise<Policy | null>;
785
+ findDefaultPolicy(): Promise<Policy | null>;
786
+ listPolicies(): Promise<Policy[]>;
787
+ createPolicy(data: CreatePolicyInput): Promise<Policy>;
788
+ updatePolicy(id: string, data: UpdatePolicyInput): Promise<Policy>;
789
+ deletePolicy(id: string): Promise<void>;
790
+
791
+ // Applications
792
+ findApplication(id: string): Promise<Application | null>;
793
+ findApplicationByPackage(packageName: string, version?: string): Promise<Application | null>;
794
+ listApplications(activeOnly?: boolean): Promise<Application[]>;
795
+ createApplication(data: CreateApplicationInput): Promise<Application>;
796
+ updateApplication(id: string, data: UpdateApplicationInput): Promise<Application>;
797
+ deleteApplication(id: string): Promise<void>;
798
+
799
+ // Commands
800
+ findCommand(id: string): Promise<Command | null>;
801
+ listCommands(filter?: CommandFilter): Promise<Command[]>;
802
+ createCommand(data: SendCommandInput): Promise<Command>;
803
+ updateCommand(id: string, data: Partial<Command>): Promise<Command>;
804
+ getPendingCommands(deviceId: string): Promise<Command[]>;
805
+
806
+ // Events
807
+ createEvent(event: Omit<MDMEvent, 'id' | 'createdAt'>): Promise<MDMEvent>;
808
+ listEvents(filter?: EventFilter): Promise<MDMEvent[]>;
809
+
810
+ // Groups
811
+ findGroup(id: string): Promise<Group | null>;
812
+ listGroups(): Promise<Group[]>;
813
+ createGroup(data: CreateGroupInput): Promise<Group>;
814
+ updateGroup(id: string, data: UpdateGroupInput): Promise<Group>;
815
+ deleteGroup(id: string): Promise<void>;
816
+ listDevicesInGroup(groupId: string): Promise<Device[]>;
817
+ addDeviceToGroup(deviceId: string, groupId: string): Promise<void>;
818
+ removeDeviceFromGroup(deviceId: string, groupId: string): Promise<void>;
819
+ getDeviceGroups(deviceId: string): Promise<Group[]>;
820
+
821
+ // Push Tokens
822
+ findPushToken(deviceId: string, provider: string): Promise<PushToken | null>;
823
+ upsertPushToken(data: RegisterPushTokenInput): Promise<PushToken>;
824
+ deletePushToken(deviceId: string, provider?: string): Promise<void>;
825
+
826
+ // App Versions (optional - for version tracking)
827
+ listAppVersions?(packageName: string): Promise<AppVersion[]>;
828
+ createAppVersion?(data: Omit<AppVersion, 'id' | 'createdAt'>): Promise<AppVersion>;
829
+ setMinimumVersion?(packageName: string, versionCode: number): Promise<void>;
830
+ getMinimumVersion?(packageName: string): Promise<AppVersion | null>;
831
+
832
+ // Rollback History (optional)
833
+ createRollback?(data: CreateAppRollbackInput): Promise<AppRollback>;
834
+ updateRollback?(id: string, data: Partial<AppRollback>): Promise<AppRollback>;
835
+ listRollbacks?(filter?: { deviceId?: string; packageName?: string }): Promise<AppRollback[]>;
836
+
837
+ // Transactions (optional)
838
+ transaction?<T>(fn: () => Promise<T>): Promise<T>;
839
+ }
840
+
841
+ export interface PushAdapter {
842
+ /** Send push message to a device */
843
+ send(deviceId: string, message: PushMessage): Promise<PushResult>;
844
+ /** Send push message to multiple devices */
845
+ sendBatch(deviceIds: string[], message: PushMessage): Promise<PushBatchResult>;
846
+ /** Register device push token */
847
+ registerToken?(deviceId: string, token: string): Promise<void>;
848
+ /** Unregister device push token */
849
+ unregisterToken?(deviceId: string): Promise<void>;
850
+ /** Subscribe device to topic */
851
+ subscribe?(deviceId: string, topic: string): Promise<void>;
852
+ /** Unsubscribe device from topic */
853
+ unsubscribe?(deviceId: string, topic: string): Promise<void>;
854
+ }
855
+
856
+ export interface PushMessage {
857
+ type: string;
858
+ payload?: Record<string, unknown>;
859
+ priority?: 'high' | 'normal';
860
+ ttl?: number;
861
+ collapseKey?: string;
862
+ }
863
+
864
+ export interface PushResult {
865
+ success: boolean;
866
+ messageId?: string;
867
+ error?: string;
868
+ }
869
+
870
+ export interface PushBatchResult {
871
+ successCount: number;
872
+ failureCount: number;
873
+ results: Array<{ deviceId: string; result: PushResult }>;
874
+ }
875
+
876
+ // ============================================
877
+ // Plugin Interface
878
+ // ============================================
879
+
880
+ export interface MDMPlugin {
881
+ /** Unique plugin name */
882
+ name: string;
883
+ /** Plugin version */
884
+ version: string;
885
+
886
+ /** Called when MDM is initialized */
887
+ onInit?(mdm: MDMInstance): Promise<void>;
888
+
889
+ /** Called when MDM is destroyed */
890
+ onDestroy?(): Promise<void>;
891
+
892
+ /** Additional routes to mount */
893
+ routes?: PluginRoute[];
894
+
895
+ /** Middleware to apply to all routes */
896
+ middleware?: PluginMiddleware[];
897
+
898
+ /** Extend enrollment process */
899
+ onEnroll?(device: Device, request: EnrollmentRequest): Promise<void>;
900
+
901
+ /** Extend device processing */
902
+ onDeviceEnrolled?(device: Device): Promise<void>;
903
+ onDeviceUnenrolled?(device: Device): Promise<void>;
904
+ onHeartbeat?(device: Device, heartbeat: Heartbeat): Promise<void>;
905
+
906
+ /** Extend policy processing */
907
+ policySchema?: Record<string, unknown>;
908
+ validatePolicy?(settings: PolicySettings): Promise<{ valid: boolean; errors?: string[] }>;
909
+ applyPolicy?(device: Device, policy: Policy): Promise<void>;
910
+
911
+ /** Extend command processing */
912
+ commandTypes?: CommandType[];
913
+ executeCommand?(device: Device, command: Command): Promise<CommandResult>;
914
+ }
915
+
916
+ export interface PluginRoute {
917
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
918
+ path: string;
919
+ handler: (context: unknown) => Promise<unknown>;
920
+ auth?: boolean;
921
+ admin?: boolean;
922
+ }
923
+
924
+ export type PluginMiddleware = (
925
+ context: unknown,
926
+ next: () => Promise<unknown>
927
+ ) => Promise<unknown>;
928
+
929
+ // ============================================
930
+ // MDM Instance Interface
931
+ // ============================================
932
+
933
+ export interface WebhookManager {
934
+ /** Deliver an event to all matching webhook endpoints */
935
+ deliver<T>(event: MDMEvent<T>): Promise<WebhookDeliveryResult[]>;
936
+ /** Add a webhook endpoint at runtime */
937
+ addEndpoint(endpoint: WebhookEndpoint): void;
938
+ /** Remove a webhook endpoint */
939
+ removeEndpoint(endpointId: string): void;
940
+ /** Update a webhook endpoint */
941
+ updateEndpoint(endpointId: string, updates: Partial<WebhookEndpoint>): void;
942
+ /** Get all configured endpoints */
943
+ getEndpoints(): WebhookEndpoint[];
944
+ /** Test a webhook endpoint with a test payload */
945
+ testEndpoint(endpointId: string): Promise<WebhookDeliveryResult>;
946
+ }
947
+
948
+ export interface WebhookDeliveryResult {
949
+ endpointId: string;
950
+ success: boolean;
951
+ statusCode?: number;
952
+ error?: string;
953
+ retryCount: number;
954
+ deliveredAt?: Date;
955
+ }
956
+
957
+ export interface MDMInstance {
958
+ /** Device management */
959
+ devices: DeviceManager;
960
+ /** Policy management */
961
+ policies: PolicyManager;
962
+ /** Application management */
963
+ apps: ApplicationManager;
964
+ /** Command management */
965
+ commands: CommandManager;
966
+ /** Group management */
967
+ groups: GroupManager;
968
+
969
+ /** Push notification service */
970
+ push: PushAdapter;
971
+
972
+ /** Webhook delivery (if configured) */
973
+ webhooks?: WebhookManager;
974
+
975
+ /** Database adapter */
976
+ db: DatabaseAdapter;
977
+
978
+ /** Configuration */
979
+ config: MDMConfig;
980
+
981
+ /** Subscribe to events */
982
+ on<T extends EventType>(event: T, handler: EventHandler<T>): () => void;
983
+ /** Emit an event */
984
+ emit<T extends EventType>(event: T, data: EventPayloadMap[T]): Promise<void>;
985
+
986
+ /** Process device enrollment */
987
+ enroll(request: EnrollmentRequest): Promise<EnrollmentResponse>;
988
+ /** Process device heartbeat */
989
+ processHeartbeat(deviceId: string, heartbeat: Heartbeat): Promise<void>;
990
+ /** Verify device token */
991
+ verifyDeviceToken(token: string): Promise<{ deviceId: string } | null>;
992
+
993
+ /** Get loaded plugins */
994
+ getPlugins(): MDMPlugin[];
995
+ /** Get plugin by name */
996
+ getPlugin(name: string): MDMPlugin | undefined;
997
+ }
998
+
999
+ // ============================================
1000
+ // Manager Interfaces
1001
+ // ============================================
1002
+
1003
+ export interface DeviceManager {
1004
+ get(id: string): Promise<Device | null>;
1005
+ getByEnrollmentId(enrollmentId: string): Promise<Device | null>;
1006
+ list(filter?: DeviceFilter): Promise<DeviceListResult>;
1007
+ create(data: CreateDeviceInput): Promise<Device>;
1008
+ update(id: string, data: UpdateDeviceInput): Promise<Device>;
1009
+ delete(id: string): Promise<void>;
1010
+ assignPolicy(deviceId: string, policyId: string | null): Promise<Device>;
1011
+ addToGroup(deviceId: string, groupId: string): Promise<void>;
1012
+ removeFromGroup(deviceId: string, groupId: string): Promise<void>;
1013
+ getGroups(deviceId: string): Promise<Group[]>;
1014
+ sendCommand(deviceId: string, input: Omit<SendCommandInput, 'deviceId'>): Promise<Command>;
1015
+ sync(deviceId: string): Promise<Command>;
1016
+ reboot(deviceId: string): Promise<Command>;
1017
+ lock(deviceId: string, message?: string): Promise<Command>;
1018
+ wipe(deviceId: string, preserveData?: boolean): Promise<Command>;
1019
+ }
1020
+
1021
+ export interface PolicyManager {
1022
+ get(id: string): Promise<Policy | null>;
1023
+ getDefault(): Promise<Policy | null>;
1024
+ list(): Promise<Policy[]>;
1025
+ create(data: CreatePolicyInput): Promise<Policy>;
1026
+ update(id: string, data: UpdatePolicyInput): Promise<Policy>;
1027
+ delete(id: string): Promise<void>;
1028
+ setDefault(id: string): Promise<Policy>;
1029
+ getDevices(policyId: string): Promise<Device[]>;
1030
+ applyToDevice(policyId: string, deviceId: string): Promise<void>;
1031
+ }
1032
+
1033
+ export interface ApplicationManager {
1034
+ get(id: string): Promise<Application | null>;
1035
+ getByPackage(packageName: string, version?: string): Promise<Application | null>;
1036
+ list(activeOnly?: boolean): Promise<Application[]>;
1037
+ register(data: CreateApplicationInput): Promise<Application>;
1038
+ update(id: string, data: UpdateApplicationInput): Promise<Application>;
1039
+ delete(id: string): Promise<void>;
1040
+ activate(id: string): Promise<Application>;
1041
+ deactivate(id: string): Promise<Application>;
1042
+ deploy(packageName: string, target: DeployTarget): Promise<void>;
1043
+ installOnDevice(packageName: string, deviceId: string, version?: string): Promise<Command>;
1044
+ uninstallFromDevice(packageName: string, deviceId: string): Promise<Command>;
1045
+ }
1046
+
1047
+ export interface CommandManager {
1048
+ get(id: string): Promise<Command | null>;
1049
+ list(filter?: CommandFilter): Promise<Command[]>;
1050
+ send(input: SendCommandInput): Promise<Command>;
1051
+ cancel(id: string): Promise<Command>;
1052
+ acknowledge(id: string): Promise<Command>;
1053
+ complete(id: string, result: CommandResult): Promise<Command>;
1054
+ fail(id: string, error: string): Promise<Command>;
1055
+ getPending(deviceId: string): Promise<Command[]>;
1056
+ }
1057
+
1058
+ export interface GroupManager {
1059
+ get(id: string): Promise<Group | null>;
1060
+ list(): Promise<Group[]>;
1061
+ create(data: CreateGroupInput): Promise<Group>;
1062
+ update(id: string, data: UpdateGroupInput): Promise<Group>;
1063
+ delete(id: string): Promise<void>;
1064
+ getDevices(groupId: string): Promise<Device[]>;
1065
+ addDevice(groupId: string, deviceId: string): Promise<void>;
1066
+ removeDevice(groupId: string, deviceId: string): Promise<void>;
1067
+ getChildren(groupId: string): Promise<Group[]>;
1068
+ }
1069
+
1070
+ // ============================================
1071
+ // Event Handler Types
1072
+ // ============================================
1073
+
1074
+ export type EventHandler<T extends EventType> = (
1075
+ event: MDMEvent<EventPayloadMap[T]>
1076
+ ) => Promise<void> | void;
1077
+
1078
+ export interface EventPayloadMap {
1079
+ 'device.enrolled': { device: Device };
1080
+ 'device.unenrolled': { device: Device; reason?: string };
1081
+ 'device.blocked': { device: Device; reason: string };
1082
+ 'device.heartbeat': { device: Device; heartbeat: Heartbeat };
1083
+ 'device.locationUpdated': { device: Device; location: DeviceLocation };
1084
+ 'device.statusChanged': { device: Device; oldStatus: DeviceStatus; newStatus: DeviceStatus };
1085
+ 'device.policyChanged': { device: Device; oldPolicyId?: string; newPolicyId?: string };
1086
+ 'app.installed': { device: Device; app: InstalledApp };
1087
+ 'app.uninstalled': { device: Device; packageName: string };
1088
+ 'app.updated': { device: Device; app: InstalledApp; oldVersion: string };
1089
+ 'app.crashed': { device: Device; packageName: string; error?: string };
1090
+ 'app.started': { device: Device; packageName: string };
1091
+ 'app.stopped': { device: Device; packageName: string };
1092
+ 'policy.applied': { device: Device; policy: Policy };
1093
+ 'policy.failed': { device: Device; policy: Policy; error: string };
1094
+ 'command.received': { device: Device; command: Command };
1095
+ 'command.acknowledged': { device: Device; command: Command };
1096
+ 'command.completed': { device: Device; command: Command; result: CommandResult };
1097
+ 'command.failed': { device: Device; command: Command; error: string };
1098
+ 'security.tamper': { device: Device; type: string; details?: unknown };
1099
+ 'security.rootDetected': { device: Device };
1100
+ 'security.screenLocked': { device: Device };
1101
+ 'security.screenUnlocked': { device: Device };
1102
+ custom: Record<string, unknown>;
1103
+ }
1104
+
1105
+ // ============================================
1106
+ // Error Types
1107
+ // ============================================
1108
+
1109
+ export class MDMError extends Error {
1110
+ constructor(
1111
+ message: string,
1112
+ public code: string,
1113
+ public statusCode: number = 500,
1114
+ public details?: unknown
1115
+ ) {
1116
+ super(message);
1117
+ this.name = 'MDMError';
1118
+ }
1119
+ }
1120
+
1121
+ export class DeviceNotFoundError extends MDMError {
1122
+ constructor(deviceId: string) {
1123
+ super(`Device not found: ${deviceId}`, 'DEVICE_NOT_FOUND', 404);
1124
+ }
1125
+ }
1126
+
1127
+ export class PolicyNotFoundError extends MDMError {
1128
+ constructor(policyId: string) {
1129
+ super(`Policy not found: ${policyId}`, 'POLICY_NOT_FOUND', 404);
1130
+ }
1131
+ }
1132
+
1133
+ export class ApplicationNotFoundError extends MDMError {
1134
+ constructor(identifier: string) {
1135
+ super(`Application not found: ${identifier}`, 'APPLICATION_NOT_FOUND', 404);
1136
+ }
1137
+ }
1138
+
1139
+ export class EnrollmentError extends MDMError {
1140
+ constructor(message: string, details?: unknown) {
1141
+ super(message, 'ENROLLMENT_ERROR', 400, details);
1142
+ }
1143
+ }
1144
+
1145
+ export class AuthenticationError extends MDMError {
1146
+ constructor(message: string = 'Authentication required') {
1147
+ super(message, 'AUTHENTICATION_ERROR', 401);
1148
+ }
1149
+ }
1150
+
1151
+ export class AuthorizationError extends MDMError {
1152
+ constructor(message: string = 'Access denied') {
1153
+ super(message, 'AUTHORIZATION_ERROR', 403);
1154
+ }
1155
+ }
1156
+
1157
+ export class ValidationError extends MDMError {
1158
+ constructor(message: string, details?: unknown) {
1159
+ super(message, 'VALIDATION_ERROR', 400, details);
1160
+ }
1161
+ }