@rbxts/replion 1.0.16 → 1.0.18

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
@@ -2,4 +2,4 @@
2
2
 
3
3
  ## Install
4
4
  Install with [wally](https://wally.run/):\
5
- `Replion = "shouxtech/replion@1.0.16"`
5
+ `Replion = "shouxtech/replion@1.0.18"`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rbxts/replion",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "",
5
5
  "main": "src/init.lua",
6
6
  "scripts": {},
@@ -20,9 +20,5 @@
20
20
  "@rbxts/types": "^1.0.896",
21
21
  "roblox-ts": "^3.0.0",
22
22
  "typescript": "^5.9.3"
23
- },
24
- "dependencies": {
25
- "@rbxts/sift": "^0.0.11",
26
- "@rbxts/sleitnick-signal": "^1.0.8"
27
23
  }
28
24
  }
@@ -0,0 +1,56 @@
1
+ --!strict
2
+ export type GenericDataTable = { [string]: any };
3
+
4
+ local isTypeScriptEnv = script.Parent.Name == 'src';
5
+ local dependencies = if isTypeScriptEnv then script.Parent.Parent.Parent else script.Parent.Parent;
6
+ local Shared = require(script.Parent.Shared);
7
+ local Signal = require(isTypeScriptEnv and dependencies['sleitnick-signal'] or dependencies.Signal);
8
+
9
+ local BaseReplion = {};
10
+ BaseReplion.__index = BaseReplion;
11
+
12
+ function BaseReplion.new(data: GenericDataTable)
13
+ local self = setmetatable({}, BaseReplion);
14
+
15
+ self.data = data;
16
+
17
+ self._signals = {} :: { [string]: any };
18
+ self._allSignal = Signal.new();
19
+
20
+ return self;
21
+ end;
22
+
23
+ function BaseReplion:get(path: { string }?)
24
+ if not path then return self.data; end;
25
+ return Shared.getNestedValue(self.data, path);
26
+ end;
27
+
28
+ function BaseReplion:observe(path: { string }?, callback: (any, any) -> ())
29
+ if not path then
30
+ task.spawn(callback, self.data, nil);
31
+ return self._allSignal:Connect(callback);
32
+ end;
33
+
34
+ local signalKey = Shared.getSignalKey(path);
35
+ if not self._signals[signalKey] then
36
+ self._signals[signalKey] = Signal.new();
37
+ end;
38
+
39
+ local initialValue = Shared.getNestedValue(self.data, path);
40
+
41
+ task.spawn(callback, initialValue, nil);
42
+ local connection = self._signals[signalKey]:Connect(callback);
43
+ return function()
44
+ connection:Disconnect();
45
+ end;
46
+ end;
47
+
48
+ function BaseReplion:destroy()
49
+ self._allSignal:Destroy();
50
+ for _, signal in self._signals do
51
+ signal:Destroy();
52
+ end;
53
+ self._signals = {};
54
+ end;
55
+
56
+ return BaseReplion;
package/src/Client.lua CHANGED
@@ -1,6 +1,7 @@
1
1
  --!strict
2
2
  local isTypeScriptEnv = script.Parent.Name == 'src';
3
3
  local dependencies = if isTypeScriptEnv then script.Parent.Parent.Parent else script.Parent.Parent;
4
+ local BaseReplion = require(script.Parent.BaseReplion)
4
5
  local Shared = require(script.Parent.Shared);
5
6
  local Signal = require(isTypeScriptEnv and dependencies['sleitnick-signal'] or dependencies.Signal);
6
7
 
@@ -52,33 +53,18 @@ function Client.new(channel: string, data: Shared.GenericDataTable)
52
53
  local self = setmetatable({}, Client);
53
54
 
54
55
  self.channel = channel;
55
- self.data = data;
56
- self._signals = {} :: { [string]: any };
57
- self._allSignal = Signal.new();
56
+
57
+ self._base = BaseReplion.new(data);
58
58
 
59
59
  return self;
60
60
  end;
61
61
 
62
62
  function Client:get(path: { string }?)
63
- if not path then return self.data; end;
64
- return Shared.getNestedValue(self.data, path);
63
+ return self._base:get(path);
65
64
  end;
66
65
 
