@warpfx/nui-core 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -10,11 +10,13 @@
10
10
  * - Client → NUI: SendNUIMessage → window.message event
11
11
  * - NUI → Client: fetch('https://warp/...') → RegisterNUICallback
12
12
  */
13
+ type MessageHandler = (data: any) => void;
13
14
  type Unsubscribe = () => void;
14
15
  type VisibilityHandler = (panel?: string) => void;
15
16
  declare class WarpBridge {
16
17
  private state;
17
18
  private syncHandlers;
19
+ private messageHandlers;
18
20
  private showHandlers;
19
21
  private hideHandlers;
20
22
  private _visible;
@@ -31,20 +33,57 @@ declare class WarpBridge {
31
33
  get panel(): string | undefined;
32
34
  /** Get current state for a key. */
33
35
  getState<T>(key: string): T | undefined;
36
+ /**
37
+ * Listen for custom messages from the FiveM client.
38
+ *
39
+ * Catches any `SendNUIMessage({ type, ... })` call that isn't already
40
+ * handled by the sync or visibility system. Returns an unsubscribe function.
41
+ *
42
+ * @example
43
+ * bridge.on('myModule:update', (data) => { ... });
44
+ */
45
+ on(type: string, handler: MessageHandler): Unsubscribe;
34
46
  /**
35
47
  * Subscribe to synced state updates for a key.
36
48
  * Fires immediately with current state if available.
37
49
  * Returns an unsubscribe function.
38
50
  */
39
51
  onSync<T>(key: string, callback: (state: T) => void): Unsubscribe;
52
+ /**
53
+ * Call a server function and await the result.
54
+ *
55
+ * Routes through the FiveM client via NUI callback → callServer → server.
56
+ * Returns a Promise that resolves with the server's return value.
57
+ *
58
+ * @param name The registered function name.
59
+ * @param payload Optional data to send to the server.
60
+ * @param timeout Timeout in ms (default: 10000).
61
+ *
62
+ * @example
63
+ * const { balance } = await bridge.call<{ balance: number }>('economy:getBalance');
64
+ */
65
+ call<T = any>(name: string, payload?: any, timeout?: number): Promise<T>;
40
66
  /** Dispatch an action to the server via the FiveM client. */
41
67
  action(name: string, payload?: any): void;
68
+ /**
69
+ * Send a message to the FiveM client via NUI callback.
70
+ * The client handles it with `onNUI(name, handler)`.
71
+ *
72
+ * Use this for client-only communication (NUI → client).
73
+ * For server mutations, use `action()` instead.
74
+ *
75
+ * @example
76
+ * bridge.send('chatSend', { message: '/help' });
77
+ * bridge.send('inventoryDrop', { itemId: 'abc', slot: 3 });
78
+ */
79
+ send(name: string, data?: any): void;
42
80
  /** Request the client to close/hide the NUI. */
43
81
  close(): void;
44
82
  /** Register a callback for when the NUI becomes visible. */
45
83
  onShow(callback: VisibilityHandler): Unsubscribe;
46
84
  /** Register a callback for when the NUI is hidden. */
47
85
  onHide(callback: () => void): Unsubscribe;
86
+ private handleMessage;
48
87
  private handleSync;
49
88
  private handleVisibility;
50
89
  }
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  var WarpBridge = class _WarpBridge {
3
3
  state = /* @__PURE__ */ new Map();
4
4
  syncHandlers = /* @__PURE__ */ new Map();
5
+ messageHandlers = /* @__PURE__ */ new Map();
5
6
  showHandlers = /* @__PURE__ */ new Set();
6
7
  hideHandlers = /* @__PURE__ */ new Set();
7
8
  _visible = false;
@@ -14,6 +15,8 @@ var WarpBridge = class _WarpBridge {
14
15
  this.handleSync(data);
15
16
  } else if (data.type === "warp:visibility") {
16
17
  this.handleVisibility(data.visible, data.panel);
18
+ } else if (data.type) {
19
+ this.handleMessage(data.type, data);
17
20
  }
18
21
  });
19
22
  }
@@ -43,6 +46,26 @@ var WarpBridge = class _WarpBridge {
43
46
  return this.state.get(key);
44
47
  }
45
48
  // -------------------------------------------------------------------------
49
+ // Message subscriptions
50
+ // -------------------------------------------------------------------------
51
+ /**
52
+ * Listen for custom messages from the FiveM client.
53
+ *
54
+ * Catches any `SendNUIMessage({ type, ... })` call that isn't already
55
+ * handled by the sync or visibility system. Returns an unsubscribe function.
56
+ *
57
+ * @example
58
+ * bridge.on('myModule:update', (data) => { ... });
59
+ */
60
+ on(type, handler) {
61
+ if (!this.messageHandlers.has(type)) {
62
+ this.messageHandlers.set(type, /* @__PURE__ */ new Set());
63
+ }
64
+ const set = this.messageHandlers.get(type);
65
+ set.add(handler);
66
+ return () => set.delete(handler);
67
+ }
68
+ // -------------------------------------------------------------------------
46
69
  // Sync subscriptions
