@rbxts/replion 1.0.11 → 1.0.13
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 +1 -1
- package/src/Client.lua +58 -62
- package/src/Server.lua +37 -42
- package/src/Shared.lua +1 -1
- package/src/index.d.ts +36 -30
package/README.md
CHANGED
package/package.json
CHANGED
package/src/Client.lua
CHANGED
|
@@ -58,82 +58,78 @@ function Client.new(channel: string, data: Shared.GenericDataTable)
|
|
|
58
58
|
return self;
|
|
59
59
|
end;
|
|
60
60
|
|
|
61
|
-
function Client:get(
|
|
62
|
-
if not
|
|
63
|
-
|
|
64
|
-
return Shared.getNestedValue(self.data, key);
|
|
65
|
-
end;
|
|
66
|
-
|
|
67
|
-
return self.data[key];
|
|
61
|
+
function Client:get(path: { string }?)
|
|
62
|
+
if not path then return self.data; end;
|
|
63
|
+
return Shared.getNestedValue(self.data, path);
|
|
68
64
|
end;
|
|
69
65
|
|
|
70
|
-
function Client:observe(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
function Client:observe(path: { string }?, callback: Observer)
|
|
67
|
+
if not path then
|
|
68
|
+
task.spawn(callback, self.data, nil);
|
|
69
|
+
return self._allSignal:Connect(callback);
|
|
70
|
+
end;
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
local signalKey = Shared.getSignalKey(path);
|
|
73
|
+
if not self._signals[signalKey] then
|
|
74
|
+
self._signals[signalKey] = Signal.new();
|
|
75
|
+
end;
|
|
80
76
|
|
|
81
|
-
|
|
77
|
+
local initialValue = Shared.getNestedValue(self.data, path);
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
task.spawn(callback, initialValue, nil);
|
|
80
|
+
return self._signals[signalKey]:Connect(callback);
|
|
85
81
|
end;
|
|
86
82
|
|
|
87
83
|
function Client:_recursiveSignalCheck(currentPath: { string }, updateTree: any)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
84
|
+
for k, v in updateTree do
|
|
85
|
+
local nextPath = table.clone(currentPath);
|
|
86
|
+
table.insert(nextPath, k);
|
|
87
|
+
|
|
88
|
+
local signalKey = Shared.getSignalKey(nextPath);
|
|
89
|
+
local signal = self._signals[signalKey];
|
|
90
|
+
|
|
91
|
+
if signal then
|
|
92
|
+
local newValue = Shared.getNestedValue(self.data, nextPath);
|
|
93
|
+
signal:Fire(newValue, newValue); -- Warning: Sends improper 'old' argument due to shallow copy optimization.
|
|
94
|
+
end;
|
|
95
|
+
|
|
96
|
+
if typeof(v) == 'table' then
|
|
97
|
+
self:_recursiveSignalCheck(nextPath, v);
|
|
98
|
+
end;
|
|
99
|
+
end;
|
|
104
100
|
end;
|
|
105
101
|
|
|
106
102
|
function Client:_applyUpdates(updates: Shared.GenericDataTable)
|
|
107
|
-
|
|
103
|
+
local oldData = table.clone(self.data); -- Note: Shallow copy instead of deep copy.
|
|
108
104
|
|
|
109
|
-
|
|
105
|
+
deepMergeAndApply(self.data, updates);
|
|
110
106
|
|
|
111
|
-
|
|
107
|
+
local anyChanged = false;
|
|
108
|
+
|
|
109
|
+
for key, newValue in updates do
|
|
110
|
+
local oldValue = oldData[key];
|
|
112
111
|
|
|
113
|
-
for key, newValue in updates do
|
|
114
|
-
local oldValue = oldData[key];
|
|
115
|
-
|
|
116
112
|
-- If it's a table, we assume it changed if it's in the updates list.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
113
|
+
if typeof(newValue) == 'table' then
|
|
114
|
+
anyChanged = true
|
|
115
|
+
local signal = self._signals[key];
|
|
116
|
+
if signal then
|
|
117
|
+
signal:Fire(self.data[key], self.data[key]); -- Warning: Sends improper 'old' argument due to shallow copy optimization.
|
|
118
|
+
end;
|
|
119
|
+
|
|
120
|
+
self:_recursiveSignalCheck({key}, newValue);
|
|
121
|
+
elseif self.data[key] ~= oldValue then
|
|
122
|
+
anyChanged = true;
|
|
123
|
+
local signal = self._signals[key];
|
|
124
|
+
if signal then
|
|
125
|
+
signal:Fire(self.data[key], oldValue);
|
|
126
|
+
end;
|
|
127
|
+
end;
|
|
128
|
+
end;
|
|
129
|
+
|
|
130
|
+
if anyChanged then
|
|
131
|
+
self._allSignal:Fire(self.data, updates);
|
|
132
|
+
end;
|
|
137
133
|
end;
|
|
138
134
|
|
|
139
135
|
function Client:destroy()
|
|
@@ -148,7 +144,7 @@ remote.OnClientEvent:Connect(function(type, channel, payload)
|
|
|
148
144
|
if type == 'I' then
|
|
149
145
|
local newReplion = Client.new(channel, payload);
|
|
150
146
|
Client._activeReplions[channel] = newReplion;
|
|
151
|
-
|
|
147
|
+
|
|
152
148
|
local initializedSignal = Client._replionInitializedSignals[channel];
|
|
153
149
|
if not initializedSignal then return; end;
|
|
154
150
|
|
package/src/Server.lua
CHANGED
|
@@ -99,66 +99,61 @@ function Server.new(config: ServerConfig)
|
|
|
99
99
|
return self;
|
|
100
100
|
end;
|
|
101
101
|
|
|
102
|
-
function Server:observe(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
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
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
local signalKey = Shared.getSignalKey(path);
|
|
109
|
+
if not self._signals[signalKey] then
|
|
110
|
+
self._signals[signalKey] = Signal.new();
|
|
111
|
+
end;
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
local initialValue = Shared.getNestedValue(self.data, path);
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
task.spawn(callback, initialValue, nil);
|
|
116
|
+
return self._signals[signalKey]:Connect(callback);
|
|
117
117
|
end;
|
|
118
118
|
|
|
119
|
-
function Server:set(
|
|
120
|
-
|
|
119
|
+
function Server:set(path: { string }, value: any)
|
|
120
|
+
if self._destroyed then return; end;
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
local topKey = path[1];
|
|
122
|
+
local topKey = path[1];
|
|
124
123
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
local oldValue = Shared.getNestedValue(self.data, path);
|
|
125
|
+
local newValue = if typeof(value) == 'function' then value(oldValue) else value;
|
|
126
|
+
if oldValue == newValue then return; end;
|
|
128
127
|
|
|
129
|
-
|
|
128
|
+
local oldTop = self.data[topKey];
|
|
130
129
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
setNestedValue(self.data, path, newValue);
|
|
131
|
+
queueNestedUpdate(self._queuedUpdates, path, newValue);
|
|
132
|
+
self:_scheduleUpdatesFlush();
|
|
134
133
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
do
|
|
135
|
+
local topSignal = self._signals[topKey];
|
|
136
|
+
if topSignal then
|
|
137
|
+
topSignal:Fire(self.data[topKey], oldTop);
|
|
138
|
+
end;
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
local signalKey = Shared.getSignalKey(path);
|
|
141
|
+
if signalKey ~= topKey then
|
|
143
142
|
local pathSignal = self._signals[signalKey];
|
|
144
143
|
if pathSignal then
|
|
145
144
|
pathSignal:Fire(newValue, oldValue)
|
|
146
145
|
end;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
local thisUpdate = {};
|
|
150
|
-
setNestedValue(thisUpdate, path, newValue);
|
|
151
|
-
self._allSignal:Fire(self.data, thisUpdate);
|
|
152
|
-
end;
|
|
153
|
-
end;
|
|
146
|
+
end;
|
|
154
147
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return Shared.getNestedValue(self.data, key);
|
|
148
|
+
local thisUpdate = {};
|
|
149
|
+
setNestedValue(thisUpdate, path, newValue);
|
|
150
|
+
self._allSignal:Fire(self.data, thisUpdate);
|
|
159
151
|
end;
|
|
152
|
+
end;
|
|
160
153
|
|
|
161
|
-
|
|
154
|
+
function Server:get(path: { string }?)
|
|
155
|
+
if not path then return self.data; end;
|
|
156
|
+
return Shared.getNestedValue(self.data, path);
|
|
162
157
|
end;
|
|
163
158
|
|
|
164
159
|
function Server:destroy()
|
package/src/Shared.lua
CHANGED
|
@@ -15,7 +15,7 @@ function Shared.getNestedValue(root: any, path: { string })
|
|
|
15
15
|
return current;
|
|
16
16
|
end;
|
|
17
17
|
|
|
18
|
-
function Shared.getSignalKey(key: string | {string})
|
|
18
|
+
function Shared.getSignalKey(key: string | { string })
|
|
19
19
|
if typeof(key) == 'string' then return key; end;
|
|
20
20
|
return table.concat(key, '\0');
|
|
21
21
|
end;
|
package/src/index.d.ts
CHANGED
|
@@ -1,53 +1,59 @@
|
|
|
1
1
|
import { Connection } from '@rbxts/sleitnick-signal';
|
|
2
2
|
|
|
3
3
|
declare namespace Replion {
|
|
4
|
+
/**
|
|
5
|
+
* Recursively builds a union of all valid path tuples for object T.
|
|
6
|
+
* E.g., { a: { b: 1 } } -> ['a'] | ['a', 'b']
|
|
7
|
+
*/
|
|
8
|
+
type Path<T> = T extends object
|
|
9
|
+
? { [K in keyof T & string]: [K] | [K, ...Path<T[K]>] }[keyof T & string]
|
|
10
|
+
: never;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolves the type of the value at a specific path P within object T.
|
|
14
|
+
*/
|
|
15
|
+
type PathValue<T, P extends any[]> = P extends [infer K, ...infer R]
|
|
16
|
+
? K extends keyof T
|
|
17
|
+
? R extends []
|
|
18
|
+
? T[K]
|
|
19
|
+
: PathValue<T[K], R>
|
|
20
|
+
: never
|
|
21
|
+
: never;
|
|
22
|
+
|
|
4
23
|
interface ServerConfig<T extends object> {
|
|
5
24
|
channel: string;
|
|
6
25
|
replicateTo?: Player;
|
|
7
26
|
data: T;
|
|
8
27
|
}
|
|
9
28
|
|
|
10
|
-
|
|
11
|
-
constructor(config: ServerConfig<T>);
|
|
12
|
-
|
|
13
|
-
set<K extends keyof T>(key: K, value: T[K] | ((oldValue: T[K]) => T[K])): void;
|
|
14
|
-
set(path: string[], value: any | ((oldValue: any) => any)): void;
|
|
15
|
-
|
|
29
|
+
interface ReplionBase<T extends object> {
|
|
16
30
|
get(): T;
|
|
17
|
-
get<
|
|
18
|
-
get(path: string[]): any;
|
|
31
|
+
get<P extends Path<T>>(path: P): PathValue<T, P>;
|
|
19
32
|
|
|
20
33
|
observe(key: undefined, callback: (newValue: T, oldValue: Partial<T>) => void): Connection;
|
|
21
|
-
observe<
|
|
22
|
-
|
|
23
|
-
callback: (newValue: T
|
|
24
|
-
): Connection;
|
|
25
|
-
observe(
|
|
26
|
-
path: string[],
|
|
27
|
-
callback: (newValue: any, oldValue: any) => void,
|
|
34
|
+
observe<P extends Path<T>>(
|
|
35
|
+
path: P,
|
|
36
|
+
callback: (newValue: PathValue<T, P>, oldValue: PathValue<T, P> | undefined) => void,
|
|
28
37
|
): Connection;
|
|
29
38
|
|
|
30
39
|
destroy(): void;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
interface Server<T extends object> extends ReplionBase<T> { }
|
|
43
|
+
class Server<T extends object> {
|
|
44
|
+
constructor(config: ServerConfig<T>);
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
set<P extends Path<T>>(
|
|
47
|
+
path: P,
|
|
48
|
+
value: PathValue<T, P> | ((oldValue: PathValue<T, P>) => PathValue<T, P>),
|
|
49
|
+
): void;
|
|
50
|
+
}
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
callback: (newValue: T[K], oldValue: T[K] | undefined) => void,
|
|
44
|
-
): Connection;
|
|
45
|
-
observe(
|
|
46
|
-
path: string[],
|
|
47
|
-
callback: (newValue: any, oldValue: any) => void,
|
|
48
|
-
): Connection;
|
|
52
|
+
interface Client<T extends object> extends ReplionBase<T> { }
|
|
53
|
+
class Client<T extends object> {
|
|
54
|
+
static waitForReplion: <T extends object>(channel: string) => Client<T>;
|
|
49
55
|
|
|
50
|
-
|
|
56
|
+
private constructor();
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
|