@rbxts/tether 1.0.7 → 1.0.9

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
@@ -29,7 +29,7 @@ export interface MessageData {
29
29
  ```ts
30
30
  import { Message, messaging } from "shared/messaging";
31
31
 
32
- messaging.onServerMessage(Message.TEST, (player, data) => {
32
+ messaging.onServerMessage(Message.Test, (player, data) => {
33
33
  print(player, "sent data:", data);
34
34
  });
35
35
  ```
@@ -38,27 +38,53 @@ messaging.onServerMessage(Message.TEST, (player, data) => {
38
38
  ```ts
39
39
  import { Message, messaging } from "shared/messaging";
40
40
 
41
- messaging.emitServer(Message.TEST, {
41
+ messaging.emitServer(Message.Test, {
42
42
  foo: "bar",
43
43
  n: 69
44
44
  });
45
45
  ```
46
46
 
47
47
  ## Middleware
48
+ Drop or delay requests
48
49
 
49
50
  ### Creating middleware
51
+
52
+ #### Client
53
+ ```ts
54
+ import { type ClientMiddleware, DropRequest } from "@rbxts/tether";
55
+
56
+ export function logClient(): ClientMiddleware {
57
+ return message =>
58
+ (player, data) =>
59
+ print(`[LOG]: Message ${message} sent to player ${player} with data ${data}`);
60
+ }
61
+ ```
62
+
63
+ #### Server
50
64
  ```ts
51
- import { type Middleware, DropRequest } from "@rbxts/tether";
65
+ import { type ServerMiddleware, DropRequest } from "@rbxts/tether";
52
66
 
53
- export function rateLimit(interval: number): Middleware {
67
+ export function logServer(): ServerMiddleware {
68
+ return message =>
69
+ data =>
70
+ print(`[LOG]: Message ${message} sent to server with data ${data}`);
71
+ }
72
+ ```
73
+
74
+ #### Shared
75
+ ```ts
76
+ import { type SharedMiddleware, DropRequest } from "@rbxts/tether";
77
+
78
+ export function rateLimit(interval: number): SharedMiddleware<MessageData> {
54
79
  let lastRequest = 0;
55
80
 
56
- return () => {
57
- if (os.clock() - lastRequest < interval)
58
- return DropRequest;
81
+ return message =>
82
+ () => {
83
+ if (os.clock() - lastRequest < interval)
84
+ return DropRequest;
59
85
 
60
- lastRequest = os.clock();
61
- };
86
+ lastRequest = os.clock();
87
+ };
62
88
  }
63
89
  ```
64
90
 
@@ -72,11 +98,11 @@ messaging.middleware
72
98
  // only allows requests to the server every 5 seconds,
73
99
  // drops any requests that occur within 5 seconds of each other
74
100
  .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
101
+ // automatically validates that the data sent through the remote matches
102
+ // the data associated with the message at runtime using type guards
77
103
  .useShared(Message.Test, [BuiltinMiddlewares.validateClient()])
78
- // rate limit every server remote (global)
79
- .useServerGlobal([BuiltinMiddlewares.rateLimit(1)]);
104
+ .useClientGlobal([BuiltinMiddlewares.logClient()]);
105
+ .useServerGlobal([BuiltinMiddlewares.logServer()]);
80
106
 
81
107
  export const enum Message {
82
108
  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,12 +132,16 @@ do
132
132
  if unreliable == nil then
133
133
  unreliable = false
134
134
  end
135
- if self.middleware ~= nil then
136
- for _, middleware in self.middleware:getServer(message) do
137
- local result = middleware(data)
138
- if result == DropRequest then
139
- return nil
140
- end
135
+ for _, globalMiddleware in self.middleware:getServerGlobal() do
136
+ local result = globalMiddleware(data)
137
+ if result == DropRequest then
138
+ return nil
139
+ end
140
+ end
141
+ for _, middleware in self.middleware:getServer(message) do
142
+ local result = middleware(message)(data)
143
+ if result == DropRequest then
144
+ return nil
141
145
  end
142
146
  end
143
147
  local send = if unreliable then self.clientEvents.sendUnreliableServerMessage else self.clientEvents.sendServerMessage
@@ -147,12 +151,16 @@ do
147
151
  if unreliable == nil then
148
152
  unreliable = false
149
153
  end
150
- if self.middleware ~= nil then
151
- for _, middleware in self.middleware:getClient(message) do
152
- local result = middleware(player, data)
153
- if result == DropRequest then
154
- return nil
155
- end
154
+ for _, globalMiddleware in self.middleware:getClientGlobal() do
155
+ local result = globalMiddleware(player, data)
156
+ if result == DropRequest then
157
+ return nil
158
+ end
159
+ end
160
+ for _, middleware in self.middleware:getClient(message) do
161
+ local result = middleware(message)(player, data)
162
+ if result == DropRequest then
163
+ return nil
156
164
  end
157
165
  end
158
166
  local send = if unreliable then self.serverEvents.sendUnreliableClientMessage else self.serverEvents.sendClientMessage
@@ -162,13 +170,19 @@ do
162
170
  if unreliable == nil then
163
171
  unreliable = false
164
172
  end
165
- if self.middleware ~= nil then
166
- for _, middleware in self.middleware:getClient(message) do
167
- for _1, player in Players:GetPlayers() do
168
- local result = middleware(player, data)
169
- if result == DropRequest then
170
- return nil
171
- end
173
+ for _, globalMiddleware in self.middleware:getClientGlobal() do
174
+ for _1, player in Players:GetPlayers() do
175
+ local result = globalMiddleware(player, data)
176
+ if result == DropRequest then
177
+ return nil
178
+ end
179
+ end
180
+ end
181
+ for _, middleware in self.middleware:getClient(message) do
182
+ for _1, player in Players:GetPlayers() do
183
+ local result = middleware(message)(player, data)
184
+ if result == DropRequest then
185
+ return nil
172
186
  end
173
187
  end
174
188
  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.7",
3
+ "version": "1.0.9",
4
4
  "main": "out/init.lua",
5
5
  "scripts": {
6
6
  "build": "rbxtsc",
@@ -11,7 +11,9 @@
11
11
  "roblox",
12
12
  "tether",
13
13
  "networking",
14
- "message"
14
+ "message",
15
+ "serialization",
16
+ "middleware"
15
17
  ],
16
18
  "repository": {
17
19
  "url": "git+https://github.com/R-unic/tether.git"