|
1 |
| -# Typescript Node Package Repository Template |
| 1 | +# json-schema-fns |
2 | 2 |
|
3 |
| -> Create a new repo from this template to get started creating a Typescript npm package |
| 3 | +> Modern utility library and typescript typings for building JSON Schema documents dynamically |
4 | 4 |
|
5 | 5 | <!--  -->
|
6 |
| -<!--  --> |
7 |
| -<!-- [](https://npmjs.com/@jsonhero/ts-node-package-template) --> |
8 |
| -<!-- [](https://packagephobia.com/result?p=@jsonhero/ts-node-package-template) --> |
| 6 | +<!--  --> |
| 7 | +<!-- [](https://npmjs.com/@jsonhero/json-schema-fns) --> |
| 8 | +<!-- [](https://packagephobia.com/result?p=@jsonhero/json-schema-fns) --> |
9 | 9 |
|
10 | 10 | ## Features
|
11 | 11 |
|
12 |
| -- Written in typescript |
13 |
| -- Github workflows for running tests and publishing package to NPM on Github release |
14 |
| -- Rollup for building commonjs and esm compatible npm package |
15 |
| -- ts-node and ts-jest integration |
16 |
| -- Generate coverage badges |
17 |
| -- ESLint with Typescript and prettier support |
18 |
| -- Pre-commit hooks to format code with prettier and run ESLint |
| 12 | +- Build JSON Schema documents for various drafts (currently only draft-2020-12 but more coming soon) |
| 13 | +- Strongly typed documents using Typescript |
| 14 | +- Allows you to build correct JSON Schema documents using dynamic data |
19 | 15 |
|
20 | 16 | ## Usage
|
21 | 17 |
|
22 |
| -Create a new repository from this template on Github with the [following instructions](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template) |
| 18 | +Create a simple draft-2020-12 document: |
| 19 | + |
| 20 | +```ts |
| 21 | +import { s } from "json-schema-fns"; |
| 22 | + |
| 23 | +const schema = s.object({ |
| 24 | + properties: [s.requiredProperty("foo", s.string()), s.property("bar", s.int())], |
| 25 | +}); |
| 26 | + |
| 27 | +schema.toSchemaDocument(); |
| 28 | +``` |
| 29 | + |
| 30 | +Will result in |
| 31 | + |
| 32 | +```json |
| 33 | +{ |
| 34 | + "$schema": "https://json-schema.org/draft/2020-12/schema#", |
| 35 | + "$id": "https://jsonhero.io/schemas/root.json", |
| 36 | + "type": "object", |
| 37 | + "properties": { |
| 38 | + "foo": { "type": "string" }, |
| 39 | + "bar": { "type": "integer" } |
| 40 | + }, |
| 41 | + "required": ["foo"] |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +You can also import the types for a specific draft to use, like so: |
| 46 | + |
| 47 | +```typescript |
| 48 | +import { s, Schema, IntSchema, StringSchema, StringFormat } from "json-schema-fns"; |
| 49 | + |
| 50 | +function buildIntSchema(maximum: number, minimum: number): IntSchema { |
| 51 | + return s.int({ minimum, maximum }); |
| 52 | +} |
| 53 | + |
| 54 | +function buildStringFormat(format: JSONStriStringFormatgFormat): StringSchema { |
| 55 | + return s.string({ format }); |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +`json-schema-fns` support all the features of JSON schema: |
| 60 | + |
| 61 | +```typescript |
| 62 | +import { s } from "json-schema-fns"; |
| 63 | + |
| 64 | +const phoneNumber = s.def("phoneNumber", s.string({ pattern: "^[0-9]{3}-[0-9]{3}-[0-9]{4}$" })); |
| 65 | +const usAddress = s.def( |
| 66 | + "usAddress", |
| 67 | + s.object({ |
| 68 | + properties: [s.requiredProperty("zipCode", s.string())], |
| 69 | + }), |
| 70 | +); |
| 71 | + |
| 72 | +const ukAddress = s.def( |
| 73 | + "ukAddress", |
| 74 | + s.object({ |
| 75 | + properties: [s.requiredProperty("postCode", s.string())], |
| 76 | + }), |
| 77 | +); |
| 78 | + |
| 79 | +s.object({ |
| 80 | + $id: "/schemas/person", |
| 81 | + title: "Person Profile", |
| 82 | + description: "Attributes of a person object", |
| 83 | + examples: [ |
| 84 | + { |
| 85 | + name: "Eric", |
| 86 | + |
| 87 | + }, |
| 88 | + ], |
| 89 | + $comment: "This is just a preview", |
| 90 | + default: {}, |
| 91 | + properties: [ |
| 92 | + s.requiredProperty("name", s.string()), |
| 93 | + s.property("email", s.string({ format: "email" })), |
| 94 | + s.property("phoneNumber", s.ref("phoneNumber")), |
| 95 | + s.property("billingAddress", s.oneOf(s.ref("ukAddress"), s.ref("usAddress"))), |
| 96 | + ], |
| 97 | + patternProperties: [s.patternProperty("^[A-Za-z]$", s.string())], |
| 98 | + additionalProperties: s.array({ |
| 99 | + items: s.number({ minimum: 0, maximum: 5000 }), |
| 100 | + }), |
| 101 | + propertyNames: "^[A-Za-z_][A-Za-z0-9_]*$", |
| 102 | + minProperties: 3, |
| 103 | + maxProperties: 20, |
| 104 | + unevaluatedProperties: false, |
| 105 | + defs: [phoneNumber, usAddress, ukAddress], |
| 106 | +}).toSchemaDocument(); |
| 107 | +``` |
| 108 | + |
| 109 | +Will result in |
| 110 | + |
| 111 | +```json |
| 112 | +{ |
| 113 | + "$schema": "https://json-schema.org/draft/2020-12/schema", |
| 114 | + "type": "object", |
| 115 | + "$id": "/schemas/person", |
| 116 | + "title": "Person Profile", |
| 117 | + "description": "Attributes of a person object", |
| 118 | + "examples": [ |
| 119 | + { |
| 120 | + "name": "Eric", |
| 121 | + |
| 122 | + } |
| 123 | + ], |
| 124 | + "$comment": "This is just a preview", |
| 125 | + "default": {}, |
| 126 | + "minProperties": 3, |
| 127 | + "maxProperties": 20, |
| 128 | + "unevaluatedProperties": false, |
| 129 | + "properties": { |
| 130 | + "name": { |
| 131 | + "type": "string" |
| 132 | + }, |
| 133 | + "email": { |
| 134 | + "type": "string", |
| 135 | + "format": "email" |
| 136 | + }, |
| 137 | + "phoneNumber": { |
| 138 | + "$ref": "#/$defs/phoneNumber" |
| 139 | + }, |
| 140 | + "billingAddress": { |
| 141 | + "oneOf": [ |
| 142 | + { |
| 143 | + "$ref": "#/$defs/ukAddress" |
| 144 | + }, |
| 145 | + { |
| 146 | + "$ref": "#/$defs/usAddress" |
| 147 | + } |
| 148 | + ] |
| 149 | + } |
| 150 | + }, |
| 151 | + "required": ["name"], |
| 152 | + "patternProperties": { |
| 153 | + "^[A-Za-z]$": { |
| 154 | + "type": "string" |
| 155 | + } |
| 156 | + }, |
| 157 | + "propertyNames": { |
| 158 | + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" |
| 159 | + }, |
| 160 | + "additionalProperties": { |
| 161 | + "type": "array", |
| 162 | + "items": { |
| 163 | + "type": "number", |
| 164 | + "minimum": 0, |
| 165 | + "maximum": 5000 |
| 166 | + } |
| 167 | + }, |
| 168 | + "$defs": { |
| 169 | + "phoneNumber": { |
| 170 | + "type": "string", |
| 171 | + "pattern": "^[0-9]{3}-[0-9]{3}-[0-9]{4}$" |
| 172 | + }, |
| 173 | + "usAddress": { |
| 174 | + "type": "object", |
| 175 | + "properties": { |
| 176 | + "zipCode": { |
| 177 | + "type": "string" |
| 178 | + } |
| 179 | + }, |
| 180 | + "required": ["zipCode"] |
| 181 | + }, |
| 182 | + "ukAddress": { |
| 183 | + "type": "object", |
| 184 | + "properties": { |
| 185 | + "postCode": { |
| 186 | + "type": "string" |
| 187 | + } |
| 188 | + }, |
| 189 | + "required": ["postCode"] |
| 190 | + } |
| 191 | + } |
| 192 | +} |
| 193 | +``` |
| 194 | + |
| 195 | +# API |
| 196 | + |
| 197 | +## `s` |
| 198 | + |
| 199 | +All the builder methods for creating subschemas are available on the `s` object |
| 200 | + |
| 201 | +```typescript |
| 202 | +import { s } from "json-schema-fns"; |
| 203 | +``` |
| 204 | + |
| 205 | +Or if you want to import a specific dialect: |
| 206 | + |
| 207 | +```typescript |
| 208 | +import { s } from "json-schema-fns/2020"; |
| 209 | +``` |
| 210 | + |
| 211 | +### `object` |
| 212 | + |
| 213 | +### `array` |
| 214 | + |
| 215 | +### `string` |
| 216 | + |
| 217 | +### `integer` and `number` |
| 218 | + |
| 219 | +### `nil` |
| 220 | + |
| 221 | +### `boolean` |
| 222 | + |
| 223 | +## Roadmap |
| 224 | + |
| 225 | +- Support draft-04 |
| 226 | +- Support draft-06 |
| 227 | +- Support draft-07 |
| 228 | +- Support draft/2019-09 |
0 commit comments