fivem-rpc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,218 @@
1
+ # fivem-rpc
2
+
3
+ ![NPM Version](https://img.shields.io/npm/v/fivem-rpc)
4
+
5
+ Typed RPC for FiveM. Replaces raw `emitNet`/`onNet` calls with typed, Promise-based procedures between server, client and NUI.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ pnpm add fivem-rpc
11
+ ```
12
+
13
+ ## Setup
14
+
15
+ Declare your procedures once by augmenting the registry interfaces from `fivem-rpc/types`. Both sides import the same declarations.
16
+
17
+ ```ts
18
+ import type { RpcPayload } from "fivem-rpc/types";
19
+
20
+ declare module "fivem-rpc/types" {
21
+ interface ClientToServerRpc {
22
+ "foo:getData": RpcPayload<{ id: number }, { name: string }>;
23
+ "foo:ping": RpcPayload<undefined, undefined>;
24
+ }
25
+
26
+ interface ServerToClientRpc {
27
+ "bar:notify": RpcPayload<{ message: string }, { seen: boolean }>;
28
+ }
29
+
30
+ interface NuiToClientRpc {
31
+ "ui:getInfo": RpcPayload<undefined, { name: string }>;
32
+ }
33
+
34
+ interface ClientToNuiRpc {
35
+ "ui:show": RpcPayload<{ text: string }, { ok: boolean }>;
36
+ }
37
+ }
38
+ ```
39
+
40
+ `RpcPayload<Request, Response>` describes the argument and return value of a procedure. Use `undefined` for no argument or no return value.
41
+
42
+ ## Client to Server
43
+
44
+ On the server, register a handler:
45
+
46
+ ```ts
47
+ import { initializeRpc } from "fivem-rpc/server";
48
+
49
+ const { onClientRpc } = initializeRpc();
50
+
51
+ onClientRpc("foo:getData", async (source, { id }) => {
52
+ return { name: "bar" };
53
+ });
54
+ ```
55
+
56
+ On the client, call the procedure:
57
+
58
+ ```ts
59
+ import { initializeRpc } from "fivem-rpc/client";
60
+
61
+ const { callServerRpc } = initializeRpc();
62
+
63
+ const result = await callServerRpc("foo:getData", { id: 1 });
64
+ if (result.success) {
65
+ console.log(result.data.name);
66
+ }
67
+ ```
68
+
69
+ ## Server to Client
70
+
71
+ On the client, register a handler:
72
+
73
+ ```ts
74
+ const { onServerRpc } = initializeRpc();
75
+
76
+ onServerRpc("bar:notify", async ({ message }) => {
77
+ console.log(message);
78
+ return { seen: true };
79
+ });
80
+ ```
81
+
82
+ On the server, call the procedure:
83
+
84
+ ```ts
85
+ const { callClientRpc } = initializeRpc();
86
+
87
+ const result = await callClientRpc(playerId, "bar:notify", { message: "hello" });
88
+ if (result.success) {
89
+ console.log(result.data.seen);
90
+ }
91
+ ```
92
+
93
+ ## NUI ↔ Client
94
+
95
+ ### NUI to Client
96
+
97
+ On the client, register a handler:
98
+
99
+ ```ts
100
+ // client script
101
+ const { onNuiRpc } = initializeRpc();
102
+
103
+ onNuiRpc("ui:getInfo", async () => {
104
+ return { name: GetPlayerName("-1") };
105
+ });
106
+ ```
107
+
108
+ In NUI, call the procedure:
109
+
110
+ ```ts
111
+ // browser (fivem-rpc/nui)
112
+ import { initializeRpc } from "fivem-rpc/nui";
113
+
114
+ const { callClientRpc } = initializeRpc();
115
+
116
+ const result = await callClientRpc("ui:getInfo");
117
+ if (result.success) {
118
+ document.title = result.data.name;
119
+ }
120
+ ```
121
+
122
+ ### Client to NUI
123
+
124
+ In NUI, register a handler:
125
+
126
+ ```ts
127
+ // browser (fivem-rpc/nui)
128
+ const { onClientRpc } = initializeRpc();
129
+
130
+ onClientRpc("ui:show", async ({ text }) => {
131
+ showNotification(text);
132
+ return { ok: true };
133
+ });
134
+ ```
135
+
136
+ On the client, call the procedure:
137
+
138
+ ```ts
139
+ // client script
140
+ const { callNuiRpc } = initializeRpc();
141
+
142
+ const result = await callNuiRpc("ui:show", { text: "hello" });
143
+ if (result.success) {
144
+ console.log(result.data.ok);
145
+ }
146
+ ```
147
+
148
+ ## Removing handlers
149
+
150
+ All `onXxxRpc` functions have a corresponding `offXxxRpc`. Call it with just the procedure name to unconditionally remove the handler, or pass the original handler reference to only remove it if it is still the currently registered one.
151
+
152
+ ```ts
153
+ const handler = async () => ({ name: "bar" });
154
+
155
+ onClientRpc("foo:getData", handler);
156
+
157
+ // Remove unconditionally:
158
+ offClientRpc("foo:getData");
159
+
160
+ // Remove only if this is still the registered handler:
161
+ offClientRpc("foo:getData", handler);
162
+ ```
163
+
164
+ | Entry point | Register | Unregister |
165
+ | --- | --- | --- |
166
+ | `fivem-rpc/server` | `onClientRpc` | `offClientRpc` |
167
+ | `fivem-rpc/client` | `onServerRpc`, `onNuiRpc` | `offServerRpc`, `offNuiRpc` |
168
+ | `fivem-rpc/nui` | `onClientRpc` | `offClientRpc` |
169
+
170
+ ## Error handling
171
+
172
+ All call functions always resolve. Check `result.success` before accessing `result.data`.
173
+
174
+ On failure, `result.error` is a discriminated union:
175
+
176
+ ```ts
177
+ const result = await callServerRpc("foo:getData", { id: 1 });
178
+
179
+ if (!result.success) {
180
+ switch (result.error.code) {
181
+ case "ERR_NO_HANDLER":
182
+ console.error("no handler for", result.error.procedure);
183
+ break;
184
+ case "ERR_TIMEOUT":
185
+ console.error("timed out waiting for", result.error.procedure);
186
+ break;
187
+ case "ERR_HANDLER":
188
+ console.error("handler threw:", result.error.message);
189
+ break;
190
+ }
191
+ }
192
+ ```
193
+
194
+ | Code | Cause |
195
+ | --- | --- |
196
+ | `ERR_NO_HANDLER` | No handler registered for the procedure |
197
+ | `ERR_TIMEOUT` | Response not received within the timeout |
198
+ | `ERR_HANDLER` | Handler threw at runtime |
199
+
200
+ ## Channels
201
+
202
+ `initializeRpc` accepts an optional `channel` name (defaults to `"default"`). A channel scopes all internal event names so that two independent systems in the same resource cannot interfere with each other.
203
+
204
+ Use multiple channels when you want to initialise RPC more than once in the same resource, for example to keep unrelated feature sets isolated:
205
+
206
+ ```ts
207
+ const core = initializeRpc({ channel: "core" });
208
+ const player = initializeRpc({ channel: "player" });
209
+ ```
210
+
211
+ The channel name must match on both the server and client side. If you only call `initializeRpc` once per resource, you can omit it entirely and rely on the default.
212
+
213
+ ## Options
214
+
215
+ | Option | Default | Description |
216
+ | --- | --- | --- |
217
+ | `channel` | `"default"` | Scopes all event names to this channel |
218
+ | `timeout` | `10000` | Milliseconds to wait for a response before resolving with `ERR_TIMEOUT` |
@@ -0,0 +1,50 @@
1
+ import type { ClientToServerRpc, ServerToClientRpc, NuiToClientRpc, ClientToNuiRpc, RpcPayload, RpcResult } from "./types.js";
2
+ type InitializeRpcOptions = {
3
+ /**
4
+ * Channel name used to scope RPC events.
5
+ * Must match the `channel` passed to `initializeRpc` on the server.
6
+ * @default "default"
7
+ */
8
+ channel?: string;
9
+ /**
10
+ * Timeout for RPC responses in milliseconds.
11
+ * @default 10000
12
+ */
13
+ timeout?: number;
14
+ };
15
+ /**
16
+ * Initializes a client-side RPC channel.
17
+ *
18
+ * Each channel is isolated. Calling with the same channel name twice throws.
19
+ *
20
+ * @example
21
+ * const { callServerRpc } = initializeRpc({ channel: "core" });
22
+ * const result = await callServerRpc("core:ping");
23
+ */
24
+ export declare const initializeRpc: ({ channel, timeout, }?: InitializeRpcOptions) => {
25
+ /**
26
+ * Calls a server-side RPC procedure. Always resolves; inspect `result.success` to branch.
27
+ *
28
+ * @example
29
+ * const result = await callServerRpc("player:getData");
30
+ * if (result.success) console.log(result.data.name);
31
+ * else console.error(result.error.message);
32
+ */
33
+ callServerRpc: <Key extends keyof ClientToServerRpc>(procedure: Key, ...args: ClientToServerRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<RpcResult<ClientToServerRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>>;
34
+ /** Registers a handler for a server-to-client RPC procedure. */
35
+ onServerRpc: <Key extends keyof ServerToClientRpc>(procedure: Key, handler: (...args: ServerToClientRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<ServerToClientRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>) => void;
36
+ /** Removes a handler for a server-to-client RPC procedure. */
37
+ offServerRpc: <Key extends keyof ServerToClientRpc>(procedure: Key, handler?: (...args: ServerToClientRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<ServerToClientRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>) => void;
38
+ /**
39
+ * Calls a NUI procedure and returns a typed Promise.
40
+ * The NUI must have a corresponding `onClientRpc` handler registered.
41
+ * Always resolves; inspect `result.success` to branch.
42
+ */
43
+ callNuiRpc: <Key extends keyof ClientToNuiRpc>(procedure: Key, ...args: ClientToNuiRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<RpcResult<ClientToNuiRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>>;
44
+ /** Registers a handler for a NUI-to-client RPC procedure. */
45
+ onNuiRpc: <Key extends keyof NuiToClientRpc>(procedure: Key, handler: (...args: NuiToClientRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<NuiToClientRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>) => void;
46
+ /** Removes a handler for a NUI-to-client RPC procedure. */
47
+ offNuiRpc: <Key extends keyof NuiToClientRpc>(procedure: Key, handler?: (...args: NuiToClientRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<NuiToClientRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>) => void;
48
+ };
49
+ export {};
50
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,189 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/utils.ts
3
+ /** Generates a unique call ID scoped to a procedure and channel. */
4
+ const generateCallId = ({ procedure, channel }) => `${channel}:${procedure}:${"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
5
+ const r = Math.random() * 16 | 0;
6
+ return (c === "x" ? r : r & 3 | 8).toString(16);
7
+ })}`;
8
+ //#endregion
9
+ //#region src/client.ts
10
+ const activeChannels = /* @__PURE__ */ new Set();
11
+ /**
12
+ * Initializes a client-side RPC channel.
13
+ *
14
+ * Each channel is isolated. Calling with the same channel name twice throws.
15
+ *
16
+ * @example
17
+ * const { callServerRpc } = initializeRpc({ channel: "core" });
18
+ * const result = await callServerRpc("core:ping");
19
+ */
20
+ const initializeRpc = ({ channel = "default", timeout = 1e4 } = {}) => {
21
+ if (activeChannels.has(channel)) throw new Error(`RPC channel already initialized: "${channel}"`);
22
+ activeChannels.add(channel);
23
+ const request = `__rpc_c2s_${channel}__`;
24
+ const response = `__rpc_c2s_response_${channel}__`;
25
+ const pendingResponses = /* @__PURE__ */ new Map();
26
+ const onRpcResponse = (handler) => onNet(response, handler);
27
+ const emitRpcRequest = (callId, procedure, args) => emitNet(request, callId, procedure, args);
28
+ onRpcResponse((callId, result) => {
29
+ const pending = pendingResponses.get(callId);
30
+ if (!pending) return;
31
+ pendingResponses.delete(callId);
32
+ clearTimeout(pending.timer);
33
+ pending.resolve(result);
34
+ });
35
+ const serverToClientHandlers = /* @__PURE__ */ new Map();
36
+ const nuiToClientHandlers = /* @__PURE__ */ new Map();
37
+ const clientToNuiPending = /* @__PURE__ */ new Map();
38
+ RegisterNuiCallbackType(`__rpc_nui2c_${channel}__`);
39
+ on(`__cfx_nui:__rpc_nui2c_${channel}__`, async (data, cb) => {
40
+ const handler = nuiToClientHandlers.get(data.procedure);
41
+ if (!handler) {
42
+ cb({
43
+ success: false,
44
+ error: {
45
+ code: "ERR_NO_HANDLER",
46
+ procedure: data.procedure
47
+ }
48
+ });
49
+ return;
50
+ }
51
+ try {
52
+ cb({
53
+ success: true,
54
+ data: await handler(data.args)
55
+ });
56
+ } catch (e) {
57
+ cb({
58
+ success: false,
59
+ error: {
60
+ code: "ERR_HANDLER",
61
+ message: e instanceof Error ? e.message : String(e)
62
+ }
63
+ });
64
+ }
65
+ });
66
+ RegisterNuiCallbackType(`__rpc_c2nui_response_${channel}__`);
67
+ on(`__cfx_nui:__rpc_c2nui_response_${channel}__`, (data, cb) => {
68
+ const pending = clientToNuiPending.get(data.callId);
69
+ if (pending) {
70
+ clientToNuiPending.delete(data.callId);
71
+ clearTimeout(pending.timer);
72
+ pending.resolve(data.result);
73
+ }
74
+ cb({});
75
+ });
76
+ onNet(`__rpc_s2c_${channel}__`, async (callId, procedure, args) => {
77
+ const handler = serverToClientHandlers.get(procedure);
78
+ if (!handler) {
79
+ emitNet(`__rpc_s2c_response_${channel}__`, callId, {
80
+ success: false,
81
+ error: {
82
+ code: "ERR_NO_HANDLER",
83
+ procedure
84
+ }
85
+ });
86
+ return;
87
+ }
88
+ try {
89
+ const data = await handler(args);
90
+ emitNet(`__rpc_s2c_response_${channel}__`, callId, {
91
+ success: true,
92
+ data
93
+ });
94
+ } catch (e) {
95
+ const message = e instanceof Error ? e.message : String(e);
96
+ emitNet(`__rpc_s2c_response_${channel}__`, callId, {
97
+ success: false,
98
+ error: {
99
+ code: "ERR_HANDLER",
100
+ message
101
+ }
102
+ });
103
+ }
104
+ });
105
+ return {
106
+ /**
107
+ * Calls a server-side RPC procedure. Always resolves; inspect `result.success` to branch.
108
+ *
109
+ * @example
110
+ * const result = await callServerRpc("player:getData");
111
+ * if (result.success) console.log(result.data.name);
112
+ * else console.error(result.error.message);
113
+ */
114
+ callServerRpc: (procedure, ...args) => {
115
+ const callId = generateCallId({
116
+ procedure,
117
+ channel
118
+ });
119
+ return new Promise((resolve) => {
120
+ const timer = setTimeout(() => {
121
+ pendingResponses.delete(callId);
122
+ resolve({
123
+ success: false,
124
+ error: {
125
+ code: "ERR_TIMEOUT",
126
+ procedure
127
+ }
128
+ });
129
+ }, timeout);
130
+ pendingResponses.set(callId, {
131
+ resolve,
132
+ timer
133
+ });
134
+ emitRpcRequest(callId, procedure, args[0] ?? null);
135
+ });
136
+ },
137
+ /** Registers a handler for a server-to-client RPC procedure. */
138
+ onServerRpc: (procedure, handler) => {
139
+ serverToClientHandlers.set(procedure, handler);
140
+ },
141
+ /** Removes a handler for a server-to-client RPC procedure. */
142
+ offServerRpc: (procedure, handler) => {
143
+ if (!handler || serverToClientHandlers.get(procedure) === handler) serverToClientHandlers.delete(procedure);
144
+ },
145
+ /**
146
+ * Calls a NUI procedure and returns a typed Promise.
147
+ * The NUI must have a corresponding `onClientRpc` handler registered.
148
+ * Always resolves; inspect `result.success` to branch.
149
+ */
150
+ callNuiRpc: (procedure, ...args) => {
151
+ const callId = generateCallId({
152
+ procedure,
153
+ channel
154
+ });
155
+ return new Promise((resolve) => {
156
+ const timer = setTimeout(() => {
157
+ clientToNuiPending.delete(callId);
158
+ resolve({
159
+ success: false,
160
+ error: {
161
+ code: "ERR_TIMEOUT",
162
+ procedure
163
+ }
164
+ });
165
+ }, timeout);
166
+ clientToNuiPending.set(callId, {
167
+ resolve,
168
+ timer
169
+ });
170
+ SendNuiMessage(JSON.stringify({
171
+ type: `__rpc_c2nui_${channel}__`,
172
+ callId,
173
+ procedure,
174
+ args: args[0] ?? null
175
+ }));
176
+ });
177
+ },
178
+ /** Registers a handler for a NUI-to-client RPC procedure. */
179
+ onNuiRpc: (procedure, handler) => {
180
+ nuiToClientHandlers.set(procedure, handler);
181
+ },
182
+ /** Removes a handler for a NUI-to-client RPC procedure. */
183
+ offNuiRpc: (procedure, handler) => {
184
+ if (!handler || nuiToClientHandlers.get(procedure) === handler) nuiToClientHandlers.delete(procedure);
185
+ }
186
+ };
187
+ };
188
+ //#endregion
189
+ exports.initializeRpc = initializeRpc;
package/dist/nui.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ import type { NuiToClientRpc, ClientToNuiRpc, RpcPayload, RpcResult } from "./types.js";
2
+ type InitializeRpcOptions = {
3
+ /**
4
+ * Channel name used to scope RPC events.
5
+ * Must match the `channel` passed to `initializeRpc` on the client.
6
+ * @default "default"
7
+ */
8
+ channel?: string;
9
+ /**
10
+ * Milliseconds to wait for a response before resolving with ERR_TIMEOUT.
11
+ * @default 10000
12
+ */
13
+ timeout?: number;
14
+ };
15
+ /**
16
+ * Initializes a NUI-side RPC channel.
17
+ *
18
+ * - {@link callClientRpc} — calls a procedure on the client script (NuiToClientRpc).
19
+ * - {@link onClientRpc} — registers a handler for client-initiated calls (ClientToNuiRpc).
20
+ *
21
+ * @example
22
+ * const { callClientRpc, onClientRpc } = initializeRpc({ channel: "ui" });
23
+ *
24
+ * onClientRpc("ui:showNotification", async ({ message }) => {
25
+ * showToast(message);
26
+ * return { seen: true };
27
+ * });
28
+ *
29
+ * const result = await callClientRpc("ui:getPlayerInfo");
30
+ * if (result.success) setName(result.data.name);
31
+ */
32
+ export declare const initializeRpc: ({ channel, timeout, }?: InitializeRpcOptions) => {
33
+ /**
34
+ * Calls a procedure on the client script and returns a typed Promise.
35
+ * Always resolves; inspect `result.success` to branch.
36
+ */
37
+ callClientRpc: <Key extends keyof NuiToClientRpc>(procedure: Key, ...args: NuiToClientRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<RpcResult<NuiToClientRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>>;
38
+ /** Registers a handler for a client-to-NUI RPC procedure. */
39
+ onClientRpc: <Key extends keyof ClientToNuiRpc>(procedure: Key, handler: (...args: ClientToNuiRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<ClientToNuiRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>) => void;
40
+ /** Removes a handler for a client-to-NUI RPC procedure. */
41
+ offClientRpc: <Key extends keyof ClientToNuiRpc>(procedure: Key, handler?: (...args: ClientToNuiRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<ClientToNuiRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>) => void;
42
+ };
43
+ export {};
44
+ //# sourceMappingURL=nui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nui.d.ts","sourceRoot":"","sources":["../src/nui.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,cAAc,EACd,cAAc,EACd,UAAU,EACV,SAAS,EAET,MAAM,YAAY,CAAC;AAUpB,KAAK,oBAAoB,GAAG;IAC3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,GAAI,wBAG3B,oBAAyB;IAmD1B;;;OAGG;oBACmB,GAAG,SAAS,MAAM,cAAc,aAC1C,GAAG,WACL,cAAc,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,GAAG,EAAE,MAAM,IAAI,CAAC,GACnE,GAAG,SAAS,SAAS,GACpB,EAAE,GACF,CAAC,GAAG,CAAC,GACN,KAAK,KACN,OAAO,CACT,SAAS,CACR,cAAc,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,GAC1D,GAAG,GACH,KAAK,CACR,CACD;IAwCD,6DAA6D;kBAC/C,GAAG,SAAS,MAAM,cAAc,aAClC,GAAG,WACL,CACR,GAAG,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,GAAG,EAAE,MAAM,IAAI,CAAC,GACnE,GAAG,SAAS,SAAS,GACpB,EAAE,GACF,CAAC,GAAG,CAAC,GACN,KAAK,KACJ,OAAO,CACX,cAAc,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,GAC1D,GAAG,GACH,KAAK,CACR;IAQF,2DAA2D;mBAC5C,GAAG,SAAS,MAAM,cAAc,aACnC,GAAG,YACJ,CACT,GAAG,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,GAAG,EAAE,MAAM,IAAI,CAAC,GACnE,GAAG,SAAS,SAAS,GACpB,EAAE,GACF,CAAC,GAAG,CAAC,GACN,KAAK,KACJ,OAAO,CACX,cAAc,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,GAC1D,GAAG,GACH,KAAK,CACR;CAWH,CAAC"}
package/dist/nui.js ADDED
@@ -0,0 +1,115 @@
1
+ //#region src/nui.ts
2
+ /** Tracks initialized channel names to prevent double-initialization. */
3
+ const activeChannels = /* @__PURE__ */ new Set();
4
+ /**
5
+ * Initializes a NUI-side RPC channel.
6
+ *
7
+ * - {@link callClientRpc} — calls a procedure on the client script (NuiToClientRpc).
8
+ * - {@link onClientRpc} — registers a handler for client-initiated calls (ClientToNuiRpc).
9
+ *
10
+ * @example
11
+ * const { callClientRpc, onClientRpc } = initializeRpc({ channel: "ui" });
12
+ *
13
+ * onClientRpc("ui:showNotification", async ({ message }) => {
14
+ * showToast(message);
15
+ * return { seen: true };
16
+ * });
17
+ *
18
+ * const result = await callClientRpc("ui:getPlayerInfo");
19
+ * if (result.success) setName(result.data.name);
20
+ */
21
+ const initializeRpc = ({ channel = "default", timeout = 1e4 } = {}) => {
22
+ if (activeChannels.has(channel)) throw new Error(`RPC channel already initialized: "${channel}"`);
23
+ activeChannels.add(channel);
24
+ const resourceName = GetParentResourceName();
25
+ const nuiToClientEndpoint = `https://${resourceName}/__rpc_nui2c_${channel}__`;
26
+ const clientToNuiResponseEndpoint = `https://${resourceName}/__rpc_c2nui_response_${channel}__`;
27
+ const clientToNuiHandlers = /* @__PURE__ */ new Map();
28
+ window.addEventListener("message", async (event) => {
29
+ const data = event.data;
30
+ if (data.type !== `__rpc_c2nui_${channel}__`) return;
31
+ const { callId, procedure, args } = data;
32
+ if (!callId || !procedure) return;
33
+ const handler = clientToNuiHandlers.get(procedure);
34
+ const result = handler ? await handler(args).then((d) => ({
35
+ success: true,
36
+ data: d
37
+ }), (e) => ({
38
+ success: false,
39
+ error: {
40
+ code: "ERR_HANDLER",
41
+ message: e instanceof Error ? e.message : String(e)
42
+ }
43
+ })) : {
44
+ success: false,
45
+ error: {
46
+ code: "ERR_NO_HANDLER",
47
+ procedure
48
+ }
49
+ };
50
+ await fetch(clientToNuiResponseEndpoint, {
51
+ method: "POST",
52
+ headers: { "Content-Type": "application/json" },
53
+ body: JSON.stringify({
54
+ callId,
55
+ result
56
+ })
57
+ });
58
+ });
59
+ return {
60
+ /**
61
+ * Calls a procedure on the client script and returns a typed Promise.
62
+ * Always resolves; inspect `result.success` to branch.
63
+ */
64
+ callClientRpc: async (procedure, ...args) => {
65
+ const controller = new AbortController();
66
+ const timer = setTimeout(() => controller.abort(), timeout);
67
+ try {
68
+ const response = await fetch(nuiToClientEndpoint, {
69
+ method: "POST",
70
+ headers: { "Content-Type": "application/json" },
71
+ body: JSON.stringify({
72
+ procedure,
73
+ args: args[0] ?? null
74
+ }),
75
+ signal: controller.signal
76
+ });
77
+ clearTimeout(timer);
78
+ if (!response.ok) return {
79
+ success: false,
80
+ error: {
81
+ code: "ERR_HANDLER",
82
+ message: `NUI fetch failed: ${response.status}`
83
+ }
84
+ };
85
+ return await response.json();
86
+ } catch (e) {
87
+ clearTimeout(timer);
88
+ if (e instanceof DOMException && e.name === "AbortError") return {
89
+ success: false,
90
+ error: {
91
+ code: "ERR_TIMEOUT",
92
+ procedure
93
+ }
94
+ };
95
+ return {
96
+ success: false,
97
+ error: {
98
+ code: "ERR_HANDLER",
99
+ message: e instanceof Error ? e.message : String(e)
100
+ }
101
+ };
102
+ }
103
+ },
104
+ /** Registers a handler for a client-to-NUI RPC procedure. */
105
+ onClientRpc: (procedure, handler) => {
106
+ clientToNuiHandlers.set(procedure, handler);
107
+ },
108
+ /** Removes a handler for a client-to-NUI RPC procedure. */
109
+ offClientRpc: (procedure, handler) => {
110
+ if (!handler || clientToNuiHandlers.get(procedure) === handler) clientToNuiHandlers.delete(procedure);
111
+ }
112
+ };
113
+ };
114
+ //#endregion
115
+ export { initializeRpc };
@@ -0,0 +1,47 @@
1
+ import type { ClientToServerRpc, RpcPayload, RpcResult, ServerToClientRpc } from "./types.js";
2
+ type InitializeRpcOptions = {
3
+ /**
4
+ * Channel name used to scope RPC events.
5
+ * Must match the `channel` passed to `initializeRpc` on the client.
6
+ * @default "default"
7
+ */
8
+ channel?: string;
9
+ /**
10
+ * Timeout for S2C RPC responses in milliseconds.
11
+ * @default 10000
12
+ */
13
+ timeout?: number;
14
+ };
15
+ /**
16
+ * Initializes a server-side RPC channel.
17
+ *
18
+ * Each channel is isolated. Calling with the same channel name twice throws.
19
+ *
20
+ * @example
21
+ * const core = initializeRpc({ channel: "core" });
22
+ * core.onClientRpc("core:ping", async (source) => ({ pong: true }));
23
+ */
24
+ export declare const initializeRpc: ({ channel, timeout, }?: InitializeRpcOptions) => {
25
+ /**
26
+ * Registers a handler for a client-to-server RPC procedure.
27
+ *
28
+ * Registering the same procedure again overwrites the previous handler.
29
+ *
30
+ * @example
31
+ * onClientRpc("player:getData", async (source) => ({
32
+ * name: GetPlayerName(String(source)),
33
+ * }));
34
+ */
35
+ onClientRpc: <Key extends keyof ClientToServerRpc>(procedure: Key, handler: (source: number, ...args: ClientToServerRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<ClientToServerRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>) => void;
36
+ /**
37
+ * Removes a handler for a client-to-server RPC procedure.
38
+ * If `handler` is provided, only removes it if it is the currently registered handler.
39
+ */
40
+ offClientRpc: <Key extends keyof ClientToServerRpc>(procedure: Key, handler?: (source: number, ...args: ClientToServerRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<ClientToServerRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>) => void;
41
+ /**
42
+ * Calls a procedure on a specific client. Always resolves; inspect `result.success` to branch.
43
+ */
44
+ callClientRpc: <Key extends keyof ServerToClientRpc>(playerId: number, procedure: Key, ...args: ServerToClientRpc[Key] extends RpcPayload<infer Req, infer _Res> ? Req extends undefined ? [] : [Req] : never) => Promise<RpcResult<ServerToClientRpc[Key] extends RpcPayload<infer _Req, infer Res> ? Res : never>>;
45
+ };
46
+ export {};
47
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,iBAAiB,EAEjB,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,MAAM,YAAY,CAAC;AAgBpB,KAAK,oBAAoB,GAAG;IAC3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,GAAI,wBAG3B,oBAAyB;IA8E1B;;;;;;;;;OASG;kBACW,GAAG,SAAS,MAAM,iBAAiB,aACrC,GAAG,WACL,CACR,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,iBAAiB,CAAC,GAAG,CAAC,SAAS,UAAU,CACjD,MAAM,GAAG,EACT,MAAM,IAAI,CACV,GACE,GAAG,SAAS,SAAS,GACpB,EAAE,GACF,CAAC,GAAG,CAAC,GACN,KAAK,KACJ,OAAO,CACX,iBAAiB,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,GAC7D,GAAG,GACH,KAAK,CACR;IAQF;;;OAGG;mBACY,GAAG,SAAS,MAAM,iBAAiB,aACtC,GAAG,YACJ,CACT,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,iBAAiB,CAAC,GAAG,CAAC,SAAS,UAAU,CACjD,MAAM,GAAG,EACT,MAAM,IAAI,CACV,GACE,GAAG,SAAS,SAAS,GACpB,EAAE,GACF,CAAC,GAAG,CAAC,GACN,KAAK,KACJ,OAAO,CACX,iBAAiB,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,GAC7D,GAAG,GACH,KAAK,CACR;IAWF;;OAEG;oBACa,GAAG,SAAS,MAAM,iBAAiB,YACxC,MAAM,aACL,GAAG,WACL,iBAAiB,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,GAAG,EAAE,MAAM,IAAI,CAAC,GACtE,GAAG,SAAS,SAAS,GACpB,EAAE,GACF,CAAC,GAAG,CAAC,GACN,KAAK,KACN,OAAO,CACT,SAAS,CACR,iBAAiB,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,GAC7D,GAAG,GACH,KAAK,CACR,CACD;CAwBF,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,126 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/utils.ts
3
+ /** Generates a unique call ID scoped to a procedure and channel. */
4
+ const generateCallId = ({ procedure, channel }) => `${channel}:${procedure}:${"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
5
+ const r = Math.random() * 16 | 0;
6
+ return (c === "x" ? r : r & 3 | 8).toString(16);
7
+ })}`;
8
+ //#endregion
9
+ //#region src/server.ts
10
+ const activeChannels = /* @__PURE__ */ new Set();
11
+ /**
12
+ * Initializes a server-side RPC channel.
13
+ *
14
+ * Each channel is isolated. Calling with the same channel name twice throws.
15
+ *
16
+ * @example
17
+ * const core = initializeRpc({ channel: "core" });
18
+ * core.onClientRpc("core:ping", async (source) => ({ pong: true }));
19
+ */
20
+ const initializeRpc = ({ channel = "default", timeout = 1e4 } = {}) => {
21
+ if (activeChannels.has(channel)) throw new Error(`RPC channel already initialized: "${channel}"`);
22
+ activeChannels.add(channel);
23
+ const request = `__rpc_c2s_${channel}__`;
24
+ const response = `__rpc_c2s_response_${channel}__`;
25
+ const clientToServerHandlers = /* @__PURE__ */ new Map();
26
+ const onRpcRequest = (handler) => onNet(request, handler);
27
+ const emitRpcResponse = ({ target, callId, result }) => emitNet(response, target, callId, result);
28
+ onRpcRequest(async (callId, procedure, args) => {
29
+ const playerId = source;
30
+ const handler = clientToServerHandlers.get(procedure);
31
+ if (!handler) {
32
+ emitRpcResponse({
33
+ target: playerId,
34
+ callId,
35
+ result: {
36
+ success: false,
37
+ error: {
38
+ code: "ERR_NO_HANDLER",
39
+ procedure
40
+ }
41
+ }
42
+ });
43
+ return;
44
+ }
45
+ try {
46
+ emitRpcResponse({
47
+ target: playerId,
48
+ callId,
49
+ result: {
50
+ success: true,
51
+ data: await handler(playerId, args)
52
+ }
53
+ });
54
+ } catch (e) {
55
+ emitRpcResponse({
56
+ target: playerId,
57
+ callId,
58
+ result: {
59
+ success: false,
60
+ error: {
61
+ code: "ERR_HANDLER",
62
+ message: e instanceof Error ? e.message : String(e)
63
+ }
64
+ }
65
+ });
66
+ }
67
+ });
68
+ const serverToClientPending = /* @__PURE__ */ new Map();
69
+ onNet(`__rpc_s2c_response_${channel}__`, (callId, result) => {
70
+ const pending = serverToClientPending.get(callId);
71
+ if (!pending) return;
72
+ serverToClientPending.delete(callId);
73
+ clearTimeout(pending.timer);
74
+ pending.resolve(result);
75
+ });
76
+ return {
77
+ /**
78
+ * Registers a handler for a client-to-server RPC procedure.
79
+ *
80
+ * Registering the same procedure again overwrites the previous handler.
81
+ *
82
+ * @example
83
+ * onClientRpc("player:getData", async (source) => ({
84
+ * name: GetPlayerName(String(source)),
85
+ * }));
86
+ */
87
+ onClientRpc: (procedure, handler) => {
88
+ clientToServerHandlers.set(procedure, handler);
89
+ },
90
+ /**
91
+ * Removes a handler for a client-to-server RPC procedure.
92
+ * If `handler` is provided, only removes it if it is the currently registered handler.
93
+ */
94
+ offClientRpc: (procedure, handler) => {
95
+ if (!handler || clientToServerHandlers.get(procedure) === handler) clientToServerHandlers.delete(procedure);
96
+ },
97
+ /**
98
+ * Calls a procedure on a specific client. Always resolves; inspect `result.success` to branch.
99
+ */
100
+ callClientRpc: (playerId, procedure, ...args) => {
101
+ const callId = generateCallId({
102
+ procedure,
103
+ channel
104
+ });
105
+ return new Promise((resolve) => {
106
+ const timer = setTimeout(() => {
107
+ serverToClientPending.delete(callId);
108
+ resolve({
109
+ success: false,
110
+ error: {
111
+ code: "ERR_TIMEOUT",
112
+ procedure
113
+ }
114
+ });
115
+ }, timeout);
116
+ serverToClientPending.set(callId, {
117
+ resolve,
118
+ timer
119
+ });
120
+ emitNet(`__rpc_s2c_${channel}__`, playerId, callId, procedure, args[0] ?? null);
121
+ });
122
+ }
123
+ };
124
+ };
125
+ //#endregion
126
+ exports.initializeRpc = initializeRpc;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Registry of client-to-server RPC procedures.
3
+ *
4
+ * @example
5
+ * declare module "fivem-rpc/types" {
6
+ * interface ClientToServerRpc {
7
+ * "player:getData": RpcPayload<undefined, { name: string }>;
8
+ * }
9
+ * }
10
+ */
11
+ export interface ClientToServerRpc {
12
+ }
13
+ /**
14
+ * Registry of server-to-client RPC procedures.
15
+ *
16
+ * @example
17
+ * declare module "fivem-rpc/types" {
18
+ * interface ServerToClientRpc {
19
+ * "player:showNotification": RpcPayload<{ message: string }, { seen: boolean }>;
20
+ * }
21
+ * }
22
+ */
23
+ export interface ServerToClientRpc {
24
+ }
25
+ /**
26
+ * Registry of NUI-to-client RPC procedures (NUI calls client script).
27
+ *
28
+ * @example
29
+ * declare module "fivem-rpc/types" {
30
+ * interface NuiToClientRpc {
31
+ * "ui:getPlayerInfo": RpcPayload<undefined, { name: string }>;
32
+ * }
33
+ * }
34
+ */
35
+ export interface NuiToClientRpc {
36
+ }
37
+ /**
38
+ * Registry of client-to-NUI RPC procedures (client script calls NUI).
39
+ *
40
+ * @example
41
+ * declare module "fivem-rpc/types" {
42
+ * interface ClientToNuiRpc {
43
+ * "ui:showNotification": RpcPayload<{ message: string }, { seen: boolean }>;
44
+ * }
45
+ * }
46
+ */
47
+ export interface ClientToNuiRpc {
48
+ }
49
+ /**
50
+ * Describes the request and response types for a single RPC procedure.
51
+ *
52
+ * @example
53
+ * "player:setJob": RpcPayload<{ job: string }, { ok: boolean }>
54
+ * "player:ping": RpcPayload<undefined, undefined>
55
+ */
56
+ export type RpcPayload<Request extends Record<string, unknown> | undefined = undefined, Response extends Record<string, unknown> | undefined = undefined> = {
57
+ request: Request;
58
+ response: Response;
59
+ };
60
+ /**
61
+ * Returned by all RPC call functions. Narrow on `success` before accessing `data` or `error`.
62
+ *
63
+ * @example
64
+ * const result = await callServerRpc("player:getData");
65
+ * if (result.success) console.log(result.data.name);
66
+ * else if (result.error.code === "ERR_TIMEOUT") console.warn("timed out");
67
+ */
68
+ export type RpcResult<T> = {
69
+ success: true;
70
+ data: T;
71
+ error?: never;
72
+ } | {
73
+ success: false;
74
+ data?: never;
75
+ error: RpcError;
76
+ };
77
+ /**
78
+ * Discriminated error union returned in `RpcResult` on failure.
79
+ *
80
+ * - `ERR_NO_HANDLER`: no handler registered for the procedure
81
+ * - `ERR_TIMEOUT`: response not received within the timeout
82
+ * - `ERR_HANDLER`: handler threw at runtime
83
+ */
84
+ export type RpcError = {
85
+ code: "ERR_NO_HANDLER";
86
+ procedure: string;
87
+ } | {
88
+ code: "ERR_TIMEOUT";
89
+ procedure: string;
90
+ } | {
91
+ code: "ERR_HANDLER";
92
+ message: string;
93
+ };
94
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,iBAAiB;CAAG;AAErC;;;;;;;;;GASG;AAEH,MAAM,WAAW,iBAAiB;CAAG;AAErC;;;;;;;;;GASG;AAEH,MAAM,WAAW,cAAc;CAAG;AAElC;;;;;;;;;GASG;AAEH,MAAM,WAAW,cAAc;CAAG;AAElC;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,CACrB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,SAAS,EAC/D,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,SAAS,IAC7D;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IACpB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GACzC;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GACjB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC"}
package/dist/types.js ADDED
File without changes
@@ -0,0 +1,6 @@
1
+ /** Generates a unique call ID scoped to a procedure and channel. */
2
+ export declare const generateCallId: ({ procedure, channel, }: {
3
+ procedure: string;
4
+ channel: string;
5
+ }) => string;
6
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,eAAO,MAAM,cAAc,GAAI,yBAG5B;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB,KAAG,MASA,CAAC"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "fivem-rpc",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "A TypeScript library for creating type-safe RPCs in FiveM.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/carlos-menezes/fivem-rpc"
9
+ },
10
+ "author": "Carlos Menezes",
11
+ "license": "MIT",
12
+ "exports": {
13
+ "./types": {
14
+ "types": "./dist/types.d.ts",
15
+ "default": "./dist/types.js"
16
+ },
17
+ "./client": {
18
+ "types": "./dist/client.d.ts",
19
+ "default": "./dist/client.js"
20
+ },
21
+ "./server": {
22
+ "types": "./dist/server.d.ts",
23
+ "default": "./dist/server.js"
24
+ },
25
+ "./nui": {
26
+ "types": "./dist/nui.d.ts",
27
+ "default": "./dist/nui.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "devDependencies": {
34
+ "@biomejs/biome": "2.4.16",
35
+ "@changesets/cli": "^2.31.0",
36
+ "@citizenfx/client": "^2.0.29753-1",
37
+ "@citizenfx/server": "^2.0.29753-1",
38
+ "@commitlint/cli": "^21.0.2",
39
+ "@commitlint/config-conventional": "^21.0.2",
40
+ "happy-dom": "^20.9.0",
41
+ "husky": "^9.1.7",
42
+ "rolldown": "^1.0.3",
43
+ "typescript": "^6.0.3",
44
+ "vitest": "^4.1.7"
45
+ },
46
+ "peerDependencies": {
47
+ "@citizenfx/client": "^2.0.29753-1",
48
+ "@citizenfx/server": "^2.0.29753-1"
49
+ },
50
+ "scripts": {
51
+ "build": "rolldown --config rolldown.config.ts && tsc",
52
+ "test": "vitest run",
53
+ "test:watch": "vitest",
54
+ "format": "biome format --write",
55
+ "lint": "biome lint --write",
56
+ "changeset": "changeset",
57
+ "version": "changeset version",
58
+ "release": "pnpm build && changeset publish"
59
+ }
60
+ }