@rbxts/replion 1.0.17 → 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.17"`
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.17",
3
+ "version": "1.0.18",
4
4
  "description": "",
5
5
  "main": "src/init.lua",
6
6
  "scripts": {},
@@ -21,4 +21,4 @@
21
21
  "roblox-ts": "^3.0.0",
22
22
  "typescript": "^5.9.3"
23
23
  }
24
- }
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,36 +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
- local connection = self._signals[signalKey]:Connect(callback);
82
- return function()
83
- connection:Disconnect();
84
- end;
67
+ return self._base:observe(path, callback);
85
68
  end;
86
69
 
87
70
  function Client:_collectDeletions(target: any, updates: any, path: { string }, out: { Deletion })
@@ -101,12 +84,14 @@ function Client:_collectDeletions(target: any, updates: any, path: { string }, o
101
84
  end;
102
85
 
103
86
  function Client:_notifyDeepDeletion(path: { string }, oldTable: any)
87
+ local signals = self._base._signals;
88
+
104
89
  for k, v in oldTable do
105
90
  local nextPath = table.clone(path);
106
91
  table.insert(nextPath, k);
107
92
 
108
93
  local signalKey = Shared.getSignalKey(nextPath);
109
- local signal = self._signals[signalKey];
94
+ local signal = signals[signalKey];
110
95
  if signal then
111
96
  signal:Fire(nil, v);
112
97
  end;
@@ -118,15 +103,17 @@ function Client:_notifyDeepDeletion(path: { string }, oldTable: any)
118
103
  end;
119
104
 
120
105
  function Client:_notifyRecursiveUpdates(currentPath: { string }, updateTree: any)
106
+ local signals = self._base._signals;
107
+
121
108
  for k, v in updateTree do
122
109
  local nextPath = table.clone(currentPath);
123
110
  table.insert(nextPath, k);
124
111
 
125
112
  local signalKey = Shared.getSignalKey(nextPath);
126
- local signal = self._signals[signalKey];
113
+ local signal = signals[signalKey];
127
114
 
128
115
  if signal then
129
- local newValue = Shared.getNestedValue(self.data, nextPath);
116
+ local newValue = Shared.getNestedValue(self._base.data, nextPath);
130
117
  signal:Fire(newValue, newValue); -- Warning: Sends improper 'old' argument due to shallow copy optimization.
131
118
  end;
132
119
 
@@ -138,11 +125,11 @@ end;
138
125
 
139
126
  function Client:_applyUpdates(updates: Shared.GenericDataTable)
140
127
  local deletions: { Deletion } = {};
141
- self:_collectDeletions(self.data, updates, {}, deletions);
128
+ self:_collectDeletions(self._base.data, updates, {}, deletions);
142
129
 
143
- 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.
144
131
 
145
- deepMergeAndApply(self.data, updates);
132
+ deepMergeAndApply(self._base.data, updates);
146
133
 
147
134
  local anyChanged = false;
148
135
 
@@ -156,31 +143,27 @@ function Client:_applyUpdates(updates: Shared.GenericDataTable)
156
143
  -- If it's a table, we assume it changed if it's in the updates list.
157
144
  if typeof(newValue) == 'table' then
158
145
  anyChanged = true;
159
- local signal = self._signals[key];
146
+ local signal = self._base._signals[key];
160
147
  if signal then
161
- 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.
162
149
  end;
163
150
  self:_notifyRecursiveUpdates({key}, newValue);
164
- elseif self.data[key] ~= oldValue then
151
+ elseif self._base.data[key] ~= oldValue then
165
152
  anyChanged = true;
166
- local signal = self._signals[key];
153
+ local signal = self._base._signals[key];
167
154
  if signal then
168
- signal:Fire(self.data[key], oldValue);
155
+ signal:Fire(self._base.data[key], oldValue);
169
156
  end;
170
157
  end;
171
158
  end;
172
159
 
173
160
  if anyChanged then
174
- self._allSignal:Fire(self.data, updates);
161
+ self._base._allSignal:Fire(self._base.data, updates);
175
162
  end;
176
163
  end;
177
164
 
178
165
  function Client:destroy()
179
- self._allSignal:Destroy();
180
- for _, signal in self._signals do
181
- signal:Destroy();
182
- end;
183
- self._signals = {};
166
+ self._base:destroy();
184
167
  end;
185
168
 
186
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,26 +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
- local connection = self._signals[signalKey]:Connect(callback);
117
- return function()
118
- connection:Disconnect();
119
- end;
104
+ function Server:get(path: { string }?)
105
+ return self._base:get(path);
120
106
  end;
121
107
 
122
108
  function Server:_notifyDeep(currentPath: { string }, newValue: any, oldValue: any)
109
+ local signals = self._base._signals;
110
+
123
111
  if typeof(newValue) == 'table' then
124
112
  for k, v in newValue do
125
113
  local nextPath = table.clone(currentPath);
@@ -128,7 +116,7 @@ function Server:_notifyDeep(currentPath: { string }, newValue: any, oldValue: an
128
116
  local nextOld = if typeof(oldValue) == 'table' then oldValue[k] else nil;
129
117
 
130
118
  local signalKey = Shared.getSignalKey(nextPath);
131
- local signal = self._signals[signalKey];
119
+ local signal = signals[signalKey];
132
120
  if signal then
133
121
  signal:Fire(v, nextOld);
134
122
  end;
@@ -145,7 +133,7 @@ function Server:_notifyDeep(currentPath: { string }, newValue: any, oldValue: an
145
133
  table.insert(nextPath, k);
146
134
 
147
135
  local signalKey = Shared.getSignalKey(nextPath);
148
- local signal = self._signals[signalKey];
136
+ local signal = signals[signalKey];
149
137
  if signal then
150
138
  signal:Fire(nil, v);
151
139
  end;
@@ -160,25 +148,26 @@ function Server:set(path: { string }, value: any)
160
148
 
161
149
  local topKey = path[1];
162
150
 
163
- local oldValue = Shared.getNestedValue(self.data, path);
151
+ local oldValue = Shared.getNestedValue(self._base.data, path);
164
152
  local newValue = if typeof(value) == 'function' then value(oldValue) else value;
165
153
  if oldValue == newValue then return; end;
166
154
 
167
- local oldTop = self.data[topKey];
155
+ local oldTop = self._base.data[topKey];
168
156
 
169
- setNestedValue(self.data, path, newValue);
157
+ setNestedValue(self._base.data, path, newValue);
170
158
  queueNestedUpdate(self._queuedUpdates, path, newValue);
171
159
  self:_scheduleUpdatesFlush();
172
160
 
173
161
  do
174
- local topSignal = self._signals[topKey];
162
+ local signals = self._base._signals;
163
+ local topSignal = signals[topKey];
175
164
  if topSignal then
176
- topSignal:Fire(self.data[topKey], oldTop);
165
+ topSignal:Fire(self._base.data[topKey], oldTop);
177
166
  end;
178
167
 
179
168
  local signalKey = Shared.getSignalKey(path);
180
169
  if signalKey ~= topKey then
181
- local pathSignal = self._signals[signalKey];
170
+ local pathSignal = signals[signalKey];
182
171
  if pathSignal then
183
172
  pathSignal:Fire(newValue, oldValue)
184
173
  end;
@@ -188,26 +177,16 @@ function Server:set(path: { string }, value: any)
188
177
 
189
178
  local thisUpdate = {};
190
179
  setNestedValue(thisUpdate, path, newValue);
191
- self._allSignal:Fire(self.data, thisUpdate);
180
+ self._base._allSignal:Fire(self._base.data, thisUpdate);
192
181
  end;
193
182
  end;
194
183
 
195
- function Server:get(path: { string }?)
196
- if not path then return self.data; end;
197
- return Shared.getNestedValue(self.data, path);
198
- end;
199
-
200
184
  function Server:destroy()
201
185
  self._destroyed = true;
202
- self.data = {};
203
186
  self._queuedUpdates = {};
204
-
205
- self._allSignal:Destroy();
206
- for _, signal in self._signals do
207
- signal:Destroy();
208
- end;
209
- self._signals = {};
210
-
187
+
188
+ self._base:destroy();
189
+
211
190
  if self.player then
212
191
  local playerReplions = self._activeReplions[self.player];
213
192
  if not playerReplions then return; end;
@@ -249,7 +228,7 @@ function Server:_sendInitial(player: Player?)
249
228
  local receiver = player or self.player;
250
229
  if not receiver then return; end;
251
230
 
252
- remote:FireClient(receiver, 'I', self.channel, self.data);
231
+ remote:FireClient(receiver, 'I', self.channel, self._base.data);
253
232
  end;
254
233
 
255
234
  remote.OnServerEvent:Connect(function(player, action, ...)