@rbxts/replion 1.0.15 → 1.0.17
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 +1 -1
- package/package.json +2 -6
- package/src/Client.lua +49 -6
- package/src/Server.lua +35 -17
- package/src/index.d.ts +4 -4
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rbxts/replion",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
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
|
+
}
|
package/src/Client.lua
CHANGED
|
@@ -5,6 +5,7 @@ local Shared = require(script.Parent.Shared);
|
|
|
5
5
|
local Signal = require(isTypeScriptEnv and dependencies['sleitnick-signal'] or dependencies.Signal);
|
|
6
6
|
|
|
7
7
|
type Observer = (newValue: any, oldValue: any) -> ();
|
|
8
|
+
type Deletion = { path: { string }, oldTable: any };
|
|
8
9
|
|
|
9
10
|
local remote = script.Parent:WaitForChild(Shared.REMOTE_NAME) :: RemoteEvent;
|
|
10
11
|
|
|
@@ -77,10 +78,46 @@ function Client:observe(path: { string }?, callback: Observer)
|
|
|
77
78
|
local initialValue = Shared.getNestedValue(self.data, path);
|
|
78
79
|
|
|
79
80
|
task.spawn(callback, initialValue, nil);
|
|
80
|
-
|
|
81
|
+
local connection = self._signals[signalKey]:Connect(callback);
|
|
82
|
+
return function()
|
|
83
|
+
connection:Disconnect();
|
|
84
|
+
end;
|
|
85
|
+
end;
|
|
86
|
+
|
|
87
|
+
function Client:_collectDeletions(target: any, updates: any, path: { string }, out: { Deletion })
|
|
88
|
+
for k, v in updates do
|
|
89
|
+
local currentVal = target[k];
|
|
90
|
+
local newPath = table.clone(path);
|
|
91
|
+
table.insert(newPath, k);
|
|
92
|
+
|
|
93
|
+
if v == Shared.NIL_SENTINEL or (typeof(currentVal) == 'table' and typeof(v) ~= 'table') then
|
|
94
|
+
if typeof(currentVal) == 'table' then
|
|
95
|
+
table.insert(out, { path = newPath, oldTable = currentVal });
|
|
96
|
+
end;
|
|
97
|
+
elseif typeof(v) == 'table' and typeof(currentVal) == 'table' then
|
|
98
|
+
self:_collectDeletions(currentVal, v, newPath, out);
|
|
99
|
+
end;
|
|
100
|
+
end;
|
|
101
|
+
end;
|
|
102
|
+
|
|
103
|
+
function Client:_notifyDeepDeletion(path: { string }, oldTable: any)
|
|
104
|
+
for k, v in oldTable do
|
|
105
|
+
local nextPath = table.clone(path);
|
|
106
|
+
table.insert(nextPath, k);
|
|
107
|
+
|
|
108
|
+
local signalKey = Shared.getSignalKey(nextPath);
|
|
109
|
+
local signal = self._signals[signalKey];
|
|
110
|
+
if signal then
|
|
111
|
+
signal:Fire(nil, v);
|
|
112
|
+
end;
|
|
113
|
+
|
|
114
|
+
if typeof(v) == 'table' then
|
|
115
|
+
self:_notifyDeepDeletion(nextPath, v);
|
|
116
|
+
end;
|
|
117
|
+
end;
|
|
81
118
|
end;
|
|
82
119
|
|
|
83
|
-
function Client:
|
|
120
|
+
function Client:_notifyRecursiveUpdates(currentPath: { string }, updateTree: any)
|
|
84
121
|
for k, v in updateTree do
|
|
85
122
|
local nextPath = table.clone(currentPath);
|
|
86
123
|
table.insert(nextPath, k);
|
|
@@ -94,30 +131,36 @@ function Client:_recursiveSignalCheck(currentPath: { string }, updateTree: any)
|
|
|
94
131
|
end;
|
|
95
132
|
|
|
96
133
|
if typeof(v) == 'table' then
|
|
97
|
-
self:
|
|
134
|
+
self:_notifyRecursiveUpdates(nextPath, v);
|
|
98
135
|
end;
|
|
99
136
|
end;
|
|
100
137
|
end;
|
|
101
138
|
|
|
102
139
|
function Client:_applyUpdates(updates: Shared.GenericDataTable)
|
|
140
|
+
local deletions: { Deletion } = {};
|
|
141
|
+
self:_collectDeletions(self.data, updates, {}, deletions);
|
|
142
|
+
|
|
103
143
|
local oldData = table.clone(self.data); -- Note: Shallow copy instead of deep copy.
|
|
104
144
|
|
|
105
145
|
deepMergeAndApply(self.data, updates);
|
|
106
146
|
|
|
107
147
|
local anyChanged = false;
|
|
108
148
|
|
|
149
|
+
for _, item in deletions do
|
|
150
|
+
self:_notifyDeepDeletion(item.path, item.oldTable);
|
|
151
|
+
end;
|
|
152
|
+
|
|
109
153
|
for key, newValue in updates do
|
|
110
154
|
local oldValue = oldData[key];
|
|
111
155
|
|
|
112
156
|
-- If it's a table, we assume it changed if it's in the updates list.
|
|
113
157
|
if typeof(newValue) == 'table' then
|
|
114
|
-
anyChanged = true
|
|
158
|
+
anyChanged = true;
|
|
115
159
|
local signal = self._signals[key];
|
|
116
160
|
if signal then
|
|
117
161
|
signal:Fire(self.data[key], self.data[key]); -- Warning: Sends improper 'old' argument due to shallow copy optimization.
|
|
118
162
|
end;
|
|
119
|
-
|
|
120
|
-
self:_recursiveSignalCheck({key}, newValue);
|
|
163
|
+
self:_notifyRecursiveUpdates({key}, newValue);
|
|
121
164
|
elseif self.data[key] ~= oldValue then
|
|
122
165
|
anyChanged = true;
|
|
123
166
|
local signal = self._signals[key];
|
package/src/Server.lua
CHANGED
|
@@ -113,24 +113,44 @@ function Server:observe(path: { string }?, callback: Observer)
|
|
|
113
113
|
local initialValue = Shared.getNestedValue(self.data, path);
|
|
114
114
|
|
|
115
115
|
task.spawn(callback, initialValue, nil);
|
|
116
|
-
|
|
116
|
+
local connection = self._signals[signalKey]:Connect(callback);
|
|
117
|
+
return function()
|
|
118
|
+
connection:Disconnect();
|
|
119
|
+
end;
|
|
117
120
|
end;
|
|
118
121
|
|
|
119
|
-
function Server:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
function Server:_notifyDeep(currentPath: { string }, newValue: any, oldValue: any)
|
|
123
|
+
if typeof(newValue) == 'table' then
|
|
124
|
+
for k, v in newValue do
|
|
125
|
+
local nextPath = table.clone(currentPath);
|
|
126
|
+
table.insert(nextPath, k);
|
|
127
|
+
|
|
128
|
+
local nextOld = if typeof(oldValue) == 'table' then oldValue[k] else nil;
|
|
129
|
+
|
|
130
|
+
local signalKey = Shared.getSignalKey(nextPath);
|
|
131
|
+
local signal = self._signals[signalKey];
|
|
132
|
+
if signal then
|
|
133
|
+
signal:Fire(v, nextOld);
|
|
134
|
+
end;
|
|
126
135
|
|
|
127
|
-
|
|
128
|
-
local newValue = Shared.getNestedValue(self.data, nextPath);
|
|
129
|
-
signal:Fire(newValue, newValue); -- Warning: Sends improper 'old' argument due to shallow copy optimization.
|
|
136
|
+
self:_notifyDeep(nextPath, v, nextOld);
|
|
130
137
|
end;
|
|
138
|
+
end;
|
|
139
|
+
|
|
140
|
+
if typeof(oldValue) == 'table' then
|
|
141
|
+
for k, v in oldValue do
|
|
142
|
+
if typeof(newValue) == 'table' and newValue[k] ~= nil then continue; end;
|
|
131
143
|
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
local nextPath = table.clone(currentPath);
|
|
145
|
+
table.insert(nextPath, k);
|
|
146
|
+
|
|
147
|
+
local signalKey = Shared.getSignalKey(nextPath);
|
|
148
|
+
local signal = self._signals[signalKey];
|
|
149
|
+
if signal then
|
|
150
|
+
signal:Fire(nil, v);
|
|
151
|
+
end;
|
|
152
|
+
|
|
153
|
+
self:_notifyDeep(nextPath, nil, v);
|
|
134
154
|
end;
|
|
135
155
|
end;
|
|
136
156
|
end;
|
|
@@ -163,10 +183,8 @@ function Server:set(path: { string }, value: any)
|
|
|
163
183
|
pathSignal:Fire(newValue, oldValue)
|
|
164
184
|
end;
|
|
165
185
|
end;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
self:_recursiveSignalCheck(path, newValue);
|
|
169
|
-
end;
|
|
186
|
+
|
|
187
|
+
self:_notifyDeep(path, newValue, oldValue);
|
|
170
188
|
|
|
171
189
|
local thisUpdate = {};
|
|
172
190
|
setNestedValue(thisUpdate, path, newValue);
|
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):
|
|
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
|
-
):
|
|
37
|
+
): Cleanup;
|
|
38
38
|
|
|
39
39
|
destroy(): void;
|
|
40
40
|
}
|