Repository: pandastrike/jsck
Branch: master
Commit: 944aa33bcc30
Files: 107
Total size: 145.5 KB
Directory structure:
gitextract__sjmg1fx/
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── benchmarks/
│ ├── benchmark.coffee
│ ├── draft3/
│ │ ├── index.coffee
│ │ ├── medium/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ └── valid_doc.coffee
│ │ ├── trivial/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ └── valid_doc.coffee
│ │ └── validators.coffee
│ ├── draft4/
│ │ ├── complex/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ ├── test.coffee
│ │ │ └── valid_doc.coffee
│ │ ├── index.coffee
│ │ ├── medium/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ └── valid_doc.coffee
│ │ ├── trivial/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ └── valid_doc.coffee
│ │ └── validators.coffee
│ ├── index.coffee
│ ├── results/
│ │ └── all.txt
│ ├── runner.coffee
│ └── statistics.js
├── doc/
│ ├── README.pfm.md
│ ├── benchmarks.md
│ ├── benchmarks.pfm.md
│ ├── tests.md
│ └── tests.pfm.md
├── examples/
│ └── draft4/
│ ├── advanced.coffee
│ └── basic.coffee
├── package.json
├── schemas/
│ ├── draft-03/
│ │ └── schema.json
│ └── draft-04/
│ └── schema.json
├── src/
│ ├── common/
│ │ ├── arrays.coffee
│ │ ├── comparison.coffee
│ │ ├── numeric.coffee
│ │ ├── objects.coffee
│ │ ├── strings.coffee
│ │ └── type.coffee
│ ├── draft3/
│ │ ├── logical.coffee
│ │ ├── numeric.coffee
│ │ ├── objects.coffee
│ │ └── strings.coffee
│ ├── draft3.coffee
│ ├── draft4/
│ │ ├── logical.coffee
│ │ ├── numeric.coffee
│ │ ├── objects.coffee
│ │ ├── strings.coffee
│ │ └── type.coffee
│ ├── draft4.coffee
│ ├── index.coffee
│ ├── uri.coffee
│ ├── util.coffee
│ └── validator.coffee
├── tasks/
│ ├── build/
│ │ ├── benchmarks.coffee
│ │ ├── browser.coffee
│ │ ├── docs.coffee
│ │ ├── index.coffee
│ │ └── javascript.coffee
│ ├── helpers.coffee
│ ├── update/
│ │ ├── index.coffee
│ │ └── submodules.coffee
│ └── watch/
│ ├── docs.coffee
│ └── src.coffee
└── test/
├── .gitignore
├── draft3/
│ ├── builtins.coffee
│ ├── index.coffee
│ ├── official.coffee
│ └── unit/
│ ├── errors.coffee
│ ├── index.coffee
│ ├── references.coffee
│ └── uri_test.coffee
├── draft4/
│ ├── adhoc.coffee
│ ├── builtins.coffee
│ ├── index.coffee
│ ├── invalid/
│ │ ├── additionalItems.coffee
│ │ ├── additionalProperties.coffee
│ │ ├── allOf.coffee
│ │ ├── anyOf.coffee
│ │ ├── dependencies.coffee
│ │ ├── items.coffee
│ │ ├── not.coffee
│ │ ├── numbers.coffee
│ │ ├── oneOf.coffee
│ │ ├── patternProperties.coffee
│ │ ├── properties.coffee
│ │ ├── required.coffee
│ │ ├── strings.coffee
│ │ └── type.coffee
│ ├── invalid.coffee
│ ├── official.coffee
│ ├── unit/
│ │ ├── errors.coffee
│ │ ├── index.coffee
│ │ ├── logical.coffee
│ │ ├── references.coffee
│ │ └── uri_test.coffee
│ ├── valid/
│ │ ├── basic.coffee
│ │ ├── definitions.coffee
│ │ └── properties.coffee
│ └── valid.coffee
└── index.coffee
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
lib
node_modules
mine
.ruby-gemset
npm-debug.log
*.tgz
================================================
FILE: .gitmodules
================================================
[submodule "test/JSON-Schema-Test-Suite"]
path = test/JSON-Schema-Test-Suite
url = git@github.com:json-schema/JSON-Schema-Test-Suite.git
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2013 Matthew King
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# JSON Schema Compiled checK
JSCK is one of the fastest [JSON Schema](http://json-schema.org) validators for Node.js.
It supports JSON Schema drafts
[3][draft3_doc] and
[4][draft4_doc],
with a few caveats (see the [Coverage section](#coverage) below).
## Installation and Usage
Install with NPM:
npm install --save jsck
Require:
```coffee
JSCK = require "jsck"
```
JSCK can create validators from multiple schemas, but this requires that
each schema be identified with a URI in a top-level "id" field. In many
cases, only a single schema is needed, and there is no need to uniquely
identify the schema. This is the easiest way to use JSCK, as it is a
common pattern.
```coffee
# Construct a validator for a schema lacking an "id" declaration
jsck = new JSCK.draft4
type: "object"
properties:
user:
type: "object"
required: ["login"]
properties:
login:
type: "string"
pattern: "^[\\w\\d_]{3,32}$"
email:
type: "string"
format: "email"
console.log "valid document:", jsck.validate
user:
login: "matthew"
email: "matthew@pandastrike.com"
{errors} = jsck.validate
user:
login: "matthew"
email: "pandastrike.com"
console.log "invalid document:", errors
```
To use Draft 3 schemas:
```.coffee
validator = new JSCK.draft3(schema)
```
See these [advanced usage examples](examples/draft4/advanced.coffee) for help
working with multiple schemas.
## Why JSCK?
JSCK is [faster](#benchmarks) than most other JavaScript/CoffeeScript libraries
for validating JSON Schemas because it "compiles" the schemas. That is, JSCK
generates the tree of functions needed to validate a particular schema when you
construct a validator. The schema is thus traversed only during preparation, and
most of the work of interpreting the schema is done at this time, rather than
for every document submitted for validation. This minimizes the work required
during validation, which leads to substantial performance improvements over
non-compiling validators.
## Coverage
### Draft 4
JSCK passes all tests in the canonical
[JSON Schema Test Suite][canonical], except for these items:
* use of `maxLength` and `minLength` with Unicode surrogate pairs.
* `refRemote` (this is an essential feature we do plan to support)
* `ref`
* remote ref, containing refs itself
* `uniqueItems`
* `optional/zeroTerminatedFloats`
### Draft 3
Currently passing the canonical [test suite][canonical] for draft3 except for
these items:
* `refRemote`
* `ref`
* remote ref, containing refs itself
* `uniqueItems`
* `optional/zeroTerminatedFloats`
### Managing resolution scope with the "id" attribute
JSCK does not support the full range of scope manipulations suggested by JSON
Schema drafts 3 and 4. Scope manipulation is a controversial topic, and with
JSCK we have chosen to play it safe, supporting "id" declarations only in cases
that will (probably) not lead to any ambiguity. Specifically, JSCK uses "id"
declarations only in these cases:
* at the top level of a schema, to provide a namespace for schemas not loaded from URIs.
* non-JSON-pointer fragments (`"id": "#user"`), which serve merely as aliases for specific subschemas, and are thus convenient and unambiguous.
For more information on the topic of the "id" attribute and scope manipulation,
see this issue: https://github.com/json-schema/json-schema/issues/77.
## Contributing
To contribute, hack on it, or run the tests:
```shell
git clone git@github.com:pandastrike/jsck.git
cd jsck
coffee tasks/update
npm install
```
### Tests
JSCK uses the official [JSON Schema Test Suite][canonical] as well as some
custom tests. To run all tests for all versions:
coffee test
See [this document](doc/tests.md) for more information on working with JSCK tests.
## Benchmarks
JSCK has fairly comprehensive benchmarks which show it to be one of the very
fastest JSON Schema validators available for Node.js. Pull requests welcome, of
course.
Because performance varies (at very least) based on the complexity
of the schema being validated, we run benchmarks against several different
schemas, ranging from quite simple to moderately complex.
For JSON Schema Draft4, we run benchmarks against JSCK, tv4, jayschema,
z-schema, and other validators. On the
[trivial schema](benchmarks/draft4/trivial/schema.coffee),
our benchmarks produce this relative performance for these validators
(lower is better):
```coffee
ajv : 1
jsen : 2.9
is-my-json-valid : 4.4
Themis (minimal) : 5.2
Themis : 5.3
JSCK : 34.2
z-schema : 48.3
tv4 : 54.4
jayschema : 2507.4
```
For the schema of [medium complexity](benchmarks/draft4/medium/schema.coffee),
our benchmarks produce this relative performance for the tested validators
(lower is better):
```coffee
ajv : 1
is-my-json-valid : 2.8
jsen : 3.0
Themis (minimal) : 11.1
Themis : 11.6
JSCK : 22.0
tv4 : 43.4
z-schema : 46.0
jayschema : 2319.4
```
For the schema of [higher complexity](benchmarks/draft4/complex/schema.coffee),
our benchmarks produce this relative performance for the tested validators
(lower is better):
```coffee
ajv : 1
is-my-json-valid : 1.23
jsen : 1.31
Themis (minimal) : 1.7
Themis : 1.8
JSCK : 4.8
z-schema : 17.2
tv4 : 27.0
jayschema : 1215.1
```
As the complexity of the schema increases, the performance benefits of the
compilation model become more evident.
See [this document](doc/benchmarks.md) for detailed results and information on
running and creating benchmarks.
[draft3_doc]:http://tools.ietf.org/html/draft-zyp-json-schema-03
[draft3_impl]:https://github.com/json-schema/json-schema/tree/master/draft-03
[draft4_doc]:http://tools.ietf.org/html/draft-zyp-json-schema-04
[canonical]:https://github.com/json-schema/JSON-Schema-Test-Suite
================================================
FILE: benchmarks/benchmark.coffee
================================================
microtime = require "microtime"
require "./statistics"
module.exports = class Benchmark
@compare: (benchmarks, options) ->
out = {}
for benchmark in benchmarks
out[benchmark.name] = benchmark.run(options)
out
constructor: (@name, callback) ->
callback(@)
setup: (@_setup) ->
measure: (@_measure) ->
run: ({samples, warmup}) ->
console.error " #{@name}"
results = []
subject = @_setup()
if warmup
process.stderr.write " Warming up: "
for i in [1..warmup]
@_measure(subject)
process.stderr.write "."
process.stderr.write("\n")
process.stderr.write " Iterations: "
for i in [1..samples]
t0 = microtime.now()
@_measure(subject)
t1 = microtime.now()
results.push (t1 - t0) / 1000
process.stderr.write "."
console.error("\n")
results
================================================
FILE: benchmarks/draft3/index.coffee
================================================
console.log "## Benchmarks for Draft 3"
console.log
require "./trivial/"
require "./medium/"
================================================
FILE: benchmarks/draft3/medium/index.coffee
================================================
validators = require "../validators"
{benchmark} = require("../../runner")(validators)
benchmark {
name: "Configuration"
repeats: 128
schema: require "./schema"
document: require "./valid_doc"
}
================================================
FILE: benchmarks/draft3/medium/schema.coffee
================================================
module.exports =
description: "A moderately complex schema with some nesting and value constraints"
type: "object"
additionalProperties: false
properties:
api_server:
description: "Settings for the HTTP API server"
type: "object"
additionalProperties: false
required: true
properties:
url:
type: "string"
format: "uri"
required: true
host:
type: "string"
required: true
port:
type: "integer"
minimum: 1000
required: true
transport:
description: "Settings for the Redis tranport"
additionalProperties: false
required: true
properties:
server:
type: "string"
required: true
options:
type: "object"
queues:
properties:
blocking_timeout:
type: "integer"
minimum: 0
storage:
description: "Settings for the PostgreSQL storage"
required: true
properties:
server:
type: "string"
required: true
database:
type: "string"
required: true
user:
type: "string"
required: true
options:
type: "object"
chain:
description: "Settings for the Chain.com client"
required: true
properties:
api_key_id:
type: "string"
required: true
api_key_secret:
type: "string"
required: true
================================================
FILE: benchmarks/draft3/medium/valid_doc.coffee
================================================
module.exports =
api_server:
url: "http://example.com:8998"
host: "example.com"
port: 8998
transport:
server: "127.0.0.1:6381"
queues:
blocking_timeout: 0
storage:
server: "127.0.0.1:5432"
database: "thingy-test"
user: "thingy-test"
password: "password"
chain:
api_key_id: "cafebabe"
api_key_secret: "babecafe"
================================================
FILE: benchmarks/draft3/trivial/index.coffee
================================================
validators = require "../validators"
{benchmark} = require("../../runner")(validators)
benchmark {
name: "Event"
repeats: 512
schema: require "./schema"
document: require "./valid_doc"
}
================================================
FILE: benchmarks/draft3/trivial/schema.coffee
================================================
module.exports =
description: "A simple schema, exercising very few attributes"
type: "object"
additionalProperties: false
properties:
origin:
required: true
type: "string"
name:
type: "string"
required: true
tags:
type: "array"
items: {type: "string"}
timestamp:
type: "integer"
data: { type: "object" }
================================================
FILE: benchmarks/draft3/trivial/valid_doc.coffee
================================================
module.exports =
origin: "monkey"
name: "shines"
tags: ["in", "the", "closet"]
timestamp: Date.now()
data:
uno: 1
dos: 2
================================================
FILE: benchmarks/draft3/validators.coffee
================================================
module.exports =
"JSCK":
setup: (schema) ->
JSCK = require "../../src/draft3"
new JSCK(schema)
validate: ({validator, schema, document}) ->
validator.validate(document)
error: (result) ->
if result.valid == true
false
else
result.errors
"amanda":
setup: (schema) ->
amanda = require "amanda"
validate: ({validator, schema, document}) ->
result = null
validator.validate document, schema, (error) ->
result = error
result
error: (result) ->
result || false
"JSV":
setup: (schema) ->
JSV = require("JSV").JSV
jsv = JSV.createEnvironment("json-schema-draft-03")
jsv.createSchema(schema)
validate: ({validator, schema, document}) ->
validator.validate(document)
error: (result) ->
if result.errors.length == 0
false
else
result.errors
"json-gate":
setup: (schema) ->
jsonGateCreateSchema = require("json-gate").createSchema
jsonGateCreateSchema(schema)
validate: ({validator, schema, document}) ->
try
validator.validate(document)
catch e
e
error: (result) ->
result || false
================================================
FILE: benchmarks/draft4/complex/index.coffee
================================================
validators = require "../validators"
{benchmark} = require("../../runner")(validators)
benchmark {
name: "Transaction"
repeats: 64
schema: require "./schema"
document: require "./valid_doc"
}
================================================
FILE: benchmarks/draft4/complex/schema.coffee
================================================
module.exports =
type: "array"
items: {$ref: "#transaction"}
minItems: 1
definitions:
base58:
id: "#base58"
# https://en.bitcoin.it/wiki/Base58Check_encoding
type: "string"
pattern: "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$"
hex:
id: "#hex"
type: "string"
pattern: "^[0123456789A-Fa-f]+$"
tx_id:
id: "#tx_id"
allOf: [
{$ref: "#hex"}
{
minLength: 64
maxLength: 64
}
]
address:
id: "#address"
allOf: [
{$ref: "#base58"}
{
minLength: 34
maxLength: 34
}
]
signature:
id: "#signature"
allOf: [
{$ref: "#hex"}
{
minLength: 128
maxLength: 128
}
]
transaction:
id: "#transaction"
additionalProperties: false
required: ["metadata", "hash", "inputs", "outputs"]
properties:
metadata:
type: "object"
required: ["amount", "fee"]
properties:
amount:
type: "integer"
fee:
type: "integer"
multipleOf: 10000
status:
type: "string"
enum: ["unsigned", "unconfirmed", "confirmed", "invalid"]
confirmations:
type: "integer"
minimum: 0
block_time:
type: "integer"
version: {type: "integer"}
lock_time: {type: "integer"}
hash: {$ref: "#tx_id"}
inputs:
type: "array"
items: {$ref: "#input"}
minItems: 1
outputs:
type: "array"
items: {$ref: "#output"}
minItems: 1
input:
id: "#input"
type: "object"
additionalProperties: false
required: ["index", "output", "script_sig"]
properties:
index:
type: "integer"
minimum: 0
output: {$ref: "#output"}
sig_hash: {$ref: "#hex"}
script_sig: {$ref: "#hex"}
signatures:
type: "object"
description: "A dictionary of signatures. Keys represent keypair names"
minProperties: 1
maxProperties: 3
additionalProperties: {$ref: "#signature"}
output:
id: "#output"
type: "object"
additionalProperties: false
required: ["hash", "index", "value", "script"]
properties:
hash: {$ref: "#tx_id"}
index:
type: "integer"
minimum: 0
value:
type: "integer"
script:
type: "object"
properties:
type:
type: "string"
enum: ["standard", "p2sh"]
asm:
type: "string"
address: {$ref: "#address"}
metadata:
type: "object"
dependencies:
# if a wallet path is given, the metadata must also contain the
# HDW public seed values. Use case would be for verifying that
# a change output is going to a legit address.
wallet_path: ["public_seeds"]
properties:
wallet_path:
type: "string"
public_seeds:
type: "object"
minProperties: 1
maxProperties: 3
additionalProperties:
anyOf: [
{$ref: "#base58"}
{$ref: "#hex"}
]
================================================
FILE: benchmarks/draft4/complex/test.coffee
================================================
JSCK = require "../../../src/draft4"
schema = require "./schema"
valid_doc = require "./valid_doc"
schema =
type: "array"
items: {$ref: "#smurf"}
minItems: 1
definitions:
smurf:
id: "#smurf"
type: "object"
valid_doc = [
{}
]
jsck = new JSCK(schema)
report = jsck.validate(valid_doc)
console.log "JSCK"
console.log JSON.stringify(report, null, 2)
ZSchema = require("z-schema")
validator = new ZSchema()
console.log "ZSchema"
console.log validator.validate(valid_doc, schema)
console.log validator.getLastErrors()
console.log "JSONSchema"
JSONSchema = require('jsonschema').Validator
validator = new JSONSchema()
{errors} = validator.validate(valid_doc, schema)
console.log errors
================================================
FILE: benchmarks/draft4/complex/valid_doc.coffee
================================================
$ = module.exports = []
$.push
metadata:
amount: 38043749285
fee: 2 * 10000
status: "confirmed"
confirmations: 73
block_time: 1415993584376
version: 1
lock_time: 0
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
inputs: [
index: 0
script_sig: "3046022100be69797cf5d784412b1258256eb657c191a04893479dfa2ae5c7f2088c7adbe0022100e6b000bd633b286ed1b9bc7682fe753d9fdad61fbe5da2a6e9444198e33a670f01"
signatures:
primary: "3046022100be69797cf5d784412b1258256eb657c191a04893479dfa2ae5c7f2088c7adbe0022100e6b000bd633b286ed1b9bc7682fe753d9fdad61fbe5da2a7"
cosigner: "a2ad5ebf16dadf9d357ef2867cb9b1de682b336db000b6e0012200ebda7c8802f7c5ea2afd97439840a191c756be6528521b214487d5fc79796eb00122064037"
output:
hash: "6b040cd7a4676b5c7b11f144e73c1958c177fcd79e934f6be8ce02c8cd12546d"
index: 1
value: 38043749285
script:
type: "standard"
asm: "OP_DUP OP_HASH160 7d4e6d55e1dffb0df85f509343451d170d147551 OP_EQUALVERIFY OP_CHECKSIG"
]
outputs: [
{
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
index: 0
value: 38042249285
script:
type: "standard"
asm: "OP_DUP OP_HASH160 7d4e6d55e1dffb0df85f509343451d170d147551 OP_EQUALVERIFY OP_CHECKSIG"
address: "1CRZRBwfuwUaVSPJtd6DBuezbm7XPBHLa1"
metadata:
type: "change"
wallet_path: "m/44/0/1/356"
public_seeds:
primary: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
cosigner: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
}
{
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
index: 1
value: 1500000
script:
type: "standard"
asm: "OP_DUP OP_HASH160 3bc576e6960a9d45201ba5087e39224d0a05a079 OP_EQUALVERIFY OP_CHECKSIG"
address: "16T3RPZLmxtXQCgWi1S8kef5Ca6jqXhoeT"
}
]
$.push
metadata:
amount: 38043749285
fee: 2 * 10000
status: "unconfirmed"
confirmations: 73
block_time: 1415993584376
version: 1
lock_time: 0
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
inputs: [
index: 0
script_sig: "3046022100be69797cf5d784412b1258256eb657c191a04893479dfa2ae5c7f2088c7adbe0022100e6b000bd633b286ed1b9bc7682fe753d9fdad61fbe5da2a6e9444198e33a670f01"
output:
hash: "6b040cd7a4676b5c7b11f144e73c1958c177fcd79e934f6be8ce02c8cd12546d"
index: 1
value: 38043749285
script:
type: "standard"
asm: "OP_DUP OP_HASH160 7d4e6d55e1dffb0df85f509343451d170d147551 OP_EQUALVERIFY OP_CHECKSIG"
]
outputs: [
{
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
index: 0
value: 38042249285
script:
type: "standard"
asm: "OP_DUP OP_HASH160 7d4e6d55e1dffb0df85f509343451d170d147551 OP_EQUALVERIFY OP_CHECKSIG"
address: "1CRZRBwfuwUaVSPJtd6DBuezbm7XPBHLa1"
metadata:
type: "change"
}
{
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
index: 1
value: 1500000
script:
type: "standard"
asm: "OP_DUP OP_HASH160 3bc576e6960a9d45201ba5087e39224d0a05a079 OP_EQUALVERIFY OP_CHECKSIG"
address: "16T3RPZLmxtXQCgWi1S8kef5Ca6jqXhoeT"
}
]
$.push
metadata:
amount: 38043749285
fee: 2 * 10000
status: "unconfirmed"
confirmations: 73
block_time: 1415993584376
version: 1
lock_time: 0
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
inputs: [
index: 0
script_sig: "3046022100be69797cf5d784412b1258256eb657c191a04893479dfa2ae5c7f2088c7adbe0022100e6b000bd633b286ed1b9bc7682fe753d9fdad61fbe5da2a6e9444198e33a670f01"
output:
hash: "6b040cd7a4676b5c7b11f144e73c1958c177fcd79e934f6be8ce02c8cd12546d"
index: 1
value: 38043749285
script:
type: "standard"
asm: "OP_DUP OP_HASH160 7d4e6d55e1dffb0df85f509343451d170d147551 OP_EQUALVERIFY OP_CHECKSIG"
]
outputs: [
{
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
index: 0
value: 38042249285
script:
type: "standard"
asm: "OP_DUP OP_HASH160 7d4e6d55e1dffb0df85f509343451d170d147551 OP_EQUALVERIFY OP_CHECKSIG"
address: "1CRZRBwfuwUaVSPJtd6DBuezbm7XPBHLa1"
metadata:
type: "change"
}
{
hash: "60c1f1a3160042152114e2bba45600a5045711c3a8a458016248acec59653471"
index: 1
value: 1500000
script:
type: "standard"
asm: "OP_DUP OP_HASH160 3bc576e6960a9d45201ba5087e39224d0a05a079 OP_EQUALVERIFY OP_CHECKSIG"
address: "16T3RPZLmxtXQCgWi1S8kef5Ca6jqXhoeT"
}
]
================================================
FILE: benchmarks/draft4/index.coffee
================================================
console.log "## Benchmarks for Draft 4"
console.log
require "./trivial/"
require "./medium/"
require "./complex/"
================================================
FILE: benchmarks/draft4/medium/index.coffee
================================================
validators = require "../validators"
{benchmark} = require("../../runner")(validators)
benchmark {
name: "Configuration"
repeats: 256
schema: require "./schema"
document: require "./valid_doc"
}
================================================
FILE: benchmarks/draft4/medium/schema.coffee
================================================
module.exports =
description: "A moderately complex schema with some nesting and value constraints"
type: "object"
additionalProperties: false
required: ["api_server", "transport", "storage", "chain"]
properties:
api_server:
description: "Settings for the HTTP API server"
type: "object"
additionalProperties: false
required: ["url", "host", "port"]
properties:
url:
type: "string"
format: "uri"
host:
type: "string"
port:
type: "integer"
minimum: 1000
transport:
description: "Settings for the Redis tranport"
additionalProperties: false
required: ["server"]
properties:
server:
type: "string"
options:
type: "object"
queues:
properties:
blocking_timeout:
type: "integer"
minimum: 0
storage:
description: "Settings for the PostgreSQL storage"
required: ["server", "database", "user"]
properties:
server:
type: "string"
database:
type: "string"
user:
type: "string"
options:
type: "object"
chain:
description: "Settings for the Chain.com client"
required: ["api_key_id", "api_key_secret"]
properties:
api_key_id:
type: "string"
api_key_secret:
type: "string"
================================================
FILE: benchmarks/draft4/medium/valid_doc.coffee
================================================
module.exports =
api_server:
url: "http://example.com:8998"
host: "example.com"
port: 8998
transport:
server: "127.0.0.1:6381"
queues:
blocking_timeout: 0
storage:
server: "127.0.0.1:5432"
database: "thingy-test"
user: "thingy-test"
password: "password"
chain:
api_key_id: "cafebabe"
api_key_secret: "babecafe"
================================================
FILE: benchmarks/draft4/trivial/index.coffee
================================================
validators = require "../validators"
{benchmark} = require("../../runner")(validators)
benchmark {
name: "Event - Valid document"
repeats: 1024
schema: require "./schema"
document: require "./valid_doc"
}
================================================
FILE: benchmarks/draft4/trivial/schema.coffee
================================================
module.exports =
description: "A simple schema, exercising very few attributes"
type: "object"
additionalProperties: false
required: ["origin", "name", "tags", "timestamp", "data"]
properties:
origin:
type: "string"
name:
type: "string"
tags:
type: "array"
items: {type: "string"}
timestamp:
type: "integer"
data: { type: "object" }
================================================
FILE: benchmarks/draft4/trivial/valid_doc.coffee
================================================
module.exports =
origin: "monkey"
name: "shines"
tags: ["in", "the", "closet"]
timestamp: Date.now()
data:
uno: 1
dos: 2
================================================
FILE: benchmarks/draft4/validators.coffee
================================================
module.exports =
"JSCK":
setup: (schema) ->
JSCK = require "../../src/draft4"
new JSCK(schema)
validate: ({validator, schema, document}) ->
validator.validate(document)
error: (result) ->
if result.valid == true
false
else
result.errors
"ajv":
setup: (schema) ->
ajv = require("ajv")()
ajv.compile(schema)
validate: ({validator, schema, document}) ->
validator(document)
error: (result) ->
if result == true
false
else
validator.errors
"tv4":
setup: (schema) ->
require("tv4").tv4
validate: ({validator, schema, document}) ->
validator.validateResult(document, schema)
error: (result) ->
if result.valid == true
false
else
result.error
"Themis (minimal)":
setup: (schema) ->
Themis = require('themis')
Themis.validator(schema, {
enable_defaults: false,
algorithm: 'none',
errors: { messages: false, validator_value: false, schema: false } })
validate: ({validator, schema, document}) ->
validator(document, '0')
error: (result) ->
if result.valid == true
false
else
result.errors
"Themis":
setup: (schema) ->
Themis = require('themis')
Themis.validator(schema)
validate: ({validator, schema, document}) ->
validator(document, '0')
error: (result) ->
if result.valid == true
false
else
result.errors
"jayschema":
setup: (schema) ->
JaySchema = require("jayschema")
new JaySchema()
validate: ({validator, schema, document}) ->
validator.validate(document, schema)
error: (result) ->
if result.length == 0
false
else
result
"is-my-json-valid":
setup: (schema) ->
require("is-my-json-valid")(schema)
validate: ({validator, schema, document}) ->
validator(document)
error: (result) ->
if result == true
false
else
validator.errors
# Putting z-schema last because it appears to be affecting other libs'
# performance if run first.
"z-schema":
setup: (schema) ->
z = require("z-schema")
validator = new z()
url = "http://json-schema.org/draft-04/schema"
# don't actually download the draft, because GitHub Pages might be down.
actualDraft = require("fs").readFileSync("./test/json-schema/draft-04/schema", "utf8")
validator.setRemoteReference(url, JSON.parse(actualDraft))
validator
validate: ({validator, schema, document}) ->
valid = validator.validate(document, schema)
if valid == false
validator.getLastErrors()
error: (result) ->
result || false
"jsen":
setup: (schema) ->
require("jsen")(schema)
validate: ({validator, schema, document}) ->
validator(document)
error: (result) ->
if result == true
false
else
validator.errors
================================================
FILE: benchmarks/index.coffee
================================================
require "./draft3/"
require "./draft4/"
================================================
FILE: benchmarks/results/all.txt
================================================
## Benchmarks for Draft 3
Schema: 'Event'. A simple schema, exercising very few attributes
Sample size: 64
Validations per sample: 512
JSCK
Warming up: ................................
Iterations: ................................................................
amanda
Warming up: ................................
Iterations: ................................................................
JSV
Warming up: ................................
Iterations: ................................................................
json-gate
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 193.025 max: 202.692 min: 175.945
amanda: validations/millisecond
median: 4.258 max: 4.608 min: 3.778
JSV: validations/millisecond
median: 2.685 max: 2.748 min: 2.43
json-gate: validations/millisecond
median: 82.514 max: 86.036 min: 48.234
Relative speeds:
JSCK : 1.000
json-gate : 2.339
amanda : 45.337
JSV : 71.896
Schema: 'Configuration'. A moderately complex schema with some nesting and value constraints
Sample size: 64
Validations per sample: 128
JSCK
Warming up: ................................
Iterations: ................................................................
amanda
Warming up: ................................
Iterations: ................................................................
JSV
Warming up: ................................
Iterations: ................................................................
json-gate
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 108.936 max: 109.966 min: 85.964
amanda: validations/millisecond
median: 3.788 max: 5.651 min: 2.371
JSV: validations/millisecond
median: 1.344 max: 1.359 min: 1.255
json-gate: validations/millisecond
median: 44.176 max: 45.845 min: 41.803
Relative speeds:
JSCK : 1.000
json-gate : 2.466
amanda : 28.760
JSV : 81.025
## Benchmarks for Draft 4
Schema: 'Event - Valid document'. A simple schema, exercising very few attributes
Sample size: 64
Validations per sample: 1024
JSCK
Warming up: ................................
Iterations: ................................................................
tv4
Warming up: ................................
Iterations: ................................................................
z-schema
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 186.81 max: 191.617 min: 138.716
tv4: validations/millisecond
median: 54.377 max: 55.907 min: 46.942
z-schema: validations/millisecond
median: 93.657 max: 93.979 min: 89.44
Relative speeds:
JSCK : 1.000
z-schema : 1.995
tv4 : 3.435
Schema: 'Configuration'. A moderately complex schema with some nesting and value constraints
Sample size: 64
Validations per sample: 256
JSCK
Warming up: ................................
Iterations: ................................................................
tv4
Warming up: ................................
Iterations: ................................................................
z-schema
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 118.573 max: 119.07 min: 105.22
tv4: validations/millisecond
median: 19.608 max: 20.389 min: 16.505
z-schema: validations/millisecond
median: 40.606 max: 40.934 min: 34.934
Relative speeds:
JSCK : 1.000
z-schema : 2.920
tv4 : 6.047
Schema: 'Transaction'.
Sample size: 64
Validations per sample: 64
JSCK
Warming up: ................................
Iterations: ................................................................
tv4
Warming up: ................................
Iterations: ................................................................
z-schema
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 12.814 max: 14.873 min: 6.984
tv4: validations/millisecond
median: 1.946 max: 1.965 min: 1.832
z-schema: validations/millisecond
median: 3.768 max: 3.836 min: 3.297
Relative speeds:
JSCK : 1.000
z-schema : 3.401
tv4 : 6.583
================================================
FILE: benchmarks/runner.coffee
================================================
# stdlib
util = require "util"
# our simple benchmarking library
Benchmark = require "./benchmark.coffee"
samples = 64
module.exports = (validators) ->
benchmark: ({name, schema, document, repeats}) ->
libraries = []
for library, {setup, validate, error} of validators
## preflight to check for incorrect validation errors
## Currently disabled because of issues with z-schema.
#validator = setup(schema)
#e = error(validate({validator, schema, document}))
#if e
#console.log "Aborting because #{library} declared the document invalid:"
#console.log e
#process.exit(1)
do (setup, validate) ->
libraries.push new Benchmark library, (bm) ->
bm.setup ->
setup(schema)
bm.measure (validator) ->
for i in [1..repeats]
validate({validator, schema, document})
console.log """
Schema: '#{name}'. #{schema.description || ''}
Sample size: #{samples}
Validations per sample: #{repeats}
"""
results = Benchmark.compare libraries, {samples, warmup: 32}
console.log()
x = for name, result of results
{median, max, min} = result.summarize()
console.log " #{name}: validations/millisecond"
console.log util.format " median: %d max: %d min: %d ",
(repeats * 1 / median).toFixed(3),
(repeats * 1 / min).toFixed(3),
(repeats * 1 / max).toFixed(3)
console.log()
[name, median]
sorted = x.sort (a, b) ->
a[1] > b[1]
fastest = sorted[0]
ftime = fastest[1]
console.log "Relative speeds:"
for [name, time] in x
console.log name, ":", (time / ftime).toFixed(3)
console.log()
================================================
FILE: benchmarks/statistics.js
================================================
/****************************************************************************************************
JavaScript Math and Statistics Library
@file pseudoMathStats.js
@version 1.0
@author Paul Ellis
@url http://code.google.com/p/pseudosavant
@copyright Copyright 2010, Paul Ellis
@license BSD
****************************************************************************************************/
/****************************************************************************************************
Calculates the Standard Deviation of an array
****************************************************************************************************/
Array.prototype.stdDev = function ()
{
// Calculate Mean
var mean = this.mean();
var length = this.length;
// Calculate Variance
for (var i = 0, sumOfSquares = 0; i < length; i++)
{
sumOfSquares += Math.pow(this[i] - mean, 2);
}
var stdDev = Math.sqrt(sumOfSquares / length);
return stdDev;
}
/****************************************************************************************************
Calculates the Variance of an array
****************************************************************************************************/
Array.prototype.variance = function ()
{
// Calculate Mean
var mean = this.mean();
var length = this.length;
// Calculate Variance
for (var i = 0, sumOfSquares = 0; i < length; i++)
{
sumOfSquares += Math.pow(this[i] - mean, 2);
}
var variance = sumOfSquares / length;
return variance;
}
/****************************************************************************************************
Sums all values in an array
****************************************************************************************************/
Array.prototype.sum = function ()
{
for (var i = 0, length = this.length, sum = 0; i < length; sum += this[i++]);
return sum;
}
/****************************************************************************************************
Calculates the arithmetic mean of an array
****************************************************************************************************/
Array.prototype.mean = function ()
{
for (var i = 0, length = this.length, sum = 0; i < length; sum += this[i++]);
var mean = sum / length;
return mean;
}
/****************************************************************************************************
Returns the highest numeric value of an array
****************************************************************************************************/
Array.prototype.max = function ()
{
return this.reduceRight(function(i,j){if(j<i){return i}else{return j} });
}
/****************************************************************************************************
Returns the lowest numeric value of an array
****************************************************************************************************/
Array.prototype.min = function ()
{
return this.reduceRight(function(i,j){if(j>i){return i}else{return j} });
}
/****************************************************************************************************
Calculates the median of an array
****************************************************************************************************/
Array.prototype.median = function ()
{
var length = this.length;
if (length % 2 == 1) // Odd
{
var middle = Math.floor(length / 2);
var median = this.sortNumber()[middle];
}
else // Even
{
var middle = length / 2;
var sorted = this.sortNumber();
var median = (sorted[middle-1] + sorted[middle]) / 2;
}
return median;
}
/****************************************************************************************************
Returns the array sorted ascendingly, or decendingly if sortNumber(true).
****************************************************************************************************/
Array.prototype.sortNumber = function (invert)
{
if (invert == true) // Decending
{
return this.slice().sort(function (a, b) { return a - b }).reverse(); // Using reverse() is faster than b - a.
}
else // Ascending, default
{
return this.slice().sort(function (a, b) { return a - b });
}
}
/****************************************************************************************************
Returns an object containing the most common stats for the array.
****************************************************************************************************/
Array.prototype.summarize = function summarize (places) {
if (!this.length) { return { error: "no values" }; }
return {
max: fixedDecimal(this.max(), places),
median: fixedDecimal(this.median(), places),
min: fixedDecimal(this.min(), places),
mean: fixedDecimal(this.mean(), places),
stdDev: fixedDecimal(this.stdDev(), places),
sample_size: this.length,
};
};
var fixedDecimal = function fixedDecimal (num, numOfDec) {
var pow10s = Math.pow( 10, numOfDec || 0 );
return ( numOfDec ) ? Math.round( pow10s * num ) / pow10s : num;
};
/****************************************************************************************************
Lower tail quantile for standard normal distribution function.
Written by Alankar Misra (alankar@digitalsutras.com)
Algorithm by Peter John Acklam (pjacklam@online.no, http://home.online.no/~pjacklam)
****************************************************************************************************/
function normsinv(p)
{
// Coefficients in rational approximations
var a = new Array(-3.969683028665376e+01, 2.209460984245205e+02,
-2.759285104469687e+02, 1.383577518672690e+02,
-3.066479806614716e+01, 2.506628277459239e+00);
var b = new Array(-5.447609879822406e+01, 1.615858368580409e+02,
-1.556989798598866e+02, 6.680131188771972e+01,
-1.328068155288572e+01);
var c = new Array(-7.784894002430293e-03, -3.223964580411365e-01,
-2.400758277161838e+00, -2.549732539343734e+00,
4.374664141464968e+00, 2.938163982698783e+00);
var d = new Array(7.784695709041462e-03, 3.224671290700398e-01,
2.445134137142996e+00, 3.754408661907416e+00);
// Define break-points.
var plow = 0.02425;
var phigh = 1 - plow;
// Rational approximation for lower region:
if (p < plow)
{
var q = Math.sqrt(-2 * Math.log(p));
return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
}
// Rational approximation for upper region:
if (phigh < p)
{
var q = Math.sqrt(-2 * Math.log(1 - p));
return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
}
// Rational approximation for central region:
var q = p - 0.5;
var r = q * q;
return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
}
================================================
FILE: doc/README.pfm.md
================================================
# JSON Schema Compiled checK
JSCK is one of the fastest [JSON Schema](http://json-schema.org) validators for Node.js.
It supports JSON Schema drafts
[3][draft3_doc] and
[4][draft4_doc],
with a few caveats (see the [Coverage section](#coverage) below).
## Installation and Usage
Install with NPM:
npm install --save jsck
Require:
```coffee
JSCK = require "jsck"
```
JSCK can create validators from multiple schemas, but this requires that
each schema be identified with a URI in a top-level "id" field. In many
cases, only a single schema is needed, and there is no need to uniquely
identify the schema. This is the easiest way to use JSCK, as it is a
common pattern.
```examples/draft4/basic.coffee#L3-29```
To use Draft 3 schemas:
```.coffee
validator = new JSCK.draft3(schema)
```
See these [advanced usage examples](examples/draft4/advanced.coffee) for help
working with multiple schemas.
## Why JSCK?
JSCK is [faster](#benchmarks) than most other JavaScript/CoffeeScript libraries
for validating JSON Schemas because it "compiles" the schemas. That is, JSCK
generates the tree of functions needed to validate a particular schema when you
construct a validator. The schema is thus traversed only during preparation, and
most of the work of interpreting the schema is done at this time, rather than
for every document submitted for validation. This minimizes the work required
during validation, which leads to substantial performance improvements over
non-compiling validators.
## Coverage
### Draft 4
JSCK passes all tests in the canonical
[JSON Schema Test Suite][canonical], except for these items:
* use of `maxLength` and `minLength` with Unicode surrogate pairs.
* `refRemote` (this is an essential feature we do plan to support)
* `ref`
* remote ref, containing refs itself
* `uniqueItems`
* `optional/zeroTerminatedFloats`
### Draft 3
Currently passing the canonical [test suite][canonical] for draft3 except for
these items:
* `refRemote`
* `ref`
* remote ref, containing refs itself
* `uniqueItems`
* `optional/zeroTerminatedFloats`
### Managing resolution scope with the "id" attribute
JSCK does not support the full range of scope manipulations suggested by JSON
Schema drafts 3 and 4. Scope manipulation is a controversial topic, and with
JSCK we have chosen to play it safe, supporting "id" declarations only in cases
that will (probably) not lead to any ambiguity. Specifically, JSCK uses "id"
declarations only in these cases:
* at the top level of a schema, to provide a namespace for schemas not loaded from URIs.
* non-JSON-pointer fragments (`"id": "#user"`), which serve merely as aliases for specific subschemas, and are thus convenient and unambiguous.
For more information on the topic of the "id" attribute and scope manipulation,
see this issue: https://github.com/json-schema/json-schema/issues/77.
## Contributing
To contribute, hack on it, or run the tests:
```shell
git clone git@github.com:pandastrike/jsck.git
cd jsck
coffee tasks/update
npm install
```
### Tests
JSCK uses the official [JSON Schema Test Suite][canonical] as well as some
custom tests. To run all tests for all versions:
coffee test
See [this document](doc/tests.md) for more information on working with JSCK tests.
## Benchmarks
JSCK has fairly comprehensive benchmarks which show it to be one of the very
fastest JSON Schema validators available for Node.js. Pull requests welcome, of
course.
Because performance varies (at very least) based on the complexity
of the schema being validated, we run benchmarks against several different
schemas, ranging from quite simple to moderately complex.
For JSON Schema Draft4, we run benchmarks against JSCK, tv4, jayschema,
z-schema, and other validators. On the
[trivial schema](benchmarks/draft4/trivial/schema.coffee),
our benchmarks produce this relative performance for these validators
(lower is better):
```coffee
ajv : 1
jsen : 2.9
is-my-json-valid : 4.4
Themis (minimal) : 5.2
Themis : 5.3
JSCK : 34.2
z-schema : 48.3
tv4 : 54.4
jayschema : 2507.4
```
For the schema of [medium complexity](benchmarks/draft4/medium/schema.coffee),
our benchmarks produce this relative performance for the tested validators
(lower is better):
```coffee
ajv : 1
is-my-json-valid : 2.8
jsen : 3.0
Themis (minimal) : 11.1
Themis : 11.6
JSCK : 22.0
tv4 : 43.4
z-schema : 46.0
jayschema : 2319.4
```
For the schema of [higher complexity](benchmarks/draft4/complex/schema.coffee),
our benchmarks produce this relative performance for the tested validators
(lower is better):
```coffee
ajv : 1
is-my-json-valid : 1.23
jsen : 1.31
Themis (minimal) : 1.7
Themis : 1.8
JSCK : 4.8
z-schema : 17.2
tv4 : 27.0
jayschema : 1215.1
```
As the complexity of the schema increases, the performance benefits of the
compilation model become more evident.
See [this document](doc/benchmarks.md) for detailed results and information on
running and creating benchmarks.
[draft3_doc]:http://tools.ietf.org/html/draft-zyp-json-schema-03
[draft3_impl]:https://github.com/json-schema/json-schema/tree/master/draft-03
[draft4_doc]:http://tools.ietf.org/html/draft-zyp-json-schema-04
[canonical]:https://github.com/json-schema/JSON-Schema-Test-Suite
================================================
FILE: doc/benchmarks.md
================================================
# Benchmarks
JSCK has fairly comprehensive benchmarks which show it to be one of the fastest
JSON Schema validators available for Node.js.
To run the benchmarks immediately after you `git clone` the repo:
```shell
git clone git@github.com:pandastrike/jsck.git && \
git submodule update --init && \
npm install && \
coffee benchmarks/
```
Benchmarking harness and content lives in `benchmarks`, with separate
directories for each draft. For each draft there are a number of different
benchmarks (contained in subdirectories) that can be run in whole or in part.
This is the basic directory structure:
benchmarks/
draft3/
draft4/
complex/
medium/
trivial/
index.coffee
validators.coffee
## Adding Additional Validators
The validators are defined in `validators.coffee`. Here's an example from
the Draft 4 benchmarks:
```coffee
setup: (schema) ->
ajv = require("ajv")()
ajv.compile(schema)
validate: ({validator, schema, document}) ->
validator(document)
error: (result) ->
if result == true
false
else
validator.errors
```
The `setup` function returns a validator object usable by the `validate`
function. The `validate` function performs the actual validation work that
is measured in benchmarking. The `error` function is used only in the
preflight stage of benchmarking to determine whether the result of the
`validate` function contains any errors. This is useful in making sure that all
the validators are actually operating correctly and thus being compared fairly.
## Adding New Benchmarks
Each individual benchmark consists of a directory containing a schema, a valid
document, and an `index.coffee` file which runs the benchmark.
```coffee
validators = require "../validators"
{benchmark} = require("../../runner")(validators)
benchmark {
name: "Event - Valid document"
repeats: 1024
schema: require "./schema"
document: require "./valid_doc"
}
```
The "repeats" parameter determines how many times the document will be
validated during a single measurement sample.
## Running Benchmarks
You can run very specific benchmarks, like the medium-complexity benchmarks for draft 3 only, like so:
`coffee benchmarks/draft3/medium`
You can run all benchmarks for a specific JSON Schema draft:
`coffee benchmarks/draft4`
Or, to run all benchmarks:
`coffee benchmarks/`
You should then see something like this:
```txt
## Benchmarks for Draft 3
Schema: 'Event'. A simple schema, exercising very few attributes
Sample size: 64
Validations per sample: 512
JSCK
Warming up: ................................
Iterations: ................................................................
amanda
Warming up: ................................
Iterations: ................................................................
JSV
Warming up: ................................
Iterations: ................................................................
json-gate
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 193.025 max: 202.692 min: 175.945
amanda: validations/millisecond
median: 4.258 max: 4.608 min: 3.778
JSV: validations/millisecond
median: 2.685 max: 2.748 min: 2.43
json-gate: validations/millisecond
median: 82.514 max: 86.036 min: 48.234
Relative speeds:
JSCK : 1.000
json-gate : 2.339
amanda : 45.337
JSV : 71.896
Schema: 'Configuration'. A moderately complex schema with some nesting and value constraints
Sample size: 64
Validations per sample: 128
JSCK
Warming up: ................................
Iterations: ................................................................
amanda
Warming up: ................................
Iterations: ................................................................
JSV
Warming up: ................................
Iterations: ................................................................
json-gate
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 108.936 max: 109.966 min: 85.964
amanda: validations/millisecond
median: 3.788 max: 5.651 min: 2.371
JSV: validations/millisecond
median: 1.344 max: 1.359 min: 1.255
json-gate: validations/millisecond
median: 44.176 max: 45.845 min: 41.803
Relative speeds:
JSCK : 1.000
json-gate : 2.466
amanda : 28.760
JSV : 81.025
## Benchmarks for Draft 4
Schema: 'Event - Valid document'. A simple schema, exercising very few attributes
Sample size: 64
Validations per sample: 1024
JSCK
Warming up: ................................
Iterations: ................................................................
tv4
Warming up: ................................
Iterations: ................................................................
z-schema
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 186.81 max: 191.617 min: 138.716
tv4: validations/millisecond
median: 54.377 max: 55.907 min: 46.942
z-schema: validations/millisecond
median: 93.657 max: 93.979 min: 89.44
Relative speeds:
JSCK : 1.000
z-schema : 1.995
tv4 : 3.435
Schema: 'Configuration'. A moderately complex schema with some nesting and value constraints
Sample size: 64
Validations per sample: 256
JSCK
Warming up: ................................
Iterations: ................................................................
tv4
Warming up: ................................
Iterations: ................................................................
z-schema
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 118.573 max: 119.07 min: 105.22
tv4: validations/millisecond
median: 19.608 max: 20.389 min: 16.505
z-schema: validations/millisecond
median: 40.606 max: 40.934 min: 34.934
Relative speeds:
JSCK : 1.000
z-schema : 2.920
tv4 : 6.047
Schema: 'Transaction'.
Sample size: 64
Validations per sample: 64
JSCK
Warming up: ................................
Iterations: ................................................................
tv4
Warming up: ................................
Iterations: ................................................................
z-schema
Warming up: ................................
Iterations: ................................................................
JSCK: validations/millisecond
median: 12.814 max: 14.873 min: 6.984
tv4: validations/millisecond
median: 1.946 max: 1.965 min: 1.832
z-schema: validations/millisecond
median: 3.768 max: 3.836 min: 3.297
Relative speeds:
JSCK : 1.000
z-schema : 3.401
tv4 : 6.583
```
================================================
FILE: doc/benchmarks.pfm.md
================================================
# Benchmarks
JSCK has fairly comprehensive benchmarks which show it to be one of the fastest
JSON Schema validators available for Node.js.
To run the benchmarks immediately after you `git clone` the repo:
```shell
git clone git@github.com:pandastrike/jsck.git && \
git submodule update --init && \
npm install && \
coffee benchmarks/
```
Benchmarking harness and content lives in `benchmarks`, with separate
directories for each draft. For each draft there are a number of different
benchmarks (contained in subdirectories) that can be run in whole or in part.
This is the basic directory structure:
benchmarks/
draft3/
draft4/
complex/
medium/
trivial/
index.coffee
validators.coffee
## Adding Additional Validators
The validators are defined in `validators.coffee`. Here's an example from
the Draft 4 benchmarks:
```benchmarks/draft4/validators.coffee#L16-25```
The `setup` function returns a validator object usable by the `validate`
function. The `validate` function performs the actual validation work that
is measured in benchmarking. The `error` function is used only in the
preflight stage of benchmarking to determine whether the result of the
`validate` function contains any errors. This is useful in making sure that all
the validators are actually operating correctly and thus being compared fairly.
## Adding New Benchmarks
Each individual benchmark consists of a directory containing a schema, a valid
document, and an `index.coffee` file which runs the benchmark.
```benchmarks/draft4/trivial/index.coffee```
The "repeats" parameter determines how many times the document will be
validated during a single measurement sample.
## Running Benchmarks
You can run very specific benchmarks, like the medium-complexity benchmarks for draft 3 only, like so:
`coffee benchmarks/draft3/medium`
You can run all benchmarks for a specific JSON Schema draft:
`coffee benchmarks/draft4`
Or, to run all benchmarks:
`coffee benchmarks/`
You should then see something like this:
```benchmarks/results/all.txt```
================================================
FILE: doc/tests.md
================================================
# Testing JSCK
JSCK is developed against several sets of tests:
* the official [JSON Schema Test Suite][canonical]
* tests for schemas that should be considered invalid
* tests for schemas that should be considered valid
* unit tests particular to JSCK
To run all tests for all versions of JSON Schema:
coffee test
## The official test suite
The good folks behind JSON Schema have provided a language-agnostic test suite,
composed of JSON documents that define schemas and input objects that should be
judged as valid or invalid for each schema. The README for the test suite
provides a
[good overview of the structure](https://github.com/json-schema/JSON-Schema-Test-Suite#structure-of-a-test)
of the JSON test files.
JSCK includes the official test suite as a git submodule at
`test/JSON-Schema-Test-Suite`. Running `coffee tasks/update` will initialize
and/or update the submodule.
As a concrete example, here is the first case for the "items" attribute in Draft 4:
```json
{
"description": "a schema given for items",
"schema": {
"items": {"type": "integer"}
},
"tests": [
{
"description": "valid items",
"data": [ 1, 2, 3 ],
"valid": true
},
{
"description": "wrong type of items",
"data": [1, "x"],
"valid": false
},
{
"description": "ignores non-arrays",
"data": {"foo" : "bar"},
"valid": true
}
]
},
```
Breaking this down
* the case described is for when the value of "items" is a schema
* the schema under consideration does not declare a type for the input data
* if the input data is an array, all of its items must be integers
There are three tests for this case:
* valid input data
* invalid input data
* irrelevant input data
### Running the official tests
To run the official tests for a specific draft:
coffee test/draft4
It can be difficult to troubleshoot bugs when running hundreds of tests.
To run only the tests for a specific attribute (in this case,
"patternProperties"):
coffee test/draft4 patternProperties
And to run only a certain test for that attribute, append the (zero-based)
index of the test:
coffee test/draft4 patternProperties 3
### Ignoring specific tests
The test harness provided by JSCK allows you to ignore specific tests from
the official suite. Here is how we define the ignores for Draft 4:
```coffee
#cmd = "node_modules/.bin/nserver -p 5725 -d test/JSON-Schema-Test-Suite/remotes"
#proc = shell.exec cmd, (code, output) ->
#
#Testify = require "testify"
#Testify.once "done", ->
#console.log "Shutting down the 'remotes' test server"
#proc.kill("SIGTERM")
[_node, _script, attribute, test_number] = process.argv
suite {
attribute
test_number
version: "draft4"
validate: (schema, document) ->
v = new draft4(schema)
v.validate(document)
ignores:
# Doubtful value for the majority of use cases.
# https://github.com/pandastrike/jsck/issues/42
minLength: [
"one supplementary Unicode code point is not long enough"
```
## Invalid Schemas
A tool for working with JSON Schemas must be able to reject invalid schemas.
The official test suite (at this time) only provides tests data for validating
documents against correctly formed schemas, so JSCK introduces a similar
test format for checking the invalidity of schema definitions. These tests
are defined as CoffeeScript files in `test/draft{3,4}/invalid/`. Though we
use CoffeeScript for easy maintenance, the actual content could be represented
as JSON, or YAML, or any other data format.
Here's an example of an invalidity test for the "type" attribute:
```coffee
{
description: "string values must be one of the primitive types"
schemas: [
type: "bogus"
]
}
```
All of the objects in the `schemas` array are invalid schemas that should
cause JSCK (or any other validation library) to reject the schema.
One of the benefits of using CoffeeScript (or JavaScript, or any other
language) to generate the test content is the ability to use helper functions
to create multiple test schemas:
```coffee
{
description: "value MUST be either a string or an array"
schemas: for value in json_types.except("string", "array")
{type: value}
}
```
In the above example, `schemas` is defined using a list comprehension and a
helper function that generates an invalid schema for all of the primitive JSON
Schema types except those explicitly named.
To run only the invalidity tests:
coffee test/draft4/invalid.coffee
## Valid Schemas
It is also valuable to have tests for the acceptance of valid schemas that
buggy or overzealous libraries might reject. JSCK's schema validity tests
are defined as CoffeeScript files in `test/draft{3,4}/valid/`.
As an example, the names of JSON Schema attributes ("properties", "type",
"required", etc.) are not reserved terms. It is legal to use these strings
as the names of object properties. During one round of refactoring, JSCK
began rejecting schemas that used JSON Schema attribute names as properties.
To prevent this, a validity test was added:
```coffee
required:
type: "object"
properties:
required:
type: "boolean"
id:
type: "object"
properties:
id:
```
To run only the validity tests:
coffee test/draft4/valid.coffee
## Unit tests
JSCK also uses a few unit tests to assist in developing and troubleshooting
some of the internal functions, such as scope resolution and URI construction.
To run only the unit tests for a particular draft version:
coffee test/draft4/unit
[canonical]:https://github.com/json-schema/JSON-Schema-Test-Suite
================================================
FILE: doc/tests.pfm.md
================================================
# Testing JSCK
JSCK is developed against several sets of tests:
* the official [JSON Schema Test Suite][canonical]
* tests for schemas that should be considered invalid
* tests for schemas that should be considered valid
* unit tests particular to JSCK
To run all tests for all versions of JSON Schema:
coffee test
## The official test suite
The good folks behind JSON Schema have provided a language-agnostic test suite,
composed of JSON documents that define schemas and input objects that should be
judged as valid or invalid for each schema. The README for the test suite
provides a
[good overview of the structure](https://github.com/json-schema/JSON-Schema-Test-Suite#structure-of-a-test)
of the JSON test files.
JSCK includes the official test suite as a git submodule at
`test/JSON-Schema-Test-Suite`. Running `coffee tasks/update` will initialize
and/or update the submodule.
As a concrete example, here is the first case for the "items" attribute in Draft 4:
```test/JSON-Schema-Test-Suite/tests/draft4/items.json#L2-24```
Breaking this down
* the case described is for when the value of "items" is a schema
* the schema under consideration does not declare a type for the input data
* if the input data is an array, all of its items must be integers
There are three tests for this case:
* valid input data
* invalid input data
* irrelevant input data
### Running the official tests
To run the official tests for a specific draft:
coffee test/draft4
It can be difficult to troubleshoot bugs when running hundreds of tests.
To run only the tests for a specific attribute (in this case,
"patternProperties"):
coffee test/draft4 patternProperties
And to run only a certain test for that attribute, append the (zero-based)
index of the test:
coffee test/draft4 patternProperties 3
### Ignoring specific tests
The test harness provided by JSCK allows you to ignore specific tests from
the official suite. Here is how we define the ignores for Draft 4:
```test/draft4/official.coffee#L7-32```
## Invalid Schemas
A tool for working with JSON Schemas must be able to reject invalid schemas.
The official test suite (at this time) only provides tests data for validating
documents against correctly formed schemas, so JSCK introduces a similar
test format for checking the invalidity of schema definitions. These tests
are defined as CoffeeScript files in `test/draft{3,4}/invalid/`. Though we
use CoffeeScript for easy maintenance, the actual content could be represented
as JSON, or YAML, or any other data format.
Here's an example of an invalidity test for the "type" attribute:
```test/draft4/invalid/type.coffee#L24-29```
All of the objects in the `schemas` array are invalid schemas that should
cause JSCK (or any other validation library) to reject the schema.
One of the benefits of using CoffeeScript (or JavaScript, or any other
language) to generate the test content is the ability to use helper functions
to create multiple test schemas:
```test/draft4/invalid/type.coffee#L5-9```
In the above example, `schemas` is defined using a list comprehension and a
helper function that generates an invalid schema for all of the primitive JSON
Schema types except those explicitly named.
To run only the invalidity tests:
coffee test/draft4/invalid.coffee
## Valid Schemas
It is also valuable to have tests for the acceptance of valid schemas that
buggy or overzealous libraries might reject. JSCK's schema validity tests
are defined as CoffeeScript files in `test/draft{3,4}/valid/`.
As an example, the names of JSON Schema attributes ("properties", "type",
"required", etc.) are not reserved terms. It is legal to use these strings
as the names of object properties. During one round of refactoring, JSCK
began rejecting schemas that used JSON Schema attribute names as properties.
To prevent this, a validity test was added:
```test/draft4/valid/properties.coffee#L5-15```
To run only the validity tests:
coffee test/draft4/valid.coffee
## Unit tests
JSCK also uses a few unit tests to assist in developing and troubleshooting
some of the internal functions, such as scope resolution and URI construction.
To run only the unit tests for a particular draft version:
coffee test/draft4/unit
[canonical]:https://github.com/json-schema/JSON-Schema-Test-Suite
================================================
FILE: examples/draft4/advanced.coffee
================================================
JSCK = require("../../src/index")
# using a schema that declares a URI with "id"
jsck = new JSCK.draft4
$schema: "http://json-schema.org/draft-04/schema#"
id: "urn:jsck.examples.advanced#"
definitions:
user:
type: "object"
required: ["login"]
properties:
login:
type: "string"
pattern: "^[\\w\\d_]{3,32}$"
email:
type: "string"
validator = jsck.validator(uri: "urn:jsck.examples.advanced#")
{valid} = validator.validate
login: "automatthew"
email: "automatthew@mailinator.com"
console.log "Schema with id:", valid
# validating against a subschema using a JSON Pointer
validator = jsck.validator "urn:jsck.examples.advanced#/definitions/user"
{valid} = validator.validate
login: "automatthew"
email: "automatthew@mailinator.com"
console.log "Schema identified by JSON Pointer:", valid
# Adding multiple schemas
#
# You can instantiate JSCK with multiple schemas or add them later
#
# Instantiation:
# validator = new JSCK(schema1, schema2, schema3)
jsck.add
id: "urn:jsck.examples.user_list#"
type: "array"
items: {$ref: "urn:jsck.examples.advanced#/definitions/user"}
validator = jsck.validator "urn:jsck.examples.user_list#"
{valid} = validator.validate [
{ login: "dyoder" }
{ login: "automatthew" }
]
console.log "Multiple schemas:", valid
================================================
FILE: examples/draft4/basic.coffee
================================================
JSCK = require("../../src/index")
# Construct a validator for a schema lacking an "id" declaration
jsck = new JSCK.draft4
type: "object"
properties:
user:
type: "object"
required: ["login"]
properties:
login:
type: "string"
pattern: "^[\\w\\d_]{3,32}$"
email:
type: "string"
format: "email"
console.log "valid document:", jsck.validate
user:
login: "matthew"
email: "matthew@pandastrike.com"
{errors} = jsck.validate
user:
login: "matthew"
email: "pandastrike.com"
console.log "invalid document:", errors
================================================
FILE: package.json
================================================
{
"name": "jsck",
"version": "0.3.2",
"description": "JSON Schema Compiled checK",
"scripts": {
"test": "coffee test",
"prepublish": "coffee tasks/build"
},
"main": "lib/index.js",
"files": [
"lib/",
"schemas/",
"README.md"
],
"dependencies": {},
"devDependencies": {
"ajv": "latest",
"amanda": "latest",
"ajv": "^0.6.1",
"amanda": "~0.5.1",
"coffee-script": "~1.7",
"commonjs-everywhere": "^0.9.7",
"glob": "~3.2.6",
"is-my-json-valid": "latest",
"jayschema": "latest",
"jsen": "latest",
"json-gate": "latest",
"json-schema-tests": "^0.1.1",
"request": "~2.48.0",
"shelljs": "^0.3.0",
"simple-http-server": "~0.1.8",
"testify": "~0.2.11",
"themis": "latest",
"tv4": "latest",
"z-schema": "latest"
},
"repository": "git@github.com:pandastrike/jsck.git",
"author": "Matthew King <matthew@pandastrike.com>",
"license": "MIT"
}
================================================
FILE: schemas/draft-03/schema.json
================================================
{
"$schema" : "http://json-schema.org/draft-03/schema#",
"id" : "http://json-schema.org/draft-03/schema#",
"type" : "object",
"properties" : {
"type" : {
"type" : ["string", "array"],
"items" : {
"type" : ["string", {"$ref" : "#"}]
},
"uniqueItems" : true,
"default" : "any"
},
"properties" : {
"type" : "object",
"additionalProperties" : {"$ref" : "#"},
"default" : {}
},
"patternProperties" : {
"type" : "object",
"additionalProperties" : {"$ref" : "#"},
"default" : {}
},
"additionalProperties" : {
"type" : [{"$ref" : "#"}, "boolean"],
"default" : {}
},
"items" : {
"type" : [{"$ref" : "#"}, "array"],
"items" : {"$ref" : "#"},
"default" : {}
},
"additionalItems" : {
"type" : [{"$ref" : "#"}, "boolean"],
"default" : {}
},
"required" : {
"type" : "boolean",
"default" : false
},
"dependencies" : {
"type" : "object",
"additionalProperties" : {
"type" : ["string", "array", {"$ref" : "#"}],
"items" : {
"type" : "string"
}
},
"default" : {}
},
"minimum" : {
"type" : "number"
},
"maximum" : {
"type" : "number"
},
"exclusiveMinimum" : {
"type" : "boolean",
"default" : false
},
"exclusiveMaximum" : {
"type" : "boolean",
"default" : false
},
"minItems" : {
"type" : "integer",
"minimum" : 0,
"default" : 0
},
"maxItems" : {
"type" : "integer",
"minimum" : 0
},
"uniqueItems" : {
"type" : "boolean",
"default" : false
},
"pattern" : {
"type" : "string",
"format" : "regex"
},
"minLength" : {
"type" : "integer",
"minimum" : 0,
"default" : 0
},
"maxLength" : {
"type" : "integer"
},
"enum" : {
"type" : "array",
"minItems" : 1,
"uniqueItems" : true
},
"default" : {
"type" : "any"
},
"title" : {
"type" : "string"
},
"description" : {
"type" : "string"
},
"format" : {
"type" : "string"
},
"divisibleBy" : {
"type" : "number",
"minimum" : 0,
"exclusiveMinimum" : true,
"default" : 1
},
"disallow" : {
"type" : ["string", "array"],
"items" : {
"type" : ["string", {"$ref" : "#"}]
},
"uniqueItems" : true
},
"extends" : {
"type" : [{"$ref" : "#"}, "array"],
"items" : {"$ref" : "#"},
"default" : {}
},
"id" : {
"type" : "string",
"format" : "uri"
},
"$ref" : {
"type" : "string",
"format" : "uri"
},
"$schema" : {
"type" : "string",
"format" : "uri"
}
},
"dependencies" : {
"exclusiveMinimum" : "minimum",
"exclusiveMaximum" : "maximum"
},
"default" : {}
}
================================================
FILE: schemas/draft-04/schema.json
================================================
{
"id": "http://json-schema.org/draft-04/schema#",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Core schema meta-schema",
"definitions": {
"schemaArray": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#" }
},
"positiveInteger": {
"type": "integer",
"minimum": 0
},
"positiveIntegerDefault0": {
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
},
"simpleTypes": {
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
},
"stringArray": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
},
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uri"
},
"$schema": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"default": {},
"multipleOf": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "boolean",
"default": false
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "boolean",
"default": false
},
"maxLength": { "$ref": "#/definitions/positiveInteger" },
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
"pattern": {
"type": "string",
"format": "regex"
},
"additionalItems": {
"anyOf": [
{ "type": "boolean" },
{ "$ref": "#" }
],
"default": {}
},
"items": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": {}
},
"maxItems": { "$ref": "#/definitions/positiveInteger" },
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
"uniqueItems": {
"type": "boolean",
"default": false
},
"maxProperties": { "$ref": "#/definitions/positiveInteger" },
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
"required": { "$ref": "#/definitions/stringArray" },
"additionalProperties": {
"anyOf": [
{ "type": "boolean" },
{ "$ref": "#" }
],
"default": {}
},
"definitions": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"properties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"dependencies": {
"type": "object",
"additionalProperties": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/stringArray" }
]
}
},
"enum": {
"type": "array",
"minItems": 1,
"uniqueItems": true
},
"type": {
"anyOf": [
{ "$ref": "#/definitions/simpleTypes" },
{
"type": "array",
"items": { "$ref": "#/definitions/simpleTypes" },
"minItems": 1,
"uniqueItems": true
}
]
},
"allOf": { "$ref": "#/definitions/schemaArray" },
"anyOf": { "$ref": "#/definitions/schemaArray" },
"oneOf": { "$ref": "#/definitions/schemaArray" },
"not": { "$ref": "#" }
},
"dependencies": {
"exclusiveMaximum": [ "maximum" ],
"exclusiveMinimum": [ "minimum" ]
},
"default": {}
}
================================================
FILE: src/common/arrays.coffee
================================================
module.exports =
# handlers
maxItems: (value, context) ->
(data, runtime) =>
if @test_type "array", data
if data.length > value
runtime.error context, data, description:
"Array must have fewer than #{value} items."
minItems: (value, context) ->
(data, runtime) =>
if @test_type "array", data
if data.length < value
runtime.error context, data, description:
"Array must have more than #{value} items."
items: (definition, context) ->
if @test_type "array", definition
test = @_tuple_items definition, context
else if @test_type "object", definition
test = @compile(context, definition)
# TODO check for array data?
(data, runtime) =>
for item, i in data
test item, runtime.child(i)
null
else
throw new Error "The 'items' attribute must be an object or an array"
_additionalItems: (definition, context) ->
if @test_type "object", definition
test = @compile(context, definition)
else if definition == false
test = (data, runtime) ->
runtime.error context, data, description:
"Array is supposed to be a tuple, but has too many items."
else if definition == true
# valid
else
throw new Error "The 'additionalItems' attribute must be an object or false"
(data, runtime) =>
for item, i in data
test item, runtime.child(i)
null
_tuple_items: (definition, context) ->
{additionalItems} = context.modifiers
if additionalItems?
add_item_test = @_additionalItems additionalItems,
context.sibling "additionalItems"
else
add_item_test = null
tests = []
for schema, i in definition
unless @test_type "object", schema
throw new Error "The 'items' attribute must be an object or an array"
tests.push @compile context.child(i), schema
(data, runtime) =>
if @test_type "array", data
for test, i in tests
test data[i], runtime.child(i)
if (data.length > tests.length) && add_item_test
add_item_test data.slice(tests.length), runtime
uniqueItems: (definition, context) ->
null
================================================
FILE: src/common/comparison.coffee
================================================
module.exports =
# handlers
enum: (definition, context) ->
# TODO: add more cases to the draft3 test suite for enum.js,
# as they're not doing full coverage
if @test_type "array", definition
(data, runtime) =>
for value in definition
return if @equal(data, value)
values = definition.join ', '
runtime.error context, data, description:
"Value must be one of #{values}."
else
throw new Error "Value of 'enum' MUST be an Array"
# helpers
equal: (got, want) ->
if want instanceof Array
@array_equal(got, want)
else if @is_object(want)
@object_equal(got, want)
else
got == want
array_equal: (got, want) ->
return false unless (got instanceof Array)
return true if want.length == 0
return false unless got.length == want.length
for item, i in want
return false if !@equal(got[i], item)
true
object_equal: (got, want) ->
return false unless @is_object(got)
return false unless Object.keys(got).length == Object.keys(want).length
for key, value of want
return false if !@equal(got[key], value)
true
================================================
FILE: src/common/numeric.coffee
================================================
module.exports =
minimum: (value, context) ->
{modifiers: {exclusiveMinimum}} = context
if exclusiveMinimum
(data, runtime) =>
if @test_type "number", data
if !(data > value)
runtime.error context, data, description:
"Value must be > #{value}."
else
(data, runtime) =>
if @test_type "number", data
if !(data >= value)
runtime.error context, data, description:
"Value must be >= #{value}."
maximum: (value, context) ->
{modifiers: {exclusiveMaximum}} = context
if exclusiveMaximum
(data, runtime) =>
if @test_type "number", data
if !(data < value)
runtime.error context, data, description:
"Value must be < #{value}."
else
(data, runtime) =>
if @test_type "number", data
if !(data <= value)
runtime.error context, data, description:
"Value must be <= #{value}."
================================================
FILE: src/common/objects.coffee
================================================
module.exports =
# handlers
patternProperties: (definition, context) ->
{additionalProperties} = context.modifiers
if additionalProperties
# The additionalProperties compiler runs the patternProperties
# validation. This is necessary because properties are not considered
# additional if they match a pattern.
return null
if !@test_type "object", definition
throw new Error "The 'patternProperties' attribute must be an object"
if Object.keys(definition).length == 0
throw new Error "The 'patternProperties' object should not be empty"
tests = {}
for pattern, schema of definition
unless @test_type "object", schema
throw new Error "Values of 'patternProperties' must be an objects"
tests[pattern] =
regex: new RegExp(pattern)
test: @compile context.child(pattern), schema
(data, runtime) =>
for property, value of data
for pattern, object of tests
if object.regex.test(property)
object.test value, runtime.child(property)
null
additionalProperties: (definition, context) ->
# TODO: refactor this method for clarity. It's likely that this will
# also improve performance.
{properties, patternProperties} = context.modifiers
if @test_type "object", definition
add_prop_test = @compile(context, definition)
else if definition == false
add_prop_test = (data, runtime) =>
runtime.error context, data, description:
"Unspecified properties are not allowed on this object."
else if definition == undefined
add_prop_test = null
else
throw new Error "The 'additionalProperties' attribute must be an object or false"
patterns = {}
for pattern, schema of patternProperties
patterns[pattern] =
regex: new RegExp(pattern)
test: @compile(context.sibling("patternProperties").child(pattern), schema)
(data, runtime) =>
if @test_type "object", data
for property, value of data
explicit = false
patterned = false
if properties?[property]
explicit = true
if patterns
for pattern, object of patterns
if object.regex.test(property)
patterned = true
object.test value, runtime.child(property)
if !explicit && !patterned && add_prop_test
add_prop_test value, runtime.child(property)
null
================================================
FILE: src/common/strings.coffee
================================================
module.exports =
pattern: (pattern, context) ->
unless @test_type "string", pattern
throw new Error "Value of 'pattern' must be a string"
regex = new RegExp(pattern)
(data, runtime) =>
if @test_type "string", data
if !regex.test(data)
runtime.error context, data, description:
"String did not match regex pattern."
minLength: (value, context) ->
unless @test_type "integer", value
throw new Error "Value of 'minLength' must be an integer"
(data, runtime) =>
if @test_type "string", data
if !(data.length >= value)
runtime.error context, data, description:
"String must be longer than #{value} bytes."
maxLength: (value, context) ->
unless @test_type "integer", value
throw new Error "Value of 'maxLength' must be an integer"
(data, runtime) =>
if @test_type "string", data
if !(data.length <= value)
runtime.error context, data, description:
"String must be smaller than #{value} bytes."
================================================
FILE: src/common/type.coffee
================================================
module.exports =
# handlers
type: (definition, context) ->
if @test_type "array", definition
tests = []
for type in definition
do (type) =>
if @test_type "object", type
test = @compile context, type
tests.push (data, runtime) =>
temp = new runtime.constructor
pointer: ""
errors: []
test data, temp
temp.errors.length == 0
else
tests.push (data, runtime) =>
@test_type type, data
(data, runtime) =>
valid = false
for test in tests
if test(data, runtime)
valid = true
if valid == false
runtime.error context, data
else if @test_type "object", definition
@compile(context, definition)
else
(data, runtime) =>
if !@test_type definition, data
runtime.error context, data
# helpers
is_object: (data) ->
data? && (typeof data) == "object" &&
!(data instanceof Array) &&
!(data instanceof Date)
is_primitive: (name) ->
name in ["integer", "number", "string", "object", "array", "boolean", "null"]
get_type: (data) ->
if typeof(data) == "number" && data % 1 == 0
return "integer"
if typeof(data) == "number"
return "number"
if typeof(data) == "string"
return "string"
if @is_object(data)
return "object"
if data instanceof Array
return "array"
if typeof data == "boolean"
return "boolean"
if data == null
return "null"
test_type: (type_name, data) ->
switch type_name
when "integer"
typeof(data) == "number" && data % 1 == 0
when "number"
typeof(data) == "number"
when "string"
typeof(data) == "string"
when "object"
@is_object(data)
when "array"
data instanceof Array
when "boolean"
typeof data == "boolean"
when "null"
data == null
when "any"
true
else
throw new Error "Bad type: '#{type_name}'"
================================================
FILE: src/draft3/logical.coffee
================================================
URI = require "../uri"
module.exports =
extends: (schemas, context) ->
unless @test_type "array", schemas
schemas = [schemas]
for schema, i in schemas
if (ref = schema.$ref)?
uri = URI.resolve(context.scope, ref)
parent = @find(uri)
if !parent
throw new Error "No schema found for $ref '#{ref}'"
else
schemas[i] = parent
tests = []
for schema, i in schemas
new_context = context.child(i)
tests.push @compile(new_context, schema)
(data, runtime) =>
for test in tests
test data, runtime
disallow: (definition, context) ->
if @test_type "array", definition
tests = []
for type, i in definition
do (i) =>
if @test_type "object", type
inverse = @compile context, type
tests.push (data, runtime) =>
temp = new runtime.constructor
pointer: ""
errors: []
inverse data, temp
if temp.errors.length == 0
runtime.error context, data
else
tests.push @disallow type, context
(data, runtime) =>
for test in tests
test data, runtime
else
(data, runtime) =>
if @test_type definition, data
runtime.error context, data
================================================
FILE: src/draft3/numeric.coffee
================================================
module.exports =
divisibleBy: (value, context) ->
(data, runtime) =>
if @test_type "number", data
if !((data / value) % 1 == 0)
runtime.error context, data
================================================
FILE: src/draft3/objects.coffee
================================================
module.exports =
properties: (definition, context) ->
if !@test_type "object", definition
throw new Error "The 'properties' attribute must be an object"
tests = {}
required = []
for property, schema of definition
new_context = context.child(property)
test = @compile(new_context, schema)
tests[property] = test
if schema.required == true
required.push property
(data, runtime) =>
if @test_type "object", data
for property, value of data
if (test = tests[property])?
test value, runtime.child(property)
for key in required
if data[key] == undefined
runtime.error context.child(key).child("required")
true
dependencies: (definition, context) ->
unless @test_type "object", definition
throw new Error "Value of 'dependencies' must be an object"
else
tests = []
for property, dependency of definition
if @test_type "string", dependency
tests.push (data, runtime) =>
if data[property]? && !data[dependency]?
runtime.child(property).error context
else if @test_type "array", dependency
tests.push (data, runtime) =>
if data[property]?
for item in dependency
if !data[item]?
runtime.child(property).error context
null
else if @test_type "object", dependency
fn = @compile context, dependency
tests.push (data, runtime) =>
if data[property]
fn data, runtime
else
true
else
throw new Error "Invalid dependency"
(data, runtime) =>
if @test_type "object", data
for test in tests
test data, runtime
null
================================================
FILE: src/draft3/strings.coffee
================================================
module.exports =
format: (format_name, context) ->
if format_name == "regex"
(data, runtime) =>
if @test_type "string", data
try
new RegExp(data)
catch error
runtime.error context, data
else if regex = format_regexes[format_name]
do (regex) =>
(data, runtime) =>
if @test_type "string", data
if !regex.test(data)
runtime.error context, data
else
throw new Error "Invalid format_name for 'format'"
# regexes below were derived from
# https://github.com/tdegrunt/jsonschema
#
#Copyright (C) 2012-2013 Tom de Grunt <tom@degrunt.nl>
#Permission is hereby granted, free of charge, to any person obtaining a copy of
#this software and associated documentation files (the "Software"), to deal in
#the Software without restriction, including without limitation the rights to
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
#of the Software, and to permit persons to whom the Software is furnished to do
#so, subject to the following conditions:
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
format_regexes =
"date-time": /^(\d{4}-(?:0[0-9]|1[0-2])-[0-9]{2}(T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.\d+)?(Z|(\-|\+)[0-9]{2}:[0-9]{2})?)?)$/
date: /^(\d{4}-(?:0[0-9]|1[0-2])-[0-9]{2})$/
time: /^\d{2}:\d{2}:\d{2}$/
email: /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/
"ip-address": /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
ipv6: /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
uri: /^[a-zA-Z][a-zA-Z0-9+-.]*:[^\s]*$/
color: /^(((#[0-9A-Fa-f]{3,6}))|(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)|(rgb\(\s*([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\s*,\s*([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\s*,\s*([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/
"host-name": /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])){0,3}\.?)$/
alpha: /^[a-zA-Z]+$/
alphanumeric: /^[a-zA-Z0-9]+$/
"utc-millisec": (input) ->
(typeof input is "string") and parseFloat(input) is parseInt(input, 10) and not isNaN(input)
style: /\s*(.+?):\s*([^;]+);?/g
phone: /^\+(?:[0-9] ?){6,14}[0-9]$/
================================================
FILE: src/draft3.coffee
================================================
validator = require("./validator")
module.exports = validator
schema_uri: "http://json-schema.org/draft-03/schema#"
mixins: [
require "./draft3/logical"
require "./draft3/numeric"
require "./draft3/objects"
require "./draft3/strings"
]
================================================
FILE: src/draft4/logical.coffee
================================================
module.exports =
anyOf: (definition, context) ->
unless @test_type "array", definition
throw new Error "The 'anyOf' attribute must be an array"
if definition.length == 0
throw new Error "The 'anyOf' array may not be empty"
tests = []
for schema, i in definition
unless @test_type "object", schema
throw new Error "The 'anyOf' array values must be objects"
new_context = context.child(i)
tests.push @compile(new_context, schema)
(data, runtime) =>
most_items_tested = 0
best_errors = []
answer = tests.some (test) =>
temp = new runtime.constructor
pointer: ""
error_pointer: runtime.pointer
errors: []
test(data, temp)
if temp.items_tested && temp.items_tested > most_items_tested
best_errors = temp.errors
most_items_tested = temp.items_tested
temp.errors.length == 0
unless answer
if @options.closestMatch
Array::push.apply runtime.errors, best_errors
else
runtime.error context, data
# Note from author of draft4 on how allOf works w/r/t additionalProperties
# http://stackoverflow.com/questions/22689900/json-schema-allof-with-additionalproperties/23001194#23001194
# also https://github.com/fge/json-schema-validator/issues/88
allOf: (definition, context) ->
unless @test_type "array", definition
throw new Error "The 'allOf' attribute must be an array"
if definition.length == 0
throw new Error "The 'allOf' array may not be empty"
# TODO: check for proper error reporting. Do we need to create new
# runtimes, contexts, etc.?
tests = []
for schema, i in definition
unless @test_type "object", schema
throw new Error "The 'allOf' array values must be objects"
new_context = context.child(i)
tests.push @compile(new_context, schema)
(data, runtime) =>
for test in tests
test data, runtime
null
oneOf: (definition, context) ->
unless @test_type "array", definition
throw new Error "The 'oneOf' attribute must be an array"
if definition.length == 0
throw new Error "The 'oneOf' array may not be empty"
tests = []
for schema, i in definition
unless @test_type "object", schema
throw new Error "The 'oneOf' array values must be objects"
new_context = context.child(i)
tests.push @compile(new_context, schema)
# TODO optimize?
(data, runtime) =>
valids = 0
most_items_tested = 0
best_errors = []
for test in tests
temp = new runtime.constructor
pointer: ""
error_pointer: runtime.pointer
errors: []
test(data, temp)
if temp.errors.length == 0
valids++
else
if temp.items_tested && temp.items_tested > most_items_tested
best_errors = temp.errors
most_items_tested = temp.items_tested
if valids == 0 && @options.closestMatch
Array::push.apply runtime.errors, best_errors
else if valids != 1
runtime.error context, data
not: (definition, context) ->
unless @test_type "object", definition
throw new Error "The 'not' attribute must be an object"
inverse = @compile context, definition
(data, runtime) =>
temp = new runtime.constructor
pointer: ""
errors: []
inverse data, temp
if temp.errors.length == 0
runtime.error context, data
================================================
FILE: src/draft4/numeric.coffee
================================================
module.exports =
multipleOf: (value, context) ->
unless @test_type "number", value
throw new Error "The 'multipleOf' attribute must be a number"
(data, runtime) =>
if @test_type "number", data
if !((data / value) % 1 == 0)
runtime.error context, data, description:
"Value `#{data}` is not a multiple of #{value}"
================================================
FILE: src/draft4/objects.coffee
================================================
module.exports =
# handlers
required: (definition, context) ->
unless @test_type "array", definition
throw new Error "The 'required' attribute must be an array"
if definition.length == 0
throw new Error "The 'required' array must have at least one element"
for property, i in definition
unless @test_type "string", property
throw new Error "The 'required' array may only contain strings"
(data, runtime) =>
if @test_type "object", data
for property, i in definition
if data[property] == undefined
c = context.child(i)
c.definition = property
runtime.error c, undefined, description:
"Required property '#{property}' is missing"
null
properties: (definition, context) ->
unless @test_type "object", definition
throw new Error "The 'properties' attribute must be an object"
tests = {}
for property, schema of definition
unless @test_type "object", schema
throw new Error "The 'properties' attribute must be an object"
new_context = context.child(property)
test = @compile(new_context, schema)
tests[property] = test
(data, runtime) =>
if @test_type "object", data
for property, value of data
if (test = tests[property])?
test value, runtime.child(property)
null
minProperties: (definition, context) ->
(data, runtime) =>
if @test_type "object", data
if Object.keys(data).length < definition
runtime.error context, data, description:
"Object must have at least #{definition} properties."
maxProperties: (definition, context) ->
(data, runtime) =>
if @test_type "object", data
if Object.keys(data).length > definition
runtime.error context, data, description:
"Object cannot have more than #{definition} properties."
dependencies: (definition, context) ->
unless @test_type "object", definition
throw new Error "Value of 'dependencies' must be an object"
else
tests = []
for property, dependency of definition
if @test_type "array", dependency
if dependency.length == 0
throw new Error "Arrays in 'dependencies' may not be empty"
for name in dependency
unless @test_type "string", name
throw new Error "Vales of 'dependencies' arrays must be strings"
tests.push (data, runtime) =>
if data[property]?
for item in dependency
if !data[item]?
runtime.child(property).error context
null
else if @test_type "object", dependency
fn = @compile context, dependency
tests.push (data, runtime) =>
if data[property]
fn data, runtime
else
true
else
throw new Error "Invalid dependency"
(data, runtime) =>
if @test_type "object", data
for test in tests
test data, runtime
null
================================================
FILE: src/draft4/strings.coffee
================================================
module.exports =
format: (format_name, context) ->
if format_name == "regex"
(data, runtime) =>
if @test_type "string", data
try
new RegExp(data)
catch error
runtime.error context, data, description:
"String is not in the #{format_name} format."
else if regex = format_regexes[format_name]
do (regex) =>
(data, runtime) =>
if @test_type "string", data
if !regex.test(data)
runtime.error context, data, description:
"String is not in the #{format_name} format."
else
throw new Error "Invalid format_name for 'format'"
# regexes below were derived from
# https://github.com/tdegrunt/jsonschema
#
#Copyright (C) 2012-2013 Tom de Grunt <tom@degrunt.nl>
#Permission is hereby granted, free of charge, to any person obtaining a copy of
#this software and associated documentation files (the "Software"), to deal in
#the Software without restriction, including without limitation the rights to
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
#of the Software, and to permit persons to whom the Software is furnished to do
#so, subject to the following conditions:
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
format_regexes =
"date-time": /^(\d{4}-(?:0[0-9]|1[0-2])-[0-9]{2}(T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.\d+)?(Z|(\-|\+)[0-9]{2}:[0-9]{2})?)?)$/
date: /^(\d{4}-(?:0[0-9]|1[0-2])-[0-9]{2})$/
time: /^\d{2}:\d{2}:\d{2}$/
email: /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/
"ipv4": /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
ipv6: /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
uri: /^[a-zA-Z][a-zA-Z0-9+-.]*:[^\s]*$/
color: /^(((#[0-9A-Fa-f]{3,6}))|(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)|(rgb\(\s*([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\s*,\s*([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\s*,\s*([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/
"hostname": /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])){0,3}\.?)$/
alpha: /^[a-zA-Z]+$/
alphanumeric: /^[a-zA-Z0-9]+$/
"utc-millisec": (input) ->
(typeof input is "string") and parseFloat(input) is parseInt(input, 10) and not isNaN(input)
style: /\s*(.+?):\s*([^;]+);?/g
phone: /^\+(?:[0-9] ?){6,14}[0-9]$/
================================================
FILE: src/draft4/type.coffee
================================================
module.exports =
# handlers
type: (definition, context) ->
if @test_type "array", definition
if definition.length == 0
throw new Error "Invalid 'type': arrays may not be empty"
tests = []
for type in definition
unless @is_primitive(type)
throw new Error "Invalid 'type': #{type} is not a primitive type"
do (type) =>
tests.push (data, runtime) =>
@test_type type, data
(data, runtime) =>
valid = false
for test in tests
if test(data, runtime)
valid = true
if valid == false
types = definition.join ', '
runtime.error context, data, description:
"Expected type to be one of [#{types}] but found `#{@get_type(data)}`"
else if @test_type "string", definition
unless @is_primitive(definition)
throw new Error "Invalid 'type': #{definition} is not a primitive type"
(data, runtime) =>
if !@test_type definition, data
runtime.error context, data, description:
"Expected type to be `#{definition}` but found `#{@get_type(data)}`"
else
throw new Error "The value of 'type' must be a string or an array"
================================================
FILE: src/draft4.coffee
================================================
validator = require("./validator")
module.exports = validator
schema_uri: "http://json-schema.org/draft-04/schema#"
mixins: [
require "./draft4/type"
require "./draft4/logical"
require "./draft4/numeric"
require "./draft4/objects"
require "./draft4/strings"
]
================================================
FILE: src/index.coffee
================================================
module.exports =
draft3: require "./draft3"
draft4: require "./draft4"
================================================
FILE: src/uri.coffee
================================================
is_absolute = (string) ->
/^[\w\d+.-]+:/.test(string)
is_url = (string) ->
/^[\w\d+.-]+:\/\//.test(string)
resolve = (scope, uri) ->
if is_absolute(uri)
uri
else
scope = scope.replace /#$/, ""
if uri.indexOf("#") == 0
[main, _frag] = scope.split("#")
main + uri
else
if is_url(scope) || scope.indexOf("/") != -1
scope.replace /\/[^/]+$/, "/#{uri}"
else
"#{scope}/#{uri}"
module.exports = {is_absolute, is_url, resolve}
================================================
FILE: src/util.coffee
================================================
module.exports =
escape: (string) ->
string
.replace(/~0/g, "~")
.replace(/~1/g, "/")
.replace(/%25/g, "%")
# Used during document validation. Maintains a list of errors and the
# JSON pointer for the section of the document.
Runtime: class Runtime
constructor: ({@errors, @pointer, @error_pointer, @tested_item}) ->
@items_tested = 0
@tested_item ?= => @items_tested++
@error_pointer ?= @pointer
child: (token) ->
new @constructor
errors: @errors
pointer: "#{@pointer}/#{token.toString()}"
error_pointer: "#{@error_pointer}/#{token.toString()}"
tested_item: @tested_item
path: ->
@pointer.slice(2).replace(/\//g, ".")
error: (context, value, options={}) ->
e =
schema:
pointer: context.pointer
attribute: context._attribute
definition: context.definition
document:
pointer: @error_pointer
path: @path()
value: value
if options.description?
e.description = options.description
@errors.push e
# Maintains the URI scope and JSON pointer during traversal
# of a schema.
Context: class Context
constructor: ({@pointer, @scope, @_attribute}) ->
attribute: (name) ->
new Context
pointer: "#{@pointer}/#{name.toString()}"
scope: @scope
_attribute: name
child: (token) ->
new Context
pointer: "#{@pointer}/#{token.toString()}"
scope: @scope
_attribute: @_attribute
sibling: (token) ->
pointer = @pointer.replace(/\/.*$/, "/#{token.toString()}")
new Context
pointer: pointer
scope: @scope
================================================
FILE: src/validator.coffee
================================================
URI = require "./uri"
{escape, Runtime, Context} = require "./util"
# Schemas should always be JSON stringifiable, so this is a simple
# method for obtaining a deep clone of one. This function only gets
# used at schema-compilation time, so there are no performance
# implications unless you are constantly compiling new schemas.
clone = (value) ->
JSON.parse(JSON.stringify(value))
DEFINITIONS =
"http://json-schema.org/draft-03/schema#":
require "../schemas/draft-03/schema.json"
"http://json-schema.org/draft-04/schema#":
require "../schemas/draft-04/schema.json"
module.exports = ({schema_uri, mixins}) ->
SCHEMA_URI = schema_uri
class Validator
@modifiers:
patternProperties: [ "additionalProperties" ]
additionalProperties: [
"properties"
"patternProperties"
]
items: [ "additionalItems" ]
minimum: [ "exclusiveMinimum" ]
maximum: [ "exclusiveMaximum" ]
common_modules =
"type": require "./common/type"
"numeric": require "./common/numeric"
"comparison": require "./common/comparison"
"arrays": require "./common/arrays"
"objects": require "./common/objects"
"strings": require "./common/strings"
common = for name of common_modules
mixin = common_modules[name]
for name, method of mixin
Validator.prototype[name] = method
for mixin in mixins
for name, method of mixin
Validator.prototype[name] = method
constructor: (schemas...) ->
@uris = {}
@media_types = {}
@unresolved = {}
@options = {}
@add(DEFINITIONS[SCHEMA_URI])
for schema in schemas
if schema["$schema"]? && schema["$schema"] != SCHEMA_URI
throw "This validator doesn't support this JSON schema."
@add(schema)
set_options: (args) ->
@options = args
return this
add: (schema) ->
# Clone the schema to prevent any user changes from affecting JSCK.
schema = clone(schema)
if schema.id
# Make sure the schema id always ends with "#"
schema.id = schema.id.replace /#?$/, "#"
# The context keeps track of where we are in the schema while
# we traverse it for compilation.
context = new Context
pointer: schema.id || "#"
scope: schema.id || "#"
@compile_references context, schema
@compile(context, schema)
validate: (data) ->
@validator("#").validate(data)
validator: (arg) ->
if (schema = @find arg)?
validate: (data) =>
errors = []
runtime = new Runtime {errors, pointer: "#"}
schema._test(data, runtime)
if errors.length > 0
for error in errors
[base..., attribute] = error.schema.pointer.split("/")
pointer = base.join("/")
error.schema.definition ?= @resolve_uri(pointer)?[attribute]
if error.document.value is undefined
delete error.document.value
valid = runtime.errors.length == 0
{valid, errors}
toJSON: (args...) ->
schema
else
throw new Error "No schema found for '#{JSON.stringify(arg)}'"
# Find a registered schema.
#
# Takes either a URI string or an options object.
# Valid options:
# * uri
# * mediaType
find: (arg) ->
if @test_type "string", arg
uri = escape(arg)
#if /required/.test uri
#for u, def of @uris when /required/.test u
#console.error u
@uris[uri]
else if (uri = arg.uri)?
uri = escape(uri)
@uris[uri]
else if (media_type = arg.mediaType)?
@media_types[media_type]
else
null
resolve_uri: (uri, scope) ->
if (schema = @find(uri))?
if schema.$ref
@resolve_uri URI.resolve(scope, schema.$ref)
else
schema
register: (uri, schema) ->
@uris[uri] = schema
# TODO: enforce uniqueness of types
if (media_type = schema.mediaType)?
if media_type != "application/json"
@media_types[media_type] = schema
compile_references: (context, schema) ->
# Make an initial pass over the schema looking for $ref fields,
# recording their targets for use in actual compilation.
@schema_references(context, schema)
# We try a second time to resolve $ref values, because a schema may have
# been defined after we initially tried to resolve a $ref.
for ref, {scope, uri} of @unresolved
if (found_schema = @resolve_uri(uri, scope))?
delete @unresolved[ref]
@register ref, found_schema
if Object.keys(@unresolved).length > 0
pointers = (uri for key, {uri} of @unresolved)
throw new Error "Unresolvable $ref values: #{JSON.stringify pointers}"
schema_references: (context, schema) ->
if !@test_type "object", schema
throw new Error "Schema must be an object - #{context.pointer}"
{scope, pointer} = context
@register pointer, schema
# This is one of the two cases where we pay attention to an "id"
# attribute. The other is top-level id declaration, serving to identify
# the entire schema.
#
# Here, we treat bare fragment identifiers (e.g. "#user") as aliases.
if schema.id && schema.id.indexOf("#") == 0
uri = URI.resolve scope, schema.id
schema.id = uri
@register uri, schema
for attribute, definition of schema
if "$ref" == attribute
@resolve_reference(context, schema, definition)
else
new_context = context.child(attribute)
if "properties" == attribute
@properties_references new_context, definition
else if "items" == attribute
@items_references new_context, definition
else if "definitions" == attribute
@definitions_references new_context, definition
else if @test_type "object", definition
@schema_references new_context, definition
else if attribute in ["allOf", "anyOf", "not"]
for s, i in definition
@schema_references new_context.child(i), s
resolve_reference: (context, schema, definition) ->
{scope, pointer} = context
# turn relative refs into absolute URIs
uri = URI.resolve(scope, definition)
# When the URI of a $ref is a substring of the present context's URI,
# we're in a recursive reference situation.
# Ignore recursive references during this stage.
if pointer.indexOf(uri + "/") != 0
if (found_schema = @resolve_uri(uri, scope))?
delete schema.$ref
for k, v of found_schema
schema[k] = v
@schema_references context, schema
else
# Store the unresolvable reference so we can try to resolve
# it again after having traversed the all schemas.
@unresolved[pointer] = {scope, uri}
properties_references: (context, properties) ->
if !@test_type "object", properties
throw new Error "Properties must be an object - #{context.pointer}"
for property, schema of properties
@schema_references context.child(property), schema
items_references: (context, definition) ->
if @test_type "array", definition
for def, i in definition
@schema_references context.child(i), def
else
@schema_references context, definition
definitions_references: (context, object) ->
if !@test_type "object", object
throw new Error "Value of 'definitions' must be an object - #{context.pointer}"
for name, schema of object
@schema_references context.child(name), schema
compile: (context, schema) ->
{scope, pointer} = context
tests = []
# When the schema contains the $ref attribute, locate the referenced
# schema and use in place of the present schema.
if (uri = schema.$ref)?
if @uris[uri]
return (args...) =>
@uris[uri]._test(args...)
uri = URI.resolve(scope, uri)
if pointer.indexOf(uri) == 0
# When the URI of a $ref is a substring of the present context's URI,
# we're in a recursive reference situation.
return @recursive_test(schema, context)
schema = @find(uri)
if !schema
throw new Error "No schema found for $ref '#{uri}'"
for key, definition of schema when key != "_test"
# Create a child context to track our progress into a new attribute.
new_context = context.attribute(key)
if @[key]?
test = @compile_attribute(new_context, key, schema, definition)
tests.push(test) if test
else
# If the key doesn't correspond to a known attribute name, treat
# the object as a container of definitions.
@compile_definitions(new_context, definition)
test_function = (data, runtime) ->
return null if typeof(data) == "undefined"
runtime.tested_item()
for test in tests
test(data, runtime)
null
# Record the test function for use by such things as @recursive_test.
@find(pointer)?._test = test_function
# Also record the function for schemas with "alias" ids.
if schema.id
uri = URI.resolve scope, schema.id
@find(uri)?._test = test_function
return test_function
compile_attribute: (context, attribute, schema, definition) ->
# Some validation attributes can be modified by other attributes
# at the same level. E.g. minimum is modified by exclusiveMinimum.
# Here we check the schema for such auxiliary attributes and stow
# them in the context, so the primary attribute handler can act
# on them.
context.modifiers = {}
if (modifiers = Validator.modifiers[attribute])?
for key in modifiers
context.modifiers[key] = schema[key]
# Call the attribute's handler.
# The return value will be a function that validates a document.
# In rare cases, the attribute handler does not return a test
# function, because some related attribute performs the test.
if @[attribute]?
if (test = @[attribute](definition, context))?
return test
compile_definitions: (context, object) ->
if @is_schema(object)
@compile(context, object)
else if @test_type "object", object
for name, definition of object
@compile_definitions context.child(name), definition
is_schema: (object) ->
object.type? || object.$ref? ||
object.allOf? || object.anyOf? || object.not?
recursive_test: (schema, {scope, pointer}) ->
uri = URI.resolve(scope, schema.$ref)
if (schema = @find uri)?
(data, runtime) ->
schema._test(data, runtime)
else
throw new Error "No schema found for $ref '#{uri}'"
================================================
FILE: tasks/build/benchmarks.coffee
================================================
fs = require "fs"
shell = require "shelljs"
result = shell.exec "coffee benchmarks"
fs.writeFileSync "benchmarks/results/all.txt", result.output
================================================
FILE: tasks/build/browser.coffee
================================================
{exec} = require "../helpers"
require "./javascript"
base = "node_modules/.bin/cjsify lib/index.js --no-node --export JSCK"
# https://www.npmjs.com/package/commonjs-everywhere
exec "#{base} -o jsck.js"
exec "#{base} -m -o jsck.min.js"
================================================
FILE: tasks/build/docs.coffee
================================================
{build_docs} = require "../helpers"
build_docs()
================================================
FILE: tasks/build/index.coffee
================================================
require "./javascript"
require "./docs"
================================================
FILE: tasks/build/javascript.coffee
================================================
{exec} = require "../helpers"
exec "mkdir -p lib"
exec "coffee --compile --bare --output lib/ src/"
================================================
FILE: tasks/helpers.coffee
================================================
shell = require "shelljs"
exec = (cmd) ->
console.log cmd
shell.exec cmd
docs =
"doc/README.pfm.md": "README.md"
"doc/tests.pfm.md": "doc/tests.md"
"doc/benchmarks.pfm.md": "doc/benchmarks.md"
module.exports =
exec: exec
docs: docs
build_docs: (name) ->
# pfm is a rubygem for converting Panda Flavored Markdown into
# GitHub Flavored Markdown.
# gem install pfm
if name
destination = docs[name]
exec "pfm #{name} -o #{destination}"
else
for source, destination of docs
exec "pfm #{source} -o #{destination}"
================================================
FILE: tasks/update/index.coffee
================================================
require "./submodules"
================================================
FILE: tasks/update/submodules.coffee
================================================
{exec} = require "../helpers"
exec "git submodule init"
exec "git submodule update"
================================================
FILE: tasks/watch/docs.coffee
================================================
fs = require "fs"
{docs, build_docs} = require "../helpers"
for source, _dest of docs
do (source) ->
fs.watchFile source, (curr, prev) ->
build_docs(source)
================================================
FILE: tasks/watch/src.coffee
================================================
fs = require "fs"
{exec} = require "../helpers"
exec "coffee --compile --watch --bare --output lib/ src/", ->
================================================
FILE: test/.gitignore
================================================
suite
suite.tgz
================================================
FILE: test/draft3/builtins.coffee
================================================
assert = require "assert"
Testify = require "testify"
JSCK = require("../../src/index").draft3
Testify.test "Built in schemas", (context) ->
context.test "$ref to draft 3 works", ->
jsck = new JSCK
type: "object"
properties:
foo:
$ref: "http://json-schema.org/draft-03/schema#"
report = jsck.validate
foo:
properties: {}
assert.equal report.valid, true
report = jsck.validate
foo:
properties: "smurf"
assert.equal report.valid, false
================================================
FILE: test/draft3/index.coffee
================================================
require "./unit"
require "./official"
require "./builtins"
# TODO: implement these tests for draft3
#require "./invalid"
#require "./valid"
================================================
FILE: test/draft3/official.coffee
================================================
{draft3} = require "../../src/index"
suite = require "json-schema-tests"
[_node, _script, attribute, test_number] = process.argv
suite {
attribute
test_number
version: "draft3"
validate: (schema, document) ->
v = new draft3(schema)
v.validate(document)
ignores:
# Doubtful value for the majority of use cases.
# https://github.com/pandastrike/jsck/issues/42
minLength: [
"one supplementary Unicode code point is not long enough"
]
maxLength: [
"two supplementary Unicode code points is long enough"
]
# Not supported because of the potential performance implications
# https://github.com/pandastrike/jsck/issues/2
uniqueItems: true
# Impossible to test when using output of JSON.parse
# https://github.com/pandastrike/jsck/issues/6
"optional/zeroTerminatedFloats": true
# The following items require fetching of remote schemas.
# Support for remote references is planned for the next version of JSCK
refRemote: true
ref: [
"remote ref, containing refs itself"
]
definitions: true
}
================================================
FILE: test/draft3/unit/errors.coffee
================================================
assert = require "assert"
Testify = require "testify"
JSCK = require("../../src/index").draft3
jsck = new JSCK
type: "object"
additionalProperties:
type: "object"
additionalProperties: false
properties:
resource:
required: true
type: "string"
url:
type: "string"
report = jsck.validate
smurf: {url: 4}
console.log()
console.log report.errors[0].schema.pointer
console.log()
================================================
FILE: test/draft3/unit/index.coffee
================================================
#require "./errors"
require "./references"
require "./uri_test"
================================================
FILE: test/draft3/unit/references.coffee
================================================
assert = require "assert"
Testify = require "testify"
JSCK = require("../../../src/index").draft3
Testify.test "JSCK draft 3 dereferencing", (context) ->
context.test "Finding by uri", (context) ->
jsck = new JSCK
$schema: "http://json-schema.org/draft-03/schema#"
id: "urn:jsck.test"
type: "object"
properties:
user_list:
id: "#user_list"
type: "array"
items: {$ref: "#/properties/user"}
user_dict: {$ref: "#/properties/user"}
user:
id: "#user"
type: "object"
properties:
name:
required: true
type: "string"
email:
type: "string"
context.test "JSON Pointer", ->
result = jsck.validator("urn:jsck.test#/properties/user").validate {name: "automatthew"}
assert.equal result.valid, true
context.test "id fragment", ->
result = jsck.validator("urn:jsck.test#user").validate {name: "automatthew"}
assert.equal result.valid, true
context.test "schema without 'id'", (context) ->
test_schema =
definitions:
schema1:
id: "#foo"
type: "string"
format: "uri"
jsck = new JSCK(test_schema)
context.test "JSON pointers", (context) ->
context.test "Pointer relative to empty URI", ->
schema = jsck.find "#/foo"
assert.deepEqual schema, test_schema.schema1
return
# FIXME: find out whether these tests fail because of app problems
# or test problems.
context.test "find", (context) ->
test_schema =
id: "http://x.y.z/rootschema.json#"
schema1:
id: "#foo"
schema2:
id: "otherschema.json"
type: "string"
nested:
id: "#bar"
alsonested:
id: "t/inner.json#a"
schema3:
id: "some://where.else/completely#"
schema4:
$ref: "#foo"
jsck = new JSCK(test_schema)
context.test "JSON pointers", (context) ->
context.test "Absolute URI", ->
schema = jsck.find "http://x.y.z/rootschema.json#/schema1"
assert.deepEqual schema, test_schema.schema1
schema = jsck.find "http://x.y.z/rootschema.json#/schema2/nested"
assert.deepEqual schema, test_schema.schema2.nested
context.test "Setting scope with 'id'", (context) ->
context.test "works for fragment", ->
schema = jsck.find "http://x.y.z/rootschema.json#foo"
assert.deepEqual schema, test_schema.schema1
context.test "ignores path change", ->
schema = jsck.find "http://x.y.z/otherschema.json#bar"
assert.deepEqual schema, undefined
context.test "ignores nested path change", ->
schema = jsck.find "http://x.y.z/t/inner.json#a"
assert.deepEqual schema, undefined
context.test "Inline reference resolution", ->
schema = jsck.find "http://x.y.z/rootschema.json#/schema4"
assert.deepEqual schema, test_schema.schema1
================================================
FILE: test/draft3/unit/uri_test.coffee
================================================
assert = require "assert"
Testify = require "testify"
URI = require "../../../src/uri"
Testify.test "URI helper methods", (context) ->
context.test "is_absolute", (context) ->
context.test "URNs", ->
assert.equal URI.is_absolute("urn:foo:bar:baz"), true
assert.equal URI.is_absolute("urn"), false
context.test "URLs", ->
assert.equal URI.is_absolute("http://example.com:1452/smurf"), true
assert.equal URI.is_absolute("example.com"), false
context.test "is_url", ->
assert.equal URI.is_url("https://monkeyshines.org/x?y=z"), true
assert.equal URI.is_url("urn:foo:bar/baz,bat"), false
context.test "resolve", (context) ->
context.test "URL scope", (context) ->
scope = "http://pandastrike.com/patchboard.json#"
context.test "absolute reference", ->
assert.equal URI.resolve(scope, "http://example.com/schema.json#"),
"http://example.com/schema.json#"
context.test "fragment", ->
assert.equal URI.resolve(scope, "#service"),
"http://pandastrike.com/patchboard.json#service"
context.test "root reference", ->
assert.equal URI.resolve(scope, "#"),
"http://pandastrike.com/patchboard.json#"
context.test "fragment reference", ->
assert.equal URI.resolve(scope, "#/properties/resource"),
"http://pandastrike.com/patchboard.json#/properties/resource"
context.test "hierarchical references", ->
assert.equal URI.resolve(scope, "other.json"),
"http://pandastrike.com/other.json"
scope = "http://pandastrike.com/schemas/patchboard.json#"
assert.equal URI.resolve(scope, "other.json"),
"http://pandastrike.com/schemas/other.json"
context.test "scope reference, root reference", ->
s = "http://pandastrike.com/patchboard.json#/properties/foo"
assert.equal URI.resolve(s, "#"),
"http://pandastrike.com/patchboard.json#"
context.test "URI other than URL scope", (context) ->
scope = "urn:jsck.anon#"
context.test "fragment", ->
assert.equal URI.resolve(scope, "#service"),
"urn:jsck.anon#service"
context.test "hierarchical references", ->
assert.equal URI.resolve(scope, "other.json"),
"urn:jsck.anon/other.json"
================================================
FILE: test/draft4/adhoc.coffee
================================================
assert = require "assert"
Testify = require "testify"
JSCK = require("../../src/index").draft4
Testify.test "Ad hoc tests", (context) ->
context.test "undefinedness", (context) ->
jsck = new JSCK
$schema: "http://json-schema.org/draft-04/schema#"
type: "object"
properties:
foo:
type: [ "number" ]
context.test "valid", ->
result = jsck.validate
foo: undefined
assert.equal result.valid, true
result = jsck.validate
foo: 42
assert.equal result.valid, true
result = jsck.validate Object.create({foo: 42})
assert.equal result.valid, true
context.test "invalid", ->
result = jsck.validate
foo: null
assert.equal result.valid, false
context.test "issue #94 from SciencePiggy", ->
jsck = new JSCK
type: "object"
required: [ "fullName" ]
properties:
fullName:
type: "string"
settings:
$ref: "#/definitions/userSettings"
definitions:
userSettings:
type: "object"
required: [ "language" ]
properties:
language:
type: "string"
result = jsck.validate
fullName: "Homer Simpson"
settings: null
assert.equal result.valid, false
================================================
FILE: test/draft4/builtins.coffee
================================================
assert = require "assert"
Testify = require "testify"
JSCK = require("../../src/index").draft4
Testify.test "Built in schemas", (context) ->
context.test "$ref to draft 4 works", ->
jsck = new JSCK
type: "object"
properties:
foo:
$ref: "http://json-schema.org/draft-04/schema#"
report = jsck.validate
foo:
properties: {}
assert.equal report.valid, true
report = jsck.validate
foo:
properties: "smurf"
assert.equal report.valid, false
================================================
FILE: test/draft4/index.coffee
================================================
require "./unit"
require "./official"
require "./invalid"
require "./valid"
require "./builtins"
================================================
FILE: test/draft4/invalid/additionalItems.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be either a boolean or an object"
schemas: for value in json_types.except("boolean", "object")
# additionalItems only takes effect when the value of 'items' is an array
items: [ {} ]
additionalItems: value
}
]
================================================
FILE: test/draft4/invalid/additionalProperties.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be a boolean or an object"
schemas: for value in json_types.except("boolean", "object")
additionalProperties: value
}
]
================================================
FILE: test/draft4/invalid/allOf.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be an array"
schemas: for value in json_types.except("array")
allOf: value
}
{
description: "array MUST have at least one element"
schemas: [
allOf: []
]
}
{
description: "elements of the array MUST be objects"
schemas: for value in json_types.except("object")
allOf: [ value ]
}
]
================================================
FILE: test/draft4/invalid/anyOf.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be an array"
schemas: for value in json_types.except("array")
anyOf: value
}
{
description: "array MUST have at least one element"
schemas: [
anyOf: []
]
}
{
description: "elements of the array MUST be objects"
schemas: for value in json_types.except("object")
anyOf: [ value ]
}
]
================================================
FILE: test/draft4/invalid/dependencies.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be an object"
schemas: for value in json_types.except("object")
dependencies: value
}
{
description: "each value MUST be either an object or an array"
schemas: for value in json_types.except("object", "array")
dependencies:
foo: value
}
{
description: "array values must have at least one element"
schemas: [
dependencies:
foo: []
]
}
{
description: "array values must contain only strings"
schemas: [
dependencies:
foo: [33]
]
}
]
================================================
FILE: test/draft4/invalid/items.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be either an object or an array"
schemas: for value in json_types.except("object", "array")
{items: value}
}
{
description: "arrays may only contain objects"
schemas: for value in json_types.except("object")
{items: [value]}
}
]
================================================
FILE: test/draft4/invalid/not.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be an object"
schemas: for value in json_types.except("object")
not: value
}
]
================================================
FILE: test/draft4/invalid/numbers.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "'multipleOf' value MUST be a number"
schemas: for value in json_types.except("number", "integer")
multipleOf: value
}
]
================================================
FILE: test/draft4/invalid/oneOf.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be an array"
schemas: for value in json_types.except("array")
oneOf: value
}
{
description: "array MUST have at least one element"
schemas: [
oneOf: []
]
}
{
description: "elements of the array MUST be objects"
schemas: for value in json_types.except("object")
oneOf: [ value ]
}
]
================================================
FILE: test/draft4/invalid/patternProperties.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be an object"
schemas: for value in json_types.except("object")
patternProperties: value
}
{
description: "object should not be empty"
schemas: for value in json_types.except("object")
patternProperties: {}
}
{
description: "names should be valid regexes"
schemas: [
{
patternProperties:
"[": {}
}
]
}
{
description: "object values MUST themselves be objects"
schemas: for value in json_types.except("object")
patternProperties:
foo: value
}
]
================================================
FILE: test/draft4/invalid/properties.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be an object"
schemas: for value in json_types.except("object")
properties: value
}
{
description: "property values must themselves be objects"
schemas: for value in json_types.except("object")
properties:
big: value
}
]
================================================
FILE: test/draft4/invalid/required.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be an array"
schemas: for value in json_types.except("array")
required: value
}
{
description: "the array must have at least one value"
schemas: [
required: []
]
}
{
description: "the array values must be strings"
schemas: for value in json_types.except("string")
required: [value]
}
]
================================================
FILE: test/draft4/invalid/strings.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "'pattern' value must be a string"
schemas: for value in json_types.except("string")
pattern: value
}
{
description: "'maxLength' value must be an integer"
schemas: for value in json_types.except("integer")
maxLength: value
}
{
description: "'minLength' value must be an integer"
schemas: for value in json_types.except("integer")
minLength: value
}
]
================================================
FILE: test/draft4/invalid/type.coffee
================================================
module.exports = ({json_types}) ->
[
{
description: "value MUST be either a string or an array"
schemas: for value in json_types.except("string", "array")
{type: value}
}
{
description: "arrays may not be empty"
schemas: [
{type: []}
]
}
{
description: "arrays may only contain strings"
schemas: for value in json_types.except("string")
{type: value}
}
{
description: "string values must be one of the primitive types"
schemas: [
type: "bogus"
]
}
]
================================================
FILE: test/draft4/invalid.coffee
================================================
glob = require "glob"
assert = require "assert"
Testify = require "testify"
{draft4} = require("../../src/index")
[_, _, single_attr] = process.argv
helpers =
json_types:
values:
integer: 2
number: 2.1
string: "foo"
object: {}
array: [ ]
boolean: false
null: null
except: (names...) ->
for name, value of @values when !(name in names)
value
Testify.test "Rejecting invalid schemas", (context) ->
files = glob.sync("#{__dirname}/invalid/*.coffee").sort()
l = "#{__dirname}/invalid/".length
for file in files
# chomp .coffee
attribute_name = file.slice(l, -7)
if single_attr && attribute_name != single_attr
continue
context.test attribute_name, (context) ->
tests = require(file)
# Dependency injection, where needed
if tests.constructor == Function
tests = tests(helpers)
for test in tests
context.test test.description, ->
for schema in test.schemas
try
new draft4(schema)
context.fail "#{test.description} - #{JSON.stringify schema}"
catch e
if test.debug
console.log "\n", attribute_name, "-", test.description
console.log schema
console.log e.stack
================================================
FILE: test/draft4/official.coffee
================================================
{draft4} = require "../../src/index"
suite = require "json-schema-tests"
#shell = require "shelljs"
#cmd = "node_modules/.bin/nserver -p 5725 -d test/JSON-Schema-Test-Suite/remotes"
#proc = shell.exec cmd, (code, output) ->
#
#Testify = require "testify"
#Testify.once "done", ->
#console.log "Shutting down the 'remotes' test server"
#proc.kill("SIGTERM")
[_node, _script, attribute, test_number] = process.argv
suite {
attribute
test_number
version: "draft4"
validate: (schema, document) ->
v = new draft4(schema)
v.validate(document)
ignores:
# Doubtful value for the majority of use cases.
# https://github.com/pandastrike/jsck/issues/42
minLength: [
"one supplementary Unicode code point is not long enough"
]
maxLength: [
"two supplementary Unicode code points is long enough"
]
# Not supported because of the potential performance implications
# https://github.com/pandastrike/jsck/issues/2
uniqueItems: true
# Impossible to test when using output of JSON.parse
# https://github.com/pandastrike/jsck/issues/6
"optional/zeroTerminatedFloats": true
# The following items require fetching of remote schemas.
# Support for remote references is planned for the next version of JSCK
refRemote: true
ref: [
"remote ref, containing refs itself"
]
definitions: true
}
================================================
FILE: test/draft4/unit/errors.coffee
================================================
assert = require "assert"
Testify = require "testify"
JSCK = require("../../src/index").draft3
jsck = new JSCK
type: "object"
additionalProperties:
type: "object"
additionalProperties: false
properties:
resource:
required: true
type: "string"
url:
type: "string"
report = jsck.validate
smurf: {url: 4}
console.log()
console.log report.errors[0].schema.pointer
console.log()
================================================
FILE: test/draft4/unit/index.coffee
================================================
#require "./errors"
require "./references"
require "./uri_test"
================================================
FILE: test/draft4/unit/logical.coffee
================================================
Testify = require "testify"
assert = require "assert"
JSCK = require("../../../src")
jsck = new JSCK.draft4
definitions:
assetType:
type: "string"
enum: [
"vote"
"image"
"audio"
"video"
]
campaign:
base:
type: "object"
properties:
name:
type: "string"
metadata:
type: "object"
assetTypes:
type: "array"
items: {$ref: "#/definitions/assetType"}
active:
type: "boolean"
startTime:
type: "string"
format: "date-time"
endTime:
type: "string"
format: "date-time"
create:
allOf: [
{$ref: "#/definitions/campaign/base"}
{
required: ["name", "assetTypes"]
}
]
validator = jsck.validator("#/definitions/campaign/create")
Testify.test "using logical keywords", (context) ->
context.test "allOf", ->
{errors} = validator.validate
name: "foobar"
assetTypes: ["vote"]
startTime: "monkey"
assert.equal errors.length, 0, "Unexpected errors:\n#{JSON.stringify(errors, null, 2)}"
================================================
FILE: test/draft4/unit/references.coffee
================================================
assert = require "assert"
Testify = require "testify"
JSCK = require("../../../src/index").draft4
Testify.test "JSCK draft 4 dereferencing", (context) ->
context.test "Finding by uri", (context) ->
jsck = new JSCK
$schema: "http://json-schema.org/draft-04/schema#"
id: "urn:jsck.test"
type: "object"
properties:
user_list:
id: "#user_list"
type: "array"
items: {$ref: "#/properties/user"}
user_dict: {$ref: "#/properties/user"}
user:
id: "#user"
type: "object"
required: ["name"]
properties:
name:
type: "string"
email:
type: "string"
context.test "JSON Pointer", ->
result = jsck.validator("urn:jsck.test#/properties/user").validate {name: "automatthew"}
assert.equal result.valid, true
context.test "id fragment", ->
result = jsck.validator("urn:jsck.test#user").validate {name: "automatthew"}
assert.equal result.valid, true
context.test "schema without 'id'", (context) ->
test_schema =
definitions:
schema1:
id: "#foo"
type: "string"
format: "uri"
jsck = new JSCK(test_schema)
context.test "JSON pointers", (context) ->
context.test "Pointer relative to empty URI", ->
schema = jsck.find "#/foo"
assert.deepEqual schema, test_schema.schema1
return
# FIXME: find out whether these tests fail because of app problems
# or test problems.
context.test "find", (context) ->
test_schema =
id: "http://x.y.z/rootschema.json#"
schema1:
id: "#foo"
schema2:
id: "otherschema.json"
type: "string"
nested:
id: "#bar"
alsonested:
id: "t/inner.json#a"
schema3:
id: "some://where.else/completely#"
schema4:
$ref: "#foo"
jsck = new JSCK(test_schema)
context.test "JSON pointers", (context) ->
context.test "Absolute URI", ->
schema = jsck.find "http://x.y.z/rootschema.json#/schema1"
assert.deepEqual schema, test_schema.schema1
schema = jsck.find "http://x.y.z/rootschema.json#/schema2/nested"
assert.deepEqual schema, test_schema.schema2.nested
context.test "Setting scope with 'id'", (context) ->
context.test "works for fragment", ->
schema = jsck.find "http://x.y.z/rootschema.json#foo"
assert.deepEqual schema, test_schema.schema1
context.test "ignores path change", ->
schema = jsck.find "http://x.y.z/otherschema.json#bar"
assert.deepEqual schema, undefined
context.test "ignores nested path change", ->
schema = jsck.find "http://x.y.z/t/inner.json#a"
assert.deepEqual schema, undefined
context.test "Inline reference resolution", ->
schema = jsck.find "http://x.y.z/rootschema.json#/schema4"
assert.deepEqual schema, test_schema.schema1
================================================
FILE: test/draft4/unit/uri_test.coffee
================================================
assert = require "assert"
Testify = require "testify"
URI = require "../../../src/uri"
Testify.test "URI helper methods", (context) ->
context.test "is_absolute", (context) ->
context.test "URNs", ->
assert.equal URI.is_absolute("urn:foo:bar:baz"), true
assert.equal URI.is_absolute("urn"), false
context.test "URLs", ->
assert.equal URI.is_absolute("http://example.com:1452/smurf"), true
assert.equal URI.is_absolute("example.com"), false
context.test "is_url", ->
assert.equal URI.is_url("https://monkeyshines.org/x?y=z"), true
assert.equal URI.is_url("urn:foo:bar/baz,bat"), false
context.test "resolve", (context) ->
context.test "URL scope", (context) ->
scope = "http://pandastrike.com/patchboard.json#"
context.test "absolute reference", ->
assert.equal URI.resolve(scope, "http://example.com/schema.json#"),
"http://example.com/schema.json#"
context.test "fragment", ->
assert.equal URI.resolve(scope, "#service"),
"http://pandastrike.com/patchboard.json#service"
context.test "root reference", ->
assert.equal URI.resolve(scope, "#"),
"http://pandastrike.com/patchboard.json#"
context.test "fragment reference", ->
assert.equal URI.resolve(scope, "#/properties/resource"),
"http://pandastrike.com/patchboard.json#/properties/resource"
context.test "hierarchical references", ->
assert.equal URI.resolve(scope, "other.json"),
"http://pandastrike.com/other.json"
scope = "http://pandastrike.com/schemas/patchboard.json#"
assert.equal URI.resolve(scope, "other.json"),
"http://pandastrike.com/schemas/other.json"
context.test "scope reference, root reference", ->
s = "http://pandastrike.com/patchboard.json#/properties/foo"
assert.equal URI.resolve(s, "#"),
"http://pandastrike.com/patchboard.json#"
context.test "URI other than URL scope", (context) ->
scope = "urn:jsck.anon#"
context.test "fragment", ->
assert.equal URI.resolve(scope, "#service"),
"urn:jsck.anon#service"
context.test "hierarchical references", ->
assert.equal URI.resolve(scope, "other.json"),
"urn:jsck.anon/other.json"
================================================
FILE: test/draft4/valid/basic.coffee
================================================
module.exports = ({json_types}) ->
"may contain properties which are not schema keywords":
schemas:
"string value":
monkey: "shines"
"integer value":
monkey: 84
"schema value":
monkey:
type: "array"
================================================
FILE: test/draft4/valid/definitions.coffee
================================================
module.exports = ({json_types}) ->
"known attribute name as key of 'definitions' dictionary":
schemas:
required:
definitions:
required:
type: "object"
id:
definitions:
id:
type: "object"
items:
definitions:
items:
type: "object"
"nested definitions":
schemas:
foo:
definitions:
bar: {type: "string"}
definitions:
baz: {type: "string"}
================================================
FILE: test/draft4/valid/properties.coffee
================================================
module.exports = ({json_types}) ->
"known attribute name":
schemas:
required:
type: "object"
properties:
required:
type: "boolean"
id:
type: "object"
properties:
id:
type: "string"
properties:
type: "object"
properties:
properties: {type: "array"}
================================================
FILE: test/draft4/valid.coffee
================================================
glob = require "glob"
assert = require "assert"
Testify = require "testify"
{draft4} = require("../../src/index")
helpers =
json_types:
values:
integer: 2
number: 2.1
string: "foo"
object: {}
array: [ ]
boolean: false
null: null
except: (names...) ->
for name, value of @values when !(name in names)
value
Testify.test "Accepting valid schemas", (context) ->
files = glob.sync("#{__dirname}/valid/*.coffee").sort()
l = "#{__dirname}/valid/".length
for file in files
# chomp .coffee
attribute_name = file.slice(l, -7)
context.test attribute_name, (context) ->
tests = require(file)
# Dependency injection, where needed
if tests.constructor == Function
tests = tests(helpers)
for name, test of tests
context.test name, (context) ->
for k, schema of test.schemas
context.test k, ->
try
new draft4(schema)
catch e
console.log e.stack
context.fail(e)
================================================
FILE: test/index.coffee
================================================
require "./draft3/unit"
require "./draft3"
require "./draft4/unit"
require "./draft4"
gitextract__sjmg1fx/
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── benchmarks/
│ ├── benchmark.coffee
│ ├── draft3/
│ │ ├── index.coffee
│ │ ├── medium/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ └── valid_doc.coffee
│ │ ├── trivial/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ └── valid_doc.coffee
│ │ └── validators.coffee
│ ├── draft4/
│ │ ├── complex/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ ├── test.coffee
│ │ │ └── valid_doc.coffee
│ │ ├── index.coffee
│ │ ├── medium/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ └── valid_doc.coffee
│ │ ├── trivial/
│ │ │ ├── index.coffee
│ │ │ ├── schema.coffee
│ │ │ └── valid_doc.coffee
│ │ └── validators.coffee
│ ├── index.coffee
│ ├── results/
│ │ └── all.txt
│ ├── runner.coffee
│ └── statistics.js
├── doc/
│ ├── README.pfm.md
│ ├── benchmarks.md
│ ├── benchmarks.pfm.md
│ ├── tests.md
│ └── tests.pfm.md
├── examples/
│ └── draft4/
│ ├── advanced.coffee
│ └── basic.coffee
├── package.json
├── schemas/
│ ├── draft-03/
│ │ └── schema.json
│ └── draft-04/
│ └── schema.json
├── src/
│ ├── common/
│ │ ├── arrays.coffee
│ │ ├── comparison.coffee
│ │ ├── numeric.coffee
│ │ ├── objects.coffee
│ │ ├── strings.coffee
│ │ └── type.coffee
│ ├── draft3/
│ │ ├── logical.coffee
│ │ ├── numeric.coffee
│ │ ├── objects.coffee
│ │ └── strings.coffee
│ ├── draft3.coffee
│ ├── draft4/
│ │ ├── logical.coffee
│ │ ├── numeric.coffee
│ │ ├── objects.coffee
│ │ ├── strings.coffee
│ │ └── type.coffee
│ ├── draft4.coffee
│ ├── index.coffee
│ ├── uri.coffee
│ ├── util.coffee
│ └── validator.coffee
├── tasks/
│ ├── build/
│ │ ├── benchmarks.coffee
│ │ ├── browser.coffee
│ │ ├── docs.coffee
│ │ ├── index.coffee
│ │ └── javascript.coffee
│ ├── helpers.coffee
│ ├── update/
│ │ ├── index.coffee
│ │ └── submodules.coffee
│ └── watch/
│ ├── docs.coffee
│ └── src.coffee
└── test/
├── .gitignore
├── draft3/
│ ├── builtins.coffee
│ ├── index.coffee
│ ├── official.coffee
│ └── unit/
│ ├── errors.coffee
│ ├── index.coffee
│ ├── references.coffee
│ └── uri_test.coffee
├── draft4/
│ ├── adhoc.coffee
│ ├── builtins.coffee
│ ├── index.coffee
│ ├── invalid/
│ │ ├── additionalItems.coffee
│ │ ├── additionalProperties.coffee
│ │ ├── allOf.coffee
│ │ ├── anyOf.coffee
│ │ ├── dependencies.coffee
│ │ ├── items.coffee
│ │ ├── not.coffee
│ │ ├── numbers.coffee
│ │ ├── oneOf.coffee
│ │ ├── patternProperties.coffee
│ │ ├── properties.coffee
│ │ ├── required.coffee
│ │ ├── strings.coffee
│ │ └── type.coffee
│ ├── invalid.coffee
│ ├── official.coffee
│ ├── unit/
│ │ ├── errors.coffee
│ │ ├── index.coffee
│ │ ├── logical.coffee
│ │ ├── references.coffee
│ │ └── uri_test.coffee
│ ├── valid/
│ │ ├── basic.coffee
│ │ ├── definitions.coffee
│ │ └── properties.coffee
│ └── valid.coffee
└── index.coffee
SYMBOL INDEX (1 symbols across 1 files) FILE: benchmarks/statistics.js function normsinv (line 160) | function normsinv(p)
Condensed preview — 107 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (165K chars).
[
{
"path": ".gitignore",
"chars": 56,
"preview": "lib\nnode_modules\nmine\n.ruby-gemset\nnpm-debug.log\n*.tgz\n\n"
},
{
"path": ".gitmodules",
"chars": 139,
"preview": "[submodule \"test/JSON-Schema-Test-Suite\"]\n\tpath = test/JSON-Schema-Test-Suite\n\turl = git@github.com:json-schema/JSON-Sch"
},
{
"path": "LICENSE",
"chars": 1080,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2013 Matthew King\n\nPermission is hereby granted, free of charge, to any person obta"
},
{
"path": "README.md",
"chars": 5783,
"preview": "# JSON Schema Compiled checK\n\nJSCK is one of the fastest [JSON Schema](http://json-schema.org) validators for Node.js.\nI"
},
{
"path": "benchmarks/benchmark.coffee",
"chars": 869,
"preview": "microtime = require \"microtime\"\nrequire \"./statistics\"\n\nmodule.exports = class Benchmark\n\n @compare: (benchmarks, optio"
},
{
"path": "benchmarks/draft3/index.coffee",
"chars": 94,
"preview": "console.log \"## Benchmarks for Draft 3\"\nconsole.log\n\nrequire \"./trivial/\"\nrequire \"./medium/\"\n"
},
{
"path": "benchmarks/draft3/medium/index.coffee",
"chars": 207,
"preview": "validators = require \"../validators\"\n{benchmark} = require(\"../../runner\")(validators)\n\nbenchmark {\n name: \"Configurati"
},
{
"path": "benchmarks/draft3/medium/schema.coffee",
"chars": 1542,
"preview": "module.exports =\n description: \"A moderately complex schema with some nesting and value constraints\"\n type: \"object\"\n "
},
{
"path": "benchmarks/draft3/medium/valid_doc.coffee",
"chars": 374,
"preview": "module.exports =\n api_server:\n url: \"http://example.com:8998\"\n host: \"example.com\"\n port: 8998\n\n transport:\n "
},
{
"path": "benchmarks/draft3/trivial/index.coffee",
"chars": 197,
"preview": "validators = require \"../validators\"\n{benchmark} = require(\"../../runner\")(validators)\n\nbenchmark {\n name: \"Event\"\n re"
},
{
"path": "benchmarks/draft3/trivial/schema.coffee",
"chars": 376,
"preview": "module.exports =\n description: \"A simple schema, exercising very few attributes\"\n type: \"object\"\n additionalPropertie"
},
{
"path": "benchmarks/draft3/trivial/valid_doc.coffee",
"chars": 140,
"preview": "module.exports =\n origin: \"monkey\"\n name: \"shines\"\n tags: [\"in\", \"the\", \"closet\"]\n timestamp: Date.now()\n data:\n "
},
{
"path": "benchmarks/draft3/validators.coffee",
"chars": 1219,
"preview": "module.exports =\n\n \"JSCK\":\n setup: (schema) ->\n JSCK = require \"../../src/draft3\"\n new JSCK(schema)\n va"
},
{
"path": "benchmarks/draft4/complex/index.coffee",
"chars": 203,
"preview": "validators = require \"../validators\"\n{benchmark} = require(\"../../runner\")(validators)\n\nbenchmark {\n name: \"Transaction"
},
{
"path": "benchmarks/draft4/complex/schema.coffee",
"chars": 3512,
"preview": "\nmodule.exports =\n type: \"array\"\n items: {$ref: \"#transaction\"}\n minItems: 1\n\n definitions:\n\n base58:\n id: \""
},
{
"path": "benchmarks/draft4/complex/test.coffee",
"chars": 715,
"preview": "\nJSCK = require \"../../../src/draft4\"\nschema = require \"./schema\"\nvalid_doc = require \"./valid_doc\"\n\nschema =\n type: \"a"
},
{
"path": "benchmarks/draft4/complex/valid_doc.coffee",
"chars": 4899,
"preview": "$ = module.exports = []\n\n$.push\n metadata:\n amount: 38043749285\n fee: 2 * 10000\n status: \"confirmed\"\n confi"
},
{
"path": "benchmarks/draft4/index.coffee",
"chars": 116,
"preview": "console.log \"## Benchmarks for Draft 4\"\nconsole.log\n\nrequire \"./trivial/\"\nrequire \"./medium/\"\nrequire \"./complex/\"\n\n"
},
{
"path": "benchmarks/draft4/medium/index.coffee",
"chars": 205,
"preview": "validators = require \"../validators\"\n{benchmark} = require(\"../../runner\")(validators)\n\nbenchmark {\n name: \"Configurati"
},
{
"path": "benchmarks/draft4/medium/schema.coffee",
"chars": 1456,
"preview": "module.exports =\n description: \"A moderately complex schema with some nesting and value constraints\"\n type: \"object\"\n "
},
{
"path": "benchmarks/draft4/medium/valid_doc.coffee",
"chars": 374,
"preview": "module.exports =\n api_server:\n url: \"http://example.com:8998\"\n host: \"example.com\"\n port: 8998\n\n transport:\n "
},
{
"path": "benchmarks/draft4/trivial/index.coffee",
"chars": 215,
"preview": "validators = require \"../validators\"\n{benchmark} = require(\"../../runner\")(validators)\n\nbenchmark {\n name: \"Event - Val"
},
{
"path": "benchmarks/draft4/trivial/schema.coffee",
"chars": 394,
"preview": "module.exports =\n description: \"A simple schema, exercising very few attributes\"\n type: \"object\"\n additionalPropertie"
},
{
"path": "benchmarks/draft4/trivial/valid_doc.coffee",
"chars": 140,
"preview": "module.exports =\n origin: \"monkey\"\n name: \"shines\"\n tags: [\"in\", \"the\", \"closet\"]\n timestamp: Date.now()\n data:\n "
},
{
"path": "benchmarks/draft4/validators.coffee",
"chars": 3011,
"preview": "module.exports =\n\n \"JSCK\":\n setup: (schema) ->\n JSCK = require \"../../src/draft4\"\n new JSCK(schema)\n va"
},
{
"path": "benchmarks/index.coffee",
"chars": 41,
"preview": "require \"./draft3/\"\nrequire \"./draft4/\"\n\n"
},
{
"path": "benchmarks/results/all.txt",
"chars": 4669,
"preview": "## Benchmarks for Draft 3\n\nSchema: 'Event'. A simple schema, exercising very few attributes\nSample size: 64\nValidations"
},
{
"path": "benchmarks/runner.coffee",
"chars": 1748,
"preview": "# stdlib\nutil = require \"util\"\n\n# our simple benchmarking library\nBenchmark = require \"./benchmark.coffee\"\nsamples = 64\n"
},
{
"path": "benchmarks/statistics.js",
"chars": 7260,
"preview": "/****************************************************************************************************\nJavaScript Math an"
},
{
"path": "doc/README.pfm.md",
"chars": 5233,
"preview": "# JSON Schema Compiled checK\n\nJSCK is one of the fastest [JSON Schema](http://json-schema.org) validators for Node.js.\nI"
},
{
"path": "doc/benchmarks.md",
"chars": 7149,
"preview": "# Benchmarks\n\nJSCK has fairly comprehensive benchmarks which show it to be one of the fastest\nJSON Schema validators ava"
},
{
"path": "doc/benchmarks.pfm.md",
"chars": 2100,
"preview": "# Benchmarks\n\nJSCK has fairly comprehensive benchmarks which show it to be one of the fastest\nJSON Schema validators ava"
},
{
"path": "doc/tests.md",
"chars": 5922,
"preview": "# Testing JSCK\n\nJSCK is developed against several sets of tests:\n\n* the official [JSON Schema Test Suite][canonical]\n* t"
},
{
"path": "doc/tests.pfm.md",
"chars": 4354,
"preview": "# Testing JSCK\n\nJSCK is developed against several sets of tests:\n\n* the official [JSON Schema Test Suite][canonical]\n* t"
},
{
"path": "examples/draft4/advanced.coffee",
"chars": 1351,
"preview": "JSCK = require(\"../../src/index\")\n\n# using a schema that declares a URI with \"id\"\n\njsck = new JSCK.draft4\n $schema: \"ht"
},
{
"path": "examples/draft4/basic.coffee",
"chars": 616,
"preview": "JSCK = require(\"../../src/index\")\n\n# Construct a validator for a schema lacking an \"id\" declaration\n\njsck = new JSCK.dra"
},
{
"path": "package.json",
"chars": 949,
"preview": "{\n \"name\": \"jsck\",\n \"version\": \"0.3.2\",\n \"description\": \"JSON Schema Compiled checK\",\n \"scripts\": {\n \"test\": \"cof"
},
{
"path": "schemas/draft-03/schema.json",
"chars": 2700,
"preview": "{\n\t\"$schema\" : \"http://json-schema.org/draft-03/schema#\",\n\t\"id\" : \"http://json-schema.org/draft-03/schema#\",\n\t\"type\" : \""
},
{
"path": "schemas/draft-04/schema.json",
"chars": 4375,
"preview": "{\n \"id\": \"http://json-schema.org/draft-04/schema#\",\n \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n \"de"
},
{
"path": "src/common/arrays.coffee",
"chars": 2223,
"preview": "module.exports =\n\n # handlers\n\n maxItems: (value, context) ->\n (data, runtime) =>\n if @test_type \"array\", data"
},
{
"path": "src/common/comparison.coffee",
"chars": 1161,
"preview": "module.exports =\n\n # handlers\n\n enum: (definition, context) ->\n # TODO: add more cases to the draft3 test suite for"
},
{
"path": "src/common/numeric.coffee",
"chars": 992,
"preview": "module.exports =\n\n minimum: (value, context) ->\n {modifiers: {exclusiveMinimum}} = context\n if exclusiveMinimum\n "
},
{
"path": "src/common/objects.coffee",
"chars": 2487,
"preview": "module.exports =\n\n # handlers\n\n patternProperties: (definition, context) ->\n {additionalProperties} = context.modif"
},
{
"path": "src/common/strings.coffee",
"chars": 1055,
"preview": "module.exports =\n\n pattern: (pattern, context) ->\n unless @test_type \"string\", pattern\n throw new Error \"Value "
},
{
"path": "src/common/type.coffee",
"chars": 2103,
"preview": "module.exports =\n\n # handlers\n\n type: (definition, context) ->\n if @test_type \"array\", definition\n tests = []\n"
},
{
"path": "src/draft3/logical.coffee",
"chars": 1343,
"preview": "URI = require \"../uri\"\nmodule.exports =\n\n extends: (schemas, context) ->\n unless @test_type \"array\", schemas\n s"
},
{
"path": "src/draft3/numeric.coffee",
"chars": 188,
"preview": "module.exports =\n\n divisibleBy: (value, context) ->\n (data, runtime) =>\n if @test_type \"number\", data\n i"
},
{
"path": "src/draft3/objects.coffee",
"chars": 1834,
"preview": "module.exports =\n\n properties: (definition, context) ->\n if !@test_type \"object\", definition\n throw new Error \""
},
{
"path": "src/draft3/strings.coffee",
"chars": 4267,
"preview": "module.exports =\n\n format: (format_name, context) ->\n if format_name == \"regex\"\n (data, runtime) =>\n if "
},
{
"path": "src/draft3.coffee",
"chars": 259,
"preview": "validator = require(\"./validator\")\n\nmodule.exports = validator\n schema_uri: \"http://json-schema.org/draft-03/schema#\"\n "
},
{
"path": "src/draft4/logical.coffee",
"chars": 3529,
"preview": "\nmodule.exports =\n\n anyOf: (definition, context) ->\n unless @test_type \"array\", definition\n throw new Error \"Th"
},
{
"path": "src/draft4/numeric.coffee",
"chars": 368,
"preview": "module.exports =\n\n multipleOf: (value, context) ->\n unless @test_type \"number\", value\n throw new Error \"The 'mu"
},
{
"path": "src/draft4/objects.coffee",
"chars": 3102,
"preview": "module.exports =\n\n # handlers\n\n required: (definition, context) ->\n unless @test_type \"array\", definition\n thr"
},
{
"path": "src/draft4/strings.coffee",
"chars": 4410,
"preview": "module.exports =\n\n format: (format_name, context) ->\n if format_name == \"regex\"\n (data, runtime) =>\n if "
},
{
"path": "src/draft4/type.coffee",
"chars": 1232,
"preview": "\nmodule.exports =\n\n # handlers\n\n type: (definition, context) ->\n if @test_type \"array\", definition\n if definit"
},
{
"path": "src/draft4.coffee",
"chars": 287,
"preview": "validator = require(\"./validator\")\n\nmodule.exports = validator\n schema_uri: \"http://json-schema.org/draft-04/schema#\"\n "
},
{
"path": "src/index.coffee",
"chars": 77,
"preview": "\nmodule.exports =\n draft3: require \"./draft3\"\n draft4: require \"./draft4\"\n\n"
},
{
"path": "src/uri.coffee",
"chars": 486,
"preview": "is_absolute = (string) ->\n /^[\\w\\d+.-]+:/.test(string)\n\nis_url = (string) ->\n /^[\\w\\d+.-]+:\\/\\//.test(string)\n\nresolve"
},
{
"path": "src/util.coffee",
"chars": 1715,
"preview": "module.exports =\n \n escape: (string) ->\n string\n .replace(/~0/g, \"~\")\n .replace(/~1/g, \"/\")\n .replac"
},
{
"path": "src/validator.coffee",
"chars": 11035,
"preview": "URI = require \"./uri\"\n\n{escape, Runtime, Context} = require \"./util\"\n\n# Schemas should always be JSON stringifiable, so "
},
{
"path": "tasks/build/benchmarks.coffee",
"chars": 147,
"preview": "fs = require \"fs\"\nshell = require \"shelljs\"\n\nresult = shell.exec \"coffee benchmarks\"\nfs.writeFileSync \"benchmarks/result"
},
{
"path": "tasks/build/browser.coffee",
"chars": 239,
"preview": "{exec} = require \"../helpers\"\n\nrequire \"./javascript\"\n\nbase = \"node_modules/.bin/cjsify lib/index.js --no-node --export "
},
{
"path": "tasks/build/docs.coffee",
"chars": 51,
"preview": "{build_docs} = require \"../helpers\"\n\nbuild_docs()\n\n"
},
{
"path": "tasks/build/index.coffee",
"chars": 44,
"preview": "require \"./javascript\"\nrequire \"./docs\"\n\n\n\n\n"
},
{
"path": "tasks/build/javascript.coffee",
"chars": 101,
"preview": "{exec} = require \"../helpers\"\n\nexec \"mkdir -p lib\"\nexec \"coffee --compile --bare --output lib/ src/\"\n"
},
{
"path": "tasks/helpers.coffee",
"chars": 580,
"preview": "shell = require \"shelljs\"\n\nexec = (cmd) ->\n console.log cmd\n shell.exec cmd\n\ndocs =\n \"doc/README.pfm.md\": \"README.md\""
},
{
"path": "tasks/update/index.coffee",
"chars": 25,
"preview": "require \"./submodules\"\n\n\n"
},
{
"path": "tasks/update/submodules.coffee",
"chars": 85,
"preview": "{exec} = require \"../helpers\"\n\nexec \"git submodule init\"\nexec \"git submodule update\"\n"
},
{
"path": "tasks/watch/docs.coffee",
"chars": 172,
"preview": "fs = require \"fs\"\n{docs, build_docs} = require \"../helpers\"\n\nfor source, _dest of docs\n do (source) ->\n fs.watchFile"
},
{
"path": "tasks/watch/src.coffee",
"chars": 112,
"preview": "fs = require \"fs\"\n{exec} = require \"../helpers\"\n\nexec \"coffee --compile --watch --bare --output lib/ src/\", ->\n\n"
},
{
"path": "test/.gitignore",
"chars": 16,
"preview": "suite\nsuite.tgz\n"
},
{
"path": "test/draft3/builtins.coffee",
"chars": 523,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nJSCK = require(\"../../src/index\").draft3\n\n\nTestify.test \"Built in"
},
{
"path": "test/draft3/index.coffee",
"chars": 141,
"preview": "require \"./unit\"\nrequire \"./official\"\nrequire \"./builtins\"\n\n# TODO: implement these tests for draft3\n#require \"./invalid"
},
{
"path": "test/draft3/official.coffee",
"chars": 1103,
"preview": "{draft3} = require \"../../src/index\"\nsuite = require \"json-schema-tests\"\n\n[_node, _script, attribute, test_number] = pro"
},
{
"path": "test/draft3/unit/errors.coffee",
"chars": 432,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nJSCK = require(\"../../src/index\").draft3\n\njsck = new JSCK\n type:"
},
{
"path": "test/draft3/unit/index.coffee",
"chars": 64,
"preview": "#require \"./errors\"\nrequire \"./references\"\nrequire \"./uri_test\"\n"
},
{
"path": "test/draft3/unit/references.coffee",
"chars": 3003,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nJSCK = require(\"../../../src/index\").draft3\n\n\nTestify.test \"JSCK "
},
{
"path": "test/draft3/unit/uri_test.coffee",
"chars": 2314,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nURI = require \"../../../src/uri\"\n\nTestify.test \"URI helper method"
},
{
"path": "test/draft4/adhoc.coffee",
"chars": 1302,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nJSCK = require(\"../../src/index\").draft4\n\n\nTestify.test \"Ad hoc t"
},
{
"path": "test/draft4/builtins.coffee",
"chars": 522,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nJSCK = require(\"../../src/index\").draft4\n\n\nTestify.test \"Built in"
},
{
"path": "test/draft4/index.coffee",
"chars": 97,
"preview": "require \"./unit\"\nrequire \"./official\"\nrequire \"./invalid\"\nrequire \"./valid\"\nrequire \"./builtins\"\n"
},
{
"path": "test/draft4/invalid/additionalItems.coffee",
"chars": 328,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be either a boolean or an object\"\n sc"
},
{
"path": "test/draft4/invalid/additionalProperties.coffee",
"chars": 221,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be a boolean or an object\"\n schemas: "
},
{
"path": "test/draft4/invalid/allOf.coffee",
"chars": 446,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be an array\"\n schemas: for value in j"
},
{
"path": "test/draft4/invalid/anyOf.coffee",
"chars": 446,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be an array\"\n schemas: for value in j"
},
{
"path": "test/draft4/invalid/dependencies.coffee",
"chars": 661,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be an object\"\n schemas: for value in "
},
{
"path": "test/draft4/invalid/items.coffee",
"chars": 359,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be either an object or an array\"\n sch"
},
{
"path": "test/draft4/invalid/not.coffee",
"chars": 180,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be an object\"\n schemas: for value in "
},
{
"path": "test/draft4/invalid/numbers.coffee",
"chars": 211,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"'multipleOf' value MUST be a number\"\n schemas: f"
},
{
"path": "test/draft4/invalid/oneOf.coffee",
"chars": 447,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be an array\"\n schemas: for value in j"
},
{
"path": "test/draft4/invalid/patternProperties.coffee",
"chars": 678,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be an object\"\n schemas: for value in "
},
{
"path": "test/draft4/invalid/properties.coffee",
"chars": 360,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be an object\"\n schemas: for value in "
},
{
"path": "test/draft4/invalid/required.coffee",
"chars": 452,
"preview": "module.exports = ({json_types}) ->\n [\n\n {\n description: \"value MUST be an array\"\n schemas: for value in js"
},
{
"path": "test/draft4/invalid/strings.coffee",
"chars": 501,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"'pattern' value must be a string\"\n schemas: for "
},
{
"path": "test/draft4/invalid/type.coffee",
"chars": 585,
"preview": "module.exports = ({json_types}) ->\n\n [\n\n {\n description: \"value MUST be either a string or an array\"\n sche"
},
{
"path": "test/draft4/invalid.coffee",
"chars": 1321,
"preview": "glob = require \"glob\"\nassert = require \"assert\"\nTestify = require \"testify\"\n\n{draft4} = require(\"../../src/index\")\n\n[_, "
},
{
"path": "test/draft4/official.coffee",
"chars": 1394,
"preview": "{draft4} = require \"../../src/index\"\n\nsuite = require \"json-schema-tests\"\n\n\n#shell = require \"shelljs\"\n#cmd = \"node_modu"
},
{
"path": "test/draft4/unit/errors.coffee",
"chars": 432,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nJSCK = require(\"../../src/index\").draft3\n\njsck = new JSCK\n type:"
},
{
"path": "test/draft4/unit/index.coffee",
"chars": 64,
"preview": "#require \"./errors\"\nrequire \"./references\"\nrequire \"./uri_test\"\n"
},
{
"path": "test/draft4/unit/logical.coffee",
"chars": 1222,
"preview": "Testify = require \"testify\"\nassert = require \"assert\"\n\nJSCK = require(\"../../../src\")\n\njsck = new JSCK.draft4\n definiti"
},
{
"path": "test/draft4/unit/references.coffee",
"chars": 3003,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nJSCK = require(\"../../../src/index\").draft4\n\n\nTestify.test \"JSCK "
},
{
"path": "test/draft4/unit/uri_test.coffee",
"chars": 2314,
"preview": "assert = require \"assert\"\nTestify = require \"testify\"\n\nURI = require \"../../../src/uri\"\n\nTestify.test \"URI helper method"
},
{
"path": "test/draft4/valid/basic.coffee",
"chars": 263,
"preview": "module.exports = ({json_types}) ->\n\n \"may contain properties which are not schema keywords\":\n schemas:\n\n \"strin"
},
{
"path": "test/draft4/valid/definitions.coffee",
"chars": 507,
"preview": "module.exports = ({json_types}) ->\n\n \"known attribute name as key of 'definitions' dictionary\":\n schemas:\n\n req"
},
{
"path": "test/draft4/valid/properties.coffee",
"chars": 381,
"preview": "module.exports = ({json_types}) ->\n\n \"known attribute name\":\n schemas:\n\n required:\n type: \"object\"\n "
},
{
"path": "test/draft4/valid.coffee",
"chars": 1078,
"preview": "glob = require \"glob\"\nassert = require \"assert\"\nTestify = require \"testify\"\n\n{draft4} = require(\"../../src/index\")\n\nhelp"
},
{
"path": "test/index.coffee",
"chars": 90,
"preview": "\nrequire \"./draft3/unit\"\nrequire \"./draft3\"\n\nrequire \"./draft4/unit\"\nrequire \"./draft4\"\n\n\n"
}
]
About this extraction
This page contains the full source code of the pandastrike/jsck GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 107 files (145.5 KB), approximately 42.2k tokens, and a symbol index with 1 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.