@fivem-nui/svelte 0.0.0-stage → 1.0.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/README.md +284 -2
- package/dist/actions/clickOutside/clickOutside.d.ts +24 -0
- package/dist/actions/clickOutside/clickOutside.d.ts.map +1 -0
- package/dist/actions/clickOutside/clickOutside.js +62 -0
- package/dist/actions/clickOutside/clickOutside.js.map +1 -0
- package/dist/actions/clickOutside/handler.d.ts +11 -0
- package/dist/actions/clickOutside/handler.d.ts.map +1 -0
- package/dist/actions/clickOutside/handler.js +45 -0
- package/dist/actions/clickOutside/handler.js.map +1 -0
- package/dist/actions/clickOutside/index.d.ts +4 -0
- package/dist/actions/clickOutside/index.d.ts.map +1 -0
- package/dist/actions/clickOutside/index.js +4 -0
- package/dist/actions/clickOutside/index.js.map +1 -0
- package/dist/actions/clickOutside/types.d.ts +36 -0
- package/dist/actions/clickOutside/types.d.ts.map +1 -0
- package/dist/actions/clickOutside/types.js +6 -0
- package/dist/actions/clickOutside/types.js.map +1 -0
- package/dist/actions/clickOutside.d.ts +63 -0
- package/dist/actions/clickOutside.d.ts.map +1 -0
- package/dist/actions/clickOutside.js +106 -0
- package/dist/actions/clickOutside.js.map +1 -0
- package/dist/actions/escape/escape.d.ts +26 -0
- package/dist/actions/escape/escape.d.ts.map +1 -0
- package/dist/actions/escape/escape.js +74 -0
- package/dist/actions/escape/escape.js.map +1 -0
- package/dist/actions/escape/handler.d.ts +11 -0
- package/dist/actions/escape/handler.d.ts.map +1 -0
- package/dist/actions/escape/handler.js +50 -0
- package/dist/actions/escape/handler.js.map +1 -0
- package/dist/actions/escape/index.d.ts +4 -0
- package/dist/actions/escape/index.d.ts.map +1 -0
- package/dist/actions/escape/index.js +4 -0
- package/dist/actions/escape/index.js.map +1 -0
- package/dist/actions/escape/types.d.ts +70 -0
- package/dist/actions/escape/types.d.ts.map +1 -0
- package/dist/actions/escape/types.js +12 -0
- package/dist/actions/escape/types.js.map +1 -0
- package/dist/actions/escape.d.ts +99 -0
- package/dist/actions/escape.d.ts.map +1 -0
- package/dist/actions/escape.js +128 -0
- package/dist/actions/escape.js.map +1 -0
- package/dist/actions/focusTrap/focusTrap.d.ts +19 -0
- package/dist/actions/focusTrap/focusTrap.d.ts.map +1 -0
- package/dist/actions/focusTrap/focusTrap.js +198 -0
- package/dist/actions/focusTrap/focusTrap.js.map +1 -0
- package/dist/actions/focusTrap/focusables.d.ts +39 -0
- package/dist/actions/focusTrap/focusables.d.ts.map +1 -0
- package/dist/actions/focusTrap/focusables.js +142 -0
- package/dist/actions/focusTrap/focusables.js.map +1 -0
- package/dist/actions/focusTrap/index.d.ts +5 -0
- package/dist/actions/focusTrap/index.d.ts.map +1 -0
- package/dist/actions/focusTrap/index.js +5 -0
- package/dist/actions/focusTrap/index.js.map +1 -0
- package/dist/actions/focusTrap/trapStack.d.ts +33 -0
- package/dist/actions/focusTrap/trapStack.d.ts.map +1 -0
- package/dist/actions/focusTrap/trapStack.js +84 -0
- package/dist/actions/focusTrap/trapStack.js.map +1 -0
- package/dist/actions/focusTrap/types.d.ts +50 -0
- package/dist/actions/focusTrap/types.d.ts.map +1 -0
- package/dist/actions/focusTrap/types.js +10 -0
- package/dist/actions/focusTrap/types.js.map +1 -0
- package/dist/actions/focusTrap.d.ts +24 -0
- package/dist/actions/focusTrap.d.ts.map +1 -0
- package/dist/actions/focusTrap.js +201 -0
- package/dist/actions/focusTrap.js.map +1 -0
- package/dist/actions/focusTrap.types.d.ts +50 -0
- package/dist/actions/focusTrap.types.d.ts.map +1 -0
- package/dist/actions/focusTrap.types.js +10 -0
- package/dist/actions/focusTrap.types.js.map +1 -0
- package/dist/actions/focusables.d.ts +39 -0
- package/dist/actions/focusables.d.ts.map +1 -0
- package/dist/actions/focusables.js +142 -0
- package/dist/actions/focusables.js.map +1 -0
- package/dist/actions/index.d.ts +5 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +5 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/layer/index.d.ts +4 -0
- package/dist/actions/layer/index.d.ts.map +1 -0
- package/dist/actions/layer/index.js +4 -0
- package/dist/actions/layer/index.js.map +1 -0
- package/dist/actions/layer/layer.d.ts +28 -0
- package/dist/actions/layer/layer.d.ts.map +1 -0
- package/dist/actions/layer/layer.js +118 -0
- package/dist/actions/layer/layer.js.map +1 -0
- package/dist/actions/layer/layerStack.d.ts +10 -0
- package/dist/actions/layer/layerStack.d.ts.map +1 -0
- package/dist/actions/layer/layerStack.js +153 -0
- package/dist/actions/layer/layerStack.js.map +1 -0
- package/dist/actions/layer/types.d.ts +105 -0
- package/dist/actions/layer/types.d.ts.map +1 -0
- package/dist/actions/layer/types.js +14 -0
- package/dist/actions/layer/types.js.map +1 -0
- package/dist/actions/layer.d.ts +133 -0
- package/dist/actions/layer.d.ts.map +1 -0
- package/dist/actions/layer.js +269 -0
- package/dist/actions/layer.js.map +1 -0
- package/dist/actions/trapStack.d.ts +33 -0
- package/dist/actions/trapStack.d.ts.map +1 -0
- package/dist/actions/trapStack.js +84 -0
- package/dist/actions/trapStack.js.map +1 -0
- package/dist/client.d.ts +179 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +176 -0
- package/dist/client.js.map +1 -0
- package/dist/context/index.d.ts +68 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +112 -0
- package/dist/context/index.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/mock/index.d.ts +27 -0
- package/dist/mock/index.d.ts.map +1 -0
- package/dist/mock/index.js +137 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/mock/interceptor.d.ts +19 -0
- package/dist/mock/interceptor.d.ts.map +1 -0
- package/dist/mock/interceptor.js +128 -0
- package/dist/mock/interceptor.js.map +1 -0
- package/dist/mock/types.d.ts +100 -0
- package/dist/mock/types.d.ts.map +1 -0
- package/dist/mock/types.js +27 -0
- package/dist/mock/types.js.map +1 -0
- package/dist/mock/url.d.ts +14 -0
- package/dist/mock/url.d.ts.map +1 -0
- package/dist/mock/url.js +38 -0
- package/dist/mock/url.js.map +1 -0
- package/dist/runes/clickOutside.svelte.d.ts +29 -0
- package/dist/runes/clickOutside.svelte.d.ts.map +1 -0
- package/dist/runes/clickOutside.svelte.js +42 -0
- package/dist/runes/clickOutside.svelte.js.map +1 -0
- package/dist/runes/escape.svelte.d.ts +34 -0
- package/dist/runes/escape.svelte.d.ts.map +1 -0
- package/dist/runes/escape.svelte.js +44 -0
- package/dist/runes/escape.svelte.js.map +1 -0
- package/dist/runes/focusTrap.svelte.d.ts +21 -0
- package/dist/runes/focusTrap.svelte.d.ts.map +1 -0
- package/dist/runes/focusTrap.svelte.js +37 -0
- package/dist/runes/focusTrap.svelte.js.map +1 -0
- package/dist/runes/index.svelte.d.ts +8 -0
- package/dist/runes/index.svelte.d.ts.map +1 -0
- package/dist/runes/index.svelte.js +8 -0
- package/dist/runes/index.svelte.js.map +1 -0
- package/dist/runes/layer.svelte.d.ts +36 -0
- package/dist/runes/layer.svelte.d.ts.map +1 -0
- package/dist/runes/layer.svelte.js +48 -0
- package/dist/runes/layer.svelte.js.map +1 -0
- package/dist/runes/query.svelte.d.ts +104 -0
- package/dist/runes/query.svelte.d.ts.map +1 -0
- package/dist/runes/query.svelte.js +179 -0
- package/dist/runes/query.svelte.js.map +1 -0
- package/dist/runes/state.svelte.d.ts +72 -0
- package/dist/runes/state.svelte.d.ts.map +1 -0
- package/dist/runes/state.svelte.js +98 -0
- package/dist/runes/state.svelte.js.map +1 -0
- package/dist/runes/window.svelte.d.ts +120 -0
- package/dist/runes/window.svelte.d.ts.map +1 -0
- package/dist/runes/window.svelte.js +196 -0
- package/dist/runes/window.svelte.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/nui.d.ts +34 -0
- package/dist/types/nui.d.ts.map +1 -0
- package/dist/types/nui.js +17 -0
- package/dist/types/nui.js.map +1 -0
- package/dist/utils/env.d.ts +21 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +36 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/error.d.ts +11 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/error.js +30 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/keyboard.d.ts +127 -0
- package/dist/utils/keyboard.d.ts.map +1 -0
- package/dist/utils/keyboard.js +152 -0
- package/dist/utils/keyboard.js.map +1 -0
- package/package.json +76 -4
- package/src/actions/clickOutside/clickOutside.ts +78 -0
- package/src/actions/clickOutside/handler.ts +49 -0
- package/src/actions/clickOutside/index.ts +11 -0
- package/src/actions/clickOutside/types.ts +54 -0
- package/src/actions/escape/escape.ts +90 -0
- package/src/actions/escape/handler.ts +52 -0
- package/src/actions/escape/index.ts +11 -0
- package/src/actions/escape/types.ts +88 -0
- package/src/actions/focusTrap/focusTrap.ts +228 -0
- package/src/actions/focusTrap/focusables.ts +155 -0
- package/src/actions/focusTrap/index.ts +22 -0
- package/src/actions/focusTrap/trapStack.ts +109 -0
- package/src/actions/focusTrap/types.ts +64 -0
- package/src/actions/index.ts +35 -0
- package/src/actions/layer/index.ts +13 -0
- package/src/actions/layer/layer.ts +140 -0
- package/src/actions/layer/layerStack.ts +169 -0
- package/src/actions/layer/types.ts +132 -0
- package/src/client.ts +323 -0
- package/src/context/index.ts +127 -0
- package/src/index.ts +53 -0
- package/src/mock/index.ts +176 -0
- package/src/mock/interceptor.ts +177 -0
- package/src/mock/types.ts +125 -0
- package/src/mock/url.ts +37 -0
- package/src/runes/clickOutside.svelte.ts +53 -0
- package/src/runes/escape.svelte.ts +71 -0
- package/src/runes/focusTrap.svelte.ts +56 -0
- package/src/runes/index.svelte.ts +38 -0
- package/src/runes/layer.svelte.ts +74 -0
- package/src/runes/query.svelte.ts +313 -0
- package/src/runes/state.svelte.ts +175 -0
- package/src/runes/window.svelte.ts +308 -0
- package/src/utils/env.ts +36 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/keyboard.ts +244 -0
package/src/client.ts
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { NuiClient as CoreNuiClient } from '@fivem-nui/core';
|
|
2
|
+
import type {
|
|
3
|
+
NuiClientOptions,
|
|
4
|
+
NuiEventKey,
|
|
5
|
+
NuiEventPayload,
|
|
6
|
+
NuiEventRequest,
|
|
7
|
+
NuiEventResponse,
|
|
8
|
+
NuiWindowAction,
|
|
9
|
+
SendPayload
|
|
10
|
+
} from '@fivem-nui/core';
|
|
11
|
+
import { installGlobalErrorHandlers, type GlobalErrorListenerOptions } from '@fivem-nui/error';
|
|
12
|
+
|
|
13
|
+
type UntypedNuiClient = Omit<
|
|
14
|
+
InstanceType<typeof CoreNuiClient>,
|
|
15
|
+
'on' | 'once' | 'off' | 'send' | 'sendAndWait' | 'request' | 'callback' | 'onWindow'
|
|
16
|
+
> & {
|
|
17
|
+
on(eventName: string, handler: (data: unknown) => void): () => void;
|
|
18
|
+
once(eventName: string, handler: (data: unknown) => void): () => void;
|
|
19
|
+
off(eventName: string, handler?: (data: unknown) => void): void;
|
|
20
|
+
send(eventName: string, payload?: unknown): void;
|
|
21
|
+
sendAndWait(eventName: string, payload?: unknown): Promise<void>;
|
|
22
|
+
request<TResponse = unknown>(
|
|
23
|
+
eventName: string,
|
|
24
|
+
request?: unknown,
|
|
25
|
+
timeout?: number,
|
|
26
|
+
signal?: AbortSignal
|
|
27
|
+
): Promise<TResponse>;
|
|
28
|
+
callback(
|
|
29
|
+
eventName: string,
|
|
30
|
+
handler: (
|
|
31
|
+
request: unknown
|
|
32
|
+
) => Promise<{ success: boolean; value?: unknown; error?: { code: string; message: string } }>
|
|
33
|
+
): void;
|
|
34
|
+
onWindow(handler: (action: string, window: string | undefined) => void): () => void;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Configuration options for creating a {@link SvelteNuiClient}.
|
|
39
|
+
*/
|
|
40
|
+
export interface SvelteNuiClientOptions extends Partial<NuiClientOptions> {
|
|
41
|
+
/**
|
|
42
|
+
* Optional pre-configured `@fivem-nui/core` client instance to wrap.
|
|
43
|
+
*/
|
|
44
|
+
client?: InstanceType<typeof CoreNuiClient>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Whether to automatically start listening for window `message` events upon instantiation.
|
|
48
|
+
* @defaultValue true
|
|
49
|
+
*/
|
|
50
|
+
autoInitialize?: boolean;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether to install global window error & unhandled rejection listeners.
|
|
54
|
+
* If `true`, uses default configuration. You can also pass custom {@link GlobalErrorListenerOptions}.
|
|
55
|
+
* @defaultValue false
|
|
56
|
+
*/
|
|
57
|
+
captureGlobalErrors?: boolean | GlobalErrorListenerOptions;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Svelte NUI Client adapter.
|
|
62
|
+
*
|
|
63
|
+
* Wraps `@fivem-nui/core`'s transport layer with type-safe overloads for contract maps,
|
|
64
|
+
* event listeners, RPC requests, and lifecycle tracking.
|
|
65
|
+
*/
|
|
66
|
+
export class SvelteNuiClient {
|
|
67
|
+
private readonly _typedClient: InstanceType<typeof CoreNuiClient>;
|
|
68
|
+
private readonly _client: UntypedNuiClient;
|
|
69
|
+
private readonly _listeners = new Set<() => void>();
|
|
70
|
+
private _listening = false;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a new SvelteNuiClient instance.
|
|
74
|
+
*
|
|
75
|
+
* @param options - Configuration options for the client adapter and underlying transport.
|
|
76
|
+
*/
|
|
77
|
+
constructor(options: SvelteNuiClientOptions = {}) {
|
|
78
|
+
this._typedClient = options.client ?? new CoreNuiClient(options);
|
|
79
|
+
this._client = this._typedClient as unknown as UntypedNuiClient;
|
|
80
|
+
|
|
81
|
+
if (options.autoInitialize !== false) {
|
|
82
|
+
this.listen();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (options.captureGlobalErrors) {
|
|
86
|
+
const errorOpts = typeof options.captureGlobalErrors === 'object' ? options.captureGlobalErrors : {};
|
|
87
|
+
const unsubErrors = installGlobalErrorHandlers(errorOpts);
|
|
88
|
+
this._listeners.add(unsubErrors);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private _ensureListening(): void {
|
|
93
|
+
if (!this._listening) {
|
|
94
|
+
const unsub = this._client.listen();
|
|
95
|
+
this._listeners.add(unsub);
|
|
96
|
+
this._listening = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// --- Subscriptions ---
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Subscribes to an event emitted from C# / game backend.
|
|
104
|
+
*
|
|
105
|
+
* @param eventName - Name of the event emitted by C# / game backend.
|
|
106
|
+
* @param handler - Callback function invoked when the event payload arrives.
|
|
107
|
+
* @returns Unsubscribe function to remove the event listener.
|
|
108
|
+
*/
|
|
109
|
+
on<K extends NuiEventKey>(eventName: K, handler: (data: NuiEventPayload<K>) => void): () => void;
|
|
110
|
+
on<T = unknown>(eventName: string, handler: (data: T) => void): () => void;
|
|
111
|
+
on(eventName: string, handler: (data: any) => void): () => void {
|
|
112
|
+
this._ensureListening();
|
|
113
|
+
const unsub = this._client.on(eventName, handler);
|
|
114
|
+
this._listeners.add(unsub);
|
|
115
|
+
return () => {
|
|
116
|
+
this._listeners.delete(unsub);
|
|
117
|
+
unsub();
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Subscribes to an event once and automatically unsubscribes upon first receipt.
|
|
123
|
+
*
|
|
124
|
+
* @param eventName - Name of the event to listen for once.
|
|
125
|
+
* @param handler - Callback function invoked upon first receipt of the event.
|
|
126
|
+
* @returns Unsubscribe function to remove the one-time listener prior to firing.
|
|
127
|
+
*/
|
|
128
|
+
once<K extends NuiEventKey>(eventName: K, handler: (data: NuiEventPayload<K>) => void): () => void;
|
|
129
|
+
once<T = unknown>(eventName: string, handler: (data: T) => void): () => void;
|
|
130
|
+
once(eventName: string, handler: (data: any) => void): () => void {
|
|
131
|
+
this._ensureListening();
|
|
132
|
+
let unsub: () => void;
|
|
133
|
+
const wrappedHandler = (data: any) => {
|
|
134
|
+
this._listeners.delete(unsub);
|
|
135
|
+
handler(data);
|
|
136
|
+
};
|
|
137
|
+
unsub = this._client.once(eventName, wrappedHandler);
|
|
138
|
+
this._listeners.add(unsub);
|
|
139
|
+
return () => {
|
|
140
|
+
this._listeners.delete(unsub);
|
|
141
|
+
unsub();
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Removes an event listener.
|
|
147
|
+
*
|
|
148
|
+
* @param eventName - Name of the event to stop listening for.
|
|
149
|
+
* @param handler - Specific handler callback to remove. If omitted, removes all handlers for this event.
|
|
150
|
+
*/
|
|
151
|
+
off(eventName: string, handler?: (data: unknown) => void): void {
|
|
152
|
+
this._client.off(eventName, handler);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// --- Sending ---
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Sends a fire-and-forget message to C# via the NUI transport layer.
|
|
159
|
+
*
|
|
160
|
+
* @param eventName - Target event name on the game backend.
|
|
161
|
+
* @param payload - Optional data payload to transmit.
|
|
162
|
+
*/
|
|
163
|
+
send<K extends NuiEventKey>(eventName: K, payload?: SendPayload<K>): void;
|
|
164
|
+
send(eventName: string, payload?: unknown): void;
|
|
165
|
+
send(eventName: string, payload?: unknown): void {
|
|
166
|
+
this._ensureListening();
|
|
167
|
+
this._client.send(eventName, payload);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Sends a message to C# and awaits delivery to the transport layer.
|
|
172
|
+
*
|
|
173
|
+
* @param eventName - Target event name on the game backend.
|
|
174
|
+
* @param payload - Optional data payload to transmit.
|
|
175
|
+
* @returns Promise that resolves once the transport layer confirms transmission.
|
|
176
|
+
*/
|
|
177
|
+
async sendAndWait<K extends NuiEventKey>(eventName: K, payload?: SendPayload<K>): Promise<void>;
|
|
178
|
+
async sendAndWait(eventName: string, payload?: unknown): Promise<void>;
|
|
179
|
+
async sendAndWait(eventName: string, payload?: unknown): Promise<void> {
|
|
180
|
+
this._ensureListening();
|
|
181
|
+
await this._client.sendAndWait(eventName, payload);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// --- Requests (UI -> C#) ---
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Sends an RPC request to C# and waits for the correlated response envelope.
|
|
188
|
+
*
|
|
189
|
+
* @param eventName - RPC event name on the game backend.
|
|
190
|
+
* @param request - Request payload data.
|
|
191
|
+
* @param timeout - Request timeout in milliseconds.
|
|
192
|
+
* @param signal - AbortSignal to cancel the pending request.
|
|
193
|
+
* @returns Promise resolving to the correlated response payload from C#.
|
|
194
|
+
*/
|
|
195
|
+
request<K extends NuiEventKey>(
|
|
196
|
+
eventName: K,
|
|
197
|
+
request?: NuiEventRequest<K>,
|
|
198
|
+
timeout?: number,
|
|
199
|
+
signal?: AbortSignal
|
|
200
|
+
): Promise<NuiEventResponse<K>>;
|
|
201
|
+
request<TRequest = unknown, TResponse = unknown>(
|
|
202
|
+
eventName: string,
|
|
203
|
+
request?: TRequest,
|
|
204
|
+
timeout?: number,
|
|
205
|
+
signal?: AbortSignal
|
|
206
|
+
): Promise<TResponse>;
|
|
207
|
+
async request(
|
|
208
|
+
eventName: string,
|
|
209
|
+
request?: unknown,
|
|
210
|
+
timeout?: number,
|
|
211
|
+
signal?: AbortSignal
|
|
212
|
+
): Promise<unknown> {
|
|
213
|
+
this._ensureListening();
|
|
214
|
+
return this._client.request(eventName, request, timeout, signal);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// --- Lifecycle Callbacks ---
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Sends a notification to a specific native callback endpoint (e.g. `'ready'`, `'close'`).
|
|
221
|
+
*
|
|
222
|
+
* @param callbackName - Name of the native callback endpoint.
|
|
223
|
+
* @param payload - Optional data to send with the notification.
|
|
224
|
+
*/
|
|
225
|
+
notify(callbackName: string, payload?: unknown): void {
|
|
226
|
+
this._client.notify(callbackName, payload);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Sends a notification and awaits HTTP transmission confirmation.
|
|
231
|
+
*
|
|
232
|
+
* @param callbackName - Name of the native callback endpoint.
|
|
233
|
+
* @param payload - Optional data to send with the notification.
|
|
234
|
+
* @returns Promise that resolves once the HTTP notification is acknowledged.
|
|
235
|
+
*/
|
|
236
|
+
async notifyAndWait(callbackName: string, payload?: unknown): Promise<void> {
|
|
237
|
+
await this._client.notifyAndWait(callbackName, payload);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Registers a handler for requests initiated from C# to the UI.
|
|
242
|
+
*
|
|
243
|
+
* @param eventName - Name of the incoming RPC endpoint from C#.
|
|
244
|
+
* @param handler - Handler function returning a promise with response envelope.
|
|
245
|
+
*/
|
|
246
|
+
callback(
|
|
247
|
+
eventName: string,
|
|
248
|
+
handler: (request: unknown) => Promise<{ success: boolean; value?: unknown; error?: { code: string; message: string } }>
|
|
249
|
+
): void {
|
|
250
|
+
this._ensureListening();
|
|
251
|
+
this._client.callback(eventName, handler);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Unregisters an incoming RPC callback handler previously registered with {@link callback}.
|
|
256
|
+
*
|
|
257
|
+
* @param eventName - Name of the RPC endpoint to unregister.
|
|
258
|
+
* @returns True if a handler was registered and removed, false otherwise.
|
|
259
|
+
*/
|
|
260
|
+
unregisterCallback(eventName: string): boolean {
|
|
261
|
+
return this._client.unregisterCallback(eventName);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// --- Window Lifecycle ---
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Listens for window lifecycle actions from C# (`'open'`, `'close'`, `'focus'`, `'blur'`).
|
|
268
|
+
*
|
|
269
|
+
* @param handler - Callback invoked with the lifecycle action and target window identifier.
|
|
270
|
+
* @returns Unsubscribe function to remove the window action listener.
|
|
271
|
+
*/
|
|
272
|
+
onWindow(handler: (action: NuiWindowAction, window: string | undefined) => void): () => void {
|
|
273
|
+
this._ensureListening();
|
|
274
|
+
const unsub = this._client.onWindow((action, win) => handler(action as NuiWindowAction, win));
|
|
275
|
+
this._listeners.add(unsub);
|
|
276
|
+
return () => {
|
|
277
|
+
this._listeners.delete(unsub);
|
|
278
|
+
unsub();
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Starts listening for NUI message events dispatched to the window.
|
|
284
|
+
*
|
|
285
|
+
* @returns Unsubscribe function to stop listening to window messages.
|
|
286
|
+
*/
|
|
287
|
+
listen(): () => void {
|
|
288
|
+
const unsub = this._client.listen();
|
|
289
|
+
this._listeners.add(unsub);
|
|
290
|
+
this._listening = true;
|
|
291
|
+
return unsub;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Returns an untyped view of the underlying transport client.
|
|
296
|
+
*
|
|
297
|
+
* @returns Untyped transport client reference.
|
|
298
|
+
*/
|
|
299
|
+
getClient(): UntypedNuiClient {
|
|
300
|
+
return this._client;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Returns the underlying strongly typed `@fivem-nui/core` client instance.
|
|
305
|
+
*
|
|
306
|
+
* @returns The underlying `NuiClient` instance.
|
|
307
|
+
*/
|
|
308
|
+
getTypedClient(): InstanceType<typeof CoreNuiClient> {
|
|
309
|
+
return this._typedClient;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Cleans up all active listeners, stops listening, and disposes the underlying transport client.
|
|
314
|
+
*/
|
|
315
|
+
destroy(): void {
|
|
316
|
+
for (const unsub of this._listeners) {
|
|
317
|
+
unsub();
|
|
318
|
+
}
|
|
319
|
+
this._listeners.clear();
|
|
320
|
+
this._listening = false;
|
|
321
|
+
this._typedClient.dispose();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { getContext, setContext, hasContext } from 'svelte';
|
|
2
|
+
import type { SvelteNuiClient } from '../client';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Global Symbol key used for Svelte Context API registration.
|
|
6
|
+
* Scoped with package prefix to prevent realm collisions with other libraries.
|
|
7
|
+
*/
|
|
8
|
+
export const NUI_CONTEXT_KEY = Symbol.for('@fivem-nui/svelte:client');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Global Symbol key used to store the default client singleton on `globalThis`.
|
|
12
|
+
* Ensures duplicate package copies (e.g. nested node_modules or micro-frontends)
|
|
13
|
+
* share the same default client instance.
|
|
14
|
+
*/
|
|
15
|
+
const GLOBAL_DEFAULT_KEY = Symbol.for('@fivem-nui/svelte:default-client');
|
|
16
|
+
|
|
17
|
+
function getDefaultClient(): SvelteNuiClient | null {
|
|
18
|
+
const g = globalThis as unknown as { [GLOBAL_DEFAULT_KEY]?: SvelteNuiClient | null };
|
|
19
|
+
return g[GLOBAL_DEFAULT_KEY] ?? null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function setDefaultClient(client: SvelteNuiClient | null): void {
|
|
23
|
+
const g = globalThis as unknown as { [GLOBAL_DEFAULT_KEY]?: SvelteNuiClient | null };
|
|
24
|
+
g[GLOBAL_DEFAULT_KEY] = client;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Registers the global default `SvelteNuiClient` instance used when no scoped context is found.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* **SSR & CEF Environment**:
|
|
32
|
+
* The default client is a singleton stored on `globalThis` intended for client-side FiveM CEF
|
|
33
|
+
* and SPA environments. In server-side rendering (SSR) environments, avoid relying on global
|
|
34
|
+
* singletons and prefer component-scoped context via `setNuiClient()`.
|
|
35
|
+
*
|
|
36
|
+
* @param client - The client instance to register as the default.
|
|
37
|
+
* @returns The registered client instance for convenient chaining.
|
|
38
|
+
*/
|
|
39
|
+
export function setDefaultNuiClient(client: SvelteNuiClient): SvelteNuiClient {
|
|
40
|
+
const current = getDefaultClient();
|
|
41
|
+
const isHmr = typeof import.meta !== 'undefined' && Boolean((import.meta as any)?.hot);
|
|
42
|
+
|
|
43
|
+
if (!isHmr && current !== null && current !== client) {
|
|
44
|
+
console.warn(
|
|
45
|
+
'[fivem-nui-svelte] setDefaultNuiClient called with a new client instance while a default client was already registered. Overwriting default client.'
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
setDefaultClient(client);
|
|
49
|
+
return client;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Directly retrieves the global default `SvelteNuiClient` instance without checking Svelte context.
|
|
54
|
+
* Useful in standalone stores, utility files, or outside component initialization.
|
|
55
|
+
*
|
|
56
|
+
* @returns The registered global default client, or `null` if none has been registered.
|
|
57
|
+
*/
|
|
58
|
+
export function getDefaultNuiClient(): SvelteNuiClient | null {
|
|
59
|
+
return getDefaultClient();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resets the default global client instance to null.
|
|
64
|
+
* Intended for test isolation and teardown.
|
|
65
|
+
*/
|
|
66
|
+
export function resetDefaultNuiClient(): void {
|
|
67
|
+
setDefaultClient(null);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Provides a `SvelteNuiClient` instance to the component hierarchy via Svelte's Context API.
|
|
72
|
+
*
|
|
73
|
+
* @param client - The client instance to provide to child components.
|
|
74
|
+
* @returns The provided client instance for convenient inline assignment.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```svelte
|
|
78
|
+
* <script>
|
|
79
|
+
* import { setNuiClient, SvelteNuiClient } from '@fivem-nui/svelte';
|
|
80
|
+
* const client = setNuiClient(new SvelteNuiClient());
|
|
81
|
+
* </script>
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function setNuiClient(client: SvelteNuiClient): SvelteNuiClient {
|
|
85
|
+
setContext(NUI_CONTEXT_KEY, client);
|
|
86
|
+
return client;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Retrieves the currently active `SvelteNuiClient`.
|
|
91
|
+
*
|
|
92
|
+
* Resolution priority order:
|
|
93
|
+
* 1. **Component Context**: Scoped client provided via `setNuiClient()` in an ancestor component
|
|
94
|
+
* (checked safely; outside component lifecycle, this step is skipped).
|
|
95
|
+
* 2. **Explicit Override**: `fallbackClient` argument passed directly to `useNuiClient(fallbackClient)`.
|
|
96
|
+
* 3. **Global Default**: Singleton client registered via `setDefaultNuiClient()` or the default `nui` instance
|
|
97
|
+
* registered upon importing the root package entrypoint (`src/index.ts`).
|
|
98
|
+
*
|
|
99
|
+
* @remarks
|
|
100
|
+
* **Lifecycle Safety**:
|
|
101
|
+
* Svelte's `hasContext()` / `getContext()` throw a `lifecycle_outside_component` runtime error
|
|
102
|
+
* when invoked outside component initialization (e.g. in async handlers, stores, or outside component trees).
|
|
103
|
+
* This function safely catches that error to allow graceful fallback to the explicit or global client.
|
|
104
|
+
*
|
|
105
|
+
* @param fallbackClient - Optional client to use if no scoped context is found.
|
|
106
|
+
* @returns The resolved `SvelteNuiClient`.
|
|
107
|
+
* @throws Error if no client is found in context, fallback, or default singleton.
|
|
108
|
+
*/
|
|
109
|
+
export function useNuiClient(fallbackClient?: SvelteNuiClient): SvelteNuiClient {
|
|
110
|
+
try {
|
|
111
|
+
if (hasContext(NUI_CONTEXT_KEY)) {
|
|
112
|
+
return getContext<SvelteNuiClient>(NUI_CONTEXT_KEY);
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// Outside component initialization lifecycle (e.g. async handler, store, or external helper).
|
|
116
|
+
// Safely proceed to explicit fallback or global default client.
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const client = fallbackClient ?? getDefaultClient();
|
|
120
|
+
if (!client) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
'[fivem-nui-svelte] No NuiClient available. Call setNuiClient(client) in a parent component, provide an explicit client argument to useNuiClient(fallback), or register a default client via setDefaultNuiClient(client) at startup.'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return client;
|
|
127
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Main entrypoint for @fivem-nui/svelte
|
|
2
|
+
// High-performance Svelte SDK for FiveM NUI
|
|
3
|
+
|
|
4
|
+
import { SvelteNuiClient, type SvelteNuiClientOptions } from './client';
|
|
5
|
+
import { setDefaultNuiClient } from './context';
|
|
6
|
+
|
|
7
|
+
// ===== GLOBAL CLIENT SINGLETON & FACTORY =====
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default global `SvelteNuiClient` singleton instance.
|
|
11
|
+
* Automatically registered with {@link setDefaultNuiClient}.
|
|
12
|
+
*/
|
|
13
|
+
export const nui = new SvelteNuiClient();
|
|
14
|
+
setDefaultNuiClient(nui);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new custom `SvelteNuiClient` instance.
|
|
18
|
+
*
|
|
19
|
+
* @param options - Configuration options for the client.
|
|
20
|
+
* @returns A new `SvelteNuiClient` instance.
|
|
21
|
+
*/
|
|
22
|
+
export function createNui(options?: SvelteNuiClientOptions): SvelteNuiClient {
|
|
23
|
+
return new SvelteNuiClient(options);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ===== CLIENT CLASS & TYPES =====
|
|
27
|
+
|
|
28
|
+
export { SvelteNuiClient, type SvelteNuiClientOptions };
|
|
29
|
+
|
|
30
|
+
// ===== DOMAINS RE-EXPORTS =====
|
|
31
|
+
|
|
32
|
+
export * from './context';
|
|
33
|
+
export * from './runes/index.svelte';
|
|
34
|
+
export * from './actions';
|
|
35
|
+
export * from './utils';
|
|
36
|
+
export * from './mock';
|
|
37
|
+
export * from '@fivem-nui/error';
|
|
38
|
+
|
|
39
|
+
// ===== CORE CONTRACTS RE-EXPORTS =====
|
|
40
|
+
|
|
41
|
+
export type {
|
|
42
|
+
NuiEventMap,
|
|
43
|
+
NuiEventKey,
|
|
44
|
+
NuiEventRequest,
|
|
45
|
+
NuiEventResponse,
|
|
46
|
+
NuiEventPayload,
|
|
47
|
+
SendPayload,
|
|
48
|
+
RequestPayload,
|
|
49
|
+
RequestResponse,
|
|
50
|
+
NuiWindowAction,
|
|
51
|
+
NuiMessage,
|
|
52
|
+
NuiError
|
|
53
|
+
} from '@fivem-nui/core';
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { NuiWindowAction } from '@fivem-nui/core';
|
|
2
|
+
import { isNui, getResourceName } from '../utils/env';
|
|
3
|
+
import {
|
|
4
|
+
type NuiMockOptions,
|
|
5
|
+
type NuiMockController,
|
|
6
|
+
NuiMessageType,
|
|
7
|
+
NOOP_CONTROLLER
|
|
8
|
+
} from './types';
|
|
9
|
+
import { createFetchInterceptor } from './interceptor';
|
|
10
|
+
|
|
11
|
+
export { NuiMessageType };
|
|
12
|
+
export type { NuiMockOptions, NuiMockController };
|
|
13
|
+
export { isNuiUrl, parseNuiUrl } from './url';
|
|
14
|
+
export { createFetchInterceptor, extractRequestBody } from './interceptor';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sets up a browser mock environment for FiveM NUI.
|
|
18
|
+
*
|
|
19
|
+
* Intercepts POST requests sent to `https://<resource>/<callback>` and
|
|
20
|
+
* responds with mock data when running outside FiveM CEF.
|
|
21
|
+
*
|
|
22
|
+
* @param options - Configuration options for the mock environment.
|
|
23
|
+
* @returns Controller for emitting simulated events and registering dynamic mocks.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { setupNuiMock } from '@fivem-nui/svelte';
|
|
28
|
+
*
|
|
29
|
+
* const mock = setupNuiMock({
|
|
30
|
+
* requests: {
|
|
31
|
+
* getPlayerData: () => ({ id: 1, name: 'John Doe' })
|
|
32
|
+
* }
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function setupNuiMock(options: NuiMockOptions = {}): NuiMockController {
|
|
37
|
+
const {
|
|
38
|
+
requests = {},
|
|
39
|
+
callbacks = {},
|
|
40
|
+
onSend: sendHandlers = {},
|
|
41
|
+
latencyMs = 50,
|
|
42
|
+
log = true,
|
|
43
|
+
resourceName: customResource
|
|
44
|
+
} = options;
|
|
45
|
+
|
|
46
|
+
// 1. SSR check first to avoid referencing window when unavailable
|
|
47
|
+
if (typeof window === 'undefined') {
|
|
48
|
+
return NOOP_CONTROLLER;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 2. In CEF FiveM, the mock interceptor must remain disabled
|
|
52
|
+
if (isNui()) {
|
|
53
|
+
if (log) {
|
|
54
|
+
console.info('[fivem-nui-svelte] Running inside FiveM CEF - mock interceptor disabled.');
|
|
55
|
+
}
|
|
56
|
+
return NOOP_CONTROLLER;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 3. Clean up any previously active mock instance (e.g. on Vite HMR reload)
|
|
60
|
+
if ((window as any).__nuiMock && typeof (window as any).__nuiMock.destroy === 'function') {
|
|
61
|
+
(window as any).__nuiMock.destroy();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const targetResource = customResource || getResourceName('') || 'nui';
|
|
65
|
+
const activeRequests: Record<string, (payload: any) => Promise<any> | any> = { ...requests };
|
|
66
|
+
const activeCallbacks: Record<string, (payload: any) => void> = { ...callbacks };
|
|
67
|
+
const activeSendHandlers: Record<string, (payload: any) => void> = { ...sendHandlers };
|
|
68
|
+
|
|
69
|
+
const originalFetch = window.fetch;
|
|
70
|
+
|
|
71
|
+
function emitEvent(eventName: string, payload?: unknown, delayMs?: number): void {
|
|
72
|
+
const dispatch = () => {
|
|
73
|
+
if (log) {
|
|
74
|
+
console.debug(`[NUI Mock] <- EVENT '${eventName}'`, payload);
|
|
75
|
+
}
|
|
76
|
+
window.postMessage(
|
|
77
|
+
{
|
|
78
|
+
type: NuiMessageType.Event,
|
|
79
|
+
event: eventName,
|
|
80
|
+
payload,
|
|
81
|
+
// 'data' preserved alongside 'payload' for backward compatibility with legacy scripts
|
|
82
|
+
data: payload
|
|
83
|
+
},
|
|
84
|
+
'*'
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if (delayMs && delayMs > 0) {
|
|
89
|
+
setTimeout(dispatch, delayMs);
|
|
90
|
+
} else {
|
|
91
|
+
dispatch();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function emitWindow(action: NuiWindowAction, windowName?: string, delayMs?: number): void {
|
|
96
|
+
const dispatch = () => {
|
|
97
|
+
if (log) {
|
|
98
|
+
console.debug(`[NUI Mock] <- WINDOW action='${action}' window='${windowName ?? '*'}'`);
|
|
99
|
+
}
|
|
100
|
+
window.postMessage(
|
|
101
|
+
{
|
|
102
|
+
type: action,
|
|
103
|
+
event: windowName,
|
|
104
|
+
action,
|
|
105
|
+
window: windowName
|
|
106
|
+
},
|
|
107
|
+
'*'
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if (delayMs && delayMs > 0) {
|
|
112
|
+
setTimeout(dispatch, delayMs);
|
|
113
|
+
} else {
|
|
114
|
+
dispatch();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const interceptedFetch = createFetchInterceptor({
|
|
119
|
+
targetResource,
|
|
120
|
+
latencyMs,
|
|
121
|
+
log,
|
|
122
|
+
activeRequests,
|
|
123
|
+
activeCallbacks,
|
|
124
|
+
activeSendHandlers,
|
|
125
|
+
originalFetch
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
window.fetch = interceptedFetch;
|
|
129
|
+
|
|
130
|
+
const controller: NuiMockController = {
|
|
131
|
+
emitEvent,
|
|
132
|
+
emitWindow,
|
|
133
|
+
mockRequest<TRequest = unknown, TResponse = unknown>(
|
|
134
|
+
eventName: string,
|
|
135
|
+
handler: (payload: TRequest) => Promise<TResponse> | TResponse
|
|
136
|
+
) {
|
|
137
|
+
activeRequests[eventName] = handler;
|
|
138
|
+
return () => {
|
|
139
|
+
if (activeRequests[eventName] === handler) {
|
|
140
|
+
delete activeRequests[eventName];
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
removeRequest(eventName: string) {
|
|
145
|
+
delete activeRequests[eventName];
|
|
146
|
+
},
|
|
147
|
+
onSend<T = unknown>(eventName: string, handler: (payload: T) => void) {
|
|
148
|
+
activeSendHandlers[eventName] = handler;
|
|
149
|
+
return () => {
|
|
150
|
+
if (activeSendHandlers[eventName] === handler) {
|
|
151
|
+
delete activeSendHandlers[eventName];
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
destroy() {
|
|
156
|
+
if (typeof window !== 'undefined') {
|
|
157
|
+
if (window.fetch === interceptedFetch) {
|
|
158
|
+
window.fetch = originalFetch;
|
|
159
|
+
}
|
|
160
|
+
if ((window as any).__nuiMock === controller) {
|
|
161
|
+
delete (window as any).__nuiMock;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
(window as any).__nuiMock = controller;
|
|
168
|
+
|
|
169
|
+
if (log) {
|
|
170
|
+
console.info(
|
|
171
|
+
`[fivem-nui-svelte] Mock interceptor active for resource '${targetResource}'. window.__nuiMock ready.`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return controller;
|
|
176
|
+
}
|