{"id":"post-ill/schema","name":"schema","scope":"post-ill","platform":"roblox","description":"Minimal, highly composable runtime type checker for Roblox.","version":"1.0.1","latest":"1.0.1","versions":["1.0.1"],"license":"Unlicense","licenseRating":"safe","licenseCaveats":["The package archive does not include its license text; the license is declared in its manifest metadata."],"licenseVerified":false,"dependencies":{},"integrity":"83e24e4a1acce6660a87e625aad6f8f9bc949bc6d3dda9537d26d5a72279a18e","likes":0,"downloads":0,"install":"forest install post-ill/schema","url":"https://forest.dev/p/roblox/post-ill/schema","files":"https://api.forest.dev/ai/package/roblox/post-ill/schema/files","readme":"# schema\n\n*schema* is a minimal, highly composable runtime type checker for Roblox.\n\n## Aim\n- **Clean implementation**: Everything results in a function\n- **Fast ship**: The library is a single file at *src/schema.luau*\n- **Readable API**: The developer can figure out what each schema is about by just looking at its name\n\n## Installation\n\n*schema* is available on [wally](https://wally.run/package/post-ill/schema), so on [pesde](https://docs.pesde.dev/guides/dependencies/#wally-dependencies).\nYou can also just go to the latest release, pick the [script](https://github.com/post-ill/schema/blob/master/src/schema.luau) and drop it into your project ;)\n\n## Usage\n\nAn schema is simply a function that expects a single argument and returns an error message in case something bad\nhappens.\n\n```luau\nlocal s = require(path.to.schema)\n\nlocal yourType = s.whatever\nlocal err = yourType(yourValue)\n\nif not err then\n   print(\"Your value matches your schema\")\n   return\nend\n\nprint(`Schema error: {err}`)\n```\n\n## API\n\n### 1. Primitives\n\n| type        | accepts                  |\n|-------------|--------------------------|\n| `s.boolean` | `true` or `false`        |\n| `s.integer` | numbers without decimals |\n| `s.number`  | all numbers              |\n| `s.string`  | all forms of string      |\n| `s.any`     | anything                 |\n\n### 2. Constraints\n\n| type            | accepts                                                                     | example           |\n|-----------------|-----------------------------------------------------------------------------|-------------------|\n| `s.min(n)`      | numbers, strings or tables with a minimum size of `n` (inclusive)           | `s.min(5)`        |\n| `s.max(n)`      | numbers, strings or tables with a maximum size of `n` (inclusive)           | `s.max(10)`       |\n| `s.range(n, m)` | numbers, strings or tables with a size between `n` and `m` (both inclusive) | `s.range(5, 10)`  |\n| `s.size(n)`     | strings or tables with a size of `n`                                        | `s.size(10)`      |\n| `s.unsigned`    | numbers greater or equal to `0`                                             | `s.unsigned(0.5)` |\n\n### 3. Combinators\n\n| type                   | accepts                                                                                                                                                                                                                          | example                                                   |\n|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|\n| `s.array(t)`           | tables with consecutive integer keys whose elements match `t` schema                                                                                                                                                             | `s.array(s.integer)`                                      |\n| `s.set(t, i?)`         | values matching `s.array(t)` schema with no duplicates based on `i` identity function. The callback defaults to `function(value) return value end`. Identity function allows storing complex objects while associating an unique key | `s.set(s.string)`                                         |\n| `s.map(kt, vt)`        | regular tables whose keys matches `kt` schema and values `vt` schema                                                                                                                                                             | `s.map(s.string, s.boolean)`                              |\n| `s.object(o)`          | tables matching at least all `o` keys and values                                                                                                                                                                                 | `s.object({ id = s.integer, vip = s.boolean })`           |\n| `s.shape(o)`           | tables matching `s.object(o)` schema containing only `o` entries                                                                                                                                                                 | `s.shape({ key = s.string, value = s.number })`           |\n| `s.union(...t)`        | values matching at least one of the `...t` schemas                                                                                                                                                                               | `s.union(s.literal(\"r\"), s.literal(\"g\"), s.literal(\"b\"))` |\n| `s.intersection(...t)` | values matching all `...t` schemas                                                                                                                                                                                               | `s.intersection(s.integer, s.unsigned)`                   |\n| `s.optional(t)`        | values matching `t` or `nil`                                                                                                                                                                                                     | `s.optional(s.string)`                                    |\n\n### 4. Roblox types\n\n| type             | accepts                                   | example                        |\n|------------------|-------------------------------------------|--------------------------------|\n| `s.enum(e)`      | enum items of `e`                         | `s.enum(Enum.HumanoidRigType)` |\n| `s.dataType(dt)` | data objects matching `dt`                | `s.dataType(\"Vector3\")`        |\n| `s.instance(c)`  | instances that match or inherit `c` class | `s.instance(\"GuiObject\")`      |\n| `s.class(c)`     | instance with class `c`                   | `s.class(\"Script\")`            |\n\n## Complex examples\n\n```luau\n-- Number arrays with a max. size of 10 elements\ns.intersection(s.array(s.number), s.max(10))\n\n-- Uniquely identified shape based on its 'id' field\nlocal shape = s.shape({\n   id = s.integer,\n   name = s.string,\n   interests = s.set(s.string)\n})\nlocal function identity(value)\n   return value.id\nend\ns.intersection(s.set(shape, identity), s.min(5))\n\n-- Maps with combined key\ns.map(s.intersection(s.string, s.size(1)), s.number)\n\n-- Tagged union\ns.union(\n   s.shape({\n      type = s.literal(\"red\"),\n      hex = s.string\n   }),\n   s.shape({\n      type = s.literal(\"green\"),\n      hex = s.string\n   }),\n   s.shape({\n      type = s.literal(\"blue\"),\n      hex = s.string\n   })\n)\n```\n\n## Future plans\n\n*schema* ended up looking more like [t](https://github.com/osyrisrblx/t/) than I expected. This is not a bad\nthing at all, it's just something that happened. In a principle, it was supposed to have mixed explicit and\nimplicit typing (by explicit types meaning literals) using a builder `schema.build` function for instance.\n\n```luau\nlocal builtType = s.build({ -- This being a shape\n   primitiveLiteral = 3.14,\n   tuple = { s.number, s.integer, s.string },\n   array = { s.number },\n   set = { s.unique(s.number) }, -- Not really a thing, at that point just use s.set(type) lol\n   map = { [s.string] = s.boolean },\n   enum = Enum.HumanoidRigType,\n   object = s.object({}) -- Objects could only be specified through this way\n})\n```\n\nFor the sake of consistency, combinators should also have to use this build function internally for the receiving types. At the\ntime **I decided to keep literals out**. If the library gets to somewhere I could maybe start thinking more about adding it\nas a v2.\n\n## Issues\n\nFeel free to open an [issue](https://github.com/post-ill/schema/issues/new) if you find out that the library\nis not behaving as expected or maybe you want a feature that would fit in.\n","readmeTruncated":false}