feat: add toRaw()

This commit is contained in:
littensy 2025-11-19 21:38:56 -08:00
parent f082956ed9
commit 9f66803f04
13 changed files with 297 additions and 198 deletions

View file

@ -5,7 +5,7 @@ local suite = require("../../../../test/suite")
local test = suite.test
local afterEach = suite.afterEach
local function mockArraySerdes(object: { [any]: any }): { [any]: any }
local function mockArraySerdes(object: {}): {}
if object[1] then
local copy = {}
for index = 1, #object do

View file

@ -2,13 +2,11 @@ local Charm = require("../Charm")
local patch = require("./patch")
local types = require("./types")
type ReactiveProxy<T = {}> = Charm.ReactiveProxy<T>
type SyncPayload = types.SyncPayload
type SignalSetter<T = any> = (update: ((T) -> T) | T) -> ()
type ReactiveSetter<T = any> = (update: ((T) -> ()) | T) -> ()
type Reactive<T = any> = typeof(setmetatable({}, {} :: { setValue: ReactiveSetter<T> }))
type SignalSetter<T = any> = (T | (T) -> T) -> ()
type ReactiveSetter<T = any> = (T | (T) -> ()) -> ()
local trackedSignals: { [string]: SignalSetter } = {}
local trackedReactives: { [string]: ReactiveSetter } = {}
@ -24,12 +22,12 @@ local trackedReactives: { [string]: ReactiveSetter } = {}
@param signals A map of setter functions to sync with the server.
]]
local function addSignals(signals: { [string]: SignalSetter | { [any]: any } })
local function addSignals(signals: { [string]: SignalSetter | {} })
for key, signal in signals do
if type(signal) == "function" then
trackedSignals[key] = signal
else
trackedReactives[key] = getmetatable((signal :: any) :: Reactive).setValue
trackedReactives[key] = getmetatable(signal :: ReactiveProxy).set
end
end
end

View file

@ -39,7 +39,7 @@ end
long as the target array is not sparse. This function ensures that the diff
function produces a patch that can be correctly applied.
]]
local function stringifySparseArray(object: { any }): { [string | number]: any }
local function stringifySparseArray(object: {}): { [string | number]: any }
local maxn = table.maxn(object)
if maxn == 0 or maxn == #object then
@ -65,7 +65,7 @@ local function diff(oldState: any, newState: any): any
return newState
end
local diffs: { [any]: any } = table.clone(newState)
local diffs = table.clone(newState)
for key, oldValue in oldState do
diffs[key] = diff(oldValue, newState[key])

View file

@ -4,20 +4,19 @@ local patch = require("./patch")
local types = require("./types")
local utils = require("./utils")
type ReactiveProxy<T = {}> = Charm.ReactiveProxy<T>
type SyncPayload = types.SyncPayload
type SyncStatus = number
type ClientKeys = { [string]: SyncStatus }
type Reactive<T> = typeof(setmetatable({}, {} :: { initialValue: T, track: () -> () }))
type TrackedState<T = any> = {
target: any,
currentValue: T,
lastSyncedValue: T,
dispose: () -> (),
isReactive: boolean?,
dispose: () -> (),
}
type StateUpdate = {
@ -68,21 +67,21 @@ local function trackSignal<T>(key: string, getter: () -> T)
trackedStates[key] = trackedState
end
local function trackReactive<T>(key: string, reactive: Reactive<T>)
local function trackReactive<T>(key: string, proxy: ReactiveProxy<T>)
if trackedStates[key] then
if trackedStates[key].target ~= reactive then
if trackedStates[key].target ~= proxy then
warn("[Charm] Attempted to sync different states with the same key:", key)
end
return
end
local meta = getmetatable(reactive)
local snapshot = utils.deepCopy(meta.initialValue)
local meta = getmetatable(proxy)
local snapshot = utils.deepCopy(meta.value)
local firstRun = true
local trackedState = {
target = reactive,
currentValue = meta.initialValue,
target = proxy,
currentValue = meta.value,
lastSyncedValue = snapshot,
isReactive = true,
}
@ -93,7 +92,7 @@ local function trackReactive<T>(key: string, reactive: Reactive<T>)
firstRun = false
else
local oldSnapshot = snapshot
snapshot = utils.deepCopy(meta.initialValue)
snapshot = utils.deepCopy(meta.value)
table.insert(globalUpdates, {
key = key,
newValue = snapshot,
@ -258,7 +257,7 @@ end
@param client The client receiving state updates.
@param signals A map of getter functions to sync with the client.
]]
local function addSignalsToClient(client: any, signals: { [string]: { [any]: any } | () -> any })
local function addSignalsToClient(client: any, signals: { [string]: {} | () -> any })
if not clients[client] then
clients[client] = {}
end

View file

@ -6,10 +6,6 @@ type NestedTable = {
[any]: NestedTable | any,
}
type FlattenedTable = {
[any]: any,
}
--[[
Flattens a nested table into a single table with slash-separated keys.
Useful for recursively collecting atoms returned by modules.
@ -17,15 +13,15 @@ type FlattenedTable = {
@param input A nested table.
@return A flattened table with slash-separated keys.
]]
local function flatten(input: NestedTable): FlattenedTable
local result: FlattenedTable = {}
local function flatten<Flattened>(input: {}): Flattened
local result = {}
local function visit(node: NestedTable, path: string)
local function visit(node: {}, path: string)
for key, value in node do
local location = if path == "" then key else path .. "/" .. key
local location = if path == "" then tostring(key) else path .. "/" .. tostring(key)
if type(value) == "table" then
visit(value :: NestedTable, location)
visit(value :: {}, location)
else
result[location] = value
end
@ -34,7 +30,7 @@ local function flatten(input: NestedTable): FlattenedTable
visit(input, "")
return result
return result :: any
end
--[[

View file

@ -1,4 +1,4 @@
local function isTableUnsafe(object: { [any]: any })
local function isTableUnsafe(object: {})
local key = next(object)
if key == nil then
@ -12,7 +12,7 @@ local function isTableUnsafe(object: { [any]: any })
for key in object do
if type(key) ~= keyType then
return true
elseif keyType == "number" and key > maxKey then
elseif type(key) == "number" and key > maxKey then
maxKey = key
end
end
@ -33,7 +33,7 @@ end
@param value The table value.
@param key The key of the value.
]]
local function validate(value: { [any]: any })
local function validate(value: {})
if isTableUnsafe(value) then
warn(
`Cannot sync tables unsupported by remote events! See the table below for more info.\n\n`