@codingfactory/messenger-client 0.1.6 → 0.2.1

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,12 +1,25 @@
1
1
  import type { Message, ReactionSummary } from '../stores/messaging';
2
+ import type { TypingSubscriptionCallbacks } from '../types/typing';
2
3
  export type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
3
4
  export interface EchoChannel {
4
5
  listen(event: string, callback: (payload: unknown) => void): unknown;
5
6
  stopListening?(event: string, callback?: (payload: unknown) => void): unknown;
6
7
  }
8
+ export interface EchoPusherConnection {
9
+ bind?(event: string, callback: (payload?: unknown) => void): void;
10
+ unbind?(event: string, callback?: (payload?: unknown) => void): void;
11
+ state?: string;
12
+ }
7
13
  export interface EchoClient {
8
14
  private(channel: string): EchoChannel;
15
+ join?(channel: string): EchoChannel;
9
16
  leave(channel: string): void;
17
+ disconnect?(): void;
18
+ connector?: {
19
+ pusher?: {
20
+ connection?: EchoPusherConnection;
21
+ };
22
+ };
10
23
  }
11
24
  export interface MessageSentEvent {
12
25
  conversation_id: string;
@@ -64,19 +77,28 @@ export interface ConversationSubscriptionCallbacks {
64
77
  export interface EchoRuntimeConfig {
65
78
  initializedEvent?: string;
66
79
  getEcho?: () => EchoClient | null;
80
+ getConnectionStatus?: () => ConnectionStatus;
67
81
  onConnectionStatusChange?: (callback: (status: ConnectionStatus) => void) => () => void;
82
+ reconnect?: () => Promise<void>;
68
83
  subscribeToPresenceStatus?: (callback: (event: PresenceStatusChangedEvent) => void) => void;
69
84
  unsubscribeFromPresenceStatus?: () => void;
70
85
  subscribeToConversation?: (conversationId: string, callbacks: ConversationSubscriptionCallbacks) => () => void;
71
86
  unsubscribeFromConversation?: (conversationId: string) => void;
87
+ subscribeToTyping?: (conversationId: string, callbacks: TypingSubscriptionCallbacks) => () => void;
88
+ unsubscribeFromTyping?: (conversationId: string) => void;
72
89
  }
73
90
  export declare const getEchoInitializedEvent: () => string;
74
91
  export declare const getEcho: () => EchoClient | null;
92
+ export declare const getEchoConnectionStatus: () => ConnectionStatus;
93
+ export declare const getConnectionStatus: () => ConnectionStatus;
75
94
  export declare const onConnectionStatusChange: (callback: (status: ConnectionStatus) => void) => (() => void);
95
+ export declare const reconnectEcho: () => Promise<void>;
76
96
  export declare const subscribeToPresenceStatus: (callback: (event: PresenceStatusChangedEvent) => void) => void;
77
97
  export declare const unsubscribeFromPresenceStatus: () => void;
78
98
  export declare const subscribeToConversation: (conversationId: string, callbacks: ConversationSubscriptionCallbacks) => (() => void);
79
99
  export declare const unsubscribeFromConversation: (conversationId: string) => void;
100
+ export declare const subscribeToTyping: (conversationId: string, callbacks: TypingSubscriptionCallbacks) => (() => void);
101
+ export declare const unsubscribeFromTyping: (conversationId: string) => void;
80
102
  export declare function setEchoRuntimeConfig(config: EchoRuntimeConfig): void;
81
103
  export declare function resetEchoRuntimeConfig(): void;
82
104
  export declare function isValidMessageSentEvent(data: unknown): data is MessageSentEvent;
@@ -0,0 +1,4 @@
1
+ import type { MessagingHttpClient } from './auth';
2
+ export declare function getMainApi(): MessagingHttpClient;
3
+ export declare function setMainApiClient(client: MessagingHttpClient): void;
4
+ export declare function resetMainApiClient(): void;
@@ -0,0 +1,15 @@
1
+ import type { ConnectionStatus, EchoClient } from './echo';
2
+ export interface MainEchoRuntimeConfig {
3
+ initializedEvent?: string;
4
+ getEcho?: () => EchoClient | null;
5
+ getConnectionStatus?: () => ConnectionStatus;
6
+ onConnectionStatusChange?: (callback: (status: ConnectionStatus) => void) => () => void;
7
+ reconnect?: () => Promise<void>;
8
+ }
9
+ export declare function getMainEchoInitializedEvent(): string;
10
+ export declare function getMainEcho(): EchoClient | null;
11
+ export declare function getMainEchoConnectionStatus(): ConnectionStatus;
12
+ export declare function onMainEchoConnectionStatusChange(callback: (status: ConnectionStatus) => void): () => void;
13
+ export declare function reconnectMainEcho(): Promise<void>;
14
+ export declare function setMainEchoRuntimeConfig(config: MainEchoRuntimeConfig): void;
15
+ export declare function resetMainEchoRuntimeConfig(): void;
@@ -0,0 +1,48 @@
1
+ import type { MessagingHttpClient } from './auth';
2
+ import type { ConnectionStatus, EchoClient, EchoRuntimeConfig } from './echo';
3
+ import { type CreateLogger } from '../utils/logger';
4
+ export interface MessengerBootstrapPayload {
5
+ auth: {
6
+ token: string;
7
+ tokenType: string;
8
+ expiresAt: string | null;
9
+ tenantId: string | null;
10
+ user: {
11
+ id: string;
12
+ handle: string;
13
+ name: string;
14
+ avatarUrl: string | null;
15
+ email: string | null;
16
+ };
17
+ };
18
+ service: {
19
+ baseUrl: string;
20
+ apiBaseUrl: string;
21
+ broadcastingAuthUrl: string;
22
+ reverb: {
23
+ appKey: string;
24
+ host: string;
25
+ port: number;
26
+ scheme: 'http' | 'https';
27
+ path?: string;
28
+ };
29
+ };
30
+ }
31
+ export interface CreateMessengerEchoOptions {
32
+ bootstrapClient: Pick<MessagingHttpClient, 'get'>;
33
+ getSourceToken?: () => string | null;
34
+ initializedEvent?: string;
35
+ bootstrapEndpoint?: string;
36
+ bootstrapRefreshSkewMs?: number;
37
+ requestTimeoutMs?: number;
38
+ presenceFreshnessTtlMs?: number;
39
+ createLogger?: CreateLogger;
40
+ }
41
+ export interface MessengerEchoRuntime extends EchoRuntimeConfig {
42
+ apiClient: MessagingHttpClient;
43
+ getEcho(): EchoClient | null;
44
+ getConnectionStatus(): ConnectionStatus;
45
+ reconnect(): Promise<void>;
46
+ clearRuntime(): void;
47
+ }
48
+ export declare function createMessengerEcho(options: CreateMessengerEchoOptions): MessengerEchoRuntime;
@@ -0,0 +1,29 @@
1
+ export interface PresenceRuntimeConfig {
2
+ sessionOpenUrl?: string;
3
+ sessionHeartbeatUrl?: string;
4
+ sessionCloseUrl?: string;
5
+ statusUrl?: string;
6
+ clientIdStorageKey?: string;
7
+ accountLimitedUntilStorageKey?: string;
8
+ idleAfterMs?: number;
9
+ activityDebounceMs?: number;
10
+ heartbeatIntervalMs?: number;
11
+ userAgentResolver?: () => string;
12
+ authTokenResolver?: () => string | null;
13
+ }
14
+ export interface ResolvedPresenceRuntimeConfig {
15
+ sessionOpenUrl: string;
16
+ sessionHeartbeatUrl: string;
17
+ sessionCloseUrl: string;
18
+ statusUrl: string;
19
+ clientIdStorageKey: string;
20
+ accountLimitedUntilStorageKey: string;
21
+ idleAfterMs: number;
22
+ activityDebounceMs: number;
23
+ heartbeatIntervalMs: number;
24
+ userAgentResolver: () => string;
25
+ authTokenResolver: () => string | null;
26
+ }
27
+ export declare function getPresenceRuntimeConfig(): ResolvedPresenceRuntimeConfig;
28
+ export declare function setPresenceRuntimeConfig(config: PresenceRuntimeConfig): void;
29
+ export declare function resetPresenceRuntimeConfig(): void;
@@ -6,6 +6,7 @@ export interface MessagingCurrentUser {
6
6
  }
7
7
  export interface MessagingAuthStore {
8
8
  currentUser: MessagingCurrentUser | null;
9
+ isAuthenticated?: boolean;
9
10
  }
10
11
  export type UseMessagingAuthStore = () => MessagingAuthStore;
11
12
  export declare function getAuthStore(): UseMessagingAuthStore;
@@ -96,6 +96,7 @@ export interface Conversation {
96
96
  other_user_id?: string;
97
97
  other_online?: boolean;
98
98
  other_presence_status?: PresenceStatus;
99
+ other_last_seen_at?: string | null;
99
100
  }
100
101
  export interface Participant {
101
102
  id: string;
@@ -237,6 +238,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
237
238
  other_user_id?: string | undefined;
238
239
  other_online?: boolean | undefined;
239
240
  other_presence_status?: PresenceStatus | undefined;
241
+ other_last_seen_at?: string | null | undefined;
240
242
  }[];
241
243
  requests: {
242
244
  id: string;
@@ -323,6 +325,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
323
325
  other_user_id?: string | undefined;
324
326
  other_online?: boolean | undefined;
325
327
  other_presence_status?: PresenceStatus | undefined;
328
+ other_last_seen_at?: string | null | undefined;
326
329
  }[];
327
330
  folders: {
328
331
  id: string;
@@ -491,6 +494,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
491
494
  other_user_id?: string | undefined;
492
495
  other_online?: boolean | undefined;
493
496
  other_presence_status?: PresenceStatus | undefined;
497
+ other_last_seen_at?: string | null | undefined;
494
498
  }[];
495
499
  requests: {
496
500
  id: string;
@@ -577,6 +581,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
577
581
  other_user_id?: string | undefined;
578
582
  other_online?: boolean | undefined;
579
583
  other_presence_status?: PresenceStatus | undefined;
584
+ other_last_seen_at?: string | null | undefined;
580
585
  }[];
581
586
  folders: {
582
587
  id: string;
@@ -745,6 +750,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
745
750
  other_user_id?: string | undefined;
746
751
  other_online?: boolean | undefined;
747
752
  other_presence_status?: PresenceStatus | undefined;
753
+ other_last_seen_at?: string | null | undefined;
748
754
  }[];
749
755
  requests: {
750
756
  id: string;
@@ -831,6 +837,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
831
837
  other_user_id?: string | undefined;
832
838
  other_online?: boolean | undefined;
833
839
  other_presence_status?: PresenceStatus | undefined;
840
+ other_last_seen_at?: string | null | undefined;
834
841
  }[];
835
842
  folders: {
836
843
  id: string;
@@ -999,6 +1006,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
999
1006
  other_user_id?: string | undefined;
1000
1007
  other_online?: boolean | undefined;
1001
1008
  other_presence_status?: PresenceStatus | undefined;
1009
+ other_last_seen_at?: string | null | undefined;
1002
1010
  }[];
1003
1011
  requests: {
1004
1012
  id: string;
@@ -1085,6 +1093,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
1085
1093
  other_user_id?: string | undefined;
1086
1094
  other_online?: boolean | undefined;
1087
1095
  other_presence_status?: PresenceStatus | undefined;
1096
+ other_last_seen_at?: string | null | undefined;
1088
1097
  }[];
1089
1098
  folders: {
1090
1099
  id: string;
@@ -1253,6 +1262,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
1253
1262
  other_user_id?: string | undefined;
1254
1263
  other_online?: boolean | undefined;
1255
1264
  other_presence_status?: PresenceStatus | undefined;
1265
+ other_last_seen_at?: string | null | undefined;
1256
1266
  }[];
1257
1267
  requests: {
1258
1268
  id: string;
@@ -1339,6 +1349,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
1339
1349
  other_user_id?: string | undefined;
1340
1350
  other_online?: boolean | undefined;
1341
1351
  other_presence_status?: PresenceStatus | undefined;
1352
+ other_last_seen_at?: string | null | undefined;
1342
1353
  }[];
1343
1354
  folders: {
1344
1355
  id: string;
@@ -1507,6 +1518,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
1507
1518
  other_user_id?: string | undefined;
1508
1519
  other_online?: boolean | undefined;
1509
1520
  other_presence_status?: PresenceStatus | undefined;
1521
+ other_last_seen_at?: string | null | undefined;
1510
1522
  }[];
1511
1523
  requests: {
1512
1524
  id: string;
@@ -1593,6 +1605,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
1593
1605
  other_user_id?: string | undefined;
1594
1606
  other_online?: boolean | undefined;
1595
1607
  other_presence_status?: PresenceStatus | undefined;
1608
+ other_last_seen_at?: string | null | undefined;
1596
1609
  }[];
1597
1610
  folders: {
1598
1611
  id: string;
@@ -1761,6 +1774,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
1761
1774
  other_user_id?: string | undefined;
1762
1775
  other_online?: boolean | undefined;
1763
1776
  other_presence_status?: PresenceStatus | undefined;
1777
+ other_last_seen_at?: string | null | undefined;
1764
1778
  }[];
1765
1779
  requests: {
1766
1780
  id: string;
@@ -1847,6 +1861,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
1847
1861
  other_user_id?: string | undefined;
1848
1862
  other_online?: boolean | undefined;
1849
1863
  other_presence_status?: PresenceStatus | undefined;
1864
+ other_last_seen_at?: string | null | undefined;
1850
1865
  }[];
1851
1866
  folders: {
1852
1867
  id: string;
@@ -2015,6 +2030,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
2015
2030
  other_user_id?: string | undefined;
2016
2031
  other_online?: boolean | undefined;
2017
2032
  other_presence_status?: PresenceStatus | undefined;
2033
+ other_last_seen_at?: string | null | undefined;
2018
2034
  }[];
2019
2035
  requests: {
2020
2036
  id: string;
@@ -2101,6 +2117,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
2101
2117
  other_user_id?: string | undefined;
2102
2118
  other_online?: boolean | undefined;
2103
2119
  other_presence_status?: PresenceStatus | undefined;
2120
+ other_last_seen_at?: string | null | undefined;
2104
2121
  }[];
2105
2122
  folders: {
2106
2123
  id: string;
@@ -2269,6 +2286,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
2269
2286
  other_user_id?: string | undefined;
2270
2287
  other_online?: boolean | undefined;
2271
2288
  other_presence_status?: PresenceStatus | undefined;
2289
+ other_last_seen_at?: string | null | undefined;
2272
2290
  }[];
2273
2291
  requests: {
2274
2292
  id: string;
@@ -2355,6 +2373,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
2355
2373
  other_user_id?: string | undefined;
2356
2374
  other_online?: boolean | undefined;
2357
2375
  other_presence_status?: PresenceStatus | undefined;
2376
+ other_last_seen_at?: string | null | undefined;
2358
2377
  }[];
2359
2378
  folders: {
2360
2379
  id: string;
@@ -2523,6 +2542,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
2523
2542
  other_user_id?: string | undefined;
2524
2543
  other_online?: boolean | undefined;
2525
2544
  other_presence_status?: PresenceStatus | undefined;
2545
+ other_last_seen_at?: string | null | undefined;
2526
2546
  }[];
2527
2547
  requests: {
2528
2548
  id: string;
@@ -2609,6 +2629,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
2609
2629
  other_user_id?: string | undefined;
2610
2630
  other_online?: boolean | undefined;
2611
2631
  other_presence_status?: PresenceStatus | undefined;
2632
+ other_last_seen_at?: string | null | undefined;
2612
2633
  }[];
2613
2634
  folders: {
2614
2635
  id: string;
@@ -2777,6 +2798,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
2777
2798
  other_user_id?: string | undefined;
2778
2799
  other_online?: boolean | undefined;
2779
2800
  other_presence_status?: PresenceStatus | undefined;
2801
+ other_last_seen_at?: string | null | undefined;
2780
2802
  }[];
2781
2803
  requests: {
2782
2804
  id: string;
@@ -2863,6 +2885,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
2863
2885
  other_user_id?: string | undefined;
2864
2886
  other_online?: boolean | undefined;
2865
2887
  other_presence_status?: PresenceStatus | undefined;
2888
+ other_last_seen_at?: string | null | undefined;
2866
2889
  }[];
2867
2890
  folders: {
2868
2891
  id: string;
@@ -3031,6 +3054,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
3031
3054
  other_user_id?: string | undefined;
3032
3055
  other_online?: boolean | undefined;
3033
3056
  other_presence_status?: PresenceStatus | undefined;
3057
+ other_last_seen_at?: string | null | undefined;
3034
3058
  }[];
3035
3059
  requests: {
3036
3060
  id: string;
@@ -3117,6 +3141,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
3117
3141
  other_user_id?: string | undefined;
3118
3142
  other_online?: boolean | undefined;
3119
3143
  other_presence_status?: PresenceStatus | undefined;
3144
+ other_last_seen_at?: string | null | undefined;
3120
3145
  }[];
3121
3146
  folders: {
3122
3147
  id: string;
@@ -3285,6 +3310,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
3285
3310
  other_user_id?: string | undefined;
3286
3311
  other_online?: boolean | undefined;
3287
3312
  other_presence_status?: PresenceStatus | undefined;
3313
+ other_last_seen_at?: string | null | undefined;
3288
3314
  }[];
3289
3315
  requests: {
3290
3316
  id: string;
@@ -3371,6 +3397,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
3371
3397
  other_user_id?: string | undefined;
3372
3398
  other_online?: boolean | undefined;
3373
3399
  other_presence_status?: PresenceStatus | undefined;
3400
+ other_last_seen_at?: string | null | undefined;
3374
3401
  }[];
3375
3402
  folders: {
3376
3403
  id: string;
@@ -3539,6 +3566,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
3539
3566
  other_user_id?: string | undefined;
3540
3567
  other_online?: boolean | undefined;
3541
3568
  other_presence_status?: PresenceStatus | undefined;
3569
+ other_last_seen_at?: string | null | undefined;
3542
3570
  }[];
3543
3571
  requests: {
3544
3572
  id: string;
@@ -3625,6 +3653,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
3625
3653
  other_user_id?: string | undefined;
3626
3654
  other_online?: boolean | undefined;
3627
3655
  other_presence_status?: PresenceStatus | undefined;
3656
+ other_last_seen_at?: string | null | undefined;
3628
3657
  }[];
3629
3658
  folders: {
3630
3659
  id: string;
@@ -3747,7 +3776,7 @@ export declare const useMessagingStore: import("pinia").StoreDefinition<"messagi
3747
3776
  addTypingUser(conversationId: string, user: TypingUser): void;
3748
3777
  removeTypingUser(conversationId: string, userId: string): void;
3749
3778
  updateConversationLastMessage(conversationId: string, message: Message): void;
3750
- updatePresenceForUser(userId: string, status: PresenceStatus): void;
3779
+ updatePresenceForUser(userId: string, status: PresenceStatus, lastSeenAt?: string | null): void;
3751
3780
  acceptRequest(conversationId: string): Promise<void>;
3752
3781
  declineRequest(conversationId: string): Promise<void>;
3753
3782
  searchConversations(query: string, filter?: ConversationListFilter, folderId?: string): Promise<Conversation[]>;
@@ -0,0 +1,32 @@
1
+ export interface TypingStartEvent {
2
+ user_id: string;
3
+ user: {
4
+ id: string;
5
+ name: string;
6
+ avatar?: string;
7
+ };
8
+ conversation_id: string;
9
+ started_at: string;
10
+ }
11
+ export interface TypingStopEvent {
12
+ user_id: string;
13
+ conversation_id: string;
14
+ stopped_at: string;
15
+ }
16
+ export interface TypingStateChangedEvent {
17
+ state?: 'start' | 'stop' | 'typing' | 'idle' | 'viewing';
18
+ user_id: string;
19
+ conversation_id: string;
20
+ user?: {
21
+ id: string;
22
+ name: string;
23
+ avatar?: string;
24
+ };
25
+ ts?: string;
26
+ started_at?: string;
27
+ stopped_at?: string;
28
+ }
29
+ export interface TypingSubscriptionCallbacks {
30
+ onTypingStart?: (data: TypingStartEvent) => void;
31
+ onTypingStop?: (data: TypingStopEvent) => void;
32
+ }
@@ -0,0 +1,18 @@
1
+ import type { PresenceStatus } from '../stores/messaging';
2
+ export interface PresenceSnapshotLike {
3
+ status?: string | null;
4
+ last_seen_at?: string | null;
5
+ }
6
+ export interface ResolvePresenceStatusOptions {
7
+ now?: number;
8
+ staleAfterMs?: number;
9
+ }
10
+ export declare const DEFAULT_PRESENCE_STALE_AFTER_MS = 60000;
11
+ export declare function normalizePresenceStatus(value: string | null | undefined): PresenceStatus | null;
12
+ export declare function normalizePresenceTimestamp(value: string | null | undefined): string | null;
13
+ export declare function parsePresenceTimestamp(value: string | null | undefined): Date | null;
14
+ export declare function resolveFreshPresenceStatus(status: PresenceStatus | null, lastSeenAt: string | null, options?: ResolvePresenceStatusOptions): PresenceStatus | null;
15
+ export declare function resolvePresenceSnapshot(snapshot: PresenceSnapshotLike | null | undefined, options?: ResolvePresenceStatusOptions): {
16
+ status: PresenceStatus | null;
17
+ lastSeenAt: string | null;
18
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codingfactory/messenger-client",
3
- "version": "0.1.6",
3
+ "version": "0.2.1",
4
4
  "description": "Shared messaging frontend state, API helpers, and realtime composables.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -24,7 +24,10 @@
24
24
  "check": "pnpm lint && pnpm typecheck && pnpm test && pnpm build"
25
25
  },
26
26
  "dependencies": {
27
- "axios": "^1.13.6"
27
+ "axios": "^1.13.6",
28
+ "ionicons": "^8.0.13",
29
+ "laravel-echo": "^2.2.0",
30
+ "pusher-js": "^8.4.0"
28
31
  },
29
32
  "peerDependencies": {
30
33
  "pinia": "^3.0.0",
@@ -34,6 +37,7 @@
34
37
  "devDependencies": {
35
38
  "@eslint/js": "^10.0.1",
36
39
  "@types/node": "^25.3.5",
40
+ "@vue/test-utils": "^2.4.6",
37
41
  "eslint": "^10.0.3",
38
42
  "globals": "^17.4.0",
39
43
  "jsdom": "^28.1.0",