@rbxts/tether 1.0.8 → 1.0.10

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,6 +1,9 @@
1
1
  # Tether
2
2
  A message-based networking solution for Roblox with automatic binary serialization.
3
3
 
4
+ > [!CAUTION]
5
+ > Depends on `rbxts-transformer-flamework`!
6
+
4
7
  ## Usage
5
8
 
6
9
  ### In `shared/messaging.ts`
@@ -29,7 +32,7 @@ export interface MessageData {
29
32
  ```ts
30
33
  import { Message, messaging } from "shared/messaging";
31
34
 
32
- messaging.onServerMessage(Message.TEST, (player, data) => {
35
+ messaging.onServerMessage(Message.Test, (player, data) => {
33
36
  print(player, "sent data:", data);
34
37
  });
35
38
  ```
@@ -38,27 +41,49 @@ messaging.onServerMessage(Message.TEST, (player, data) => {
38
41
  ```ts
39
42
  import { Message, messaging } from "shared/messaging";
40
43
 
41
- messaging.emitServer(Message.TEST, {
44
+ messaging.emitServer(Message.Test, {
42
45
  foo: "bar",
43
46
  n: 69
44
47
  });
45
48
  ```
46
49
 
47
50
  ## Middleware
51
+ Drop or delay requests
48
52
 
49
53
  ### Creating middleware
54
+
55
+ #### Client, Global
56
+ ```ts
57
+ import type { ClientGlobalMiddleware } from "@rbxts/tether";
58
+
59
+ export function logClient(): ClientGlobalMiddleware {
60
+ return (player, data) => print(`[LOG]: Sent message to player ${player} with data:`, data);
61
+ }
62
+ ```
63
+
64
+ #### Server, Global
50
65
  ```ts
51
- import { type Middleware, DropRequest } from "@rbxts/tether";
66
+ import type { ServerGlobalMiddleware } from "@rbxts/tether";
67
+
68
+ export function logServer(): ServerGlobalMiddleware {
69
+ return data => print(`[LOG]: Sent message to server with data:`, data);
70
+ }
71
+ ```
52
72
 
53
- export function rateLimit(interval: number): Middleware {
73
+ #### Shared
74
+ ```ts
75
+ import { type SharedMiddleware, DropRequest } from "@rbxts/tether";
76
+
77
+ export function rateLimit(interval: number): SharedMiddleware<MessageData> {
54
78
  let lastRequest = 0;
55
79
 
56
- return () => {
57
- if (os.clock() - lastRequest < interval)
58
- return DropRequest;
80
+ return message => // message attempting to be sent
81
+ () => { // no data/player - it's a shared middleware
82
+ if (os.clock() - lastRequest < interval)
83
+ return DropRequest;
59
84
 
60
- lastRequest = os.clock();
61
- };
85
+ lastRequest = os.clock();
86
+ };
62
87
  }
63
88
  ```
64
89
 
@@ -72,11 +97,11 @@ messaging.middleware
72
97
  // only allows requests to the server every 5 seconds,
73
98
  // drops any requests that occur within 5 seconds of each other
74
99
  .useServer(Message.Test, [BuiltinMiddlewares.rateLimit(5)])
75
- // automatically validates that data sent through the remote
76
- // matches the data associated with the message at runtime
100
+ // automatically validates that the data sent through the remote matches
101
+ // the data associated with the message at runtime using type guards
77
102
  .useShared(Message.Test, [BuiltinMiddlewares.validateClient()])
78
- // rate limit every server remote (global)
79
- .useServerGlobal([BuiltinMiddlewares.rateLimit(1)]);
103
+ .useClientGlobal([logClient()]);
104
+ .useServerGlobal([logServer()]);
80
105
 
81
106
  export const enum Message {
82
107
  Test
@@ -3,27 +3,30 @@ import { type SharedMiddleware, type ServerMiddleware, type ClientMiddleware } f
3
3
  type Guard<T> = (value: unknown) => value is T;
4
4
  export declare namespace BuiltinMiddlewares {
5
5
  /**
6
- * Creates a universal middleware that will drop any message that occurs within the given interval of the previous message.
7
- * @param interval The interval in seconds that the middleware should wait before allowing a new request.
8
- * @returns A middleware that will drop any message that occurs within the given interval.
6
+ * Creates a shared per-message middleware that will drop any message that occurs within the given interval of the previous message
7
+ *
8
+ * @param interval The interval in seconds that the middleware should wait before allowing a new request
9
+ * @returns A middleware that will drop any message that occurs within the given interval
9
10
  */
10
11
  function rateLimit(interval: number): SharedMiddleware;
11
12
  /**
12
- * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided).
13
- * If the guard fails, the middleware will drop the message.
13
+ * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided)
14
14
  *
15
- * @param guard The guard to use to validate the data.
16
- * @returns A server middleware that validates the data with the given guard.
17
- * @macro
15
+ * If the guard fails, the middleware will drop the message
16
+ *
17
+ * @param guard The guard to use to validate the data
18
+ * @returns A server middleware that validates the data with the given guard
19
+ * @metadata macro
18
20
  */
19
21
  function validateServer<T>(guard?: Guard<T> | Modding.Generic<T, "guard">): ServerMiddleware<T>;
20
22
  /**
21
- * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided).
22
- * If the guard fails, the middleware will drop the message.
23
+ * Creates a client middleware that validates the data with the given guard (or a generated guard if none was provided)
24
+ *
25
+ * If the guard fails, the middleware will drop the message
23
26
  *
24
27
  * @param guard The guard to use to validate the data.
25
- * @returns A server middleware that validates the data with the given guard.
26
- * @macro
28
+ * @returns A client middleware that validates the data with the given guard.
29
+ * @metadata macro
27
30
  */
28
31
  function validateClient<T>(guard?: Guard<T> | Modding.Generic<T, "guard">): ClientMiddleware<T>;
29
32
  }
@@ -1,38 +1,47 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
2
  local TS = _G[script]
3
3
  local DropRequest = TS.import(script, script.Parent, "middleware").DropRequest
4
- local noOp = function() end
4
+ local noOp = function()
5
+ return function() end
6
+ end
5
7
  local validationGuardGenerationFailed = function(context)
6
8
  return `[@rbxts/tether]: Failed to generate guard for validate{context}<T> builtin middleware - skipping validation`
7
9
  end
10
+ local guardFailed = function(message)
11
+ return `[@rbxts/tether]: Type validation failed for message '{tostring(message)}' - check your sent data`
12
+ end
8
13
  local BuiltinMiddlewares = {}
9
14
  do
10
15
  local _container = BuiltinMiddlewares
11
16
  --[[
12
17
  *
13
- * Creates a universal middleware that will drop any message that occurs within the given interval of the previous message.
14
- * @param interval The interval in seconds that the middleware should wait before allowing a new request.
15
- * @returns A middleware that will drop any message that occurs within the given interval.
18
+ * Creates a shared per-message middleware that will drop any message that occurs within the given interval of the previous message
19
+ *
20
+ * @param interval The interval in seconds that the middleware should wait before allowing a new request
21
+ * @returns A middleware that will drop any message that occurs within the given interval
16
22
 
17
23
  ]]
18
24
  local function rateLimit(interval)
19
25
  local lastRequest = 0
20
26
  return function()
21
- if os.clock() - lastRequest < interval then
22
- return DropRequest
27
+ return function()
28
+ if os.clock() - lastRequest < interval then
29
+ return DropRequest
30
+ end
31
+ lastRequest = os.clock()
23
32
  end
24
- lastRequest = os.clock()
25
33
  end
26
34
  end
27
35
  _container.rateLimit = rateLimit
28
36
  --[[
29
37
  *
30
- * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided).
31
- * If the guard fails, the middleware will drop the message.
38
+ * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided)
32
39
  *
33
- * @param guard The guard to use to validate the data.
34
- * @returns A server middleware that validates the data with the given guard.
35
- * @macro
40
+ * If the guard fails, the middleware will drop the message
41
+ *
42
+ * @param guard The guard to use to validate the data
43
+ * @returns A server middleware that validates the data with the given guard
44
+ * @metadata macro
36
45
 
37
46
  ]]
38
47
  local function validateServer(guard)
@@ -40,22 +49,26 @@ do
40
49
  warn(validationGuardGenerationFailed("Server"))
41
50
  return noOp
42
51
  end
43
- return function(data)
44
- if guard(data) then
45
- return nil
52
+ return function(message)
53
+ return function(data)
54
+ if guard(data) then
55
+ return nil
56
+ end
57
+ warn(guardFailed(message))
58
+ return DropRequest
46
59
  end
47
- return DropRequest
48
60
  end
49
61
  end
50
62
  _container.validateServer = validateServer
51
63
  --[[
52
64
  *
53
- * Creates a server middleware that validates the data with the given guard (or a generated guard if none was provided).
54
- * If the guard fails, the middleware will drop the message.
65
+ * Creates a client middleware that validates the data with the given guard (or a generated guard if none was provided)
66
+ *
67
+ * If the guard fails, the middleware will drop the message
55
68
  *
56
69
  * @param guard The guard to use to validate the data.
57
- * @returns A server middleware that validates the data with the given guard.
58
- * @macro
70
+ * @returns A client middleware that validates the data with the given guard.
71
+ * @metadata macro
59
72
 
60
73
  ]]
61
74
  local function validateClient(guard)
@@ -63,11 +76,14 @@ do
63
76
  warn(validationGuardGenerationFailed("Client"))
64
77
  return noOp
65
78
  end
66
- return function(data)
67
- if guard(data) then
68
- return nil
79
+ return function(message)
80
+ return function(player, data)
81
+ if guard(data) then
82
+ return nil
83
+ end
84
+ warn(guardFailed(message))
85
+ return DropRequest
69
86
  end
70
- return DropRequest
71
87
  end
72
88
  end
73
89
  _container.validateClient = validateClient
package/out/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { MessageEmitter } from "./message-emitter";
2
2
  export { BuiltinMiddlewares } from "./builtin-middlewares";
3
- export { DropRequest, type ClientMiddleware, type ServerMiddleware, type Middleware } from "./middleware";
3
+ export { DropRequest, type ClientMiddleware, type ServerMiddleware, type SharedMiddleware, type Middleware, type ClientGlobalMiddleware, type ServerGlobalMiddleware, type SharedGlobalMiddleware, type GlobalMiddleware } from "./middleware";
@@ -16,37 +16,37 @@ export declare class MessageEmitter<MessageData> extends Destroyable {
16
16
  }>): MessageEmitter<MessageData>;
17
17
  private constructor();
18
18
  /**.
19
- * @returns A destructor function that disconnects the callback from the message.
19
+ * @returns A destructor function that disconnects the callback from the message
20
20
  */
21
21
  onServerMessage<Kind extends keyof MessageData>(message: Kind, callback: ServerMessageCallback<MessageData[Kind]>): () => void;
22
22
  /**.
23
- * @returns A destructor function that disconnects the callback from the message.
23
+ * @returns A destructor function that disconnects the callback from the message
24
24
  */
25
25
  onClientMessage<Kind extends keyof MessageData>(message: Kind, callback: ClientMessageCallback<MessageData[Kind]>): () => void;
26
26
  private on;
27
27
  /**
28
- * Emits a message to all connected clients.
28
+ * Emits a message to all connected clients
29
29
  *
30
- * @param message - The message kind to be sent.
31
- * @param data - The data associated with the message.
32
- * @param unreliable - Optional flag indicating if the message should be sent unreliably.
30
+ * @param message The message kind to be sent
31
+ * @param data The data associated with the message
32
+ * @param unreliable Whether the message should be sent unreliably
33
33
  */
34
34
  emitServer<Kind extends keyof MessageData>(message: Kind, data?: MessageData[Kind], unreliable?: boolean): void;
35
35
  /**
36
- * Emits a message to a specific client.
36
+ * Emits a message to a specific client
37
37
  *
38
- * @param player - The player to whom the message is sent.
39
- * @param message - The message kind to be sent.
40
- * @param data - The data associated with the message.
41
- * @param unreliable - Optional flag indicating if the message should be sent unreliably.
38
+ * @param player The player to whom the message is sent
39
+ * @param message The message kind to be sent
40
+ * @param data The data associated with the message
41
+ * @param unreliable Whether the message should be sent unreliably
42
42
  */
43
43
  emitClient<Kind extends keyof MessageData>(player: Player, message: Kind, data?: MessageData[Kind], unreliable?: boolean): void;
44
44
  /**
45
- * Emits a message to all connected clients.
45
+ * Emits a message to all connected clients
46
46
  *
47
- * @param message - The message kind to be sent.
48
- * @param data - The data associated with the message.
49
- * @param unreliable - Optional flag indicating if the message should be sent unreliably.
47
+ * @param message The message kind to be sent
48
+ * @param data The data associated with the message
49
+ * @param unreliable Whether the message should be sent unreliably
50
50
  */
51
51
  emitAllClients<Kind extends keyof MessageData>(message: Kind, data?: MessageData[Kind], unreliable?: boolean): void;
52
52
  private initialize;
@@ -132,14 +132,14 @@ do
132
132
  if unreliable == nil then
133
133
  unreliable = false
134
134
  end
135
- for _, middleware in self.middleware:getServerGlobal() do
136
- local result = middleware(data)
135
+ for _, globalMiddleware in self.middleware:getServerGlobal() do
136
+ local result = globalMiddleware(data)
137
137
  if result == DropRequest then
138
138
  return nil
139
139
  end
140
140
  end
141
141
  for _, middleware in self.middleware:getServer(message) do
142
- local result = middleware(data)
142
+ local result = middleware(message)(data)
143
143
  if result == DropRequest then
144
144
  return nil
145
145
  end
@@ -151,14 +151,14 @@ do
151
151
  if unreliable == nil then
152
152
  unreliable = false
153
153
  end
154
- for _, middleware in self.middleware:getClientGlobal() do
155
- local result = middleware(player, data)
154
+ for _, globalMiddleware in self.middleware:getClientGlobal() do
155
+ local result = globalMiddleware(player, data)
156
156
  if result == DropRequest then
157
157
  return nil
158
158
  end
159
159
  end
160
160
  for _, middleware in self.middleware:getClient(message) do
161
- local result = middleware(player, data)
161
+ local result = middleware(message)(player, data)
162
162
  if result == DropRequest then
163
163
  return nil
164
164
  end
@@ -170,9 +170,9 @@ do
170
170
  if unreliable == nil then
171
171
  unreliable = false
172
172
  end
173
- for _, middleware in self.middleware:getClientGlobal() do
173
+ for _, globalMiddleware in self.middleware:getClientGlobal() do
174
174
  for _1, player in Players:GetPlayers() do
175
- local result = middleware(player, data)
175
+ local result = globalMiddleware(player, data)
176
176
  if result == DropRequest then
177
177
  return nil
178
178
  end
@@ -180,7 +180,7 @@ do
180
180
  end
181
181
  for _, middleware in self.middleware:getClient(message) do
182
182
  for _1, player in Players:GetPlayers() do
183
- local result = middleware(player, data)
183
+ local result = middleware(message)(player, data)
184
184
  if result == DropRequest then
185
185
  return nil
186
186
  end
@@ -1,11 +1,16 @@
1
+ import type { BaseMessage } from "./structs";
1
2
  type DropRequestSymbol = symbol & {
2
3
  _skip_middleware?: undefined;
3
4
  };
4
5
  export declare const DropRequest: DropRequestSymbol;
5
- export type ClientMiddleware<Data = unknown> = (player: Player, data: Readonly<Data> | undefined) => DropRequestSymbol | void;
6
- export type ServerMiddleware<Data = unknown> = (data: Readonly<Data> | undefined) => DropRequestSymbol | void;
7
- export type SharedMiddleware = () => DropRequestSymbol | void;
6
+ export type ClientMiddleware<Data = unknown> = (message: BaseMessage) => (player: Player, data: Readonly<Data> | undefined) => DropRequestSymbol | void;
7
+ export type ServerMiddleware<Data = unknown> = (message: BaseMessage) => (data: Readonly<Data> | undefined) => DropRequestSymbol | void;
8
+ export type SharedMiddleware = (message: BaseMessage) => () => DropRequestSymbol | void;
8
9
  export type Middleware<Data = unknown> = ServerMiddleware<Data> & ClientMiddleware<Data>;
10
+ export type ClientGlobalMiddleware<Data = unknown> = (player: Player, data: Readonly<Data> | undefined) => DropRequestSymbol | void;
11
+ export type ServerGlobalMiddleware<Data = unknown> = (data: Readonly<Data> | undefined) => DropRequestSymbol | void;
12
+ export type SharedGlobalMiddleware = () => DropRequestSymbol | void;
13
+ export type GlobalMiddleware<Data = unknown> = ServerGlobalMiddleware<Data> & ClientGlobalMiddleware<Data>;
9
14
  export declare class MiddlewareProvider<MessageData> {
10
15
  private readonly clientGlobalMiddlewares;
11
16
  private readonly serverGlobalMiddlewares;
@@ -16,14 +21,14 @@ export declare class MiddlewareProvider<MessageData> {
16
21
  /** @hidden */
17
22
  getServer<Kind extends keyof MessageData>(message: Kind): ServerMiddleware<MessageData[Kind]>[];
18
23
  /** @hidden */
19
- getClientGlobal<Data>(): ClientMiddleware<Data>[];
24
+ getClientGlobal<Data>(): ClientGlobalMiddleware<Data>[];
20
25
  /** @hidden */
21
- getServerGlobal<Data>(): ServerMiddleware<Data>[];
26
+ getServerGlobal<Data>(): ServerGlobalMiddleware<Data>[];
22
27
  useClient<Kind extends keyof MessageData>(message: Kind, middlewares: ClientMiddleware<MessageData[Kind]> | readonly ClientMiddleware<MessageData[Kind]>[], order?: number): this;
23
28
  useServer<Kind extends keyof MessageData>(message: Kind, middlewares: ServerMiddleware<MessageData[Kind]> | readonly ServerMiddleware<MessageData[Kind]>[], order?: number): this;
24
29
  useShared<Kind extends keyof MessageData>(message: Kind, middlewares: SharedMiddleware | readonly SharedMiddleware[], order?: number): this;
25
- useClientGlobal<Data>(middlewares: ClientMiddleware<Data> | readonly ClientMiddleware<Data>[], order?: number): this;
26
- useServerGlobal<Data>(middlewares: ServerMiddleware<Data> | readonly ServerMiddleware<Data>[], order?: number): this;
27
- useSharedGlobal(middlewares: SharedMiddleware | readonly SharedMiddleware[], order?: number): this;
30
+ useClientGlobal<Data>(middlewares: ClientGlobalMiddleware<Data> | readonly ClientGlobalMiddleware<Data>[], order?: number): this;
31
+ useServerGlobal<Data>(middlewares: ServerGlobalMiddleware<Data> | readonly ServerGlobalMiddleware<Data>[], order?: number): this;
32
+ useSharedGlobal(middlewares: SharedGlobalMiddleware | readonly SharedGlobalMiddleware[], order?: number): this;
28
33
  }
29
34
  export {};
@@ -78,7 +78,7 @@ do
78
78
  function MiddlewareProvider:useClientGlobal(middlewares, order)
79
79
  local globalMiddleware = self:getClientGlobal()
80
80
  local _middlewares = middlewares
81
- if typeof(_middlewares) == "function" then
81
+ if type(_middlewares) == "function" then
82
82
  local _condition = order
83
83
  if _condition == nil then
84
84
  _condition = #globalMiddleware - 1
@@ -95,7 +95,7 @@ do
95
95
  function MiddlewareProvider:useServerGlobal(middlewares, order)
96
96
  local globalMiddleware = self:getServerGlobal()
97
97
  local _middlewares = middlewares
98
- if typeof(_middlewares) == "function" then
98
+ if type(_middlewares) == "function" then
99
99
  local _condition = order
100
100
  if _condition == nil then
101
101
  _condition = #globalMiddleware - 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rbxts/tether",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "main": "out/init.lua",
5
5
  "scripts": {
6
6
  "build": "rbxtsc",
@@ -32,13 +32,13 @@
32
32
  "devDependencies": {
33
33
  "@rbxts/compiler-types": "^3.0.0-types.0",
34
34
  "@rbxts/types": "^1.0.835",
35
- "rbxts-transformer-flamework": "latest",
35
+ "rbxts-transformer-flamework": "^1.2.4",
36
36
  "roblox-ts": "^3.0.0",
37
37
  "typescript": "^5.5.3"
38
38
  },
39
39
  "dependencies": {
40
- "@flamework/core": "^1.2.3",
41
- "@flamework/networking": "^1.2.3",
40
+ "@flamework/core": "^1.2.4",
41
+ "@flamework/networking": "^1.2.4",
42
42
  "@rbxts/destroyable": "^1.0.1",
43
43
  "@rbxts/flamework-binary-serializer": "^0.6.0",
44
44
  "@rbxts/services": "^1.5.5"