### Installation Source: https://github.com/exodusoss/schemasafe/blob/master/README.md Install the schemasafe package using npm. ```sh npm install --save @exodus/schemasafe ``` -------------------------------- ### minContains example Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2020-12/minContains.md This JavaScript code demonstrates the usage of minContains. ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref0 = function validate(data) { if (Array.isArray(data)) { let passes0 = 0 for (let i = 0; i < data.length; i++) { const sub0 = (() => { if (data[i] !== undefined && hasOwn(data, i)) { if (!(data[i] === 1)) return false } return true })() if (sub0) passes0++ } if (passes0 < 0) return false if (passes0 > 1) return false } return true }; return ref0 ``` -------------------------------- ### Schema Example 5 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/ref.md Demonstrates using a URN with an r-component as a base URI. ```json { "$comment": "RFC 8141 ยง2.3.1", "$id": "urn:example:foo-bar-baz-qux?+CCResolve:cc=uk", "properties": { "foo": { "$ref": "#/$defs/bar" } }, "$defs": { "bar": { "type": "string" } } } ``` -------------------------------- ### Schema Example 2 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/ref.md Demonstrates $ref with $anchor and $id for a local fragment. ```json { "$comment": "$id must be evaluated before $ref to get the proper $ref destination", "$id": "https://example.com/draft/next/ref-and-id2/base.json", "$ref": "#bigint", "$defs": { "bigint": { "$comment": "canonical uri: https://example.com/ref-and-id2/base.json#/$defs/bigint; another valid uri for this location: https://example.com/ref-and-id2/base.json#bigint", "$anchor": "bigint", "maximum": 10 }, "smallint": { "$comment": "canonical uri: https://example.com/ref-and-id2#/$defs/smallint; another valid uri for this location: https://example.com/ref-and-id2/#bigint", "$id": "/draft/next/ref-and-id2/", "$anchor": "bigint", "maximum": 2 } } } ``` -------------------------------- ### Schema Example 1 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/ref.md Demonstrates $ref and $id with a local file reference. ```json { "$comment": "$id must be evaluated before $ref to get the proper $ref destination", "$id": "https://example.com/draft/next/ref-and-id1/base.json", "$ref": "int.json", "$defs": { "bigint": { "$comment": "canonical uri: https://example.com/ref-and-id1/int.json", "$id": "int.json", "maximum": 10 }, "smallint": { "$comment": "canonical uri: https://example.com/ref-and-id1-int.json", "$id": "/draft/next/ref-and-id1-int.json", "maximum": 2 } } } ``` -------------------------------- ### Schema Example 3 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/ref.md Demonstrates using a URN as a base URI with $ref. ```json { "$comment": "URIs do not have to have HTTP(s) schemes", "$id": "urn:uuid:deadbeef-1234-ffff-ffff-4321feebdaed", "minimum": 30, "properties": { "foo": { "$ref": "urn:uuid:deadbeef-1234-ffff-ffff-4321feebdaed" } } } ``` -------------------------------- ### Unanchored Pattern Example Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2019-09/pattern.md Example schema and JavaScript code demonstrating an unanchored pattern and its validation logic. ```json { "pattern": "a+" } ``` ```javascript 'use strict' const ref0 = function validate(data) { if (typeof data === "string") { if (!(data.includes("a"))) return false } return true }; return ref0 ``` -------------------------------- ### Custom Formats - Parser Source: https://github.com/exodusoss/schemasafe/blob/master/README.md Example of adding custom formats to the parser. ```js const parse = parser({ $schema: 'https://json-schema.org/draft/2019-09/schema', type: 'string', format: 'only-a' }, { formats: { 'only-a': /^a+$/, } }) console.log(parse('"aa"')) // { valid: true, value: 'aa' } console.log(parse('"ab"')) // { valid: false } ``` -------------------------------- ### oneOf Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft4/oneOf.md This example demonstrates a basic usage of the oneOf keyword with integer and minimum constraints. ```json { "oneOf": [{ "type": "integer" }, { "minimum": 2 }] } ``` ```javascript 'use strict' const ref0 = function validate(data) { let passes0 = 0 const sub0 = (() => { if (!Number.isInteger(data)) return false return true })() if (sub0) passes0++ const sub1 = (() => { if (typeof data === "number") { if (!(2 <= data)) return false } return true })() if (sub1) passes0++ if (passes0 !== 1) return false return true }; return ref0 ``` -------------------------------- ### oneOf Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/oneOf.md A basic example of oneOf with integer and minimum constraints. ```json { "oneOf": [{ "type": "integer" }, { "minimum": 2 }] } ``` ```javascript 'use strict' const ref0 = function validate(data) { let passes0 = 0 const sub0 = (() => { if (!Number.isInteger(data)) return false return true })() if (sub0) passes0++ const sub1 = (() => { if (typeof data === "number") { if (!(2 <= data)) return false } return true })() if (sub1) passes0++ if (passes0 !== 1) return false return true }; return ref0 ``` -------------------------------- ### anyOf Example Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft4/anyOf.md A JSON schema demonstrating 'anyOf' with an integer type and a minimum value constraint. ```json { "anyOf": [{ "type": "integer" }, { "minimum": 2 }] } ``` ```javascript 'use strict' const ref0 = function validate(data) { const sub0 = (() => { if (!Number.isInteger(data)) return false return true })() if (!sub0) { const sub1 = (() => { if (typeof data === "number") { if (!(2 <= data)) return false } return true })() if (!sub1) return false } return true }; return ref0 ``` -------------------------------- ### Schema Example 4 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/ref.md Demonstrates using a URN as a base URI with a JSON pointer in $ref. ```json { "$comment": "URIs do not have to have HTTP(s) schemes", "$id": "urn:uuid:deadbeef-1234-00ff-ff00-4321feebdaed", "properties": { "foo": { "$ref": "#/$defs/bar" } }, "$defs": { "bar": { "type": "string" } } } ``` -------------------------------- ### Reference Example Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft6/ref.md This snippet demonstrates how references are handled within JSON Schema validation. ```javascript const ref6 = function validate(data) { if (!(typeof data === "string")) return false return true }; const ref5 = function validate(data) { if (!(typeof data === "object" && data && !Array.isArray(data))) return false for (const key of Object.keys(data)) { if (!validate(data[key])) return false } return true }; const ref4 = function validate(data) { if (!Array.isArray(data)) return false if (data.length < 1) return false for (let l = 0; l < data.length; l++) { if (data[l] !== undefined && hasOwn(data, l)) { if (!validate(data[l])) return false } } if (!unique(data)) return false return true }; const ref3 = function validate(data) { if (!(typeof data === "object" && data && !Array.isArray(data))) return false for (const key of Object.keys(data)) { if (!validate(data[key])) return false } return true }; const ref2 = function validate(data) { if (!(typeof data === "object" && data && !Array.isArray(data))) return false for (const key of Object.keys(data)) { if (!validate(data[key])) return false } return true }; const ref1 = function validate(data) { if (!(typeof data === "object" && data && !Array.isArray(data))) return false for (const key of Object.keys(data)) { if (key === "$ref") { if (!ref6(data[key])) return false } if (key === "$schema") { if (!ref6(data[key])) return false } if (key === "title") { if (!ref6(data[key])) return false } if (key === "description") { if (!ref6(data[key])) return false } if (key === "default") { if (!ref6(data[key])) return false } if (key === "readOnly") { if (!ref6(data[key])) return false } if (key === "writeOnly") { if (!ref6(data[key])) return false } if (key === "examples") { if (!ref4(data[key])) return false } if (key === "multipleOf") { if (!ref6(data[key])) return false } if (key === "maximum") { if (!ref6(data[key])) return false } if (key === "exclusiveMaximum") { if (!ref6(data[key])) return false } if (key === "minimum") { if (!ref6(data[key])) return false } if (key === "exclusiveMinimum") { if (!ref6(data[key])) return false } if (key === "maxLength") { if (!ref6(data[key])) return false } if (key === "minLength") { if (!ref6(data[key])) return false } if (key === "pattern") { if (!ref6(data[key])) return false } if (key === "additionalItems") { if (!validate(data[key])) return false } if (key === "items") { if (!validate(data[key])) return false } if (key === "contains") { if (!validate(data[key])) return false } if (key === "additionalProperties") { if (!validate(data[key])) return false } if (key === "properties") { if (!ref3(data[key])) return false } if (key === "patternProperties") { if (!ref2(data[key])) return false } if (key === "dependencies") { if (!ref5(data[key])) return false } if (key === "propertyNames") { if (!validate(data[key])) return false } if (key === "enum") { if (!Array.isArray(data[key])) return false if (data[key].length < 1) return false if (!unique(data[key])) return false } if (key === "type") { const sub4 = (() => { if (!ref6(data[key])) return false return true })() if (!sub4) { const sub5 = (() => { if (!Array.isArray(data[key])) return false if (data[key].length < 1) return false for (let l = 0; l < data[key].length; l++) { if (data[key][l] !== undefined && hasOwn(data[key], l)) { if (!ref6(data[key][l])) return false } } if (!unique(data[key])) return false return true })() if (!sub5) return false } } if (key === "format") { if (!(typeof data[key] === "string")) return false } if (key === "allOf") { if (!ref4(data[key])) return false } if (key === "anyOf") { if (!ref4(data[key])) return false } if (key === "oneOf") { if (!ref4(data[key])) return false } if (key === "not") { if (!validate(data[key])) return false } } return true }; const ref0 = function validate(data) { if (!ref1(data)) return false return true }; return ref0 ``` -------------------------------- ### Dynamic Reference Example 1 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2020-12/dynamicRef.md JavaScript code demonstrating dynamic references with multiple paths. ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref2 = function validate(data, dynAnchors) { if (!(typeof data === "number")) return false return true }; const ref4 = function validate(data, dynAnchors) { return true }; const dynamicResolve = (anchors, id) => (anchors.filter((x) => x[id])[0] || {})[id]; const ref3 = function validate(data, dynAnchors = []) { const dynLocal = [{}] dynLocal[0]["#itemType"] = ref4 if (typeof data === "object" && data && !Array.isArray(data)) { if (data.list !== undefined && hasOwn(data, "list")) { if (Array.isArray(data.list)) { for (let i = 0; i < data.list.length; i++) { if (data.list[i] !== undefined && hasOwn(data.list, i)) { if (!(dynamicResolve(dynAnchors || [], "#itemType") || ref4)(data.list[i], [...dynAnchors, dynLocal[0] || []])) return false } } } } } return true }; const ref1 = function validate(data, dynAnchors = []) { const dynLocal = [{}] dynLocal[0]["#itemType"] = ref2 if (!ref3(data, [...dynAnchors, dynLocal[0] || []])) return false return true }; const ref6 = function validate(data, dynAnchors) { if (!(typeof data === "string")) return false return true }; const ref5 = function validate(data, dynAnchors = []) { const dynLocal = [{}] dynLocal[0]["#itemType"] = ref6 if (!ref3(data, [...dynAnchors, dynLocal[0] || []])) return false return true }; const ref0 = function validate(data, dynAnchors = []) { const dynLocal = [{}] const sub0 = (() => { if (typeof data === "object" && data && !Array.isArray(data)) { if (!(data.kindOfList !== undefined && hasOwn(data, "kindOfList"))) return false if (!(data.kindOfList === "numbers")) return false } return true })() if (sub0) { if (!ref1(data, [...dynAnchors, dynLocal[0] || []])) return false } else { if (!ref5(data, [...dynAnchors, dynLocal[0] || []])) return false } return true }; return ref0 ``` -------------------------------- ### Validation Code Example 1 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/ref.md JavaScript code generated for validating the schema above. ```javascript 'use strict' const ref1 = function validate(data) { if (typeof data === "number") { if (!(10 >= data)) return false } return true }; const ref0 = function validate(data) { if (!ref1(data)) return false return true }; return ref0 ``` -------------------------------- ### Unanchored Pattern Schema Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/pattern.md Example schema demonstrating a 'pattern' keyword with an unanchored regex. ```json { "pattern": "a+" } ``` -------------------------------- ### Location-independent identifier Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft4/ref.md This example demonstrates using a location-independent identifier ('#foo') for schema references. ```json { "allOf": [{ "$ref": "#foo" }], "definitions": { "A": { "id": "#foo", "type": "integer" } } } ``` ```javascript 'use strict' const ref1 = function validate(data) { if (!Number.isInteger(data)) return false return true }; const ref0 = function validate(data) { if (!ref1(data)) return false return true }; return ref0 ``` -------------------------------- ### Code Example 3 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2020-12/dynamicRef.md JavaScript code for dynamic resolution, corresponding to the schema with a non-matching $dynamicAnchor. ```javascript 'use strict' const ref1 = function validate(data, dynAnchors) { if (!(typeof data === "string")) return false return true }; const ref3 = function validate(data, dynAnchors) { return true }; const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const dynamicResolve = (anchors, id) => (anchors.filter((x) => x[id])[0] || {})[id]; const ref2 = function validate(data, dynAnchors = []) { const dynLocal = [{}] dynLocal[0]["#foo"] = ref3 if (!Array.isArray(data)) return false for (let i = 0; i < data.length; i++) { if (data[i] !== undefined && hasOwn(data, i)) { if (!ref3(data[i], [...dynAnchors, dynLocal[0] || []])) return false } } return true }; const ref0 = function validate(data, dynAnchors = []) { const dynLocal = [{}] dynLocal[0]["#items"] = ref1 if (!ref2(data, [...dynAnchors, dynLocal[0] || []])) return false return true }; return ref0 ``` -------------------------------- ### regexes are not anchored by default and are case sensitive Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2019-09/patternProperties.md This example illustrates that regexes in patternProperties are not anchored by default and are case-sensitive. It validates properties that consist of two or more digits as booleans, and properties starting with 'X_' as strings. ```json { "patternProperties": { "[0-9]{2,}": { "type": "boolean" }, "X_": { "type": "string" } } } ``` ```javascript 'use strict' const pattern0 = new RegExp("[0-9]{2,}", "u"); const ref0 = function validate(data) { if (typeof data === "object" && data && !Array.isArray(data)) { for (const key0 of Object.keys(data)) { if (pattern0.test(key0)) { if (!(typeof data[key0] === "boolean")) return false } if (key0.includes("X_")) { if (!(typeof data[key0] === "string")) return false } } } return true }; return ref0 ``` -------------------------------- ### Example 2 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/unevaluatedItems.md This example shows how unevaluatedItems can be used to validate data that is not covered by other keywords in the schema. ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref0 = function validate(data) { validate.evaluatedDynamic = null const evaluatedItem0 = [] const evaluatedItems0 = [0] const evaluatedProps0 = [[], []] if (Array.isArray(data)) { let passes0 = 0 for (let i = 0; i < data.length; i++) { const sub0 = (() => { if (data[i] !== undefined && hasOwn(data, i)) { if (!(typeof data[i] === "string")) return false } return true })() if (sub0) { passes0++ evaluatedItem0.push(i) } } if (passes0 < 1) return false } if (typeof data === "object" && data && !Array.isArray(data)) { let passes1 = 0 for (const key0 of Object.keys(data)) { const sub1 = (() => { if (!(typeof data[key0] === "string")) return false return true })() if (sub1) { passes1++ evaluatedProps0[0].push(key0) } } if (passes1 < 1) return false } if (Array.isArray(data)) { for (let j = Math.max(1, ...evaluatedItems0); j < data.length; j++) { if (evaluatedItem0.includes(j)) continue if (data[j] !== undefined && hasOwn(data, j)) return false } } validate.evaluatedDynamic = [evaluatedItem0, evaluatedItems0, evaluatedProps0] return true }; return ref0 ``` -------------------------------- ### Enabling Strong Mode Source: https://github.com/exodusoss/schemasafe/blob/master/doc/Strong-mode.md Example of how to enable strong mode using the `mode` option. ```yaml mode: "strong" ``` -------------------------------- ### Example 1 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/unevaluatedItems.md This example shows how unevaluatedItems can be used to validate data that is not covered by other keywords in the schema. ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref0 = function validate(data) { if (typeof data === "object" && data && !Array.isArray(data)) { if (data.foo !== undefined && hasOwn(data, "foo")) { if (Array.isArray(data.foo)) { if (data.foo[0] !== undefined && hasOwn(data.foo, 0)) { if (!(typeof data.foo[0] === "string")) return false } } if (Array.isArray(data.foo)) { if (data.foo.length > 1) return false } } } if (typeof data === "object" && data && !Array.isArray(data)) { if (data.foo !== undefined && hasOwn(data, "foo")) { if (Array.isArray(data.foo)) { if (data.foo[1] !== undefined && hasOwn(data.foo, 1)) { if (!(typeof data.foo[1] === "string")) return false } } } } return true }; return ref0 ``` -------------------------------- ### items validation adjusts the starting index for additionalItems Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2019-09/additionalItems.md Demonstrates how the 'items' array definition influences the starting index for 'additionalItems' validation. ```json { "items": [ { "type": "string" } ], "additionalItems": { "type": "integer" } } ``` -------------------------------- ### multiple simultaneous patternProperties are validated Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft4/patternProperties.md This code snippet shows a schema with multiple simultaneous patternProperties, where properties starting with 'a*' must be integers, and properties starting with 'aaa*' must have a maximum value of 20. ```json { "patternProperties": { "a*": { "type": "integer" }, "aaa*": { "maximum": 20 } } } ``` -------------------------------- ### Development environment setup Source: https://github.com/exodusoss/schemasafe/blob/master/README.md Commands to set up a local development environment for contributing to the schemasafe project. ```shell git clone https://github.com/ExodusMovement/schemasafe cd schemasafe git submodule update --init --recursive yarn yarn lint yarn test ``` -------------------------------- ### by number Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/multipleOf.md Example of using multipleOf with a non-integer number. ```json { "multipleOf": 1.5 } ``` ```javascript 'use strict' const ref0 = function validate(data) { if (typeof data === "number") { if (data % 1.5 !== 0) return false } return true }; return ref0 ``` -------------------------------- ### Example of using useDefaults Source: https://github.com/exodusoss/schemasafe/blob/master/doc/Defaults-assigning.md Demonstrates how to enable default value assignment during parsing and shows the output for both existing and missing properties. ```javascript 'use strict' const { parser } = require('@exodus/schemasafe') const schema = { $schema: 'https://json-schema.org/draft/2019-09/schema', type: 'object', required: [], properties: { bar: { type: 'number', default: 0 } }, additionalProperties: false, } const parse = parser(schema, { useDefaults: true }) console.log(parse('{"bar": 9}')) // { valid: true, value: { bar: 3 } } console.log(parse('{}')) // { valid: true, value: { bar: 0 } } ``` -------------------------------- ### by int Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/multipleOf.md Example of using multipleOf with an integer divisor. ```json { "multipleOf": 2 } ``` ```javascript 'use strict' const ref0 = function validate(data) { if (typeof data === "number") { if (data % 2 !== 0) return false } return true }; return ref0 ``` -------------------------------- ### Basic Span Creation Source: https://github.com/exodusoss/schemasafe/blob/master/test/tracing.input.txt Demonstrates the fundamental way to create a span for tracing a specific operation. ```go package main import ( "context" "log" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" stdout "io.opentelemetry.io/otel/sdk/trace/stdout" ) func main() { ctx := context.Background() // Configure a simple stdout exporter exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { log.Fatal(err) } // Configure a tracer provider tracerProvider := tracesdk.NewTracerProvider( tracesdk.WithBatcher(exporter), tracesdk.WithResource(resource.NewWithAttributes( resource.Default(), attribute.String("service.name", "my-service"), )), ) defer func() { if err := tracerProvider.Shutdown(ctx); err != nil { log.Printf("Error shutting down tracer provider: %v", err) } }() // Set the global tracer provider otel.SetTracerProvider(tracerProvider) // Get a tracer instance tracer := otel.Tracer("my-component") // Start a new span ctx, span := tracer.Start(ctx, "my-operation") defer span.End() // Add attributes to the span span.SetAttributes(attribute.String("key", "value")) // Simulate some work log.Println("Performing operation...") // End the span (implicitly called by defer) log.Println("Operation finished.") } ``` -------------------------------- ### unevaluatedItems with $ref Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/unevaluatedItems.md Example of `unevaluatedItems: false` used with a `$ref`. ```json { "$ref": "#/$defs/bar", "prefixItems": [{ "type": "string" }], "unevaluatedItems": false, "$defs": { "bar": { "prefixItems": [true, { "type": "string" }] } } } ``` ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref1 = function validate(data) { if (Array.isArray(data)) { if (data[1] !== undefined && hasOwn(data, 1)) { if (!(typeof data[1] === "string")) return false } } return true }; const ref0 = function validate(data) { if (!ref1(data)) return false if (Array.isArray(data)) { if (data[0] !== undefined && hasOwn(data, 0)) { if (!(typeof data[0] === "string")) return false } } if (Array.isArray(data)) { if (data.length > 2) return false } return true }; return ref0 ``` -------------------------------- ### not Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/not.md This example demonstrates a basic `not` schema that validates if the data is not an integer. ```json { "not": { "type": "integer" } } ``` ```javascript 'use strict' const ref0 = function validate(data) { const sub0 = (() => { if (!Number.isInteger(data)) return false return true })() if (sub0) return false return true }; return ref0 ``` -------------------------------- ### items + contains Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/contains.md Example combining 'items' and 'contains' keywords. ```json { "additionalProperties": { "multipleOf": 2 }, "items": { "multipleOf": 2 }, "contains": { "multipleOf": 3 } } ``` ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref0 = function validate(data) { if (Array.isArray(data)) { for (let i = 0; i < data.length; i++) { if (data[i] !== undefined && hasOwn(data, i)) { if (typeof data[i] === "number") { if (data[i] % 2 !== 0) return false } } } let passes0 = 0 for (let j = 0; j < data.length; j++) { const sub0 = (() => { if (data[j] !== undefined && hasOwn(data, j)) { if (typeof data[j] === "number") { if (data[j] % 3 !== 0) return false } } return true })() if (sub0) passes0++ } if (passes0 < 1) return false } if (typeof data === "object" && data && !Array.isArray(data)) { for (const key0 of Object.keys(data)) { if (typeof data[key0] === "number") { if (data[key0] % 2 !== 0) return false } } let passes1 = 0 for (const key1 of Object.keys(data)) { const sub1 = (() => { if (typeof data[key1] === "number") { if (data[key1] % 3 !== 0) return false } return true })() if (sub1) passes1++ } if (passes1 < 1) return false } return true }; return ref0 ``` -------------------------------- ### Schema Example 1 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2020-12/dynamicRef.md JSON Schema demonstrating a $dynamicRef without a matching $dynamicAnchor in the same resource, behaving like a normal $ref. ```json { "$id": "https://test.json-schema.org/dynamic-resolution-without-bookend/root", "$ref": "list", "$defs": { "foo": { "$dynamicAnchor": "items", "type": "string" }, "list": { "$id": "list", "type": "array", "items": { "$dynamicRef": "#items" }, "$defs": { "items": { "$comment": "This is only needed to give the reference somewhere to resolve to when it behaves like $ref", "$anchor": "items" } } } } } ``` -------------------------------- ### External Schemas Source: https://github.com/exodusoss/schemasafe/blob/master/README.md Example of using external schemas with the validator. ```js const ext = { type: 'string' } const schema = { $ref: 'ext#' // references another schema called ext } // pass the external schemas as an option const validate = validator(schema, { schemas: { ext: ext }}) console.log(validate('hello')) // true console.log(validate(42)) // false ``` -------------------------------- ### Linter Usage Example Source: https://github.com/exodusoss/schemasafe/blob/master/doc/Linter.md Demonstrates how to use the linter mode to collect and log all schema errors from multiple JSON files in a directory. ```cjs const { lint } = require('@exodus/schemasafe') const fs = require('fs') const path = require('path') const dir = 'schemas/json' const files = fs.readdirSync(dir).sort().map(x => path.join(dir, x)) const schemas = files.map(x => [x, JSON.parse(fs.readFileSync(x, 'utf-8'))]) for (const [name, schema] of schemas) { const errors = lint(schema) // lint(schema, { mode: 'strong' }) for (const e of errors) { console.log(`${name}: ${e.message}`) } } ``` -------------------------------- ### unevaluatedItems with tuple Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2019-09/unevaluatedItems.md This example shows unevaluatedItems: false combined with a tuple. ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty) const ref0 = function validate(data) { if (Array.isArray(data)) { if (data[0] !== undefined && hasOwn(data, 0)) { if (!(typeof data[0] === "string")) return false } } if (Array.isArray(data)) { if (data.length > 1) return false } return true }; return ref0 ``` -------------------------------- ### Tracing with SchemaSafe Source: https://github.com/exodusoss/schemasafe/blob/master/test/tracing.input.txt Illustrates how SchemaSafe integrates with OpenTelemetry for tracing, showing how to create spans for validation and transformation steps. ```go package main import ( "context" "encoding/json" "log" "github.com/exodusoss/schemasafe" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" stdout "io.opentelemetry.io/otel/sdk/trace/stdout" ) func main() { ctx := context.Background() // --- OpenTelemetry Setup (same as previous example) --- exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { log.Fatal(err) } tracerProvider := tracesdk.NewTracerProvider( tracesdk.WithBatcher(exporter), tracesdk.WithResource(resource.NewWithAttributes( resource.Default(), attribute.String("service.name", "schemasafe-example"), )), ) defer func() { if err := tracerProvider.Shutdown(ctx); err != nil { log.Printf("Error shutting down tracer provider: %v", err) } }() otel.SetTracerProvider(tracerProvider) // ----------------------------------------------------- // Get a tracer instance for SchemaSafe operations sssTracer := otel.Tracer("github.com/exodusoss/schemasafe") // Example data data := map[string]interface{}{ "name": "Alice", "age": 30, } // Validate data using SchemaSafe with tracing // The 'Validate' function will automatically create spans if tracing is enabled. // We can also manually create spans around the validation call for more context. ctx, validationSpan := sssTracer.Start(ctx, "SchemaSafe Validation") defer validationSpan.End() // Assume 'schema' is a valid SchemaSafe schema object // For demonstration, we'll use a simple JSON schema schemaJSON := `{ "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer", "minimum": 0} }, "required": ["name", "age"] }` svar schema schemasafe.Schema err = json.Unmarshal([]byte(schemaJSON), &schema) if err != nil { log.Fatalf("Failed to unmarshal schema: %v", err) } // Perform validation validationResult, err := schema.Validate(ctx, data) if err != nil { validationSpan.RecordError(err) log.Printf("Validation error: %v\n", err) } else { log.Printf("Validation successful: %v\n", validationResult) } // Example of transformation (if applicable) // ctx, transformSpan := sssTracer.Start(ctx, "SchemaSafe Transformation") // defer transformSpan.End() // transformedData, err := schema.Transform(ctx, data) // ... handle transformation ... log.Println("SchemaSafe operations completed.") } ``` -------------------------------- ### Schema example Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/unevaluatedItems.md A JSON schema demonstrating the interaction between 'contains' and 'unevaluatedItems'. ```json { "if": { "contains": { "const": "a" } }, "then": { "if": { "contains": { "const": "b" } }, "then": { "if": { "contains": { "const": "c" } } } }, "unevaluatedItems": false } ``` -------------------------------- ### additional items are allowed by default Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/prefixItems.md This example illustrates that additional items are permitted by default when using prefixItems, as long as the defined prefix items are valid. ```json { "prefixItems": [{ "type": "integer" }] } ``` ```javascript "use strict" const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref0 = function validate(data) { if (Array.isArray(data)) { if (data[0] !== undefined && hasOwn(data, 0)) { if (!(Number.isInteger(data[0]))) return false } } return true }; return ref0 ``` -------------------------------- ### by small number Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/multipleOf.md Example of using multipleOf with a small decimal number. ```json { "multipleOf": 0.0001 } ``` ```javascript 'use strict' const isMultipleOf = (value, divisor, factor, factorMultiple) => { if (value % divisor === 0) return true let multiple = value * factor if (multiple === Infinity || multiple === -Infinity) multiple = value if (multiple % factorMultiple === 0) return true const normal = Math.floor(multiple + 0.5) return normal / factor === value && normal % factorMultiple === 0 }; const ref0 = function validate(data) { if (typeof data === "number") { if (!isMultipleOf(data, 0.0001, 1e4, 1)) return false } return true }; return ref0 ``` -------------------------------- ### Schema with file URI ID resolution (*nix) Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft4/ref.md This schema uses a file URI for its 'id' and a local reference. It demonstrates how file URIs are resolved. ```json { "id": "file:///folder/file.json", "definitions": { "foo": { "type": "number" } }, "allOf": [{ "$ref": "#/definitions/foo" }] } ``` -------------------------------- ### Custom Formats - Validator Source: https://github.com/exodusoss/schemasafe/blob/master/README.md Example of adding custom formats to the validator. ```js const validate = validator({ type: 'string', format: 'no-foo' }, { formats: { 'no-foo': (str) => !str.includes('foo'), } }) console.log(validate('test')) // true console.log(validate('foo')) // false ``` -------------------------------- ### Schema with $ref to definitions and external schema Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft7/ref.md This example demonstrates referencing definitions within the same schema and referencing an external schema using a full URI. ```json { "$id": "http://example.com/a.json", "definitions": { "x": { "$id": "http://example.com/b/c.json", "not": { "definitions": { "y": { "$id": "d.json", "type": "number" } } } } }, "allOf": [{ "$ref": "http://example.com/b/d.json" }] } ``` -------------------------------- ### Validation Code Example 4 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/ref.md JavaScript code generated for validating the schema above. ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref1 = function validate(data) { if (!(typeof data === "string")) return false return true }; const ref0 = function validate(data) { if (typeof data === "object" && data && !Array.isArray(data)) { if (data.foo !== undefined && hasOwn(data, "foo")) { if (!ref1(data.foo)) return false } } return true }; return ref0 ``` -------------------------------- ### Schema Example 2 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2020-12/dynamicRef.md JSON Schema demonstrating a $dynamicRef with a non-matching $dynamicAnchor in the same resource, behaving like a normal $ref to $anchor. ```json { "$id": "https://test.json-schema.org/unmatched-dynamic-anchor/root", "$ref": "list", "$defs": { "foo": { "$dynamicAnchor": "items", "type": "string" }, "list": { "$id": "list", "type": "array", "items": { "$dynamicRef": "#items" }, "$defs": { "items": { "$comment": "This is only needed to give the reference somewhere to resolve to when it behaves like $ref", "$anchor": "items", "$dynamicAnchor": "foo" } } } } } ``` -------------------------------- ### Validation Code Example 3 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/ref.md JavaScript code generated for validating the schema above. ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref0 = function validate(data) { if (typeof data === "number") { if (!(30 <= data)) return false } if (typeof data === "object" && data && !Array.isArray(data)) { if (data.foo !== undefined && hasOwn(data, "foo")) { if (!validate(data.foo)) return false } } return true }; return ref0 ``` -------------------------------- ### anyOf Schema Example Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/anyOf.md A JSON schema demonstrating the 'anyOf' keyword, allowing a value to be either an integer or a number greater than or equal to 2. ```json { "anyOf": [{ "type": "integer" }, { "minimum": 2 }] } ``` -------------------------------- ### Invalid string value for default Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/default.md Shows a schema where the 'default' string value does not meet the 'minLength' requirement. ```json { "properties": { "bar": { "type": "string", "minLength": 4, "default": "bad" } } } ``` ```javascript 'use strict' const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const stringLength = (string) => /\uD800-\uDFFF/.test(string) ? [...string].length : string.length; const ref0 = function validate(data) { if (typeof data === "object" && data && !Array.isArray(data)) { if (data.bar !== undefined && hasOwn(data, "bar")) { if (!(typeof data.bar === "string")) return false if (data.bar.length < 4 || stringLength(data.bar) < 4) return false } } return true }; return ref0 ``` -------------------------------- ### Code Example 2 Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft2020-12/dynamicRef.md JavaScript code for dynamic resolution, corresponding to the schema where $dynamicRef behaves like $ref. ```javascript 'use strict' const ref1 = function validate(data, dynAnchors) { if (!(typeof data === "string")) return false return true }; const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty); const ref3 = function validate(data, dynAnchors) { return true }; const dynamicResolve = (anchors, id) => (anchors.filter((x) => x[id])[0] || {})[id]; const ref2 = function validate(data, dynAnchors) { if (!Array.isArray(data)) return false for (let i = 0; i < data.length; i++) { if (data[i] !== undefined && hasOwn(data, i)) { if (!ref3(data[i], dynAnchors)) return false } } return true }; const ref0 = function validate(data, dynAnchors = []) { const dynLocal = [{}] dynLocal[0]["#items"] = ref1 if (!ref2(data, [...dynAnchors, dynLocal[0] || []])) return false return true }; return ref0 ``` -------------------------------- ### Nested anyOf Example Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/anyOf.md A JSON schema demonstrating nested 'anyOf' keywords to check validation semantics. ```json { "anyOf": [{ "anyOf": [{ "type": "null" }] }] } ``` -------------------------------- ### small multiple of large integer Source: https://github.com/exodusoss/schemasafe/blob/master/doc/samples/draft-next/multipleOf.md Example with a small multiple of a large integer. ```json { "type": "integer", "multipleOf": 1e-8 } ``` ```javascript 'use strict' const isMultipleOf = (value, divisor, factor, factorMultiple) => { if (value % divisor === 0) return true let multiple = value * factor if (multiple === Infinity || multiple === -Infinity) multiple = value if (multiple % factorMultiple === 0) return true const normal = Math.floor(multiple + 0.5) return normal / factor === value && normal % factorMultiple === 0 }; const ref0 = function validate(data) { if (!Number.isInteger(data)) return false if (!isMultipleOf(data, 1e-8, 1e8, 1)) return false return true }; return ref0 ```