@laot/nuix 1.0.1 → 1.0.3

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
@@ -1,24 +1,37 @@
1
1
  # NUIX
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@laot/nuix.svg)](https://www.npmjs.com/package/@laot/nuix)
4
+ [![license](https://img.shields.io/npm/l/@laot/nuix.svg)](https://github.com/laot7490/nuix/blob/main/LICENSE)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@laot/nuix)](https://bundlephobia.com/package/@laot/nuix)
6
+
3
7
  > Modular, type-safe TypeScript library for FiveM NUI projects. Zero runtime dependencies.
4
8
 
5
9
  ## Install
6
10
 
7
11
  ```bash
8
12
  npm install @laot/nuix
13
+ pnpm add @laot/nuix
14
+ yarn add @laot/nuix
15
+ bun add @laot/nuix
9
16
  ```
10
17
 
11
18
  ## Quick Start
12
19
 
13
- ### 1. Define Your Event Map
20
+ ### 1. Define Your Event Maps
14
21
 
15
22
  ```ts
16
23
  import type { NuiEventMap } from "@laot/nuix";
17
24
 
18
- interface MyEvents extends NuiEventMap {
19
- getPlayer: { request: { id: number }; response: { name: string; level: number } };
20
- sendNotify: { request: { message: string }; response: void };
21
- showMenu: { request: { items: string[] }; response: void };
25
+ // fetchNui callbacks (TS → Lua → TS)
26
+ interface CallbackEvents extends NuiEventMap {
27
+ getPlayer: { data: { id: number }; response: { name: string; level: number } };
28
+ sendNotify: { data: { message: string }; response: void };
29
+ }
30
+
31
+ // Lua push messages (Lua → TS via SendNUIMessage)
32
+ interface MessageEvents extends NuiEventMap {
33
+ showMenu: { data: { items: string[] }; response: void };
34
+ hideMenu: { data: void; response: void };
22
35
  }
23
36
  ```
24
37
 
@@ -27,14 +40,14 @@ interface MyEvents extends NuiEventMap {
27
40
  ```ts
28
41
  import { createFetchNui } from "@laot/nuix";
29
42
 
30
- const fetchNui = createFetchNui<MyEvents>();
43
+ const fetchNui = createFetchNui<CallbackEvents>();
31
44
 
32
45
  const player = await fetchNui("getPlayer", { id: 1 });
33
46
  console.log(player.name); // string
34
47
 
35
48
  await fetchNui("sendNotify", { message: "Hello!" });
36
49
 
37
- // With timeout (throws descriptive error on timeout)
50
+ // With timeout
38
51
  const data = await fetchNui("getPlayer", { id: 2 }, { timeout: 5000 });
39
52
  ```
40
53
 
@@ -45,13 +58,13 @@ const data = await fetchNui("getPlayer", { id: 2 }, { timeout: 5000 });
45
58
  ```ts
46
59
  import { onNuiMessage } from "@laot/nuix";
47
60
 
48
- const unsub = onNuiMessage<MyEvents>((action, data) => {
61
+ const unsub = onNuiMessage<MessageEvents>((action, data) => {
49
62
  switch (action) {
50
- case "getPlayer":
51
- console.log(data.name);
63
+ case "showMenu":
64
+ console.log(data.items);
52
65
  break;
53
- case "sendNotify":
54
- console.log(data.message);
66
+ case "hideMenu":
67
+ closeMenu();
55
68
  break;
56
69
  }
57
70
  });
@@ -62,7 +75,7 @@ unsub(); // stop listening
62
75
  **Per-action** — filtered by action, `data` is fully typed:
63
76
 
64
77
  ```ts
65
- const unsub = onNuiMessage<MyEvents, "showMenu">("showMenu", (data) => {
78
+ const unsub = onNuiMessage<MessageEvents, "showMenu">("showMenu", (data) => {
66
79
  console.log(data.items); // ✅ typed as string[]
67
80
  });
68
81
  ```
@@ -88,8 +101,8 @@ import { registerLocales, _U, onNuiMessage } from "@laot/nuix";
88
101
  import type { NuiEventMap, LocaleRecord } from "@laot/nuix";
89
102
 
90
103
  interface Events extends NuiEventMap {
91
- setLocales: { request: LocaleRecord; response: void };
92
- showMenu: { request: { items: string[] }; response: void };
104
+ setLocales: { data: LocaleRecord; response: void };
105
+ showMenu: { data: { items: string[] }; response: void };
93
106
  }
94
107
 
95
108
  onNuiMessage<Events>((action, data) => {
@@ -146,7 +159,7 @@ const merged = mergeLocales(base, patch);
146
159
  Enable console logging for every `fetchNui` call:
147
160
 
148
161
  ```ts
149
- const fetchNui = createFetchNui<MyEvents>({ debug: true });
162
+ const fetchNui = createFetchNui<CallbackEvents>({ debug: true });
150
163
 
151
164
  await fetchNui("getPlayer", { id: 1 });
152
165
  // [NUIX] → getPlayer { id: 1 }
@@ -158,12 +171,12 @@ await fetchNui("getPlayer", { id: 1 });
158
171
  Return pre-defined responses without real HTTP calls — useful when developing outside FiveM:
159
172
 
160
173
  ```ts
161
- const fetchNui = createFetchNui<MyEvents>({
174
+ const fetchNui = createFetchNui<CallbackEvents>({
162
175
  debug: true,
163
176
  mockData: {
164
177
  getPlayer: { name: "DevPlayer", level: 99 },
165
- sendNotify: (req) => {
166
- console.log("Mock notification:", req.message);
178
+ sendNotify: (data) => {
179
+ console.log("Mock notification:", data.message);
167
180
  },
168
181
  },
169
182
  });
package/dist/index.cjs CHANGED
@@ -169,7 +169,7 @@ function mergeLocales(...records) {
169
169
  for (const record of records) {
170
170
  for (const [key, value] of Object.entries(record)) {
171
171
  const existing = result[key];
172
- if (typeof value === "object" && typeof existing === "object" && existing !== void 0) {
172
+ if (value !== null && existing !== null && typeof value === "object" && typeof existing === "object" && existing !== void 0) {
173
173
  result[key] = mergeLocales(existing, value);
174
174
  } else {
175
175
  result[key] = value;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/listener.ts","../src/utils.ts"],"sourcesContent":["// ─── Types ───\r\nexport type {\r\n\tNuiEventMap,\r\n\tNuiMessagePayload,\r\n\tFetchNuiOptions,\r\n\tFetchNuiFactoryOptions,\r\n\tLocaleRecord,\r\n\tTranslatorOptions,\r\n\tTranslatorFn,\r\n\tFormatArg,\r\n\tUnsubscribeFn,\r\n\tNuiMessageHandler,\r\n} from \"./types\";\r\n\r\n// ─── Client ───\r\nexport { createFetchNui } from \"./client\";\r\n\r\n// ─── Listener ───\r\nexport { onNuiMessage } from \"./listener\";\r\n\r\n// ─── Utils ───\r\nexport { luaFormat, createTranslator, mergeLocales, registerLocales, extendLocales, _U } from \"./utils\";\r\n","import type { NuiEventMap, FetchNuiOptions, FetchNuiFactoryOptions } from \"./types\";\r\n\r\n// ─── Resource Name ───\r\n\r\n/**\r\n * Grabs the resource name from FiveM's injected global.\r\n * Falls back to `\"nui-frame-app\"` when running outside the game (local dev, tests).\r\n */\r\nfunction getResourceName(): string {\r\n\tif (typeof window !== \"undefined\" && window.GetParentResourceName) {\r\n\t\treturn window.GetParentResourceName();\r\n\t}\r\n\treturn \"nui-frame-app\";\r\n}\r\n\r\n// ─── FetchNui Factory ───\r\n\r\n/**\r\n * Creates a fully typed `fetchNui` function tied to your event map.\r\n *\r\n * The returned function POSTs JSON to `https://<resourceName>/<event>`,\r\n * which maps 1:1 to `RegisterNUICallback` on the Lua side.\r\n *\r\n * Supports optional debug logging, mock data for local dev, and per-call timeout.\r\n *\r\n * @example\r\n * ```ts\r\n * interface MyEvents extends NuiEventMap {\r\n * getPlayer: { request: { id: number }; response: { name: string } };\r\n * notify: { request: { msg: string }; response: void };\r\n * }\r\n *\r\n * // Production usage\r\n * const fetchNui = createFetchNui<MyEvents>();\r\n * const player = await fetchNui(\"getPlayer\", { id: 1 });\r\n * // player.name is typed as string\r\n *\r\n * // Local dev with mocks + debug\r\n * const fetchNui = createFetchNui<MyEvents>({\r\n * debug: true,\r\n * mockData: {\r\n * getPlayer: { name: \"DevPlayer\" },\r\n * notify: (req) => { console.log(\"Mock:\", req.msg); },\r\n * },\r\n * });\r\n * ```\r\n *\r\n * Lua side:\r\n * ```lua\r\n * RegisterNUICallback(\"getPlayer\", function(data, cb)\r\n * local player = GetPlayerData(data.id)\r\n * cb({ name = player.name })\r\n * end)\r\n * ```\r\n */\r\nexport function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: FetchNuiFactoryOptions<TMap>) {\r\n\tconst debug = factoryOptions?.debug ?? false;\r\n\tconst mockData = factoryOptions?.mockData;\r\n\r\n\treturn async function fetchNui<K extends keyof TMap & string>(\r\n\t\tevent: K,\r\n\t\t...args: TMap[K][\"request\"] extends void\r\n\t\t\t? [data?: TMap[K][\"request\"], options?: FetchNuiOptions]\r\n\t\t\t: [data: TMap[K][\"request\"], options?: FetchNuiOptions]\r\n\t): Promise<TMap[K][\"response\"]> {\r\n\t\tconst [data, options] = args;\r\n\t\tif (debug) {\r\n\t\t\tconsole.log(`[NUIX] → ${event}`, data ?? {});\r\n\t\t}\r\n\r\n\t\t// ─── Mock Mode ───\r\n\r\n\t\tif (mockData && event in mockData) {\r\n\t\t\tconst mock = mockData[event];\r\n\r\n\t\t\tif (mock === undefined) {\r\n\t\t\t\tthrow new Error(`[NUIX] Mock data for \"${event}\" is undefined`);\r\n\t\t\t}\r\n\r\n\t\t\tconst result =\r\n\t\t\t\ttypeof mock === \"function\"\r\n\t\t\t\t\t? (mock as (req: TMap[K][\"request\"]) => TMap[K][\"response\"])(data as TMap[K][\"request\"])\r\n\t\t\t\t\t: mock;\r\n\r\n\t\t\tif (debug) {\r\n\t\t\t\tconsole.log(`[NUIX] ← ${event} (mock)`, result);\r\n\t\t\t}\r\n\r\n\t\t\treturn result as TMap[K][\"response\"];\r\n\t\t}\r\n\r\n\t\t// ─── Real Fetch ───\r\n\r\n\t\tconst url = `https://${getResourceName()}/${event}`;\r\n\r\n\t\tconst controller = options?.timeout ? new AbortController() : undefined;\r\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\r\n\r\n\t\tif (controller && options?.timeout) {\r\n\t\t\ttimeoutId = setTimeout(() => controller.abort(), options.timeout);\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst response = await fetch(url, {\r\n\t\t\t\tmethod: \"POST\",\r\n\t\t\t\theaders: { \"Content-Type\": \"application/json; charset=UTF-8\" },\r\n\t\t\t\tbody: JSON.stringify(data ?? {}),\r\n\t\t\t\tsignal: controller?.signal,\r\n\t\t\t});\r\n\r\n\t\t\tif (!response.ok) {\r\n\t\t\t\tthrow new Error(`[NUIX] fetchNui(\"${event}\") failed with HTTP ${response.status}`);\r\n\t\t\t}\r\n\r\n\t\t\tconst result = (await response.json()) as TMap[K][\"response\"];\r\n\r\n\t\t\tif (debug) {\r\n\t\t\t\tconsole.log(`[NUIX] ← ${event}`, result);\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\r\n\t\t\t\tthrow new Error(`[NUIX] fetchNui(\"${event}\") timed out after ${options?.timeout}ms`);\r\n\t\t\t}\r\n\t\t\tthrow error;\r\n\t\t} finally {\r\n\t\t\tif (timeoutId !== undefined) clearTimeout(timeoutId);\r\n\t\t}\r\n\t};\r\n}\r\n","import type { NuiEventMap, NuiMessagePayload, NuiMessageHandler, UnsubscribeFn } from \"./types\";\r\n\r\n// ─── NUI Message Listener ───\r\n\r\n/**\r\n * Listens for NUI messages from Lua (`SendNUIMessage`).\r\n *\r\n * **Per-action** — filters by action name, `data` is fully typed:\r\n * ```ts\r\n * onNuiMessage<Events, \"showMenu\">(\"showMenu\", (data) => {\r\n * console.log(data.items); // ✅ typed as string[]\r\n * });\r\n * ```\r\n *\r\n * **Switch-case** — single listener for all actions:\r\n * ```ts\r\n * onNuiMessage<Events>((action, data) => {\r\n * switch (action) {\r\n * case \"setLocales\":\r\n * registerLocales(data);\r\n * break;\r\n * case \"showMenu\":\r\n * openMenu(data.items);\r\n * break;\r\n * }\r\n * });\r\n * ```\r\n *\r\n * Lua side:\r\n * ```lua\r\n * SendNUIMessage({ action = \"showMenu\", data = { items = {\"Pistol\", \"Rifle\"} } })\r\n * ```\r\n */\r\n\r\n// Per-action overload\r\nexport function onNuiMessage<TMap extends NuiEventMap, K extends keyof TMap & string>(\r\n\taction: K,\r\n\thandler: NuiMessageHandler<TMap[K][\"request\"]>,\r\n): UnsubscribeFn;\r\n\r\n// Switch-case overload\r\nexport function onNuiMessage<TMap extends NuiEventMap>(\r\n\thandler: (action: keyof TMap & string, data: any) => void,\r\n): UnsubscribeFn;\r\n\r\n// Implementation\r\nexport function onNuiMessage(\r\n\tactionOrHandler: string | ((action: string, data: unknown) => void),\r\n\thandler?: (data: unknown) => void,\r\n): UnsubscribeFn {\r\n\tconst listener = (event: MessageEvent<NuiMessagePayload>) => {\r\n\t\tconst payload = event.data;\r\n\r\n\t\tif (!payload || typeof payload !== \"object\") return;\r\n\t\tif (!payload.action) return;\r\n\r\n\t\tif (typeof actionOrHandler === \"string\") {\r\n\t\t\tif (payload.action !== actionOrHandler) return;\r\n\t\t\thandler!(payload.data);\r\n\t\t} else {\r\n\t\t\tactionOrHandler(payload.action, payload.data);\r\n\t\t}\r\n\t};\r\n\r\n\twindow.addEventListener(\"message\", listener);\r\n\r\n\treturn () => {\r\n\t\twindow.removeEventListener(\"message\", listener);\r\n\t};\r\n}\r\n","import type { FormatArg, LocaleRecord, TranslatorFn, TranslatorOptions } from \"./types\";\r\n\r\n// ─── Lua-Style Formatter ───\r\n\r\n/**\r\n * Formats a string using Lua-style placeholders.\r\n *\r\n * Specifiers:\r\n * - `%s` — string (null/undefined becomes empty string)\r\n * - `%d` — integer (floors the value, NaN becomes 0)\r\n * - `%f` — float (NaN becomes 0)\r\n * - `%%` — literal percent sign\r\n *\r\n * @example\r\n * ```ts\r\n * luaFormat(\"Hello %s, you are level %d\", \"Laot\", 42);\r\n * // → \"Hello Laot, you are level 42\"\r\n *\r\n * luaFormat(\"Accuracy: %f%%\", 99.5);\r\n * // → \"Accuracy: 99.5%\"\r\n *\r\n * luaFormat(\"Safe: %s %d\", undefined, NaN);\r\n * // → \"Safe: 0\"\r\n * ```\r\n */\r\nexport function luaFormat(template: string, ...args: FormatArg[]): string {\r\n\tlet argIndex = 0;\r\n\r\n\treturn template.replace(/%([sdfi%])/g, (match, specifier: string) => {\r\n\t\tif (specifier === \"%\") return \"%\";\r\n\r\n\t\tconst raw = args[argIndex++];\r\n\r\n\t\tswitch (specifier) {\r\n\t\t\tcase \"s\":\r\n\t\t\t\treturn String(raw ?? \"\");\r\n\r\n\t\t\tcase \"d\":\r\n\t\t\tcase \"i\": {\r\n\t\t\t\tconst num = Number(raw);\r\n\t\t\t\treturn String(Number.isNaN(num) ? 0 : Math.floor(num));\r\n\t\t\t}\r\n\r\n\t\t\tcase \"f\": {\r\n\t\t\t\tconst num = Number(raw);\r\n\t\t\t\treturn String(Number.isNaN(num) ? 0 : num);\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\treturn match;\r\n\t\t}\r\n\t});\r\n}\r\n\r\n// ─── Dot-Notation Resolver ───\r\n\r\n/**\r\n * Walks a nested locale object by splitting the key on `.`\r\n * Returns the string value at the end of the path, or `undefined` if any segment is missing.\r\n *\r\n * `\"client.greeting\"` → looks up `locales.client.greeting`\r\n * `\"flat_key\"` → looks up `locales.flat_key` directly\r\n */\r\nfunction resolveKey(locales: LocaleRecord, key: string): string | undefined {\r\n\tconst segments = key.split(\".\");\r\n\tlet current: LocaleRecord | string = locales;\r\n\r\n\tfor (const segment of segments) {\r\n\t\tif (typeof current !== \"object\" || current === null) return undefined;\r\n\t\tconst next: string | LocaleRecord | undefined = current[segment];\r\n\t\tif (next === undefined) return undefined;\r\n\t\tcurrent = next;\r\n\t}\r\n\r\n\treturn typeof current === \"string\" ? current : undefined;\r\n}\r\n\r\n// ─── Translator Factory ───\r\n\r\n/**\r\n * Creates a `_U(key, fallback, ...args)` translator bound to a locale record.\r\n *\r\n * The key supports dot-notation to traverse nested locale objects.\r\n * If the key isn't found, the fallback string is returned as-is.\r\n * Any extra args are passed through `luaFormat` for placeholder substitution.\r\n *\r\n * @example\r\n * ```ts\r\n * const _U = createTranslator({\r\n * locales: {\r\n * client: {\r\n * greeting: \"Hello %s!\",\r\n * level: \"Level %d\",\r\n * },\r\n * server: {\r\n * error: \"Error: %s\",\r\n * },\r\n * flat_key: \"Plain message\",\r\n * },\r\n * });\r\n *\r\n * _U(\"client.greeting\", \"MISSING\", \"Laot\"); // → \"Hello Laot!\"\r\n * _U(\"client.level\", \"MISSING\", 42); // → \"Level 42\"\r\n * _U(\"flat_key\", \"MISSING\"); // → \"Plain message\"\r\n * _U(\"no.key\", \"Not found\"); // → \"Not found\"\r\n * ```\r\n */\r\nexport function createTranslator(options: TranslatorOptions): TranslatorFn {\r\n\tconst { locales } = options;\r\n\r\n\treturn (key: string, fallback: string, ...args: FormatArg[]): string => {\r\n\t\tconst template = resolveKey(locales, key);\r\n\r\n\t\tif (template === undefined) {\r\n\t\t\treturn fallback;\r\n\t\t}\r\n\r\n\t\treturn args.length > 0 ? luaFormat(template, ...args) : template;\r\n\t};\r\n}\r\n\r\n// ─── Global Locale Registry ───\r\n\r\nlet _locales: LocaleRecord = {};\r\n\r\n/**\r\n * Sets the global locale map. Call this when Lua sends locale data to the NUI.\r\n *\r\n * @example\r\n * ```ts\r\n * // Lua side:\r\n * // SendNUIMessage({ action = \"setLocales\", data = locales })\r\n *\r\n * onNuiMessage<Events>((action, data) => {\r\n * switch (action) {\r\n * case \"setLocales\":\r\n * registerLocales(data);\r\n * break;\r\n * }\r\n * });\r\n * ```\r\n */\r\nexport function registerLocales(locales: LocaleRecord): void {\r\n\t_locales = locales;\r\n}\r\n\r\n/**\r\n * Merges new entries into the current global locale map without replacing it.\r\n *\r\n * @example\r\n * ```ts\r\n * registerLocales({ ui: { title: \"Dashboard\" } });\r\n * extendLocales({ ui: { subtitle: \"Overview\" } });\r\n *\r\n * _U(\"ui.title\", \"\"); // → \"Dashboard\"\r\n * _U(\"ui.subtitle\", \"\"); // → \"Overview\"\r\n * ```\r\n */\r\nexport function extendLocales(...records: LocaleRecord[]): void {\r\n\t_locales = mergeLocales(_locales, ...records);\r\n}\r\n\r\n/**\r\n * Global translator — reads from the locale map set by `registerLocales` / `extendLocales`.\r\n *\r\n * @param key Dot-notated key, e.g. `\"ui.greeting\"` or flat `\"title\"`\r\n * @param fallback Returned as-is when the key doesn't exist\r\n * @param args Values for `%s`, `%d`, `%f` placeholders\r\n *\r\n * @example\r\n * ```ts\r\n * import { registerLocales, _U } from \"nuix\";\r\n *\r\n * // After Lua sends locales:\r\n * // { ui: { greeting: \"Hello %s!\", level: \"Level %d\" } }\r\n *\r\n * _U(\"ui.greeting\", \"Hi\", \"Laot\"); // → \"Hello Laot!\"\r\n * _U(\"ui.level\", \"Lv.\", 42); // → \"Level 42\"\r\n * _U(\"missing.key\", \"Fallback\"); // → \"Fallback\"\r\n * ```\r\n */\r\nexport function _U(key: string, fallback: string, ...args: FormatArg[]): string {\r\n\tconst template = resolveKey(_locales, key);\r\n\tif (template === undefined) return fallback;\r\n\treturn args.length > 0 ? luaFormat(template, ...args) : template;\r\n}\r\n\r\n// ─── Deep Merge ───\r\n\r\n/**\r\n * Deep-merges multiple locale records into one.\r\n * Later records override earlier ones on key conflicts.\r\n * Nested objects are merged recursively, not replaced entirely.\r\n *\r\n * @example\r\n * ```ts\r\n * const base = { client: { greeting: \"Hello %s!\", level: \"Level %d\" } };\r\n * const overrides = { client: { greeting: \"Hey %s!\" } };\r\n *\r\n * const merged = mergeLocales(base, overrides);\r\n * // merged.client.greeting → \"Hey %s!\"\r\n * // merged.client.level → \"Level %d\" (preserved from base)\r\n * ```\r\n */\r\nexport function mergeLocales(...records: LocaleRecord[]): LocaleRecord {\r\n\tconst result: LocaleRecord = {};\r\n\r\n\tfor (const record of records) {\r\n\t\tfor (const [key, value] of Object.entries(record)) {\r\n\t\t\tconst existing = result[key];\r\n\r\n\t\t\tif (typeof value === \"object\" && typeof existing === \"object\" && existing !== undefined) {\r\n\t\t\t\tresult[key] = mergeLocales(existing, value);\r\n\t\t\t} else {\r\n\t\t\t\tresult[key] = value;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn result;\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/listener.ts","../src/utils.ts"],"sourcesContent":["// ─── Types ───\r\nexport type {\r\n\tNuiEventMap,\r\n\tNuiMessagePayload,\r\n\tFetchNuiOptions,\r\n\tFetchNuiFactoryOptions,\r\n\tLocaleRecord,\r\n\tTranslatorOptions,\r\n\tTranslatorFn,\r\n\tFormatArg,\r\n\tUnsubscribeFn,\r\n\tNuiMessageHandler,\r\n} from \"./types\";\r\n\r\n// ─── Client ───\r\nexport { createFetchNui } from \"./client\";\r\n\r\n// ─── Listener ───\r\nexport { onNuiMessage } from \"./listener\";\r\n\r\n// ─── Utils ───\r\nexport { luaFormat, createTranslator, mergeLocales, registerLocales, extendLocales, _U } from \"./utils\";\r\n","import type { NuiEventMap, FetchNuiOptions, FetchNuiFactoryOptions } from \"./types\";\r\n\r\n// ─── Resource Name ───\r\n\r\n/**\r\n * Grabs the resource name from FiveM's injected global.\r\n * Falls back to `\"nui-frame-app\"` when running outside the game (local dev, tests).\r\n */\r\nfunction getResourceName(): string {\r\n\tif (typeof window !== \"undefined\" && window.GetParentResourceName) {\r\n\t\treturn window.GetParentResourceName();\r\n\t}\r\n\treturn \"nui-frame-app\";\r\n}\r\n\r\n// ─── FetchNui Factory ───\r\n\r\n/**\r\n * Creates a typed `fetchNui` function for your event map.\r\n * POSTs JSON to `https://<resourceName>/<event>`, matching `RegisterNUICallback` on Lua side.\r\n *\r\n * @example\r\n * ```ts\r\n * interface MyEvents extends NuiEventMap {\r\n * getPlayer: { data: { id: number }; response: { name: string } };\r\n * notify: { data: { msg: string }; response: void };\r\n * }\r\n *\r\n * const fetchNui = createFetchNui<MyEvents>();\r\n * const player = await fetchNui(\"getPlayer\", { id: 1 });\r\n *\r\n * // With mocks + debug\r\n * const fetchNui = createFetchNui<MyEvents>({\r\n * debug: true,\r\n * mockData: {\r\n * getPlayer: { name: \"DevPlayer\" },\r\n * notify: (data) => { console.log(\"Mock:\", data.msg); },\r\n * },\r\n * });\r\n * ```\r\n *\r\n * Lua side:\r\n * ```lua\r\n * RegisterNUICallback(\"getPlayer\", function(data, cb)\r\n * local player = GetPlayerData(data.id)\r\n * cb({ name = player.name })\r\n * end)\r\n * ```\r\n */\r\nexport function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: FetchNuiFactoryOptions<TMap>) {\r\n\tconst debug = factoryOptions?.debug ?? false;\r\n\tconst mockData = factoryOptions?.mockData;\r\n\r\n\treturn async function fetchNui<K extends keyof TMap & string>(\r\n\t\tevent: K,\r\n\t\t...args: TMap[K][\"data\"] extends void\r\n\t\t\t? [data?: TMap[K][\"data\"], options?: FetchNuiOptions]\r\n\t\t\t: [data: TMap[K][\"data\"], options?: FetchNuiOptions]\r\n\t): Promise<TMap[K][\"response\"]> {\r\n\t\tconst [data, options] = args;\r\n\t\tif (debug) {\r\n\t\t\tconsole.log(`[NUIX] → ${event}`, data ?? {});\r\n\t\t}\r\n\r\n\t\t// ─── Mock Mode ───\r\n\r\n\t\tif (mockData && event in mockData) {\r\n\t\t\tconst mock = mockData[event];\r\n\r\n\t\t\tif (mock === undefined) {\r\n\t\t\t\tthrow new Error(`[NUIX] Mock data for \"${event}\" is undefined`);\r\n\t\t\t}\r\n\r\n\t\t\tconst result =\r\n\t\t\t\ttypeof mock === \"function\"\r\n\t\t\t\t\t? (mock as (data: TMap[K][\"data\"]) => TMap[K][\"response\"])(data as TMap[K][\"data\"])\r\n\t\t\t\t\t: mock;\r\n\r\n\t\t\tif (debug) {\r\n\t\t\t\tconsole.log(`[NUIX] ← ${event} (mock)`, result);\r\n\t\t\t}\r\n\r\n\t\t\treturn result as TMap[K][\"response\"];\r\n\t\t}\r\n\r\n\t\t// ─── Real Fetch ───\r\n\r\n\t\tconst url = `https://${getResourceName()}/${event}`;\r\n\r\n\t\tconst controller = options?.timeout ? new AbortController() : undefined;\r\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\r\n\r\n\t\tif (controller && options?.timeout) {\r\n\t\t\ttimeoutId = setTimeout(() => controller.abort(), options.timeout);\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst response = await fetch(url, {\r\n\t\t\t\tmethod: \"POST\",\r\n\t\t\t\theaders: { \"Content-Type\": \"application/json; charset=UTF-8\" },\r\n\t\t\t\tbody: JSON.stringify(data ?? {}),\r\n\t\t\t\tsignal: controller?.signal,\r\n\t\t\t});\r\n\r\n\t\t\tif (!response.ok) {\r\n\t\t\t\tthrow new Error(`[NUIX] fetchNui(\"${event}\") failed with HTTP ${response.status}`);\r\n\t\t\t}\r\n\r\n\t\t\tconst result = (await response.json()) as TMap[K][\"response\"];\r\n\r\n\t\t\tif (debug) {\r\n\t\t\t\tconsole.log(`[NUIX] ← ${event}`, result);\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\r\n\t\t\t\tthrow new Error(`[NUIX] fetchNui(\"${event}\") timed out after ${options?.timeout}ms`);\r\n\t\t\t}\r\n\t\t\tthrow error;\r\n\t\t} finally {\r\n\t\t\tif (timeoutId !== undefined) clearTimeout(timeoutId);\r\n\t\t}\r\n\t};\r\n}\r\n","import type { NuiEventMap, NuiMessagePayload, NuiMessageHandler, UnsubscribeFn } from \"./types\";\r\n\r\n// ─── NUI Message Listener ───\r\n\r\n/**\r\n * Listens for NUI messages from Lua (`SendNUIMessage`).\r\n *\r\n * **Per-action** — filters by action name, `data` is fully typed:\r\n * ```ts\r\n * onNuiMessage<Events, \"showMenu\">(\"showMenu\", (data) => {\r\n * console.log(data.items); // ✅ typed as string[]\r\n * });\r\n * ```\r\n *\r\n * **Switch-case** — single listener for all actions:\r\n * ```ts\r\n * onNuiMessage<Events>((action, data) => {\r\n * switch (action) {\r\n * case \"setLocales\":\r\n * registerLocales(data);\r\n * break;\r\n * case \"showMenu\":\r\n * openMenu(data.items);\r\n * break;\r\n * }\r\n * });\r\n * ```\r\n *\r\n * Lua side:\r\n * ```lua\r\n * SendNUIMessage({ action = \"showMenu\", data = { items = {\"Pistol\", \"Rifle\"} } })\r\n * ```\r\n */\r\n\r\n// Per-action overload\r\nexport function onNuiMessage<TMap extends NuiEventMap, K extends keyof TMap & string>(\r\n\taction: K,\r\n\thandler: NuiMessageHandler<TMap[K][\"data\"]>,\r\n): UnsubscribeFn;\r\n\r\n// Switch-case overload\r\nexport function onNuiMessage<TMap extends NuiEventMap>(\r\n\thandler: (action: keyof TMap & string, data: any) => void,\r\n): UnsubscribeFn;\r\n\r\n// Implementation\r\nexport function onNuiMessage(\r\n\tactionOrHandler: string | ((action: string, data: unknown) => void),\r\n\thandler?: (data: unknown) => void,\r\n): UnsubscribeFn {\r\n\tconst listener = (event: MessageEvent<NuiMessagePayload>) => {\r\n\t\tconst payload = event.data;\r\n\r\n\t\tif (!payload || typeof payload !== \"object\") return;\r\n\t\tif (!payload.action) return;\r\n\r\n\t\tif (typeof actionOrHandler === \"string\") {\r\n\t\t\tif (payload.action !== actionOrHandler) return;\r\n\t\t\thandler!(payload.data);\r\n\t\t} else {\r\n\t\t\tactionOrHandler(payload.action, payload.data);\r\n\t\t}\r\n\t};\r\n\r\n\twindow.addEventListener(\"message\", listener);\r\n\r\n\treturn () => {\r\n\t\twindow.removeEventListener(\"message\", listener);\r\n\t};\r\n}\r\n","import type { FormatArg, LocaleRecord, TranslatorFn, TranslatorOptions } from \"./types\";\r\n\r\n// ─── Lua-Style Formatter ───\r\n\r\n/**\r\n * Formats a string using Lua-style placeholders.\r\n *\r\n * Specifiers:\r\n * - `%s` — string (null/undefined becomes empty string)\r\n * - `%d` / `%i` — integer (floors the value, NaN becomes 0)\r\n * - `%f` — float (NaN becomes 0)\r\n * - `%%` — literal percent sign\r\n *\r\n * @example\r\n * ```ts\r\n * luaFormat(\"Hello %s, you are level %d\", \"Laot\", 42);\r\n * // → \"Hello Laot, you are level 42\"\r\n *\r\n * luaFormat(\"Accuracy: %f%%\", 99.5);\r\n * // → \"Accuracy: 99.5%\"\r\n *\r\n * luaFormat(\"Safe: %s %d\", undefined, NaN);\r\n * // → \"Safe: 0\"\r\n * ```\r\n */\r\nexport function luaFormat(template: string, ...args: FormatArg[]): string {\r\n\tlet argIndex = 0;\r\n\r\n\treturn template.replace(/%([sdfi%])/g, (match, specifier: string) => {\r\n\t\tif (specifier === \"%\") return \"%\";\r\n\r\n\t\tconst raw = args[argIndex++];\r\n\r\n\t\tswitch (specifier) {\r\n\t\t\tcase \"s\":\r\n\t\t\t\treturn String(raw ?? \"\");\r\n\r\n\t\t\tcase \"d\":\r\n\t\t\tcase \"i\": {\r\n\t\t\t\tconst num = Number(raw);\r\n\t\t\t\treturn String(Number.isNaN(num) ? 0 : Math.floor(num));\r\n\t\t\t}\r\n\r\n\t\t\tcase \"f\": {\r\n\t\t\t\tconst num = Number(raw);\r\n\t\t\t\treturn String(Number.isNaN(num) ? 0 : num);\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\treturn match;\r\n\t\t}\r\n\t});\r\n}\r\n\r\n// ─── Dot-Notation Resolver ───\r\n\r\n/**\r\n * Walks a nested locale object by splitting the key on `.`\r\n * Returns the string value at the end of the path, or `undefined` if any segment is missing.\r\n *\r\n * `\"client.greeting\"` → looks up `locales.client.greeting`\r\n * `\"flat_key\"` → looks up `locales.flat_key` directly\r\n */\r\nfunction resolveKey(locales: LocaleRecord, key: string): string | undefined {\r\n\tconst segments = key.split(\".\");\r\n\tlet current: LocaleRecord | string = locales;\r\n\r\n\tfor (const segment of segments) {\r\n\t\tif (typeof current !== \"object\" || current === null) return undefined;\r\n\t\tconst next: string | LocaleRecord | undefined = current[segment];\r\n\t\tif (next === undefined) return undefined;\r\n\t\tcurrent = next;\r\n\t}\r\n\r\n\treturn typeof current === \"string\" ? current : undefined;\r\n}\r\n\r\n// ─── Translator Factory ───\r\n\r\n/**\r\n * Creates an isolated translator bound to a specific locale record.\r\n * Use this when you need a separate translator instance, independent of the global `_U`.\r\n *\r\n * @example\r\n * ```ts\r\n * const _T = createTranslator({\r\n * locales: {\r\n * greeting: \"Hello %s!\",\r\n * level: \"Level %d\",\r\n * },\r\n * });\r\n *\r\n * _T(\"greeting\", \"MISSING\", \"Laot\"); // → \"Hello Laot!\"\r\n * _T(\"level\", \"MISSING\", 42); // → \"Level 42\"\r\n * _T(\"no.key\", \"Not found\"); // → \"Not found\"\r\n * ```\r\n */\r\nexport function createTranslator(options: TranslatorOptions): TranslatorFn {\r\n\tconst { locales } = options;\r\n\r\n\treturn (key: string, fallback: string, ...args: FormatArg[]): string => {\r\n\t\tconst template = resolveKey(locales, key);\r\n\r\n\t\tif (template === undefined) {\r\n\t\t\treturn fallback;\r\n\t\t}\r\n\r\n\t\treturn args.length > 0 ? luaFormat(template, ...args) : template;\r\n\t};\r\n}\r\n\r\n// ─── Global Locale Registry ───\r\n\r\nlet _locales: LocaleRecord = {};\r\n\r\n/**\r\n * Sets the global locale map. Call this when Lua sends locale data to the NUI.\r\n *\r\n * @example\r\n * ```ts\r\n * // Lua side:\r\n * // SendNUIMessage({ action = \"setLocales\", data = locales })\r\n *\r\n * onNuiMessage<Events>((action, data) => {\r\n * switch (action) {\r\n * case \"setLocales\":\r\n * registerLocales(data);\r\n * break;\r\n * }\r\n * });\r\n * ```\r\n */\r\nexport function registerLocales(locales: LocaleRecord): void {\r\n\t_locales = locales;\r\n}\r\n\r\n/**\r\n * Merges new entries into the current global locale map without replacing it.\r\n *\r\n * @example\r\n * ```ts\r\n * registerLocales({ ui: { title: \"Dashboard\" } });\r\n * extendLocales({ ui: { subtitle: \"Overview\" } });\r\n *\r\n * _U(\"ui.title\", \"\"); // → \"Dashboard\"\r\n * _U(\"ui.subtitle\", \"\"); // → \"Overview\"\r\n * ```\r\n */\r\nexport function extendLocales(...records: LocaleRecord[]): void {\r\n\t_locales = mergeLocales(_locales, ...records);\r\n}\r\n\r\n/**\r\n * Global translator — reads from the locale map set by `registerLocales` / `extendLocales`.\r\n *\r\n * @param key Dot-notated key, e.g. `\"ui.greeting\"` or flat `\"title\"`\r\n * @param fallback Returned as-is when the key doesn't exist\r\n * @param args Values for `%s`, `%d`, `%f` placeholders\r\n *\r\n * @example\r\n * ```ts\r\n * import { registerLocales, _U } from \"@laot/nuix\";\r\n *\r\n * // After Lua sends locales:\r\n * // { ui: { greeting: \"Hello %s!\", level: \"Level %d\" } }\r\n *\r\n * _U(\"ui.greeting\", \"Hi\", \"Laot\"); // → \"Hello Laot!\"\r\n * _U(\"ui.level\", \"Lv.\", 42); // → \"Level 42\"\r\n * _U(\"missing.key\", \"Fallback\"); // → \"Fallback\"\r\n * ```\r\n */\r\nexport function _U(key: string, fallback: string, ...args: FormatArg[]): string {\r\n\tconst template = resolveKey(_locales, key);\r\n\tif (template === undefined) return fallback;\r\n\treturn args.length > 0 ? luaFormat(template, ...args) : template;\r\n}\r\n\r\n// ─── Deep Merge ───\r\n\r\n/**\r\n * Deep-merges multiple locale records into one.\r\n * Later records override earlier ones on key conflicts.\r\n * Nested objects are merged recursively, not replaced entirely.\r\n *\r\n * @example\r\n * ```ts\r\n * const base = { client: { greeting: \"Hello %s!\", level: \"Level %d\" } };\r\n * const overrides = { client: { greeting: \"Hey %s!\" } };\r\n *\r\n * const merged = mergeLocales(base, overrides);\r\n * // merged.client.greeting → \"Hey %s!\"\r\n * // merged.client.level → \"Level %d\" (preserved from base)\r\n * ```\r\n */\r\nexport function mergeLocales(...records: LocaleRecord[]): LocaleRecord {\r\n\tconst result: LocaleRecord = {};\r\n\r\n\tfor (const record of records) {\r\n\t\tfor (const [key, value] of Object.entries(record)) {\r\n\t\t\tconst existing = result[key];\r\n\r\n\t\t\tif (\r\n\t\t\t\tvalue !== null &&\r\n\t\t\t\texisting !== null &&\r\n\t\t\t\ttypeof value === \"object\" &&\r\n\t\t\t\ttypeof existing === \"object\" &&\r\n\t\t\t\texisting !== undefined\r\n\t\t\t) {\r\n\t\t\t\tresult[key] = mergeLocales(existing, value);\r\n\t\t\t} else {\r\n\t\t\t\tresult[key] = value;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn result;\r\n}\r\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -5,8 +5,8 @@
5
5
  * @example
6
6
  * ```ts
7
7
  * interface MyEvents extends NuiEventMap {
8
- * getPlayer: { request: { id: number }; response: { name: string; level: number } };
9
- * sendNotify: { request: { message: string }; response: void };
8
+ * getPlayer: { data: { id: number }; response: { name: string; level: number } };
9
+ * sendNotify: { data: { message: string }; response: void };
10
10
  * }
11
11
  *
12
12
  * // Now fetchNui("getPlayer", { id: 1 }) returns Promise<{ name: string; level: number }>
@@ -14,7 +14,7 @@
14
14
  */
15
15
  interface NuiEventMap {
16
16
  [event: string]: {
17
- request: unknown;
17
+ data: unknown;
18
18
  response: unknown;
19
19
  };
20
20
  }
@@ -39,7 +39,7 @@ interface FetchNuiOptions {
39
39
  timeout?: number;
40
40
  }
41
41
  /**
42
- * Options passed to `createFetchNui()` at factory level.
42
+ * Config for `createFetchNui()`.
43
43
  *
44
44
  * @example
45
45
  * ```ts
@@ -47,31 +47,26 @@ interface FetchNuiOptions {
47
47
  * debug: true,
48
48
  * mockData: {
49
49
  * getPlayer: { name: "Dev", level: 99 },
50
- * sendNotify: (req) => { console.log(req.message); },
50
+ * sendNotify: (data) => { console.log(data.message); },
51
51
  * },
52
52
  * });
53
53
  * ```
54
54
  */
55
55
  interface FetchNuiFactoryOptions<TMap extends NuiEventMap> {
56
- /** Logs every `fetchNui` call and response to the console with `[NUIX]` prefix. */
56
+ /** Logs every call and response to the console with `[NUIX]` prefix. */
57
57
  debug?: boolean;
58
- /**
59
- * Mock responses for local development outside FiveM.
60
- * Can be a static value or a function that receives the request and returns the response.
61
- * When a mock exists for an event, no HTTP call is made.
62
- */
58
+ /** Static or dynamic mock responses — when set, no real HTTP call is made. */
63
59
  mockData?: {
64
- [K in keyof TMap]?: TMap[K]["response"] | ((request: TMap[K]["request"]) => TMap[K]["response"]);
60
+ [K in keyof TMap]?: TMap[K]["response"] | ((data: TMap[K]["data"]) => TMap[K]["response"]);
65
61
  };
66
62
  }
67
63
  /**
68
- * Locale record can be flat strings or nested objects.
69
- * Nested keys are accessed via dot-notation in the translator.
64
+ * Flat strings or nested objects. Nested keys use dot-notation (`"ui.greeting"`).
70
65
  *
71
66
  * @example
72
67
  * ```ts
73
68
  * const locales: LocaleRecord = {
74
- * client: {
69
+ * ui: {
75
70
  * greeting: "Hello %s!",
76
71
  * level: "Level %d",
77
72
  * },
@@ -83,7 +78,7 @@ type LocaleRecord = {
83
78
  [key: string]: string | LocaleRecord;
84
79
  };
85
80
  interface TranslatorOptions {
86
- /** The locale map. Same structure as `LocaleRecord` — flat or nested. */
81
+ /** The locale map — flat or nested. */
87
82
  locales: LocaleRecord;
88
83
  }
89
84
  /**
@@ -111,31 +106,25 @@ type UnsubscribeFn = () => void;
111
106
  type NuiMessageHandler<TData = unknown> = (data: TData) => void;
112
107
 
113
108
  /**
114
- * Creates a fully typed `fetchNui` function tied to your event map.
115
- *
116
- * The returned function POSTs JSON to `https://<resourceName>/<event>`,
117
- * which maps 1:1 to `RegisterNUICallback` on the Lua side.
118
- *
119
- * Supports optional debug logging, mock data for local dev, and per-call timeout.
109
+ * Creates a typed `fetchNui` function for your event map.
110
+ * POSTs JSON to `https://<resourceName>/<event>`, matching `RegisterNUICallback` on Lua side.
120
111
  *
121
112
  * @example
122
113
  * ```ts
123
114
  * interface MyEvents extends NuiEventMap {
124
- * getPlayer: { request: { id: number }; response: { name: string } };
125
- * notify: { request: { msg: string }; response: void };
115
+ * getPlayer: { data: { id: number }; response: { name: string } };
116
+ * notify: { data: { msg: string }; response: void };
126
117
  * }
127
118
  *
128
- * // Production usage
129
119
  * const fetchNui = createFetchNui<MyEvents>();
130
120
  * const player = await fetchNui("getPlayer", { id: 1 });
131
- * // player.name is typed as string
132
121
  *
133
- * // Local dev with mocks + debug
122
+ * // With mocks + debug
134
123
  * const fetchNui = createFetchNui<MyEvents>({
135
124
  * debug: true,
136
125
  * mockData: {
137
126
  * getPlayer: { name: "DevPlayer" },
138
- * notify: (req) => { console.log("Mock:", req.msg); },
127
+ * notify: (data) => { console.log("Mock:", data.msg); },
139
128
  * },
140
129
  * });
141
130
  * ```
@@ -148,7 +137,7 @@ type NuiMessageHandler<TData = unknown> = (data: TData) => void;
148
137
  * end)
149
138
  * ```
150
139
  */
151
- declare function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: FetchNuiFactoryOptions<TMap>): <K extends keyof TMap & string>(event: K, ...args: TMap[K]["request"] extends void ? [data?: TMap[K]["request"], options?: FetchNuiOptions] : [data: TMap[K]["request"], options?: FetchNuiOptions]) => Promise<TMap[K]["response"]>;
140
+ declare function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: FetchNuiFactoryOptions<TMap>): <K extends keyof TMap & string>(event: K, ...args: TMap[K]["data"] extends void ? [data?: TMap[K]["data"], options?: FetchNuiOptions] : [data: TMap[K]["data"], options?: FetchNuiOptions]) => Promise<TMap[K]["response"]>;
152
141
 
153
142
  /**
154
143
  * Listens for NUI messages from Lua (`SendNUIMessage`).
@@ -179,7 +168,7 @@ declare function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: Fetch
179
168
  * SendNUIMessage({ action = "showMenu", data = { items = {"Pistol", "Rifle"} } })
180
169
  * ```
181
170
  */
182
- declare function onNuiMessage<TMap extends NuiEventMap, K extends keyof TMap & string>(action: K, handler: NuiMessageHandler<TMap[K]["request"]>): UnsubscribeFn;
171
+ declare function onNuiMessage<TMap extends NuiEventMap, K extends keyof TMap & string>(action: K, handler: NuiMessageHandler<TMap[K]["data"]>): UnsubscribeFn;
183
172
  declare function onNuiMessage<TMap extends NuiEventMap>(handler: (action: keyof TMap & string, data: any) => void): UnsubscribeFn;
184
173
 
185
174
  /**
@@ -187,7 +176,7 @@ declare function onNuiMessage<TMap extends NuiEventMap>(handler: (action: keyof
187
176
  *
188
177
  * Specifiers:
189
178
  * - `%s` — string (null/undefined becomes empty string)
190
- * - `%d` — integer (floors the value, NaN becomes 0)
179
+ * - `%d` / `%i` — integer (floors the value, NaN becomes 0)
191
180
  * - `%f` — float (NaN becomes 0)
192
181
  * - `%%` — literal percent sign
193
182
  *
@@ -205,31 +194,21 @@ declare function onNuiMessage<TMap extends NuiEventMap>(handler: (action: keyof
205
194
  */
206
195
  declare function luaFormat(template: string, ...args: FormatArg[]): string;
207
196
  /**
208
- * Creates a `_U(key, fallback, ...args)` translator bound to a locale record.
209
- *
210
- * The key supports dot-notation to traverse nested locale objects.
211
- * If the key isn't found, the fallback string is returned as-is.
212
- * Any extra args are passed through `luaFormat` for placeholder substitution.
197
+ * Creates an isolated translator bound to a specific locale record.
198
+ * Use this when you need a separate translator instance, independent of the global `_U`.
213
199
  *
214
200
  * @example
215
201
  * ```ts
216
- * const _U = createTranslator({
202
+ * const _T = createTranslator({
217
203
  * locales: {
218
- * client: {
219
- * greeting: "Hello %s!",
220
- * level: "Level %d",
221
- * },
222
- * server: {
223
- * error: "Error: %s",
224
- * },
225
- * flat_key: "Plain message",
204
+ * greeting: "Hello %s!",
205
+ * level: "Level %d",
226
206
  * },
227
207
  * });
228
208
  *
229
- * _U("client.greeting", "MISSING", "Laot"); // → "Hello Laot!"
230
- * _U("client.level", "MISSING", 42); // → "Level 42"
231
- * _U("flat_key", "MISSING"); // → "Plain message"
232
- * _U("no.key", "Not found"); // → "Not found"
209
+ * _T("greeting", "MISSING", "Laot"); // → "Hello Laot!"
210
+ * _T("level", "MISSING", 42); // → "Level 42"
211
+ * _T("no.key", "Not found"); // → "Not found"
233
212
  * ```
234
213
  */
235
214
  declare function createTranslator(options: TranslatorOptions): TranslatorFn;
@@ -273,7 +252,7 @@ declare function extendLocales(...records: LocaleRecord[]): void;
273
252
  *
274
253
  * @example
275
254
  * ```ts
276
- * import { registerLocales, _U } from "nuix";
255
+ * import { registerLocales, _U } from "@laot/nuix";
277
256
  *
278
257
  * // After Lua sends locales:
279
258
  * // { ui: { greeting: "Hello %s!", level: "Level %d" } }
package/dist/index.d.ts CHANGED
@@ -5,8 +5,8 @@
5
5
  * @example
6
6
  * ```ts
7
7
  * interface MyEvents extends NuiEventMap {
8
- * getPlayer: { request: { id: number }; response: { name: string; level: number } };
9
- * sendNotify: { request: { message: string }; response: void };
8
+ * getPlayer: { data: { id: number }; response: { name: string; level: number } };
9
+ * sendNotify: { data: { message: string }; response: void };
10
10
  * }
11
11
  *
12
12
  * // Now fetchNui("getPlayer", { id: 1 }) returns Promise<{ name: string; level: number }>
@@ -14,7 +14,7 @@
14
14
  */
15
15
  interface NuiEventMap {
16
16
  [event: string]: {
17
- request: unknown;
17
+ data: unknown;
18
18
  response: unknown;
19
19
  };
20
20
  }
@@ -39,7 +39,7 @@ interface FetchNuiOptions {
39
39
  timeout?: number;
40
40
  }
41
41
  /**
42
- * Options passed to `createFetchNui()` at factory level.
42
+ * Config for `createFetchNui()`.
43
43
  *
44
44
  * @example
45
45
  * ```ts
@@ -47,31 +47,26 @@ interface FetchNuiOptions {
47
47
  * debug: true,
48
48
  * mockData: {
49
49
  * getPlayer: { name: "Dev", level: 99 },
50
- * sendNotify: (req) => { console.log(req.message); },
50
+ * sendNotify: (data) => { console.log(data.message); },
51
51
  * },
52
52
  * });
53
53
  * ```
54
54
  */
55
55
  interface FetchNuiFactoryOptions<TMap extends NuiEventMap> {
56
- /** Logs every `fetchNui` call and response to the console with `[NUIX]` prefix. */
56
+ /** Logs every call and response to the console with `[NUIX]` prefix. */
57
57
  debug?: boolean;
58
- /**
59
- * Mock responses for local development outside FiveM.
60
- * Can be a static value or a function that receives the request and returns the response.
61
- * When a mock exists for an event, no HTTP call is made.
62
- */
58
+ /** Static or dynamic mock responses — when set, no real HTTP call is made. */
63
59
  mockData?: {
64
- [K in keyof TMap]?: TMap[K]["response"] | ((request: TMap[K]["request"]) => TMap[K]["response"]);
60
+ [K in keyof TMap]?: TMap[K]["response"] | ((data: TMap[K]["data"]) => TMap[K]["response"]);
65
61
  };
66
62
  }
67
63
  /**
68
- * Locale record can be flat strings or nested objects.
69
- * Nested keys are accessed via dot-notation in the translator.
64
+ * Flat strings or nested objects. Nested keys use dot-notation (`"ui.greeting"`).
70
65
  *
71
66
  * @example
72
67
  * ```ts
73
68
  * const locales: LocaleRecord = {
74
- * client: {
69
+ * ui: {
75
70
  * greeting: "Hello %s!",
76
71
  * level: "Level %d",
77
72
  * },
@@ -83,7 +78,7 @@ type LocaleRecord = {
83
78
  [key: string]: string | LocaleRecord;
84
79
  };
85
80
  interface TranslatorOptions {
86
- /** The locale map. Same structure as `LocaleRecord` — flat or nested. */
81
+ /** The locale map — flat or nested. */
87
82
  locales: LocaleRecord;
88
83
  }
89
84
  /**
@@ -111,31 +106,25 @@ type UnsubscribeFn = () => void;
111
106
  type NuiMessageHandler<TData = unknown> = (data: TData) => void;
112
107
 
113
108
  /**
114
- * Creates a fully typed `fetchNui` function tied to your event map.
115
- *
116
- * The returned function POSTs JSON to `https://<resourceName>/<event>`,
117
- * which maps 1:1 to `RegisterNUICallback` on the Lua side.
118
- *
119
- * Supports optional debug logging, mock data for local dev, and per-call timeout.
109
+ * Creates a typed `fetchNui` function for your event map.
110
+ * POSTs JSON to `https://<resourceName>/<event>`, matching `RegisterNUICallback` on Lua side.
120
111
  *
121
112
  * @example
122
113
  * ```ts
123
114
  * interface MyEvents extends NuiEventMap {
124
- * getPlayer: { request: { id: number }; response: { name: string } };
125
- * notify: { request: { msg: string }; response: void };
115
+ * getPlayer: { data: { id: number }; response: { name: string } };
116
+ * notify: { data: { msg: string }; response: void };
126
117
  * }
127
118
  *
128
- * // Production usage
129
119
  * const fetchNui = createFetchNui<MyEvents>();
130
120
  * const player = await fetchNui("getPlayer", { id: 1 });
131
- * // player.name is typed as string
132
121
  *
133
- * // Local dev with mocks + debug
122
+ * // With mocks + debug
134
123
  * const fetchNui = createFetchNui<MyEvents>({
135
124
  * debug: true,
136
125
  * mockData: {
137
126
  * getPlayer: { name: "DevPlayer" },
138
- * notify: (req) => { console.log("Mock:", req.msg); },
127
+ * notify: (data) => { console.log("Mock:", data.msg); },
139
128
  * },
140
129
  * });
141
130
  * ```
@@ -148,7 +137,7 @@ type NuiMessageHandler<TData = unknown> = (data: TData) => void;
148
137
  * end)
149
138
  * ```
150
139
  */
151
- declare function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: FetchNuiFactoryOptions<TMap>): <K extends keyof TMap & string>(event: K, ...args: TMap[K]["request"] extends void ? [data?: TMap[K]["request"], options?: FetchNuiOptions] : [data: TMap[K]["request"], options?: FetchNuiOptions]) => Promise<TMap[K]["response"]>;
140
+ declare function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: FetchNuiFactoryOptions<TMap>): <K extends keyof TMap & string>(event: K, ...args: TMap[K]["data"] extends void ? [data?: TMap[K]["data"], options?: FetchNuiOptions] : [data: TMap[K]["data"], options?: FetchNuiOptions]) => Promise<TMap[K]["response"]>;
152
141
 
153
142
  /**
154
143
  * Listens for NUI messages from Lua (`SendNUIMessage`).
@@ -179,7 +168,7 @@ declare function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: Fetch
179
168
  * SendNUIMessage({ action = "showMenu", data = { items = {"Pistol", "Rifle"} } })
180
169
  * ```
181
170
  */
182
- declare function onNuiMessage<TMap extends NuiEventMap, K extends keyof TMap & string>(action: K, handler: NuiMessageHandler<TMap[K]["request"]>): UnsubscribeFn;
171
+ declare function onNuiMessage<TMap extends NuiEventMap, K extends keyof TMap & string>(action: K, handler: NuiMessageHandler<TMap[K]["data"]>): UnsubscribeFn;
183
172
  declare function onNuiMessage<TMap extends NuiEventMap>(handler: (action: keyof TMap & string, data: any) => void): UnsubscribeFn;
184
173
 
185
174
  /**
@@ -187,7 +176,7 @@ declare function onNuiMessage<TMap extends NuiEventMap>(handler: (action: keyof
187
176
  *
188
177
  * Specifiers:
189
178
  * - `%s` — string (null/undefined becomes empty string)
190
- * - `%d` — integer (floors the value, NaN becomes 0)
179
+ * - `%d` / `%i` — integer (floors the value, NaN becomes 0)
191
180
  * - `%f` — float (NaN becomes 0)
192
181
  * - `%%` — literal percent sign
193
182
  *
@@ -205,31 +194,21 @@ declare function onNuiMessage<TMap extends NuiEventMap>(handler: (action: keyof
205
194
  */
206
195
  declare function luaFormat(template: string, ...args: FormatArg[]): string;
207
196
  /**
208
- * Creates a `_U(key, fallback, ...args)` translator bound to a locale record.
209
- *
210
- * The key supports dot-notation to traverse nested locale objects.
211
- * If the key isn't found, the fallback string is returned as-is.
212
- * Any extra args are passed through `luaFormat` for placeholder substitution.
197
+ * Creates an isolated translator bound to a specific locale record.
198
+ * Use this when you need a separate translator instance, independent of the global `_U`.
213
199
  *
214
200
  * @example
215
201
  * ```ts
216
- * const _U = createTranslator({
202
+ * const _T = createTranslator({
217
203
  * locales: {
218
- * client: {
219
- * greeting: "Hello %s!",
220
- * level: "Level %d",
221
- * },
222
- * server: {
223
- * error: "Error: %s",
224
- * },
225
- * flat_key: "Plain message",
204
+ * greeting: "Hello %s!",
205
+ * level: "Level %d",
226
206
  * },
227
207
  * });
228
208
  *
229
- * _U("client.greeting", "MISSING", "Laot"); // → "Hello Laot!"
230
- * _U("client.level", "MISSING", 42); // → "Level 42"
231
- * _U("flat_key", "MISSING"); // → "Plain message"
232
- * _U("no.key", "Not found"); // → "Not found"
209
+ * _T("greeting", "MISSING", "Laot"); // → "Hello Laot!"
210
+ * _T("level", "MISSING", 42); // → "Level 42"
211
+ * _T("no.key", "Not found"); // → "Not found"
233
212
  * ```
234
213
  */
235
214
  declare function createTranslator(options: TranslatorOptions): TranslatorFn;
@@ -273,7 +252,7 @@ declare function extendLocales(...records: LocaleRecord[]): void;
273
252
  *
274
253
  * @example
275
254
  * ```ts
276
- * import { registerLocales, _U } from "nuix";
255
+ * import { registerLocales, _U } from "@laot/nuix";
277
256
  *
278
257
  * // After Lua sends locales:
279
258
  * // { ui: { greeting: "Hello %s!", level: "Level %d" } }
package/dist/index.js CHANGED
@@ -136,7 +136,7 @@ function mergeLocales(...records) {
136
136
  for (const record of records) {
137
137
  for (const [key, value] of Object.entries(record)) {
138
138
  const existing = result[key];
139
- if (typeof value === "object" && typeof existing === "object" && existing !== void 0) {
139
+ if (value !== null && existing !== null && typeof value === "object" && typeof existing === "object" && existing !== void 0) {
140
140
  result[key] = mergeLocales(existing, value);
141
141
  } else {
142
142
  result[key] = value;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/listener.ts","../src/utils.ts"],"sourcesContent":["import type { NuiEventMap, FetchNuiOptions, FetchNuiFactoryOptions } from \"./types\";\r\n\r\n// ─── Resource Name ───\r\n\r\n/**\r\n * Grabs the resource name from FiveM's injected global.\r\n * Falls back to `\"nui-frame-app\"` when running outside the game (local dev, tests).\r\n */\r\nfunction getResourceName(): string {\r\n\tif (typeof window !== \"undefined\" && window.GetParentResourceName) {\r\n\t\treturn window.GetParentResourceName();\r\n\t}\r\n\treturn \"nui-frame-app\";\r\n}\r\n\r\n// ─── FetchNui Factory ───\r\n\r\n/**\r\n * Creates a fully typed `fetchNui` function tied to your event map.\r\n *\r\n * The returned function POSTs JSON to `https://<resourceName>/<event>`,\r\n * which maps 1:1 to `RegisterNUICallback` on the Lua side.\r\n *\r\n * Supports optional debug logging, mock data for local dev, and per-call timeout.\r\n *\r\n * @example\r\n * ```ts\r\n * interface MyEvents extends NuiEventMap {\r\n * getPlayer: { request: { id: number }; response: { name: string } };\r\n * notify: { request: { msg: string }; response: void };\r\n * }\r\n *\r\n * // Production usage\r\n * const fetchNui = createFetchNui<MyEvents>();\r\n * const player = await fetchNui(\"getPlayer\", { id: 1 });\r\n * // player.name is typed as string\r\n *\r\n * // Local dev with mocks + debug\r\n * const fetchNui = createFetchNui<MyEvents>({\r\n * debug: true,\r\n * mockData: {\r\n * getPlayer: { name: \"DevPlayer\" },\r\n * notify: (req) => { console.log(\"Mock:\", req.msg); },\r\n * },\r\n * });\r\n * ```\r\n *\r\n * Lua side:\r\n * ```lua\r\n * RegisterNUICallback(\"getPlayer\", function(data, cb)\r\n * local player = GetPlayerData(data.id)\r\n * cb({ name = player.name })\r\n * end)\r\n * ```\r\n */\r\nexport function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: FetchNuiFactoryOptions<TMap>) {\r\n\tconst debug = factoryOptions?.debug ?? false;\r\n\tconst mockData = factoryOptions?.mockData;\r\n\r\n\treturn async function fetchNui<K extends keyof TMap & string>(\r\n\t\tevent: K,\r\n\t\t...args: TMap[K][\"request\"] extends void\r\n\t\t\t? [data?: TMap[K][\"request\"], options?: FetchNuiOptions]\r\n\t\t\t: [data: TMap[K][\"request\"], options?: FetchNuiOptions]\r\n\t): Promise<TMap[K][\"response\"]> {\r\n\t\tconst [data, options] = args;\r\n\t\tif (debug) {\r\n\t\t\tconsole.log(`[NUIX] → ${event}`, data ?? {});\r\n\t\t}\r\n\r\n\t\t// ─── Mock Mode ───\r\n\r\n\t\tif (mockData && event in mockData) {\r\n\t\t\tconst mock = mockData[event];\r\n\r\n\t\t\tif (mock === undefined) {\r\n\t\t\t\tthrow new Error(`[NUIX] Mock data for \"${event}\" is undefined`);\r\n\t\t\t}\r\n\r\n\t\t\tconst result =\r\n\t\t\t\ttypeof mock === \"function\"\r\n\t\t\t\t\t? (mock as (req: TMap[K][\"request\"]) => TMap[K][\"response\"])(data as TMap[K][\"request\"])\r\n\t\t\t\t\t: mock;\r\n\r\n\t\t\tif (debug) {\r\n\t\t\t\tconsole.log(`[NUIX] ← ${event} (mock)`, result);\r\n\t\t\t}\r\n\r\n\t\t\treturn result as TMap[K][\"response\"];\r\n\t\t}\r\n\r\n\t\t// ─── Real Fetch ───\r\n\r\n\t\tconst url = `https://${getResourceName()}/${event}`;\r\n\r\n\t\tconst controller = options?.timeout ? new AbortController() : undefined;\r\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\r\n\r\n\t\tif (controller && options?.timeout) {\r\n\t\t\ttimeoutId = setTimeout(() => controller.abort(), options.timeout);\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst response = await fetch(url, {\r\n\t\t\t\tmethod: \"POST\",\r\n\t\t\t\theaders: { \"Content-Type\": \"application/json; charset=UTF-8\" },\r\n\t\t\t\tbody: JSON.stringify(data ?? {}),\r\n\t\t\t\tsignal: controller?.signal,\r\n\t\t\t});\r\n\r\n\t\t\tif (!response.ok) {\r\n\t\t\t\tthrow new Error(`[NUIX] fetchNui(\"${event}\") failed with HTTP ${response.status}`);\r\n\t\t\t}\r\n\r\n\t\t\tconst result = (await response.json()) as TMap[K][\"response\"];\r\n\r\n\t\t\tif (debug) {\r\n\t\t\t\tconsole.log(`[NUIX] ← ${event}`, result);\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\r\n\t\t\t\tthrow new Error(`[NUIX] fetchNui(\"${event}\") timed out after ${options?.timeout}ms`);\r\n\t\t\t}\r\n\t\t\tthrow error;\r\n\t\t} finally {\r\n\t\t\tif (timeoutId !== undefined) clearTimeout(timeoutId);\r\n\t\t}\r\n\t};\r\n}\r\n","import type { NuiEventMap, NuiMessagePayload, NuiMessageHandler, UnsubscribeFn } from \"./types\";\r\n\r\n// ─── NUI Message Listener ───\r\n\r\n/**\r\n * Listens for NUI messages from Lua (`SendNUIMessage`).\r\n *\r\n * **Per-action** — filters by action name, `data` is fully typed:\r\n * ```ts\r\n * onNuiMessage<Events, \"showMenu\">(\"showMenu\", (data) => {\r\n * console.log(data.items); // ✅ typed as string[]\r\n * });\r\n * ```\r\n *\r\n * **Switch-case** — single listener for all actions:\r\n * ```ts\r\n * onNuiMessage<Events>((action, data) => {\r\n * switch (action) {\r\n * case \"setLocales\":\r\n * registerLocales(data);\r\n * break;\r\n * case \"showMenu\":\r\n * openMenu(data.items);\r\n * break;\r\n * }\r\n * });\r\n * ```\r\n *\r\n * Lua side:\r\n * ```lua\r\n * SendNUIMessage({ action = \"showMenu\", data = { items = {\"Pistol\", \"Rifle\"} } })\r\n * ```\r\n */\r\n\r\n// Per-action overload\r\nexport function onNuiMessage<TMap extends NuiEventMap, K extends keyof TMap & string>(\r\n\taction: K,\r\n\thandler: NuiMessageHandler<TMap[K][\"request\"]>,\r\n): UnsubscribeFn;\r\n\r\n// Switch-case overload\r\nexport function onNuiMessage<TMap extends NuiEventMap>(\r\n\thandler: (action: keyof TMap & string, data: any) => void,\r\n): UnsubscribeFn;\r\n\r\n// Implementation\r\nexport function onNuiMessage(\r\n\tactionOrHandler: string | ((action: string, data: unknown) => void),\r\n\thandler?: (data: unknown) => void,\r\n): UnsubscribeFn {\r\n\tconst listener = (event: MessageEvent<NuiMessagePayload>) => {\r\n\t\tconst payload = event.data;\r\n\r\n\t\tif (!payload || typeof payload !== \"object\") return;\r\n\t\tif (!payload.action) return;\r\n\r\n\t\tif (typeof actionOrHandler === \"string\") {\r\n\t\t\tif (payload.action !== actionOrHandler) return;\r\n\t\t\thandler!(payload.data);\r\n\t\t} else {\r\n\t\t\tactionOrHandler(payload.action, payload.data);\r\n\t\t}\r\n\t};\r\n\r\n\twindow.addEventListener(\"message\", listener);\r\n\r\n\treturn () => {\r\n\t\twindow.removeEventListener(\"message\", listener);\r\n\t};\r\n}\r\n","import type { FormatArg, LocaleRecord, TranslatorFn, TranslatorOptions } from \"./types\";\r\n\r\n// ─── Lua-Style Formatter ───\r\n\r\n/**\r\n * Formats a string using Lua-style placeholders.\r\n *\r\n * Specifiers:\r\n * - `%s` — string (null/undefined becomes empty string)\r\n * - `%d` — integer (floors the value, NaN becomes 0)\r\n * - `%f` — float (NaN becomes 0)\r\n * - `%%` — literal percent sign\r\n *\r\n * @example\r\n * ```ts\r\n * luaFormat(\"Hello %s, you are level %d\", \"Laot\", 42);\r\n * // → \"Hello Laot, you are level 42\"\r\n *\r\n * luaFormat(\"Accuracy: %f%%\", 99.5);\r\n * // → \"Accuracy: 99.5%\"\r\n *\r\n * luaFormat(\"Safe: %s %d\", undefined, NaN);\r\n * // → \"Safe: 0\"\r\n * ```\r\n */\r\nexport function luaFormat(template: string, ...args: FormatArg[]): string {\r\n\tlet argIndex = 0;\r\n\r\n\treturn template.replace(/%([sdfi%])/g, (match, specifier: string) => {\r\n\t\tif (specifier === \"%\") return \"%\";\r\n\r\n\t\tconst raw = args[argIndex++];\r\n\r\n\t\tswitch (specifier) {\r\n\t\t\tcase \"s\":\r\n\t\t\t\treturn String(raw ?? \"\");\r\n\r\n\t\t\tcase \"d\":\r\n\t\t\tcase \"i\": {\r\n\t\t\t\tconst num = Number(raw);\r\n\t\t\t\treturn String(Number.isNaN(num) ? 0 : Math.floor(num));\r\n\t\t\t}\r\n\r\n\t\t\tcase \"f\": {\r\n\t\t\t\tconst num = Number(raw);\r\n\t\t\t\treturn String(Number.isNaN(num) ? 0 : num);\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\treturn match;\r\n\t\t}\r\n\t});\r\n}\r\n\r\n// ─── Dot-Notation Resolver ───\r\n\r\n/**\r\n * Walks a nested locale object by splitting the key on `.`\r\n * Returns the string value at the end of the path, or `undefined` if any segment is missing.\r\n *\r\n * `\"client.greeting\"` → looks up `locales.client.greeting`\r\n * `\"flat_key\"` → looks up `locales.flat_key` directly\r\n */\r\nfunction resolveKey(locales: LocaleRecord, key: string): string | undefined {\r\n\tconst segments = key.split(\".\");\r\n\tlet current: LocaleRecord | string = locales;\r\n\r\n\tfor (const segment of segments) {\r\n\t\tif (typeof current !== \"object\" || current === null) return undefined;\r\n\t\tconst next: string | LocaleRecord | undefined = current[segment];\r\n\t\tif (next === undefined) return undefined;\r\n\t\tcurrent = next;\r\n\t}\r\n\r\n\treturn typeof current === \"string\" ? current : undefined;\r\n}\r\n\r\n// ─── Translator Factory ───\r\n\r\n/**\r\n * Creates a `_U(key, fallback, ...args)` translator bound to a locale record.\r\n *\r\n * The key supports dot-notation to traverse nested locale objects.\r\n * If the key isn't found, the fallback string is returned as-is.\r\n * Any extra args are passed through `luaFormat` for placeholder substitution.\r\n *\r\n * @example\r\n * ```ts\r\n * const _U = createTranslator({\r\n * locales: {\r\n * client: {\r\n * greeting: \"Hello %s!\",\r\n * level: \"Level %d\",\r\n * },\r\n * server: {\r\n * error: \"Error: %s\",\r\n * },\r\n * flat_key: \"Plain message\",\r\n * },\r\n * });\r\n *\r\n * _U(\"client.greeting\", \"MISSING\", \"Laot\"); // → \"Hello Laot!\"\r\n * _U(\"client.level\", \"MISSING\", 42); // → \"Level 42\"\r\n * _U(\"flat_key\", \"MISSING\"); // → \"Plain message\"\r\n * _U(\"no.key\", \"Not found\"); // → \"Not found\"\r\n * ```\r\n */\r\nexport function createTranslator(options: TranslatorOptions): TranslatorFn {\r\n\tconst { locales } = options;\r\n\r\n\treturn (key: string, fallback: string, ...args: FormatArg[]): string => {\r\n\t\tconst template = resolveKey(locales, key);\r\n\r\n\t\tif (template === undefined) {\r\n\t\t\treturn fallback;\r\n\t\t}\r\n\r\n\t\treturn args.length > 0 ? luaFormat(template, ...args) : template;\r\n\t};\r\n}\r\n\r\n// ─── Global Locale Registry ───\r\n\r\nlet _locales: LocaleRecord = {};\r\n\r\n/**\r\n * Sets the global locale map. Call this when Lua sends locale data to the NUI.\r\n *\r\n * @example\r\n * ```ts\r\n * // Lua side:\r\n * // SendNUIMessage({ action = \"setLocales\", data = locales })\r\n *\r\n * onNuiMessage<Events>((action, data) => {\r\n * switch (action) {\r\n * case \"setLocales\":\r\n * registerLocales(data);\r\n * break;\r\n * }\r\n * });\r\n * ```\r\n */\r\nexport function registerLocales(locales: LocaleRecord): void {\r\n\t_locales = locales;\r\n}\r\n\r\n/**\r\n * Merges new entries into the current global locale map without replacing it.\r\n *\r\n * @example\r\n * ```ts\r\n * registerLocales({ ui: { title: \"Dashboard\" } });\r\n * extendLocales({ ui: { subtitle: \"Overview\" } });\r\n *\r\n * _U(\"ui.title\", \"\"); // → \"Dashboard\"\r\n * _U(\"ui.subtitle\", \"\"); // → \"Overview\"\r\n * ```\r\n */\r\nexport function extendLocales(...records: LocaleRecord[]): void {\r\n\t_locales = mergeLocales(_locales, ...records);\r\n}\r\n\r\n/**\r\n * Global translator — reads from the locale map set by `registerLocales` / `extendLocales`.\r\n *\r\n * @param key Dot-notated key, e.g. `\"ui.greeting\"` or flat `\"title\"`\r\n * @param fallback Returned as-is when the key doesn't exist\r\n * @param args Values for `%s`, `%d`, `%f` placeholders\r\n *\r\n * @example\r\n * ```ts\r\n * import { registerLocales, _U } from \"nuix\";\r\n *\r\n * // After Lua sends locales:\r\n * // { ui: { greeting: \"Hello %s!\", level: \"Level %d\" } }\r\n *\r\n * _U(\"ui.greeting\", \"Hi\", \"Laot\"); // → \"Hello Laot!\"\r\n * _U(\"ui.level\", \"Lv.\", 42); // → \"Level 42\"\r\n * _U(\"missing.key\", \"Fallback\"); // → \"Fallback\"\r\n * ```\r\n */\r\nexport function _U(key: string, fallback: string, ...args: FormatArg[]): string {\r\n\tconst template = resolveKey(_locales, key);\r\n\tif (template === undefined) return fallback;\r\n\treturn args.length > 0 ? luaFormat(template, ...args) : template;\r\n}\r\n\r\n// ─── Deep Merge ───\r\n\r\n/**\r\n * Deep-merges multiple locale records into one.\r\n * Later records override earlier ones on key conflicts.\r\n * Nested objects are merged recursively, not replaced entirely.\r\n *\r\n * @example\r\n * ```ts\r\n * const base = { client: { greeting: \"Hello %s!\", level: \"Level %d\" } };\r\n * const overrides = { client: { greeting: \"Hey %s!\" } };\r\n *\r\n * const merged = mergeLocales(base, overrides);\r\n * // merged.client.greeting → \"Hey %s!\"\r\n * // merged.client.level → \"Level %d\" (preserved from base)\r\n * ```\r\n */\r\nexport function mergeLocales(...records: LocaleRecord[]): LocaleRecord {\r\n\tconst result: LocaleRecord = {};\r\n\r\n\tfor (const record of records) {\r\n\t\tfor (const [key, value] of Object.entries(record)) {\r\n\t\t\tconst existing = result[key];\r\n\r\n\t\t\tif (typeof value === \"object\" && typeof existing === \"object\" && existing !== undefined) {\r\n\t\t\t\tresult[key] = mergeLocales(existing, value);\r\n\t\t\t} else {\r\n\t\t\t\tresult[key] = value;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn result;\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/listener.ts","../src/utils.ts"],"sourcesContent":["import type { NuiEventMap, FetchNuiOptions, FetchNuiFactoryOptions } from \"./types\";\r\n\r\n// ─── Resource Name ───\r\n\r\n/**\r\n * Grabs the resource name from FiveM's injected global.\r\n * Falls back to `\"nui-frame-app\"` when running outside the game (local dev, tests).\r\n */\r\nfunction getResourceName(): string {\r\n\tif (typeof window !== \"undefined\" && window.GetParentResourceName) {\r\n\t\treturn window.GetParentResourceName();\r\n\t}\r\n\treturn \"nui-frame-app\";\r\n}\r\n\r\n// ─── FetchNui Factory ───\r\n\r\n/**\r\n * Creates a typed `fetchNui` function for your event map.\r\n * POSTs JSON to `https://<resourceName>/<event>`, matching `RegisterNUICallback` on Lua side.\r\n *\r\n * @example\r\n * ```ts\r\n * interface MyEvents extends NuiEventMap {\r\n * getPlayer: { data: { id: number }; response: { name: string } };\r\n * notify: { data: { msg: string }; response: void };\r\n * }\r\n *\r\n * const fetchNui = createFetchNui<MyEvents>();\r\n * const player = await fetchNui(\"getPlayer\", { id: 1 });\r\n *\r\n * // With mocks + debug\r\n * const fetchNui = createFetchNui<MyEvents>({\r\n * debug: true,\r\n * mockData: {\r\n * getPlayer: { name: \"DevPlayer\" },\r\n * notify: (data) => { console.log(\"Mock:\", data.msg); },\r\n * },\r\n * });\r\n * ```\r\n *\r\n * Lua side:\r\n * ```lua\r\n * RegisterNUICallback(\"getPlayer\", function(data, cb)\r\n * local player = GetPlayerData(data.id)\r\n * cb({ name = player.name })\r\n * end)\r\n * ```\r\n */\r\nexport function createFetchNui<TMap extends NuiEventMap>(factoryOptions?: FetchNuiFactoryOptions<TMap>) {\r\n\tconst debug = factoryOptions?.debug ?? false;\r\n\tconst mockData = factoryOptions?.mockData;\r\n\r\n\treturn async function fetchNui<K extends keyof TMap & string>(\r\n\t\tevent: K,\r\n\t\t...args: TMap[K][\"data\"] extends void\r\n\t\t\t? [data?: TMap[K][\"data\"], options?: FetchNuiOptions]\r\n\t\t\t: [data: TMap[K][\"data\"], options?: FetchNuiOptions]\r\n\t): Promise<TMap[K][\"response\"]> {\r\n\t\tconst [data, options] = args;\r\n\t\tif (debug) {\r\n\t\t\tconsole.log(`[NUIX] → ${event}`, data ?? {});\r\n\t\t}\r\n\r\n\t\t// ─── Mock Mode ───\r\n\r\n\t\tif (mockData && event in mockData) {\r\n\t\t\tconst mock = mockData[event];\r\n\r\n\t\t\tif (mock === undefined) {\r\n\t\t\t\tthrow new Error(`[NUIX] Mock data for \"${event}\" is undefined`);\r\n\t\t\t}\r\n\r\n\t\t\tconst result =\r\n\t\t\t\ttypeof mock === \"function\"\r\n\t\t\t\t\t? (mock as (data: TMap[K][\"data\"]) => TMap[K][\"response\"])(data as TMap[K][\"data\"])\r\n\t\t\t\t\t: mock;\r\n\r\n\t\t\tif (debug) {\r\n\t\t\t\tconsole.log(`[NUIX] ← ${event} (mock)`, result);\r\n\t\t\t}\r\n\r\n\t\t\treturn result as TMap[K][\"response\"];\r\n\t\t}\r\n\r\n\t\t// ─── Real Fetch ───\r\n\r\n\t\tconst url = `https://${getResourceName()}/${event}`;\r\n\r\n\t\tconst controller = options?.timeout ? new AbortController() : undefined;\r\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\r\n\r\n\t\tif (controller && options?.timeout) {\r\n\t\t\ttimeoutId = setTimeout(() => controller.abort(), options.timeout);\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst response = await fetch(url, {\r\n\t\t\t\tmethod: \"POST\",\r\n\t\t\t\theaders: { \"Content-Type\": \"application/json; charset=UTF-8\" },\r\n\t\t\t\tbody: JSON.stringify(data ?? {}),\r\n\t\t\t\tsignal: controller?.signal,\r\n\t\t\t});\r\n\r\n\t\t\tif (!response.ok) {\r\n\t\t\t\tthrow new Error(`[NUIX] fetchNui(\"${event}\") failed with HTTP ${response.status}`);\r\n\t\t\t}\r\n\r\n\t\t\tconst result = (await response.json()) as TMap[K][\"response\"];\r\n\r\n\t\t\tif (debug) {\r\n\t\t\t\tconsole.log(`[NUIX] ← ${event}`, result);\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t} catch (error) {\r\n\t\t\tif (error instanceof DOMException && error.name === \"AbortError\") {\r\n\t\t\t\tthrow new Error(`[NUIX] fetchNui(\"${event}\") timed out after ${options?.timeout}ms`);\r\n\t\t\t}\r\n\t\t\tthrow error;\r\n\t\t} finally {\r\n\t\t\tif (timeoutId !== undefined) clearTimeout(timeoutId);\r\n\t\t}\r\n\t};\r\n}\r\n","import type { NuiEventMap, NuiMessagePayload, NuiMessageHandler, UnsubscribeFn } from \"./types\";\r\n\r\n// ─── NUI Message Listener ───\r\n\r\n/**\r\n * Listens for NUI messages from Lua (`SendNUIMessage`).\r\n *\r\n * **Per-action** — filters by action name, `data` is fully typed:\r\n * ```ts\r\n * onNuiMessage<Events, \"showMenu\">(\"showMenu\", (data) => {\r\n * console.log(data.items); // ✅ typed as string[]\r\n * });\r\n * ```\r\n *\r\n * **Switch-case** — single listener for all actions:\r\n * ```ts\r\n * onNuiMessage<Events>((action, data) => {\r\n * switch (action) {\r\n * case \"setLocales\":\r\n * registerLocales(data);\r\n * break;\r\n * case \"showMenu\":\r\n * openMenu(data.items);\r\n * break;\r\n * }\r\n * });\r\n * ```\r\n *\r\n * Lua side:\r\n * ```lua\r\n * SendNUIMessage({ action = \"showMenu\", data = { items = {\"Pistol\", \"Rifle\"} } })\r\n * ```\r\n */\r\n\r\n// Per-action overload\r\nexport function onNuiMessage<TMap extends NuiEventMap, K extends keyof TMap & string>(\r\n\taction: K,\r\n\thandler: NuiMessageHandler<TMap[K][\"data\"]>,\r\n): UnsubscribeFn;\r\n\r\n// Switch-case overload\r\nexport function onNuiMessage<TMap extends NuiEventMap>(\r\n\thandler: (action: keyof TMap & string, data: any) => void,\r\n): UnsubscribeFn;\r\n\r\n// Implementation\r\nexport function onNuiMessage(\r\n\tactionOrHandler: string | ((action: string, data: unknown) => void),\r\n\thandler?: (data: unknown) => void,\r\n): UnsubscribeFn {\r\n\tconst listener = (event: MessageEvent<NuiMessagePayload>) => {\r\n\t\tconst payload = event.data;\r\n\r\n\t\tif (!payload || typeof payload !== \"object\") return;\r\n\t\tif (!payload.action) return;\r\n\r\n\t\tif (typeof actionOrHandler === \"string\") {\r\n\t\t\tif (payload.action !== actionOrHandler) return;\r\n\t\t\thandler!(payload.data);\r\n\t\t} else {\r\n\t\t\tactionOrHandler(payload.action, payload.data);\r\n\t\t}\r\n\t};\r\n\r\n\twindow.addEventListener(\"message\", listener);\r\n\r\n\treturn () => {\r\n\t\twindow.removeEventListener(\"message\", listener);\r\n\t};\r\n}\r\n","import type { FormatArg, LocaleRecord, TranslatorFn, TranslatorOptions } from \"./types\";\r\n\r\n// ─── Lua-Style Formatter ───\r\n\r\n/**\r\n * Formats a string using Lua-style placeholders.\r\n *\r\n * Specifiers:\r\n * - `%s` — string (null/undefined becomes empty string)\r\n * - `%d` / `%i` — integer (floors the value, NaN becomes 0)\r\n * - `%f` — float (NaN becomes 0)\r\n * - `%%` — literal percent sign\r\n *\r\n * @example\r\n * ```ts\r\n * luaFormat(\"Hello %s, you are level %d\", \"Laot\", 42);\r\n * // → \"Hello Laot, you are level 42\"\r\n *\r\n * luaFormat(\"Accuracy: %f%%\", 99.5);\r\n * // → \"Accuracy: 99.5%\"\r\n *\r\n * luaFormat(\"Safe: %s %d\", undefined, NaN);\r\n * // → \"Safe: 0\"\r\n * ```\r\n */\r\nexport function luaFormat(template: string, ...args: FormatArg[]): string {\r\n\tlet argIndex = 0;\r\n\r\n\treturn template.replace(/%([sdfi%])/g, (match, specifier: string) => {\r\n\t\tif (specifier === \"%\") return \"%\";\r\n\r\n\t\tconst raw = args[argIndex++];\r\n\r\n\t\tswitch (specifier) {\r\n\t\t\tcase \"s\":\r\n\t\t\t\treturn String(raw ?? \"\");\r\n\r\n\t\t\tcase \"d\":\r\n\t\t\tcase \"i\": {\r\n\t\t\t\tconst num = Number(raw);\r\n\t\t\t\treturn String(Number.isNaN(num) ? 0 : Math.floor(num));\r\n\t\t\t}\r\n\r\n\t\t\tcase \"f\": {\r\n\t\t\t\tconst num = Number(raw);\r\n\t\t\t\treturn String(Number.isNaN(num) ? 0 : num);\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\treturn match;\r\n\t\t}\r\n\t});\r\n}\r\n\r\n// ─── Dot-Notation Resolver ───\r\n\r\n/**\r\n * Walks a nested locale object by splitting the key on `.`\r\n * Returns the string value at the end of the path, or `undefined` if any segment is missing.\r\n *\r\n * `\"client.greeting\"` → looks up `locales.client.greeting`\r\n * `\"flat_key\"` → looks up `locales.flat_key` directly\r\n */\r\nfunction resolveKey(locales: LocaleRecord, key: string): string | undefined {\r\n\tconst segments = key.split(\".\");\r\n\tlet current: LocaleRecord | string = locales;\r\n\r\n\tfor (const segment of segments) {\r\n\t\tif (typeof current !== \"object\" || current === null) return undefined;\r\n\t\tconst next: string | LocaleRecord | undefined = current[segment];\r\n\t\tif (next === undefined) return undefined;\r\n\t\tcurrent = next;\r\n\t}\r\n\r\n\treturn typeof current === \"string\" ? current : undefined;\r\n}\r\n\r\n// ─── Translator Factory ───\r\n\r\n/**\r\n * Creates an isolated translator bound to a specific locale record.\r\n * Use this when you need a separate translator instance, independent of the global `_U`.\r\n *\r\n * @example\r\n * ```ts\r\n * const _T = createTranslator({\r\n * locales: {\r\n * greeting: \"Hello %s!\",\r\n * level: \"Level %d\",\r\n * },\r\n * });\r\n *\r\n * _T(\"greeting\", \"MISSING\", \"Laot\"); // → \"Hello Laot!\"\r\n * _T(\"level\", \"MISSING\", 42); // → \"Level 42\"\r\n * _T(\"no.key\", \"Not found\"); // → \"Not found\"\r\n * ```\r\n */\r\nexport function createTranslator(options: TranslatorOptions): TranslatorFn {\r\n\tconst { locales } = options;\r\n\r\n\treturn (key: string, fallback: string, ...args: FormatArg[]): string => {\r\n\t\tconst template = resolveKey(locales, key);\r\n\r\n\t\tif (template === undefined) {\r\n\t\t\treturn fallback;\r\n\t\t}\r\n\r\n\t\treturn args.length > 0 ? luaFormat(template, ...args) : template;\r\n\t};\r\n}\r\n\r\n// ─── Global Locale Registry ───\r\n\r\nlet _locales: LocaleRecord = {};\r\n\r\n/**\r\n * Sets the global locale map. Call this when Lua sends locale data to the NUI.\r\n *\r\n * @example\r\n * ```ts\r\n * // Lua side:\r\n * // SendNUIMessage({ action = \"setLocales\", data = locales })\r\n *\r\n * onNuiMessage<Events>((action, data) => {\r\n * switch (action) {\r\n * case \"setLocales\":\r\n * registerLocales(data);\r\n * break;\r\n * }\r\n * });\r\n * ```\r\n */\r\nexport function registerLocales(locales: LocaleRecord): void {\r\n\t_locales = locales;\r\n}\r\n\r\n/**\r\n * Merges new entries into the current global locale map without replacing it.\r\n *\r\n * @example\r\n * ```ts\r\n * registerLocales({ ui: { title: \"Dashboard\" } });\r\n * extendLocales({ ui: { subtitle: \"Overview\" } });\r\n *\r\n * _U(\"ui.title\", \"\"); // → \"Dashboard\"\r\n * _U(\"ui.subtitle\", \"\"); // → \"Overview\"\r\n * ```\r\n */\r\nexport function extendLocales(...records: LocaleRecord[]): void {\r\n\t_locales = mergeLocales(_locales, ...records);\r\n}\r\n\r\n/**\r\n * Global translator — reads from the locale map set by `registerLocales` / `extendLocales`.\r\n *\r\n * @param key Dot-notated key, e.g. `\"ui.greeting\"` or flat `\"title\"`\r\n * @param fallback Returned as-is when the key doesn't exist\r\n * @param args Values for `%s`, `%d`, `%f` placeholders\r\n *\r\n * @example\r\n * ```ts\r\n * import { registerLocales, _U } from \"@laot/nuix\";\r\n *\r\n * // After Lua sends locales:\r\n * // { ui: { greeting: \"Hello %s!\", level: \"Level %d\" } }\r\n *\r\n * _U(\"ui.greeting\", \"Hi\", \"Laot\"); // → \"Hello Laot!\"\r\n * _U(\"ui.level\", \"Lv.\", 42); // → \"Level 42\"\r\n * _U(\"missing.key\", \"Fallback\"); // → \"Fallback\"\r\n * ```\r\n */\r\nexport function _U(key: string, fallback: string, ...args: FormatArg[]): string {\r\n\tconst template = resolveKey(_locales, key);\r\n\tif (template === undefined) return fallback;\r\n\treturn args.length > 0 ? luaFormat(template, ...args) : template;\r\n}\r\n\r\n// ─── Deep Merge ───\r\n\r\n/**\r\n * Deep-merges multiple locale records into one.\r\n * Later records override earlier ones on key conflicts.\r\n * Nested objects are merged recursively, not replaced entirely.\r\n *\r\n * @example\r\n * ```ts\r\n * const base = { client: { greeting: \"Hello %s!\", level: \"Level %d\" } };\r\n * const overrides = { client: { greeting: \"Hey %s!\" } };\r\n *\r\n * const merged = mergeLocales(base, overrides);\r\n * // merged.client.greeting → \"Hey %s!\"\r\n * // merged.client.level → \"Level %d\" (preserved from base)\r\n * ```\r\n */\r\nexport function mergeLocales(...records: LocaleRecord[]): LocaleRecord {\r\n\tconst result: LocaleRecord = {};\r\n\r\n\tfor (const record of records) {\r\n\t\tfor (const [key, value] of Object.entries(record)) {\r\n\t\t\tconst existing = result[key];\r\n\r\n\t\t\tif (\r\n\t\t\t\tvalue !== null &&\r\n\t\t\t\texisting !== null &&\r\n\t\t\t\ttypeof value === \"object\" &&\r\n\t\t\t\ttypeof existing === \"object\" &&\r\n\t\t\t\texisting !== undefined\r\n\t\t\t) {\r\n\t\t\t\tresult[key] = mergeLocales(existing, value);\r\n\t\t\t} else {\r\n\t\t\t\tresult[key] = value;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn result;\r\n}\r\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laot/nuix",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Modular, type-safe TypeScript library for FiveM NUI projects",
5
5
  "sideEffects": false,
6
6
  "type": "module",