JSON-Schema-Tiny-0.034 000755 000766 000024 0 15221771652 14014 5 ustar 00ether staff 000000 000000 README 100644 000766 000024 576 15221771652 14745 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 This archive contains the distribution JSON-Schema-Tiny,
version 0.034:
Validate data against a schema, minimally
This software is copyright (c) 2021 by Karen Etheridge.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
This README file was generated by Dist::Zilla::Plugin::Readme v6.037.
Changes 100640 000766 000024 13155 15221771652 15411 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 Revision history for JSON-Schema-Tiny
0.034 2026-07-03 17:38:43Z
- removed dependency on namespace::clean
- config option "max_traversal_depth" has been renamed to
"max_depth"
- marked a problematic bigint test as todo (see issue #9)
0.033 2026-06-19 20:26:49Z
- removed dependencies on strictures, autovivification
0.032 2026-06-19 19:24:46Z
- removed dependencies on Path::Tiny, Ref::Util and Storable
0.031 2025-11-26 18:07:48Z
- update test results and exemptions for TJSA 1.034
- modernize usage of test libraries
0.030 2025-04-22 16:44:19Z
- skip a test of "multipleOf" that fails on ivsize < 8
0.029 2025-04-19 20:33:18Z
- check for builtin::Backport at install time
0.028 2024-11-22 00:06:06Z
- metadata test change only
0.027 2024-11-17 16:51:42Z
- bump builtin::compat prereq for compatibility issues
0.026 2024-11-16 22:05:15Z
- some performance optimizations for schema traversal and evaluation
- is_equal() utility function, and the const and enum keywords, now
provide more detailed error diagnostics
- numeric type checking is relaxed, now allowing for dualvars
arising from the simple case of using a number in string context
or a string in numeric context (only a problem in perls <5.36)
0.025 2024-06-11 20:17:39Z
- update test results and exemptions for TJSA 1.025
0.024 2024-05-09 16:27:25Z
- update test results and exemptions for TJSA 1.024
0.023 2024-04-17 17:55:32Z
- remove use of JSON::MaybeXS, to avoid potential use of JSON::XS;
now we use Cpanel::JSON::XS or JSON::PP directly, using the same
environment variables as in Mojo::JSON for customization.
0.022 2023-12-17 00:42:56Z
- new $STRINGY_NUMBERS option, for validating numbers more loosely
0.021 2023-04-22 17:25:12Z
- fix bad handling of empty patterns in "pattern",
"patternProperties" keywords
- use bignum calculations for any non-integer in "multipleOf", to
better handle overflows on older architectures
0.020 2023-04-12 03:28:28Z
- update test results and exemptions for TJSA 1.019
0.019 2023-01-08 19:54:14Z
- fix warning when using a $ref to a URI with no fragment
- update test skip list for latest release of the test suite
0.018 2022-08-06 22:02:20Z
- update test skip list for latest release of the test suite
0.017 2022-07-10 04:09:23Z
- fix 'experimental' prereq for older perls
0.016 2022-07-09 18:29:49Z
- avoid new experimental warning on 5.35.9
- the "specification_version" configuration option now accepts
values without "draft" in the name, to facilitate a new naming
convention used for future specification versions
0.015 2022-01-29 17:52:15Z
- pod fixes
0.014 2022-01-06 04:05:12Z
- updated error message for the "type" keyword to include the actual
type, as well as the expected type(s)
0.013 2021-12-31 21:20:46Z
- enable bignum mode when deserializing test data
0.012 2021-12-31 02:24:05Z
- minimum perl version raised to 5.020
- very large/small numbers are now properly accomodated in all
cases, including from JSON-serialized data
0.011 2021-10-22 22:08:02Z
- update acceptance tests for Test::JSON::Schema::Acceptance 1.013
0.010 2021-09-11 23:09:29Z
- skip some regex tests when Unicode library is too old for those
character classes being tested
0.009 2021-08-05 03:51:04Z
- skip acceptance test for integer overflow when nvsize is too
large to produce the expected error
0.008 2021-08-03 03:50:09Z
- better handling of blessed data types and other references
- support for specification versions draft7 and draft2020-12,
through the "$schema" keyword and the new $SPECIFICATION_VERSION
variable
0.007 2021-07-22 05:20:31Z
- add incomplete prereq declaration for Mojo::URL
- skip acceptance test for integer overflow when nvsize is too
large to produce the expected error
- fix parsing of $recursiveAnchor in inner scope
- the $MOJO_BOOLEANS option is now $SCALARREF_BOOLEANS (all JSON
backends support them, not just Mojo::JSON)
0.006 2021-06-06 18:20:19Z
- properly update initial_schema_uri in the state object while passing
through $id keywords, to keep error locations accurate
0.005 2021-06-05 17:56:50Z
- errors are no longer generated on the presence of unsupported
keywords from other draft specification versions
- now checking syntax for the keywords: $id, $anchor,
$recursiveAnchor, $vocabulary, $comment
0.004 2021-05-21 05:03:42Z
- new optional OO interface for options, as an alternative to global
variables
0.003 2021-05-15 18:17:16Z
- fixed error strings used for failing "dependentRequired"
- keywords in the applicator vocabulary are now applied before the
keywords in the validation vocabulary (true/false results are not
affected, but the order of annotations and errors will change)
0.002 2021-04-08 23:46:59Z
- new config option $MOJO_BOOLEANS, which will treat \0, \1 in data
as json booleans
0.001 2021-03-30 05:39:59Z
- Initial release.
t 000755 000766 000024 0 15221771652 14200 5 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 ref.t 100640 000766 000024 5276 15221771652 15307 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
subtest 'local JSON pointer' => sub {
cmp_result(
evaluate(true, { '$defs' => { true => true }, '$ref' => '#/$defs/true' }),
{ valid => true },
'can follow local $ref to a true schema',
);
cmp_result(
evaluate(true, { '$defs' => { false => false }, '$ref' => '#/$defs/false' }),
{
valid => false,
errors => [
{
error => 'subschema is false',
instanceLocation => '',
keywordLocation => '/$ref',
absoluteKeywordLocation => '#/$defs/false',
},
],
},
'can follow local $ref to a false schema',
);
ok(
lives {
my $result = evaluate(true, { '$ref' => '#/$defs/nowhere' });
like(
$result->{errors}[0]{error},
qr{^EXCEPTION: unable to find resource \#/\$defs/nowhere},
'got error for unresolvable ref',
);
},
'no exception',
);
};
subtest 'fragment with URI-escaped and JSON Pointer-escaped characters' => sub {
cmp_result(
evaluate(
1,
{
'$defs' => { 'foo-bar-tilde~-slash/-braces{}-def' => true },
'$ref' => '#/$defs/foo-bar-tilde~0-slash~1-braces%7B%7D-def',
},
),
{ valid => true },
'can follow $ref with escaped components',
);
};
subtest 'local anchor' => sub {
todo '$anchor is not yet supported' => sub {
fail;
};
};
subtest '$ref using the local $id' => sub {
cmp_result(
evaluate(
1,
{
'$id' => 'https://localhost:1234/blah',
'$defs' => { 'my-definition' => true },
'$ref' => 'https://localhost:1234/blah#/$defs/my-definition',
},
),
{ valid => true },
'can follow $ref using a base URI that matches our document',
);
cmp_result(
evaluate(
[ 'foo', [ 'bar' ] ],
{
'$id' => 'https://localhost:1234/blah',
anyOf => [
{ type => 'string' },
{ type => 'array', items => { '$ref' => 'https://localhost:1234/blah' } },
],
},
),
{ valid => true },
'can follow $ref using a base URI that matches our document',
);
};
done_testing;
LICENCE 100644 000766 000024 46312 15221771652 15110 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 This software is copyright (c) 2021 by Karen Etheridge.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
Terms of the Perl programming language system itself
a) the GNU General Public License as published by the Free
Software Foundation; either version 1, or (at your option) any
later version, or
b) the "Artistic License"
--- The GNU General Public License, Version 1, February 1989 ---
This software is Copyright (c) 2021 by Karen Etheridge.
This is free software, licensed under:
The GNU General Public License, Version 1, February 1989
GNU GENERAL PUBLIC LICENSE
Version 1, February 1989
Copyright (C) 1989 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The license agreements of most software companies try to keep users
at the mercy of those companies. By contrast, our General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. The
General Public License applies to the Free Software Foundation's
software and to any other program whose authors commit to using it.
You can use it for your programs, too.
When we speak of free software, we are referring to freedom, not
price. Specifically, the General Public License is designed to make
sure that you have the freedom to give away or sell copies of free
software, that you receive source code or can get it if you want it,
that you can change the software or use pieces of it in new free
programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of a such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must tell them their rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any program or other work which
contains a notice placed by the copyright holder saying it may be
distributed under the terms of this General Public License. The
"Program", below, refers to any such program or work, and a "work based
on the Program" means either the Program or any work containing the
Program or a portion of it, either verbatim or with modifications. Each
licensee is addressed as "you".
1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this
General Public License and to the absence of any warranty; and give any
other recipients of the Program a copy of this General Public License
along with the Program. You may charge a fee for the physical act of
transferring a copy.
2. You may modify your copy or copies of the Program or any portion of
it, and copy and distribute such modifications under the terms of Paragraph
1 above, provided that you also do the following:
a) cause the modified files to carry prominent notices stating that
you changed the files and the date of any change; and
b) cause the whole of any work that you distribute or publish, that
in whole or in part contains the Program or any part thereof, either
with or without modifications, to be licensed at no charge to all
third parties under the terms of this General Public License (except
that you may choose to grant warranty protection to some or all
third parties, at your option).
c) If the modified program normally reads commands interactively when
run, you must cause it, when started running for such interactive use
in the simplest and most usual way, to print or display an
announcement including an appropriate copyright notice and a notice
that there is no warranty (or else, saying that you provide a
warranty) and that users may redistribute the program under these
conditions, and telling the user how to view a copy of this General
Public License.
d) You may charge a fee for the physical act of transferring a
copy, and you may at your option offer warranty protection in
exchange for a fee.
Mere aggregation of another independent work with the Program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other work under the scope of these terms.
3. You may copy and distribute the Program (or a portion or derivative of
it, under Paragraph 2) in object code or executable form under the terms of
Paragraphs 1 and 2 above provided that you also do one of the following:
a) accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of
Paragraphs 1 and 2 above; or,
b) accompany it with a written offer, valid for at least three
years, to give any third party free (except for a nominal charge
for the cost of distribution) a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of
Paragraphs 1 and 2 above; or,
c) accompany it with the information you received as to where the
corresponding source code may be obtained. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form alone.)
Source code for a work means the preferred form of the work for making
modifications to it. For an executable file, complete source code means
all the source code for all modules it contains; but, as a special
exception, it need not include source code for modules which are standard
libraries that accompany the operating system on which the executable
file runs, or for standard header files or definitions files that
accompany that operating system.
4. You may not copy, modify, sublicense, distribute or transfer the
Program except as expressly provided under this General Public License.
Any attempt otherwise to copy, modify, sublicense, distribute or transfer
the Program is void, and will automatically terminate your rights to use
the Program under this License. However, parties who have received
copies, or rights to use copies, from you under this General Public
License will not have their licenses terminated so long as such parties
remain in full compliance.
5. By copying, distributing or modifying the Program (or any work based
on the Program) you indicate your acceptance of this license to do so,
and all its terms and conditions.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these
terms and conditions. You may not impose any further restrictions on the
recipients' exercise of the rights granted herein.
7. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of the license which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
the license, you may choose any version ever published by the Free Software
Foundation.
8. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to humanity, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest to
attach them to the start of each source file to most effectively convey
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C) 19yy
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see .
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19xx name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the
appropriate parts of the General Public License. Of course, the
commands you use may be called something other than `show w' and `show
c'; they could even be mouse-clicks or menu items--whatever suits your
program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
program `Gnomovision' (a program to direct compilers to make passes
at assemblers) written by James Hacker.
, 1 April 1989
Moe Ghoul, President of Vice
That's all there is to it!
--- The Perl Artistic License 1.0 ---
This software is Copyright (c) 2021 by Karen Etheridge.
This is free software, licensed under:
The Perl Artistic License 1.0
The "Artistic License"
Preamble
The intent of this document is to state the conditions under which a
Package may be copied, such that the Copyright Holder maintains some
semblance of artistic control over the development of the package,
while giving the users of the package the right to use and distribute
the Package in a more-or-less customary fashion, plus the right to make
reasonable modifications.
Definitions:
"Package" refers to the collection of files distributed by the
Copyright Holder, and derivatives of that collection of files
created through textual modification.
"Standard Version" refers to such a Package if it has not been
modified, or has been modified in accordance with the wishes
of the Copyright Holder as specified below.
"Copyright Holder" is whoever is named in the copyright or
copyrights for the package.
"You" is you, if you're thinking about copying or distributing
this Package.
"Reasonable copying fee" is whatever you can justify on the
basis of media cost, duplication charges, time of people involved,
and so on. (You will not be required to justify it to the
Copyright Holder, but only to the computing community at large
as a market that must bear the fee.)
"Freely Available" means that no fee is charged for the item
itself, though there may be fees involved in handling the item.
It also means that recipients of the item may redistribute it
under the same conditions they received it.
1. You may make and give away verbatim copies of the source form of the
Standard Version of this Package without restriction, provided that you
duplicate all of the original copyright notices and associated disclaimers.
2. You may apply bug fixes, portability fixes and other modifications
derived from the Public Domain or from the Copyright Holder. A Package
modified in such a way shall still be considered the Standard Version.
3. You may otherwise modify your copy of this Package in any way, provided
that you insert a prominent notice in each changed file stating how and
when you changed that file, and provided that you do at least ONE of the
following:
a) place your modifications in the Public Domain or otherwise make them
Freely Available, such as by posting said modifications to Usenet or
an equivalent medium, or placing the modifications on a major archive
site such as uunet.uu.net, or by allowing the Copyright Holder to include
your modifications in the Standard Version of the Package.
b) use the modified Package only within your corporation or organization.
c) rename any non-standard executables so the names do not conflict
with standard executables, which must also be provided, and provide
a separate manual page for each non-standard executable that clearly
documents how it differs from the Standard Version.
d) make other distribution arrangements with the Copyright Holder.
4. You may distribute the programs of this Package in object code or
executable form, provided that you do at least ONE of the following:
a) distribute a Standard Version of the executables and library files,
together with instructions (in the manual page or equivalent) on where
to get the Standard Version.
b) accompany the distribution with the machine-readable source of
the Package with your modifications.
c) give non-standard executables non-standard names, and clearly
document the differences in manual pages (or equivalent), together
with instructions on where to get the Standard Version.
d) make other distribution arrangements with the Copyright Holder.
5. You may charge a reasonable copying fee for any distribution of this
Package. You may charge any fee you choose for support of this
Package. You may not charge a fee for this Package itself. However,
you may distribute this Package in aggregate with other (possibly
commercial) programs as part of a larger (possibly commercial) software
distribution provided that you do not advertise this Package as a
product of your own. You may embed this Package's interpreter within
an executable of yours (by linking); this shall be construed as a mere
form of aggregation, provided that the complete Standard Version of the
interpreter is so embedded.
6. The scripts and library files supplied as input to or produced as
output from the programs of this Package do not automatically fall
under the copyright of this Package, but belong to whoever generated
them, and may be sold commercially, and may be aggregated with this
Package. If such scripts or library files are aggregated with this
Package via the so-called "undump" or "unexec" methods of producing a
binary executable image, then distribution of such an image shall
neither be construed as a distribution of this Package nor shall it
fall under the restrictions of Paragraphs 3 and 4, provided that you do
not represent such an executable image as a Standard Version of this
Package.
7. C subroutines (or comparably compiled subroutines in other
languages) supplied by you and linked into this Package in order to
emulate subroutines and variables of the language defined by this
Package shall not be considered part of this Package, but are the
equivalent of input as in Paragraph 6, provided these subroutines do
not change the language in any way that would cause it to fail the
regression tests for the language.
8. Aggregation of this Package with a commercial distribution is always
permitted provided that the use of this Package is embedded; that is,
when no overt attempt is made to make this Package's interfaces visible
to the end user of the commercial distribution. Such use shall not be
construed as a distribution of this Package.
9. The name of the Copyright Holder may not be used to endorse or promote
products derived from this software without specific prior written permission.
10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
The End
INSTALL 100644 000766 000024 4672 15221771652 15137 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 This is the Perl distribution JSON-Schema-Tiny.
Installing JSON-Schema-Tiny is straightforward.
## Installation with cpanm
If you have cpanm, you only need one line:
% cpanm JSON::Schema::Tiny
If it does not have permission to install modules to the current perl, cpanm
will automatically set up and install to a local::lib in your home directory.
See the local::lib documentation (https://metacpan.org/pod/local::lib) for
details on enabling it in your environment.
## Installing with the CPAN shell
Alternatively, if your CPAN shell is set up, you should just be able to do:
% cpan JSON::Schema::Tiny
## Manual installation
As a last resort, you can manually install it. If you have not already
downloaded the release tarball, you can find the download link on the module's
MetaCPAN page: https://metacpan.org/pod/JSON::Schema::Tiny
Untar the tarball, install configure prerequisites (see below), then build it:
% perl Build.PL
% ./Build && ./Build test
Then install it:
% ./Build install
Or the more portable variation:
% perl Build.PL
% perl Build
% perl Build test
% perl Build install
If your perl is system-managed, you can create a local::lib in your home
directory to install modules to. For details, see the local::lib documentation:
https://metacpan.org/pod/local::lib
The prerequisites of this distribution will also have to be installed manually. The
prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated
by running the manual build process described above.
## Configure Prerequisites
This distribution requires other modules to be installed before this
distribution's installer can be run. They can be found under the
"configure_requires" key of META.yml or the
"{prereqs}{configure}{requires}" key of META.json.
## Other Prerequisites
This distribution may require additional modules to be installed after running
Build.PL.
Look for prerequisites in the following phases:
* to run ./Build, PHASE = build
* to use the module code itself, PHASE = runtime
* to run tests, PHASE = test
They can all be found in the "PHASE_requires" key of MYMETA.yml or the
"{prereqs}{PHASE}{requires}" key of MYMETA.json.
## Documentation
JSON-Schema-Tiny documentation is available as POD.
You can run `perldoc` from a shell to read the documentation:
% perldoc JSON::Schema::Tiny
For more information on installing Perl modules via CPAN, please see:
https://www.cpan.org/modules/INSTALL.html
dist.ini 100640 000766 000024 2735 15221771652 15544 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 name = JSON-Schema-Tiny
author = Karen Etheridge
copyright_holder = Karen Etheridge
copyright_year = 2021
license = Perl_5
[=inc::CheckConflicts]
[@Author::ETHER]
:version = 0.154
bugtracker = github
Test::MinimumVersion.max_target_perl = 5.020 ; may go higher later on
Git::GatherDir.exclude_filename = pull_request_template.md
Test::ReportPrereqs.include[0] = JSON::PP
Test::ReportPrereqs.include[1] = Cpanel::JSON::XS
Test::ReportPrereqs.include[2] = JSON::XS
Test::ReportPrereqs.include[3] = Math::BigInt
Test::ReportPrereqs.include[4] = Mojolicious
Test::ReportPrereqs.include[5] = builtin
Test::ReportPrereqs.include[6] = builtin::Backport
-remove = Test::Pod::No404s
StaticInstall.mode = off
[Prereqs / RuntimeRequires]
Mojolicious = 7.23 ; Mojo::URL::to_unsafe_string
builtin::compat = 0.003003
[Prereqs / RuntimeSuggests]
Cpanel::JSON::XS = 0
[Prereqs / DevelopRequires]
Test2::Warnings = 0
strictures = 2
autovivification = 0
[RemovePrereqs]
remove = B
remove = Carp
remove = Data::Dumper
remove = Encode
remove = Exporter
remove = File::Spec
remove = IO::Handle
remove = IPC::Open3
remove = JSON::PP
remove = Math::BigInt
remove = Math::BigFloat
remove = Module::Metadata
remove = Mojo::File
remove = Mojo::JSON
remove = Mojo::JSON::Pointer
remove = Mojo::URL
remove = Scalar::Util
remove = Test2::API
remove = Test::More
remove = constant
remove = lib
remove = open
remove = strict
remove = utf8
remove = warnings
type.t 100640 000766 000024 21654 15221771652 15532 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use utf8;
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use Scalar::Util qw(isdual dualvar);
use JSON::Schema::Tiny ();
use Math::BigInt;
use Math::BigFloat;
use lib 't/lib';
use Helper;
sub is_type { goto &JSON::Schema::Tiny::is_type }
sub get_type { goto &JSON::Schema::Tiny::get_type }
my %inflated_data = (
null => [ undef ],
boolean => [ false, true ],
object => [ {}, { a => 1 } ],
array => [ [], [ 1 ] ],
number => [ 3.1, 1.23456789012e10, Math::BigFloat->new('0.123') ],
integer => [ 0, -1, 2, 2.0, 2**31-1, 2**31, 2**63-1, 2**63, 2**64, 2**65, 1000000000000000,
Math::BigInt->new('1e20'), Math::BigInt->new('1'), Math::BigInt->new('1.0'),
Math::BigFloat->new('2e1'), Math::BigFloat->new('1'), Math::BigFloat->new('1.0') ],
string => [ '', '0', '-1', '2', '2.0', '3.1', 'école', 'ಠ_ಠ' ],
);
my %json_data = (
null => [ 'null' ],
boolean => [ 'false', 'true' ],
object => [ '{}', '{"a":1}' ],
array => [ '[]', '[1]' ],
number => [ '3.1', '1.23456789012e10' ],
integer => [ '0', '-1', '2.0', (map $_.'', 2**31-1, 2**31, 2**63-1, 2**63, 2**64, 2**65), '1000000000000000', '2e1' ],
string => [ '""', '"0"', '"-1"', '"2.0"', '"3.1"',
qq{"\x{c3}\x{a9}cole"}, qq{"\x{e0}\x{b2}\x{a0}_\x{e0}\x{b2}\x{a0}"} ],
);
foreach my $type (sort keys %inflated_data) {
subtest 'inflated data, type: '.$type => sub {
foreach my $value ($inflated_data{$type}->@*) {
my $value_copy = $value;
ok(is_type($type, $value), json_sprintf(('is_type("'.$type.'", %s) is true'), $value_copy ));
ok(is_type('number', $value), json_sprintf(('is_type("number", %s) is true'), $value_copy ))
if $type eq 'integer';
is(get_type($value), $type, json_sprintf(('get_type(%s) = '.$type), $value_copy));
foreach my $other_type (sort keys %inflated_data) {
next if $other_type eq $type;
next if $type eq 'integer' and $other_type eq 'number';
ok(!is_type($other_type, $value),
json_sprintf('is_type("'.$other_type.'", %s) is false', $value));
}
ok(!isdual($value), 'data is not tampered with while it is tested (not dualvar)');
}
};
}
my $decoder = (Mojo::JSON::JSON_XS ? 'Cpanel::JSON::XS' : 'JSON::PP')->new
->allow_nonref(1)
->canonical(1)
->utf8(1)
->allow_bignum(1);
foreach my $type (sort keys %json_data) {
subtest 'JSON-encoded data, type: '.$type => sub {
foreach my $value ($json_data{$type}->@*) {
$value = $decoder->decode($value);
my $value_copy = $value;
ok(is_type($type, $value), json_sprintf(('is_type("'.$type.'", %s) is true'), $value_copy ));
ok(is_type('number', $value), json_sprintf(('is_type("number", %s) is true'), $value_copy ))
if $type eq 'integer';
is(get_type($value), $type, json_sprintf(('get_type(%s) = '.$type), $value_copy));
foreach my $other_type (sort keys %json_data) {
next if $other_type eq $type;
next if $type eq 'integer' and $other_type eq 'number';
ok(!is_type($other_type, $value),
json_sprintf('is_type("'.$other_type.'", %s) is false', $value));
}
ok(!isdual($value), 'data is not tampered with while it is tested (not dualvar)');
}
};
}
ok(!is_type('foo', 'wharbarbl'), 'non-existent type does not result in exception');
subtest 'ambiguous types' => sub {
subtest 'integers' => sub {
my $integer = dualvar(5, 'five');
is(get_type($integer), 'ambiguous type', 'dualvar integers with different values are ambiguous');
ok(!is_type($_, $integer), "dualvar integers with different values are not ${_}s") foreach qw(integer number string);
$integer = 5;
()= sprintf('%s', $integer);
# legacy behaviour (this only happens for IVs, not NVs)
SKIP: {
skip 'on perls < 5.35.9, reading the string form of an integer value sets the flag SVf_POK', 1
if "$]" >= 5.035009;
is(get_type($integer), 'string', 'older perls only: integer that is later used as a string is now identified as a string');
ok(is_type('integer', $integer), 'integer that is later used as a string is still an integer');
ok(is_type('number', $integer), 'integer that is later used as a string is still a number');
ok(is_type('string', $integer), 'older perls only: integer that is later used as a string is now a string');
}
# modern behaviour
SKIP: {
skip 'on perls < 5.35.9, reading the string form of an integer value sets the flag SVf_POK', 1
if "$]" < 5.035009;
is(get_type($integer), 'integer', 'integer that is later used as a string is still identified as a integer');
ok(is_type('integer', $integer), 'integer that is later used as a string is still an integer');
ok(is_type('number', $integer), 'integer that is later used as a string is still a number');
ok(!is_type('string', $integer), 'integer that is later used as a string is not a string');
}
};
subtest 'numbers' => sub {
my $number = dualvar(5.1, 'five');
is(get_type($number), 'ambiguous type', 'dualvar numbers are ambiguous in get_type');
ok(!is_type($_, $number), "dualvar numbers are not ${_}s") foreach qw(integer number string);
$number = 5.1;
()= sprintf('%s', $number);
is(get_type($number), 'number', 'number that is later used as a string is still identified as a number');
ok(!is_type('integer', $number), 'number that is later used as a string is not an integer');
ok(is_type('number', $number), 'number that is later used as a string is still a number');
ok(!is_type('string', $number), 'number that is later used as a string is not a string');
};
subtest 'strings' => sub {
my $string = dualvar(5.1, 'five');
is(get_type($string), 'ambiguous type', 'dualvar strings are ambiguous in get_type');
ok(!is_type($_, $string), "dualvar strings are not ${_}s") foreach qw(integer number string);
$string = '5';
()= 0+$string;
is(get_type($string), 'string', 'string that is later used as an integer is still identified as a string');
# legacy behaviour
SKIP: {
skip 'on perls < 5.35.9, reading the string form of an integer value sets the flag SVf_POK', 1
if "$]" >= 5.035009;
ok(is_type('integer', $string), 'older perls only: string that is later used as an integer becomes an integer');
ok(is_type('number', $string), 'older perls only: string that is later used as an integer becomes a number');
}
# modern behaviour
SKIP: {
skip 'on perls < 5.35.9, reading the integer form of a string value sets the flag SVf_IOK', 1
if "$]" < 5.035009;
ok(!is_type('integer', $string), 'string that is later used as an integer is not an integer');
ok(!is_type('number', $string), 'string that is later used as an integer is not a number');
}
ok(is_type('string', $string), 'string that is later used as an integer is still a string');
$string = '5.1';
()= 0+$string;
is(get_type($string), 'string', 'string that is later used as a number is still identified as a string');
ok(!is_type('integer', $string), 'string that is later used as a number is not an integer');
# legacy behaviour
SKIP: {
skip 'on perls < 5.35.9, reading the string form of an integer value sets the flag SVf_POK', 1
if "$]" >= 5.035009;
ok(is_type('number', $string), 'older perls only: string that is later used as a number becomes a number');
}
# modern behaviour
SKIP: {
skip 'on perls < 5.35.9, reading the numeric form of a string value sets the flag SVf_NOK', 1
if "$]" < 5.035009;
ok(!is_type('number', $string), 'string that is later used as a number is not a number');
}
ok(is_type('string', $string), 'string that is later used as a number is still a string');
};
};
subtest 'is_type and get_type for references' => sub {
foreach my $test (
[ \1, 'reference to SCALAR' ],
[ \\2, 'reference to REF' ],
[ sub { 1 }, 'reference to CODE' ],
[ \*stdout, 'reference to GLOB' ],
[ \substr('a', '1'), 'reference to LVALUE' ],
[ \v1.2.3, 'reference to VSTRING' ],
[ qr/foo/, 'Regexp' ],
[ *STDIN{IO}, 'IO::File' ],
[ bless({}, 'Foo'), 'Foo' ],
[ bless({}, '0'), '0' ],
) {
is(get_type($test->[0]), $test->[1], $test->[1].' type is reported without exception');
ok(is_type($test->[1], $test->[0]), 'value is a '.$test->[1]);
foreach my $type (qw(null object array boolean string number integer)) {
ok(!is_type($type, $test->[0]), 'value is not a '.$type);
}
}
};
done_testing;
META.yml 100644 000766 000024 67241 15221771652 15400 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 ---
abstract: 'Validate data against a schema, minimally'
author:
- 'Karen Etheridge '
build_requires:
List::Util: '1.50'
Test2::V0: '0'
Test2::Warnings: '0'
Test::Deep: '0'
Test::JSON::Schema::Acceptance: '1.026'
perl: '5.020'
configure_requires:
Module::Build::Tiny: '0.034'
perl: '5.020'
dynamic_config: 0
generated_by: 'Dist::Zilla version 6.037, CPAN::Meta::Converter version 2.150013'
keywords:
- JSON
- Schema
- data
- validation
- structure
- specification
- tiny
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: '1.4'
name: JSON-Schema-Tiny
no_index:
directory:
- inc
- t
- xt
provides:
JSON::Schema::Tiny:
file: lib/JSON/Schema/Tiny.pm
version: '0.034'
requires:
Feature::Compat::Try: '0'
Mojolicious: '7.23'
autovivification: '0'
builtin::compat: '0.003003'
experimental: '0.026'
if: '0'
perl: '5.020'
stable: '0.031'
resources:
bugtracker: https://github.com/karenetheridge/JSON-Schema-Tiny/issues
homepage: https://github.com/karenetheridge/JSON-Schema-Tiny
repository: https://github.com/karenetheridge/JSON-Schema-Tiny.git
version: '0.034'
x_Dist_Zilla:
perl:
version: '5.044000'
plugins:
-
class: inc::CheckConflicts
name: =inc::CheckConflicts
version: ~
-
class: Dist::Zilla::Plugin::Prereqs
config:
Dist::Zilla::Plugin::Prereqs:
phase: develop
type: recommends
name: '@Author::ETHER/pluginbundle version'
version: '6.037'
-
class: Dist::Zilla::Plugin::PromptIfStale
config:
Dist::Zilla::Plugin::PromptIfStale:
check_all_plugins: 0
check_all_prereqs: 0
modules:
- Dist::Zilla::PluginBundle::Author::ETHER
phase: build
run_under_travis: 0
skip: []
name: '@Author::ETHER/stale modules, build'
version: '0.060'
-
class: Dist::Zilla::Plugin::FileFinder::ByName
name: '@Author::ETHER/Examples'
version: '6.037'
-
class: Dist::Zilla::Plugin::Git::GatherDir
config:
Dist::Zilla::Plugin::GatherDir:
exclude_filename:
- CONTRIBUTING
- INSTALL
- LICENCE
- README.pod
- TODO
- pull_request_template.md
exclude_match: []
include_dotfiles: 0
prefix: ''
prune_directory: []
root: .
Dist::Zilla::Plugin::Git::GatherDir:
include_untracked: 0
name: '@Author::ETHER/Git::GatherDir'
version: '2.052'
-
class: Dist::Zilla::Plugin::MetaYAML
name: '@Author::ETHER/MetaYAML'
version: '6.037'
-
class: Dist::Zilla::Plugin::MetaJSON
name: '@Author::ETHER/MetaJSON'
version: '6.037'
-
class: Dist::Zilla::Plugin::Readme
name: '@Author::ETHER/Readme'
version: '6.037'
-
class: Dist::Zilla::Plugin::Manifest
name: '@Author::ETHER/Manifest'
version: '6.037'
-
class: Dist::Zilla::Plugin::License
name: '@Author::ETHER/License'
version: '6.037'
-
class: Dist::Zilla::Plugin::GenerateFile::FromShareDir
config:
Dist::Zilla::Plugin::GenerateFile::FromShareDir:
destination_filename: CONTRIBUTING
dist: Dist-Zilla-PluginBundle-Author-ETHER
encoding: UTF-8
has_xs: 0
location: build
source_filename: CONTRIBUTING
Dist::Zilla::Role::RepoFileInjector:
allow_overwrite: 1
repo_root: .
version: '0.009'
name: '@Author::ETHER/generate CONTRIBUTING'
version: '0.016'
-
class: Dist::Zilla::Plugin::InstallGuide
config:
Dist::Zilla::Role::ModuleMetadata:
Module::Metadata: '1.000039'
version: '0.006'
name: '@Author::ETHER/InstallGuide'
version: '1.200014'
-
class: Dist::Zilla::Plugin::Test::Compile
config:
Dist::Zilla::Plugin::Test::Compile:
bail_out_on_fail: 1
fail_on_warning: author
fake_home: 0
filename: xt/author/00-compile.t
module_finder:
- ':InstallModules'
needs_display: 0
phase: develop
script_finder:
- ':PerlExecFiles'
- '@Author::ETHER/Examples'
skips: []
switch: []
name: '@Author::ETHER/Test::Compile'
version: '2.059'
-
class: Dist::Zilla::Plugin::Test::NoTabs
config:
Dist::Zilla::Plugin::Test::NoTabs:
filename: xt/author/no-tabs.t
finder:
- ':InstallModules'
- ':ExecFiles'
- '@Author::ETHER/Examples'
- ':TestFiles'
- ':ExtraTestFiles'
name: '@Author::ETHER/Test::NoTabs'
version: '0.15'
-
class: Dist::Zilla::Plugin::Test::EOL
config:
Dist::Zilla::Plugin::Test::EOL:
filename: xt/author/eol.t
finder:
- ':ExecFiles'
- ':ExtraTestFiles'
- ':InstallModules'
- ':TestFiles'
- '@Author::ETHER/Examples'
trailing_whitespace: 1
name: '@Author::ETHER/Test::EOL'
version: '0.19'
-
class: Dist::Zilla::Plugin::MetaTests
name: '@Author::ETHER/MetaTests'
version: '6.037'
-
class: Dist::Zilla::Plugin::Test::CPAN::Changes
config:
Dist::Zilla::Plugin::Test::CPAN::Changes:
changelog: Changes
filename: xt/release/cpan-changes.t
name: '@Author::ETHER/Test::CPAN::Changes'
version: '0.013'
-
class: Dist::Zilla::Plugin::Test::ChangesHasContent
name: '@Author::ETHER/Test::ChangesHasContent'
version: '0.011'
-
class: Dist::Zilla::Plugin::Test::MinimumVersion
config:
Dist::Zilla::Plugin::Test::MinimumVersion:
max_target_perl: '5.020'
name: '@Author::ETHER/Test::MinimumVersion'
version: '2.000011'
-
class: Dist::Zilla::Plugin::PodSyntaxTests
name: '@Author::ETHER/PodSyntaxTests'
version: '6.037'
-
class: Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe
config:
Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe:
finder:
- ':InstallModules'
name: '@Author::ETHER/Test::Pod::Coverage::TrustMe'
version: v1.0.1
-
class: Dist::Zilla::Plugin::Test::PodSpelling
config:
Dist::Zilla::Plugin::Test::PodSpelling:
directories:
- examples
- lib
- script
- t
- xt
spell_cmd: ''
stopwords:
- irc
wordlist: Pod::Wordlist
name: '@Author::ETHER/Test::PodSpelling'
version: '2.007006'
-
class: Dist::Zilla::Plugin::Test::Kwalitee
config:
Dist::Zilla::Plugin::Test::Kwalitee:
filename: xt/author/kwalitee.t
skiptest: []
name: '@Author::ETHER/Test::Kwalitee'
version: '2.13'
-
class: Dist::Zilla::Plugin::MojibakeTests
name: '@Author::ETHER/MojibakeTests'
version: '0.8'
-
class: Dist::Zilla::Plugin::Test::ReportPrereqs
name: '@Author::ETHER/Test::ReportPrereqs'
version: '0.029'
-
class: Dist::Zilla::Plugin::Test::Portability
config:
Dist::Zilla::Plugin::Test::Portability:
options: ''
name: '@Author::ETHER/Test::Portability'
version: '2.001003'
-
class: Dist::Zilla::Plugin::Test::CleanNamespaces
config:
Dist::Zilla::Plugin::Test::CleanNamespaces:
filename: xt/author/clean-namespaces.t
skips: []
name: '@Author::ETHER/Test::CleanNamespaces'
version: '0.006'
-
class: Dist::Zilla::Plugin::Git::Describe
name: '@Author::ETHER/Git::Describe'
version: '0.008'
-
class: Dist::Zilla::Plugin::PodWeaver
config:
Dist::Zilla::Plugin::PodWeaver:
finder:
- ':InstallModules'
- ':PerlExecFiles'
plugins:
-
class: Pod::Weaver::Section::GenerateSection
name: SUPPORT
version: '4.020'
-
class: Pod::Weaver::Section::GenerateSection
name: VERSION
version: '4.020'
-
class: Pod::Weaver::Plugin::EnsurePod5
name: '@Author::ETHER/EnsurePod5'
version: '4.020'
-
class: Pod::Weaver::Plugin::H1Nester
name: '@Author::ETHER/H1Nester'
version: '4.020'
-
class: Pod::Weaver::Plugin::SingleEncoding
name: '@Author::ETHER/SingleEncoding'
version: '4.020'
-
class: Pod::Weaver::Plugin::Transformer
name: '@Author::ETHER/List'
version: '4.020'
-
class: Pod::Weaver::Plugin::Transformer
name: '@Author::ETHER/Verbatim'
version: '4.020'
-
class: Pod::Weaver::Section::Region
name: '@Author::ETHER/header'
version: '4.020'
-
class: Pod::Weaver::Section::Name
name: '@Author::ETHER/Name'
version: '4.020'
-
class: Pod::Weaver::Section::Version
name: '@Author::ETHER/Version'
version: '4.020'
-
class: Pod::Weaver::Section::Region
name: '@Author::ETHER/prelude'
version: '4.020'
-
class: Pod::Weaver::Section::Generic
name: SYNOPSIS
version: '4.020'
-
class: Pod::Weaver::Section::Generic
name: DESCRIPTION
version: '4.020'
-
class: Pod::Weaver::Section::Generic
name: OVERVIEW
version: '4.020'
-
class: Pod::Weaver::Section::Collect
name: ATTRIBUTES
version: '4.020'
-
class: Pod::Weaver::Section::Collect
name: METHODS
version: '4.020'
-
class: Pod::Weaver::Section::Collect
name: FUNCTIONS
version: '4.020'
-
class: Pod::Weaver::Section::Collect
name: TYPES
version: '4.020'
-
class: Pod::Weaver::Section::Leftovers
name: '@Author::ETHER/Leftovers'
version: '4.020'
-
class: Pod::Weaver::Section::Region
name: '@Author::ETHER/postlude'
version: '4.020'
-
class: Pod::Weaver::Section::GenerateSection
name: '@Author::ETHER/generate GIVING THANKS'
version: '4.020'
-
class: Pod::Weaver::Section::GenerateSection
name: '@Author::ETHER/generate SUPPORT'
version: '4.020'
-
class: Pod::Weaver::Section::Authors
name: '@Author::ETHER/Authors'
version: '4.020'
-
class: Pod::Weaver::Section::AllowOverride
name: '@Author::ETHER/allow override AUTHOR'
version: '0.05'
-
class: Pod::Weaver::Section::Contributors
name: '@Author::ETHER/Contributors'
version: '0.009'
-
class: Pod::Weaver::Section::Legal
name: '@Author::ETHER/Legal'
version: '4.020'
-
class: Pod::Weaver::Section::Region
name: '@Author::ETHER/footer'
version: '4.020'
-
class: inc::AppendSection
name: AppendSupport
version: ~
-
class: inc::AppendSection
name: AppendVersion
version: ~
name: '@Author::ETHER/PodWeaver'
version: '4.010'
-
class: Dist::Zilla::Plugin::GithubMeta
name: '@Author::ETHER/GithubMeta'
version: '0.58'
-
class: Dist::Zilla::Plugin::AutoMetaResources
name: '@Author::ETHER/AutoMetaResources'
version: '1.21'
-
class: Dist::Zilla::Plugin::Authority
name: '@Author::ETHER/Authority'
version: '1.009'
-
class: Dist::Zilla::Plugin::MetaNoIndex
name: '@Author::ETHER/MetaNoIndex'
version: '6.037'
-
class: Dist::Zilla::Plugin::MetaProvides::Package
config:
Dist::Zilla::Plugin::MetaProvides::Package:
finder:
- ':InstallModules'
finder_objects:
-
class: Dist::Zilla::Plugin::FinderCode
name: ':InstallModules'
version: '6.037'
include_underscores: 0
Dist::Zilla::Role::MetaProvider::Provider:
$Dist::Zilla::Role::MetaProvider::Provider::VERSION: '2.002004'
inherit_missing: 0
inherit_version: 0
meta_noindex: 1
Dist::Zilla::Role::ModuleMetadata:
Module::Metadata: '1.000039'
version: '0.006'
name: '@Author::ETHER/MetaProvides::Package'
version: '2.004003'
-
class: Dist::Zilla::Plugin::MetaConfig
name: '@Author::ETHER/MetaConfig'
version: '6.037'
-
class: Dist::Zilla::Plugin::Keywords
config:
Dist::Zilla::Plugin::Keywords:
keywords:
- JSON
- Schema
- data
- validation
- structure
- specification
- tiny
name: '@Author::ETHER/Keywords'
version: '0.007'
-
class: Dist::Zilla::Plugin::UseUnsafeInc
config:
Dist::Zilla::Plugin::UseUnsafeInc:
dot_in_INC: 0
name: '@Author::ETHER/UseUnsafeInc'
version: '0.002'
-
class: Dist::Zilla::Plugin::AutoPrereqs
name: '@Author::ETHER/AutoPrereqs'
version: '6.037'
-
class: Dist::Zilla::Plugin::Prereqs::AuthorDeps
name: '@Author::ETHER/Prereqs::AuthorDeps'
version: '0.007'
-
class: Dist::Zilla::Plugin::MinimumPerl
name: '@Author::ETHER/MinimumPerl'
version: '1.006'
-
class: Dist::Zilla::Plugin::ModuleBuildTiny
config:
Dist::Zilla::Role::TestRunner:
default_jobs: 9
name: '@Author::ETHER/ModuleBuildTiny'
version: '0.020'
-
class: Dist::Zilla::Plugin::Git::Contributors
config:
Dist::Zilla::Plugin::Git::Contributors:
git_version: 2.52.0
include_authors: 0
include_releaser: 1
order_by: commits
paths: []
name: '@Author::ETHER/Git::Contributors'
version: '0.039'
-
class: Dist::Zilla::Plugin::StaticInstall
config:
Dist::Zilla::Plugin::StaticInstall:
dry_run: 0
mode: off
name: '@Author::ETHER/StaticInstall'
version: '0.012'
-
class: Dist::Zilla::Plugin::RunExtraTests
config:
Dist::Zilla::Role::TestRunner:
default_jobs: 9
name: '@Author::ETHER/RunExtraTests'
version: '0.029'
-
class: Dist::Zilla::Plugin::CheckSelfDependency
config:
Dist::Zilla::Plugin::CheckSelfDependency:
finder:
- ':InstallModules'
Dist::Zilla::Role::ModuleMetadata:
Module::Metadata: '1.000039'
version: '0.006'
name: '@Author::ETHER/CheckSelfDependency'
version: '0.011'
-
class: Dist::Zilla::Plugin::Run::AfterBuild
config:
Dist::Zilla::Plugin::Run::Role::Runner:
fatal_errors: 1
quiet: 1
run:
- "bash -c \"test -e .ackrc && grep -q -- '--ignore-dir=.latest' .ackrc || echo '--ignore-dir=.latest' >> .ackrc; if [[ `dirname '%d'` != .build ]]; then test -e .ackrc && grep -q -- '--ignore-dir=%d' .ackrc || echo '--ignore-dir=%d' >> .ackrc; fi\""
version: '0.050'
name: '@Author::ETHER/.ackrc'
version: '0.050'
-
class: Dist::Zilla::Plugin::Run::AfterBuild
config:
Dist::Zilla::Plugin::Run::Role::Runner:
eval:
- "if ('%d' =~ /^%n-[.[:xdigit:]]+$/) { unlink '.latest'; symlink '%d', '.latest'; }"
fatal_errors: 0
quiet: 1
version: '0.050'
name: '@Author::ETHER/.latest'
version: '0.050'
-
class: Dist::Zilla::Plugin::CheckStrictVersion
name: '@Author::ETHER/CheckStrictVersion'
version: '0.001'
-
class: Dist::Zilla::Plugin::CheckMetaResources
name: '@Author::ETHER/CheckMetaResources'
version: '0.001'
-
class: Dist::Zilla::Plugin::EnsureLatestPerl
config:
Dist::Zilla::Plugin::EnsureLatestPerl:
Module::CoreList: '5.20260722'
name: '@Author::ETHER/EnsureLatestPerl'
version: '0.010'
-
class: Dist::Zilla::Plugin::PromptIfStale
config:
Dist::Zilla::Plugin::PromptIfStale:
check_all_plugins: 1
check_all_prereqs: 1
modules: []
phase: release
run_under_travis: 0
skip: []
name: '@Author::ETHER/stale modules, release'
version: '0.060'
-
class: Dist::Zilla::Plugin::Git::Check
config:
Dist::Zilla::Plugin::Git::Check:
untracked_files: die
Dist::Zilla::Role::Git::DirtyFiles:
allow_dirty: []
allow_dirty_match: []
changelog: Changes
Dist::Zilla::Role::Git::Repo:
git_version: 2.52.0
repo_root: .
name: '@Author::ETHER/initial check'
version: '2.052'
-
class: Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts
config:
Dist::Zilla::Role::Git::Repo:
git_version: 2.52.0
repo_root: .
name: '@Author::ETHER/Git::CheckFor::MergeConflicts'
version: '0.014'
-
class: Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch
config:
Dist::Zilla::Role::Git::Repo:
git_version: 2.52.0
repo_root: .
name: '@Author::ETHER/Git::CheckFor::CorrectBranch'
version: '0.014'
-
class: Dist::Zilla::Plugin::Git::Remote::Check
name: '@Author::ETHER/Git::Remote::Check'
version: 0.1.2
-
class: Dist::Zilla::Plugin::CheckPrereqsIndexed
name: '@Author::ETHER/CheckPrereqsIndexed'
version: '0.022'
-
class: Dist::Zilla::Plugin::TestRelease
name: '@Author::ETHER/TestRelease'
version: '6.037'
-
class: Dist::Zilla::Plugin::Git::Check
config:
Dist::Zilla::Plugin::Git::Check:
untracked_files: die
Dist::Zilla::Role::Git::DirtyFiles:
allow_dirty: []
allow_dirty_match: []
changelog: Changes
Dist::Zilla::Role::Git::Repo:
git_version: 2.52.0
repo_root: .
name: '@Author::ETHER/after tests'
version: '2.052'
-
class: Dist::Zilla::Plugin::CheckIssues
name: '@Author::ETHER/CheckIssues'
version: '0.011'
-
class: Dist::Zilla::Plugin::UploadToCPAN
name: '@Author::ETHER/UploadToCPAN'
version: '6.037'
-
class: Dist::Zilla::Plugin::CopyFilesFromRelease
config:
Dist::Zilla::Plugin::CopyFilesFromRelease:
filename:
- CONTRIBUTING
- INSTALL
- LICENCE
- LICENSE
- ppport.h
match: []
name: '@Author::ETHER/copy generated files'
version: '0.007'
-
class: Dist::Zilla::Plugin::ReadmeAnyFromPod
config:
Dist::Zilla::Role::FileWatcher:
version: '0.006'
name: '@Author::ETHER/ReadmeAnyFromPod'
version: '0.163250'
-
class: Dist::Zilla::Plugin::Prereqs
config:
Dist::Zilla::Plugin::Prereqs:
phase: develop
type: recommends
name: '@Author::ETHER/@Git::VersionManager/pluginbundle version'
version: '6.037'
-
class: Dist::Zilla::Plugin::RewriteVersion::Transitional
config:
Dist::Zilla::Plugin::RewriteVersion:
add_tarball_name: 0
finders:
- ':ExecFiles'
- ':InstallModules'
global: 1
skip_version_provider: 0
Dist::Zilla::Plugin::RewriteVersion::Transitional: {}
name: '@Author::ETHER/@Git::VersionManager/RewriteVersion::Transitional'
version: '0.009'
-
class: Dist::Zilla::Plugin::MetaProvides::Update
name: '@Author::ETHER/@Git::VersionManager/MetaProvides::Update'
version: '0.007'
-
class: Dist::Zilla::Plugin::CopyFilesFromRelease
config:
Dist::Zilla::Plugin::CopyFilesFromRelease:
filename:
- Changes
match: []
name: '@Author::ETHER/@Git::VersionManager/CopyFilesFromRelease'
version: '0.007'
-
class: Dist::Zilla::Plugin::Git::Commit
config:
Dist::Zilla::Plugin::Git::Commit:
add_files_in:
- .
commit_msg: '%N-%v%t%n%n%c'
signoff: 0
Dist::Zilla::Role::Git::DirtyFiles:
allow_dirty:
- CONTRIBUTING
- Changes
- INSTALL
- LICENCE
- README.pod
allow_dirty_match: []
changelog: Changes
Dist::Zilla::Role::Git::Repo:
git_version: 2.52.0
repo_root: .
Dist::Zilla::Role::Git::StringFormatter:
time_zone: local
name: '@Author::ETHER/@Git::VersionManager/release snapshot'
version: '2.052'
-
class: Dist::Zilla::Plugin::Git::Tag
config:
Dist::Zilla::Plugin::Git::Tag:
branch: ~
changelog: Changes
signed: 0
tag: v0.034
tag_format: v%V
tag_message: v%v%t
Dist::Zilla::Role::Git::Repo:
git_version: 2.52.0
repo_root: .
Dist::Zilla::Role::Git::StringFormatter:
time_zone: local
name: '@Author::ETHER/@Git::VersionManager/Git::Tag'
version: '2.052'
-
class: Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional
config:
Dist::Zilla::Plugin::BumpVersionAfterRelease:
finders:
- ':InstallModules'
global: 1
munge_makefile_pl: 1
Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional: {}
name: '@Author::ETHER/@Git::VersionManager/BumpVersionAfterRelease::Transitional'
version: '0.009'
-
class: Dist::Zilla::Plugin::NextRelease
name: '@Author::ETHER/@Git::VersionManager/NextRelease'
version: '6.037'
-
class: Dist::Zilla::Plugin::Git::Commit
config:
Dist::Zilla::Plugin::Git::Commit:
add_files_in: []
commit_msg: 'increment $VERSION after %v release'
signoff: 0
Dist::Zilla::Role::Git::DirtyFiles:
allow_dirty:
- Build.PL
- Changes
- Makefile.PL
allow_dirty_match:
- (?^:^lib/.*\.pm$)
changelog: Changes
Dist::Zilla::Role::Git::Repo:
git_version: 2.52.0
repo_root: .
Dist::Zilla::Role::Git::StringFormatter:
time_zone: local
name: '@Author::ETHER/@Git::VersionManager/post-release commit'
version: '2.052'
-
class: Dist::Zilla::Plugin::Prereqs
config:
Dist::Zilla::Plugin::Prereqs:
phase: x_Dist_Zilla
type: requires
name: '@Author::ETHER/@Git::VersionManager/prereqs for @Git::VersionManager'
version: '6.037'
-
class: Dist::Zilla::Plugin::Git::Push
config:
Dist::Zilla::Plugin::Git::Push:
push_to:
- origin
remotes_must_exist: 1
Dist::Zilla::Role::Git::Repo:
git_version: 2.52.0
repo_root: .
name: '@Author::ETHER/Git::Push'
version: '2.052'
-
class: Dist::Zilla::Plugin::GitHub::Update
config:
Dist::Zilla::Plugin::GitHub::Update:
metacpan: 1
name: '@Author::ETHER/GitHub::Update'
version: '0.49'
-
class: Dist::Zilla::Plugin::Run::AfterRelease
config:
Dist::Zilla::Plugin::Run::Role::Runner:
fatal_errors: 0
quiet: 0
run:
- REDACTED
version: '0.050'
name: '@Author::ETHER/install release'
version: '0.050'
-
class: Dist::Zilla::Plugin::Run::AfterRelease
config:
Dist::Zilla::Plugin::Run::Role::Runner:
eval:
- 'print "release complete!\xa"'
fatal_errors: 1
quiet: 1
version: '0.050'
name: '@Author::ETHER/release complete'
version: '0.050'
-
class: Dist::Zilla::Plugin::ConfirmRelease
name: '@Author::ETHER/ConfirmRelease'
version: '6.037'
-
class: Dist::Zilla::Plugin::Prereqs
config:
Dist::Zilla::Plugin::Prereqs:
phase: x_Dist_Zilla
type: requires
name: '@Author::ETHER/prereqs for @Author::ETHER'
version: '6.037'
-
class: Dist::Zilla::Plugin::Prereqs
config:
Dist::Zilla::Plugin::Prereqs:
phase: runtime
type: requires
name: RuntimeRequires
version: '6.037'
-
class: Dist::Zilla::Plugin::Prereqs
config:
Dist::Zilla::Plugin::Prereqs:
phase: runtime
type: suggests
name: RuntimeSuggests
version: '6.037'
-
class: Dist::Zilla::Plugin::Prereqs
config:
Dist::Zilla::Plugin::Prereqs:
phase: develop
type: requires
name: DevelopRequires
version: '6.037'
-
class: Dist::Zilla::Plugin::RemovePrereqs
config:
Dist::Zilla::Plugin::RemovePrereqs:
modules_to_remove:
- B
- Carp
- Data::Dumper
- Encode
- Exporter
- File::Spec
- IO::Handle
- IPC::Open3
- JSON::PP
- Math::BigFloat
- Math::BigInt
- Module::Metadata
- Mojo::File
- Mojo::JSON
- Mojo::JSON::Pointer
- Mojo::URL
- Scalar::Util
- Test2::API
- Test::More
- constant
- lib
- open
- strict
- utf8
- warnings
name: RemovePrereqs
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':InstallModules'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':IncModules'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':TestFiles'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':ExtraTestFiles'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':ExecFiles'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':PerlExecFiles'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':ShareFiles'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':MainModule'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':AllFiles'
version: '6.037'
-
class: Dist::Zilla::Plugin::FinderCode
name: ':NoFiles'
version: '6.037'
-
class: Dist::Zilla::Plugin::VerifyPhases
name: '@Author::ETHER/PHASE VERIFICATION'
version: '0.016'
zilla:
class: Dist::Zilla::Dist::Builder
config:
is_trial: 0
version: '6.037'
x_authority: cpan:ETHER
x_contributors:
- 'Matt S Trout '
x_generated_by_perl: v5.44.0
x_serialization_backend: 'YAML::Tiny version 1.76'
x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later'
x_static_install: 0
x_use_unsafe_inc: 0
MANIFEST 100644 000766 000024 6126 15221771652 15233 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 # This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.037
Build.PL
CONTRIBUTING
Changes
INSTALL
LICENCE
MANIFEST
META.json
META.yml
README
dist.ini
inc/AppendSection.pm
inc/CheckConflicts.pm
lib/JSON/Schema/Tiny.pm
t/00-report-prereqs.dd
t/00-report-prereqs.t
t/additional-tests-draft2019-09.t
t/additional-tests-draft2019-09/README
t/additional-tests-draft2019-09/anchor.json
t/additional-tests-draft2019-09/badRef.json
t/additional-tests-draft2019-09/faux-buggy-schemas.json
t/additional-tests-draft2019-09/id.json
t/additional-tests-draft2019-09/integers.json
t/additional-tests-draft2019-09/invalid-input.json
t/additional-tests-draft2019-09/keyword-independence.json
t/additional-tests-draft2019-09/loose-types-const-enum.json
t/additional-tests-draft2019-09/ref-and-id.json
t/additional-tests-draft2019-09/ref.json
t/additional-tests-draft2019-09/short-circuit.json
t/additional-tests-draft2019-09/unknownKeyword.json
t/additional-tests-draft2019-09/vocabulary.json
t/additional-tests-draft2020-12.t
t/additional-tests-draft2020-12/README
t/additional-tests-draft2020-12/anchor.json
t/additional-tests-draft2020-12/badRef.json
t/additional-tests-draft2020-12/faux-buggy-schemas.json
t/additional-tests-draft2020-12/id.json
t/additional-tests-draft2020-12/integers.json
t/additional-tests-draft2020-12/invalid-input.json
t/additional-tests-draft2020-12/keyword-independence.json
t/additional-tests-draft2020-12/loose-types-const-enum.json
t/additional-tests-draft2020-12/ref.json
t/additional-tests-draft2020-12/short-circuit.json
t/additional-tests-draft2020-12/unknownKeyword.json
t/additional-tests-draft2020-12/vocabulary.json
t/additional-tests-draft7.t
t/additional-tests-draft7/README
t/additional-tests-draft7/anchor.json
t/additional-tests-draft7/badRef.json
t/additional-tests-draft7/faux-buggy-schemas.json
t/additional-tests-draft7/id.json
t/additional-tests-draft7/integers.json
t/additional-tests-draft7/invalid-input.json
t/additional-tests-draft7/keyword-independence.json
t/additional-tests-draft7/loose-types-const-enum.json
t/additional-tests-draft7/ref-and-id.json
t/additional-tests-draft7/ref.json
t/additional-tests-draft7/short-circuit.json
t/additional-tests-draft7/unknownKeyword.json
t/additional-tests-draft7/vocabulary.json
t/boolean-data.t
t/boolean-schemas.t
t/equality.t
t/errors.t
t/lib/Acceptance.pm
t/lib/Helper.pm
t/max_depth.t
t/pattern.t
t/ref.t
t/results/draft2019-09-acceptance.txt
t/results/draft2019-09-additional-tests.txt
t/results/draft2020-12-acceptance.txt
t/results/draft2020-12-additional-tests.txt
t/results/draft7-acceptance.txt
t/results/draft7-additional-tests.txt
t/specification_version.t
t/stringy-numbers.t
t/type.t
t/unsupported-keywords.t
t/zzz-acceptance-draft2019-09.t
t/zzz-acceptance-draft2020-12.t
t/zzz-acceptance-draft7.t
weaver.ini
xt/author/00-compile.t
xt/author/clean-namespaces.t
xt/author/distmeta.t
xt/author/eol.t
xt/author/kwalitee.t
xt/author/minimum-version.t
xt/author/mojibake.t
xt/author/no-tabs.t
xt/author/pod-coverage.t
xt/author/pod-spell.t
xt/author/pod-syntax.t
xt/author/portability.t
xt/release/changes_has_content.t
xt/release/cpan-changes.t
Build.PL 100644 000766 000024 507 15221771652 15353 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 # This Build.PL for JSON-Schema-Tiny was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.020.
use strict;
use warnings;
if ("$]" < 5.038) {
die "This distribution will not install where builtin::Backport exists.\n"
if eval { +require builtin::Backport; 1 };
}
use 5.020;
use Module::Build::Tiny 0.034;
Build_PL();
META.json 100644 000766 000024 136621 15221771652 15567 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 {
"abstract" : "Validate data against a schema, minimally",
"author" : [
"Karen Etheridge "
],
"dynamic_config" : 0,
"generated_by" : "Dist::Zilla version 6.037, CPAN::Meta::Converter version 2.150013",
"keywords" : [
"JSON",
"Schema",
"data",
"validation",
"structure",
"specification",
"tiny"
],
"license" : [
"perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : 2
},
"name" : "JSON-Schema-Tiny",
"no_index" : {
"directory" : [
"inc",
"t",
"xt"
]
},
"prereqs" : {
"configure" : {
"requires" : {
"Module::Build::Tiny" : "0.034",
"perl" : "5.020"
}
},
"develop" : {
"recommends" : {
"Dist::Zilla::PluginBundle::Author::ETHER" : "0.172",
"Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007"
},
"requires" : {
"Pod::Wordlist" : "0",
"Test2::Warnings" : "0",
"Test::CPAN::Changes" : "0.19",
"Test::CPAN::Meta" : "0",
"Test::CleanNamespaces" : "0.15",
"Test::EOL" : "0",
"Test::Kwalitee" : "1.21",
"Test::MinimumVersion" : "0",
"Test::Mojibake" : "0",
"Test::NoTabs" : "0",
"Test::Pod" : "1.41",
"Test::Pod::Coverage::TrustMe" : "0.002001",
"Test::Portability::Files" : "0",
"Test::Spelling" : "0.17",
"autovivification" : "0",
"strictures" : "2"
}
},
"runtime" : {
"requires" : {
"Feature::Compat::Try" : "0",
"Mojolicious" : "7.23",
"autovivification" : "0",
"builtin::compat" : "0.003003",
"experimental" : "0.026",
"if" : "0",
"perl" : "5.020",
"stable" : "0.031"
},
"suggests" : {
"Cpanel::JSON::XS" : "0"
}
},
"test" : {
"recommends" : {
"CPAN::Meta" : "2.120900"
},
"requires" : {
"List::Util" : "1.50",
"Test2::V0" : "0",
"Test2::Warnings" : "0",
"Test::Deep" : "0",
"Test::JSON::Schema::Acceptance" : "1.026",
"perl" : "5.020"
}
},
"x_Dist_Zilla" : {
"requires" : {
"Dist::Zilla" : "5",
"Dist::Zilla::Plugin::Authority" : "1.009",
"Dist::Zilla::Plugin::AutoMetaResources" : "0",
"Dist::Zilla::Plugin::AutoPrereqs" : "5.038",
"Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : "0.004",
"Dist::Zilla::Plugin::CheckIssues" : "0",
"Dist::Zilla::Plugin::CheckMetaResources" : "0",
"Dist::Zilla::Plugin::CheckPrereqsIndexed" : "0.019",
"Dist::Zilla::Plugin::CheckSelfDependency" : "0",
"Dist::Zilla::Plugin::CheckStrictVersion" : "0",
"Dist::Zilla::Plugin::ConfirmRelease" : "0",
"Dist::Zilla::Plugin::CopyFilesFromRelease" : "0",
"Dist::Zilla::Plugin::EnsureLatestPerl" : "0",
"Dist::Zilla::Plugin::FileFinder::ByName" : "0",
"Dist::Zilla::Plugin::GenerateFile::FromShareDir" : "0",
"Dist::Zilla::Plugin::Git::Check" : "0",
"Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch" : "0.004",
"Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts" : "0",
"Dist::Zilla::Plugin::Git::Commit" : "2.020",
"Dist::Zilla::Plugin::Git::Contributors" : "0.029",
"Dist::Zilla::Plugin::Git::Describe" : "0.004",
"Dist::Zilla::Plugin::Git::GatherDir" : "2.016",
"Dist::Zilla::Plugin::Git::Push" : "0",
"Dist::Zilla::Plugin::Git::Remote::Check" : "0",
"Dist::Zilla::Plugin::Git::Tag" : "0",
"Dist::Zilla::Plugin::GitHub::Update" : "0.40",
"Dist::Zilla::Plugin::GithubMeta" : "0.54",
"Dist::Zilla::Plugin::InstallGuide" : "1.200005",
"Dist::Zilla::Plugin::Keywords" : "0.004",
"Dist::Zilla::Plugin::License" : "5.038",
"Dist::Zilla::Plugin::Manifest" : "0",
"Dist::Zilla::Plugin::MetaConfig" : "0",
"Dist::Zilla::Plugin::MetaJSON" : "0",
"Dist::Zilla::Plugin::MetaNoIndex" : "0",
"Dist::Zilla::Plugin::MetaProvides::Package" : "1.15000002",
"Dist::Zilla::Plugin::MetaTests" : "0",
"Dist::Zilla::Plugin::MetaYAML" : "0",
"Dist::Zilla::Plugin::MinimumPerl" : "1.006",
"Dist::Zilla::Plugin::ModuleBuildTiny" : "0.012",
"Dist::Zilla::Plugin::MojibakeTests" : "0.8",
"Dist::Zilla::Plugin::NextRelease" : "5.033",
"Dist::Zilla::Plugin::PodSyntaxTests" : "5.040",
"Dist::Zilla::Plugin::PodWeaver" : "4.008",
"Dist::Zilla::Plugin::Prereqs" : "0",
"Dist::Zilla::Plugin::Prereqs::AuthorDeps" : "0.006",
"Dist::Zilla::Plugin::PromptIfStale" : "0",
"Dist::Zilla::Plugin::Readme" : "0",
"Dist::Zilla::Plugin::ReadmeAnyFromPod" : "0.142180",
"Dist::Zilla::Plugin::RemovePrereqs" : "0",
"Dist::Zilla::Plugin::RewriteVersion::Transitional" : "0.006",
"Dist::Zilla::Plugin::Run::AfterBuild" : "0.041",
"Dist::Zilla::Plugin::Run::AfterRelease" : "0.038",
"Dist::Zilla::Plugin::RunExtraTests" : "0.024",
"Dist::Zilla::Plugin::StaticInstall" : "0.005",
"Dist::Zilla::Plugin::Test::CPAN::Changes" : "0.012",
"Dist::Zilla::Plugin::Test::ChangesHasContent" : "0",
"Dist::Zilla::Plugin::Test::CleanNamespaces" : "0.006",
"Dist::Zilla::Plugin::Test::Compile" : "2.039",
"Dist::Zilla::Plugin::Test::EOL" : "0.17",
"Dist::Zilla::Plugin::Test::Kwalitee" : "2.10",
"Dist::Zilla::Plugin::Test::MinimumVersion" : "2.000010",
"Dist::Zilla::Plugin::Test::NoTabs" : "0.08",
"Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe" : "0",
"Dist::Zilla::Plugin::Test::PodSpelling" : "2.006003",
"Dist::Zilla::Plugin::Test::Portability" : "2.000007",
"Dist::Zilla::Plugin::Test::ReportPrereqs" : "0.022",
"Dist::Zilla::Plugin::TestRelease" : "0",
"Dist::Zilla::Plugin::UploadToCPAN" : "0",
"Dist::Zilla::Plugin::UseUnsafeInc" : "0",
"Dist::Zilla::PluginBundle::Author::ETHER" : "0.154",
"Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007",
"Software::License::Perl_5" : "0"
}
}
},
"provides" : {
"JSON::Schema::Tiny" : {
"file" : "lib/JSON/Schema/Tiny.pm",
"version" : "0.034"
}
},
"release_status" : "stable",
"resources" : {
"bugtracker" : {
"web" : "https://github.com/karenetheridge/JSON-Schema-Tiny/issues"
},
"homepage" : "https://github.com/karenetheridge/JSON-Schema-Tiny",
"repository" : {
"type" : "git",
"url" : "https://github.com/karenetheridge/JSON-Schema-Tiny.git",
"web" : "https://github.com/karenetheridge/JSON-Schema-Tiny"
}
},
"version" : "0.034",
"x_Dist_Zilla" : {
"perl" : {
"version" : "5.044000"
},
"plugins" : [
{
"class" : "inc::CheckConflicts",
"name" : "=inc::CheckConflicts",
"version" : null
},
{
"class" : "Dist::Zilla::Plugin::Prereqs",
"config" : {
"Dist::Zilla::Plugin::Prereqs" : {
"phase" : "develop",
"type" : "recommends"
}
},
"name" : "@Author::ETHER/pluginbundle version",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::PromptIfStale",
"config" : {
"Dist::Zilla::Plugin::PromptIfStale" : {
"check_all_plugins" : 0,
"check_all_prereqs" : 0,
"modules" : [
"Dist::Zilla::PluginBundle::Author::ETHER"
],
"phase" : "build",
"run_under_travis" : 0,
"skip" : []
}
},
"name" : "@Author::ETHER/stale modules, build",
"version" : "0.060"
},
{
"class" : "Dist::Zilla::Plugin::FileFinder::ByName",
"name" : "@Author::ETHER/Examples",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Git::GatherDir",
"config" : {
"Dist::Zilla::Plugin::GatherDir" : {
"exclude_filename" : [
"CONTRIBUTING",
"INSTALL",
"LICENCE",
"README.pod",
"TODO",
"pull_request_template.md"
],
"exclude_match" : [],
"include_dotfiles" : 0,
"prefix" : "",
"prune_directory" : [],
"root" : "."
},
"Dist::Zilla::Plugin::Git::GatherDir" : {
"include_untracked" : 0
}
},
"name" : "@Author::ETHER/Git::GatherDir",
"version" : "2.052"
},
{
"class" : "Dist::Zilla::Plugin::MetaYAML",
"name" : "@Author::ETHER/MetaYAML",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::MetaJSON",
"name" : "@Author::ETHER/MetaJSON",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Readme",
"name" : "@Author::ETHER/Readme",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Manifest",
"name" : "@Author::ETHER/Manifest",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::License",
"name" : "@Author::ETHER/License",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::GenerateFile::FromShareDir",
"config" : {
"Dist::Zilla::Plugin::GenerateFile::FromShareDir" : {
"destination_filename" : "CONTRIBUTING",
"dist" : "Dist-Zilla-PluginBundle-Author-ETHER",
"encoding" : "UTF-8",
"has_xs" : 0,
"location" : "build",
"source_filename" : "CONTRIBUTING"
},
"Dist::Zilla::Role::RepoFileInjector" : {
"allow_overwrite" : 1,
"repo_root" : ".",
"version" : "0.009"
}
},
"name" : "@Author::ETHER/generate CONTRIBUTING",
"version" : "0.016"
},
{
"class" : "Dist::Zilla::Plugin::InstallGuide",
"config" : {
"Dist::Zilla::Role::ModuleMetadata" : {
"Module::Metadata" : "1.000039",
"version" : "0.006"
}
},
"name" : "@Author::ETHER/InstallGuide",
"version" : "1.200014"
},
{
"class" : "Dist::Zilla::Plugin::Test::Compile",
"config" : {
"Dist::Zilla::Plugin::Test::Compile" : {
"bail_out_on_fail" : 1,
"fail_on_warning" : "author",
"fake_home" : 0,
"filename" : "xt/author/00-compile.t",
"module_finder" : [
":InstallModules"
],
"needs_display" : 0,
"phase" : "develop",
"script_finder" : [
":PerlExecFiles",
"@Author::ETHER/Examples"
],
"skips" : [],
"switch" : []
}
},
"name" : "@Author::ETHER/Test::Compile",
"version" : "2.059"
},
{
"class" : "Dist::Zilla::Plugin::Test::NoTabs",
"config" : {
"Dist::Zilla::Plugin::Test::NoTabs" : {
"filename" : "xt/author/no-tabs.t",
"finder" : [
":InstallModules",
":ExecFiles",
"@Author::ETHER/Examples",
":TestFiles",
":ExtraTestFiles"
]
}
},
"name" : "@Author::ETHER/Test::NoTabs",
"version" : "0.15"
},
{
"class" : "Dist::Zilla::Plugin::Test::EOL",
"config" : {
"Dist::Zilla::Plugin::Test::EOL" : {
"filename" : "xt/author/eol.t",
"finder" : [
":ExecFiles",
":ExtraTestFiles",
":InstallModules",
":TestFiles",
"@Author::ETHER/Examples"
],
"trailing_whitespace" : 1
}
},
"name" : "@Author::ETHER/Test::EOL",
"version" : "0.19"
},
{
"class" : "Dist::Zilla::Plugin::MetaTests",
"name" : "@Author::ETHER/MetaTests",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Test::CPAN::Changes",
"config" : {
"Dist::Zilla::Plugin::Test::CPAN::Changes" : {
"changelog" : "Changes",
"filename" : "xt/release/cpan-changes.t"
}
},
"name" : "@Author::ETHER/Test::CPAN::Changes",
"version" : "0.013"
},
{
"class" : "Dist::Zilla::Plugin::Test::ChangesHasContent",
"name" : "@Author::ETHER/Test::ChangesHasContent",
"version" : "0.011"
},
{
"class" : "Dist::Zilla::Plugin::Test::MinimumVersion",
"config" : {
"Dist::Zilla::Plugin::Test::MinimumVersion" : {
"max_target_perl" : "5.020"
}
},
"name" : "@Author::ETHER/Test::MinimumVersion",
"version" : "2.000011"
},
{
"class" : "Dist::Zilla::Plugin::PodSyntaxTests",
"name" : "@Author::ETHER/PodSyntaxTests",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe",
"config" : {
"Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe" : {
"finder" : [
":InstallModules"
]
}
},
"name" : "@Author::ETHER/Test::Pod::Coverage::TrustMe",
"version" : "v1.0.1"
},
{
"class" : "Dist::Zilla::Plugin::Test::PodSpelling",
"config" : {
"Dist::Zilla::Plugin::Test::PodSpelling" : {
"directories" : [
"examples",
"lib",
"script",
"t",
"xt"
],
"spell_cmd" : "",
"stopwords" : [
"irc"
],
"wordlist" : "Pod::Wordlist"
}
},
"name" : "@Author::ETHER/Test::PodSpelling",
"version" : "2.007006"
},
{
"class" : "Dist::Zilla::Plugin::Test::Kwalitee",
"config" : {
"Dist::Zilla::Plugin::Test::Kwalitee" : {
"filename" : "xt/author/kwalitee.t",
"skiptest" : []
}
},
"name" : "@Author::ETHER/Test::Kwalitee",
"version" : "2.13"
},
{
"class" : "Dist::Zilla::Plugin::MojibakeTests",
"name" : "@Author::ETHER/MojibakeTests",
"version" : "0.8"
},
{
"class" : "Dist::Zilla::Plugin::Test::ReportPrereqs",
"name" : "@Author::ETHER/Test::ReportPrereqs",
"version" : "0.029"
},
{
"class" : "Dist::Zilla::Plugin::Test::Portability",
"config" : {
"Dist::Zilla::Plugin::Test::Portability" : {
"options" : ""
}
},
"name" : "@Author::ETHER/Test::Portability",
"version" : "2.001003"
},
{
"class" : "Dist::Zilla::Plugin::Test::CleanNamespaces",
"config" : {
"Dist::Zilla::Plugin::Test::CleanNamespaces" : {
"filename" : "xt/author/clean-namespaces.t",
"skips" : []
}
},
"name" : "@Author::ETHER/Test::CleanNamespaces",
"version" : "0.006"
},
{
"class" : "Dist::Zilla::Plugin::Git::Describe",
"name" : "@Author::ETHER/Git::Describe",
"version" : "0.008"
},
{
"class" : "Dist::Zilla::Plugin::PodWeaver",
"config" : {
"Dist::Zilla::Plugin::PodWeaver" : {
"finder" : [
":InstallModules",
":PerlExecFiles"
],
"plugins" : [
{
"class" : "Pod::Weaver::Section::GenerateSection",
"name" : "SUPPORT",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::GenerateSection",
"name" : "VERSION",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Plugin::EnsurePod5",
"name" : "@Author::ETHER/EnsurePod5",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Plugin::H1Nester",
"name" : "@Author::ETHER/H1Nester",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Plugin::SingleEncoding",
"name" : "@Author::ETHER/SingleEncoding",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Plugin::Transformer",
"name" : "@Author::ETHER/List",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Plugin::Transformer",
"name" : "@Author::ETHER/Verbatim",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Region",
"name" : "@Author::ETHER/header",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Name",
"name" : "@Author::ETHER/Name",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Version",
"name" : "@Author::ETHER/Version",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Region",
"name" : "@Author::ETHER/prelude",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Generic",
"name" : "SYNOPSIS",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Generic",
"name" : "DESCRIPTION",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Generic",
"name" : "OVERVIEW",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Collect",
"name" : "ATTRIBUTES",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Collect",
"name" : "METHODS",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Collect",
"name" : "FUNCTIONS",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Collect",
"name" : "TYPES",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Leftovers",
"name" : "@Author::ETHER/Leftovers",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Region",
"name" : "@Author::ETHER/postlude",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::GenerateSection",
"name" : "@Author::ETHER/generate GIVING THANKS",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::GenerateSection",
"name" : "@Author::ETHER/generate SUPPORT",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Authors",
"name" : "@Author::ETHER/Authors",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::AllowOverride",
"name" : "@Author::ETHER/allow override AUTHOR",
"version" : "0.05"
},
{
"class" : "Pod::Weaver::Section::Contributors",
"name" : "@Author::ETHER/Contributors",
"version" : "0.009"
},
{
"class" : "Pod::Weaver::Section::Legal",
"name" : "@Author::ETHER/Legal",
"version" : "4.020"
},
{
"class" : "Pod::Weaver::Section::Region",
"name" : "@Author::ETHER/footer",
"version" : "4.020"
},
{
"class" : "inc::AppendSection",
"name" : "AppendSupport",
"version" : null
},
{
"class" : "inc::AppendSection",
"name" : "AppendVersion",
"version" : null
}
]
}
},
"name" : "@Author::ETHER/PodWeaver",
"version" : "4.010"
},
{
"class" : "Dist::Zilla::Plugin::GithubMeta",
"name" : "@Author::ETHER/GithubMeta",
"version" : "0.58"
},
{
"class" : "Dist::Zilla::Plugin::AutoMetaResources",
"name" : "@Author::ETHER/AutoMetaResources",
"version" : "1.21"
},
{
"class" : "Dist::Zilla::Plugin::Authority",
"name" : "@Author::ETHER/Authority",
"version" : "1.009"
},
{
"class" : "Dist::Zilla::Plugin::MetaNoIndex",
"name" : "@Author::ETHER/MetaNoIndex",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::MetaProvides::Package",
"config" : {
"Dist::Zilla::Plugin::MetaProvides::Package" : {
"finder" : [
":InstallModules"
],
"finder_objects" : [
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":InstallModules",
"version" : "6.037"
}
],
"include_underscores" : 0
},
"Dist::Zilla::Role::MetaProvider::Provider" : {
"$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004",
"inherit_missing" : 0,
"inherit_version" : 0,
"meta_noindex" : 1
},
"Dist::Zilla::Role::ModuleMetadata" : {
"Module::Metadata" : "1.000039",
"version" : "0.006"
}
},
"name" : "@Author::ETHER/MetaProvides::Package",
"version" : "2.004003"
},
{
"class" : "Dist::Zilla::Plugin::MetaConfig",
"name" : "@Author::ETHER/MetaConfig",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Keywords",
"config" : {
"Dist::Zilla::Plugin::Keywords" : {
"keywords" : [
"JSON",
"Schema",
"data",
"validation",
"structure",
"specification",
"tiny"
]
}
},
"name" : "@Author::ETHER/Keywords",
"version" : "0.007"
},
{
"class" : "Dist::Zilla::Plugin::UseUnsafeInc",
"config" : {
"Dist::Zilla::Plugin::UseUnsafeInc" : {
"dot_in_INC" : 0
}
},
"name" : "@Author::ETHER/UseUnsafeInc",
"version" : "0.002"
},
{
"class" : "Dist::Zilla::Plugin::AutoPrereqs",
"name" : "@Author::ETHER/AutoPrereqs",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Prereqs::AuthorDeps",
"name" : "@Author::ETHER/Prereqs::AuthorDeps",
"version" : "0.007"
},
{
"class" : "Dist::Zilla::Plugin::MinimumPerl",
"name" : "@Author::ETHER/MinimumPerl",
"version" : "1.006"
},
{
"class" : "Dist::Zilla::Plugin::ModuleBuildTiny",
"config" : {
"Dist::Zilla::Role::TestRunner" : {
"default_jobs" : 9
}
},
"name" : "@Author::ETHER/ModuleBuildTiny",
"version" : "0.020"
},
{
"class" : "Dist::Zilla::Plugin::Git::Contributors",
"config" : {
"Dist::Zilla::Plugin::Git::Contributors" : {
"git_version" : "2.52.0",
"include_authors" : 0,
"include_releaser" : 1,
"order_by" : "commits",
"paths" : []
}
},
"name" : "@Author::ETHER/Git::Contributors",
"version" : "0.039"
},
{
"class" : "Dist::Zilla::Plugin::StaticInstall",
"config" : {
"Dist::Zilla::Plugin::StaticInstall" : {
"dry_run" : 0,
"mode" : "off"
}
},
"name" : "@Author::ETHER/StaticInstall",
"version" : "0.012"
},
{
"class" : "Dist::Zilla::Plugin::RunExtraTests",
"config" : {
"Dist::Zilla::Role::TestRunner" : {
"default_jobs" : 9
}
},
"name" : "@Author::ETHER/RunExtraTests",
"version" : "0.029"
},
{
"class" : "Dist::Zilla::Plugin::CheckSelfDependency",
"config" : {
"Dist::Zilla::Plugin::CheckSelfDependency" : {
"finder" : [
":InstallModules"
]
},
"Dist::Zilla::Role::ModuleMetadata" : {
"Module::Metadata" : "1.000039",
"version" : "0.006"
}
},
"name" : "@Author::ETHER/CheckSelfDependency",
"version" : "0.011"
},
{
"class" : "Dist::Zilla::Plugin::Run::AfterBuild",
"config" : {
"Dist::Zilla::Plugin::Run::Role::Runner" : {
"fatal_errors" : 1,
"quiet" : 1,
"run" : [
"bash -c \"test -e .ackrc && grep -q -- '--ignore-dir=.latest' .ackrc || echo '--ignore-dir=.latest' >> .ackrc; if [[ `dirname '%d'` != .build ]]; then test -e .ackrc && grep -q -- '--ignore-dir=%d' .ackrc || echo '--ignore-dir=%d' >> .ackrc; fi\""
],
"version" : "0.050"
}
},
"name" : "@Author::ETHER/.ackrc",
"version" : "0.050"
},
{
"class" : "Dist::Zilla::Plugin::Run::AfterBuild",
"config" : {
"Dist::Zilla::Plugin::Run::Role::Runner" : {
"eval" : [
"if ('%d' =~ /^%n-[.[:xdigit:]]+$/) { unlink '.latest'; symlink '%d', '.latest'; }"
],
"fatal_errors" : 0,
"quiet" : 1,
"version" : "0.050"
}
},
"name" : "@Author::ETHER/.latest",
"version" : "0.050"
},
{
"class" : "Dist::Zilla::Plugin::CheckStrictVersion",
"name" : "@Author::ETHER/CheckStrictVersion",
"version" : "0.001"
},
{
"class" : "Dist::Zilla::Plugin::CheckMetaResources",
"name" : "@Author::ETHER/CheckMetaResources",
"version" : "0.001"
},
{
"class" : "Dist::Zilla::Plugin::EnsureLatestPerl",
"config" : {
"Dist::Zilla::Plugin::EnsureLatestPerl" : {
"Module::CoreList" : "5.20260722"
}
},
"name" : "@Author::ETHER/EnsureLatestPerl",
"version" : "0.010"
},
{
"class" : "Dist::Zilla::Plugin::PromptIfStale",
"config" : {
"Dist::Zilla::Plugin::PromptIfStale" : {
"check_all_plugins" : 1,
"check_all_prereqs" : 1,
"modules" : [],
"phase" : "release",
"run_under_travis" : 0,
"skip" : []
}
},
"name" : "@Author::ETHER/stale modules, release",
"version" : "0.060"
},
{
"class" : "Dist::Zilla::Plugin::Git::Check",
"config" : {
"Dist::Zilla::Plugin::Git::Check" : {
"untracked_files" : "die"
},
"Dist::Zilla::Role::Git::DirtyFiles" : {
"allow_dirty" : [],
"allow_dirty_match" : [],
"changelog" : "Changes"
},
"Dist::Zilla::Role::Git::Repo" : {
"git_version" : "2.52.0",
"repo_root" : "."
}
},
"name" : "@Author::ETHER/initial check",
"version" : "2.052"
},
{
"class" : "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts",
"config" : {
"Dist::Zilla::Role::Git::Repo" : {
"git_version" : "2.52.0",
"repo_root" : "."
}
},
"name" : "@Author::ETHER/Git::CheckFor::MergeConflicts",
"version" : "0.014"
},
{
"class" : "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch",
"config" : {
"Dist::Zilla::Role::Git::Repo" : {
"git_version" : "2.52.0",
"repo_root" : "."
}
},
"name" : "@Author::ETHER/Git::CheckFor::CorrectBranch",
"version" : "0.014"
},
{
"class" : "Dist::Zilla::Plugin::Git::Remote::Check",
"name" : "@Author::ETHER/Git::Remote::Check",
"version" : "0.1.2"
},
{
"class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed",
"name" : "@Author::ETHER/CheckPrereqsIndexed",
"version" : "0.022"
},
{
"class" : "Dist::Zilla::Plugin::TestRelease",
"name" : "@Author::ETHER/TestRelease",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Git::Check",
"config" : {
"Dist::Zilla::Plugin::Git::Check" : {
"untracked_files" : "die"
},
"Dist::Zilla::Role::Git::DirtyFiles" : {
"allow_dirty" : [],
"allow_dirty_match" : [],
"changelog" : "Changes"
},
"Dist::Zilla::Role::Git::Repo" : {
"git_version" : "2.52.0",
"repo_root" : "."
}
},
"name" : "@Author::ETHER/after tests",
"version" : "2.052"
},
{
"class" : "Dist::Zilla::Plugin::CheckIssues",
"name" : "@Author::ETHER/CheckIssues",
"version" : "0.011"
},
{
"class" : "Dist::Zilla::Plugin::UploadToCPAN",
"name" : "@Author::ETHER/UploadToCPAN",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::CopyFilesFromRelease",
"config" : {
"Dist::Zilla::Plugin::CopyFilesFromRelease" : {
"filename" : [
"CONTRIBUTING",
"INSTALL",
"LICENCE",
"LICENSE",
"ppport.h"
],
"match" : []
}
},
"name" : "@Author::ETHER/copy generated files",
"version" : "0.007"
},
{
"class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod",
"config" : {
"Dist::Zilla::Role::FileWatcher" : {
"version" : "0.006"
}
},
"name" : "@Author::ETHER/ReadmeAnyFromPod",
"version" : "0.163250"
},
{
"class" : "Dist::Zilla::Plugin::Prereqs",
"config" : {
"Dist::Zilla::Plugin::Prereqs" : {
"phase" : "develop",
"type" : "recommends"
}
},
"name" : "@Author::ETHER/@Git::VersionManager/pluginbundle version",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::RewriteVersion::Transitional",
"config" : {
"Dist::Zilla::Plugin::RewriteVersion" : {
"add_tarball_name" : 0,
"finders" : [
":ExecFiles",
":InstallModules"
],
"global" : 1,
"skip_version_provider" : 0
},
"Dist::Zilla::Plugin::RewriteVersion::Transitional" : {}
},
"name" : "@Author::ETHER/@Git::VersionManager/RewriteVersion::Transitional",
"version" : "0.009"
},
{
"class" : "Dist::Zilla::Plugin::MetaProvides::Update",
"name" : "@Author::ETHER/@Git::VersionManager/MetaProvides::Update",
"version" : "0.007"
},
{
"class" : "Dist::Zilla::Plugin::CopyFilesFromRelease",
"config" : {
"Dist::Zilla::Plugin::CopyFilesFromRelease" : {
"filename" : [
"Changes"
],
"match" : []
}
},
"name" : "@Author::ETHER/@Git::VersionManager/CopyFilesFromRelease",
"version" : "0.007"
},
{
"class" : "Dist::Zilla::Plugin::Git::Commit",
"config" : {
"Dist::Zilla::Plugin::Git::Commit" : {
"add_files_in" : [
"."
],
"commit_msg" : "%N-%v%t%n%n%c",
"signoff" : 0
},
"Dist::Zilla::Role::Git::DirtyFiles" : {
"allow_dirty" : [
"CONTRIBUTING",
"Changes",
"INSTALL",
"LICENCE",
"README.pod"
],
"allow_dirty_match" : [],
"changelog" : "Changes"
},
"Dist::Zilla::Role::Git::Repo" : {
"git_version" : "2.52.0",
"repo_root" : "."
},
"Dist::Zilla::Role::Git::StringFormatter" : {
"time_zone" : "local"
}
},
"name" : "@Author::ETHER/@Git::VersionManager/release snapshot",
"version" : "2.052"
},
{
"class" : "Dist::Zilla::Plugin::Git::Tag",
"config" : {
"Dist::Zilla::Plugin::Git::Tag" : {
"branch" : null,
"changelog" : "Changes",
"signed" : 0,
"tag" : "v0.034",
"tag_format" : "v%V",
"tag_message" : "v%v%t"
},
"Dist::Zilla::Role::Git::Repo" : {
"git_version" : "2.52.0",
"repo_root" : "."
},
"Dist::Zilla::Role::Git::StringFormatter" : {
"time_zone" : "local"
}
},
"name" : "@Author::ETHER/@Git::VersionManager/Git::Tag",
"version" : "2.052"
},
{
"class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional",
"config" : {
"Dist::Zilla::Plugin::BumpVersionAfterRelease" : {
"finders" : [
":InstallModules"
],
"global" : 1,
"munge_makefile_pl" : 1
},
"Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : {}
},
"name" : "@Author::ETHER/@Git::VersionManager/BumpVersionAfterRelease::Transitional",
"version" : "0.009"
},
{
"class" : "Dist::Zilla::Plugin::NextRelease",
"name" : "@Author::ETHER/@Git::VersionManager/NextRelease",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Git::Commit",
"config" : {
"Dist::Zilla::Plugin::Git::Commit" : {
"add_files_in" : [],
"commit_msg" : "increment $VERSION after %v release",
"signoff" : 0
},
"Dist::Zilla::Role::Git::DirtyFiles" : {
"allow_dirty" : [
"Build.PL",
"Changes",
"Makefile.PL"
],
"allow_dirty_match" : [
"(?^:^lib/.*\\.pm$)"
],
"changelog" : "Changes"
},
"Dist::Zilla::Role::Git::Repo" : {
"git_version" : "2.52.0",
"repo_root" : "."
},
"Dist::Zilla::Role::Git::StringFormatter" : {
"time_zone" : "local"
}
},
"name" : "@Author::ETHER/@Git::VersionManager/post-release commit",
"version" : "2.052"
},
{
"class" : "Dist::Zilla::Plugin::Prereqs",
"config" : {
"Dist::Zilla::Plugin::Prereqs" : {
"phase" : "x_Dist_Zilla",
"type" : "requires"
}
},
"name" : "@Author::ETHER/@Git::VersionManager/prereqs for @Git::VersionManager",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Git::Push",
"config" : {
"Dist::Zilla::Plugin::Git::Push" : {
"push_to" : [
"origin"
],
"remotes_must_exist" : 1
},
"Dist::Zilla::Role::Git::Repo" : {
"git_version" : "2.52.0",
"repo_root" : "."
}
},
"name" : "@Author::ETHER/Git::Push",
"version" : "2.052"
},
{
"class" : "Dist::Zilla::Plugin::GitHub::Update",
"config" : {
"Dist::Zilla::Plugin::GitHub::Update" : {
"metacpan" : 1
}
},
"name" : "@Author::ETHER/GitHub::Update",
"version" : "0.49"
},
{
"class" : "Dist::Zilla::Plugin::Run::AfterRelease",
"config" : {
"Dist::Zilla::Plugin::Run::Role::Runner" : {
"fatal_errors" : 0,
"quiet" : 0,
"run" : [
"REDACTED"
],
"version" : "0.050"
}
},
"name" : "@Author::ETHER/install release",
"version" : "0.050"
},
{
"class" : "Dist::Zilla::Plugin::Run::AfterRelease",
"config" : {
"Dist::Zilla::Plugin::Run::Role::Runner" : {
"eval" : [
"print \"release complete!\\xa\""
],
"fatal_errors" : 1,
"quiet" : 1,
"version" : "0.050"
}
},
"name" : "@Author::ETHER/release complete",
"version" : "0.050"
},
{
"class" : "Dist::Zilla::Plugin::ConfirmRelease",
"name" : "@Author::ETHER/ConfirmRelease",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Prereqs",
"config" : {
"Dist::Zilla::Plugin::Prereqs" : {
"phase" : "x_Dist_Zilla",
"type" : "requires"
}
},
"name" : "@Author::ETHER/prereqs for @Author::ETHER",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Prereqs",
"config" : {
"Dist::Zilla::Plugin::Prereqs" : {
"phase" : "runtime",
"type" : "requires"
}
},
"name" : "RuntimeRequires",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Prereqs",
"config" : {
"Dist::Zilla::Plugin::Prereqs" : {
"phase" : "runtime",
"type" : "suggests"
}
},
"name" : "RuntimeSuggests",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Prereqs",
"config" : {
"Dist::Zilla::Plugin::Prereqs" : {
"phase" : "develop",
"type" : "requires"
}
},
"name" : "DevelopRequires",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::RemovePrereqs",
"config" : {
"Dist::Zilla::Plugin::RemovePrereqs" : {
"modules_to_remove" : [
"B",
"Carp",
"Data::Dumper",
"Encode",
"Exporter",
"File::Spec",
"IO::Handle",
"IPC::Open3",
"JSON::PP",
"Math::BigFloat",
"Math::BigInt",
"Module::Metadata",
"Mojo::File",
"Mojo::JSON",
"Mojo::JSON::Pointer",
"Mojo::URL",
"Scalar::Util",
"Test2::API",
"Test::More",
"constant",
"lib",
"open",
"strict",
"utf8",
"warnings"
]
}
},
"name" : "RemovePrereqs",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":InstallModules",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":IncModules",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":TestFiles",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":ExtraTestFiles",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":ExecFiles",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":PerlExecFiles",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":ShareFiles",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":MainModule",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":AllFiles",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":NoFiles",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::VerifyPhases",
"name" : "@Author::ETHER/PHASE VERIFICATION",
"version" : "0.016"
}
],
"zilla" : {
"class" : "Dist::Zilla::Dist::Builder",
"config" : {
"is_trial" : 0
},
"version" : "6.037"
}
},
"x_authority" : "cpan:ETHER",
"x_contributors" : [
"Matt S Trout "
],
"x_generated_by_perl" : "v5.44.0",
"x_serialization_backend" : "Cpanel::JSON::XS version 4.42",
"x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later",
"x_static_install" : 0,
"x_use_unsafe_inc" : 0
}
errors.t 100640 000766 000024 77676 15221771652 16104 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
subtest 'multiple types' => sub {
cmp_result(
evaluate(true, { type => ['string','number'] }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/type',
error => 'got boolean, not one of string, number',
},
],
},
'correct error generated from type',
);
};
subtest 'multipleOf' => sub {
cmp_result(
evaluate(3, { multipleOf => 2 }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/multipleOf',
error => 'value is not a multiple of 2',
},
],
},
'correct error generated from multipleOf',
);
};
subtest 'uniqueItems' => sub {
cmp_result(
evaluate([qw(a b c d c)], { uniqueItems => true }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/uniqueItems',
error => 'items at indices 2 and 4 are not unique',
},
],
},
'correct error generated from uniqueItems',
);
};
subtest 'allOf, not, and false schema' => sub {
cmp_result(
evaluate(
my $data = 1,
my $schema = { allOf => [ true, false, { not => { not => false } } ] },
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/1',
error => 'subschema is false',
},
{
instanceLocation => '',
keywordLocation => '/allOf/2/not',
error => 'subschema is valid',
},
{
instanceLocation => '',
keywordLocation => '/allOf',
error => 'subschemas 1, 2 are not valid',
},
],
},
'correct errors with locations; did not collect errors inside "not"',
);
local $JSON::Schema::Tiny::SHORT_CIRCUIT = 1;
cmp_result(
evaluate($data, $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/1',
error => 'subschema is false',
},
{
instanceLocation => '',
keywordLocation => '/allOf',
error => 'subschema 1 is not valid',
},
],
},
'short-circuited results contain fewer errors',
);
};
subtest 'anyOf keeps all errors for false paths when invalid, discards errors for false paths when valid' => sub {
cmp_result(
evaluate(
my $data = 1,
my $schema = { anyOf => [ false, false ] },
),
my $result = {
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/anyOf/0',
error => 'subschema is false',
},
{
instanceLocation => '',
keywordLocation => '/anyOf/1',
error => 'subschema is false',
},
{
instanceLocation => '',
keywordLocation => '/anyOf',
error => 'no subschemas are valid',
},
],
},
'correct errors with locations; did not collect errors inside "not"',
);
cmp_result(
do {
local $JSON::Schema::Tiny::SHORT_CIRCUIT = 1;
evaluate($data, $schema)
},
$result,
'short-circuited results contain the same errors (short-circuiting not possible)',
);
cmp_result(
evaluate(1, { anyOf => [ false, true ], not => true }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/not',
error => 'subschema is true',
},
],
},
'did not collect errors from failure paths from successful anyOf',
);
cmp_result(
evaluate(1, { anyOf => [ false, true ] }),
{ valid => true },
'no errors collected for true validation',
);
};
subtest 'applicators with non-boolean subschemas, discarding intermediary errors - items' => sub {
my $result = evaluate(
my $data = [ 1, 2 ],
my $schema = {
items => {
anyOf => [
{ minimum => 2 },
{ allOf => [ { maximum => -1 }, { maximum => 0 } ] },
]
},
},
);
# - evaluate /items on instance ''
# - evaluate /items on instance /0
# - evaluate /items/anyOf on instance /0
# - evaluate /items/anyOf/0 on instance /0
# - evaluate /items/anyOf/0/minimum on instance /0 FAIL
# /items/anyOf/0 FAILS
# - evaluate /items/anyOf/1 on instance /0
# - evaluate /items/anyOf/1/allOf on instance /0
# - evaluate /items/anyOf/1/allOf/0 on instance /0
# - evaluate /items/anyOf/1/allOf/0/maximum on instance /0 FAIL
# - evaluate /items/anyOf/1/allOf/1 on instance /0
# - evaluate /items/anyOf/1/allOf/1/maximum on instance /0 FAIL
# /items/anyOf/1/allOf FAILS
# /items/anyOf/1 FAILS (no message)
# /items/anyOf FAILS
# /items FAILS on instance /0 (no message)
# - evaluate /items on instance /1
# - evaluate /items/anyOf on instance /1
# - evaluate /items/anyOf/0 on instance /1
# - evaluate /items/anyOf/0/minimum on instance /1 PASS
# /items/anyOf/0 PASSES
# - evaluate /items/anyOf/1 on instance /1
# - evaluate /items/anyOf/1/allOf on instance /1
# - evaluate /items/anyOf/1/allOf/0 on instance /1
# - evaluate /items/anyOf/1/allOf/0/maximum on instance /1 FAIL
# - evaluate /items/anyOf/1/allOf/1 on instance /1
# - evaluate /items/anyOf/1/allOf/1/maximum on instance /1 FAIL
# /items/anyOf/1/allOf FAILS
# /items/anyOf/1 FAILS (no message)
# /items/anyOf PASSES -- all failures above are discarded
# /items PASSES on instance /1
# /items FAILS (across all instances)
# entire schema FAILS
cmp_result(
$result,
{
valid => false,
errors => [
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf/0/minimum',
error => 'value is smaller than 2',
},
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf/1/allOf/0/maximum',
error => 'value is larger than -1',
},
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf/1/allOf/1/maximum',
error => 'value is larger than 0',
},
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf/1/allOf',
error => 'subschemas 0, 1 are not valid',
},
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf',
error => 'no subschemas are valid',
},
# these errors are discarded because /items/anyOf passes on instance /1
#{
# instanceLocation => '/1',
# keywordLocation => '/items/anyOf/1/allOf/0/maximum',
# error => 'value is larger than -1',
#},
#{
# instanceLocation => '/1',
# keywordLocation => '/items/anyOf/1/allOf/1/maximum',
# error => 'value is larger than 0',
#},
#{
# instanceLocation => '/1',
# keywordLocation => '/items/anyOf/1/allOf',
# error => 'subschemas 0, 1 are not valid',
#},
{
instanceLocation => '',
keywordLocation => '/items',
error => 'subschema is not valid against all items',
},
],
},
'collected all errors from subschemas for failing branches only (passing branches discard errors)',
);
local $JSON::Schema::Tiny::SHORT_CIRCUIT = 1;
cmp_result(
evaluate($data, $schema),
{
valid => false,
errors => [
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf/0/minimum',
error => 'value is smaller than 2'
},
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf/1/allOf/0/maximum',
error => 'value is larger than -1',
},
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf/1/allOf',
error => 'subschema 0 is not valid',
},
{
instanceLocation => '/0',
keywordLocation => '/items/anyOf',
error => 'no subschemas are valid',
},
{
instanceLocation => '',
keywordLocation => '/items',
error => 'subschema is not valid against all items',
},
],
},
'short-circuited results contain fewer errors',
);
};
subtest 'applicators with non-boolean subschemas, discarding intermediary errors - contains' => sub {
my $result = evaluate(
my $data = [
{ foo => 1 },
{ bar => 2 },
],
my $schema = {
not => true,
contains => {
properties => {
foo => false, # if 'foo' is present, then we fail
},
},
},
);
# - evaluate /not on instance ''
# - evaluate subschema "true" - PASS
# /not FAILS.
# - evaluate /contains on instance ''
# - evaluate /contains on instance /0
# - evaluate /contains/properties on instance /0
# - evaluate /contains/properties/foo on instance /0/foo
# schema is FALSE.
# /contains/properties FAILS
# /contains does not match on instance /0
# - evaluate /contains on instance /1
# - evaluate /contains/properties on instance /1
# - evaluate /contains/properties/foo on instance /1/foo - does not exist.
# /contains/properties/foo PASSES
# /contains/properties PASSES
# /contains matches on instance /1
# /contains has at least 1 match; it PASSES
# entire schema FAILS
cmp_result(
$result,
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/not',
error => 'subschema is true',
},
# these errors are discarded because /contains passes on instance /1
#{
# instanceLocation => '/0/foo',
# keywordLocation => '/contains/properties/foo',
# error => 'subschema is false',
#},
#{
# instanceLocation => '/0',
# keywordLocation => '/contains/properties',
# error => 'not all properties are valid',
#},
],
},
'collected all errors from subschemas for failing branches only (passing branches discard errors)',
);
cmp_result(
evaluate($data, $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/not',
error => 'subschema is true',
},
],
},
'short-circuited results contain the same errors',
);
};
subtest 'errors with $refs' => sub {
my $result = evaluate(
[ { x => 1 }, { x => 2 }, { x => 3 } ],
{
'$defs' => {
mydef => {
type => 'integer',
minimum => 5,
'$ref' => '#/$defs/myint',
},
myint => {
multipleOf => 5,
},
},
items => {
properties => {
x => {
'$ref' => '#/$defs/mydef',
maximum => 2,
},
},
}
},
);
# evaluation order:
# /items/properties/x/$ref (mydef) /$ref (myint) /multipleOf
# /items/properties/x/$ref (mydef) /type
# /items/properties/x/$ref (mydef) /minimum
# /items/properties/x/maximum
cmp_result(
$result,
{
valid => false,
errors => [
{
instanceLocation => '/0/x',
keywordLocation => '/items/properties/x/$ref/$ref/multipleOf',
absoluteKeywordLocation => '#/$defs/myint/multipleOf',
error => 'value is not a multiple of 5',
},
{
instanceLocation => '/0/x',
keywordLocation => '/items/properties/x/$ref/minimum',
absoluteKeywordLocation => '#/$defs/mydef/minimum',
error => 'value is smaller than 5',
},
{
instanceLocation => '/0',
keywordLocation => '/items/properties',
error => 'not all properties are valid',
},
{
instanceLocation => '/1/x',
keywordLocation => '/items/properties/x/$ref/$ref/multipleOf',
absoluteKeywordLocation => '#/$defs/myint/multipleOf',
error => 'value is not a multiple of 5',
},
{
instanceLocation => '/1/x',
keywordLocation => '/items/properties/x/$ref/minimum',
absoluteKeywordLocation => '#/$defs/mydef/minimum',
error => 'value is smaller than 5',
},
{
instanceLocation => '/1',
keywordLocation => '/items/properties',
error => 'not all properties are valid',
},
{
instanceLocation => '/2/x',
keywordLocation => '/items/properties/x/$ref/$ref/multipleOf',
absoluteKeywordLocation => '#/$defs/myint/multipleOf',
error => 'value is not a multiple of 5',
},
{
instanceLocation => '/2/x',
keywordLocation => '/items/properties/x/$ref/minimum',
absoluteKeywordLocation => '#/$defs/mydef/minimum',
error => 'value is smaller than 5',
},
{
instanceLocation => '/2/x',
keywordLocation => '/items/properties/x/maximum',
error => 'value is larger than 2',
},
{
instanceLocation => '/2',
keywordLocation => '/items/properties',
error => 'not all properties are valid',
},
{
instanceLocation => '',
keywordLocation => '/items',
error => 'subschema is not valid against all items',
},
],
},
'errors have correct absolute keyword location via $ref',
);
};
subtest 'const and enum' => sub {
cmp_result(
evaluate(
{ foo => { a => { b => { c => { d => 1 } } } } },
{
properties => {
foo => {
allOf => [
{ const => { a => { b => { c => { d => 2 } } } } },
{ enum => [ 0, 'whargarbl', { a => { b => { c => { d => 2 } } } } ] },
],
}
},
},
),
{
valid => false,
errors => [
{
instanceLocation => '/foo',
keywordLocation => '/properties/foo/allOf/0/const',
error => 'value does not match (at \'/a/b/c/d\': integers not equal)',
},
{
instanceLocation => '/foo',
keywordLocation => '/properties/foo/allOf/1/enum',
error => 'value does not match (from enum 0 at \'\': wrong type: object vs integer; from enum 1 at \'\': wrong type: object vs string; from enum 2 at \'/a/b/c/d\': integers not equal)',
},
{
instanceLocation => '/foo',
keywordLocation => '/properties/foo/allOf',
error => 'subschemas 0, 1 are not valid',
},
{
instanceLocation => '',
keywordLocation => '/properties',
error => 'not all properties are valid',
},
],
},
'got details about object differences in errors from const and enum',
);
};
subtest 'exceptions' => sub {
cmp_result(
evaluate(
{ x => 'hello' },
{
allOf => [
{ properties => { x => 1 } },
{ properties => { x => false } },
],
}
),
{
valid => false,
errors => [
{
instanceLocation => '/x',
keywordLocation => '/allOf/0/properties/x',
error => 'invalid schema type: integer',
},
],
},
'a subschema of an invalid type returns an error at the right position, and evaluation aborts',
);
cmp_result(
evaluate(
1,
{
allOf => [
{ type => 'whargarbl' },
false,
],
}
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/0/type',
error => 'unrecognized type "whargarbl"',
},
],
},
'invalid argument to "type" returns an error at the right position, and evaluation aborts',
);
};
subtest 'unresolvable $ref to a local resource' => sub {
cmp_result(
evaluate(
1,
{
'$ref' => '#/$defs/myint',
'$defs' => {
myint => {
'$ref' => '#/$defs/does-not-exist',
},
},
anyOf => [ false ],
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/$ref/$ref',
absoluteKeywordLocation => '#/$defs/myint/$ref',
error => 'EXCEPTION: unable to find resource #/$defs/does-not-exist',
},
],
},
'error for a bad $ref reports the correct absolute location that was referred to',
);
};
subtest 'abort due to a schema error' => sub {
cmp_result(
evaluate(
1,
{
oneOf => [
{ type => 'number' },
{ type => 'string' },
{ type => 'whargarbl' },
],
}
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/oneOf/2/type',
error => 'unrecognized type "whargarbl"',
},
],
},
'exception inside a oneOf (where errors are localized) are still included in the result',
);
};
subtest 'sorted property names' => sub {
cmp_result(
evaluate(
{ foo => 1, bar => 1, baz => 1, hello => 1 },
{
properties => {
foo => false,
bar => false,
},
additionalProperties => false,
}
),
{
valid => false,
errors => [
{
instanceLocation => '/bar',
keywordLocation => '/properties/bar',
error => 'property not permitted',
},
{
instanceLocation => '/foo',
keywordLocation => '/properties/foo',
error => 'property not permitted',
},
{
instanceLocation => '',
keywordLocation => '/properties',
error => 'not all properties are valid',
},
{
instanceLocation => '/baz',
keywordLocation => '/additionalProperties',
error => 'additional property not permitted',
},
{
instanceLocation => '/hello',
keywordLocation => '/additionalProperties',
error => 'additional property not permitted',
},
{
instanceLocation => '',
keywordLocation => '/additionalProperties',
error => 'not all additional properties are valid',
},
],
},
'property names are considered in sorted order',
);
};
subtest 'bad regex in schema' => sub {
cmp_result(
evaluate(
{
my_pattern => 'foo',
},
my $schema = {
type => 'object',
properties => {
my_pattern => {
type => 'string',
pattern => '(',
},
my_patternProperties => {
type => 'object',
patternProperties => { '(' => true },
additionalProperties => false,
},
my_runtime_pattern => {
type => 'string',
pattern => '\p{main::IsFoo}', # qr/$pattern/ will not find this error, but m/$pattern/ will
},
},
},
),
{
valid => false,
errors => [
{
instanceLocation => ignore, # not important - JST errors at runtime, JSM at traverse time
keywordLocation => '/properties/my_pattern/pattern',
error => re(qr/^Unmatched \( in regex/),
},
],
},
'bad "pattern" regex is properly noted in error',
);
cmp_result(
evaluate(
{
my_patternProperties => { foo => 1 },
},
$schema,
),
{
valid => false,
errors => [
{
instanceLocation => ignore, # not important - JST errors at runtime, JSM at traverse time
keywordLocation => '/properties/my_patternProperties/patternProperties/(',
error => re(qr/^Unmatched \( in regex/),
},
# Note no error for missing IsFoo
],
},
'bad "patternProperties" regex is properly noted in error',
);
cmp_result(
evaluate(
{ my_runtime_pattern => 'foo' },
$schema,
),
{
valid => false,
errors => [
{
instanceLocation => '', # not important - JST and JSM both error at runtime
keywordLocation => '',
# in 5.28 and earlier: Can't find Unicode property definition "IsFoo"
# in 5.30 and later: Unknown user-defined property name \p{main::IsFoo}
error => re(qr/^EXCEPTION: .*property.*IsFoo/),
},
],
},
'bad "pattern" regex is properly noted in error',
);
no warnings 'once';
*IsFoo = sub { "0066\n006F\n" }; # accepts 'f', 'o'
cmp_result(
evaluate(
{ my_runtime_pattern => 'foo' },
$schema,
),
{
valid => true,
},
'"pattern" regex is now valid, due to the Unicode property becoming defined',
);
};
subtest 'JSON pointer escaping' => sub {
cmp_result(
evaluate(
{ '{}' => { 'my~tilde/slash-property' => 1 } },
my $schema = {
'$defs' => {
mydef => {
properties => {
'{}' => {
properties => {
'my~tilde/slash-property' => false,
},
patternProperties => {
'/' => { minimum => 6 },
'[~/]' => { minimum => 7 },
'~' => { minimum => 5 },
'~.*/' => false,
},
},
},
},
},
'$ref' => '#/$defs/mydef',
},
),
{
valid => false,
errors => my $errors = [
{
instanceLocation => '/{}/my~0tilde~1slash-property',
keywordLocation => '/$ref/properties/{}/properties/my~0tilde~1slash-property',
absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/properties/my~0tilde~1slash-property',
error => 'property not permitted',
},
{
instanceLocation => '/{}',
keywordLocation => '/$ref/properties/{}/properties',
absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/properties',
error => 'not all properties are valid',
},
{
instanceLocation => '/{}/my~0tilde~1slash-property',
keywordLocation => '/$ref/properties/{}/patternProperties/~1/minimum',
absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/~1/minimum', # /
error => 'value is smaller than 6',
},
{
instanceLocation => '/{}/my~0tilde~1slash-property',
keywordLocation => '/$ref/properties/{}/patternProperties/[~0~1]/minimum',
absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/%5B~0~1%5D/minimum', # [~/]
error => 'value is smaller than 7',
},
{
instanceLocation => '/{}/my~0tilde~1slash-property',
keywordLocation => '/$ref/properties/{}/patternProperties/~0/minimum',
absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/~0/minimum', # ~
error => 'value is smaller than 5',
},
{
instanceLocation => '/{}/my~0tilde~1slash-property',
keywordLocation => '/$ref/properties/{}/patternProperties/~0.*~1',
absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/~0.*~1', # ~.*/
error => 'property not permitted',
},
{
instanceLocation => '/{}',
keywordLocation => '/$ref/properties/{}/patternProperties',
absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties',
error => 'not all properties are valid',
},
{
instanceLocation => '',
keywordLocation => '/$ref/properties',
absoluteKeywordLocation => '#/$defs/mydef/properties',
error => 'not all properties are valid',
},
],
},
'JSON pointers are properly escaped; URIs doubly so',
);
cmp_result(
evaluate(
{ '{}' => { 'my~tilde/slash-property' => 1 } },
$schema->{'$defs'}{mydef},
),
{
valid => false,
errors => [
map +{
error => $_->{error},
instanceLocation => $_->{instanceLocation},
keywordLocation => $_->{keywordLocation} =~ s{^/\$ref}{}r,
}, @$errors
],
},
'absoluteKeywordLocation is omitted when paths are the same, not counting uri encoding',
);
cmp_result(
evaluate(
{ '{}' => { 'my~tilde/slash-property' => 1 } },
{
'$defs' => {
mydef => {
properties => {
'{}' => {
patternProperties => {
'a{' => { minimum => 2 }, # this is a broken regex
},
},
},
},
},
'$ref' => '#/$defs/mydef',
},
),
{
valid => false,
errors => [
{
instanceLocation => ignore, # not important - JST errors at runtime, JSM at traverse time
keywordLocation => '/$ref/properties/{}/patternProperties/a{',
absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/a%7B',
error => re(qr/^Unescaped left brace in regex is (illegal here|deprecated|passed through)/),
},
],
},
# all the other _schema_path_suffix cases are tested in the earlier test case
'use of _schema_path_suffix in a fatal error',
) if "$]" >= 5.022;
};
subtest 'invalid $schema' => sub {
cmp_result(
evaluate(
1,
{
allOf => [
true,
{ '$schema' => 'https://json-schema.org/draft/2019-09/schema' },
],
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/1/$schema',
error => '$schema can only appear at the schema resource root',
},
],
},
'$schema can only appear at the root of a schema, when there is no canonical URI',
);
};
subtest 'absoluteKeywordLocation' => sub {
cmp_result(
do {
local $JSON::Schema::Tiny::MAX_DEPTH = 1;
evaluate(
[ [ 1 ] ],
{ items => { '$ref' => '#' } },
)
},
{
valid => false,
errors => [
{
instanceLocation => '/0',
keywordLocation => '/items/$ref',
absoluteKeywordLocation => '',
error => 'EXCEPTION: maximum evaluation depth exceeded',
},
],
},
'absoluteKeywordLocation is included when different from instanceLocation, even when empty',
);
cmp_result(
evaluate(1, { '$ref' => '#/does_not_exist' }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/$ref',
error => 'EXCEPTION: unable to find resource #/does_not_exist',
},
],
},
'absoluteKeywordLocation is not included when the path equals keywordLocation, even if a $ref is present',
);
cmp_result(
evaluate(
1,
my $schema = {
'$id' => 'https://localhost:1234/bloop',
allOf => [
{
'$id' => 'foo.json',
type => 'object',
},
{
'$id' => 'bar/',
allOf => [
{
'$id' => 'alpha',
type => 'object',
},
],
},
{ type => 'object' },
],
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/0/type',
absoluteKeywordLocation => 'https://localhost:1234/foo.json#/type',
error => 'got integer, not object',
},
{
instanceLocation => '',
keywordLocation => '/allOf/1/allOf/0/type',
absoluteKeywordLocation => 'https://localhost:1234/bar/alpha#/type',
error => 'got integer, not object',
},
{
instanceLocation => '',
keywordLocation => '/allOf/1/allOf',
absoluteKeywordLocation => 'https://localhost:1234/bar/#/allOf',
error => 'subschema 0 is not valid',
},
{
instanceLocation => '',
keywordLocation => '/allOf/2/type',
absoluteKeywordLocation => 'https://localhost:1234/bloop#/allOf/2/type',
error => 'got integer, not object',
},
{
instanceLocation => '',
keywordLocation => '/allOf',
absoluteKeywordLocation => 'https://localhost:1234/bloop#/allOf',
error => 'subschemas 0, 1, 2 are not valid',
},
],
},
'absoluteKeywordLocation reflects the canonical schema uri as it changes when passing through $id',
);
$schema->{'$id'} = '#my_anchor';
$schema->{allOf}[2]{'$id'} = '#my_anchor2';
cmp_result(
JSON::Schema::Tiny->new(specification_version => 'draft7')->evaluate(1, $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/0/type',
absoluteKeywordLocation => 'foo.json#/type',
error => 'got integer, not object',
},
{
instanceLocation => '',
keywordLocation => '/allOf/1/allOf/0/type',
absoluteKeywordLocation => 'bar/alpha#/type',
error => 'got integer, not object',
},
{
instanceLocation => '',
keywordLocation => '/allOf/1/allOf',
absoluteKeywordLocation => 'bar/#/allOf',
error => 'subschema 0 is not valid',
},
{
instanceLocation => '',
keywordLocation => '/allOf/2/type',
error => 'got integer, not object',
},
{
instanceLocation => '',
keywordLocation => '/allOf',
error => 'subschemas 0, 1, 2 are not valid',
},
],
},
'plain-name fragment in $id does not change canonical schema uri',
);
};
subtest dependentRequired => sub {
cmp_result(
evaluate(1, { dependentRequired => { foo => [ 1 ] } }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/dependentRequired/foo/0',
error => 'element #0 is not a string',
},
],
},
'dependentRequired traversal error',
);
};
subtest 'numbers in output' => sub {
cmp_result(
evaluate(
5,
{
multipleOf => 1.23456789,
maximum => 4.23456789,
minimum => 6.23456789,
exclusiveMaximum => 4.23456789,
exclusiveMinimum => 6.23456789,
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/multipleOf',
error => 'value is not a multiple of 1.23456789',
},
{
instanceLocation => '',
keywordLocation => '/maximum',
error => 'value is larger than 4.23456789',
},
{
instanceLocation => '',
keywordLocation => '/exclusiveMaximum',
error => 'value is equal to or larger than 4.23456789',
},
{
instanceLocation => '',
keywordLocation => '/minimum',
error => 'value is smaller than 6.23456789',
},
{
instanceLocation => '',
keywordLocation => '/exclusiveMinimum',
error => 'value is equal to or smaller than 6.23456789',
},
],
},
'numbers in errors do not lose any digits of precision',
);
};
done_testing;
weaver.ini 100640 000766 000024 1636 15221771652 16071 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 ; this section is woven into the beginning of the document, but inc::AppendSection below later
; plucks it out of its position and appends it to the generic SUPPORT section that is subsequently
; generated by the plugin bundle.
[GenerateSection / SUPPORT]
main_module_only = 0
text = =for stopwords OpenAPI
text =
text = You can also find me on the L and L, which are also great resources for finding help.
[GenerateSection / VERSION]
main_module_only = 0
text = I use a linearly-increasing version numbering scheme. No meaning should be
text = presumed or inferred from the version being less than 1.0.
[@Author::ETHER]
[=inc::AppendSection / AppendSupport]
header_re = ^SUPPORT$
match_anywhere = 1
action = append
[=inc::AppendSection / AppendVersion]
header_re = ^VERSION
match_anywhere = 1
action = append
pattern.t 100640 000766 000024 4044 15221771652 16200 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use utf8;
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
my $tests = sub ($char, $test_substr) {
cmp_result(
evaluate($char, { pattern => '[a-z]' }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/pattern',
error => 'pattern does not match',
},
],
},
$test_substr.' LATIN SMALL LETTER E WITH ACUTE does not match the ascii range [a-z]',
);
cmp_result(
evaluate($char, { pattern => '\w' }),
{
valid => true,
},
$test_substr.' LATIN SMALL LETTER E WITH ACUTE does match the "word" character class, because unicode semantics are used for matching',
);
};
my $letter = "é";
$tests->($letter, 'unchanged');
utf8::upgrade($letter);
$tests->($letter, 'upgraded');
utf8::downgrade($letter);
$tests->($letter, 'downgraded');
subtest 'empty pattern' => sub {
# create a "last successful match" in a containing scope
my $str = "furble" =~ s/fur/meow/r;
cmp_result(
evaluate('hello', { pattern => '' }),
{ valid => true },
'empty pattern in "pattern" will correctly match',
);
# create a new "last successful match"
$str = "furble" =~ s/fur/meow/r;
cmp_result(
evaluate(
{ alpha => 'hello' },
{
patternProperties => { '' => true },
additionalProperties => false,
},
),
{ valid => true },
'empty pattern in "patternProperties" will correctly match',
);
};
done_testing;
equality.t 100640 000766 000024 21276 15221771652 16406 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use utf8;
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use Scalar::Util qw(dualvar isdual);
use JSON::Schema::Tiny ();
use lib 't/lib';
use Helper;
sub is_type { goto &JSON::Schema::Tiny::is_type }
sub get_type { goto &JSON::Schema::Tiny::get_type }
sub is_equal { goto &JSON::Schema::Tiny::is_equal }
subtest 'equality, using inflated data' => sub {
foreach my $test (
[ undef, undef, true ],
[ undef, false, false, '', 'wrong type: null vs boolean' ],
[ undef, true , false, '', 'wrong type: null vs boolean' ],
[ undef, 1, false, '', 'wrong type: null vs integer' ],
[ undef, '1', false, '', 'wrong type: null vs string' ],
[ [qw(a b c)], [qw(a b c)], true ],
[ [qw(a b c)], [qw(a b)], false, '', 'element count differs: 3 vs 2' ],
[ [qw(a b)], [qw(b a)], false, '/0', 'strings not equal' ],
[ 1, 1, true ],
[ 1, 1.0, true ],
[ 1, '1.0', false, '', 'wrong type: integer vs string' ],
[ '1.1', 1.1, false, '', 'wrong type: string vs number' ],
[ '1', 1, false, '', 'wrong type: string vs integer' ],
[ '1.1', 1.1, false, '', 'wrong type: string vs number' ],
[ [1,2], [2,1], false, '/0', 'integers not equal' ],
[ { a => 1, b => 2 }, { b => 2, a => 1 }, true ],
[ { a => 1 }, { a => 1.0 }, true ],
[ [qw(école ಠ_ಠ)], ["\x{e9}cole", "\x{0ca0}_\x{0ca0}"], true ],
[ { a => 1, b => 2 }, { a => 1, b => 3 }, false, '/b', 'integers not equal' ],
[ { a => { b => 1, c => 2 }, d => { e => 3, f => 4 } },
{ a => { b => 1, c => 2 }, d => { e => 3, f => 5 } }, false, '/d/f', 'integers not equal' ],
[ [ { a => 1 } ], [ { a => 1, b => 2 } ], false, '/0', 'property count differs: 1 vs 2' ],
[ [ { a => 1 } ], [ { b => 2 } ], false, '/0', 'property names differ starting at position 0 ("a" vs "b")' ],
[ { foo => [ [ 0 ] ] }, { foo => [ [ 0, 1 ] ] }, false, '/foo/0', 'element count differs: 1 vs 2' ],
) {
my ($x, $y, $expected, $diff_path, $error) = @$test;
my @types = map get_type($_), $x, $y;
my $result = is_equal($x, $y, my $state = {});
ok(!($result xor $expected), json_sprintf('%s == %s is %s', $x, $y, $expected));
is($state->{path}, $diff_path // '', 'two instances differ at the expected place') if not $expected;
is($state->{error}, $error // '', 'error is correct') if not $expected;
is($state->{error}, undef, 'error is undefined') if $expected;
isnt($state->{error}, 'uh oh', 'no unexpected error encountered');
ok(is_type($types[0], $x), 'type of arg 0 was not mutated while making equality check');
ok(is_type($types[1], $y), 'type of arg 1 was not mutated while making equality check');
foreach my $idx (0, 1) {
ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & B::SVf_POK ), "arg $idx did not gain a POK")
if $types[$idx] eq 'integer' or $types[$idx] eq 'number';
ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & (B::SVf_IOK | B::SVf_NOK)), "arg $idx did not gain an NOK or IOK")
if $types[$idx] eq 'string';
}
note '';
}
};
my $decoder = (Mojo::JSON::JSON_XS ? 'Cpanel::JSON::XS' : 'JSON::PP')->new->allow_nonref(1)->utf8(0);
subtest 'equality, using JSON strings' => sub {
foreach my $test (
[ 'null', 'null', true ],
[ 'null', 1, false ],
[ '["a","b","c"]', '["a","b","c"]', true ],
[ '["a","b","c"]', '["a","b"]', false ],
[ '["a","b"]', '["b","a"]', false, '/0' ],
[ '1', '1', true ],
[ '1', '1.0', true ],
[ '10', '1e1', true ],
[ '[1,2]', '[2,1]', false, '/0' ],
[ '{"a":1,"b":2}', '{"a":1,"b":2}', true ],
[ '{"a":1}', '{"a":1.0}', true ],
[ '["école","ಠ_ಠ"]', qq{["\x{e9}cole", "\x{0ca0}_\x{0ca0}"]}, true ],
[ '{"a":1,"b":2}', '{"b":3,"a":1}', false, '/b' ],
[ '{"a":{"b":1,"c":2},"d":{"e":3,"f":4}}',
'{"a":{"b":1,"c":2},"d":{"e":3,"f":5}}', false, '/d/f' ],
) {
my ($x, $y, $expected, $diff_path) = @$test;
($x, $y) = map $decoder->decode($_), $x, $y;
my @types = map get_type($_), $x, $y;
my $result = is_equal($x, $y, my $state = {});
ok(!($result xor $expected), json_sprintf('%s == %s is %s', $x, $y, $expected));
is($state->{path}, $diff_path // '', 'two instances differ at the expected place') if not $expected;
isnt($state->{error}, 'uh oh', 'no unexpected error encountered');
ok(is_type($types[0], $x), 'type of arg 0 was not mutated while making equality check');
ok(is_type($types[1], $y), 'type of arg 1 was not mutated while making equality check');
foreach my $idx (0, 1) {
ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & B::SVf_POK ), "arg $idx did not gain a POK")
if $types[$idx] eq 'integer' or $types[$idx] eq 'number';
ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & (B::SVf_IOK | B::SVf_NOK)), "arg $idx did not gain an NOK or IOK")
if $types[$idx] eq 'string';
}
note '';
}
};
subtest 'equality, using scalarref booleans' => sub {
local $JSON::Schema::Tiny::SCALARREF_BOOLEANS = 1;
foreach my $test (
[ \0, true, false ],
[ \1, true, true ],
[ \0, false, true ],
[ \1, false, false ],
[ undef, \0, false ],
[ undef, false, false ],
) {
my ($x, $y, $expected, $diff_path) = @$test;
my @types = map get_type($_), $x, $y;
my $result = is_equal($x, $y, my $state = {});
ok(!($result xor $expected), json_sprintf('%s == %s is %s', $x, $y, $expected));
is($state->{path}, $diff_path // '', 'two instances differ at the expected place') if not $expected;
isnt($state->{error}, 'uh oh', 'no unexpected error encountered');
ok(is_type($types[0], $x), 'type of arg 0 was not mutated while making equality check');
ok(is_type($types[1], $y), 'type of arg 1 was not mutated while making equality check');
foreach my $idx (0, 1) {
ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & B::SVf_POK ), "arg $idx did not gain a POK")
if $types[$idx] eq 'integer' or $types[$idx] eq 'number';
ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & (B::SVf_IOK | B::SVf_NOK)), "arg $idx did not gain an NOK or IOK")
if $types[$idx] eq 'string';
}
note '';
}
};
subtest 'equality, using stringy numbers' => sub {
local $JSON::Schema::Tiny::STRINGY_NUMBERS = 1;
foreach my $test (
[ 1, 1, true ],
[ 1, 1.0, true ],
[ 1, '1.0', true ],
[ '1.1', 1.1, true ],
[ '1', 1, true ],
[ '1.1', 1.1, true ],
[ '1', '1.00', true ],
[ '1.10', '1.1000', true ],
[ 'x', 'x', true ],
[ 'x', 'y', false ],
[ 'x', 0, false ],
[ 0, 'y', false ],
[ '5', dualvar(5, '5'), true ],
[ 5, dualvar(5, '5'), true ],
[ '5', dualvar(5, 'five'), false ],
[ 5, dualvar(5, 'five'), false ],
[ dualvar(5, 'five'), dualvar(5, 'five'), false ],
) {
my ($x, $y, $expected, $diff_path) = @$test;
my @types = map get_type($_), $x, $y;
my $result = is_equal($x, $y, my $state = { stringy_numbers => 1 });
ok(!($result xor $expected), json_sprintf('%s == %s is %s', $x, $y, $expected));
is($state->{path}, $diff_path // '', 'two instances differ at the expected place') if not $expected;
isnt($state->{error}, 'uh oh', 'no unexpected error encountered');
isnt($state->{error}, 'uh oh', 'no unexpected error encountered');
is(get_type($x), $types[0], 'type of arg 0 was not mutated while making equality check (get_type returns '.$types[0].')');
is(get_type($y), $types[1], 'type of arg 1 was not mutated while making equality check (get_type returns '.$types[1].')');
ok(
is_type($types[0], $x),
"type of arg 0 was not mutated while making equality check (is_type('$types[0]') returns true)",
) if $types[0] ne 'ambiguous type';
ok(
is_type($types[1], $y),
"type of arg 1 was not mutated while making equality check (is_type('$types[1]') returns true)",
) if $types[1] ne 'ambiguous type';
foreach my $idx (0, 1) {
ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & B::SVf_POK ), "arg $idx did not gain a POK")
if $types[$idx] eq 'integer' or $types[$idx] eq 'number';
ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & (B::SVf_IOK | B::SVf_NOK)), "arg $idx did not gain an NOK or IOK")
if not ($idx == 1 and isdual($y) and $types[1] ne 'ambiguous type') and $types[$idx] eq 'string';
}
note '';
}
};
done_testing;
CONTRIBUTING 100644 000766 000024 10351 15221771652 15747 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034
CONTRIBUTING
Thank you for considering contributing to this distribution. This file
contains instructions that will help you work with the source code.
PLEASE NOTE that if you have any questions or difficulties, you can reach the
maintainer(s) through the bug queue described later in this document
(preferred), or by emailing the releaser directly. You are not required to
follow any of the steps in this document to submit a patch or bug report;
these are just recommendations, intended to help you (and help us help you
faster).
The distribution is managed with Dist::Zilla (https://metacpan.org/release/Dist-Zilla).
This means than many of the usual files you might expect are not in the
repository, but are generated at release time (e.g. Makefile.PL).
However, you can run tests directly using the 'prove' tool:
$ prove -l
$ prove -lv t/some_test_file.t
$ prove -lvr t/
In most cases, 'prove' is entirely sufficient for you to test any patches you
have.
You may need to satisfy some dependencies. The easiest way to satisfy
dependencies is to install the last release -- this is available at
https://metacpan.org/release/JSON-Schema-Tiny
If you use cpanminus, you can do it without downloading the tarball first:
$ cpanm --reinstall --installdeps --with-recommends JSON::Schema::Tiny
Dist::Zilla is a very powerful authoring tool, but requires a number of
author-specific plugins. If you would like to use it for contributing,
install it from CPAN, then run one of the following commands, depending on
your CPAN client:
$ cpan `dzil authordeps --missing`
or
$ dzil authordeps --missing | cpanm
You should then also install any additional requirements not needed by the
dzil build but may be needed by tests or other development:
$ cpan `dzil listdeps --author --missing`
or
$ dzil listdeps --author --missing | cpanm
Or, you can use the 'dzil stale' command to install all requirements at once:
$ cpan Dist::Zilla::App::Command::stale
$ cpan `dzil stale --all`
or
$ cpanm Dist::Zilla::App::Command::stale
$ dzil stale --all | cpanm
You can also do this via cpanm directly:
$ cpanm --reinstall --installdeps --with-develop --with-recommends JSON::Schema::Tiny
Once installed, here are some dzil commands you might try:
$ dzil build
$ dzil test
$ dzil test --release
$ dzil xtest
$ dzil listdeps --json
$ dzil build --notgz
You can learn more about Dist::Zilla at http://dzil.org/.
The code for this distribution is hosted at GitHub. The repository is:
https://github.com/karenetheridge/JSON-Schema-Tiny
You can submit code changes by forking the repository, pushing your code
changes to your clone, and then submitting a pull request. Please include a
suitable end-user-oriented entry in the Changes file describing your change.
Detailed instructions for doing that is available here:
https://help.github.com/articles/creating-a-pull-request
Generated files such as README, CONTRIBUTING, Makefile.PL, LICENSE etc should
*not* be included in your pull request, as they will be updated automatically
during the next release.
I will not accept AI-generated or AI-assisted contributions unless the nature
of the AI assistance is clearly identified with the pull request: submissions
should come from the owner's account, not a bot account, and be indistinguishable
from work done by that person. Multiple "drive-by" submissions, or contributions
that do not appear to have been human-reviewed, will be closed and may lead to
a ban of future contributions to any of my work.
If you have found a bug, but do not have an accompanying patch to fix it, you
can submit an issue report here:
https://github.com/karenetheridge/JSON-Schema-Tiny/issues
This is a good place to send your questions about the usage of this distribution.
If you send me a patch or pull request, your name and email address will be
included in the documentation as a contributor (using the attribution on the
commit or patch), unless you specifically request for it not to be. If you
wish to be listed under a different name or address, you should submit a pull
request to the .mailmap file to contain the correct mapping.
This file was generated via Dist::Zilla::Plugin::GenerateFile::FromShareDir 0.016
from a template file originating in Dist-Zilla-PluginBundle-Author-ETHER-0.172.
max_depth.t 100640 000766 000024 5041 15221771652 16472 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
for my $obj (0,1) {
local $JSON::Schema::Tiny::MAX_DEPTH = 6 if not $obj;
no warnings 'redefine';
local *evaluate = sub {
# using deprecated config name, to ensure it keeps working
JSON::Schema::Tiny->new(max_traversal_depth => 6)->evaluate(@_)
} if $obj;
cmp_result(
evaluate(
[ [ [ [ [ 1 ] ] ] ] ],
{
items => { '$ref' => '#' },
},
),
{
valid => false,
errors => [
{
instanceLocation => '/0/0/0/0',
keywordLocation => '/items/$ref/items/$ref/items/$ref/items',
absoluteKeywordLocation => '#/items',
error => 'EXCEPTION: maximum evaluation depth exceeded',
},
],
},
'evaluation is halted when traversal gets too deep',
);
cmp_result(
evaluate(
1,
{
'$defs' => {
loop_a => {
'$ref' => '#/$defs/loop_b',
},
loop_b => {
'$ref' => '#/$defs/loop_a',
},
},
'$ref' => '#/$defs/loop_a',
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/$ref/$ref/$ref',
absoluteKeywordLocation => '#/$defs/loop_a',
error => 'EXCEPTION: infinite loop detected (same location evaluated twice)',
},
],
},
'evaluation is halted when an instance location is evaluated against the same schema location a second time',
);
cmp_result(
evaluate(
{ foo => 1 },
{
'$defs' => {
int => { type => 'integer' },
},
anyOf => [
{ additionalProperties => { '$ref' => '#/$defs/int' } },
{ additionalProperties => { '$ref' => '#/$defs/int' } },
],
}
),
{ valid => true },
'the seen counter does not confuse two subschemas that both apply the same definition to the same instance location',
);
}
done_testing;
lib 000755 000766 000024 0 15221771652 14746 5 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t Helper.pm 100640 000766 000024 4356 15221771652 16667 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t/lib # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
# no package, so things defined here appear in the namespace of the parent.
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use Test2::V0 qw(!bag !bool !warnings), -no_pragmas => 1; # prefer Test::Deep's versions of these exports
use if $ENV{AUTHOR_TESTING}, 'Test2::Warnings';
use Test::Deep qw(!array !hash !blessed); # import symbols: ignore, re etc
use Test2::API 'context_do';
use JSON::PP ();
use Mojo::JSON;
use constant { true => JSON::PP::true, false => JSON::PP::false };
my $encoder = (Mojo::JSON::JSON_XS ? 'Cpanel::JSON::XS' : 'JSON::PP')->new
->allow_nonref(1)
->utf8(0)
->allow_bignum(1)
->allow_blessed(1)
->convert_blessed(1)
->canonical(1)
->pretty(1)
->indent_length(2);
# like sprintf, but all list items are JSON-encoded. assumes placeholders are %s!
sub json_sprintf {
sprintf(shift, map +(ref($_) =~ /^Math::Big(?:Int|Float)\z/ ? ref($_).'->new(\''.$_.'\')' : $encoder->indent(0)->encode($_)), @_);
}
# deep comparison, with Test::Deep syntax sugar
sub cmp_result ($got, $expected, $test_name) {
context_do {
my $ctx = shift;
my ($got, $expected, $test_name) = @_;
my ($equal, $stack) = Test::Deep::cmp_details($got, $expected);
if ($equal) {
$ctx->pass($test_name);
}
else {
$ctx->fail($test_name);
my $method =
# be less noisy for expected failures
(grep $_->{todo}, Test2::API::test2_stack->top->{_pre_filters}->@*) ? 'note'
: $ENV{AUTHOR_TESTING} || $ENV{AUTOMATED_TESTING} ? 'diag' : 'note';
$ctx->$method(Test::Deep::deep_diag($stack));
$ctx->$method("got result:\n".$encoder->encode($got));
}
return $equal;
} $got, $expected, $test_name;
}
sub is_passing () {
context_do { shift->hub->is_passing };
}
1;
author 000755 000766 000024 0 15221771652 15672 5 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt eol.t 100644 000766 000024 7234 15221771652 17004 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author use strict;
use warnings;
# this test was generated with Dist::Zilla::Plugin::Test::EOL 0.19
use Test::More 0.88;
use Test::EOL;
my @files = (
'lib/JSON/Schema/Tiny.pm',
't/00-report-prereqs.dd',
't/00-report-prereqs.t',
't/additional-tests-draft2019-09.t',
't/additional-tests-draft2019-09/README',
't/additional-tests-draft2019-09/anchor.json',
't/additional-tests-draft2019-09/badRef.json',
't/additional-tests-draft2019-09/faux-buggy-schemas.json',
't/additional-tests-draft2019-09/id.json',
't/additional-tests-draft2019-09/integers.json',
't/additional-tests-draft2019-09/invalid-input.json',
't/additional-tests-draft2019-09/keyword-independence.json',
't/additional-tests-draft2019-09/loose-types-const-enum.json',
't/additional-tests-draft2019-09/ref-and-id.json',
't/additional-tests-draft2019-09/ref.json',
't/additional-tests-draft2019-09/short-circuit.json',
't/additional-tests-draft2019-09/unknownKeyword.json',
't/additional-tests-draft2019-09/vocabulary.json',
't/additional-tests-draft2020-12.t',
't/additional-tests-draft2020-12/README',
't/additional-tests-draft2020-12/anchor.json',
't/additional-tests-draft2020-12/badRef.json',
't/additional-tests-draft2020-12/faux-buggy-schemas.json',
't/additional-tests-draft2020-12/id.json',
't/additional-tests-draft2020-12/integers.json',
't/additional-tests-draft2020-12/invalid-input.json',
't/additional-tests-draft2020-12/keyword-independence.json',
't/additional-tests-draft2020-12/loose-types-const-enum.json',
't/additional-tests-draft2020-12/ref.json',
't/additional-tests-draft2020-12/short-circuit.json',
't/additional-tests-draft2020-12/unknownKeyword.json',
't/additional-tests-draft2020-12/vocabulary.json',
't/additional-tests-draft7.t',
't/additional-tests-draft7/README',
't/additional-tests-draft7/anchor.json',
't/additional-tests-draft7/badRef.json',
't/additional-tests-draft7/faux-buggy-schemas.json',
't/additional-tests-draft7/id.json',
't/additional-tests-draft7/integers.json',
't/additional-tests-draft7/invalid-input.json',
't/additional-tests-draft7/keyword-independence.json',
't/additional-tests-draft7/loose-types-const-enum.json',
't/additional-tests-draft7/ref-and-id.json',
't/additional-tests-draft7/ref.json',
't/additional-tests-draft7/short-circuit.json',
't/additional-tests-draft7/unknownKeyword.json',
't/additional-tests-draft7/vocabulary.json',
't/boolean-data.t',
't/boolean-schemas.t',
't/equality.t',
't/errors.t',
't/lib/Acceptance.pm',
't/lib/Helper.pm',
't/max_depth.t',
't/pattern.t',
't/ref.t',
't/results/draft2019-09-acceptance.txt',
't/results/draft2019-09-additional-tests.txt',
't/results/draft2020-12-acceptance.txt',
't/results/draft2020-12-additional-tests.txt',
't/results/draft7-acceptance.txt',
't/results/draft7-additional-tests.txt',
't/specification_version.t',
't/stringy-numbers.t',
't/type.t',
't/unsupported-keywords.t',
't/zzz-acceptance-draft2019-09.t',
't/zzz-acceptance-draft2020-12.t',
't/zzz-acceptance-draft7.t',
'xt/author/00-compile.t',
'xt/author/clean-namespaces.t',
'xt/author/distmeta.t',
'xt/author/eol.t',
'xt/author/kwalitee.t',
'xt/author/minimum-version.t',
'xt/author/mojibake.t',
'xt/author/no-tabs.t',
'xt/author/pod-coverage.t',
'xt/author/pod-spell.t',
'xt/author/pod-syntax.t',
'xt/author/portability.t',
'xt/release/changes_has_content.t',
'xt/release/cpan-changes.t'
);
eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files;
done_testing;
boolean-data.t 100640 000766 000024 11536 15221771652 17075 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use Data::Dumper;
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
sub serialize { Data::Dumper->new([ $_[0] ])->Indent(0)->Terse(1)->Sortkeys(1)->Dump }
my ($test_schema, $failure_result);
subtest 'strict booleans (default)' => sub {
cmp_result(
evaluate($_, { type => 'boolean' }),
{ valid => true },
'in data, '.serialize($_).' is a boolean',
)
foreach (
false,
true,
);
cmp_result(
evaluate($_->[1], { type => 'boolean' }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/type',
error => 'got '.$_->[0].', not boolean',
},
],
},
'correct error generated from type for '.serialize($_->[1]),
)
foreach (
[ null => undef ],
[ integer => 0 ],
[ integer => 1 ],
[ string => '0' ],
[ string => '1' ],
[ string => 'false' ],
[ string => 'true' ],
[ 'reference to SCALAR' => \0 ],
[ 'reference to SCALAR' => \1 ],
);
cmp_result(
evaluate(
$_->[1],
$test_schema = {
allOf => [ { type => 'boolean' }, { type => ['boolean','object'] } ],
anyOf => [ { const => false }, { const => true } ],
enum => [ false, true ],
}
),
$failure_result = {
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/0/type',
error => 'got '.$_->[0].', not boolean',
},
{
instanceLocation => '',
keywordLocation => '/allOf/1/type',
error => 'got '.$_->[0].', not one of boolean, object',
},
{
instanceLocation => '',
keywordLocation => '/allOf',
error => 'subschemas 0, 1 are not valid',
},
{
instanceLocation => '',
keywordLocation => '/anyOf/0/const',
error => 'value does not match',
},
{
instanceLocation => '',
keywordLocation => '/anyOf/1/const',
error => 'value does not match',
},
{
instanceLocation => '',
keywordLocation => '/anyOf',
error => 'no subschemas are valid',
},
{
instanceLocation => '',
keywordLocation => '/enum',
error => 'value does not match',
},
],
},
'in data, '.serialize($_->[1]).' not is a boolean',
)
foreach (
[ null => undef ],
[ integer => 0 ],
[ integer => 1 ],
[ string => '0' ],
[ string => '1' ],
[ string => 'false' ],
[ string => 'true' ],
[ 'reference to SCALAR' => \0 ],
[ 'reference to SCALAR' => \1 ],
);
};
subtest '$SCALARREF_BOOLEANS = 1' => sub {
local $JSON::Schema::Tiny::SCALARREF_BOOLEANS = 1;
cmp_result(
evaluate($_, $test_schema),
{ valid => true },
'in data, '.serialize($_).' is a boolean',
)
foreach (
false,
true,
\0,
\1,
);
cmp_result(
evaluate($_->[1], $test_schema),
{
valid => false,
errors => [
do {
my ($type, $value) = $_->@*;
map +{
$_->%*,
$_->{keywordLocation} =~ /\/type\z/ ? (error => $_->{error} =~ s/^got .*, not/got $type, not/r) : (),
}, $failure_result->{errors}->@*,
},
],
},
'correct error generated from type for '.serialize($_),
)
foreach (
[ null => undef ],
[ integer => 0 ],
[ integer => 1 ],
[ string => '0' ],
[ string => '1' ],
[ string => 'false' ],
[ string => 'true' ],
);
cmp_result(
evaluate(
[
undef,
0,
1,
'0',
'1',
'false',
'true',
\0,
\1,
],
{ uniqueItems => true },
),
{ valid => true },
'items are all considered unique when types differ, even when perl treats them similarly',
);
cmp_result(
evaluate($_, { uniqueItems => true }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/uniqueItems',
error => 'items at indices 0 and 1 are not unique',
},
],
},
'scalarrefs compare as identical to their counterpart booleans',
)
foreach (
[ \0, false ],
[ false, \0 ],
[ \1, true ],
[ true, \1 ],
);
};
done_testing;
boolean-schemas.t 100640 000766 000024 4516 15221771652 17567 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
my @tests = (
{ schema => false, valid => false },
{ schema => true, valid => true },
{ schema => {}, valid => true },
{ schema => 0, valid => false },
{ schema => 1, valid => false },
{ schema => \0, valid => false },
{ schema => \1, valid => false },
);
BOOLEAN_TESTS:
note '$SCALARREF_BOOLEANS = '.(0+!!$JSON::Schema::Tiny::SCALARREF_BOOLEANS);
foreach my $test (@tests) {
my $data = 'hello';
ok(
lives {
my $result = evaluate($data, $test->{schema});
cmp_result(
$result,
{
valid => $test->{valid},
$test->{valid} ? () : (errors => supersetof()),
},
'invalid result structure looks correct',
);
local $JSON::Schema::Tiny::BOOLEAN_RESULT = 1;
my $bool_result = evaluate($data, $test->{schema});
ok(!($bool_result xor $test->{valid}), json_sprintf('schema: %s evaluates to: %s', $test->{schema}, $test->{valid}));
},
'no exceptions in evaluate',
);
}
if (not $JSON::Schema::Tiny::SCALARREF_BOOLEANS) {
# schemas still do not accept mojo booleans
$JSON::Schema::Tiny::SCALARREF_BOOLEANS = 1;
goto BOOLEAN_TESTS;
}
cmp_result(
evaluate('hello', []),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '',
error => 'invalid schema type: array',
},
],
},
'invalid schema type results in error',
);
cmp_result(
evaluate('hello', \0),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '',
error => 'invalid schema type: reference to SCALAR',
},
],
},
'scalarref for schema results in error, even when $SCALARREF_BOOLEANS is true',
);
done_testing;
Acceptance.pm 100640 000766 000024 6773 15221771652 17503 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t/lib # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
# no package, so things defined here appear in the namespace of the parent.
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use lib 't/lib';
use Helper;
use Mojo::File 'path';
use if $ENV{AUTHOR_TESTING}, 'Test2::Warnings' => ':fail_on_warning'; # hooks into done_testing unless overridden
use Test::JSON::Schema::Acceptance 1.026;
use JSON::Schema::Tiny;
BEGIN {
foreach my $env (qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING NO_TODO TEST_DIR NO_SHORT_CIRCUIT)) {
note $env.': '.($ENV{$env}//'');
}
note '';
}
sub acceptance_tests (%options) {
local $Test::Builder::Level = $Test::Builder::Level + 1;
my $accepter = Test::JSON::Schema::Acceptance->new(
include_optional => 1,
verbose => 1,
test_schemas => 0,
$options{acceptance}->%*,
$ENV{TEST_DIR} ? (test_dir => $ENV{TEST_DIR})
: $ENV{TEST_PREFIXDIR} ? (test_dir => path($ENV{TEST_PREFIXDIR}, 'tests', $options{acceptance}{specification})) : (),
);
my $jst = JSON::Schema::Tiny->new($options{evaluator}->%*);
my $jst_short_circuit = $ENV{NO_SHORT_CIRCUIT} || JSON::Schema::Tiny->new($options{evaluator}->%*, short_circuit => 1);
my $encoder = (Mojo::JSON::JSON_XS ? 'Cpanel::JSON::XS' : 'JSON::PP')->new->allow_nonref(1)->utf8(0)->canonical(1)->pretty(1);
$encoder->indent_length(2) if $encoder->can('indent_length');
$accepter->acceptance(
validate_data => sub ($schema, $instance_data) {
my $result = $jst->evaluate($instance_data, $schema);
my $result_short = $ENV{NO_SHORT_CIRCUIT} || $jst_short_circuit->evaluate($instance_data, $schema);
note 'result: ', $encoder->encode($result);
note 'short-circuited result: ', ($encoder->encode($result_short) ? 'true' : 'false')
if not $ENV{NO_SHORT_CIRCUIT} and ($result->{valid} xor $result_short->{valid});
die 'results inconsistent between short_circuit = false and true'
if not $ENV{NO_SHORT_CIRCUIT} and ($result->{valid} xor $result_short->{valid});
my $in_todo;
# if any errors contain an exception, generate a warning so we can be sure
# to count that as a failure (an exception would be caught and perhaps TODO'd).
# (This might change if tests are added that are expected to produce exceptions.)
foreach my $r ($result, ($ENV{NO_SHORT_CIRCUIT} ? () : $result_short)) {
print STDERR 'evaluation generated an exception: '.$encoder->encode($_)
foreach
grep +($_->{error} =~ /^EXCEPTION/
&& $_->{error} !~ /but short_circuit is enabled/ # unevaluated*
&& $_->{error} !~ /(max|min)imum value is not a number\z/) # optional/bignum.json
&& !($in_todo //= grep $_->{todo}, Test2::API::test2_stack->top->{_pre_filters}->@*),
($r->{errors}//[])->@*;
}
$result->{valid};
},
@ARGV ? (tests => { file => \@ARGV }) : (),
($options{test} // {})->%*,
);
path('t/results/'.$options{output_file})->spew($accepter->results_text, 'UTF-8')
if $ENV{AUTHOR_TESTING};
}
1;
stringy-numbers.t 100640 000766 000024 13000 15221771652 17703 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
foreach my $config (0, 1) {
$JSON::Schema::Tiny::STRINGY_NUMBERS = $config;
note '$STRINGY_NUMBERS = '.$config;
cmp_result(
evaluate(1, { $_ => '1' }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/'.$_,
error => $_.' value is not a number',
}
],
},
'strings cannot be used in place of numbers in schema for '.$_,
) foreach qw(multipleOf maximum exclusiveMaximum minimum exclusiveMinimum);
my $schema = {
allOf => [
{ type => 'string' },
{ type => 'number' },
{ type => 'integer' },
{ type => [ 'object', 'number' ] },
{ type => [ 'object', 'integer' ] },
],
};
cmp_result(
evaluate('1.1', $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/1/type',
error => 'got string, not number',
},
{
instanceLocation => '',
keywordLocation => '/allOf/2/type',
error => 'got string, not integer',
},
{
instanceLocation => '',
keywordLocation => '/allOf/3/type',
error => 'got string, not one of object, number',
},
{
instanceLocation => '',
keywordLocation => '/allOf/4/type',
error => 'got string, not one of object, integer',
},
{
instanceLocation => '',
keywordLocation => '/allOf',
error => 'subschemas 1, 2, 3, 4 are not valid',
},
],
},
'by default "type": "string" does not accept numbers',
) if not $config;
cmp_result(
evaluate('1.1', $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/2/type',
error => 'got string, not integer',
},
{
instanceLocation => '',
keywordLocation => '/allOf/4/type',
error => 'got string, not one of object, integer',
},
{
instanceLocation => '',
keywordLocation => '/allOf',
error => 'subschemas 2, 4 are not valid',
},
],
},
'using stringy numbers, numeric strings are treated as numbers but are still not always integers',
) if $config;
$schema = {
maximum => 5,
exclusiveMaximum => 5,
minimum => 15,
exclusiveMinimum => 15,
allOf => [
{ multipleOf => 2 },
{ multipleOf => 0.3 },
],
};
my $errors = [
{
instanceLocation => '',
keywordLocation => '/allOf/0/multipleOf',
error => 'value is not a multiple of 2',
},
{
instanceLocation => '',
keywordLocation => '/allOf/1/multipleOf',
error => 'value is not a multiple of 0.3',
},
{
instanceLocation => '',
keywordLocation => '/allOf',
error => 'subschemas 0, 1 are not valid',
},
{
instanceLocation => '',
keywordLocation => '/maximum',
error => 'value is larger than 5',
},
{
instanceLocation => '',
keywordLocation => '/exclusiveMaximum',
error => 'value is equal to or larger than 5',
},
{
instanceLocation => '',
keywordLocation => '/minimum',
error => 'value is smaller than 15',
},
{
instanceLocation => '',
keywordLocation => '/exclusiveMinimum',
error => 'value is equal to or smaller than 15',
},
];
my $data = 11e0;
cmp_result(
evaluate($data, $schema),
{
valid => false,
errors => $errors,
},
'real numbers are always evaluated',
);
$data = '11e0';
cmp_result(
evaluate($data, $schema),
{ valid => true },
'by default, stringy numbers are not evaluated by numeric keywords',
) if $config == 0;
cmp_result(
evaluate($data, $schema),
{
valid => false,
errors => $errors,
},
'with the config enabled, stringy numbers are treated as numbers by numeric keywords',
) if $config == 1;
is(JSON::Schema::Tiny::get_type($data), 'string', 'data was not mutated');
$schema = {
enum => [11, 12],
const => 11,
};
cmp_result(
evaluate($data, $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/enum',
error => 'value does not match',
},
{
instanceLocation => '',
keywordLocation => '/const',
error => 'value does not match',
},
],
},
'by default, stringy numbers are not the same as numbers using comparison keywords',
) if $config == 0;
cmp_result(
evaluate($data, $schema),
{ valid => true },
'with the config enabled, stringy numbers are the same as numbers using comparison keywords',
) if $config == 1;
is(JSON::Schema::Tiny::get_type($data), 'string', 'data was not mutated');
}
done_testing;
no-tabs.t 100644 000766 000024 7202 15221771652 17563 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author use strict;
use warnings;
# this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.15
use Test::More 0.88;
use Test::NoTabs;
my @files = (
'lib/JSON/Schema/Tiny.pm',
't/00-report-prereqs.dd',
't/00-report-prereqs.t',
't/additional-tests-draft2019-09.t',
't/additional-tests-draft2019-09/README',
't/additional-tests-draft2019-09/anchor.json',
't/additional-tests-draft2019-09/badRef.json',
't/additional-tests-draft2019-09/faux-buggy-schemas.json',
't/additional-tests-draft2019-09/id.json',
't/additional-tests-draft2019-09/integers.json',
't/additional-tests-draft2019-09/invalid-input.json',
't/additional-tests-draft2019-09/keyword-independence.json',
't/additional-tests-draft2019-09/loose-types-const-enum.json',
't/additional-tests-draft2019-09/ref-and-id.json',
't/additional-tests-draft2019-09/ref.json',
't/additional-tests-draft2019-09/short-circuit.json',
't/additional-tests-draft2019-09/unknownKeyword.json',
't/additional-tests-draft2019-09/vocabulary.json',
't/additional-tests-draft2020-12.t',
't/additional-tests-draft2020-12/README',
't/additional-tests-draft2020-12/anchor.json',
't/additional-tests-draft2020-12/badRef.json',
't/additional-tests-draft2020-12/faux-buggy-schemas.json',
't/additional-tests-draft2020-12/id.json',
't/additional-tests-draft2020-12/integers.json',
't/additional-tests-draft2020-12/invalid-input.json',
't/additional-tests-draft2020-12/keyword-independence.json',
't/additional-tests-draft2020-12/loose-types-const-enum.json',
't/additional-tests-draft2020-12/ref.json',
't/additional-tests-draft2020-12/short-circuit.json',
't/additional-tests-draft2020-12/unknownKeyword.json',
't/additional-tests-draft2020-12/vocabulary.json',
't/additional-tests-draft7.t',
't/additional-tests-draft7/README',
't/additional-tests-draft7/anchor.json',
't/additional-tests-draft7/badRef.json',
't/additional-tests-draft7/faux-buggy-schemas.json',
't/additional-tests-draft7/id.json',
't/additional-tests-draft7/integers.json',
't/additional-tests-draft7/invalid-input.json',
't/additional-tests-draft7/keyword-independence.json',
't/additional-tests-draft7/loose-types-const-enum.json',
't/additional-tests-draft7/ref-and-id.json',
't/additional-tests-draft7/ref.json',
't/additional-tests-draft7/short-circuit.json',
't/additional-tests-draft7/unknownKeyword.json',
't/additional-tests-draft7/vocabulary.json',
't/boolean-data.t',
't/boolean-schemas.t',
't/equality.t',
't/errors.t',
't/lib/Acceptance.pm',
't/lib/Helper.pm',
't/max_depth.t',
't/pattern.t',
't/ref.t',
't/results/draft2019-09-acceptance.txt',
't/results/draft2019-09-additional-tests.txt',
't/results/draft2020-12-acceptance.txt',
't/results/draft2020-12-additional-tests.txt',
't/results/draft7-acceptance.txt',
't/results/draft7-additional-tests.txt',
't/specification_version.t',
't/stringy-numbers.t',
't/type.t',
't/unsupported-keywords.t',
't/zzz-acceptance-draft2019-09.t',
't/zzz-acceptance-draft2020-12.t',
't/zzz-acceptance-draft7.t',
'xt/author/00-compile.t',
'xt/author/clean-namespaces.t',
'xt/author/distmeta.t',
'xt/author/eol.t',
'xt/author/kwalitee.t',
'xt/author/minimum-version.t',
'xt/author/mojibake.t',
'xt/author/no-tabs.t',
'xt/author/pod-coverage.t',
'xt/author/pod-spell.t',
'xt/author/pod-syntax.t',
'xt/author/portability.t',
'xt/release/changes_has_content.t',
'xt/release/cpan-changes.t'
);
notabs_ok($_) foreach @files;
done_testing;
inc 000755 000766 000024 0 15221771652 14506 5 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034 AppendSection.pm 100640 000766 000024 2556 15221771652 17744 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/inc use strict;
use warnings;
package inc::AppendSection;
use Moose;
extends 'Pod::Weaver::Section::AllowOverride';
# Warning: dirty hack ahead!
# This chicanery is to allow for [GenerateSection] followed by [AllowOverride],
# where the intention is the content added by that GenerateSection should be appended to the
# original section that was generated by a weaver bundle (as opposed to appearing in the literal
# .pm file).
# This plugin does its initial work (to find the node to pluck out and later append) in
# transform_document, and the Transformer phase is run before GenerateSection's weave_section
# has a chance to create the node.
# So this hack just runs the plugin's transform_document again if nothing was found the first time.
# Because it picks the first node it finds (to relocate to the location of the second match), we
# will now run GenerateSection first, before calling the weaver bundle.
# All of this really should be replaced by a new plugin called something like AppendSection,
# which subclasses GenerateSection to add the options provided by AllowOverride, letting us
# append (or prepend) to an existing section rather than generating a new one.
before weave_section => sub {
my ($self, $document, $input) = @_;
# if we haven't already found a matching section, look again now
$self->transform_document($document) if not $self->_override_with;
};
1;
distmeta.t 100644 000766 000024 222 15221771652 20005 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author #!perl
# This file was automatically generated by Dist::Zilla::Plugin::MetaTests
use strict;
use warnings;
use Test::CPAN::Meta;
meta_yaml_ok();
kwalitee.t 100644 000766 000024 275 15221771652 20010 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author # this test was generated with Dist::Zilla::Plugin::Test::Kwalitee 2.13
use strict;
use warnings;
use Test::More 0.88;
use Test::Kwalitee 1.21 'kwalitee_ok';
kwalitee_ok();
done_testing;
mojibake.t 100644 000766 000024 151 15221771652 17755 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author #!perl
use strict;
use warnings qw(all);
use Test::More;
use Test::Mojibake;
all_files_encoding_ok();
CheckConflicts.pm 100640 000766 000024 2010 15221771652 20053 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/inc # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
package inc::CheckConflicts;
use stable 0.031 'postderef';
use Moose;
with 'Dist::Zilla::Role::InstallTool';
sub setup_installer {
my $self = shift;
my @mfpl = grep +($_->name eq 'Makefile.PL' or $_->name eq 'Build.PL'), $self->zilla->files->@*;
$self->log_fatal('No Makefile.PL or Build.PL was found.') if not @mfpl;
foreach my $file (@mfpl) {
$self->log_debug([ 'munging %s in setup_installer phase', $file->name ]);
my $orig_content = $file->content;
$self->log_fatal('could not find position in ' . $file->name . ' to modify!')
if not $orig_content =~ m/use strict;\nuse warnings;\n\n/g;
my $pos = pos($orig_content);
my $content = <<'CONTENT';
if ("$]" < 5.038) {
die "This distribution will not install where builtin::Backport exists.\n"
if eval { +require builtin::Backport; 1 };
}
CONTENT
$file->content(substr($orig_content, 0, $pos) . $content . substr($orig_content, $pos));
}
return;
}
1;
pod-spell.t 100644 000766 000024 463 15221771652 20101 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author use strict;
use warnings;
use Test::More;
# generated by Dist::Zilla::Plugin::Test::PodSpelling 2.007006
use Test::Spelling 0.17;
use Pod::Wordlist;
add_stopwords();
all_pod_files_spelling_ok( qw( examples lib script t xt ) );
__DATA__
Etheridge
JSON
Karen
Matt
Schema
Tiny
Trout
ether
irc
lib
mst
00-report-prereqs.t 100644 000766 000024 14127 15221771652 17761 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t #!perl
use strict;
use warnings;
# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.029
use Test::More tests => 1;
use Module::Metadata;
use File::Spec;
# from $version::LAX
my $lax_version_re =
qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )?
|
(?:\.[0-9]+) (?:_[0-9]+)?
) | (?:
v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )?
|
(?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)?
)
)/x;
# hide optional CPAN::Meta modules from prereq scanner
# and check if they are available
my $cpan_meta = "CPAN::Meta";
my $cpan_meta_pre = "CPAN::Meta::Prereqs";
my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic
# Verify requirements?
my $DO_VERIFY_PREREQS = 1;
sub _max {
my $max = shift;
$max = ( $_ > $max ) ? $_ : $max for @_;
return $max;
}
sub _merge_prereqs {
my ($collector, $prereqs) = @_;
# CPAN::Meta::Prereqs object
if (ref $collector eq $cpan_meta_pre) {
return $collector->with_merged_prereqs(
CPAN::Meta::Prereqs->new( $prereqs )
);
}
# Raw hashrefs
for my $phase ( keys %$prereqs ) {
for my $type ( keys %{ $prereqs->{$phase} } ) {
for my $module ( keys %{ $prereqs->{$phase}{$type} } ) {
$collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module};
}
}
}
return $collector;
}
my @include = qw(
Encode
File::Temp
JSON::PP
Module::Runtime
Sub::Name
YAML::PP
YAML::XS
autodie
JSON::PP
Cpanel::JSON::XS
JSON::XS
Math::BigInt
Mojolicious
builtin
builtin::Backport
);
my @exclude = qw(
);
# Add static prereqs to the included modules list
my $static_prereqs = do './t/00-report-prereqs.dd';
# Merge all prereqs (either with ::Prereqs or a hashref)
my $full_prereqs = _merge_prereqs(
( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ),
$static_prereqs
);
# Add dynamic prereqs to the included modules list (if we can)
my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
my $cpan_meta_error;
if ( $source && $HAS_CPAN_META
&& (my $meta = eval { CPAN::Meta->load_file($source) } )
) {
$full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs);
}
else {
$cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source)
$source = 'static metadata';
}
my @full_reports;
my @dep_errors;
my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs;
# Add static includes into a fake section
for my $mod (@include) {
$req_hash->{other}{modules}{$mod} = 0;
}
for my $phase ( qw(configure build test runtime develop other) ) {
next unless $req_hash->{$phase};
next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING});
for my $type ( qw(requires recommends suggests conflicts modules) ) {
next unless $req_hash->{$phase}{$type};
my $title = ucfirst($phase).' '.ucfirst($type);
my @reports = [qw/Module Want Have/];
for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) {
next if grep { $_ eq $mod } @exclude;
my $want = $req_hash->{$phase}{$type}{$mod};
$want = "undef" unless defined $want;
$want = "any" if !$want && $want == 0;
if ($mod eq 'perl') {
push @reports, ['perl', $want, $]];
next;
}
my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required";
my $file = $mod;
$file =~ s{::}{/}g;
$file .= ".pm";
my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC;
if ($prefix) {
my $have = Module::Metadata->new_from_file( File::Spec->catfile($prefix, $file) )->version;
$have = "undef" unless defined $have;
push @reports, [$mod, $want, $have];
if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) {
if ( $have !~ /\A$lax_version_re\z/ ) {
push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)";
}
elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) {
push @dep_errors, "$mod version '$have' is not in required range '$want'";
}
}
}
else {
push @reports, [$mod, $want, "missing"];
if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) {
push @dep_errors, "$mod is not installed ($req_string)";
}
}
}
if ( @reports ) {
push @full_reports, "=== $title ===\n\n";
my $ml = _max( map { length $_->[0] } @reports );
my $wl = _max( map { length $_->[1] } @reports );
my $hl = _max( map { length $_->[2] } @reports );
if ($type eq 'modules') {
splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl];
push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports;
}
else {
splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl];
push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports;
}
push @full_reports, "\n";
}
}
}
if ( @full_reports ) {
diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports;
}
if ( $cpan_meta_error || @dep_errors ) {
diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n";
}
if ( $cpan_meta_error ) {
my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n";
}
if ( @dep_errors ) {
diag join("\n",
"\nThe following REQUIRED prerequisites were not satisfied:\n",
@dep_errors,
"\n"
);
}
pass('Reported prereqs');
# vim: ts=4 sts=4 sw=4 et:
00-compile.t 100644 000766 000024 2526 15221771652 20071 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author use strict;
use warnings;
# this test was generated with Dist::Zilla::Plugin::Test::Compile 2.059
use Test::More 0.94;
plan tests => 2;
my @module_files = (
'JSON/Schema/Tiny.pm'
);
# no fake home requested
my @switches = (
-d 'blib' ? '-Mblib' : '-Ilib',
);
use File::Spec;
use IPC::Open3;
use IO::Handle;
open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!";
my @warnings;
for my $lib (@module_files)
{
# see L
my $stderr = IO::Handle->new;
diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'}.$str.q{'} }
$^X, @switches, '-e', "require q[$lib]"))
if $ENV{PERL_COMPILE_TEST_DEBUG};
my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]");
binmode $stderr, ':crlf' if $^O eq 'MSWin32';
my @_warnings = <$stderr>;
waitpid($pid, 0);
is($?, 0, "$lib loaded ok");
shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/
and not eval { +require blib; blib->VERSION('1.01') };
if (@_warnings)
{
warn @_warnings;
push @warnings, @_warnings;
}
}
is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', explain(\@warnings);
BAIL_OUT("Compilation problems") if !Test::More->builder->is_passing;
pod-syntax.t 100644 000766 000024 251 15221771652 20303 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author #!perl
# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests
use strict; use warnings;
use Test::More;
use Test::Pod 1.41;
all_pod_files_ok();
00-report-prereqs.dd 100644 000766 000024 23237 15221771652 20107 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t do { my $x = {
'configure' => {
'requires' => {
'Module::Build::Tiny' => '0.034',
'perl' => '5.020'
}
},
'develop' => {
'recommends' => {
'Dist::Zilla::PluginBundle::Author::ETHER' => '0.172',
'Dist::Zilla::PluginBundle::Git::VersionManager' => '0.007'
},
'requires' => {
'Pod::Wordlist' => '0',
'Test2::Warnings' => '0',
'Test::CPAN::Changes' => '0.19',
'Test::CPAN::Meta' => '0',
'Test::CleanNamespaces' => '0.15',
'Test::EOL' => '0',
'Test::Kwalitee' => '1.21',
'Test::MinimumVersion' => '0',
'Test::Mojibake' => '0',
'Test::NoTabs' => '0',
'Test::Pod' => '1.41',
'Test::Pod::Coverage::TrustMe' => '0.002001',
'Test::Portability::Files' => '0',
'Test::Spelling' => '0.17',
'autovivification' => '0',
'strictures' => '2'
}
},
'runtime' => {
'requires' => {
'Feature::Compat::Try' => '0',
'Mojolicious' => '7.23',
'autovivification' => '0',
'builtin::compat' => '0.003003',
'experimental' => '0.026',
'if' => '0',
'perl' => '5.020',
'stable' => '0.031'
},
'suggests' => {
'Cpanel::JSON::XS' => '0'
}
},
'test' => {
'recommends' => {
'CPAN::Meta' => '2.120900'
},
'requires' => {
'List::Util' => '1.50',
'Test2::V0' => '0',
'Test2::Warnings' => '0',
'Test::Deep' => '0',
'Test::JSON::Schema::Acceptance' => '1.026',
'perl' => '5.020'
}
},
'x_Dist_Zilla' => {
'requires' => {
'Dist::Zilla' => '5',
'Dist::Zilla::Plugin::Authority' => '1.009',
'Dist::Zilla::Plugin::AutoMetaResources' => '0',
'Dist::Zilla::Plugin::AutoPrereqs' => '5.038',
'Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional' => '0.004',
'Dist::Zilla::Plugin::CheckIssues' => '0',
'Dist::Zilla::Plugin::CheckMetaResources' => '0',
'Dist::Zilla::Plugin::CheckPrereqsIndexed' => '0.019',
'Dist::Zilla::Plugin::CheckSelfDependency' => '0',
'Dist::Zilla::Plugin::CheckStrictVersion' => '0',
'Dist::Zilla::Plugin::ConfirmRelease' => '0',
'Dist::Zilla::Plugin::CopyFilesFromRelease' => '0',
'Dist::Zilla::Plugin::EnsureLatestPerl' => '0',
'Dist::Zilla::Plugin::FileFinder::ByName' => '0',
'Dist::Zilla::Plugin::GenerateFile::FromShareDir' => '0',
'Dist::Zilla::Plugin::Git::Check' => '0',
'Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch' => '0.004',
'Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts' => '0',
'Dist::Zilla::Plugin::Git::Commit' => '2.020',
'Dist::Zilla::Plugin::Git::Contributors' => '0.029',
'Dist::Zilla::Plugin::Git::Describe' => '0.004',
'Dist::Zilla::Plugin::Git::GatherDir' => '2.016',
'Dist::Zilla::Plugin::Git::Push' => '0',
'Dist::Zilla::Plugin::Git::Remote::Check' => '0',
'Dist::Zilla::Plugin::Git::Tag' => '0',
'Dist::Zilla::Plugin::GitHub::Update' => '0.40',
'Dist::Zilla::Plugin::GithubMeta' => '0.54',
'Dist::Zilla::Plugin::InstallGuide' => '1.200005',
'Dist::Zilla::Plugin::Keywords' => '0.004',
'Dist::Zilla::Plugin::License' => '5.038',
'Dist::Zilla::Plugin::Manifest' => '0',
'Dist::Zilla::Plugin::MetaConfig' => '0',
'Dist::Zilla::Plugin::MetaJSON' => '0',
'Dist::Zilla::Plugin::MetaNoIndex' => '0',
'Dist::Zilla::Plugin::MetaProvides::Package' => '1.15000002',
'Dist::Zilla::Plugin::MetaTests' => '0',
'Dist::Zilla::Plugin::MetaYAML' => '0',
'Dist::Zilla::Plugin::MinimumPerl' => '1.006',
'Dist::Zilla::Plugin::ModuleBuildTiny' => '0.012',
'Dist::Zilla::Plugin::MojibakeTests' => '0.8',
'Dist::Zilla::Plugin::NextRelease' => '5.033',
'Dist::Zilla::Plugin::PodSyntaxTests' => '5.040',
'Dist::Zilla::Plugin::PodWeaver' => '4.008',
'Dist::Zilla::Plugin::Prereqs' => '0',
'Dist::Zilla::Plugin::Prereqs::AuthorDeps' => '0.006',
'Dist::Zilla::Plugin::PromptIfStale' => '0',
'Dist::Zilla::Plugin::Readme' => '0',
'Dist::Zilla::Plugin::ReadmeAnyFromPod' => '0.142180',
'Dist::Zilla::Plugin::RemovePrereqs' => '0',
'Dist::Zilla::Plugin::RewriteVersion::Transitional' => '0.006',
'Dist::Zilla::Plugin::Run::AfterBuild' => '0.041',
'Dist::Zilla::Plugin::Run::AfterRelease' => '0.038',
'Dist::Zilla::Plugin::RunExtraTests' => '0.024',
'Dist::Zilla::Plugin::StaticInstall' => '0.005',
'Dist::Zilla::Plugin::Test::CPAN::Changes' => '0.012',
'Dist::Zilla::Plugin::Test::ChangesHasContent' => '0',
'Dist::Zilla::Plugin::Test::CleanNamespaces' => '0.006',
'Dist::Zilla::Plugin::Test::Compile' => '2.039',
'Dist::Zilla::Plugin::Test::EOL' => '0.17',
'Dist::Zilla::Plugin::Test::Kwalitee' => '2.10',
'Dist::Zilla::Plugin::Test::MinimumVersion' => '2.000010',
'Dist::Zilla::Plugin::Test::NoTabs' => '0.08',
'Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe' => '0',
'Dist::Zilla::Plugin::Test::PodSpelling' => '2.006003',
'Dist::Zilla::Plugin::Test::Portability' => '2.000007',
'Dist::Zilla::Plugin::Test::ReportPrereqs' => '0.022',
'Dist::Zilla::Plugin::TestRelease' => '0',
'Dist::Zilla::Plugin::UploadToCPAN' => '0',
'Dist::Zilla::Plugin::UseUnsafeInc' => '0',
'Dist::Zilla::PluginBundle::Author::ETHER' => '0.154',
'Dist::Zilla::PluginBundle::Git::VersionManager' => '0.007',
'Software::License::Perl_5' => '0'
}
}
};
$x;
} Schema 000755 000766 000024 0 15221771652 16454 5 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/lib/JSON Tiny.pm 100640 000766 000024 172240 15221771652 20137 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/lib/JSON/Schema # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
package JSON::Schema::Tiny; # git description: v0.033-12-g455ea01
# vim: set ts=8 sts=2 sw=2 tw=100 et :
# ABSTRACT: Validate data against a schema, minimally
# KEYWORDS: JSON Schema data validation structure specification tiny
our $VERSION = '0.034';
use 5.020; # for unicode_strings, signatures, postderef features
use stable 0.031 'postderef';
use experimental 0.026 qw(signatures args_array_with_signatures);
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use B;
use Mojo::URL;
use Mojo::JSON::Pointer;
use Carp qw(croak carp);
use Mojo::JSON (); # for JSON_XS, MOJO_NO_JSON_XS environment variables
use Feature::Compat::Try;
use JSON::PP ();
use if "$]" < 5.041010, 'List::Util' => 'any';
use if "$]" >= 5.041010, experimental => 'keyword_any';
use Scalar::Util 'looks_like_number';
use builtin::compat qw(blessed created_as_number);
use if "$]" >= 5.022, POSIX => 'isinf';
use Math::BigFloat;
use Exporter 5.57 'import';
our @EXPORT_OK = qw(evaluate);
our $BOOLEAN_RESULT = 0;
our $SHORT_CIRCUIT = 0;
our $MAX_DEPTH = 50;
our $MAX_TRAVERSAL_DEPTH; # deprecated!
our $MOJO_BOOLEANS; # deprecated; renamed to $SCALARREF_BOOLEANS
our $SCALARREF_BOOLEANS;
our $STRINGY_NUMBERS;
our $SPECIFICATION_VERSION;
my %version_uris = (
'https://json-schema.org/draft/2020-12/schema' => 'draft2020-12',
'https://json-schema.org/draft/2019-09/schema' => 'draft2019-09',
'http://json-schema.org/draft-07/schema#' => 'draft7',
);
sub new ($class, %args) {
bless(\%args, $class);
}
sub evaluate {
croak 'evaluate called in void context' if not defined wantarray;
$SCALARREF_BOOLEANS = $SCALARREF_BOOLEANS // $MOJO_BOOLEANS;
local $BOOLEAN_RESULT = $_[0]->{boolean_result} // $BOOLEAN_RESULT,
local $SHORT_CIRCUIT = $_[0]->{short_circuit} // $SHORT_CIRCUIT,
local $MAX_DEPTH = $_[0]->{max_depth} // $_[0]->{max_traversal_depth} // $MAX_TRAVERSAL_DEPTH // $MAX_DEPTH,
local $SCALARREF_BOOLEANS = $_[0]->{scalarref_booleans} // $SCALARREF_BOOLEANS // $_[0]->{mojo_booleans},
local $STRINGY_NUMBERS = $_[0]->{stringy_numbers} // $STRINGY_NUMBERS,
local $SPECIFICATION_VERSION = $_[0]->{specification_version} // $SPECIFICATION_VERSION,
shift
if blessed($_[0]) and blessed($_[0])->isa(__PACKAGE__);
if (defined $SPECIFICATION_VERSION) {
$SPECIFICATION_VERSION = 'draft'.$SPECIFICATION_VERSION
if $SPECIFICATION_VERSION !~ /^draft/ and any { 'draft'.$SPECIFICATION_VERSION eq $_ } values %version_uris;
croak '$SPECIFICATION_VERSION value is invalid' if not any { $SPECIFICATION_VERSION eq $_ } values %version_uris;
}
croak 'insufficient arguments' if @_ < 2;
my ($data, $schema) = @_;
my $state = {
depth => 0,
data_path => '',
traversed_keyword_path => '', # the accumulated traversal path up to the last $ref traversal
initial_schema_uri => Mojo::URL->new, # the canonical URI as of the start or the last traversed $ref
keyword_path => '', # the rest of the path, since the start or the last traversed $ref
errors => [],
seen => {},
short_circuit => $BOOLEAN_RESULT || $SHORT_CIRCUIT,
root_schema => $schema, # so we can do $refs within the same document
specification_version => $SPECIFICATION_VERSION,
};
my $valid;
try {
$valid = _evaluate_subschema($data, $schema, $state)
}
catch ($e) {
if (ref $e eq 'HASH') {
push $state->{errors}->@*, $e;
}
else {
E($state, 'EXCEPTION: '.$e);
}
$valid = 0;
}
warn 'result is false but there are no errors' if not $valid and not $state->{errors}->@*;
return $BOOLEAN_RESULT ? $valid : +{
valid => $valid ? JSON::PP::true : JSON::PP::false,
$valid ? () : (errors => $state->{errors}),
};
}
######## NO PUBLIC INTERFACES FOLLOW THIS POINT ########
# current spec version => { keyword => undef, or arrayref of alternatives }
my %removed_keywords = (
'draft7' => {
id => [ '$id' ],
},
'draft2019-09' => {
id => [ '$id' ],
definitions => [ '$defs' ],
dependencies => [ qw(dependentSchemas dependentRequired) ],
},
'draft2020-12' => {
id => [ '$id' ],
definitions => [ '$defs' ],
dependencies => [ qw(dependentSchemas dependentRequired) ],
'$recursiveAnchor' => [ '$dynamicAnchor' ],
'$recursiveRef' => [ '$dynamicRef' ],
additionalItems => [ 'items' ],
},
);
sub _evaluate_subschema ($data, $schema, $state) {
croak '_evaluate_subschema called in void context' if not defined wantarray;
# do not propagate upwards changes to depth, traversed paths,
# but additions to errors are by reference and will be retained
$state = { %$state };
delete $state->@{'keyword', grep /^_/, keys %$state};
abort($state, 'EXCEPTION: maximum evaluation depth exceeded')
if $state->{depth}++ > $MAX_DEPTH;
my $schema_type = get_type($schema);
return $schema || E($state, 'subschema is false') if $schema_type eq 'boolean';
abort($state, 'invalid schema type: %s', $schema_type) if $schema_type ne 'object';
return 1 if not keys %$schema;
# find all schema locations in effect at this data path + canonical_uri combination
# if any of them are absolute prefix of this schema location, we are in a loop.
my $canonical_uri = canonical_uri($state);
my $schema_location = $state->{traversed_keyword_path}.$state->{keyword_path};
{
use autovivification qw(fetch store);
abort($state, 'EXCEPTION: infinite loop detected (same location evaluated twice)')
if grep substr($schema_location, 0, length) eq $_,
keys $state->{seen}{$state->{data_path}}{$canonical_uri}->%*;
$state->{seen}{$state->{data_path}}{$canonical_uri}{$schema_location}++;
}
my $valid = 1;
my $specification_version = $state->{specification_version}//'';
foreach my $keyword (
# CORE KEYWORDS
qw($id $schema),
!$specification_version || $specification_version ne 'draft7' ? '$anchor' : (),
!$specification_version || $specification_version eq 'draft2019-09' ? '$recursiveAnchor' : (),
!$specification_version || $specification_version eq 'draft2020-12' ? '$dynamicAnchor' : (),
'$ref',
!$specification_version || $specification_version eq 'draft2019-09' ? '$recursiveRef' : (),
!$specification_version || $specification_version eq 'draft2020-12' ? '$dynamicRef' : (),
!$specification_version || $specification_version ne 'draft7' ? '$vocabulary' : (),
'$comment',
!$specification_version || $specification_version eq 'draft7' ? 'definitions' : (),
!$specification_version || $specification_version ne 'draft7' ? '$defs' : (),
# APPLICATOR KEYWORDS
qw(allOf anyOf oneOf not if),
!$specification_version || $specification_version ne 'draft7' ? 'dependentSchemas' : (),
!$specification_version || $specification_version eq 'draft7' ? 'dependencies' : (),
!$specification_version || $specification_version !~ qr/^draft(?:7|2019-09)\z/ ? 'prefixItems' : (),
'items',
!$specification_version || $specification_version =~ qr/^draft(?:7|2019-09)\z/ ? 'additionalItems' : (),
qw(contains properties patternProperties additionalProperties propertyNames),
# UNEVALUATED KEYWORDS
!$specification_version || $specification_version ne 'draft7' ? qw(unevaluatedItems unevaluatedProperties) : (),
# VALIDATOR KEYWORDS
qw(type enum const
multipleOf maximum exclusiveMaximum minimum exclusiveMinimum
maxLength minLength pattern
maxItems minItems uniqueItems),
!$specification_version || $specification_version ne 'draft7' ? qw(maxContains minContains) : (),
qw(maxProperties minProperties required),
!$specification_version || $specification_version ne 'draft7' ? 'dependentRequired' : (),
) {
next if not exists $schema->{$keyword};
# keywords adjacent to $ref (except for definitions) are not evaluated before draft2019-09
next if $keyword ne '$ref' and $keyword ne 'definitions'
and exists $schema->{'$ref'} and $specification_version eq 'draft7';
$state->{keyword} = $keyword;
my $error_count = $state->{errors}->@*;
my $sub = __PACKAGE__->can('_eval_keyword_'.($keyword =~ s/^\$//r));
if (not $sub->($data, $schema, $state)) {
warn 'result is false but there are no errors (keyword: '.$keyword.')'
if $error_count == $state->{errors}->@*;
$valid = 0;
}
last if not $valid and $state->{short_circuit};
}
# check for previously-supported but now removed keywords
foreach my $keyword (sort keys(($removed_keywords{$specification_version}//{})->%*)) {
next if not exists $schema->{$keyword};
my $message ='no-longer-supported "'.$keyword.'" keyword present (at location "'
.canonical_uri($state).'")';
if (my $alternates = $removed_keywords{$specification_version}->{$keyword}) {
my @list = map '"'.$_.'"', @$alternates;
@list = ((map $_.',', @list[0..$#list-1]), $list[-1]) if @list > 2;
splice(@list, -1, 0, 'or') if @list > 1;
$message .= ': this should be rewritten as '.join(' ', @list);
}
carp $message;
}
return $valid;
}
# KEYWORD IMPLEMENTATIONS
sub _eval_keyword_schema ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'string');
assert_uri($state, $schema);
return abort($state, '$schema can only appear at the schema resource root')
if length($state->{keyword_path});
my $specification_version = $version_uris{$schema->{'$schema'}};
abort($state, 'custom $schema URIs are not supported (must be one of: %s',
join(', ', map '"'.$_.'"', keys %version_uris))
if not $specification_version;
abort($state, '"$schema" indicates a different version than that requested by $JSON::Schema::Tiny::SPECIFICATION_VERSION')
if defined $SPECIFICATION_VERSION and $SPECIFICATION_VERSION ne $specification_version;
# we special-case this because the check in _eval for older drafts + $ref has already happened
abort($state, '$schema and $ref cannot be used together in older drafts')
if exists $schema->{'$ref'} and $specification_version eq 'draft7';
$state->{specification_version} = $specification_version;
}
sub _eval_keyword_ref ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'string');
assert_uri_reference($state, $schema);
my $uri = Mojo::URL->new($schema->{$state->{keyword}})->to_abs($state->{initial_schema_uri});
abort($state, '%ss to anchors are not supported', $state->{keyword})
if ($uri->fragment//'') !~ m{^(?:/(?:[^~]|~[01])*)?$};
# the base of the $ref uri must be the same as the base of the root schema
# unfortunately this means that many uses of $ref won't work, because we don't
# track the locations of $ids in this or other documents.
abort($state, 'only same-document, same-base JSON pointers are supported in %s', $state->{keyword})
if $uri->clone->fragment(undef) ne Mojo::URL->new($state->{root_schema}{'$id'}//'');
my $subschema = Mojo::JSON::Pointer->new($state->{root_schema})->get($uri->fragment//'');
abort($state, 'EXCEPTION: unable to find resource %s', $uri) if not defined $subschema;
return _evaluate_subschema($data, $subschema,
+{ %$state,
traversed_keyword_path => $state->{traversed_keyword_path}.$state->{keyword_path}.'/'.$state->{keyword},
initial_schema_uri => $uri,
keyword_path => '',
});
}
sub _eval_keyword_recursiveRef ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'string');
assert_uri_reference($state, $schema);
my $uri = Mojo::URL->new($schema->{'$recursiveRef'})->to_abs($state->{initial_schema_uri});
abort($state, '$recursiveRefs to anchors are not supported')
if ($uri->fragment//'') !~ m{^(?:/(?:[^~]|~[01])*)?$};
# the base of the $recursiveRef uri must be the same as the base of the root schema.
# unfortunately this means that nearly all usecases of $recursiveRef won't work, because we don't
# track the locations of $ids in this or other documents.
abort($state, 'only same-document, same-base JSON pointers are supported in $recursiveRef')
if $uri->clone->fragment(undef) ne Mojo::URL->new($state->{root_schema}{'$id'}//'');
my $subschema = Mojo::JSON::Pointer->new($state->{root_schema})->get($uri->fragment);
abort($state, 'EXCEPTION: unable to find resource %s', $uri) if not defined $subschema;
if (is_type('boolean', $subschema->{'$recursiveAnchor'}) and $subschema->{'$recursiveAnchor'}) {
$uri = Mojo::URL->new($schema->{'$recursiveRef'})
->to_abs($state->{recursive_anchor_uri} // $state->{initial_schema_uri});
$subschema = Mojo::JSON::Pointer->new($state->{root_schema})->get($uri->fragment);
abort($state, 'EXCEPTION: unable to find resource %s', $uri) if not defined $subschema;
}
return _evaluate_subschema($data, $subschema,
+{ %$state,
traversed_keyword_path => $state->{traversed_keyword_path}.$state->{keyword_path}.'/$recursiveRef',
initial_schema_uri => $uri,
keyword_path => '',
});
}
sub _eval_keyword_dynamicRef { goto \&_eval_keyword_ref }
sub _eval_keyword_id ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'string');
assert_uri_reference($state, $schema);
my $uri = Mojo::URL->new($schema->{'$id'});
if (($state->{specification_version}//'') eq 'draft7') {
if (length($uri->fragment)) {
abort($state, '$id cannot change the base uri at the same time as declaring an anchor')
if length($uri->clone->fragment(undef));
abort($state, '$id value does not match required syntax')
if $uri->fragment !~ m/^[A-Za-z][A-Za-z0-9_:.-]*\z/;
return 1;
}
}
else {
abort($state, '$id value "%s" cannot have a non-empty fragment', $uri) if length $uri->fragment;
}
$uri->fragment(undef);
return E($state, '$id cannot be empty') if not length $uri;
$state->{initial_schema_uri} = $uri->is_abs ? $uri : $uri->to_abs($state->{initial_schema_uri});
$state->{traversed_keyword_path} = $state->{traversed_keyword_path}.$state->{keyword_path};
$state->{keyword_path} = '';
return 1;
}
sub _eval_keyword_anchor ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'string');
return 1 if
(!$state->{specification_version} or $state->{specification_version} eq 'draft2019-09')
and ($schema->{'$anchor'}//'') =~ /^[A-Za-z][A-Za-z0-9_:.-]*\z/
or
(!$state->{specification_version} or $state->{specification_version} eq 'draft2020-12')
and ($schema->{'$anchor'}//'') =~ /^[A-Za-z_][A-Za-z0-9._-]*\z/;
abort($state, '$anchor value does not match required syntax');
}
sub _eval_keyword_recursiveAnchor ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'boolean');
return 1 if not $schema->{'$recursiveAnchor'} or exists $state->{recursive_anchor_uri};
# this is required because the location is used as the base URI for future resolution
# of $recursiveRef, and the fragment would be disregarded in the base
abort($state, '"$recursiveAnchor" keyword used without "$id"')
if not exists $schema->{'$id'};
# record the canonical location of the current position, to be used against future resolution
# of a $recursiveRef uri -- as if it was the current location when we encounter a $ref.
$state->{recursive_anchor_uri} = canonical_uri($state);
return 1;
}
sub _eval_keyword_dynamicAnchor ($data, $schema, $state) {
return if not assert_keyword_type($state, $schema, 'string');
abort($state, '$dynamicAnchor value does not match required syntax')
if $schema->{'$dynamicAnchor'} !~ /^[A-Za-z_][A-Za-z0-9._-]*\z/;
return 1;
}
sub _eval_keyword_vocabulary ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'object');
foreach my $uri (sort keys $schema->{'$vocabulary'}->%*) {
abort({ %$state, _keyword_path_suffix => $uri }, '$vocabulary value at "%s" is not a boolean', $uri)
if not is_type('boolean', $schema->{'$vocabulary'}{$uri});
assert_uri($state, undef, $uri);
}
abort($state, '$vocabulary can only appear at the schema resource root')
if length($state->{keyword_path});
abort($state, '$vocabulary can only appear at the document root')
if length($state->{traversed_keyword_path}.$state->{keyword_path});
return 1;
}
sub _eval_keyword_comment ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'string');
return 1;
}
sub _eval_keyword_definitions { goto \&_eval_keyword_defs }
sub _eval_keyword_defs ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'object');
return 1;
}
sub _eval_keyword_type ($data, $schema, $state) {
if (ref $schema->{type} eq 'ARRAY') {
abort($state, 'type array is empty') if not $schema->{type}->@*;
foreach my $type ($schema->{type}->@*) {
abort($state, 'unrecognized type "%s"', $type//'')
if not any { ($type//'') eq $_ } qw(null boolean object array string number integer);
}
abort($state, '"type" values are not unique') if not is_elements_unique($schema->{type});
my $type = get_type($data);
return 1 if any {
$type eq $_ or ($_ eq 'number' and $type eq 'integer')
or ($type eq 'string' and $STRINGY_NUMBERS and looks_like_number($data)
and ($_ eq 'number' or ($_ eq 'integer' and $data == int($data))))
or ($_ eq 'boolean' and $SCALARREF_BOOLEANS and $type eq 'reference to SCALAR')
} $schema->{type}->@*;
return E($state, 'got %s, not one of %s', $type, join(', ', $schema->{type}->@*));
}
else {
assert_keyword_type($state, $schema, 'string');
abort($state, 'unrecognized type "%s"', $schema->{type}//'')
if not any { ($schema->{type}//'') eq $_ } qw(null boolean object array string number integer);
my $type = get_type($data);
return 1 if $type eq $schema->{type} or ($schema->{type} eq 'number' and $type eq 'integer')
or ($type eq 'string' and $STRINGY_NUMBERS and looks_like_number($data)
and ($schema->{type} eq 'number' or ($schema->{type} eq 'integer' and $data == int($data))))
or ($schema->{type} eq 'boolean' and $SCALARREF_BOOLEANS and $type eq 'reference to SCALAR');
return E($state, 'got %s, not %s', $type, $schema->{type});
}
}
sub _eval_keyword_enum ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'array');
my @s; my $idx = 0;
return 1 if any { is_equal($data, $_, $s[$idx++] = {}) } $schema->{enum}->@*;
return E($state, 'value does not match'
.(!(grep $_->{path}, @s) ? ''
: ' ('.join('; ', map "from enum $_ at '$s[$_]->{path}': $s[$_]->{error}", 0..$#s).')'));
}
sub _eval_keyword_const ($data, $schema, $state) {
return 1 if is_equal($data, $schema->{const}, my $s = {});
return E($state, 'value does not match'.($s->{path} ? " (at '$s->{path}': $s->{error})" : ''));
}
sub _eval_keyword_multipleOf ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'number');
abort($state, 'multipleOf value is not a positive number') if $schema->{multipleOf} <= 0;
return 1 if not is_type('number', $data)
and not ($STRINGY_NUMBERS and is_type('string', $data) and looks_like_number($data)
and do { $data = 0+$data; 1 });
# if either value is a float, use the bignum library for the calculation
if (is_bignum($data) or is_bignum($schema->{multipleOf})
or get_type($data) eq 'number' or get_type($schema->{multipleOf}) eq 'number') {
$data = is_bignum($data) ? $data->copy : Math::BigFloat->new($data);
my $divisor = is_bignum($schema->{multipleOf}) ? $schema->{multipleOf} : Math::BigFloat->new($schema->{multipleOf});
my ($quotient, $remainder) = $data->bdiv($divisor);
return E($state, 'overflow while calculating quotient') if $quotient->is_inf;
return 1 if $remainder == 0;
}
else {
my $quotient = $data / $schema->{multipleOf};
return E($state, 'overflow while calculating quotient')
if "$]" >= 5.022 ? isinf($quotient) : $quotient =~ /^-?Inf\z/i;
return 1 if int($quotient) == $quotient;
}
return E($state, 'value is not a multiple of %s', sprintf_num($schema->{multipleOf}));
}
sub _eval_keyword_maximum ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'number');
return 1 if not is_type('number', $data)
and not ($STRINGY_NUMBERS and is_type('string', $data) and looks_like_number($data));
return 1 if 0+$data <= $schema->{maximum};
return E($state, 'value is larger than %s', sprintf_num($schema->{maximum}));
}
sub _eval_keyword_exclusiveMaximum ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'number');
return 1 if not is_type('number', $data)
and not ($STRINGY_NUMBERS and is_type('string', $data) and looks_like_number($data));
return 1 if 0+$data < $schema->{exclusiveMaximum};
return E($state, 'value is equal to or larger than %s', sprintf_num($schema->{exclusiveMaximum}));
}
sub _eval_keyword_minimum ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'number');
return 1 if not is_type('number', $data)
and not ($STRINGY_NUMBERS and is_type('string', $data) and looks_like_number($data));
return 1 if 0+$data >= $schema->{minimum};
return E($state, 'value is smaller than %s', sprintf_num($schema->{minimum}));
}
sub _eval_keyword_exclusiveMinimum ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'number');
return 1 if not is_type('number', $data)
and not ($STRINGY_NUMBERS and is_type('string', $data) and looks_like_number($data));
return 1 if 0+$data > $schema->{exclusiveMinimum};
return E($state, 'value is equal to or smaller than %s', sprintf_num($schema->{exclusiveMinimum}));
}
sub _eval_keyword_maxLength ($data, $schema, $state) {
assert_non_negative_integer($schema, $state);
return 1 if not is_type('string', $data);
return 1 if length($data) <= $schema->{maxLength};
return E($state, 'length is greater than %d', $schema->{maxLength});
}
sub _eval_keyword_minLength ($data, $schema, $state) {
assert_non_negative_integer($schema, $state);
return 1 if not is_type('string', $data);
return 1 if length($data) >= $schema->{minLength};
return E($state, 'length is less than %d', $schema->{minLength});
}
sub _eval_keyword_pattern ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'string');
assert_pattern($state, $schema->{pattern});
return 1 if not is_type('string', $data);
return 1 if $data =~ m/(?:$schema->{pattern})/;
return E($state, 'pattern does not match');
}
sub _eval_keyword_maxItems ($data, $schema, $state) {
assert_non_negative_integer($schema, $state);
return 1 if not is_type('array', $data);
return 1 if @$data <= $schema->{maxItems};
return E($state, 'more than %d item%s', $schema->{maxItems}, $schema->{maxItems} > 1 ? 's' : '');
}
sub _eval_keyword_minItems ($data, $schema, $state) {
assert_non_negative_integer($schema, $state);
return 1 if not is_type('array', $data);
return 1 if @$data >= $schema->{minItems};
return E($state, 'fewer than %d item%s', $schema->{minItems}, $schema->{minItems} > 1 ? 's' : '');
}
sub _eval_keyword_uniqueItems ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'boolean');
return 1 if not is_type('array', $data);
return 1 if not $schema->{uniqueItems};
return 1 if is_elements_unique($data, my $equal_indices = []);
return E($state, 'items at indices %d and %d are not unique', @$equal_indices);
}
sub _eval_keyword_maxContains ($data, $schema, $state) {
assert_non_negative_integer($schema, $state);
return 1 if not exists $state->{_num_contains};
return 1 if not is_type('array', $data);
return E($state, 'contains too many matching items')
if $state->{_num_contains} > $schema->{maxContains};
return 1;
}
sub _eval_keyword_minContains ($data, $schema, $state) {
assert_non_negative_integer($schema, $state);
return 1 if not exists $state->{_num_contains};
return 1 if not is_type('array', $data);
return E($state, 'contains too few matching items')
if $state->{_num_contains} < $schema->{minContains};
return 1;
}
sub _eval_keyword_maxProperties ($data, $schema, $state) {
assert_non_negative_integer($schema, $state);
return 1 if not is_type('object', $data);
return 1 if keys %$data <= $schema->{maxProperties};
return E($state, 'more than %d propert%s', $schema->{maxProperties},
$schema->{maxProperties} > 1 ? 'ies' : 'y');
}
sub _eval_keyword_minProperties ($data, $schema, $state) {
assert_non_negative_integer($schema, $state);
return 1 if not is_type('object', $data);
return 1 if keys %$data >= $schema->{minProperties};
return E($state, 'fewer than %d propert%s', $schema->{minProperties},
$schema->{minProperties} > 1 ? 'ies' : 'y');
}
sub _eval_keyword_required ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'array');
abort($state, '"required" element is not a string')
if any { !is_type('string', $_) } $schema->{required}->@*;
abort($state, '"required" values are not unique') if not is_elements_unique($schema->{required});
return 1 if not is_type('object', $data);
my @missing = grep !exists $data->{$_}, $schema->{required}->@*;
return 1 if not @missing;
return E($state, 'missing propert%s: %s', @missing > 1 ? 'ies' : 'y', join(', ', @missing));
}
sub _eval_keyword_dependentRequired ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'object');
foreach my $property (sort keys $schema->{dependentRequired}->%*) {
E({ %$state, _keyword_path_suffix => $property }, 'value is not an array'), next
if not is_type('array', $schema->{dependentRequired}{$property});
foreach my $index (0..$schema->{dependentRequired}{$property}->$#*) {
abort({ %$state, _keyword_path_suffix => [ $property, $index ] }, 'element #%d is not a string', $index)
if not is_type('string', $schema->{dependentRequired}{$property}[$index]);
}
abort({ %$state, _keyword_path_suffix => $property }, 'elements are not unique')
if not is_elements_unique($schema->{dependentRequired}{$property});
}
return 1 if not is_type('object', $data);
my $valid = 1;
foreach my $property (sort keys $schema->{dependentRequired}->%*) {
next if not exists $data->{$property};
if (my @missing = grep !exists($data->{$_}), $schema->{dependentRequired}{$property}->@*) {
$valid = E({ %$state, _keyword_path_suffix => $property },
'missing propert%s: %s', @missing > 1 ? 'ies' : 'y', join(', ', @missing));
}
}
return 1 if $valid;
return E($state, 'not all dependencies are satisfied');
}
sub _eval_keyword_allOf ($data, $schema, $state) {
assert_array_schemas($schema, $state);
my @invalid;
foreach my $idx (0..$schema->{allOf}->$#*) {
next if _evaluate_subschema($data, $schema->{allOf}[$idx],
+{ %$state, keyword_path => $state->{keyword_path}.'/allOf/'.$idx });
push @invalid, $idx;
last if $state->{short_circuit};
}
return 1 if @invalid == 0;
my $pl = @invalid > 1;
return E($state, 'subschema%s %s %s not valid', $pl?'s':'', join(', ', @invalid), $pl?'are':'is');
}
sub _eval_keyword_anyOf ($data, $schema, $state) {
assert_array_schemas($schema, $state);
my $valid = 0;
my @errors;
foreach my $idx (0..$schema->{anyOf}->$#*) {
next if not _evaluate_subschema($data, $schema->{anyOf}[$idx],
+{ %$state, errors => \@errors, keyword_path => $state->{keyword_path}.'/anyOf/'.$idx });
++$valid;
last if $state->{short_circuit};
}
return 1 if $valid;
push $state->{errors}->@*, @errors;
return E($state, 'no subschemas are valid');
}
sub _eval_keyword_oneOf ($data, $schema, $state) {
assert_array_schemas($schema, $state);
my (@valid, @errors);
foreach my $idx (0..$schema->{oneOf}->$#*) {
next if not _evaluate_subschema($data, $schema->{oneOf}[$idx],
+{ %$state, errors => \@errors, keyword_path => $state->{keyword_path}.'/oneOf/'.$idx });
push @valid, $idx;
last if @valid > 1 and $state->{short_circuit};
}
return 1 if @valid == 1;
if (not @valid) {
push $state->{errors}->@*, @errors;
return E($state, 'no subschemas are valid');
}
else {
return E($state, 'multiple subschemas are valid: '.join(', ', @valid));
}
}
sub _eval_keyword_not ($data, $schema, $state) {
return !$schema->{not} || E($state, 'subschema is true') if is_type('boolean', $schema->{not});
return 1 if not _evaluate_subschema($data, $schema->{not},
+{ %$state, keyword_path => $state->{keyword_path}.'/not', short_circuit => 1, errors => [] });
return E($state, 'subschema is valid');
}
sub _eval_keyword_if ($data, $schema, $state) {
return 1 if not exists $schema->{then} and not exists $schema->{else};
my $keyword = _evaluate_subschema($data, $schema->{if},
+{ %$state, keyword_path => $state->{keyword_path}.'/if', short_circuit => 1, errors => [] })
? 'then' : 'else';
return 1 if not exists $schema->{$keyword};
return $schema->{$keyword} || E({ %$state, keyword => $keyword }, 'subschema is false')
if is_type('boolean', $schema->{$keyword});
return 1 if _evaluate_subschema($data, $schema->{$keyword},
+{ %$state, keyword_path => $state->{keyword_path}.'/'.$keyword });
return E({ %$state, keyword => $keyword }, 'subschema is not valid');
}
sub _eval_keyword_dependentSchemas ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'object');
return 1 if not is_type('object', $data);
my $valid = 1;
foreach my $property (sort keys $schema->{dependentSchemas}->%*) {
next if not exists $data->{$property}
or _evaluate_subschema($data, $schema->{dependentSchemas}{$property},
+{ %$state, keyword_path => jsonp($state->{keyword_path}, 'dependentSchemas', $property) });
$valid = 0;
last if $state->{short_circuit};
}
return E($state, 'not all dependencies are satisfied') if not $valid;
return 1;
}
sub _eval_keyword_dependencies ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'object');
return 1 if not is_type('object', $data);
my $valid = 1;
foreach my $property (sort keys $schema->{dependencies}->%*) {
if (is_type('array', $schema->{dependencies}{$property})) {
# as in dependentRequired
foreach my $index (0..$schema->{dependencies}{$property}->$#*) {
$valid = E({ %$state, _keyword_path_suffix => [ $property, $index ] }, 'element #%d is not a string', $index)
if not is_type('string', $schema->{dependencies}{$property}[$index]);
}
abort({ %$state, _keyword_path_suffix => $property }, 'elements are not unique')
if not is_elements_unique($schema->{dependencies}{$property});
next if not exists $data->{$property};
if (my @missing = grep !exists($data->{$_}), $schema->{dependencies}{$property}->@*) {
$valid = E({ %$state, _keyword_path_suffix => $property },
'missing propert%s: %s', @missing > 1 ? 'ies' : 'y', join(', ', @missing));
}
}
else {
# as in dependentSchemas
next if not exists $data->{$property}
or _evaluate_subschema($data, $schema->{dependencies}{$property},
+{ %$state, keyword_path => jsonp($state->{keyword_path}, 'dependencies', $property) });
$valid = 0;
last if $state->{short_circuit};
}
}
return 1 if $valid;
return E($state, 'not all dependencies are satisfied');
}
sub _eval_keyword_prefixItems ($data, $schema, $state) {
return if not assert_array_schemas($schema, $state);
goto \&_eval_keyword__items_array_schemas;
}
sub _eval_keyword_items ($data, $schema, $state) {
if (ref $schema->{items} eq 'ARRAY') {
abort($state, 'array form of "items" not supported in %s', $state->{specification_version})
if ($state->{specification_version}//'') eq 'draft2020-12';
goto \&_eval_keyword__items_array_schemas;
}
$state->{_last_items_index} //= -1;
goto \&_eval_keyword__items_schema;
}
sub _eval_keyword_additionalItems ($data, $schema, $state) {
return 1 if not exists $state->{_last_items_index};
goto \&_eval_keyword__items_schema;
}
# prefixItems (draft 2020-12), array-based items (all drafts)
sub _eval_keyword__items_array_schemas ($data, $schema, $state) {
abort($state, '%s array is empty', $state->{keyword}) if not $schema->{$state->{keyword}}->@*;
return 1 if not is_type('array', $data);
my $valid = 1;
foreach my $idx (0..$data->$#*) {
last if $idx > $schema->{$state->{keyword}}->$#*;
$state->{_last_items_index} = $idx;
if (is_type('boolean', $schema->{$state->{keyword}}[$idx])) {
next if $schema->{$state->{keyword}}[$idx];
$valid = E({ %$state, data_path => $state->{data_path}.'/'.$idx,
_keyword_path_suffix => $idx }, 'item not permitted');
}
else {
next if _evaluate_subschema($data->[$idx], $schema->{$state->{keyword}}[$idx],
+{ %$state, data_path => $state->{data_path}.'/'.$idx,
keyword_path => $state->{keyword_path}.'/'.$state->{keyword}.'/'.$idx });
}
$valid = 0;
last if $state->{short_circuit} and not exists $schema->{
$state->{keyword} eq 'prefixItems' ? 'items'
: $state->{keyword} eq 'items' ? 'additionalItems' : die
};
}
return E($state, 'not all items are valid') if not $valid;
return 1;
}
# schema-based items (all drafts), and additionalItems (drafts 4,6,7,2019-09)
sub _eval_keyword__items_schema ($data, $schema, $state) {
return 1 if not is_type('array', $data);
return 1 if $state->{_last_items_index} == $data->$#*;
my $valid = 1;
foreach my $idx ($state->{_last_items_index}+1 .. $data->$#*) {
if (is_type('boolean', $schema->{$state->{keyword}})
and ($state->{keyword} eq 'additionalItems')) {
next if $schema->{$state->{keyword}};
$valid = E({ %$state, data_path => $state->{data_path}.'/'.$idx },
'%sitem not permitted',
exists $schema->{prefixItems} || $state->{keyword} eq 'additionalItems' ? 'additional ' : '');
}
else {
next if _evaluate_subschema($data->[$idx], $schema->{$state->{keyword}},
+{ %$state, data_path => $state->{data_path}.'/'.$idx,
keyword_path => $state->{keyword_path}.'/'.$state->{keyword} });
$valid = 0;
}
last if $state->{short_circuit};
}
$state->{_last_items_index} = $data->$#*;
return E($state, 'subschema is not valid against all %sitems',
exists $schema->{prefixItems} || $state->{keyword} eq 'additionalItems' ? 'additional ' : '')
if not $valid;
return 1;
}
sub _eval_keyword_contains ($data, $schema, $state) {
return 1 if not is_type('array', $data);
$state->{_num_contains} = 0;
my @errors;
foreach my $idx (0..$data->$#*) {
if (_evaluate_subschema($data->[$idx], $schema->{contains},
+{ %$state, errors => \@errors,
data_path => $state->{data_path}.'/'.$idx,
keyword_path => $state->{keyword_path}.'/contains' })) {
++$state->{_num_contains};
last if $state->{short_circuit}
and (not exists $schema->{maxContains} or $state->{_num_contains} > $schema->{maxContains})
and ($state->{_num_contains} >= ($schema->{minContains}//1));
}
}
# note: no items contained is only valid when minContains is explicitly 0
if (not $state->{_num_contains} and (($schema->{minContains}//1) > 0
or $state->{specification_version} and $state->{specification_version} eq 'draft7')) {
push $state->{errors}->@*, @errors;
return E($state, 'subschema is not valid against any item');
}
return 1;
}
sub _eval_keyword_properties ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'object');
return 1 if not is_type('object', $data);
my $valid = 1;
foreach my $property (sort keys $schema->{properties}->%*) {
next if not exists $data->{$property};
if (is_type('boolean', $schema->{properties}{$property})) {
next if $schema->{properties}{$property};
$valid = E({ %$state, data_path => jsonp($state->{data_path}, $property),
_keyword_path_suffix => $property }, 'property not permitted');
}
else {
next if _evaluate_subschema($data->{$property}, $schema->{properties}{$property},
+{ %$state,
data_path => jsonp($state->{data_path}, $property),
keyword_path => jsonp($state->{keyword_path}, 'properties', $property) });
$valid = 0;
}
last if $state->{short_circuit};
}
return E($state, 'not all properties are valid') if not $valid;
return 1;
}
sub _eval_keyword_patternProperties ($data, $schema, $state) {
assert_keyword_type($state, $schema, 'object');
foreach my $property (sort keys $schema->{patternProperties}->%*) {
assert_pattern({ %$state, _keyword_path_suffix => $property }, $property);
}
return 1 if not is_type('object', $data);
my $valid = 1;
foreach my $property_pattern (sort keys $schema->{patternProperties}->%*) {
foreach my $property (sort grep m/(?:$property_pattern)/, keys %$data) {
if (is_type('boolean', $schema->{patternProperties}{$property_pattern})) {
next if $schema->{patternProperties}{$property_pattern};
$valid = E({ %$state, data_path => jsonp($state->{data_path}, $property),
_keyword_path_suffix => $property_pattern }, 'property not permitted');
}
else {
next if _evaluate_subschema($data->{$property}, $schema->{patternProperties}{$property_pattern},
+{ %$state,
data_path => jsonp($state->{data_path}, $property),
keyword_path => jsonp($state->{keyword_path}, 'patternProperties', $property_pattern) });
$valid = 0;
}
last if $state->{short_circuit};
}
}
return E($state, 'not all properties are valid') if not $valid;
return 1;
}
sub _eval_keyword_additionalProperties ($data, $schema, $state) {
return 1 if not is_type('object', $data);
my $valid = 1;
foreach my $property (sort keys %$data) {
next if exists $schema->{properties} and exists $schema->{properties}{$property};
next if exists $schema->{patternProperties}
and any { $property =~ /(?:$_)/ } keys $schema->{patternProperties}->%*;
if (is_type('boolean', $schema->{additionalProperties})) {
next if $schema->{additionalProperties};
$valid = E({ %$state, data_path => jsonp($state->{data_path}, $property) },
'additional property not permitted');
}
else {
next if _evaluate_subschema($data->{$property}, $schema->{additionalProperties},
+{ %$state,
data_path => jsonp($state->{data_path}, $property),
keyword_path => $state->{keyword_path}.'/additionalProperties' });
$valid = 0;
}
last if $state->{short_circuit};
}
return E($state, 'not all additional properties are valid') if not $valid;
return 1;
}
sub _eval_keyword_propertyNames ($data, $schema, $state) {
return 1 if not is_type('object', $data);
my $valid = 1;
foreach my $property (sort keys %$data) {
next if _evaluate_subschema($property, $schema->{propertyNames},
+{ %$state,
data_path => jsonp($state->{data_path}, $property),
keyword_path => $state->{keyword_path}.'/propertyNames' });
$valid = 0;
last if $state->{short_circuit};
}
return E($state, 'not all property names are valid') if not $valid;
return 1;
}
sub _eval_keyword_unevaluatedItems ($data, $schema, $state) {
abort($state, 'keyword not yet supported');
}
sub _eval_keyword_unevaluatedProperties ($data, $schema, $state) {
abort($state, 'keyword not yet supported');
}
# UTILITIES
# supports the six core types, plus integer (which is also a number)
# we do NOT check $STRINGY_NUMBERS here -- you must do that in the caller
# note that sometimes a value may return true for more than one type, e.g. integer+number,
# or number+string, depending on its internal flags.
# copied from JSON::Schema::Modern::Utilities::is_type
sub is_type ($type, $value) {
if ($type eq 'null') {
return !(defined $value);
}
if ($type eq 'boolean') {
return is_bool($value);
}
if ($type eq 'object') {
return ref $value eq 'HASH';
}
if ($type eq 'array') {
return ref $value eq 'ARRAY';
}
if ($type eq 'string' or $type eq 'number' or $type eq 'integer') {
return 0 if not defined $value;
my $flags = B::svref_2object(\$value)->FLAGS;
# dualvars with the same string and (stringified) numeric value could be either a string or a
# number, and before 5.36 we can't tell the difference, so we will answer yes for both.
# in 5.36+, stringified numbers still get a PV but don't have POK set, whereas
# numified strings do have POK set, so we can tell which one came first.
if ($type eq 'string') {
# like created_as_string, but rejects dualvars with stringwise-unequal string and numeric parts
return !length ref($value)
&& $flags & B::SVf_POK
&& (!($flags & (B::SVf_IOK | B::SVf_NOK))
|| do { no warnings 'numeric'; 0+$value eq $value });
}
if ($type eq 'number') {
# floats in json will always be parsed into Math::BigFloat, when allow_bignum is enabled
return is_bignum($value) || created_as_number($value);
}
if ($type eq 'integer') {
# note: values that are larger than $Config{ivsize} will be represented as an NV, not IV,
# therefore they will fail this check
return is_bignum($value) && $value->is_int
# if dualvar, PV and stringified NV/IV must be identical
|| created_as_number($value) && int($value) == $value;
}
}
if ($type =~ /^reference to (.+)\z/) {
return !blessed($value) && ref($value) eq $1;
}
return ref($value) eq $type;
}
# returns one of the six core types, plus integer
# we do NOT check $STRINGY_NUMBERS here -- you must do that in the caller
# copied from JSON::Schema::Modern::Utilities::get_type
sub get_type ($value) {
return 'object' if ref $value eq 'HASH';
return 'boolean' if is_bool($value);
return 'null' if not defined $value;
return 'array' if ref $value eq 'ARRAY';
# floats in json will always be parsed into Math::BigFloat, when allow_bignum is enabled
if (length(my $ref = ref $value)) {
return $ref eq 'Math::BigInt' ? 'integer'
: $ref eq 'Math::BigFloat' ? ($value->is_int ? 'integer' : 'number')
: (defined blessed($value) ? '' : 'reference to ').$ref;
}
my $flags = B::svref_2object(\$value)->FLAGS;
# dualvars with the same string and (stringified) numeric value could be either a string or a
# number, and before 5.36 we can't tell the difference, so we choose number because it has been
# evaluated as a number already.
# in 5.36+, stringified numbers still get a PV but don't have POK set, whereas
# numified strings do have POK set, so we can tell which one came first.
# like created_as_string, but rejects dualvars with stringwise-unequal string and numeric parts
return 'string'
if $flags & B::SVf_POK
&& (!($flags & (B::SVf_IOK | B::SVf_NOK))
|| do { no warnings 'numeric'; 0+$value eq $value });
# note: values that are larger than $Config{ivsize} will be represented as an NV, not IV,
# therefore they will fail this check
return int($value) == $value ? 'integer' : 'number' if created_as_number($value);
# this might be a scalar with POK|IOK or POK|NOK set
return 'ambiguous type';
}
# lifted from JSON::MaybeXS
# note: unlike builtin::compat::is_bool on older perls, we do not accept
# dualvar(0,"") or dualvar(1,"1") because JSON::PP and Cpanel::JSON::XS
# do not encode these as booleans.
use constant HAVE_BUILTIN => "$]" >= 5.035010;
use if HAVE_BUILTIN, experimental => 'builtin';
sub is_bool ($value) {
HAVE_BUILTIN and builtin::is_bool($value)
or
!!blessed($value)
and ($value->isa('JSON::PP::Boolean')
or $value->isa('Cpanel::JSON::XS::Boolean')
or $value->isa('JSON::XS::Boolean'));
}
sub is_bignum ($value) {
ref($value) =~ /^Math::Big(?:Int|Float)\z/;
}
# compares two arbitrary data payloads for equality, as per
# https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.4.2.2
# $state hashref supports the following fields/configs:
# - path: location of the first difference
# - error: description of the difference
# - $SCALARREF_BOOLEANS: treats \0 and \1 as boolean values
# - $STRINGY_NUMBERS: strings will be typed as numbers if looks_like_number() is true
# copied from JSON::Schema::Modern::Utilities::is_equal
sub is_equal ($x, $y, $state = {}) {
$state->{path} //= '';
my @types = map get_type($_), $x, $y;
$state->{error} = 'ambiguous type encountered', return 0
if grep $types[$_] eq 'ambiguous type', 0..1;
if ($SCALARREF_BOOLEANS) {
($x, $types[0]) = (0+!!$$x, 'boolean') if $types[0] eq 'reference to SCALAR';
($y, $types[1]) = (0+!!$$y, 'boolean') if $types[1] eq 'reference to SCALAR';
}
if ($STRINGY_NUMBERS) {
($x, $types[0]) = (0+$x, int(0+$x) == $x ? 'integer' : 'number')
if $types[0] eq 'string' and looks_like_number($x);
($y, $types[1]) = (0+$y, int(0+$y) == $y ? 'integer' : 'number')
if $types[1] eq 'string' and looks_like_number($y);
}
$state->{error} = "wrong type: $types[0] vs $types[1]", return 0 if $types[0] ne $types[1];
return 1 if $types[0] eq 'null';
($x eq $y and return 1), $state->{error} = 'strings not equal', return 0
if $types[0] eq 'string';
($x == $y and return 1), $state->{error} = "$types[0]s not equal", return 0
if grep $types[0] eq $_, qw(boolean number integer);
my $path = $state->{path};
if ($types[0] eq 'object') {
$state->{error} = 'property count differs: '.keys(%$x).' vs '.keys(%$y), return 0
if keys %$x != keys %$y;
if (not is_equal(my $arr_x = [ sort keys %$x ], my $arr_y = [ sort keys %$y ], my $s={})) {
my $pos = substr($s->{path}, 1);
$state->{error} = 'property names differ starting at position '.$pos.' ("'.$arr_x->[$pos].'" vs "'.$arr_y->[$pos].'")';
return 0;
}
foreach my $property (sort keys %$x) {
$state->{path} = jsonp($path, $property);
return 0 if not is_equal($x->{$property}, $y->{$property}, $state);
}
return 1;
}
if ($types[0] eq 'array') {
$state->{error} = 'element count differs: '.@$x.' vs '.@$y, return 0 if @$x != @$y;
foreach my $idx (0 .. $x->$#*) {
$state->{path} = $path.'/'.$idx;
return 0 if not is_equal($x->[$idx], $y->[$idx], $state);
}
return 1;
}
$state->{error} = 'uh oh', return 0; # should never get here
}
# checks array elements for uniqueness. short-circuits on first pair of matching elements
# if second arrayref is provided, it is populated with the indices of identical items
# supports the following configs:
# - $SCALARREF_BOOLEANS: treats \0 and \1 as boolean values
# - $STRINGY_NUMBERS: strings will be typed as numbers if looks_like_number() is true
# copied from JSON::Schema::Modern::Utilities::is_elements_unique
sub is_elements_unique ($array, $equal_indices = undef) {
foreach my $idx0 (0 .. $array->$#*-1) {
foreach my $idx1 ($idx0+1 .. $array->$#*) {
if (is_equal($array->[$idx0], $array->[$idx1])) {
push @$equal_indices, $idx0, $idx1 if defined $equal_indices;
return 0;
}
}
}
return 1;
}
# shorthand for creating and appending json pointers
# the first argument is a json pointer; remaining arguments are path segments to be encoded and
# appended
# copied from JSON::Schema::Modern::Utilities::jsonp
sub jsonp {
return join('/', shift, map s/~/~0/gr =~ s!/!~1!gr, grep defined, @_);
}
# shorthand for finding the canonical uri of the present schema location
# copied from JSON::Schema::Modern::Utilities::canonical_uri
sub canonical_uri ($state, @extra_path) {
return $state->{initial_schema_uri} if not @extra_path and not length($state->{keyword_path});
my $uri = $state->{initial_schema_uri}->clone;
my $fragment = ($uri->fragment//'').(@extra_path ? jsonp($state->{keyword_path}, @extra_path) : $state->{keyword_path});
undef $fragment if not length($fragment);
$uri->fragment($fragment);
$uri;
}
# shorthand for creating error objects
# based on JSON::Schema::Modern::Utilities::E
sub E ($state, $error_string, @args) {
# sometimes the keyword shouldn't be at the very end of the schema path
my $sps = delete $state->{_keyword_path_suffix};
my @keyword_path_suffix = defined $sps && ref $sps eq 'ARRAY' ? $sps->@* : $sps//();
my $uri = canonical_uri($state, $state->{keyword}, @keyword_path_suffix);
my $keyword_location = $state->{traversed_keyword_path}
.jsonp($state->{keyword_path}, $state->{keyword}, @keyword_path_suffix);
undef $uri if $uri eq '' and $keyword_location eq ''
or ($uri->fragment//'') eq $keyword_location and $uri->clone->fragment(undef) eq '';
push $state->{errors}->@*, {
instanceLocation => $state->{data_path},
keywordLocation => $keyword_location,
defined $uri ? ( absoluteKeywordLocation => $uri->to_string) : (),
error => @args ? sprintf($error_string, @args) : $error_string,
};
return 0;
}
# creates an error object, but also aborts evaluation immediately
# only this error is returned, because other errors on the stack might not actually be "real"
# errors (consider if we were in the middle of evaluating a "not" or "if")
sub abort ($state, $error_string, @args) {
E($state, $error_string, @args);
die pop $state->{errors}->@*;
}
# one common usecase of abort()
sub assert_keyword_type ($state, $schema, $type) {
return 1 if is_type($type, $schema->{$state->{keyword}});
abort($state, '%s value is not a%s %s', $state->{keyword}, ($type =~ /^[aeiou]/ ? 'n' : ''), $type);
}
sub assert_pattern ($state, $pattern) {
try {
local $SIG{__WARN__} = sub { die @_ };
qr/$pattern/;
}
catch ($e) { abort($state, $e); }
return 1;
}
# based on JSON::Schema::Modern::Utilities::assert_uri_reference
sub assert_uri_reference ($state, $schema) {
my $string = $schema->{$state->{keyword}};
abort($state, '%s value is not a valid URI reference', $state->{keyword})
# see also uri-reference format sub
if fc(Mojo::URL->new($string)->to_unsafe_string) ne fc($string)
or $string =~ /[^[:ascii:]]/ # ascii characters only
or $string =~ /#/ # no fragment, except...
and $string !~ m{#$} # allow empty fragment
and $string !~ m{#[A-Za-z][A-Za-z0-9_:.-]*$} # allow plain-name fragment
and $string !~ m{#/(?:[^~]|~[01])*$}; # allow json pointer fragment
return 1;
}
# based on JSON::Schema::Modern::Utilities::assert_uri
sub assert_uri ($state, $schema, $override = undef) {
my $string = $override // $schema->{$state->{keyword}};
my $uri = Mojo::URL->new($string);
abort($state, '"%s" is not a valid URI', $string)
# see also uri format sub
if fc($uri->to_unsafe_string) ne fc($string)
or $string =~ /[^[:ascii:]]/ # ascii characters only
or not $uri->is_abs # must have a schema
or $string =~ /#/ # no fragment, except...
and $string !~ m{#$} # empty fragment
and $string !~ m{#[A-Za-z][A-Za-z0-9_:.-]*$} # plain-name fragment
and $string !~ m{#/(?:[^~]|~[01])*$}; # json pointer fragment
return 1;
}
sub assert_non_negative_integer ($schema, $state) {
assert_keyword_type($state, $schema, 'integer');
abort($state, '%s value is not a non-negative integer', $state->{keyword})
if $schema->{$state->{keyword}} < 0;
return 1;
}
sub assert_array_schemas ($schema, $state) {
assert_keyword_type($state, $schema, 'array');
abort($state, '%s array is empty', $state->{keyword}) if not $schema->{$state->{keyword}}->@*;
return 1;
}
# copied from JSON::Schema::Modern::Utilities::sprintf_num
sub sprintf_num ($value) {
# use original value as stored in the NV, without losing precision
is_bignum($value) ? $value->bstr : sprintf('%s', $value);
}
{
# avoid dependency on namespace::clean
no strict 'refs';
delete @{__PACKAGE__.'::'}{qw(croak carp any looks_like_number isinf)};
}
1;
__END__
=pod
=encoding UTF-8
=head1 SUPPORT
=for stopwords OpenAPI
You can also find me on the L and L, which are also great resources for finding help.
=for stopwords schema subschema metaschema validator evaluator
=head1 NAME
JSON::Schema::Tiny - Validate data against a schema, minimally
=head1 VERSION
version 0.034
I use a linearly-increasing version numbering scheme. No meaning should be
presumed or inferred from the version being less than 1.0.
=head1 SYNOPSIS
my $data = { hello => 1 };
my $schema = {
type => "object",
properties => { hello => { type => "integer" } },
};
# functional interface:
use JSON::Schema::Tiny qw(evaluate);
my $result = evaluate($data, $schema); # { valid => true }
# object-oriented interface:
use JSON::Schema::Tiny;
my $js = JSON::Schema::Tiny->new;
my $result = $js->evaluate($data, $schema); # { valid => true }
=head1 DESCRIPTION
This module aims to be a slimmed-down L evaluator and
validator, supporting the most popular keywords.
(See L below for exclusions.)
=head1 FUNCTIONS
=for Pod::Coverage is_type get_type is_bool is_bignum is_equal is_elements_unique jsonp canonical_uri E abort
assert_keyword_type assert_pattern assert_uri assert_non_negative_integer assert_array_schemas
new assert_uri_reference sprintf_num HAVE_BUILTIN
=head2 evaluate
my $result = evaluate($data, $schema);
Evaluates the provided instance data against the known schema document.
The data is in the form of an unblessed nested Perl data structure representing any type that JSON
allows: null, boolean, string, number, object, array. (See L below.)
The schema must represent a valid JSON Schema in the form of a Perl data structure, such as what is
returned from a JSON decode operation.
With default configuration settings, the return value is a hashref indicating the validation success
or failure, plus (when validation failed), an arrayref of error strings in standard JSON Schema
format. For example:
running:
$result = evaluate(1, { type => 'number' });
C<$result> is:
{ valid => true }
running:
$result = evaluate(1, { type => 'number', multipleOf => 2 });
C<$result> is:
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/multipleOf',
error => 'value is not a multiple of 2',
},
],
}
When L> is true, the return value is a boolean (indicating evaluation success or
failure).
=head1 OPTIONS
All options are available as package-scoped global variables. Use L to
configure them for a local scope. They may also be set via the constructor, as lower-cased values in
a hash, e.g.: C<< JSON::Schema::Tiny->new(boolean_result => 1, max_depth => 10); >>
=head2 C<$BOOLEAN_RESULT>
When true, L will return a true or false result only, with no error strings. This enables
short-circuit mode internally as this cannot affect results except get there faster. Defaults to false.
=head2 C<$MAX_DEPTH>
The maximum number of levels deep a schema descent may go, before evaluation is halted. This is to
protect against accidental infinite recursion, such as from two subschemas that each reference each
other, or badly-written schemas that could be optimized. Defaults to 50.
=head2 C<$SCALARREF_BOOLEANS>
When true, any value that is expected to be a boolean B may also be expressed as
the scalar references C<\0> or C<\1> (which are serialized as booleans by JSON backends).
Defaults to false.
=head2 C<$SHORT_CIRCUIT>
When true, L will return from evaluating each subschema as soon as a true or false result
can be determined. When C<$BOOLEAN_RESULT> is false, an incomplete list of errors will be returned.
Defaults to false.
=head2 C<$SPECIFICATION_VERSION>
When set, the version of the draft specification is locked to one particular value, and use of
keywords inconsistent with that specification version will result in an error. Will be set
internally automatically with the use of the C<$schema> keyword. When not set, all keywords will be
honoured (when otherwise supported).
Supported values for this option, and the corresponding values for the C<$schema> keyword, are:
=over 4
=item *
L or C<2020-12>|https://json-schema.org/specification-links.html#2020-12>, corresponding to metaschema C
=item *
L or C<2019-09>|https://json-schema.org/specification-links.html#2019-09-formerly-known-as-draft-8>, corresponding to metaschema C
=item *
L or C<7>|https://json-schema.org/specification-links.html#draft-7>, corresponding to metaschema C
=back
Defaults to undef.
=head2 C<$STRINGY_NUMBERS>
When true, any value that is expected to be a number or integer B may also be
expressed as a string. This applies only to the following keywords:
=over 4
=item *
C (where both C and C (and possibly C) are considered types
=item *
C and C (where the string C<"1"> will match with C<"const": 1>)
=item *
C (where strings and numbers are compared numerically to each other, if either or both are numeric)
=item *
C
=item *
C
=item *
C
=item *
C
=item *
C
=back
This allows you to write a schema like this (which validates a string representing an integer):
type: string
pattern: ^[0-9]$
multipleOf: 4
minimum: 16
maximum: 256
Such keywords are only applied if the value looks like a number, and do not generate a failure
otherwise. Values are determined to be numbers via L.
Defaults to false.
=head1 UNSUPPORTED JSON SCHEMA FEATURES
Unlike L, this is not a complete implementation of the JSON Schema
specification. Some features and keywords are left unsupported in order to keep the code small and
the execution fast. These features are not available:
=over 4
=item *
any output format other than C (when C<$BOOLEAN_RESULT> is true) or C (when it is false)
=item *
L in successful evaluation results
=item *
use of C<$ref> other than to locations in the local schema in json-pointer format (e.g. C<#/path/to/property>). This means that references to external documents, either those available locally or on the network, are not permitted.
=back
In addition, these keywords are implemented only partially or not at all (their presence in a schema
will be ignored or possibly result in an error):
=over 4
=item *
C<$schema> - only accepted if set to one of the specification metaschema URIs (see L<$SPECIFICATION_VERSION> for supported values)
=item *
C<$id>
=item *
C<$anchor>
=item *
C<$recursiveAnchor> and C<$recursiveRef> (draft2019-09), and C<$dynamicAnchor> and C<$dynamicRef> (draft2020-12 and thereafter)
=item *
C<$vocabulary>
=item *
C and C (which require annotation support)
=item *
C (does not cause an error when used)
=back
For a more full-featured implementation of the JSON Schema specification, see
L.
=head1 LIMITATIONS
=head2 Types
Perl is a more loosely-typed language than JSON. This module delves into a value's internal
representation in an attempt to derive the true "intended" type of the value.
This should not be an issue if data validation is occurring
immediately after decoding a JSON payload, or if the JSON string itself is passed to this module.
If you are having difficulties, make sure you are using Perl's fastest and most trusted and
reliable JSON decoder, L.
Other JSON decoders are known to produce data with incorrect data types,
and data from other sources may also be problematic.
For more information, see L.
=head1 SECURITY CONSIDERATIONS
The C and C keywords evaluate regular expressions from the schema.
No effort is taken (at this time) to sanitize the regular expressions for embedded code or
potentially pathological constructs that may pose a security risk, either via denial of service
or by allowing exposure to the internals of your application. B
=head1 SEE ALSO
=over 4
=item *
L: a more specification-compliant JSON Schema evaluator
=item *
L: contains the official JSON Schema test suite
=item *
L
=item *
L: tutorial-focused documentation
=back
=for stopwords OpenAPI
=head1 GIVING THANKS
=for stopwords MetaCPAN GitHub
If you found this module to be useful, please show your appreciation by
adding a +1 in L
and a star in L.
=head1 SUPPORT
Bugs may be submitted through L.
I am also usually active on irc, as 'ether' at C and C.
You can also find me on the L and L, which are also great resources for finding help.
=head1 AUTHOR
Karen Etheridge
=head1 CONTRIBUTOR
=for stopwords Matt S Trout
Matt S Trout
=head1 COPYRIGHT AND LICENCE
This software is copyright (c) 2021 by Karen Etheridge.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
portability.t 100644 000766 000024 130 15221771652 20533 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author use strict;
use warnings;
use Test::More;
use Test::Portability::Files;
run_tests();
unsupported-keywords.t 100640 000766 000024 5402 15221771652 20757 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
use Test2::Warnings qw(warnings had_no_warnings :no_end_test);
foreach my $keyword (
qw(unevaluatedItems unevaluatedProperties),
) {
cmp_result(
evaluate(true, { $keyword => {} }),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/'.$keyword,
error => 'keyword not yet supported',
},
],
},
'use of "'.$keyword.'" results in error',
);
}
my %strings = (
id => qr/^no-longer-supported "id" keyword present \(at location ""\): this should be rewritten as "\$id" at /,
definitions => qr/^no-longer-supported "definitions" keyword present \(at location ""\): this should be rewritten as "\$defs" at /,
dependencies => qr/^no-longer-supported "dependencies" keyword present \(at location ""\): this should be rewritten as "dependentSchemas" or "dependentRequired" at /,
);
my %schemas = (
id => 'https://localhost:1234',
definitions => {},
dependencies => {},
);
my @warnings = (
[ draft7 => [ qw(id) ] ],
[ 'draft2019-09' => [ qw(id definitions dependencies) ] ],
);
foreach my $index (0 .. $#warnings) {
my ($specification_version, $removed_keywords) = $warnings[$index]->@*;
note "\n", $specification_version;
my $js = JSON::Schema::Tiny->new(specification_version => $specification_version);
foreach my $keyword (@$removed_keywords) {
cmp_result(
[ warnings { ok($js->evaluate(true, { $keyword => $schemas{$keyword} }), 'schema with "'.$keyword.'" still validates in '.$specification_version) } ],
[ re($strings{$keyword}), ],
'warned for "'.$keyword.'" in '.$specification_version,
);
}
next if $index == $#warnings;
my ($next_specification_version, $removed_next_keywords) = $warnings[$index+1]->@*;
foreach my $keyword (@$removed_next_keywords) {
next if grep $keyword eq $_, @$removed_keywords;
cmp_result(
[ warnings { ok($js->evaluate(true, { $keyword => $schemas{$keyword} }), 'schema with "'.$keyword.'" still validates') } ],
[],
'did not warn for "'.$keyword.'" in '.$specification_version,
);
}
}
had_no_warnings() if $ENV{AUTHOR_TESTING};
done_testing;
pod-coverage.t 100644 000766 000024 544 15221771652 20555 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/xt/author # This file was automatically generated by Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe v1.0.1
use strict;
use warnings;
use Test::More;
use Test::Pod::Coverage::TrustMe;
my $config = {};
my $modules = [
"JSON::Schema::Tiny",
];
plan tests => scalar @$modules;
for my $module (@$modules) {
pod_coverage_ok($module, $config);
}
done_testing;
specification_version.t 100640 000766 000024 37011 15221771652 21130 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use JSON::Schema::Tiny 'evaluate';
use lib 't/lib';
use Helper;
use Test2::Warnings qw(warnings :no_end_test had_no_warnings);
{
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'ohhai';
like (
dies { ()= evaluate(true, true) },
qr/^\$SPECIFICATION_VERSION value is invalid/,
'unrecognized $SPECIFICATION_VERSION',
);
}
subtest '$schema' => sub {
cmp_result(
evaluate(
true,
{ '$schema' => 'http://wrong/url' },
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/$schema',
error => re(qr/^custom \$schema URIs are not supported \(must be one of: /),
},
],
},
'$schema, when set, must contain a recognizable URI',
);
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft2020-12';
cmp_result(
evaluate(
true,
{
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/$schema',
error => '"$schema" indicates a different version than that requested by $JSON::Schema::Tiny::SPECIFICATION_VERSION',
},
],
},
'$SPECIFICATION_VERSION cannot be inconsistent with $schema keyword value',
);
};
subtest 'specification aliases' => sub {
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = '2020-12';
ok(evaluate(1, {}), 'evaluate with 2020-12');
is(
$JSON::Schema::Tiny::SPECIFICATION_VERSION,
'draft2020-12',
'2020-12 is accepted as an alias for draft2020-12',
);
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = '2019-09';
ok(evaluate(1, {}), 'evaluate with 2019-09');
is(
$JSON::Schema::Tiny::SPECIFICATION_VERSION,
'draft2019-09',
'2019-09 is accepted as an alias for draft2019-09',
);
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = '7';
ok(evaluate(1, {}), 'evaluate with 7');
is(
$JSON::Schema::Tiny::SPECIFICATION_VERSION,
'draft7',
'7 is accepted as an alias for draft7',
);
};
subtest '$ref and older specification versions' => sub {
cmp_result(
evaluate(
true,
{
'$schema' => 'http://json-schema.org/draft-07/schema#',
'$ref' => '#/definitions/foo',
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/$schema',
error => '$schema and $ref cannot be used together in older drafts',
},
],
},
'$schema and $ref cannot be used together, when $schema is too old',
);
};
subtest '<= draft7: $ref in combination with any other keyword causes the other keywords to be ignored' => sub {
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft7';
cmp_result(
evaluate(
1,
{
allOf => [
true,
{
'$ref' => '#/allOf/0',
maximum => 0,
},
],
}
),
{
valid => true,
},
'keywords adjacent to $ref are not evaluated',
);
};
subtest '$ref adjacent to a path used in a $ref' => sub {
todo 'fixing this requires traversing the schema to mark which locations are unusable' => sub {
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft7';
cmp_result(
evaluate(
true,
{
allOf => [
true,
{
anyOf => [ false, true ],
'$ref' => '#/allOf/0',
},
{
# a reference that cannot be resolved
'$ref' => '#/allOf/1/anyOf/1',
},
],
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/$ref',
error => 'EXCEPTION: unable to find resource #/definitions/bar/anyOf/1',
},
],
},
'the presence of $ref also kills the use of other $refs to adjacent locations',
);
}};
subtest '$defs support' => sub {
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft7';
cmp_result(
evaluate(
1,
my $schema = {
'$defs' => 1,
allOf => [ { '$ref' => '#/$defs/foo' } ],
}
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/0/$ref',
error => 'EXCEPTION: unable to find resource #/$defs/foo',
},
],
},
'$defs is not recognized in <= draft7',
);
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft2019-09';
cmp_result(
evaluate(1, $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/$defs',
error => '$defs value is not an object',
},
],
},
'$defs is supported in > draft7',
);
};
subtest 'definitions support' => sub {
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft2019-09';
cmp_result(
evaluate(
1,
my $schema = {
definitions => 1,
allOf => [ { '$ref' => '#/definitions/foo' } ],
}
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/allOf/0/$ref',
error => 'EXCEPTION: unable to find resource #/definitions/foo',
},
],
},
'definitions is not recognized in >= draft2019-09',
);
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft7';
cmp_result(
evaluate(1, $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/definitions',
error => 'definitions value is not an object',
},
],
},
'definitions is supported in <= draft7',
);
};
subtest 'dependencies, dependentRequired, dependentSchemas' => sub {
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft2019-09';
my $dependencies_schema;
my @warnings = warnings {
cmp_result(
evaluate(
{ alpha => 1, beta => 2 },
$dependencies_schema = {
dependencies => {
alpha => [ qw(a b c) ],
beta => false,
},
}
),
{ valid => true },
'dependencies is not recognized in >= draft2019-09',
);
};
cmp_result(
\@warnings,
[ re(qr/^no-longer-supported "dependencies" keyword present/) ],
'warned when using no-longer-supported keyword',
);
cmp_result(
evaluate(
{ alpha => 1, beta => 2 },
my $dependentRequired_schema = {
dependentRequired => {
alpha => [ qw(a b c) ],
},
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/dependentRequired/alpha',
error => 'missing properties: a, b, c',
},
{
instanceLocation => '',
keywordLocation => '/dependentRequired',
error => 'not all dependencies are satisfied',
},
],
},
'dependentRequired is supported in >= draft2019-09',
);
cmp_result(
evaluate(
{ alpha => 1, beta => 2 },
my $dependentSchemas_schema = {
dependentSchemas => {
beta => false,
},
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/dependentSchemas/beta',
error => 'subschema is false',
},
{
instanceLocation => '',
keywordLocation => '/dependentSchemas',
error => 'not all dependencies are satisfied',
},
],
},
'dependentSchemas is supported in >= draft2019-09',
);
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft7';
cmp_result(
evaluate(
{ alpha => 1, beta => 2 },
$dependencies_schema,
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/dependencies/alpha',
error => 'missing properties: a, b, c',
},
{
instanceLocation => '',
keywordLocation => '/dependencies/beta',
error => 'subschema is false',
},
{
instanceLocation => '',
keywordLocation => '/dependencies',
error => 'not all dependencies are satisfied',
},
],
},
'dependencies is supported in <= draft7',
);
cmp_result(
evaluate(
{ alpha => 1, beta => 2 },
$dependentRequired_schema,
),
{ valid => true },
'dependentRequired is not recognized in <= draft7',
);
cmp_result(
evaluate(
{ alpha => 1, beta => 2 },
$dependentSchemas_schema,
),
{ valid => true },
'dependentSchemas is not recognized in <= draft7',
);
};
subtest 'prefixItems, items and additionalItems' => sub {
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft2020-12';
cmp_result(
evaluate(
[ 1, 2 ],
{
prefixItems => [ { maximum => 0 } ],
items => { maximum => 1 },
}
),
{
valid => false,
errors => [
{
instanceLocation => '/0',
keywordLocation => '/prefixItems/0/maximum',
error => 'value is larger than 0',
},
{
instanceLocation => '',
keywordLocation => '/prefixItems',
error => 'not all items are valid',
},
{
instanceLocation => '/1',
keywordLocation => '/items/maximum',
error => 'value is larger than 1',
},
{
instanceLocation => '',
keywordLocation => '/items',
error => 'subschema is not valid against all additional items',
},
],
},
'prefixItems+items works when $SPECIFICATION_VERSION is set to draft2020-12',
);
cmp_result(
evaluate(
[ 1 ],
{
items => [ { maximum => 0 } ],
},
),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/items',
error => 'array form of "items" not supported in draft2020-12',
},
],
},
'array form of items not supported when $SPECIFICATION_VERSION specifies draft2020-12',
);
my @warnings = warnings {
cmp_result(
evaluate(
[ 1 ],
{ additionalItems => false },
),
{ valid => true },
'additionalItems not recognized when $SPECIFICATION_VERSION specifies draft2020-12',
);
};
cmp_result(
\@warnings,
[ re(qr/^no-longer-supported "additionalItems" keyword present/) ],
'warned when using no-longer-supported keyword',
);
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = 'draft2019-09';
cmp_result(
evaluate(
[ 1 ],
{ prefixItems => [ { maximum => 0 } ] }
),
{ valid => true },
'prefixItems not supported when $SPECIFICATION_VERSION specifies other than draft2020-12',
);
local $JSON::Schema::Tiny::SPECIFICATION_VERSION;
cmp_result(
evaluate(
[ 1, 2, 3 ],
{
prefixItems => [ { maximum => 0 } ],
items => [ { maximum => 1 } ],
},
),
{
valid => false,
errors => [
{
instanceLocation => '/0',
keywordLocation => '/prefixItems/0/maximum',
error => 'value is larger than 0',
},
{
instanceLocation => '',
keywordLocation => '/prefixItems',
error => 'not all items are valid',
},
],
},
'prefixItems + array-based items',
);
cmp_result(
evaluate(
[ 1, 2, 3 ],
{
prefixItems => [ { maximum => 0 } ],
additionalItems => { maximum => 1 },
},
),
{
valid => false,
errors => [
{
instanceLocation => '/0',
keywordLocation => '/prefixItems/0/maximum',
error => 'value is larger than 0',
},
{
instanceLocation => '',
keywordLocation => '/prefixItems',
error => 'not all items are valid',
},
{
instanceLocation => '/1',
keywordLocation => '/additionalItems/maximum',
error => 'value is larger than 1',
},
{
instanceLocation => '/2',
keywordLocation => '/additionalItems/maximum',
error => 'value is larger than 1',
},
{
instanceLocation => '',
keywordLocation => '/additionalItems',
error => 'subschema is not valid against all additional items',
},
],
},
'prefixItems + additionalItems',
);
cmp_result(
evaluate(
[ 1, 2, 3 ],
{
prefixItems => [ { maximum => 0 } ],
items => { maximum => 1 },
}
),
{
valid => false,
errors => [
{
instanceLocation => '/0',
keywordLocation => '/prefixItems/0/maximum',
error => 'value is larger than 0',
},
{
instanceLocation => '',
keywordLocation => '/prefixItems',
error => 'not all items are valid',
},
{
instanceLocation => '/1',
keywordLocation => '/items/maximum',
error => 'value is larger than 1',
},
{
instanceLocation => '/2',
keywordLocation => '/items/maximum',
error => 'value is larger than 1',
},
{
instanceLocation => '',
keywordLocation => '/items',
error => 'subschema is not valid against all additional items',
},
],
},
'prefixItems + schema-based items',
);
cmp_result(
evaluate(
[ 1, 2, 3 ],
{
items => { maximum => 0 },
additionalItems => { maximum => 1 },
}
),
{
valid => false,
errors => [
(map +{
instanceLocation => '/'.$_,
keywordLocation => '/items/maximum',
error => 'value is larger than 0',
}, (0..2)),
{
instanceLocation => '',
keywordLocation => '/items',
error => 'subschema is not valid against all items',
},
],
},
'schema-based items + additionalItems, failure case',
);
cmp_result(
evaluate(
[ 1, 2, 3 ],
{
items => { maximum => 5 },
additionalItems => { maximum => 0 },
}
),
{ valid => true },
'schema-based items + additionalItems, passing case',
);
};
subtest '$id' => sub {
my $schema = { '$id' => '#/foo/bar/baz' };
foreach my $version (qw(draft2020-12 draft2019-09 draft7)) {
local $JSON::Schema::Tiny::SPECIFICATION_VERSION = $version;
cmp_result(
evaluate(1, $schema),
{
valid => false,
errors => [
{
instanceLocation => '',
keywordLocation => '/$id',
error => ($version eq 'draft7'
? '$id value does not match required syntax'
: '$id value "#/foo/bar/baz" cannot have a non-empty fragment'),
},
],
},
'json pointer fragment is valid in $id in '.$version,
);
}
};
had_no_warnings() if $ENV{AUTHOR_TESTING};
done_testing;
zzz-acceptance-draft7.t 100640 000766 000024 12610 15221771652 20647 0 ustar 00ether staff 000000 000000 JSON-Schema-Tiny-0.034/t # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et :
use strict;
use warnings;
use if $ENV{AUTHOR_TESTING}, strictures => version => 2;
use 5.020;
use stable 0.031 'postderef';
use experimental 'signatures';
use if $ENV{AUTHOR_TESTING}, autovivification => warn => qw(fetch store exists delete);
use if "$]" >= 5.022, experimental => 're_strict';
no if "$]" >= 5.031009, feature => 'indirect';
no if "$]" >= 5.033001, feature => 'multidimensional';
no if "$]" >= 5.033006, feature => 'bareword_filehandles';
use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8
use List::Util 1.50 'head';
use Config;
use lib 't/lib';
use Acceptance;
BEGIN {
my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING);
plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with '
.join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1'
if not grep $ENV{$_}, @variables;
}
my $version = 'draft7';
acceptance_tests(
acceptance => {
specification => $version,
skip_dir => 'optional/format',
},
evaluator => {
specification_version => $version,
},
output_file => $version.'-acceptance.txt',
test => {
$ENV{NO_TODO} ? () : ( todo_tests => [
# unsupported keywords or subfeatures
{ file => [ qw(
id.json
refRemote.json
vocabulary.json
optional/content.json
) ] },
{ file => 'definitions.json', group_description => [ 'valid definition', 'validate definition against metaschema' ] },
# various edge cases that are difficult to accomodate
$Config{ivsize} < 8 ? { file => 'multipleOf.json', group_description => 'small multiple of large integer', test_description => 'any integer is a multiple of 1e-8' } : (),
# only same-document, same-base JSON pointers are supported in $ref
{ file => 'ref.json', group_description => [
'$ref prevents a sibling $id from changing the base uri',
'remote ref, containing refs itself',
'Recursive references between schemas',
'Location-independent identifier',
'Reference an anchor with a non-relative URI',
'Location-independent identifier with base URI change in subschema',
'refs with relative uris and defs',
'relative refs with absolute uris and defs',
'$id must be resolved against nearest parent, not just immediate parent',
'URN base URI with URN and anchor ref',
'ref to if',
'ref to then',
'ref to else',
'ref with absolute-path-reference',
] },
{ file => ['unknownKeyword.json', 'optional/unknownKeyword.json'], group_description => '$id inside an unknown keyword is not a real identifier', test_description => 'type matches second anyOf, which has a real schema in it' },
{ file => 'optional/id.json', '$id inside an enum is not a real identifier', 'exact match to enum, and type matches' },
{ file => 'optional/cross-draft.json', group_description => 'refs to future drafts are processed as future drafts' },
# various edge cases that are difficult to accomodate
{ file => 'optional/ecmascript-regex.json', group_description => '\w in patterns matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] },
{ file => 'optional/ecmascript-regex.json', group_description => '\d in pattern matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' },
{ file => 'optional/ecmascript-regex.json', group_description => '\w in patternProperties matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] },
{ file => 'optional/ecmascript-regex.json', group_description => '\d in patternProperties matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' },
{ file => 'optional/ecmascript-regex.json', group_description => [ 'ECMA 262 \d matches ascii digits only', 'ECMA 262 \D matches everything but ascii digits', 'ECMA 262 \w matches ascii letters only', 'ECMA 262 \W matches everything but ascii letters' ] }, # TODO, see test suite PR#505
{ file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \s matches whitespace', test_description => 'zero-width whitespace matches' },
{ file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \S matches everything but whitespace', test_description => 'zero-width whitespace does not match' },
"$]" < 5.017001 ? ( # in Unicode 6.2, vertical tab was added to \s
{ file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \s matches whitespace', test_description => 'Line tabulation matches' },
{ file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \S matches everything but whitespace', test_description => 'Line tabulation does not match' },
) : (),
] ),
},
);
END {
diag <