{"version":3,"file":"validate.luau","names":[],"sources":["..\\..\\src\\document\\validate.ts"],"sourcesContent":["import type { Modding } from \"@flamework/core\";\nimport { Boba } from \"@rbxts/boba\";\nimport Log from \"@rbxts/log\";\nimport { fromEntries } from \"@rbxts/object-utils\";\nimport type { HasRest, RestType, SplitRest } from \"@rbxts/serio/metadata/tuples\";\n\nimport type { IsLiteral, IsUnion } from \"type-fest\";\n\ntype ArrayMetadata<T extends Array<unknown>> = [T] extends [\n\t{\n\t\tlength: number;\n\t},\n]\n\t? TupleMetadata<T>\n\t: ListMetadata<T>;\n\ntype ListMetadata<T extends Array<unknown>> = [\"_list\", T] extends [\n\tkeyof T,\n\t{\n\t\t_list?: [infer V];\n\t},\n]\n\t? [\"list\", ValidateMetadata<V>]\n\t: [\"list\", ValidateMetadata<T[number]>];\n\ntype TupleMetadata<T extends Array<unknown>> = [\"_tuple\", T] extends [\n\tkeyof T,\n\t{\n\t\t_tuple?: [infer V extends Array<unknown>];\n\t},\n]\n\t? [\n\t\t\t\"tuple\",\n\t\t\tSplitRest<V> extends infer A\n\t\t\t\t? {\n\t\t\t\t\t\t[K in keyof A]: ValidateMetadata<A[K]>;\n\t\t\t\t\t}\n\t\t\t\t: never,\n\t\t\tHasRest<V> extends true ? ValidateMetadata<RestType<V>> : undefined,\n\t\t]\n\t: [\n\t\t\t\"tuple\",\n\t\t\tSplitRest<T> extends infer A\n\t\t\t\t? {\n\t\t\t\t\t\t[K in keyof A]: ValidateMetadata<A[K]>;\n\t\t\t\t\t}\n\t\t\t\t: never,\n\t\t\tHasRest<T> extends true ? ValidateMetadata<RestType<T>> : undefined,\n\t\t];\n\n// oxfmt-ignore\n/**\n * Recursive type-level mapping from a Luau data shape to the Serio-based validation metadata format\n * consumed by {@link createDataStoreValidator}.\n *\n * @typeParam T - The Luau type to encode as validation metadata.\n */\nexport type ValidateMetadata<T> =\n\tIsLiteral<T> extends true\n\t? [\"literal\", Array<NonNullable<T>>]\n\t: unknown extends T\n\t? never\n\t: undefined extends T\n\t? [\"optional\", ValidateMetadata<NonNullable<T>>]\n\t: [T] extends [boolean]\n\t? [\"bool\"]\n\t: [T] extends [number]\n\t? [\"number\"]\n\t: [\"_string\", T] extends [keyof T, { _string?: infer _V }]\n\t? [\"map\", [\"string\"], ValidateMetadata<// @ts-expect-error: indexing a mapped type by string cannot be proven valid from this constraint\n\t\tT[string]>]\n\t: [T] extends [string]\n\t? [\"string\"]\n\t: [\"_set\", T] extends [keyof T, { _set?: [infer V] }]\n\t? [\"set\", ValidateMetadata<V>]\n\t: [T] extends [ReadonlySet<infer V>]\n\t? [\"set\", ValidateMetadata<V>, ValidateMetadata<number>]\n\t: [\"_map\", T] extends [keyof T, { _map?: [infer K, infer V] }]\n\t? [\"map\", ValidateMetadata<K>, ValidateMetadata<V>]\n\t: [T] extends [ReadonlyMap<infer K, infer V>]\n\t? [\"map\", ValidateMetadata<K>, ValidateMetadata<V>]\n\t: [T] extends [ReadonlyArray<infer V>]\n\t? [\"list\", ValidateMetadata<V>]\n\t: [T] extends [Array<unknown>]\n\t? ArrayMetadata<T>\n\t: IsUnion<T> extends true\n\t? [\n\t\t\"union\",\n\t\tArray<T extends infer V ? ValidateMetadata<V> : never>\n\t]\n\t: true extends IsNominal<T>\n\t? never\n\t: T extends object\n\t? [\n\t\t\"object\",\n\t\tArray<{\n\t\t\t[K in keyof T]-?: [K, ValidateMetadata<T[K]>];\n\t\t}[keyof T]>,\n\t]\n\t: never;\n\n/** Discriminated union of all possible validation schema nodes produced by {@link ValidateMetadata}. */\nexport type ValidateSchema =\n\t| [\"bool\"]\n\t| [\"string\"]\n\t| [\"number\"]\n\t| [\"set\", ValidateSchema]\n\t| [\"list\", ValidateSchema]\n\t| [\"literal\", Array<defined>]\n\t| [\"optional\", ValidateSchema]\n\t| [\"map\", ValidateSchema, ValidateSchema]\n\t| [\"union\", Array<[unknown, ValidateSchema]>]\n\t| [\"object\", Array<[string, ValidateSchema]>]\n\t| [\"tuple\", Array<ValidateSchema>, ValidateSchema];\n\nfunction wrapBobaValidator<T>(b: Boba<T>): (x: unknown) => asserts x is T {\n\treturn (x: unknown): asserts x is T => {\n\t\treturn b.assert(x);\n\t};\n}\n\n/* eslint-disable jsdoc/require-param-description -- Flamework macro */\n/**\n * Creates a `Boba` const v function for a specific type.\n *\n * @param meta\n * @metadata macro\n */\nexport function createDataStoreValidator<T, A extends boolean>(\n\traw?: A,\n\tmeta?: Modding.Many<ValidateMetadata<T>>,\n): A extends true ? Boba<T> : (x: unknown) => asserts x is T {\n\tconst schema = meta as ValidateSchema;\n\tlet validator: Boba<T> | ((x: unknown) => asserts x is T) = undefined as never;\n\n\tswitch (schema[0]) {\n\t\tcase \"bool\": {\n\t\t\tconst v = Boba.Boolean as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"list\": {\n\t\t\tconst [_, elementSchema] = schema;\n\t\t\tconst v = Boba.Array(createDataStoreValidator(true, elementSchema as never)) as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"literal\": {\n\t\t\tconst [_, literals] = schema;\n\t\t\tlet v: Boba<T> = undefined as never;\n\t\t\tfor (const value of literals) {\n\t\t\t\tv = (v !== undefined ? v.Or(Boba.Literal(value)) : Boba.Literal(value)) as Boba<T>;\n\t\t\t}\n\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"map\": {\n\t\t\tconst [_, keySchema, valueSchema] = schema;\n\t\t\tconst v = Boba.Map(\n\t\t\t\tcreateDataStoreValidator(true, keySchema as never),\n\t\t\t\tcreateDataStoreValidator(true, valueSchema as never),\n\t\t\t) as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"number\": {\n\t\t\tconst v = Boba.Number as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"object\": {\n\t\t\tconst [_, fields] = schema;\n\t\t\tconst v = Boba.Struct(\n\t\t\t\tfromEntries(\n\t\t\t\t\tfields.map(([fieldName, fieldSchema]) => {\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tfieldName,\n\t\t\t\t\t\t\tcreateDataStoreValidator(true, fieldSchema as never),\n\t\t\t\t\t\t] as const;\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t) as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"optional\": {\n\t\t\tconst [_, valueSchema] = schema;\n\t\t\tconst v = createDataStoreValidator(true, valueSchema as never).Optional() as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"set\": {\n\t\t\tconst [_, valueSchema] = schema;\n\t\t\tconst v = Boba.Set(createDataStoreValidator(true, valueSchema as never)) as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"string\": {\n\t\t\tconst v = Boba.String as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"tuple\": {\n\t\t\tconst [_, elements] = schema;\n\t\t\tlet index = 0;\n\t\t\tconst v = Boba.ExhaustiveStruct(\n\t\t\t\tfromEntries(\n\t\t\t\t\telements.map(([, tupleSchema]) => {\n\t\t\t\t\t\tindex++;\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\tcreateDataStoreValidator(true, tupleSchema as never),\n\t\t\t\t\t\t] as const;\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t) as Boba<T>;\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"union\": {\n\t\t\tconst [_, elements] = schema;\n\t\t\tlet v: Boba<T> = undefined as never;\n\t\t\tfor (const valueSchema of elements) {\n\t\t\t\tconst value = createDataStoreValidator(true, valueSchema as never);\n\t\t\t\tv = (v !== undefined ? v.Or(value) : value) as Boba<T>;\n\t\t\t}\n\n\t\t\tvalidator = raw ? v : wrapBobaValidator(v);\n\n\t\t\tbreak;\n\t\t}\n\t\tdefault: {\n\t\t\tLog.Fatal(\n\t\t\t\t`[validateDataStoreSchema]: Cannot serialize unknown schema type '${schema[0]}'`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn validator as A extends true ? Boba<T> : (x: unknown) => asserts x is T;\n}\n/* eslint-enable jsdoc/require-param-description */\n"],"mappings":";;AACA;AACA;AACA;;;;;;;;;;;AAgHA;AACC;;;AAGD;;;;;;;;;;AASA;AAIC;AACA;AAEA;AAAA;;AAEE;AACA;AAEA;;;AAGA;AAAA;AACA;AACA;AAEA;;;AAGA;AAAA;AACA;AACA;AACC;AACD;AAEA;AAEA;;;AAGA;AAAA;AAAA;AACA;AAIA;AAEA;;;AAGA;AACA;AAEA;;;AAGA;AAAA;AACA;AAAA;AAAA;;;;;AAAA;;AASY;AATZ;AAAA;AAUA;AAEA;;;AAGA;AAAA;AACA;AACA;AAEA;;;AAGA;AAAA;AACA;AACA;AAEA;;;AAGA;AACA;AAEA;;;AAGA;AAAA;AACA;AACA;AAAA;AAAA;;;;;AAAA;;AAUY;AAVZ;AAAA;AAWA;AAEA;;;AAGA;AAAA;AACA;AACA;AACC;AACA;AACD;AAEA;AAEA;;AAEQ;AACR;AAGD;;AAGD;AACD","ignoreList":[]}