@laot/nuix 1.0.3 → 1.0.4

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 CHANGED
@@ -4,56 +4,101 @@
4
4
  [![license](https://img.shields.io/npm/l/@laot/nuix.svg)](https://github.com/laot7490/nuix/blob/main/LICENSE)
5
5
  [![bundle size](https://img.shields.io/bundlephobia/minzip/@laot/nuix)](https://bundlephobia.com/package/@laot/nuix)
6
6
 
7
- > Modular, type-safe TypeScript library for FiveM NUI projects. Zero runtime dependencies.
7
+ A type-safe TypeScript helper library for FiveM NUI development. Wraps the most common NUI patterns — fetching data from Lua, listening for messages, formatting strings, and handling translations — in a clean, fully typed API. Zero runtime dependencies, works with any frontend framework.
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ - [Install](#install)
14
+ - [Quick Start](#quick-start)
15
+ - [Event Maps](#1-event-maps)
16
+ - [fetchNui](#2-fetchnui--typed-lua-callbacks)
17
+ - [onNuiMessage](#3-onnuimessage--listening-to-lua)
18
+ - [luaFormat](#4-luaformat--string-formatting)
19
+ - [Translator (Global)](#5-translator-global)
20
+ - [Translator (Isolated)](#6-translator-isolated)
21
+ - [Debug Mode](#7-debug-mode)
22
+ - [Mock Data](#8-mock-data-local-development)
23
+ - [Lua Side](#lua-side)
24
+ - [API Reference](#api-reference)
25
+ - [Build](#build)
26
+ - [License](#license)
27
+
28
+ ---
8
29
 
9
30
  ## Install
10
31
 
11
32
  ```bash
33
+ # pick your package manager
12
34
  npm install @laot/nuix
13
35
  pnpm add @laot/nuix
14
36
  yarn add @laot/nuix
15
37
  bun add @laot/nuix
16
38
  ```
17
39
 
40
+ ---
41
+
18
42
  ## Quick Start
19
43
 
20
- ### 1. Define Your Event Maps
44
+ ### 1. Event Maps
45
+
46
+ Before using anything, you'll want to define your events. NUIX uses these maps to infer the exact types for both data you send and responses you get back. You'll typically have two separate maps:
47
+
48
+ - **Callback events** — for `fetchNui` calls. Your TS code sends data to Lua, Lua processes it and sends a response back.
49
+ - **Message events** — for `onNuiMessage` listeners. Lua pushes data to TS via `SendNUIMessage`, no response needed.
21
50
 
22
51
  ```ts
23
52
  import type { NuiEventMap } from "@laot/nuix";
24
53
 
25
- // fetchNui callbacks (TS Lua → TS)
54
+ // Things you ASK Lua for (request response)
26
55
  interface CallbackEvents extends NuiEventMap {
27
56
  getPlayer: { data: { id: number }; response: { name: string; level: number } };
28
57
  sendNotify: { data: { message: string }; response: void };
29
58
  }
30
59
 
31
- // Lua push messages (Lua → TS via SendNUIMessage)
60
+ // Things Lua TELLS you about (one-way push)
32
61
  interface MessageEvents extends NuiEventMap {
33
62
  showMenu: { data: { items: string[] }; response: void };
34
63
  hideMenu: { data: void; response: void };
35
64
  }
36
65
  ```
37
66
 
38
- ### 2. Typed FetchNui
67
+ > Keeping them separate isn't mandatory, but it makes your code way easier to reason about — you'll always know which events go where.
68
+
69
+ ---
70
+
71
+ ### 2. `fetchNui` — Typed Lua Callbacks
72
+
73
+ `createFetchNui` gives you a typed function that POSTs JSON to `https://<resourceName>/<event>`, matching `RegisterNUICallback` on the Lua side. The resource name is automatically grabbed from FiveM's `GetParentResourceName()`.
39
74
 
40
75
  ```ts
41
76
  import { createFetchNui } from "@laot/nuix";
42
77
 
43
78
  const fetchNui = createFetchNui<CallbackEvents>();
44
79
 
80
+ // fully typed — player is { name: string; level: number }
45
81
  const player = await fetchNui("getPlayer", { id: 1 });
46
- console.log(player.name); // string
82
+ console.log(player.name, player.level);
47
83
 
84
+ // void response — you're just notifying Lua, no return value
48
85
  await fetchNui("sendNotify", { message: "Hello!" });
86
+ ```
49
87
 
50
- // With timeout
88
+ You can also set a **timeout** to avoid hanging forever if the Lua callback never responds:
89
+
90
+ ```ts
51
91
  const data = await fetchNui("getPlayer", { id: 2 }, { timeout: 5000 });
92
+ // rejects with "[NUIX] fetchNui("getPlayer") timed out after 5000ms" if no response
52
93
  ```
53
94
 
54
- ### 3. NUI Message Listener
95
+ ---
96
+
97
+ ### 3. `onNuiMessage` — Listening to Lua
55
98
 
56
- **Switch-case** single listener for all actions:
99
+ Listens for messages from Lua's `SendNUIMessage`. There are two ways to use it:
100
+
101
+ **Switch-case** — one listener that handles every action:
57
102
 
58
103
  ```ts
59
104
  import { onNuiMessage } from "@laot/nuix";
@@ -69,10 +114,11 @@ const unsub = onNuiMessage<MessageEvents>((action, data) => {
69
114
  }
70
115
  });
71
116
 
72
- unsub(); // stop listening
117
+ // when you're done listening
118
+ unsub();
73
119
  ```
74
120
 
75
- **Per-action** — filtered by action, `data` is fully typed:
121
+ **Per-action** — filters by action name, and `data` is fully typed automatically:
76
122
 
77
123
  ```ts
78
124
  const unsub = onNuiMessage<MessageEvents, "showMenu">("showMenu", (data) => {
@@ -80,7 +126,20 @@ const unsub = onNuiMessage<MessageEvents, "showMenu">("showMenu", (data) => {
80
126
  });
81
127
  ```
82
128
 
83
- ### 4. Lua-Style Formatter
129
+ Both overloads return an `UnsubscribeFn` — just call it to remove the event listener.
130
+
131
+ ---
132
+
133
+ ### 4. `luaFormat` — String Formatting
134
+
135
+ A small utility that formats strings using Lua-style placeholders. Handles `null` and `undefined` safely instead of crashing.
136
+
137
+ | Specifier | What it does |
138
+ |-----------|--------------------------------------|
139
+ | `%s` | String (null/undefined → `""`) |
140
+ | `%d` / `%i` | Integer (floors the value, NaN → `0`) |
141
+ | `%f` | Float (NaN → `0`) |
142
+ | `%%` | Literal `%` sign |
84
143
 
85
144
  ```ts
86
145
  import { luaFormat } from "@laot/nuix";
@@ -90,11 +149,18 @@ luaFormat("Hello %s, you are level %d", "Laot", 42);
90
149
 
91
150
  luaFormat("Accuracy: %f%%", 99.5);
92
151
  // → "Accuracy: 99.5%"
152
+
153
+ luaFormat("Safe: %s %d", undefined, NaN);
154
+ // → "Safe: 0"
93
155
  ```
94
156
 
157
+ ---
158
+
95
159
  ### 5. Translator (Global)
96
160
 
97
- Register locales once at runtime (e.g. when Lua sends them), then use `_U` anywhere:
161
+ A global translation system built on top of `luaFormat`. The idea is simple: Lua sends locale data once (usually on resource start), you register it, and then use `_U()` anywhere in your UI to get translated strings.
162
+
163
+ **Registration:**
98
164
 
99
165
  ```ts
100
166
  import { registerLocales, _U, onNuiMessage } from "@laot/nuix";
@@ -108,35 +174,44 @@ interface Events extends NuiEventMap {
108
174
  onNuiMessage<Events>((action, data) => {
109
175
  switch (action) {
110
176
  case "setLocales":
111
- registerLocales(data);
177
+ registerLocales(data); // store the locale map globally
112
178
  break;
113
179
  case "showMenu":
114
180
  openMenu(data.items);
115
181
  break;
116
182
  }
117
183
  });
184
+ ```
185
+
186
+ **Usage — anywhere in your app:**
187
+
188
+ ```ts
189
+ // assuming Lua sent: { ui: { greeting: "Hello %s!", level: "Level %d" } }
118
190
 
119
- // Use _U anywhere
120
191
  _U("ui.greeting", "Hi", "Laot"); // → "Hello Laot!"
121
192
  _U("ui.level", "Lv.", 42); // → "Level 42"
122
- _U("missing.key", "Fallback"); // → "Fallback"
193
+ _U("missing.key", "Fallback"); // → "Fallback" (key not found, returns fallback)
123
194
  ```
124
195
 
125
- You can also extend locales incrementally with `extendLocales`:
196
+ **Adding more translations later** without overwriting existing ones:
126
197
 
127
198
  ```ts
128
199
  import { extendLocales } from "@laot/nuix";
129
200
 
130
201
  extendLocales({ ui: { subtitle: "Overview" } });
131
- // Merges into existing locales without replacing them
202
+ // merges into the existing locale map won't touch other keys
132
203
  ```
133
204
 
205
+ > `_U` uses dot notation. `"ui.greeting"` looks up `locales.ui.greeting` under the hood.
206
+
207
+ ---
208
+
134
209
  ### 6. Translator (Isolated)
135
210
 
136
- If you need a separate translator instance with its own locale scope:
211
+ If you need a translator that's completely independent from the global `_U` — maybe a component with its own locale scope — use `createTranslator`:
137
212
 
138
213
  ```ts
139
- import { createTranslator, mergeLocales } from "@laot/nuix";
214
+ import { createTranslator } from "@laot/nuix";
140
215
 
141
216
  const _T = createTranslator({
142
217
  locales: {
@@ -147,34 +222,50 @@ const _T = createTranslator({
147
222
 
148
223
  _T("greeting", "MISSING", "Laot"); // → "Hello Laot!"
149
224
  _T("level", "MISSING", 42); // → "Level 42"
225
+ _T("no.key", "Not found"); // → "Not found"
226
+ ```
227
+
228
+ There's also `mergeLocales` if you need to deep-merge locale records manually:
229
+
230
+ ```ts
231
+ import { mergeLocales } from "@laot/nuix";
150
232
 
151
- // Deep-merge multiple locale records
152
233
  const base = { ui: { greeting: "Hello %s!" } };
153
234
  const patch = { ui: { greeting: "Hey %s, welcome back!" } };
235
+
154
236
  const merged = mergeLocales(base, patch);
237
+ // merged.ui.greeting → "Hey %s, welcome back!"
155
238
  ```
156
239
 
240
+ ---
241
+
157
242
  ### 7. Debug Mode
158
243
 
159
- Enable console logging for every `fetchNui` call:
244
+ Pass `debug: true` to `createFetchNui` and every call will be logged to the console with the `[NUIX]` prefix. Super useful during development:
160
245
 
161
246
  ```ts
162
247
  const fetchNui = createFetchNui<CallbackEvents>({ debug: true });
163
248
 
164
249
  await fetchNui("getPlayer", { id: 1 });
250
+ // Console:
165
251
  // [NUIX] → getPlayer { id: 1 }
166
252
  // [NUIX] ← getPlayer { name: "Laot", level: 42 }
167
253
  ```
168
254
 
255
+ ---
256
+
169
257
  ### 8. Mock Data (Local Development)
170
258
 
171
- Return pre-defined responses without real HTTP calls — useful when developing outside FiveM:
259
+ When you're building your UI outside of FiveM (like in a regular browser with `npm run dev`), there's no Lua backend to respond to your `fetchNui` calls. That's where `mockData` comes in it returns pre-defined responses without making any HTTP requests.
172
260
 
173
261
  ```ts
174
262
  const fetchNui = createFetchNui<CallbackEvents>({
175
263
  debug: true,
176
264
  mockData: {
265
+ // static response — just return this object every time
177
266
  getPlayer: { name: "DevPlayer", level: 99 },
267
+
268
+ // dynamic response — receive the data, return something based on it
178
269
  sendNotify: (data) => {
179
270
  console.log("Mock notification:", data.message);
180
271
  },
@@ -182,47 +273,79 @@ const fetchNui = createFetchNui<CallbackEvents>({
182
273
  });
183
274
 
184
275
  const player = await fetchNui("getPlayer", { id: 1 });
276
+ // Console:
185
277
  // [NUIX] → getPlayer { id: 1 }
186
278
  // [NUIX] ← getPlayer (mock) { name: "DevPlayer", level: 99 }
187
279
  ```
188
280
 
189
- ## Lua Examples
281
+ > Works great combined with `debug: true` — you can see exactly what's being sent and received in the console.
282
+
283
+ ---
284
+
285
+ ## Lua Side
286
+
287
+ Here's how the Lua side connects to everything above:
190
288
 
191
289
  ```lua
192
- -- NUI callback (responds to fetchNui calls)
290
+ -- Responds to fetchNui("getPlayer", { id = ... })
193
291
  RegisterNUICallback("getPlayer", function(data, cb)
194
292
  local player = GetPlayerData(data.id)
195
293
  cb({ name = player.name, level = player.level })
196
294
  end)
197
295
 
198
- -- Send messages to NUI
296
+ -- Pushes a message to onNuiMessage listeners
199
297
  SendNUIMessage({ action = "showMenu", data = { items = {"Pistol", "Rifle"} } })
200
298
 
201
- -- Send locales to NUI (for registerLocales)
299
+ -- Sends locale data for registerLocales
202
300
  SendNUIMessage({ action = "setLocales", data = Locales })
203
301
  ```
204
302
 
303
+ ---
304
+
205
305
  ## API Reference
206
306
 
207
- | Export | Type | Description |
208
- |---|---|---|
209
- | `createFetchNui<TMap>(options?)` | Factory | Returns a typed `fetchNui` function (supports debug & mock) |
210
- | `onNuiMessage<TMap>(handler)` | Function | Single listener for all actions (switch-case) |
211
- | `onNuiMessage<TMap, K>(action, handler)` | Function | Per-action listener with typed data |
212
- | `luaFormat(template, ...args)` | Function | Lua-style `%s`/`%d`/`%f` formatter |
213
- | `registerLocales(locales)` | Function | Sets the global locale map at runtime |
214
- | `extendLocales(...records)` | Function | Merges new entries into the global locale map |
215
- | `_U(key, fallback, ...args)` | Function | Global translator reads from registered locales |
216
- | `createTranslator(options)` | Factory | Returns an isolated translator function |
217
- | `mergeLocales(...records)` | Function | Deep-merges locale records |
307
+ ### Functions
308
+
309
+ | Export | Description |
310
+ |---|---|
311
+ | `createFetchNui<TMap>(options?)` | Returns a typed `fetchNui` function. Supports `debug` and `mockData` options. |
312
+ | `onNuiMessage<TMap>(handler)` | Listens to all NUI messages — use with a switch-case. |
313
+ | `onNuiMessage<TMap, K>(action, handler)` | Listens to a single action `data` is automatically typed. |
314
+ | `luaFormat(template, ...args)` | Lua-style string formatter with `%s` / `%d` / `%f` support. |
315
+ | `registerLocales(locales)` | Sets the global locale map (replaces the current one). |
316
+ | `extendLocales(...records)` | Merges new entries into the existing global locale map. |
317
+ | `_U(key, fallback, ...args)` | Global translator reads from the registered locale map. |
318
+ | `createTranslator(options)` | Returns an isolated translator function with its own locale scope. |
319
+ | `mergeLocales(...records)` | Deep-merges multiple locale records into one. |
320
+
321
+ ### Types
322
+
323
+ | Export | Description |
324
+ |---|---|
325
+ | `NuiEventMap` | Base interface for defining event maps. |
326
+ | `NuiMessagePayload<TData>` | Shape of `SendNUIMessage` payloads (`{ action, data }`). |
327
+ | `FetchNuiOptions` | Per-call options for `fetchNui` (e.g. `timeout`). |
328
+ | `FetchNuiFactoryOptions<TMap>` | Config for `createFetchNui` (`debug`, `mockData`). |
329
+ | `LocaleRecord` | Flat or nested string map used for translations. |
330
+ | `TranslatorOptions` | Config for `createTranslator`. |
331
+ | `TranslatorFn` | Translator function signature (`(key, fallback, ...args) => string`). |
332
+ | `FormatArg` | Accepted argument types for `luaFormat` (`string \| number \| boolean \| null \| undefined`). |
333
+ | `UnsubscribeFn` | Cleanup function returned by `onNuiMessage`. |
334
+ | `NuiMessageHandler<TData>` | Callback type for NUI message listeners. |
335
+
336
+ ---
218
337
 
219
338
  ## Build
220
339
 
221
340
  ```bash
222
- npm run build # ESM + CJS + .d.ts
223
- npm run typecheck # tsc --noEmit
341
+ npm run build # outputs ESM + CJS + .d.ts to dist/
342
+ npm run typecheck # tsc --noEmit
224
343
  ```
225
344
 
345
+ Requires Node.js ≥ 18.
346
+
347
+ ---
348
+
226
349
  ## License
227
350
 
228
- MIT
351
+ [MIT](LICENSE) © LAOT
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Extend this to map your NUI callback names to their request/response shapes.
3
- * Both `fetchNui` and `onNuiMessage` use this map for full type inference.
2
+ * Extend this to map your NUI event names to their data/response shapes.
3
+ * Both `fetchNui` and `onNuiMessage` use this map for type inference.
4
4
  *
5
5
  * @example
6
6
  * ```ts
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Extend this to map your NUI callback names to their request/response shapes.
3
- * Both `fetchNui` and `onNuiMessage` use this map for full type inference.
2
+ * Extend this to map your NUI event names to their data/response shapes.
3
+ * Both `fetchNui` and `onNuiMessage` use this map for type inference.
4
4
  *
5
5
  * @example
6
6
  * ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laot/nuix",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Modular, type-safe TypeScript library for FiveM NUI projects",
5
5
  "sideEffects": false,
6
6
  "type": "module",