-- Compiled with roblox-ts v2.3.0-dev-30dae68 local TS = _G[script] --[[ * * @license * Copyright 2024 Daymon Littrell-Reyes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ]] local Object = TS.import(script, TS.getModule(script, "@rbxts", "object-utils")) local HttpService = TS.import(script, TS.getModule(script, "@rbxts", "services")).HttpService local t = TS.import(script, TS.getModule(script, "@rbxts", "t").lib.ts).t --[[ * * Encodes an element to a JSON string. * * Internally just wraps around {@link HttpService.JSONEncode} to avoid * errors. * * @param element - The element to encode. * * @returns The JSON string, or undefined if encoding fails. ]] local function encodeToJson(element) local _exitType, _returns = TS.try(function() return TS.TRY_RETURN, { HttpService:JSONEncode(element) } end, function(e) return TS.TRY_RETURN, { nil } end) if _exitType then return unpack(_returns) end end --[[ * * Encodes an element to a JSON string or returns the element as a string. * * Variant of {@link encodeToJson} that calls {@link tostring} on failure. * * @param element - The element to encode. * * @returns The JSON string if encoding is successful, otherwise the result of * calling {@link tostring} on the element. * * @internal ]] local function encodeToJsonOrString(element) local _condition = encodeToJson(element) if _condition == nil then _condition = tostring(element) end return _condition end --[[ * * Serializes specific Roblox types to a a valid JSON representation. * * @param value - The value to serialize. * * @returns A valid JSON representation of the value, or undefined if the type is not supported. ]] local function serializeRobloxType(value) local _value = value if typeof(_value) == "Vector3" then return { X = value.X, Y = value.Y, Z = value.Z, } end local _value_1 = value if typeof(_value_1) == "Vector2" then return { X = value.X, Y = value.Y, } end local _value_2 = value if typeof(_value_2) == "Instance" then return value:GetFullName() end local _value_3 = value if typeof(_value_3) == "EnumItem" then return tostring(value) end local _value_4 = value if typeof(_value_4) == "CFrame" then return `CFrame({table.concat({ value:GetComponents() }, ", ")})` end return nil end --[[ * * Helper type to force typescript to let us call methods on {@link object objects}. ]] --[[ * * Values that can be present in an {@link object} and properly encoded * to JSON. ]] local function isArray(element) return t.array(t.any)(element) end --[[ * * Encodes a table element according to the {@link config}. * * This method also catches self reference and converts them to `` * to avoid stack overflows. Although, if there's more complex cyclic references, * this will not catch them. * * @param config - The serialization configuration. * @param element - The table element to encode. * * @returns A valid value that can be encoded into a JSON element. ]] local encodeToObjectOrString local function encodeTable(config, element) if element[config.encodeMethod] ~= nil then local method = element[config.encodeMethod] if type(method) == "function" then return method() end end if isArray(element) then -- ▼ ReadonlyArray.map ▼ local _newValue = table.create(#element) local _callback = function(it) return encodeToObjectOrString(config, it) end for _k, _v in element do _newValue[_k] = _callback(_v, _k - 1, element) end -- ▲ ReadonlyArray.map ▲ return _newValue end if not config.deepEncodeTables then return element end local newElement = {} for _, _binding in Object.entries(element) do local key = _binding[1] local value = _binding[2] if value == element then newElement[`{key}`] = `` else newElement[`{key}`] = encodeToObjectOrString(config, value) end end return newElement end --[[ * * Encodes an element to a valid JSON element according to the {@link config}. * * @param config - The serialization configuration. * @param element - The element to encode. * * @returns An encodable value or undefined if the element cannot be encoded. ]] function encodeToObjectOrString(config, element) local _element = element local elementType = typeof(_element) repeat local _fallthrough = false if elementType == "number" then _fallthrough = true end if _fallthrough or elementType == "string" then _fallthrough = true end if _fallthrough or elementType == "boolean" then return element end if elementType == "table" then return encodeTable(config, element) end if elementType == "function" then return if config.encodeFunctions then "" else nil end do if not config.encodeRobloxTypes then return `<{elementType}>` end local _condition = serializeRobloxType(element) if _condition == nil then _condition = `<{elementType}>` end return _condition end until true end --[[ * * Serializes an element to a valid JSON element. * * @param config - The serialization configuration. * @param element - The element to encode. * * @returns A copy of the element with all values encoded as JSON-safe variants. * * @public ]] local function serialize(config, element) local encodedElement = encodeToObjectOrString(config, element) -- TODO(): refactor so this cast isn't needed return encodedElement end return { encodeToJsonOrString = encodeToJsonOrString, serialize = serialize, }