67
66
  function Client:observe(path: { string }?, callback: Observer)
68
- if not path then
69
- task.spawn(callback, self.data, nil);
70
- return self._allSignal:Connect(callback);
71
- end;
72
-
73
- local signalKey = Shared.getSignalKey(path);
74
- if not self._signals[signalKey] then
75
- self._signals[signalKey] = Signal.new();
76
- end;
77
-
78
- local initialValue = Shared.getNestedValue(self.data, path);
79
-
80
- task.spawn(callback, initialValue, nil);
81
- return self._signals[signalKey]:Connect(callback);
67
+ return self._base:observe(path, callback);
82
68
  end;
83
69
 
84
70
  function Client:_collectDeletions(target: any, updates: any, path: { string }, out: { Deletion })
@@ -98,12 +84,14 @@ function Client:_collectDeletions(target: any, updates: any, path: { string }, o
98
84
  end;
99
85
 
100
86
  function Client:_notifyDeepDeletion(path: { string }, oldTable: any)
87
+ local signals = self._base._signals;
88
+
101
89
  for k, v in oldTable do
102
90
  local nextPath = table.clone(path);
103
91
  table.insert(nextPath, k);
104
92
 
105
93
  local signalKey = Shared.getSignalKey(nextPath);
106
- local signal = self._signals[signalKey];
94
+ local signal = signals[signalKey];
107
95
  if signal then
108
96
  signal:Fire(nil, v);
109
97
  end;
@@ -115,15 +103,17 @@ function Client:_notifyDeepDeletion(path: { string }, oldTable: any)
115
103
  end;
116
104
 
117
105
  function Client:_notifyRecursiveUpdates(currentPath: { string }, updateTree: any)
106
+ local signals = self._base._signals;
107
+
118
108
  for k, v in updateTree do
119
109
  local nextPath = table.clone(currentPath);
120
110
  table.insert(nextPath, k);
121
111
 
122
112
  local signalKey = Shared.getSignalKey(nextPath);
123
- local signal = self._signals[signalKey];
113
+ local signal = signals[signalKey];
124
114
 
125
115
  if signal then
126
- local newValue = Shared.getNestedValue(self.data, nextPath);
116
+ local newValue = Shared.getNestedValue(self._base.data, nextPath);
127
117
  signal:Fire(newValue, newValue); -- Warning: Sends improper 'old' argument due to shallow copy optimization.
128
118
  end;
129
119
 
@@ -135,11 +125,11 @@ end;
135
125
 
136
126
  function Client:_applyUpdates(updates: Shared.GenericDataTable)
137
127
  local deletions: { Deletion } = {};
138
- self:_collectDeletions(self.data, updates, {}, deletions);
128
+ self:_collectDeletions(self._base.data, updates, {}, deletions);
139
129
 
140
- local oldData = table.clone(self.data); -- Note: Shallow copy instead of deep copy.
130
+ local oldData = table.clone(self._base.data); -- Note: Shallow copy instead of deep copy.
141
131
 
142
- deepMergeAndApply(self.data, updates);
132
+ deepMergeAndApply(self._base.data, updates);
143
133
 
144
134
  local anyChanged = false;
145
135
 
@@ -153,31 +143,27 @@ function Client:_applyUpdates(updates: Shared.GenericDataTable)
153
143
  -- If it's a table, we assume it changed if it's in the updates list.
154
144
  if typeof(newValue) == 'table' then
155
145
  anyChanged = true;
156
- local signal = self._signals[key];
146
+ local signal = self._base._signals[key];
157
147
  if signal then
158
- signal:Fire(self.data[key], self.data[key]); -- Warning: Sends improper 'old' argument due to shallow copy optimization.
148
+ signal:Fire(self._base.data[key], self._base.data[key]); -- Warning: Sends improper 'old' argument due to shallow copy optimization.
159
149
  end;
160
150
  self:_notifyRecursiveUpdates({key}, newValue);
161
- elseif self.data[key] ~= oldValue then
151
+ elseif self._base.data[key] ~= oldValue then
162
152
  anyChanged = true;
163
- local signal = self._signals[key];
153
+ local signal = self._base._signals[key];
164
154
  if signal then
165
- signal:Fire(self.data[key], oldValue);
155
+ signal:Fire(self._base.data[key], oldValue);
166
156
  end;
167
157
  end;
168
158
  end;
169
159
 
170
160
  if anyChanged then
171
- self._allSignal:Fire(self.data, updates);
161
+ self._base._allSignal:Fire(self._base.data, updates);
172
162
  end;
173
163
  end;
174
164
 
175
165
  function Client:destroy()
176
- self._allSignal:Destroy();
177
- for _, signal in self._signals do
178
- signal:Destroy();
179
- end;
180
- self._signals = {};
166
+ self._base:destroy();
181
167
  end;
182
168
 
183
169
  remote.OnClientEvent:Connect(function(type, channel, payload)
package/src/Server.lua CHANGED
@@ -3,9 +3,9 @@ local Players = game:GetService('Players');
3
3
 
4
4
  local isTypeScriptEnv = script.Parent.Name == 'src';
5
5
  local dependencies = if isTypeScriptEnv then script.Parent.Parent.Parent else script.Parent.Parent;
6
+ local BaseReplion = require(script.Parent.BaseReplion)
6
7
  local Shared = require(script.Parent.Shared);
7
8
  local Sift = require(isTypeScriptEnv and dependencies.sift.out or dependencies.Sift);
8
- local Signal = require(isTypeScriptEnv and dependencies['sleitnick-signal'] or dependencies.Signal);
9
9
 
10
10
  type ServerConfig = {
11
11
  channel: string;
@@ -76,10 +76,8 @@ function Server.new(config: ServerConfig)
76
76
 
77
77
  self.player = config.replicateTo;
78
78
  self.channel = config.channel;
79
- self.data = Sift.Dictionary.copyDeep(config.data);
80
-
81
- self._signals = {} :: { [string]: any };
82
- self._allSignal = Signal.new();
79
+
80
+ self._base = BaseReplion.new(Sift.Dictionary.copyDeep(config.data));
83
81
 
84
82
  self._queuedUpdates = {} :: Shared.GenericDataTable;
85
83
  self._isQueued = false;
@@ -100,23 +98,16 @@ function Server.new(config: ServerConfig)
100
98
  end;
101
99
 
102
100
  function Server:observe(path: { string }?, callback: Observer)
103
- if not path then
104
- task.spawn(callback, self.data, nil);
105
- return self._allSignal:Connect(callback);
106
- end;
107
-
108
- local signalKey = Shared.getSignalKey(path);
109
- if not self._signals[signalKey] then
110
- self._signals[signalKey] = Signal.new();
111
- end;
112
-
113
- local initialValue = Shared.getNestedValue(self.data, path);
101
+ return self._base:observe(path, callback);
102
+ end;
114
103
 
115
- task.spawn(callback, initialValue, nil);
116
- return self._signals[signalKey]:Connect(callback);
104
+ function Server:get(path: { string }?)
105
+ return self._base:get(path);
117
106
  end;
118
107
 
119
108
  function Server:_notifyDeep(currentPath: { string }, newValue: any, oldValue: any)
109
+ local signals = self._base._signals;
110
+
120
111
  if typeof(newValue) == 'table' then
121
112
  for k, v in newValue do
122
113
  local nextPath = table.clone(currentPath);
@@ -125,7 +116,7 @@ function Server:_notifyDeep(currentPath: { string }, newValue: any, oldValue: an
125
116
  local nextOld = if typeof(oldValue) == 'table' then oldValue[k] else nil;
126
117
 
127
118
  local signalKey = Shared.getSignalKey(nextPath);
128
- local signal = self._signals[signalKey];
119
+ local signal = signals[signalKey];
129
120
  if signal then
130
121
  signal:Fire(v, nextOld);
131
122
  end;
@@ -142,7 +133,7 @@ function Server:_notifyDeep(currentPath: { string }, newValue: any, oldValue: an
142
133
  table.insert(nextPath, k);
143
134
 
144
135
  local signalKey = Shared.getSignalKey(nextPath);
145
- local signal = self._signals[signalKey];
136
+ local signal = signals[signalKey];
146
137
  if signal then
147
138
  signal:Fire(nil, v);
148
139
  end;
@@ -157,25 +148,26 @@ function Server:set(path: { string }, value: any)
157
148
 
158
149
  local topKey = path[1];
159
150
 
160
- local oldValue = Shared.getNestedValue(self.data, path);
151
+ local oldValue = Shared.getNestedValue(self._base.data, path);
161
152
  local newValue = if typeof(value) == 'function' then value(oldValue) else value;
162
153
  if oldValue == newValue then return; end;
163
154
 
164
- local oldTop = self.data[topKey];
155
+ local oldTop = self._base.data[topKey];
165
156
 
166
- setNestedValue(self.data, path, newValue);
157
+ setNestedValue(self._base.data, path, newValue);
167
158
  queueNestedUpdate(self._queuedUpdates, path, newValue);
168
159
  self:_scheduleUpdatesFlush();
169
160
 
170
161
  do
171
- local topSignal = self._signals[topKey];
162
+ local signals = self._base._signals;
163
+ local topSignal = signals[topKey];
172
164
  if topSignal then
173
- topSignal:Fire(self.data[topKey], oldTop);
165
+ topSignal:Fire(self._base.data[topKey], oldTop);
174
166
  end;
175
167
 
176
168
  local signalKey = Shared.getSignalKey(path);
177
169
  if signalKey ~= topKey then
178
- local pathSignal = self._signals[signalKey];
170
+ local pathSignal = signals[signalKey];
179
171
  if pathSignal then
180
172
  pathSignal:Fire(newValue, oldValue)
181
173
  end;
@@ -185,26 +177,16 @@ function Server:set(path: { string }, value: any)
185
177
 
186
178
  local thisUpdate = {};
187
179
  setNestedValue(thisUpdate, path, newValue);
188
- self._allSignal:Fire(self.data, thisUpdate);
180
+ self._base._allSignal:Fire(self._base.data, thisUpdate);
189
181
  end;
190
182
  end;
191
183
 
192
- function Server:get(path: { string }?)
193
- if not path then return self.data; end;
194
- return Shared.getNestedValue(self.data, path);
195
- end;
196
-
197
184
  function Server:destroy()
198
185
  self._destroyed = true;
199
- self.data = {};
200
186
  self._queuedUpdates = {};
201
-
202
- self._allSignal:Destroy();
203
- for _, signal in self._signals do
204
- signal:Destroy();
205
- end;
206
- self._signals = {};
207
-
187
+
188
+ self._base:destroy();
189
+
208
190
  if self.player then
209
191
  local playerReplions = self._activeReplions[self.player];
210
192
  if not playerReplions then return; end;
@@ -246,7 +228,7 @@ function Server:_sendInitial(player: Player?)
246
228
  local receiver = player or self.player;
247
229
  if not receiver then return; end;
248
230
 
249
- remote:FireClient(receiver, 'I', self.channel, self.data);
231
+ remote:FireClient(receiver, 'I', self.channel, self._base.data);
250
232
  end;
251
233
 
252
234
  remote.OnServerEvent:Connect(function(player, action, ...)
package/src/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { Connection } from '@rbxts/sleitnick-signal';
2
-
3
1
  declare namespace Replion {
4
2
  /**
5
3
  * Recursively builds a union of all valid path tuples for object T.
@@ -20,6 +18,8 @@ declare namespace Replion {
20
18
  : never
21
19
  : never;
22
20
 
21
+ type Cleanup = () => void;
22
+
23
23
  interface ServerConfig<T extends object> {
24
24
  channel: string;
25
25
  replicateTo?: Player;
@@ -30,11 +30,11 @@ declare namespace Replion {
30
30
  get(): T;
31
31
  get<P extends Path<T>>(path: P): PathValue<T, P>;
32
32
 
33
- observe(key: undefined, callback: (newValue: T, oldValue: Partial<T>) => void): Connection;
33
+ observe(key: undefined, callback: (newValue: T, oldValue: Partial<T>) => void): Cleanup;
34
34
  observe<P extends Path<T>>(
35
35
  path: P,
36
36
  callback: (newValue: PathValue<T, P>, oldValue: PathValue<T, P> | undefined) => void,
37
- ): Connection;
37
+ ): Cleanup;
38
38
 
39
39
  destroy(): void;
40
40
  }