-- Compiled with roblox-ts v3.0.0 local TS = _G[script] local TableTools = TS.import(script, script.Parent, "table_tools").TableTools local DebuggingTools = {} do local _container = DebuggingTools --*writes the error in the console and doesnt stop anything local function SafeError(message) local traceback = debug.traceback() task.spawn(function() error(message) end) print(traceback) end _container.SafeError = SafeError --[[ * * @returns list of all different keys ]] local function GetDifferentKeys(map_1, map_2) local keys = {} for key in map_1 do if map_2[key] ~= nil then continue end --adds the key keys[key] = true end for key in map_2 do if map_1[key] ~= nil then continue end keys[key] = true end local array = {} for key in keys do table.insert(array, key) end return array end _container.GetDifferentKeys = GetDifferentKeys local function FindFirstTable(array) -- ▼ ReadonlyArray.find ▼ local _callback = function(value) local _value = value return type(_value) == "table" end local _result for _i, _v in array do if _callback(_v, _i - 1, array) == true then _result = _v break end end -- ▲ ReadonlyArray.find ▲ return _result end _container.FindFirstTable = FindFirstTable --prints the value and returns it; local function PrintAndReturn(value) print(value) return value end _container.PrintAndReturn = PrintAndReturn --[[ * * prints the map, useful to debug in the RobloxPlayer Console (F9) ]] local function PrintMap(map, scope, exceptions) if scope == nil then scope = 0 end if exceptions == nil then exceptions = {} end local _scope = scope local brackets_space = string.rep("\t", _scope) --prints { if scope is 0 if scope == 0 then print(`{brackets_space}\{`) end --*spacing for the elements, they have deeper scope scope += 1 local _scope_1 = scope local scope_space = string.rep("\t", _scope_1) --checks if it's already printed the table --clones itself to avoid cycle refference if other element contains the same map exceptions = table.clone(exceptions) --adds itself to exeptions local _exceptions = exceptions local _map = map table.insert(_exceptions, _map) for key, value in map do local text = "" --spacing from the start --prints full name of an instance if typeof(value) == "Instance" then text = value:GetFullName() elseif type(value) == "table" then --if contains the same table, it's not going to print it if table.find(exceptions, value) ~= nil then text = "Cycle refference detected" else --prints the opening of the table --quick fix because it didnt display the key if the table was printed print(`{scope_space}[{key}] = \{`) --prints the table with current scope PrintMap(value, scope, exceptions) --skip the element continue end elseif type(value) == "string" then --surrounds value in " text = `"{value}"` else text = tostring(value) end print(`{scope_space}[{key}] = {text}`) end print(`{brackets_space}\}`) end _container.PrintMap = PrintMap local JSONAnalizer local Instances = {} do local _container_1 = Instances local function GetInstanceTree(instance) local instances_list = {} local recursive_instance_map = { [instance] = instances_list, } for _, child in instance:GetChildren() do if #child:GetChildren() ~= 0 then local _arg0 = GetInstanceTree(child) table.insert(instances_list, _arg0) continue end table.insert(instances_list, child) end return recursive_instance_map end _container_1.GetInstanceTree = GetInstanceTree local function GetInstanceStringTree(instance, use_full_name, use_class_name) if use_full_name == nil then use_full_name = false end if use_class_name == nil then use_class_name = false end local function GetInstanceString(instance) if use_class_name then return instance.ClassName end if use_full_name then return instance:GetFullName() end return instance.Name end local instances_string_list = {} local recursive_instance_map = { [GetInstanceString(instance)] = instances_string_list, } for _, child in instance:GetChildren() do if #child:GetChildren() ~= 0 then --recursively takes the tables local _arg0 = GetInstanceStringTree(child, use_full_name, use_class_name) table.insert(instances_string_list, _arg0) continue end local _arg0 = GetInstanceString(child) table.insert(instances_string_list, _arg0) end return recursive_instance_map end _container_1.GetInstanceStringTree = GetInstanceStringTree local function AnalizeInstanceStringTreeAndReturn(turn_arrays_into_maps, exact_values, ...) local args = { ... } local instance_string_tree = GetInstanceStringTree(unpack(args)) --analizes instance string tree as json and returns return JSONAnalizer.AnalizeAndReturnJson(instance_string_tree, turn_arrays_into_maps, exact_values) end _container_1.AnalizeInstanceStringTreeAndReturn = AnalizeInstanceStringTreeAndReturn end _container.Instances = Instances JSONAnalizer = {} do local _container_1 = JSONAnalizer local function AddToRecursiveTable(origin_table, new_table) for key, value in new_table do local tuple = origin_table[key] --0 - types local new_types = value[1] if tuple == nil then --sets to true to mark as optional because didnt exist in origin_table tuple = { new_types, true } --sets the new tuple local _origin_table = origin_table local _tuple = tuple _origin_table[key] = _tuple continue end --0 - types local origin_types = tuple[1] for _, type_name in new_types do if type(type_name) == "string" then if table.find(origin_types, type_name) ~= nil then continue end --pushes to types if doesnt exist table.insert(origin_types, type_name) continue end local origin_table_2 = FindFirstTable(origin_types) if origin_table_2 == nil then --adds the table if doesnt exist table.insert(origin_types, type_name) continue end AddToRecursiveTable(origin_table_2, type_name) end end --all different keys were applied local different_keys = GetDifferentKeys(origin_table, new_table) for _, key in different_keys do local tuple = origin_table[key] --marks previously missing keys as optional tuple[2] = true end end local function AnalizeJSON(data, turn_arrays_into_maps, exact_values) if turn_arrays_into_maps == nil then turn_arrays_into_maps = false end if exact_values == nil then exact_values = false end local function GetNontableValueTypeName(value) local _result if exact_values then _result = tostring(value) else local _value = value _result = typeof(_value) end return _result end local is_array = if turn_arrays_into_maps then false else TableTools.IsArray(data) if is_array then local data_types = {} for _, value in data do local type_name = GetNontableValueTypeName(value) local type_data = type_name if type(value) == "table" then local recursive_table = AnalizeJSON(value, turn_arrays_into_maps, exact_values) local origin_table = FindFirstTable(data_types) --if didnt find the table in the list of types, will push current if origin_table == nil then type_data = recursive_table else --otherwise will append missing keys from current to origin AddToRecursiveTable(origin_table, recursive_table) continue end else if table.find(data_types, type_name) ~= nil then continue end end local _type_data = type_data table.insert(data_types, _type_data) end return { number = { data_types, false }, } end local data_types = {} for key, value in data do local tuple = data_types[key] if tuple == nil then if type(value) == "table" then --analizes the value as defined table and adds to the table tuple = { { AnalizeJSON(value, turn_arrays_into_maps, exact_values) }, false } else --adds the value to the tuple tuple = { { GetNontableValueTypeName(value) }, false } end --add tuple to the table local _tuple = tuple data_types[key] = _tuple continue end --0 - types local types = tuple[1] if type(value) == "table" then local type_name = AnalizeJSON(value, turn_arrays_into_maps, exact_values) local origin_table = FindFirstTable(types) if origin_table == nil then --if didnt find the table, will add this table.insert(types, type_name) else --if found, will append the recursive table AddToRecursiveTable(origin_table, type_name) end continue end local type_name = GetNontableValueTypeName(value) if table.find(types, type_name) ~= nil then continue end --if didnt find the type name, add to the table table.insert(types, type_name) end return data_types end local function ConvertRecursiveTableToRecursiveMap(recursive_table) local recursive_map = {} for key, _binding in recursive_table do local types = _binding[1] local optional = _binding[2] local types_list = {} for _, type_name in types do if type(type_name) == "table" then --if is a table, convert to map and push local _arg0 = ConvertRecursiveTableToRecursiveMap(type_name) table.insert(types_list, _arg0) continue end table.insert(types_list, type_name) end --set the value to the key, key is with the question mark if optional local _arg0 = if optional then key .. "?" else key recursive_map[_arg0] = types_list end return recursive_map end --[[ * * @param turn_arrays_into_maps will treat the indexes as keys so all instances like * ```[number]: [[0]: number] will be [0...12]: [[0]: number]``` * * @param exact_values will return exact values instead of type e.g * ```[number]: {[0]: "hello", [1]: "world", [3]: "!"}```, equivalent to ```("hello" | "world" | "!")[]``` * * @returns analized content of the json, * looks like * * ```ts * { * [key]: [number] // key not optional variable that is a number * [key?]: [number, string] //key is optional variable that is a number or string * [key]: [ //key is not optional variable that is {name: string | number, sub_key?: number} * { * [name]: [string, number] * [sub_key?] [number] * } * ] * [number]: [ //equivalent to number[] * [0]: number * ] * [number]: [ //equivalent to string[] * [0]: string * ] * } * ``` * * can be confusing for results like * ```lua * { * [key] = { * [0] = {[name?]: string}, * } * ``` * it still means {[key]: {name?: string}} * * useful for analizing large databases ]] local function AnalizeAndReturnJson(...) local args = { ... } local recursive_table = AnalizeJSON(unpack(args)) local recursive_map = ConvertRecursiveTableToRecursiveMap(recursive_table) return recursive_map end _container_1.AnalizeAndReturnJson = AnalizeAndReturnJson --[[ * * @param turn_arrays_into_maps will treat the indexes as keys so all instances like * ```[number]: [[0]: number] will be [0...12]: [[0]: number]``` * * @param exact_values will return exact values instead of type e.g * ```[number]: {[0]: "hello", [1]: "world", [3]: "!"}```, equivalent to ```("hello" | "world" | "!")[]``` * * @returns analized content of the json, * looks like * * ```ts * { * [key]: [number] // key not optional variable that is a number * [key?]: [number, string] //key is optional variable that is a number or string * [key]: [ //key is not optional variable that is {name: string | number, sub_key?: number} * { * [name]: [string, number] * [sub_key?] [number] * } * ] * [number]: [ //equivalent to number[] * [0]: number * ] * [number]: [ //equivalent to string[] * [0]: string * ] * } * ``` * * can be confusing for results like * ```lua * { * [key] = { * [0] = {[name?]: string}, * } * ``` * it still means {[key]: {name?: string}} * * useful for analizing large databases ]] local function AnalizeAndPrintJson(...) local args = { ... } print(AnalizeAndReturnJson(unpack(args))) end _container_1.AnalizeAndPrintJson = AnalizeAndPrintJson end _container.JSONAnalizer = JSONAnalizer end return { DebuggingTools = DebuggingTools, }