47
70
  // -------------------------------------------------------------------------
48
71
  /**
@@ -68,6 +91,32 @@ var WarpBridge = class _WarpBridge {
68
91
  // -------------------------------------------------------------------------
69
92
  // Actions
70
93
  // -------------------------------------------------------------------------
94
+ /**
95
+ * Call a server function and await the result.
96
+ *
97
+ * Routes through the FiveM client via NUI callback → callServer → server.
98
+ * Returns a Promise that resolves with the server's return value.
99
+ *
100
+ * @param name The registered function name.
101
+ * @param payload Optional data to send to the server.
102
+ * @param timeout Timeout in ms (default: 10000).
103
+ *
104
+ * @example
105
+ * const { balance } = await bridge.call<{ balance: number }>('economy:getBalance');
106
+ */
107
+ async call(name, payload, timeout) {
108
+ const res = await fetch("https://warp/rpc", {
109
+ method: "POST",
110
+ body: JSON.stringify({ name, payload, timeout })
111
+ });
112
+ const data = await res.json();
113
+ if (!data.ok) {
114
+ const err = new Error(data.error?.message ?? "RPC call failed");
115
+ err.code = data.error?.code ?? "RPC_ERROR";
116
+ throw err;
117
+ }
118
+ return data.result;
119
+ }
71
120
  /** Dispatch an action to the server via the FiveM client. */
72
121
  action(name, payload) {
73
122
  fetch("https://warp/action", {
@@ -76,6 +125,24 @@ var WarpBridge = class _WarpBridge {
76
125
  }).catch(() => {
77
126
  });
78
127
  }
128
+ /**
129
+ * Send a message to the FiveM client via NUI callback.
130
+ * The client handles it with `onNUI(name, handler)`.
131
+ *
132
+ * Use this for client-only communication (NUI → client).
133
+ * For server mutations, use `action()` instead.
134
+ *
135
+ * @example
136
+ * bridge.send('chatSend', { message: '/help' });
137
+ * bridge.send('inventoryDrop', { itemId: 'abc', slot: 3 });
138
+ */
139
+ send(name, data) {
140
+ fetch(`https://warp/${name}`, {
141
+ method: "POST",
142
+ body: JSON.stringify(data ?? {})
143
+ }).catch(() => {
144
+ });
145
+ }
79
146
  /** Request the client to close/hide the NUI. */
80
147
  close() {
81
148
  fetch("https://warp/close", {
@@ -100,12 +167,51 @@ var WarpBridge = class _WarpBridge {
100
167
  // -------------------------------------------------------------------------
101
168
  // Internal
102
169
  // -------------------------------------------------------------------------
170
+ handleMessage(type, data) {
171
+ const handlers = this.messageHandlers.get(type);
172
+ if (!handlers) return;
173
+ for (const fn of handlers) {
174
+ try {
175
+ fn(data);
176
+ } catch {
177
+ }
178
+ }
179
+ }
103
180
  handleSync(data) {
104
- for (const [key, value] of Object.entries(data)) {
105
- if (key === "type") continue;
106
- this.state.set(key, value);
107
- const handlers = this.syncHandlers.get(key);
181
+ const deltas = data.deltas;
182
+ if (!deltas || !Array.isArray(deltas)) return;
183
+ const modified = /* @__PURE__ */ new Set();
184
+ const working = /* @__PURE__ */ new Map();
185
+ for (const delta of deltas) {
186
+ const { table, op, key, row, rows } = delta;
187
+ const keyField = key ?? "id";
188
+ if (op === "full") {
189
+ working.set(table, rows ?? []);
190
+ } else {
191
+ if (!working.has(table)) {
192
+ working.set(table, [...this.state.get(table) ?? []]);
193
+ }
194
+ const arr = working.get(table);
195
+ if (op === "insert") {
196
+ arr.push(row);
197
+ } else if (op === "update") {
198
+ const idx = arr.findIndex((r) => r[keyField] === row[keyField]);
199
+ if (idx >= 0) arr[idx] = row;
200
+ else arr.push(row);
201
+ } else if (op === "delete") {
202
+ const idx = arr.findIndex((r) => r[keyField] === row[keyField]);
203
+ if (idx >= 0) arr.splice(idx, 1);
204
+ }
205
+ }
206
+ modified.add(table);
207
+ }
208
+ for (const [table, arr] of working) {
209
+ this.state.set(table, arr);
210
+ }
211
+ for (const table of modified) {
212
+ const handlers = this.syncHandlers.get(table);
108
213
  if (handlers) {
214
+ const value = this.state.get(table);
109
215
  for (const fn of handlers) {
110
216
  try {
111
217
  fn(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warpfx/nui-core",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "description": "Warp Framework — Framework-agnostic NUI bridge for FiveM",
5
5
  "keywords": [
6
6
  "fivem",