@warpfx/server 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +122 -116
- package/dist/index.js +29 -19
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -66,10 +66,20 @@ interface ColumnDef {
|
|
|
66
66
|
/** Default value for this column when inserting a row without it. */
|
|
67
67
|
default?: unknown;
|
|
68
68
|
}
|
|
69
|
+
/** Table access mode for client sync. */
|
|
70
|
+
type AccessMode = "private" | "public";
|
|
69
71
|
/** Table definition in a warp.schema.json. */
|
|
70
72
|
interface TableDef {
|
|
71
73
|
/** Column definitions keyed by column name. */
|
|
72
74
|
columns: Record<string, ColumnDef>;
|
|
75
|
+
/**
|
|
76
|
+
* Client access mode — controls automatic sync to players.
|
|
77
|
+
*
|
|
78
|
+
* - `"private"` — Only the owning player receives their rows (scope = primary key).
|
|
79
|
+
* - `"public"` — All connected players receive all rows.
|
|
80
|
+
* - Omitted — Server-only, not synced to clients.
|
|
81
|
+
*/
|
|
82
|
+
access?: AccessMode;
|
|
73
83
|
}
|
|
74
84
|
/** The full warp.schema.json format. */
|
|
75
85
|
interface WarpSchema {
|
|
@@ -84,22 +94,22 @@ interface WarpSchema {
|
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
/**
|
|
87
|
-
* @
|
|
97
|
+
* @warpfx/server — Server-side SDK for Warp module development.
|
|
88
98
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
99
|
+
* Clean, minimal API for building FiveM server features with Warp.
|
|
100
|
+
* All calls delegate to the Warp core via FiveM resource exports.
|
|
91
101
|
*
|
|
92
102
|
* Usage:
|
|
93
|
-
* import {
|
|
103
|
+
* import { action, fn, command, error, table } from '@warpfx/server';
|
|
104
|
+
* import { accounts } from './db'; // auto-generated from schema
|
|
94
105
|
*
|
|
95
|
-
*
|
|
96
|
-
* const
|
|
97
|
-
*
|
|
98
|
-
* provide('economy', new EconomyService());
|
|
106
|
+
* action('deposit', async (player, { amount }) => {
|
|
107
|
+
* const account = accounts.find(player.citizenId);
|
|
108
|
+
* accounts.update(player.citizenId, { balance: account.balance + amount });
|
|
99
109
|
* });
|
|
100
110
|
*/
|
|
101
|
-
/** Player
|
|
102
|
-
interface
|
|
111
|
+
/** Player context passed to action/function handlers. */
|
|
112
|
+
interface Player {
|
|
103
113
|
/** FiveM server-side player ID. */
|
|
104
114
|
source: number;
|
|
105
115
|
/** Citizen ID (license identifier). */
|
|
@@ -124,63 +134,131 @@ interface PlayerRow {
|
|
|
124
134
|
type PlayerState = "connecting" | "connected" | "spawned";
|
|
125
135
|
/** Player entity with ECS component access. */
|
|
126
136
|
interface PlayerEntity {
|
|
127
|
-
/** FiveM server-side player ID. */
|
|
128
137
|
readonly source: number;
|
|
129
|
-
/** Citizen ID (license identifier). */
|
|
130
138
|
readonly citizenId: string;
|
|
131
|
-
/** Display name. */
|
|
132
139
|
readonly name: string;
|
|
133
|
-
/** Discord identifier (e.g. "discord:123456789"), if linked. */
|
|
134
140
|
readonly discordId: string | undefined;
|
|
135
|
-
/** Steam identifier (e.g. "steam:110000..."), if linked. */
|
|
136
141
|
readonly steamId: string | undefined;
|
|
137
|
-
/** Current lifecycle state. */
|
|
138
142
|
readonly state: PlayerState;
|
|
139
|
-
/** Get a component attached to this player. */
|
|
140
143
|
getComponent<T>(component: string): T | undefined;
|
|
141
|
-
/** Attach or replace a component on this player. */
|
|
142
144
|
setComponent<T>(component: string, data: T): void;
|
|
143
|
-
/** Check if this player has a specific component. */
|
|
144
145
|
hasComponent(component: string): boolean;
|
|
145
|
-
/** Remove a component from this player. */
|
|
146
146
|
removeComponent(component: string): boolean;
|
|
147
147
|
}
|
|
148
148
|
/** Typed event bus for server-side and network communication. */
|
|
149
149
|
interface ServerEventBus<TMap extends Record<string, any> = Record<string, any>> {
|
|
150
|
-
/** Subscribe to a local event. Returns an unsubscribe function. */
|
|
151
150
|
on<K extends string & keyof TMap>(event: K, handler: (data: TMap[K]) => void): () => void;
|
|
152
|
-
/** Unsubscribe from a local event. */
|
|
153
151
|
off<K extends string & keyof TMap>(event: K, handler: (data: TMap[K]) => void): void;
|
|
154
|
-
/** Emit a local event to all subscribers. */
|
|
155
152
|
emit<K extends string & keyof TMap>(event: K, data: TMap[K]): void;
|
|
156
|
-
/** Send an event to a specific client. */
|
|
157
153
|
emitClient<K extends string & keyof TMap>(event: K, target: number, data: TMap[K]): void;
|
|
158
|
-
/** Broadcast an event to all connected clients. */
|
|
159
154
|
emitAllClients<K extends string & keyof TMap>(event: K, data: TMap[K]): void;
|
|
160
|
-
/** Listen for events from clients. Returns an unsubscribe function. */
|
|
161
155
|
onClient<K extends string & keyof TMap>(event: K, handler: (source: number, data: TMap[K]) => void): () => void;
|
|
162
|
-
/** Unsubscribe from a client event. */
|
|
163
156
|
offClient<K extends string & keyof TMap>(event: K, handler: (source: number, data: TMap[K]) => void): void;
|
|
164
157
|
}
|
|
165
158
|
/** ECS-inspired entity component store. */
|
|
166
159
|
interface EntityStore {
|
|
167
|
-
/** Attach a component to an entity. */
|
|
168
160
|
attach<T>(entityId: string, component: string, data: T): void;
|
|
169
|
-
/** Get a component from an entity. Returns undefined if not found. */
|
|
170
161
|
get<T>(entityId: string, component: string): T | undefined;
|
|
171
|
-
/** Remove a component from an entity. */
|
|
172
162
|
detach(entityId: string, component: string): boolean;
|
|
173
|
-
/** Remove all components for an entity. */
|
|
174
163
|
removeEntity(entityId: string): void;
|
|
175
|
-
/** Check if an entity has a specific component. */
|
|
176
164
|
has(entityId: string, component: string): boolean;
|
|
177
|
-
/** Iterate all entities that have a specific component. */
|
|
178
165
|
query<T>(component: string): IterableIterator<[string, T]>;
|
|
179
166
|
}
|
|
167
|
+
/** Table handle for typed database operations. */
|
|
168
|
+
interface Table<T = any> {
|
|
169
|
+
/** Find a row by primary key. */
|
|
170
|
+
find(primaryKey: any): T | null;
|
|
171
|
+
/** Get all rows in the table. */
|
|
172
|
+
all(): T[];
|
|
173
|
+
/** Insert a new row. */
|
|
174
|
+
insert(data: T): Promise<void>;
|
|
175
|
+
/** Update an existing row by primary key (partial update). */
|
|
176
|
+
update(primaryKey: any, data: Partial<T>): Promise<void>;
|
|
177
|
+
/** Delete a row by primary key. */
|
|
178
|
+
delete(primaryKey: any): Promise<void>;
|
|
179
|
+
}
|
|
180
|
+
/** Parameter hint shown in command autocomplete. */
|
|
181
|
+
interface CommandParam {
|
|
182
|
+
name: string;
|
|
183
|
+
help?: string;
|
|
184
|
+
}
|
|
185
|
+
/** Options for command(). */
|
|
186
|
+
interface CommandOptions {
|
|
187
|
+
description?: string;
|
|
188
|
+
ace?: string;
|
|
189
|
+
canUse?: (source: number) => boolean;
|
|
190
|
+
params?: CommandParam[];
|
|
191
|
+
}
|
|
192
|
+
/** Action error received from the server. */
|
|
193
|
+
interface ActionError {
|
|
194
|
+
action: string;
|
|
195
|
+
code: string;
|
|
196
|
+
message: string;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Register an action handler that clients can invoke.
|
|
200
|
+
*
|
|
201
|
+
* Actions are fire-and-forget mutations — the client sends data,
|
|
202
|
+
* the server validates and mutates, state syncs back automatically.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* action('deposit', async (player, { amount }) => {
|
|
206
|
+
* const account = accounts.find(player.citizenId);
|
|
207
|
+
* accounts.update(player.citizenId, { balance: account.balance + amount });
|
|
208
|
+
* });
|
|
209
|
+
*/
|
|
210
|
+
declare function action(name: string, handler: (player: Player, payload: any) => Promise<void> | void): void;
|
|
211
|
+
/**
|
|
212
|
+
* Register a server function that clients can call and await a result.
|
|
213
|
+
*
|
|
214
|
+
* Unlike actions (fire-and-forget), functions return a value to the caller.
|
|
215
|
+
* Use for request/response patterns like data fetches or validations.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* fn('getBalance', async (player, { targetId }) => {
|
|
219
|
+
* const account = accounts.find(targetId);
|
|
220
|
+
* return { balance: account?.balance ?? 0 };
|
|
221
|
+
* });
|
|
222
|
+
*/
|
|
223
|
+
declare function fn<T = any>(name: string, handler: (player: Player, payload: any) => Promise<T> | T): void;
|
|
224
|
+
/**
|
|
225
|
+
* Register a chat command with optional autocomplete.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* command('balance', (player) => {
|
|
229
|
+
* const account = accounts.find(player.citizenId);
|
|
230
|
+
* player.notify(`Balance: $${account.balance}`);
|
|
231
|
+
* });
|
|
232
|
+
*
|
|
233
|
+
* command('give', {
|
|
234
|
+
* description: 'Give money to a player',
|
|
235
|
+
* params: [{ name: 'id' }, { name: 'amount' }],
|
|
236
|
+
* }, (player, args) => { ... });
|
|
237
|
+
*/
|
|
238
|
+
declare function command(name: string, handlerOrOptions: CommandOptions | ((player: Player, args: string[], raw: string) => void), handler?: (player: Player, args: string[], raw: string) => void): void;
|
|
239
|
+
/**
|
|
240
|
+
* Create an error to throw from action/function handlers.
|
|
241
|
+
* The error is sent back to the client with a structured code and message.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* throw error('NO_ACCOUNT', 'No account found');
|
|
245
|
+
*/
|
|
246
|
+
declare function error(code: string, message?: string): Error;
|
|
247
|
+
/**
|
|
248
|
+
* Get a table handle for database operations.
|
|
249
|
+
*
|
|
250
|
+
* The table name must be the full prefixed name (e.g. 'economy_accounts').
|
|
251
|
+
* For auto-generated handles with types, use the generated `db.ts` instead.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* const accounts = table<Account>('economy_accounts');
|
|
255
|
+
* const account = accounts.find(citizenId);
|
|
256
|
+
* await accounts.update(citizenId, { balance: 500 });
|
|
257
|
+
*/
|
|
258
|
+
declare function table<T = any>(name: string): Table<T>;
|
|
180
259
|
/**
|
|
181
260
|
* Register a callback to run when Warp is fully booted.
|
|
182
261
|
* If Warp is already ready, the callback fires immediately.
|
|
183
|
-
* All other SDK functions should be called inside this callback.
|
|
184
262
|
*/
|
|
185
263
|
declare function onReady(cb: () => void): void;
|
|
186
264
|
/** Check if Warp has finished booting. */
|
|
@@ -197,91 +275,19 @@ declare function getEntities(): EntityStore;
|
|
|
197
275
|
declare function getPlayer(citizenId: string): PlayerRow | null;
|
|
198
276
|
/** Get all cached players. */
|
|
199
277
|
declare function getPlayers(): PlayerRow[];
|
|
200
|
-
/**
|
|
201
|
-
* Get the player entity for a citizenId.
|
|
202
|
-
* Returns the entity with lifecycle state and component access.
|
|
203
|
-
*/
|
|
278
|
+
/** Get the player entity for a citizenId. */
|
|
204
279
|
declare function getEntity(citizenId: string): PlayerEntity | null;
|
|
205
|
-
/**
|
|
206
|
-
* Transition a player to the "spawned" state.
|
|
207
|
-
* Call after character selection (e.g. from a multichar module).
|
|
208
|
-
* Fires all onPlayerSpawned hooks.
|
|
209
|
-
*/
|
|
280
|
+
/** Transition a player to the "spawned" state. */
|
|
210
281
|
declare function spawnPlayer(citizenId: string): void;
|
|
211
|
-
/** Register a callback for when a player connects
|
|
212
|
-
declare function onPlayerConnect(cb: (player:
|
|
213
|
-
/** Register a callback for when a player spawns
|
|
214
|
-
declare function onPlayerSpawned(cb: (player:
|
|
282
|
+
/** Register a callback for when a player connects. */
|
|
283
|
+
declare function onPlayerConnect(cb: (player: Player) => void): void;
|
|
284
|
+
/** Register a callback for when a player spawns. */
|
|
285
|
+
declare function onPlayerSpawned(cb: (player: Player) => void): void;
|
|
215
286
|
/** Register a callback for when a player disconnects. */
|
|
216
|
-
declare function onPlayerDisconnect(cb: (player:
|
|
217
|
-
/**
|
|
218
|
-
* Get the database bridge for direct table and reducer access.
|
|
219
|
-
* Advanced — most modules should use the event bus and DI instead.
|
|
220
|
-
*/
|
|
287
|
+
declare function onPlayerDisconnect(cb: (player: Player) => void): void;
|
|
288
|
+
/** Get the database bridge for direct table and reducer access. */
|
|
221
289
|
declare function getBridge(): any;
|
|
222
|
-
/** Subscribe to database tables.
|
|
290
|
+
/** Subscribe to database tables. */
|
|
223
291
|
declare function subscribe(queries: string[]): Promise<void>;
|
|
224
|
-
/** Sync configuration for a table. */
|
|
225
|
-
interface SyncConfig {
|
|
226
|
-
/** Who receives updates for this table. */
|
|
227
|
-
to: "owner" | "all";
|
|
228
|
-
/** Column name for owner filtering (required when to: 'owner'). */
|
|
229
|
-
scope?: string;
|
|
230
|
-
/** Primary key column for row identity (defaults to 'id'). */
|
|
231
|
-
key?: string;
|
|
232
|
-
}
|
|
233
|
-
/** Context passed to action handlers. */
|
|
234
|
-
interface ActionContext {
|
|
235
|
-
/** FiveM server-side player ID. */
|
|
236
|
-
source: number;
|
|
237
|
-
/** Player's citizen ID (license identifier). */
|
|
238
|
-
citizenId: string;
|
|
239
|
-
/** Player's display name. */
|
|
240
|
-
name: string;
|
|
241
|
-
/** Discord identifier (e.g. "discord:123456789"), if linked. */
|
|
242
|
-
discordId?: string;
|
|
243
|
-
/** Steam identifier (e.g. "steam:110000..."), if linked. */
|
|
244
|
-
steamId?: string;
|
|
245
|
-
/** Database table accessors (read-only). */
|
|
246
|
-
db: any;
|
|
247
|
-
/** Database reducer proxy (for mutations). */
|
|
248
|
-
reducers: any;
|
|
249
|
-
}
|
|
250
|
-
/** Action handler function. */
|
|
251
|
-
type ActionHandler = (ctx: ActionContext, payload: any) => Promise<void> | void;
|
|
252
|
-
/**
|
|
253
|
-
* Register a table for automatic sync to clients.
|
|
254
|
-
*
|
|
255
|
-
* The sync engine subscribes to SpacetimeDB table changes and routes
|
|
256
|
-
* updates to the correct players based on the config. Call inside onReady().
|
|
257
|
-
*
|
|
258
|
-
* @example
|
|
259
|
-
* registerSync('inventory', { to: 'owner', scope: 'ownerId', key: 'id' });
|
|
260
|
-
* registerSync('itemDefinitions', { to: 'all', key: 'name' });
|
|
261
|
-
*/
|
|
262
|
-
declare function registerSync(table: string, config: SyncConfig): void;
|
|
263
|
-
/**
|
|
264
|
-
* Register an action handler that clients can invoke via useAction().
|
|
265
|
-
*
|
|
266
|
-
* Actions are the only entry point for client-initiated mutations.
|
|
267
|
-
* The sync engine handles security, error routing, and state updates.
|
|
268
|
-
*
|
|
269
|
-
* @example
|
|
270
|
-
* registerAction('inventory:moveItem', async (ctx, { itemId, toSlot }) => {
|
|
271
|
-
* await ctx.reducers.moveItem(ctx.citizenId, itemId, toSlot);
|
|
272
|
-
* });
|
|
273
|
-
*/
|
|
274
|
-
declare function registerAction(name: string, handler: ActionHandler): void;
|
|
275
|
-
/**
|
|
276
|
-
* Typed error for action handlers. Thrown errors with a `code` property
|
|
277
|
-
* are sent to the client as structured error messages.
|
|
278
|
-
*
|
|
279
|
-
* @example
|
|
280
|
-
* throw new WarpError('ITEM_NOT_FOUND', 'That item no longer exists');
|
|
281
|
-
*/
|
|
282
|
-
declare class WarpError extends Error {
|
|
283
|
-
code: string;
|
|
284
|
-
constructor(code: string, message?: string);
|
|
285
|
-
}
|
|
286
292
|
|
|
287
|
-
export { type
|
|
293
|
+
export { type AccessMode, type ActionError, type ColumnDef, type ColumnType, type CommandOptions, type CommandParam, type EntityStore, type Player, type PlayerEntity, type PlayerRow, type PlayerState, type ServerEventBus, type Table, type TableDef, type WarpSchema, action, command, error, fn, getBridge, getEntities, getEntity, getEvents, getPlayer, getPlayers, isReady, onPlayerConnect, onPlayerDisconnect, onPlayerSpawned, onReady, provide, spawnPlayer, subscribe, table, use };
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
|
|
23
|
+
action: () => action,
|
|
24
|
+
command: () => command,
|
|
25
|
+
error: () => error,
|
|
26
|
+
fn: () => fn,
|
|
24
27
|
getBridge: () => getBridge,
|
|
25
28
|
getEntities: () => getEntities,
|
|
26
29
|
getEntity: () => getEntity,
|
|
@@ -33,16 +36,34 @@ __export(index_exports, {
|
|
|
33
36
|
onPlayerSpawned: () => onPlayerSpawned,
|
|
34
37
|
onReady: () => onReady,
|
|
35
38
|
provide: () => provide,
|
|
36
|
-
registerAction: () => registerAction,
|
|
37
|
-
registerSync: () => registerSync,
|
|
38
39
|
spawnPlayer: () => spawnPlayer,
|
|
39
40
|
subscribe: () => subscribe,
|
|
41
|
+
table: () => table,
|
|
40
42
|
use: () => use
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(index_exports);
|
|
43
45
|
function warp() {
|
|
44
46
|
return globalThis.exports["warp"];
|
|
45
47
|
}
|
|
48
|
+
function action(name, handler) {
|
|
49
|
+
warp().action(name, handler);
|
|
50
|
+
}
|
|
51
|
+
function fn(name, handler) {
|
|
52
|
+
warp().fn(name, handler);
|
|
53
|
+
}
|
|
54
|
+
function command(name, handlerOrOptions, handler) {
|
|
55
|
+
if (typeof handlerOrOptions === "function") {
|
|
56
|
+
warp().command(name, {}, handlerOrOptions);
|
|
57
|
+
} else {
|
|
58
|
+
warp().command(name, handlerOrOptions, handler);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function error(code, message) {
|
|
62
|
+
return warp().error(code, message);
|
|
63
|
+
}
|
|
64
|
+
function table(name) {
|
|
65
|
+
return warp().table(name);
|
|
66
|
+
}
|
|
46
67
|
function onReady(cb) {
|
|
47
68
|
warp().onReady(cb);
|
|
48
69
|
}
|
|
@@ -88,22 +109,12 @@ function getBridge() {
|
|
|
88
109
|
function subscribe(queries) {
|
|
89
110
|
return warp().subscribe(queries);
|
|
90
111
|
}
|
|
91
|
-
function registerSync(table, config) {
|
|
92
|
-
warp().registerSync(table, config);
|
|
93
|
-
}
|
|
94
|
-
function registerAction(name, handler) {
|
|
95
|
-
warp().registerAction(name, handler);
|
|
96
|
-
}
|
|
97
|
-
var WarpError = class extends Error {
|
|
98
|
-
constructor(code, message) {
|
|
99
|
-
super(message ?? code);
|
|
100
|
-
this.code = code;
|
|
101
|
-
this.name = "WarpError";
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
112
|
// Annotate the CommonJS export names for ESM import in node:
|
|
105
113
|
0 && (module.exports = {
|
|
106
|
-
|
|
114
|
+
action,
|
|
115
|
+
command,
|
|
116
|
+
error,
|
|
117
|
+
fn,
|
|
107
118
|
getBridge,
|
|
108
119
|
getEntities,
|
|
109
120
|
getEntity,
|
|
@@ -116,9 +127,8 @@ var WarpError = class extends Error {
|
|
|
116
127
|
onPlayerSpawned,
|
|
117
128
|
onReady,
|
|
118
129
|
provide,
|
|
119
|
-
registerAction,
|
|
120
|
-
registerSync,
|
|
121
130
|
spawnPlayer,
|
|
122
131
|
subscribe,
|
|
132
|
+
table,
|
|
123
133
|
use
|
|
124
134
|
});
|