├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── settings.json ├── ARCHITECTURE.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENCE.md ├── README.md ├── build.config.ts ├── eslint.config.mjs ├── example ├── README.md ├── index.ts ├── package.json ├── person.d.ts ├── person.json ├── tsconfig.json └── yarn.lock ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── src ├── generator.ts ├── index.ts ├── linker.ts ├── normalizer.ts ├── optimizer.ts ├── optionValidator.ts ├── parser.ts ├── resolver.ts ├── types │ ├── AST.ts │ └── JSONSchema.ts ├── typesOfSchema.ts ├── utils.ts └── validator.ts ├── test ├── __fixtures__ │ ├── https---schema.management.azure.com-schemas-2014-04-01-Microsoft.Insights.ManuallyAuthored.json │ ├── https---schema.management.azure.com-schemas-2014-04-01-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2014-04-01-Microsoft.Sql.json │ ├── https---schema.management.azure.com-schemas-2014-04-01-preview-Microsoft.Cache.json │ ├── https---schema.management.azure.com-schemas-2014-04-01-preview-Microsoft.VisualStudio.json │ ├── https---schema.management.azure.com-schemas-2014-08-01-preview-Microsoft.Scheduler.json │ ├── https---schema.management.azure.com-schemas-2014-09-01-Microsoft.EventHub.json │ ├── https---schema.management.azure.com-schemas-2014-09-01-Microsoft.NotificationHubs.json │ ├── https---schema.management.azure.com-schemas-2015-01-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2015-01-01-Microsoft.Resources.json │ ├── https---schema.management.azure.com-schemas-2015-01-01-Sendgrid.Email.json │ ├── https---schema.management.azure.com-schemas-2015-02-01-preview-Microsoft.Logic.json │ ├── https---schema.management.azure.com-schemas-2015-02-28-Microsoft.Search.json │ ├── https---schema.management.azure.com-schemas-2015-03-01-preview-Microsoft.HDInsight.json │ ├── https---schema.management.azure.com-schemas-2015-03-20-Microsoft.OperationalInsights.json │ ├── https---schema.management.azure.com-schemas-2015-04-01-Microsoft.DomainRegistration.json │ ├── https---schema.management.azure.com-schemas-2015-04-01-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2015-04-08-Microsoft.DocumentDB.json │ ├── https---schema.management.azure.com-schemas-2015-05-01-Microsoft.Insights.Application.json │ ├── https---schema.management.azure.com-schemas-2015-05-01-preview-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2015-05-01-preview-Microsoft.Sql.json │ ├── https---schema.management.azure.com-schemas-2015-05-04-preview-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2015-05-21-preview-Microsoft.DevTestLab.json │ ├── https---schema.management.azure.com-schemas-2015-06-01-Microsoft.Cdn.json │ ├── https---schema.management.azure.com-schemas-2015-06-01-Microsoft.KeyVault.Secrets.json │ ├── https---schema.management.azure.com-schemas-2015-06-01-Microsoft.KeyVault.json │ ├── https---schema.management.azure.com-schemas-2015-06-01-preview-Microsoft.Security.json │ ├── https---schema.management.azure.com-schemas-2015-06-15-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2015-08-01-Microsoft.Cache.json │ ├── https---schema.management.azure.com-schemas-2015-08-01-Microsoft.CertificateRegistration.json │ ├── https---schema.management.azure.com-schemas-2015-08-01-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2015-08-01-Microsoft.EventHub.json │ ├── https---schema.management.azure.com-schemas-2015-08-01-Microsoft.ServiceBus.json │ ├── https---schema.management.azure.com-schemas-2015-08-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2015-08-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2015-08-01-preview-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2015-08-19-Microsoft.Search.json │ ├── https---schema.management.azure.com-schemas-2015-08-31-preview-Microsoft.ManagedIdentity.json │ ├── https---schema.management.azure.com-schemas-2015-10-01-Microsoft.Media.json │ ├── https---schema.management.azure.com-schemas-2015-10-01-preview-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2015-10-01-preview-Microsoft.DataLakeAnalytics.json │ ├── https---schema.management.azure.com-schemas-2015-10-01-preview-Microsoft.DataLakeStore.json │ ├── https---schema.management.azure.com-schemas-2015-10-31-Microsoft.Automation.json │ ├── https---schema.management.azure.com-schemas-2015-11-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2015-11-01-preview-Microsoft.OperationalInsights.json │ ├── https---schema.management.azure.com-schemas-2015-11-01-preview-Microsoft.OperationsManagement.json │ ├── https---schema.management.azure.com-schemas-2015-12-01-Microsoft.Batch.json │ ├── https---schema.management.azure.com-schemas-2016-01-01-Microsoft.Scheduler.json │ ├── https---schema.management.azure.com-schemas-2016-01-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2016-01-29-Microsoft.PowerBI.json │ ├── https---schema.management.azure.com-schemas-2016-02-01-Microsoft.Resources.json │ ├── https---schema.management.azure.com-schemas-2016-02-01-preview-Microsoft.CognitiveServices.json │ ├── https---schema.management.azure.com-schemas-2016-02-03-Microsoft.Devices.json │ ├── https---schema.management.azure.com-schemas-2016-03-01-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2016-03-01-Microsoft.NotificationHubs.json │ ├── https---schema.management.azure.com-schemas-2016-03-01-Microsoft.Scheduler.json │ ├── https---schema.management.azure.com-schemas-2016-03-01-Microsoft.ServiceFabric.json │ ├── https---schema.management.azure.com-schemas-2016-03-01-Microsoft.StreamAnalytics.json │ ├── https---schema.management.azure.com-schemas-2016-03-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2016-03-30-Microsoft.ContainerService.json │ ├── https---schema.management.azure.com-schemas-2016-03-30-Microsoft.DataCatalog.json │ ├── https---schema.management.azure.com-schemas-2016-03-30-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-04-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2016-04-01-Microsoft.Cache.json │ ├── https---schema.management.azure.com-schemas-2016-04-01-Microsoft.MachineLearning.json │ ├── https---schema.management.azure.com-schemas-2016-04-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-04-02-Microsoft.Cdn.json │ ├── https---schema.management.azure.com-schemas-2016-04-30-preview-Microsoft.Compute.Extensions.json │ ├── https---schema.management.azure.com-schemas-2016-04-30-preview-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2016-05-01-preview-Microsoft.MachineLearning.json │ ├── https---schema.management.azure.com-schemas-2016-05-15-Microsoft.DevTestLab.json │ ├── https---schema.management.azure.com-schemas-2016-05-16-Microsoft.AnalysisServices.json │ ├── https---schema.management.azure.com-schemas-2016-06-01-Microsoft.Logic.json │ ├── https---schema.management.azure.com-schemas-2016-06-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-06-01-Microsoft.RecoveryServices.json │ ├── https---schema.management.azure.com-schemas-2016-06-01-Microsoft.RecoveryServices.legacy.json │ ├── https---schema.management.azure.com-schemas-2016-06-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2016-06-27-preview-Microsoft.ContainerRegistry.json │ ├── https---schema.management.azure.com-schemas-2016-07-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-07-01-Microsoft.Relay.json │ ├── https---schema.management.azure.com-schemas-2016-07-07-Microsoft.ApiManagement.json │ ├── https---schema.management.azure.com-schemas-2016-08-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-08-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2016-09-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2016-09-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-09-01-Microsoft.Resources.json │ ├── https---schema.management.azure.com-schemas-2016-09-01-Microsoft.ServiceFabric.json │ ├── https---schema.management.azure.com-schemas-2016-09-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2016-09-01-preview-Microsoft.Solutions.resourcesolutions.json │ ├── https---schema.management.azure.com-schemas-2016-10-01-Microsoft.KeyVault.json │ ├── https---schema.management.azure.com-schemas-2016-10-01-Microsoft.Logic.json │ ├── https---schema.management.azure.com-schemas-2016-10-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-10-01-Microsoft.StorSimple.1200.json │ ├── https---schema.management.azure.com-schemas-2016-11-01-Microsoft.DataLakeAnalytics.json │ ├── https---schema.management.azure.com-schemas-2016-11-01-Microsoft.DataLakeStore.json │ ├── https---schema.management.azure.com-schemas-2016-11-01-Microsoft.ImportExport.json │ ├── https---schema.management.azure.com-schemas-2016-11-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-12-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2016-12-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2016-12-01-Microsoft.RecoveryServices.Backup.json │ ├── https---schema.management.azure.com-schemas-2016-12-01-Microsoft.RecoveryServices.json │ ├── https---schema.management.azure.com-schemas-2017-01-01-Microsoft.AAD.json │ ├── https---schema.management.azure.com-schemas-2017-01-01-Microsoft.MachineLearning.json │ ├── https---schema.management.azure.com-schemas-2017-01-01-preview-Microsoft.Maps.json │ ├── https---schema.management.azure.com-schemas-2017-03-01-Microsoft.ApiManagement.json │ ├── https---schema.management.azure.com-schemas-2017-03-01-Microsoft.ContainerRegistry.json │ ├── https---schema.management.azure.com-schemas-2017-03-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2017-03-01-preview-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2017-03-01-preview-Microsoft.Sql.json │ ├── https---schema.management.azure.com-schemas-2017-03-30-Microsoft.Compute.Extensions.json │ ├── https---schema.management.azure.com-schemas-2017-03-30-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2017-04-01-Microsoft.EventHub.json │ ├── https---schema.management.azure.com-schemas-2017-04-01-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2017-04-01-Microsoft.NotificationHubs.json │ ├── https---schema.management.azure.com-schemas-2017-04-01-Microsoft.Relay.json │ ├── https---schema.management.azure.com-schemas-2017-04-01-Microsoft.ServiceBus.json │ ├── https---schema.management.azure.com-schemas-2017-04-18-Microsoft.CognitiveServices.json │ ├── https---schema.management.azure.com-schemas-2017-05-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2017-05-01-preview-Microsoft.MachineLearningExperimentation.json │ ├── https---schema.management.azure.com-schemas-2017-05-10-Microsoft.Resources.json │ ├── https---schema.management.azure.com-schemas-2017-05-15-preview-Microsoft.Automation.json │ ├── https---schema.management.azure.com-schemas-2017-06-01-Microsoft.AAD.json │ ├── https---schema.management.azure.com-schemas-2017-06-01-Microsoft.AzureStack.json │ ├── https---schema.management.azure.com-schemas-2017-06-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2017-06-01-Microsoft.StorSimple.8000.json │ ├── https---schema.management.azure.com-schemas-2017-06-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2017-06-01-preview-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2017-06-01-preview-Microsoft.ContainerRegistry.json │ ├── https---schema.management.azure.com-schemas-2017-06-15-preview-Microsoft.EventGrid.json │ ├── https---schema.management.azure.com-schemas-2017-07-01-Microsoft.ContainerService.json │ ├── https---schema.management.azure.com-schemas-2017-07-01-Microsoft.Devices.json │ ├── https---schema.management.azure.com-schemas-2017-07-01-Microsoft.Logic.json │ ├── https---schema.management.azure.com-schemas-2017-07-01-Microsoft.RecoveryServices.json │ ├── https---schema.management.azure.com-schemas-2017-07-01-preview-Microsoft.ServiceFabric.json │ ├── https---schema.management.azure.com-schemas-2017-08-01-Microsoft.AnalysisServices.json │ ├── https---schema.management.azure.com-schemas-2017-08-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2017-08-01-preview-Microsoft.Genomics.json │ ├── https---schema.management.azure.com-schemas-2017-08-01-preview-Microsoft.Security.json │ ├── https---schema.management.azure.com-schemas-2017-08-15-Microsoft.NetApp.json │ ├── https---schema.management.azure.com-schemas-2017-08-21-preview-Microsoft.Devices.Provisioning.json │ ├── https---schema.management.azure.com-schemas-2017-09-01-Microsoft.Batch.json │ ├── https---schema.management.azure.com-schemas-2017-09-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2017-09-01-preview-Microsoft.DataFactory.json │ ├── https---schema.management.azure.com-schemas-2017-09-07-privatepreview-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2017-09-15-preview-Microsoft.EventGrid.json │ ├── https---schema.management.azure.com-schemas-2017-10-01-Microsoft.Cache.json │ ├── https---schema.management.azure.com-schemas-2017-10-01-Microsoft.ContainerRegistry.json │ ├── https---schema.management.azure.com-schemas-2017-10-01-Microsoft.Insights.Application.json │ ├── https---schema.management.azure.com-schemas-2017-10-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2017-10-01-Microsoft.PowerBIDedicated.json │ ├── https---schema.management.azure.com-schemas-2017-10-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2017-10-01-preview-Microsoft.Sql.json │ ├── https---schema.management.azure.com-schemas-2017-11-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2017-11-15-Microsoft.Devices.Provisioning.json │ ├── https---schema.management.azure.com-schemas-2017-11-15-Microsoft.TimeSeriesInsights.json │ ├── https---schema.management.azure.com-schemas-2017-11-15-preview-Microsoft.DataMigration.json │ ├── https---schema.management.azure.com-schemas-2017-11-15-privatepreview-Microsoft.DataMigration.json │ ├── https---schema.management.azure.com-schemas-2017-12-01-Microsoft.Compute.Extensions.json │ ├── https---schema.management.azure.com-schemas-2017-12-01-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2017-12-01-Microsoft.DBforMySQL.json │ ├── https---schema.management.azure.com-schemas-2017-12-01-Microsoft.DBforPostgreSQL.json │ ├── https---schema.management.azure.com-schemas-2017-12-01-preview-Microsoft.DBforMySQL.json │ ├── https---schema.management.azure.com-schemas-2017-12-01-preview-Microsoft.DBforPostgreSQL.json │ ├── https---schema.management.azure.com-schemas-2018-01-01-Microsoft.ApiManagement.json │ ├── https---schema.management.azure.com-schemas-2018-01-01-Microsoft.EventGrid.json │ ├── https---schema.management.azure.com-schemas-2018-01-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-01-01-preview-Microsoft.EventHub.json │ ├── https---schema.management.azure.com-schemas-2018-01-01-preview-Microsoft.ServiceBus.json │ ├── https---schema.management.azure.com-schemas-2018-01-10-Microsoft.RecoveryServices.SiteRecovery.json │ ├── https---schema.management.azure.com-schemas-2018-01-15-Microsoft.Automation.json │ ├── https---schema.management.azure.com-schemas-2018-01-22-Microsoft.Devices.json │ ├── https---schema.management.azure.com-schemas-2018-01-31-Microsoft.Consumption.json │ ├── https---schema.management.azure.com-schemas-2018-02-01-Microsoft.CertificateRegistration.json │ ├── https---schema.management.azure.com-schemas-2018-02-01-Microsoft.DomainRegistration.json │ ├── https---schema.management.azure.com-schemas-2018-02-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-02-01-Microsoft.ServiceFabric.json │ ├── https---schema.management.azure.com-schemas-2018-02-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2018-02-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2018-02-01-preview-Microsoft.ContainerRegistry.json │ ├── https---schema.management.azure.com-schemas-2018-02-14-Microsoft.KeyVault.json │ ├── https---schema.management.azure.com-schemas-2018-02-14-preview-Microsoft.KeyVault.json │ ├── https---schema.management.azure.com-schemas-2018-03-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2018-03-01-Microsoft.BatchAI.json │ ├── https---schema.management.azure.com-schemas-2018-03-01-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2018-03-01-preview-Microsoft.MachineLearningServices.json │ ├── https---schema.management.azure.com-schemas-2018-03-01-preview-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2018-03-31-Microsoft.ContainerService.json │ ├── https---schema.management.azure.com-schemas-2018-04-01-Microsoft.Compute.Extensions.json │ ├── https---schema.management.azure.com-schemas-2018-04-01-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2018-04-01-Microsoft.ContainerInstance.json │ ├── https---schema.management.azure.com-schemas-2018-04-01-Microsoft.Devices.json │ ├── https---schema.management.azure.com-schemas-2018-04-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-04-16-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2018-05-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2018-05-01-Microsoft.BatchAI.json │ ├── https---schema.management.azure.com-schemas-2018-05-01-Microsoft.Maps.json │ ├── https---schema.management.azure.com-schemas-2018-05-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-05-01-Microsoft.Resources.json │ ├── https---schema.management.azure.com-schemas-2018-05-01-preview-Microsoft.EventGrid.json │ ├── https---schema.management.azure.com-schemas-2018-05-01-preview-Microsoft.Insights.Application.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-Microsoft.Compute.Extensions.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-Microsoft.DBforMariaDB.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-Microsoft.DataFactory.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-preview-Microsoft.ApiManagement.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-preview-Microsoft.HDInsight.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-preview-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-preview-Microsoft.ManagedServices.json │ ├── https---schema.management.azure.com-schemas-2018-06-01-preview-Microsoft.Sql.json │ ├── https---schema.management.azure.com-schemas-2018-06-17-preview-Microsoft.Insights.Application.json │ ├── https---schema.management.azure.com-schemas-2018-06-30-Microsoft.Automation.json │ ├── https---schema.management.azure.com-schemas-2018-07-01-Microsoft.Media.json │ ├── https---schema.management.azure.com-schemas-2018-07-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-07-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2018-08-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-08-15-preview-Microsoft.TimeSeriesInsights.json │ ├── https---schema.management.azure.com-schemas-2018-08-20-preview-Microsoft.HealthcareApis.json │ ├── https---schema.management.azure.com-schemas-2018-09-01-Microsoft.ContainerRegistry.json │ ├── https---schema.management.azure.com-schemas-2018-09-01-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2018-09-01-Microsoft.IotCentral.json │ ├── https---schema.management.azure.com-schemas-2018-09-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-09-01-preview-Microsoft.BareMetal.json │ ├── https---schema.management.azure.com-schemas-2018-09-01-preview-Microsoft.Migrate.MigrateProjects.json │ ├── https---schema.management.azure.com-schemas-2018-09-01-preview-Microsoft.ResourceGraph.json │ ├── https---schema.management.azure.com-schemas-2018-09-07-preview-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2018-09-15-preview-Microsoft.EventGrid.json │ ├── https---schema.management.azure.com-schemas-2018-10-01-Microsoft.Compute.Extensions.json │ ├── https---schema.management.azure.com-schemas-2018-10-01-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2018-10-01-Microsoft.ContainerInstance.json │ ├── https---schema.management.azure.com-schemas-2018-10-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-10-01-Microsoft.SignalRService.json │ ├── https---schema.management.azure.com-schemas-2018-11-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2018-11-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2018-11-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2018-11-19-Microsoft.MachineLearningServices.json │ ├── https---schema.management.azure.com-schemas-2018-11-30-Microsoft.ManagedIdentity.json │ ├── https---schema.management.azure.com-schemas-2018-12-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2019-01-01-Microsoft.ApiManagement.json │ ├── https---schema.management.azure.com-schemas-2019-01-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2019-01-01-Microsoft.EventGrid.json │ ├── https---schema.management.azure.com-schemas-2019-01-01-Microsoft.Security.json │ ├── https---schema.management.azure.com-schemas-2019-01-01-preview-Microsoft.Security.json │ ├── https---schema.management.azure.com-schemas-2019-01-21-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2019-02-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2019-02-01-preview-Microsoft.AppConfiguration.json │ ├── https---schema.management.azure.com-schemas-2019-02-01-preview-Microsoft.EventGrid.json │ ├── https---schema.management.azure.com-schemas-2019-03-01-Microsoft.Compute.Extensions.json │ ├── https---schema.management.azure.com-schemas-2019-03-01-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2019-03-01-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2019-03-01-Microsoft.Network.FrontDoor.json │ ├── https---schema.management.azure.com-schemas-2019-03-01-preview-Microsoft.ServiceFabric.json │ ├── https---schema.management.azure.com-schemas-2019-04-01-Microsoft.Network.FrontDoor.json │ ├── https---schema.management.azure.com-schemas-2019-04-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2019-04-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2019-04-01-Microsoft.VMwareCloudSimple.json │ ├── https---schema.management.azure.com-schemas-2019-05-01-Microsoft.MachineLearningServices.json │ ├── https---schema.management.azure.com-schemas-2019-05-01-Microsoft.NetApp.json │ ├── https---schema.management.azure.com-schemas-2019-05-01-Microsoft.Network.FrontDoor.json │ ├── https---schema.management.azure.com-schemas-2019-05-01-Microsoft.Resources.json │ ├── https---schema.management.azure.com-schemas-2019-05-15-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-Microsoft.ContainerService.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-Microsoft.EventGrid.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-Microsoft.MachineLearningServices.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-Microsoft.ManagedServices.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-Microsoft.Storage.json │ ├── https---schema.management.azure.com-schemas-2019-06-01-preview-Microsoft.Synapse.json │ ├── https---schema.management.azure.com-schemas-2019-07-01-Microsoft.Compute.Extensions.json │ ├── https---schema.management.azure.com-schemas-2019-07-01-Microsoft.Compute.json │ ├── https---schema.management.azure.com-schemas-2019-07-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2019-08-01-Microsoft.CertificateRegistration.json │ ├── https---schema.management.azure.com-schemas-2019-08-01-Microsoft.DomainRegistration.json │ ├── https---schema.management.azure.com-schemas-2019-08-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2019-08-01-Microsoft.Security.json │ ├── https---schema.management.azure.com-schemas-2019-08-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2019-08-01-preview-Microsoft.OperationalInsights.json │ ├── https---schema.management.azure.com-schemas-2019-08-01-preview-Microsoft.Peering.json │ ├── https---schema.management.azure.com-schemas-2019-09-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2019-09-01-Microsoft.KeyVault.json │ ├── https---schema.management.azure.com-schemas-2019-09-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2019-09-01-preview-Microsoft.Peering.json │ ├── https---schema.management.azure.com-schemas-2019-09-07-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2019-09-16-Microsoft.HealthcareApis.json │ ├── https---schema.management.azure.com-schemas-2019-09-16-preview-Microsoft.WindowsESU.json │ ├── https---schema.management.azure.com-schemas-2019-10-01-Microsoft.MachineLearning.json │ ├── https---schema.management.azure.com-schemas-2019-10-01-Microsoft.Migrate.Migrate.json │ ├── https---schema.management.azure.com-schemas-2019-10-01-Microsoft.Network.FrontDoor.json │ ├── https---schema.management.azure.com-schemas-2019-10-01-preview-Microsoft.Search.json │ ├── https---schema.management.azure.com-schemas-2019-10-17-preview-Microsoft.Insights.Application.json │ ├── https---schema.management.azure.com-schemas-2019-10-17-preview-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2019-11-01-Microsoft.MachineLearningServices.json │ ├── https---schema.management.azure.com-schemas-2019-11-01-Microsoft.Network.FrontDoor.json │ ├── https---schema.management.azure.com-schemas-2019-11-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2019-11-01-preview-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2019-11-04-preview-Microsoft.Quantum.json │ ├── https---schema.management.azure.com-schemas-2019-11-09-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2019-12-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2020-01-01-Microsoft.MachineLearningServices.json │ ├── https---schema.management.azure.com-schemas-2020-01-01-Microsoft.Network.FrontDoor.json │ ├── https---schema.management.azure.com-schemas-2020-01-01-Microsoft.Security.json │ ├── https---schema.management.azure.com-schemas-2020-01-01-preview-Microsoft.Peering.json │ ├── https---schema.management.azure.com-schemas-2020-02-01-preview-Microsoft.Maps.json │ ├── https---schema.management.azure.com-schemas-2020-02-02-preview-Microsoft.Insights.Application.json │ ├── https---schema.management.azure.com-schemas-2020-02-15-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2020-03-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2020-03-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2020-03-01-preview-Microsoft.DigitalTwins.json │ ├── https---schema.management.azure.com-schemas-2020-03-01-preview-Microsoft.Insights.Application.json │ ├── https---schema.management.azure.com-schemas-2020-04-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2020-04-01-preview-Microsoft.KeyVault.json │ ├── https---schema.management.azure.com-schemas-2020-05-01-Microsoft.Network.json │ ├── https---schema.management.azure.com-schemas-2020-05-01-preview-Microsoft.Insights.json │ ├── https---schema.management.azure.com-schemas-2020-06-01-Microsoft.CertificateRegistration.json │ ├── https---schema.management.azure.com-schemas-2020-06-01-Microsoft.DomainRegistration.json │ ├── https---schema.management.azure.com-schemas-2020-06-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2020-06-14-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2020-07-01-preview-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2020-08-20-preview-Microsoft.Communication.json │ ├── https---schema.management.azure.com-schemas-2020-09-01-Microsoft.Authorization.Resources.json │ ├── https---schema.management.azure.com-schemas-2020-09-01-Microsoft.CertificateRegistration.json │ ├── https---schema.management.azure.com-schemas-2020-09-01-Microsoft.DomainRegistration.json │ ├── https---schema.management.azure.com-schemas-2020-09-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2020-09-18-Microsoft.Kusto.json │ ├── https---schema.management.azure.com-schemas-2020-10-01-Microsoft.CertificateRegistration.json │ ├── https---schema.management.azure.com-schemas-2020-10-01-Microsoft.DomainRegistration.json │ ├── https---schema.management.azure.com-schemas-2020-10-01-Microsoft.Web.json │ ├── https---schema.management.azure.com-schemas-2020-12-01-Microsoft.CertificateRegistration.json │ ├── https---schema.management.azure.com-schemas-2020-12-01-Microsoft.DomainRegistration.json │ ├── https---schema.management.azure.com-schemas-2020-12-01-Microsoft.Web.json │ └── https---schema.management.azure.com-schemas-common-definitions.json ├── __snapshots__ │ └── e2e.test.ts.snap ├── e2e.test.ts ├── e2e │ ├── JSONSchema.ts │ ├── WithComment.ts │ ├── additionalProperties.1.ts │ ├── additionalProperties.2.ts │ ├── additionalProperties.3.ts │ ├── allOf.ts │ ├── anyOf.ts │ ├── anyOfRoot.ts │ ├── arrayAdditionalItems.ts │ ├── arrayMaxMinItems.ts │ ├── arrayOfEnum.ts │ ├── arrayOfSchema.2.ts │ ├── arrayOfSchema.ts │ ├── arrayOfType.ts │ ├── basics.ts │ ├── booleanSchema.1.ts │ ├── booleanSchema.2.ts │ ├── customName.1.ts │ ├── customType.ts │ ├── deep.ts │ ├── deprecated.ts │ ├── deterministicOrdering.ts │ ├── disjointType.ts │ ├── emptyDefinition.ts │ ├── emptyProperties.ts │ ├── emptySchema.ts │ ├── enum.2.ts │ ├── enum.3.ts │ ├── enum.4.ts │ ├── enum.ts │ ├── enumInArray.ts │ ├── enumValidation.1.ts │ ├── enumValidation.2.ts │ ├── extends.1a.ts │ ├── extends.1b.ts │ ├── extends.2a.ts │ ├── extends.2b.ts │ ├── extends.3.ts │ ├── intersection.1.ts │ ├── intersection.2.ts │ ├── intersection.3.ts │ ├── namedProperty.ts │ ├── notExtensible.ts │ ├── oneOf.ts │ ├── oneOfWithDupes.ts │ ├── optimize.2.ts │ ├── optimize.ts │ ├── options.arrayIgnoreMaxMinItems.ts │ ├── options.enableConstEnums.ts │ ├── options.format.ts │ ├── options.strictIndexSignatures.ts │ ├── options.style.ts │ ├── options.unreachableDefinitions.ts │ ├── patternProperties.1.ts │ ├── patternProperties.2.ts │ ├── patternProperties.3.ts │ ├── patternProperties.4.ts │ ├── patternProperties.5.ts │ ├── patternProperties.6.ts │ ├── patternProperties.7.ts │ ├── patternProperties.8.ts │ ├── realWorld.awsQuicksight.ts │ ├── realWorld.fhir.ts │ ├── realWorld.heroku.ts │ ├── realWorld.jsonStat.ts │ ├── realWorld.jsonschema.ts │ ├── realWorld.openapi.ts │ ├── realWorld.payloadCMS.ts │ ├── realWorld.schemaStore.1.ts │ ├── realWorld.swagger.2.ts │ ├── realWorld.swagger.ts │ ├── ref.1a.ts │ ├── ref.1b.ts │ ├── ref.2.ts │ ├── ref.3.ts │ ├── ref.4.ts │ ├── ref.5.ts │ ├── ref.6a.ts │ ├── ref.6b.ts │ ├── refWithCycle.1.ts │ ├── refWithCycle.2.ts │ ├── refWithCycle.3.ts │ ├── refWithCycle.4.ts │ ├── refWithCycle.5.ts │ ├── referencesShouldBeNormalized.ts │ ├── required.ts │ ├── reservedWords.1.ts │ ├── reservedWords.2.ts │ ├── safeTypeNames.ts │ ├── schemaTitleAsTypeName.ts │ ├── specialCharacters.ts │ ├── subSchema.ts │ ├── topLevelUnion.ts │ ├── tupleItemsWithTitles.ts │ ├── tupleRef.ts │ ├── typedArray.1.ts │ ├── typedArray.2.ts │ ├── unicode.ts │ ├── union.2.ts │ ├── union.3.ts │ ├── union.4.ts │ ├── union.ts │ ├── unionWithProperties.ts │ ├── unnamedSchema.ts │ ├── withDescription.ts │ └── withDescriptionNewlines.ts ├── http.ts ├── idempotence.test.ts ├── linker.test.ts ├── normalizer.test.ts ├── normalizer │ ├── addEmptyRequiredProperty.json │ ├── constToEnum.json │ ├── defaultAdditionalProperties.2.json │ ├── defaultAdditionalProperties.json │ ├── defaultID.1.json │ ├── defaultID.2.json │ ├── destructureUnaryTypes.json │ ├── emptyStringConstToEnum.json │ ├── nonObjectItems.json │ ├── normalizeDefs.json │ ├── normalizeExtends.json │ ├── normalizeID.json │ ├── redundantNull.json │ ├── removeEmptyExtends.1.json │ ├── removeEmptyExtends.2.json │ ├── removeMaxItems.1.json │ ├── removeMaxItems.2.json │ ├── removeMaxItems.3.json │ ├── schemaIgnoreMaxMinItems.json │ ├── schemaItems.json │ └── schemaMinItems.json ├── resources │ ├── BaseType.1.json │ ├── BaseType.2.json │ ├── DefinitionsOnly.json │ ├── Enum.json │ ├── MultiSchema │ │ ├── a.json │ │ └── b.json │ ├── MultiSchema2 │ │ ├── bar │ │ │ ├── SameDirName │ │ │ │ └── MultiSchema2 │ │ │ │ │ └── e.json │ │ │ └── b.json │ │ ├── foo │ │ │ └── a.json │ │ └── foobar │ │ │ ├── c.json │ │ │ └── fuzz │ │ │ └── d.json │ ├── Person.json │ ├── ReferencedCombinationType.json │ ├── ReferencedType.json │ ├── ReferencedTypeNotNormalized.json │ ├── ReferencedTypeWithoutID.json │ ├── ReferencedTypeWithoutIDConflict.json │ ├── cycle.3.json │ ├── cycle.4.json │ ├── extends │ │ ├── Circle.json │ │ ├── Shape.json │ │ └── Square.json │ └── other │ │ └── ReferencingType.json └── utils.test.ts └── tsconfig.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: [20.x] 13 | os: [ubuntu-latest, windows-latest, macOS-latest] 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | - run: corepack enable 22 | - name: Install dependencies 23 | run: pnpm install 24 | - name: Run tests 25 | run: pnpm test 26 | env: 27 | CI: true 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | .pnp.* 4 | .yarn/* 5 | !.yarn/patches 6 | !.yarn/plugins 7 | !.yarn/releases 8 | !.yarn/sdks 9 | !.yarn/versions 10 | npm-debug.log 11 | node_modules 12 | *.js 13 | *.map 14 | dist/ 15 | dist_tests/ 16 | package-lock.json 17 | yarn-error.log 18 | tests/__fitxtures__/ 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enable the ESlint flat config support 3 | "eslint.experimental.useFlatConfig": true, 4 | 5 | // Disable the default formatter, use eslint instead 6 | "prettier.enable": false, 7 | "editor.formatOnSave": false, 8 | 9 | // Auto fix 10 | "editor.codeActionsOnSave": { 11 | "source.fixAll.eslint": "explicit", 12 | "source.organizeImports": "never" 13 | }, 14 | 15 | // Silent the stylistic rules in you IDE, but still auto fix them 16 | "eslint.rules.customizations": [ 17 | { "rule": "style/*", "severity": "off" }, 18 | { "rule": "format/*", "severity": "off" }, 19 | { "rule": "*-indent", "severity": "off" }, 20 | { "rule": "*-spacing", "severity": "off" }, 21 | { "rule": "*-spaces", "severity": "off" }, 22 | { "rule": "*-order", "severity": "off" }, 23 | { "rule": "*-dangle", "severity": "off" }, 24 | { "rule": "*-newline", "severity": "off" }, 25 | { "rule": "*quotes", "severity": "off" }, 26 | { "rule": "*semi", "severity": "off" } 27 | ], 28 | 29 | // Enable eslint for all supported languages 30 | "eslint.validate": [ 31 | "javascript", 32 | "javascriptreact", 33 | "typescript", 34 | "typescriptreact", 35 | "vue", 36 | "html", 37 | "markdown", 38 | "json", 39 | "jsonc", 40 | "yaml", 41 | "toml", 42 | "astro" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | json-schema-to-typescript compiles files from JSONSchema to TypeScript in distinct phases: 2 | 3 | #### 1. Validator 4 | 5 | TODO use an external validation library 6 | 7 | #### 2. Dereferencer 8 | 9 | Resolves referenced schemas (in the file, on the local filesystem, or over the network). 10 | 11 | #### 3. Linker 12 | 13 | Adds links back from each node in a schema to its parent (available via the `Parent` symbol on each node), for convenience. 14 | 15 | #### 4. Normalizer 16 | 17 | Normalizes input schemas so the parser can make more assumptions about schemas' properties and values. 18 | 19 | #### 5. Parser 20 | 21 | Parses JSONSchema to an intermediate representation for easy code generation. 22 | 23 | #### 6. Optimizer 24 | 25 | Optimizes the IR to produce concise and readable TypeScript in step (6). 26 | 27 | #### 7. Generator 28 | 29 | Converts the intermediate respresentation to TypeScript code. 30 | 31 | #### 8. Formatter 32 | 33 | Formats the code so it is properly indented, etc. 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | - Be sure to add a test for each change you make 4 | 5 | ## Tips 6 | 7 | - Use `npm run tdd` to compile and re-run tests when a file is modified 8 | - Use `VERBOSE=true npm run tdd` to add logging output to the above command 9 | - Add `export let only=true` to a test in test/e2e to just run that test 10 | - Add `export let exclude=true` to a test (or, add `.ignore` to its filename) in test/e2e to not run that test 11 | - To debug a test, with breakpoints: 12 | 1. Run `npm run pretest` 13 | 2. Follow the instructions [here](https://github.com/avajs/ava/blob/master/docs/recipes/debugging-with-vscode.md) for VSCode, [here](https://github.com/avajs/ava/blob/master/docs/recipes/debugging-with-webstorm.md) for Webstorm, or [here](https://github.com/avajs/ava/blob/master/docs/recipes/debugging-with-chrome-devtools.md) for Chrome Devtools 14 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | Copyright 2022 Boris Cherny 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # json-schema-to-typescript-lite 2 | 3 | > Compile json schema to typescript typings 4 | 5 | A fork of [json-schema-to-typescript](https://github.com/bcherny/json-schema-to-typescript) with lighter dependencies and a few tweaks. 6 | 7 | - Remove Prettier integrations - it now generates better formatted code by default, and you can always use Prettier manually afterward if needed 8 | - Replace `lodash` with bundled `lodash-es` for smaller bundle size 9 | - Remove CLI support and the dependencies - this package aims to have programmatic usage only 10 | - Modern build with ESNext target 11 | - ESM & CJS dual module support 12 | - Support [`customName` options](https://github.com/bcherny/json-schema-to-typescript/pull/585) 13 | -------------------------------------------------------------------------------- /build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild' 2 | 3 | export default defineBuildConfig({ 4 | entries: [ 5 | 'src/index.ts', 6 | ], 7 | rollup: { 8 | emitCJS: true, 9 | inlineDependencies: true, 10 | }, 11 | declaration: true, 12 | externals: [ 13 | 'json-schema', 14 | ], 15 | }) 16 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config' 2 | 3 | export default antfu( 4 | { 5 | ignores: [ 6 | 'test/__fixtures__/**', 7 | ], 8 | }, 9 | ) 10 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # json-schema-to-typescript example 2 | 3 | ## How to run the demo 4 | 5 | ```sh 6 | # Using Yarn: 7 | yarn 8 | yarn build 9 | 10 | # Or, using NPM: 11 | npm install 12 | npm run build 13 | ``` 14 | 15 | This will compile the example index.ts to JavaScript, and generate person.d.ts from person.json. 16 | -------------------------------------------------------------------------------- /example/index.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from 'node:fs' 2 | import { compile } from 'json-schema-to-typescript-lite' 3 | 4 | async function generate() { 5 | writeFileSync('person.d.ts', await compile(JSON.parse(readFileSync('person.json', 'utf-8')), 'Person')) 6 | } 7 | 8 | generate() 9 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-schema-to-typescript-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "author": "Boris Cherny (http://performancejs.com/)", 6 | "license": "MIT", 7 | "homepage": "https://github.com/bcherny/json-schema-to-typescript#readme", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/bcherny/json-schema-to-typescript.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/bcherny/json-schema-to-typescript/issues" 14 | }, 15 | "main": "index.js", 16 | "typings": "index.d.ts", 17 | "scripts": { 18 | "build": "npm run clean && tsc && node index.js", 19 | "clean": "rm -f index.js index.d.ts person.d.ts" 20 | }, 21 | "dependencies": { 22 | "@types/node": "^22.3.0", 23 | "json-schema-to-typescript-lite": "workspace:*", 24 | "typescript": "3.8.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/person.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was automatically generated by json-schema-to-typescript. 3 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 4 | * and run json-schema-to-typescript to regenerate this file. 5 | */ 6 | 7 | export interface Person { 8 | firstName: string 9 | lastName: string 10 | /** 11 | * Age in years 12 | */ 13 | age?: number 14 | hairColor?: 'black' | 'brown' | 'blue' 15 | [k: string]: unknown 16 | } 17 | -------------------------------------------------------------------------------- /example/person.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Person", 3 | "type": "object", 4 | "properties": { 5 | "firstName": { 6 | "type": "string" 7 | }, 8 | "lastName": { 9 | "type": "string" 10 | }, 11 | "age": { 12 | "description": "Age in years", 13 | "type": "integer", 14 | "minimum": 0 15 | }, 16 | "hairColor": { 17 | "enum": ["black", "brown", "blue"], 18 | "type": "string" 19 | } 20 | }, 21 | "required": ["firstName", "lastName"] 22 | } 23 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "lib": [ 5 | "es2015" 6 | ], 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "strictNullChecks": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitAny": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "declaration": false, 15 | "outDir": ".", 16 | "preserveConstEnums": true, 17 | "sourceMap": true 18 | }, 19 | "files": [ 20 | "./index.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-schema-to-typescript-lite", 3 | "type": "module", 4 | "version": "14.1.0", 5 | "packageManager": "pnpm@9.7.1", 6 | "description": "Lite version of json-schema-to-typescript", 7 | "author": "Boris Cherny (http://performancejs.com/)", 8 | "license": "MIT", 9 | "homepage": "https://github.com/bcherny/json-schema-to-typescript#readme", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/bcherny/json-schema-to-typescript.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/bcherny/json-schema-to-typescript/issues" 16 | }, 17 | "keywords": [ 18 | "json", 19 | "schema", 20 | "typescript", 21 | "compile", 22 | "transpile", 23 | "api", 24 | "interface", 25 | "typing", 26 | "share" 27 | ], 28 | "exports": { 29 | ".": { 30 | "import": "./dist/index.mjs", 31 | "require": "./dist/index.cjs" 32 | } 33 | }, 34 | "main": "./dist/index.mjs", 35 | "module": "./dist/index.mjs", 36 | "types": "./dist/index.d.ts", 37 | "files": [ 38 | "dist" 39 | ], 40 | "scripts": { 41 | "build": "unbuild", 42 | "lint": "eslint .", 43 | "typecheck": "tsc --noEmit", 44 | "test": "vitest", 45 | "release": "bumpp && pnpm publish", 46 | "prepublishOnly": "npm run build" 47 | }, 48 | "dependencies": { 49 | "@apidevtools/json-schema-ref-parser": "^11.7.0", 50 | "@types/json-schema": "^7.0.15" 51 | }, 52 | "devDependencies": { 53 | "@antfu/eslint-config": "^2.25.1", 54 | "@types/lodash": "^4.17.7", 55 | "@types/lodash-es": "^4.17.12", 56 | "@types/node": "^22.3.0", 57 | "bumpp": "^9.5.1", 58 | "eslint": "^9.9.0", 59 | "json-schema-to-typescript-lite": "workspace:*", 60 | "lodash-es": "^4.17.21", 61 | "typescript": "^5.5.4", 62 | "unbuild": "^2.0.0", 63 | "vitest": "^2.0.5" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - example 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process' 2 | import type { JSONSchema4 } from 'json-schema' 3 | import type { ParserOptions as $RefOptions } from '@apidevtools/json-schema-ref-parser' 4 | import cloneDeep from 'lodash-es/cloneDeep' 5 | import endsWith from 'lodash-es/endsWith' 6 | import merge from 'lodash-es/merge' 7 | import { generate } from './generator' 8 | import { normalize } from './normalizer' 9 | import { optimize } from './optimizer' 10 | import { parse } from './parser' 11 | import { dereference } from './resolver' 12 | import { validate } from './validator' 13 | import { link } from './linker' 14 | import { validateOptions } from './optionValidator' 15 | import type { JSONSchema as LinkedJSONSchema } from './types/JSONSchema' 16 | 17 | export { 18 | normalizeIdentifier, 19 | isSafeIdentifier, 20 | toSafeIdentifier, 21 | } from './utils' 22 | 23 | export type { 24 | EnumJSONSchema, 25 | JSONSchema, 26 | NamedEnumJSONSchema, 27 | CustomTypeJSONSchema, 28 | } from './types/JSONSchema' 29 | 30 | export interface Options { 31 | /** 32 | * [$RefParser](https://github.com/BigstickCarpet/json-schema-ref-parser) Options, used when resolving `$ref`s 33 | */ 34 | $refOptions: $RefOptions 35 | /** 36 | * Default value for additionalProperties, when it is not explicitly set. 37 | */ 38 | additionalProperties: boolean 39 | /** 40 | * Root directory for resolving [`$ref`](https://tools.ietf.org/id/draft-pbryan-zyp-json-ref-03.html)s. 41 | */ 42 | cwd: string 43 | /** 44 | * Declare external schemas referenced via `$ref`? 45 | */ 46 | declareExternallyReferenced: boolean 47 | /** 48 | * Prepend enums with [`const`](https://www.typescriptlang.org/docs/handbook/enums.html#computed-and-constant-members)? 49 | */ 50 | enableConstEnums: boolean 51 | /** 52 | * Ignore maxItems and minItems for `array` types, preventing tuples being generated. 53 | */ 54 | ignoreMinAndMaxItems: boolean 55 | /** 56 | * Maximum number of unioned tuples to emit when representing bounded-size array types, 57 | * before falling back to emitting unbounded arrays. Increase this to improve precision 58 | * of emitted types, decrease it to improve performance, or set it to `-1` to ignore 59 | * `minItems` and `maxItems`. 60 | */ 61 | maxItems: number 62 | /** 63 | * Append all index signatures with `| undefined` so that they are strictly typed. 64 | * 65 | * This is required to be compatible with `strictNullChecks`. 66 | */ 67 | strictIndexSignatures: boolean 68 | /** 69 | * Generate code for `definitions` that aren't referenced by the schema? 70 | */ 71 | unreachableDefinitions: boolean 72 | /** 73 | * Generate unknown type instead of any 74 | */ 75 | unknownAny: boolean 76 | /** 77 | * Custom function to provide a type name for a given schema 78 | */ 79 | customName?: (schema: LinkedJSONSchema, keyNameFromDefinition: string | undefined) => string 80 | } 81 | 82 | export const DEFAULT_OPTIONS: Options = { 83 | $refOptions: {}, 84 | additionalProperties: true, // TODO: default to empty schema (as per spec) instead 85 | cwd: process.cwd(), 86 | declareExternallyReferenced: true, 87 | enableConstEnums: true, 88 | ignoreMinAndMaxItems: false, 89 | maxItems: 20, 90 | strictIndexSignatures: false, 91 | unreachableDefinitions: false, 92 | unknownAny: true, 93 | } 94 | 95 | export async function compile(schema: JSONSchema4, name: string, options: Partial = {}): Promise { 96 | validateOptions(options) 97 | 98 | const _options = merge({}, DEFAULT_OPTIONS, options) 99 | 100 | // normalize options 101 | if (!endsWith(_options.cwd, '/')) 102 | _options.cwd += '/' 103 | 104 | // Initial clone to avoid mutating the input 105 | const _schema = cloneDeep(schema) 106 | 107 | const { dereferencedPaths, dereferencedSchema } = await dereference(_schema, _options) 108 | 109 | const linked = link(dereferencedSchema) 110 | 111 | const errors = validate(linked, name) 112 | if (errors.length) { 113 | errors.forEach(_ => console.error(_)) 114 | throw new ValidationError() 115 | } 116 | 117 | const normalized = normalize(linked, dereferencedPaths, name, _options) 118 | const parsed = parse(normalized, _options) 119 | const optimized = optimize(parsed, _options) 120 | const generated = generate(optimized, _options) 121 | 122 | return generated 123 | } 124 | 125 | export class ValidationError extends Error {} 126 | -------------------------------------------------------------------------------- /src/linker.ts: -------------------------------------------------------------------------------- 1 | import isPlainObject from 'lodash-es/isPlainObject' 2 | import type { JSONSchema4Type } from 'json-schema' 3 | import type { JSONSchema, LinkedJSONSchema } from './types/JSONSchema' 4 | import { Parent } from './types/JSONSchema' 5 | 6 | /** 7 | * Traverses over the schema, giving each node a reference to its 8 | * parent node. We need this for downstream operations. 9 | */ 10 | export function link(schema: JSONSchema4Type | JSONSchema, parent: JSONSchema4Type | null = null): LinkedJSONSchema { 11 | if (!Array.isArray(schema) && !isPlainObject(schema)) 12 | return schema as LinkedJSONSchema 13 | 14 | // Handle cycles 15 | // eslint-disable-next-line no-prototype-builtins 16 | if ((schema as JSONSchema).hasOwnProperty(Parent)) 17 | return schema as LinkedJSONSchema 18 | 19 | // Add a reference to this schema's parent 20 | Object.defineProperty(schema, Parent, { 21 | enumerable: false, 22 | value: parent, 23 | writable: false, 24 | }) 25 | 26 | // Arrays 27 | if (Array.isArray(schema)) 28 | schema.forEach(child => link(child, schema)) 29 | 30 | // Objects 31 | for (const key in schema as JSONSchema) 32 | link((schema as JSONSchema)[key], schema) 33 | 34 | return schema as LinkedJSONSchema 35 | } 36 | -------------------------------------------------------------------------------- /src/optimizer.ts: -------------------------------------------------------------------------------- 1 | import uniqBy from 'lodash-es/uniqBy' 2 | import { generateType } from './generator' 3 | import type { AST } from './types/AST' 4 | import { T_ANY, T_UNKNOWN } from './types/AST' 5 | import type { Options } from '.' 6 | 7 | export function optimize(ast: AST, options: Options, processed = new Set()): AST { 8 | if (processed.has(ast)) 9 | return ast 10 | 11 | processed.add(ast) 12 | 13 | switch (ast.type) { 14 | case 'INTERFACE': 15 | return Object.assign(ast, { 16 | params: ast.params.map(_ => Object.assign(_, { ast: optimize(_.ast, options, processed) })), 17 | }) 18 | case 'INTERSECTION': 19 | case 'UNION': { 20 | // Start with the leaves... 21 | const optimizedAST = Object.assign(ast, { 22 | params: ast.params.map(_ => optimize(_, options, processed)), 23 | }) 24 | 25 | // [A, B, C, Any] -> Any 26 | if (optimizedAST.params.some(_ => _.type === 'ANY')) { 27 | // log('cyan', 'optimizer', '[A, B, C, Any] -> Any', optimizedAST) 28 | return T_ANY 29 | } 30 | 31 | // [A, B, C, Unknown] -> Unknown 32 | if (optimizedAST.params.some(_ => _.type === 'UNKNOWN')) { 33 | // log('cyan', 'optimizer', '[A, B, C, Unknown] -> Unknown', optimizedAST) 34 | return T_UNKNOWN 35 | } 36 | 37 | // [A (named), A] -> [A (named)] 38 | if ( 39 | optimizedAST.params.every((_) => { 40 | const a = generateType(omitStandaloneName(_), options) 41 | const b = generateType(omitStandaloneName(optimizedAST.params[0]), options) 42 | return a === b 43 | }) 44 | && optimizedAST.params.some(_ => _.standaloneName !== undefined) 45 | ) { 46 | // log('cyan', 'optimizer', '[A (named), A] -> [A (named)]', optimizedAST) 47 | optimizedAST.params = optimizedAST.params.filter(_ => _.standaloneName !== undefined) 48 | } 49 | 50 | // [A, B, B] -> [A, B] 51 | const params = uniqBy(optimizedAST.params, _ => generateType(_, options)) 52 | if (params.length !== optimizedAST.params.length) { 53 | // log('cyan', 'optimizer', '[A, B, B] -> [A, B]', optimizedAST) 54 | optimizedAST.params = params 55 | } 56 | 57 | return Object.assign(optimizedAST, { 58 | params: optimizedAST.params.map(_ => optimize(_, options, processed)), 59 | }) 60 | } 61 | default: 62 | return ast 63 | } 64 | } 65 | 66 | // TODO: More clearly disambiguate standalone names vs. aliased names instead. 67 | function omitStandaloneName(ast: A): A { 68 | switch (ast.type) { 69 | case 'ENUM': 70 | return ast 71 | default: 72 | return { ...ast, standaloneName: undefined } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/optionValidator.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from '.' 2 | 3 | export function validateOptions({ maxItems }: Partial): void { 4 | if (maxItems !== undefined && maxItems < -1) 5 | throw new RangeError(`Expected options.maxItems to be >= -1, but was given ${maxItems}.`) 6 | } 7 | -------------------------------------------------------------------------------- /src/resolver.ts: -------------------------------------------------------------------------------- 1 | import type { ParserOptions } from '@apidevtools/json-schema-ref-parser' 2 | import { $RefParser } from '@apidevtools/json-schema-ref-parser' 3 | import type { JSONSchema4 } from 'json-schema' 4 | 5 | export type DereferencedPaths = WeakMap 6 | 7 | export async function dereference( 8 | schema: JSONSchema4, 9 | { cwd, $refOptions }: { cwd: string, $refOptions: ParserOptions }, 10 | ): Promise<{ dereferencedPaths: DereferencedPaths, dereferencedSchema: JSONSchema4 }> { 11 | // log('green', 'dereferencer', 'Dereferencing input schema:', cwd, schema) 12 | const parser = new $RefParser() 13 | const dereferencedPaths: DereferencedPaths = new WeakMap() 14 | const dereferencedSchema = (await parser.dereference(cwd, schema as any, { 15 | ...$refOptions, 16 | dereference: { 17 | ...$refOptions.dereference, 18 | onDereference($ref: any, schema: any) { 19 | dereferencedPaths.set(schema, $ref) 20 | }, 21 | }, 22 | })) as any // TODO: fix types 23 | return { dereferencedPaths, dereferencedSchema } 24 | } 25 | -------------------------------------------------------------------------------- /src/types/AST.ts: -------------------------------------------------------------------------------- 1 | import type { JSONSchema4Type } from 'json-schema' 2 | 3 | export type AST_TYPE = AST['type'] 4 | 5 | export type AST = 6 | | TAny 7 | | TArray 8 | | TBoolean 9 | | TEnum 10 | | TInterface 11 | | TNamedInterface 12 | | TIntersection 13 | | TLiteral 14 | | TNever 15 | | TNumber 16 | | TNull 17 | | TObject 18 | | TReference 19 | | TString 20 | | TTuple 21 | | TUnion 22 | | TUnknown 23 | | TCustomType 24 | 25 | export interface AbstractAST { 26 | comment?: string 27 | keyName?: string 28 | standaloneName?: string 29 | type: AST_TYPE 30 | deprecated?: boolean 31 | } 32 | 33 | export type ASTWithComment = AST & { comment: string } 34 | export type ASTWithName = AST & { keyName: string } 35 | export type ASTWithStandaloneName = AST & { standaloneName: string } 36 | 37 | export function hasComment(ast: AST): ast is ASTWithComment { 38 | return ( 39 | ('comment' in ast && ast.comment != null && ast.comment !== '') 40 | // Compare to true because ast.deprecated might be undefined 41 | || ('deprecated' in ast && ast.deprecated === true) 42 | ) 43 | } 44 | 45 | export function hasStandaloneName(ast: AST): ast is ASTWithStandaloneName { 46 | return 'standaloneName' in ast && ast.standaloneName != null && ast.standaloneName !== '' 47 | } 48 | 49 | /// ///////////////////////////////////////// types 50 | 51 | export interface TAny extends AbstractAST { 52 | type: 'ANY' 53 | } 54 | 55 | export interface TArray extends AbstractAST { 56 | type: 'ARRAY' 57 | params: AST 58 | } 59 | 60 | export interface TBoolean extends AbstractAST { 61 | type: 'BOOLEAN' 62 | } 63 | 64 | export interface TEnum extends AbstractAST { 65 | standaloneName: string 66 | type: 'ENUM' 67 | params: TEnumParam[] 68 | } 69 | 70 | export interface TEnumParam { 71 | ast: AST 72 | keyName: string 73 | } 74 | 75 | export interface TInterface extends AbstractAST { 76 | type: 'INTERFACE' 77 | params: TInterfaceParam[] 78 | superTypes: TNamedInterface[] 79 | } 80 | 81 | export interface TNamedInterface extends AbstractAST { 82 | standaloneName: string 83 | type: 'INTERFACE' 84 | params: TInterfaceParam[] 85 | superTypes: TNamedInterface[] 86 | } 87 | 88 | export interface TNever extends AbstractAST { 89 | type: 'NEVER' 90 | } 91 | 92 | export interface TInterfaceParam { 93 | ast: AST 94 | keyName: string 95 | isRequired: boolean 96 | isPatternProperty: boolean 97 | isUnreachableDefinition: boolean 98 | } 99 | 100 | export interface TIntersection extends AbstractAST { 101 | type: 'INTERSECTION' 102 | params: AST[] 103 | } 104 | 105 | export interface TLiteral extends AbstractAST { 106 | params: JSONSchema4Type 107 | type: 'LITERAL' 108 | } 109 | 110 | export interface TNumber extends AbstractAST { 111 | type: 'NUMBER' 112 | } 113 | 114 | export interface TNull extends AbstractAST { 115 | type: 'NULL' 116 | } 117 | 118 | export interface TObject extends AbstractAST { 119 | type: 'OBJECT' 120 | } 121 | 122 | export interface TReference extends AbstractAST { 123 | type: 'REFERENCE' 124 | params: string 125 | } 126 | 127 | export interface TString extends AbstractAST { 128 | type: 'STRING' 129 | } 130 | 131 | export interface TTuple extends AbstractAST { 132 | type: 'TUPLE' 133 | params: AST[] 134 | spreadParam?: AST 135 | minItems: number 136 | maxItems?: number 137 | } 138 | 139 | export interface TUnion extends AbstractAST { 140 | type: 'UNION' 141 | params: AST[] 142 | } 143 | 144 | export interface TUnknown extends AbstractAST { 145 | type: 'UNKNOWN' 146 | } 147 | 148 | export interface TCustomType extends AbstractAST { 149 | type: 'CUSTOM_TYPE' 150 | params: string 151 | } 152 | 153 | /// ///////////////////////////////////////// literals 154 | 155 | export const T_ANY: TAny = { 156 | type: 'ANY', 157 | } 158 | 159 | export const T_ANY_ADDITIONAL_PROPERTIES: TAny & ASTWithName = { 160 | keyName: '[k: string]', 161 | type: 'ANY', 162 | } 163 | 164 | export const T_UNKNOWN: TUnknown = { 165 | type: 'UNKNOWN', 166 | } 167 | 168 | export const T_UNKNOWN_ADDITIONAL_PROPERTIES: TUnknown & ASTWithName = { 169 | keyName: '[k: string]', 170 | type: 'UNKNOWN', 171 | } 172 | -------------------------------------------------------------------------------- /src/types/JSONSchema.ts: -------------------------------------------------------------------------------- 1 | import type { JSONSchema4, JSONSchema4Type, JSONSchema4TypeName } from 'json-schema' 2 | import isPlainObject from 'lodash-es/isPlainObject' 3 | import memoize from 'lodash-es/memoize' 4 | 5 | export type SchemaType = 6 | | 'ALL_OF' 7 | | 'UNNAMED_SCHEMA' 8 | | 'ANY' 9 | | 'ANY_OF' 10 | | 'BOOLEAN' 11 | | 'NAMED_ENUM' 12 | | 'NAMED_SCHEMA' 13 | | 'NEVER' 14 | | 'NULL' 15 | | 'NUMBER' 16 | | 'STRING' 17 | | 'OBJECT' 18 | | 'ONE_OF' 19 | | 'TYPED_ARRAY' 20 | | 'REFERENCE' 21 | | 'UNION' 22 | | 'UNNAMED_ENUM' 23 | | 'UNTYPED_ARRAY' 24 | | 'CUSTOM_TYPE' 25 | 26 | export type JSONSchemaTypeName = JSONSchema4TypeName 27 | export type JSONSchemaType = JSONSchema4Type 28 | 29 | export interface JSONSchema extends JSONSchema4 { 30 | /** 31 | * schema extension to support numeric enums 32 | */ 33 | tsEnumNames?: string[] 34 | /** 35 | * schema extension to support custom types 36 | */ 37 | tsType?: string 38 | /** 39 | * property exists at least in https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.3 40 | */ 41 | deprecated?: boolean 42 | } 43 | 44 | export const Parent = Symbol('Parent') 45 | 46 | export interface LinkedJSONSchema extends JSONSchema { 47 | /** 48 | * A reference to this schema's parent node, for convenience. 49 | * `null` when this is the root schema. 50 | */ 51 | [Parent]: LinkedJSONSchema | null 52 | 53 | additionalItems?: boolean | LinkedJSONSchema 54 | additionalProperties?: boolean | LinkedJSONSchema 55 | items?: LinkedJSONSchema | LinkedJSONSchema[] 56 | definitions?: { 57 | [k: string]: LinkedJSONSchema 58 | } 59 | properties?: { 60 | [k: string]: LinkedJSONSchema 61 | } 62 | patternProperties?: { 63 | [k: string]: LinkedJSONSchema 64 | } 65 | dependencies?: { 66 | [k: string]: LinkedJSONSchema | string[] 67 | } 68 | allOf?: LinkedJSONSchema[] 69 | anyOf?: LinkedJSONSchema[] 70 | oneOf?: LinkedJSONSchema[] 71 | not?: LinkedJSONSchema 72 | } 73 | 74 | export interface NormalizedJSONSchema extends LinkedJSONSchema { 75 | additionalItems?: boolean | NormalizedJSONSchema 76 | additionalProperties: boolean | NormalizedJSONSchema 77 | extends?: string[] 78 | items?: NormalizedJSONSchema | NormalizedJSONSchema[] 79 | $defs?: { 80 | [k: string]: NormalizedJSONSchema 81 | } 82 | properties?: { 83 | [k: string]: NormalizedJSONSchema 84 | } 85 | patternProperties?: { 86 | [k: string]: NormalizedJSONSchema 87 | } 88 | dependencies?: { 89 | [k: string]: NormalizedJSONSchema | string[] 90 | } 91 | allOf?: NormalizedJSONSchema[] 92 | anyOf?: NormalizedJSONSchema[] 93 | oneOf?: NormalizedJSONSchema[] 94 | not?: NormalizedJSONSchema 95 | required: string[] 96 | 97 | // Removed by normalizer 98 | definitions: never 99 | id: never 100 | } 101 | 102 | export interface EnumJSONSchema extends NormalizedJSONSchema { 103 | enum: any[] 104 | } 105 | 106 | export interface NamedEnumJSONSchema extends NormalizedJSONSchema { 107 | tsEnumNames: string[] 108 | } 109 | 110 | export interface SchemaSchema extends NormalizedJSONSchema { 111 | properties: { 112 | [k: string]: NormalizedJSONSchema 113 | } 114 | required: string[] 115 | } 116 | 117 | export interface JSONSchemaWithDefinitions extends NormalizedJSONSchema { 118 | $defs: { 119 | [k: string]: NormalizedJSONSchema 120 | } 121 | } 122 | 123 | export interface CustomTypeJSONSchema extends NormalizedJSONSchema { 124 | tsType: string 125 | } 126 | 127 | export const getRootSchema = memoize((schema: LinkedJSONSchema): LinkedJSONSchema => { 128 | const parent = schema[Parent] 129 | if (!parent) 130 | return schema 131 | 132 | return getRootSchema(parent) 133 | }) as (schema: LinkedJSONSchema) => LinkedJSONSchema 134 | 135 | export function isBoolean(schema: LinkedJSONSchema | JSONSchemaType): schema is boolean { 136 | return schema === true || schema === false 137 | } 138 | 139 | export function isPrimitive(schema: LinkedJSONSchema | JSONSchemaType): schema is JSONSchemaType { 140 | return !isPlainObject(schema) 141 | } 142 | 143 | export function isCompound(schema: JSONSchema): boolean { 144 | return Array.isArray(schema.type) || 'anyOf' in schema || 'oneOf' in schema 145 | } 146 | -------------------------------------------------------------------------------- /src/typesOfSchema.ts: -------------------------------------------------------------------------------- 1 | import isPlainObject from 'lodash-es/isPlainObject' 2 | import type { JSONSchema, SchemaType } from './types/JSONSchema' 3 | import { isCompound } from './types/JSONSchema' 4 | 5 | const matchers: Record boolean> = { 6 | ALL_OF(schema) { 7 | return 'allOf' in schema 8 | }, 9 | ANY(schema) { 10 | if (Object.keys(schema).length === 0) { 11 | // The empty schema {} validates any value 12 | // @see https://json-schema.org/draft-07/json-schema-core.html#rfc.section.4.3.1 13 | return true 14 | } 15 | return schema.type === 'any' 16 | }, 17 | ANY_OF(schema) { 18 | return 'anyOf' in schema 19 | }, 20 | BOOLEAN(schema) { 21 | if ('enum' in schema) 22 | return false 23 | 24 | if (schema.type === 'boolean') 25 | return true 26 | 27 | if (!isCompound(schema) && typeof schema.default === 'boolean') 28 | return true 29 | 30 | return false 31 | }, 32 | CUSTOM_TYPE() { 33 | return false // Explicitly handled before we try to match 34 | }, 35 | NAMED_ENUM(schema) { 36 | return 'enum' in schema && 'tsEnumNames' in schema 37 | }, 38 | NAMED_SCHEMA(schema) { 39 | // 8.2.1. The presence of "$id" in a subschema indicates that the subschema constitutes a distinct schema resource within a single schema document. 40 | return '$id' in schema && ('patternProperties' in schema || 'properties' in schema) 41 | }, 42 | NEVER(schema: JSONSchema | boolean) { 43 | return schema === false 44 | }, 45 | NULL(schema) { 46 | return schema.type === 'null' 47 | }, 48 | NUMBER(schema) { 49 | if ('enum' in schema) 50 | return false 51 | 52 | if (schema.type === 'integer' || schema.type === 'number') 53 | return true 54 | 55 | if (!isCompound(schema) && typeof schema.default === 'number') 56 | return true 57 | 58 | return false 59 | }, 60 | OBJECT(schema) { 61 | return ( 62 | schema.type === 'object' 63 | && !isPlainObject(schema.additionalProperties) 64 | && !schema.allOf 65 | && !schema.anyOf 66 | && !schema.oneOf 67 | && !schema.patternProperties 68 | && !schema.properties 69 | && !schema.required 70 | ) 71 | }, 72 | ONE_OF(schema) { 73 | return 'oneOf' in schema 74 | }, 75 | REFERENCE(schema) { 76 | return '$ref' in schema 77 | }, 78 | STRING(schema) { 79 | if ('enum' in schema) 80 | return false 81 | 82 | if (schema.type === 'string') 83 | return true 84 | 85 | if (!isCompound(schema) && typeof schema.default === 'string') 86 | return true 87 | 88 | return false 89 | }, 90 | TYPED_ARRAY(schema) { 91 | if (schema.type && schema.type !== 'array') 92 | return false 93 | 94 | return 'items' in schema 95 | }, 96 | UNION(schema) { 97 | return Array.isArray(schema.type) 98 | }, 99 | UNNAMED_ENUM(schema) { 100 | if ('tsEnumNames' in schema) 101 | return false 102 | 103 | if ( 104 | schema.type 105 | && schema.type !== 'boolean' 106 | && schema.type !== 'integer' 107 | && schema.type !== 'number' 108 | && schema.type !== 'string' 109 | ) { 110 | return false 111 | } 112 | 113 | return 'enum' in schema 114 | }, 115 | UNNAMED_SCHEMA() { 116 | return false // Explicitly handled as the default case 117 | }, 118 | UNTYPED_ARRAY(schema) { 119 | return schema.type === 'array' && !('items' in schema) 120 | }, 121 | } 122 | 123 | /** 124 | * Duck types a JSONSchema schema or property to determine which kind of AST node to parse it into. 125 | * 126 | * Due to what some might say is an oversight in the JSON-Schema spec, a given schema may 127 | * implicitly be an *intersection* of multiple JSON-Schema directives (ie. multiple TypeScript 128 | * types). The spec leaves it up to implementations to decide what to do with this 129 | * loosely-defined behavior. 130 | */ 131 | export function typesOfSchema(schema: JSONSchema): readonly [SchemaType, ...SchemaType[]] { 132 | // tsType is an escape hatch that supercedes all other directives 133 | if (schema.tsType) 134 | return ['CUSTOM_TYPE'] 135 | 136 | // Collect matched types 137 | const matchedTypes: SchemaType[] = [] 138 | for (const [schemaType, f] of Object.entries(matchers)) { 139 | if (f(schema)) 140 | matchedTypes.push(schemaType as SchemaType) 141 | } 142 | 143 | // Default to an unnamed schema 144 | if (!matchedTypes.length) 145 | return ['UNNAMED_SCHEMA'] 146 | 147 | return matchedTypes as [SchemaType, ...SchemaType[]] 148 | } 149 | -------------------------------------------------------------------------------- /src/validator.ts: -------------------------------------------------------------------------------- 1 | import type { JSONSchema, LinkedJSONSchema } from './types/JSONSchema' 2 | import { traverse } from './utils' 3 | 4 | type Rule = (schema: JSONSchema) => boolean | void 5 | const rules = new Map() 6 | 7 | rules.set('Enum members and tsEnumNames must be of the same length', (schema) => { 8 | if (schema.enum && schema.tsEnumNames && schema.enum.length !== schema.tsEnumNames.length) 9 | return false 10 | }) 11 | 12 | rules.set('tsEnumNames must be an array of strings', (schema) => { 13 | if (schema.tsEnumNames && schema.tsEnumNames.some(_ => typeof _ !== 'string')) 14 | return false 15 | }) 16 | 17 | rules.set('When both maxItems and minItems are present, maxItems >= minItems', (schema) => { 18 | const { maxItems, minItems } = schema 19 | if (typeof maxItems === 'number' && typeof minItems === 'number') 20 | return maxItems >= minItems 21 | }) 22 | 23 | rules.set('When maxItems exists, maxItems >= 0', (schema) => { 24 | const { maxItems } = schema 25 | if (typeof maxItems === 'number') 26 | return maxItems >= 0 27 | }) 28 | 29 | rules.set('When minItems exists, minItems >= 0', (schema) => { 30 | const { minItems } = schema 31 | if (typeof minItems === 'number') 32 | return minItems >= 0 33 | }) 34 | 35 | rules.set('deprecated must be a boolean', (schema) => { 36 | const typeOfDeprecated = typeof schema.deprecated 37 | return typeOfDeprecated === 'boolean' || typeOfDeprecated === 'undefined' 38 | }) 39 | 40 | export function validate(schema: LinkedJSONSchema, filename: string): string[] { 41 | const errors: string[] = [] 42 | rules.forEach((rule, ruleName) => { 43 | traverse(schema, (schema, key) => { 44 | if (rule(schema) === false) 45 | errors.push(`Error at key "${key}" in file "${filename}": ${ruleName}`) 46 | 47 | return schema 48 | }) 49 | }) 50 | return errors 51 | } 52 | -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2015-01-01-Sendgrid.Email.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2014-04-01-preview/Sendgrid.json", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Sendgrid.Email", 5 | "description": "Sendgrid Resource Types", 6 | "resourceDefinitions": { 7 | "accounts": { 8 | "type": "object", 9 | "properties": { 10 | "name": { 11 | "anyOf": [ 12 | { 13 | "type": "string", 14 | "minLength": 4, 15 | "maxLength": 50 16 | }, 17 | { 18 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 19 | } 20 | ], 21 | "description": "Name of the resource" 22 | }, 23 | "type": { 24 | "enum": [ 25 | "Sendgrid.Email/accounts" 26 | ] 27 | }, 28 | "apiVersion": { 29 | "enum": [ 30 | "2015-01-01" 31 | ] 32 | }, 33 | "plan": { 34 | "type": "object", 35 | "properties": { 36 | "name": { 37 | "oneOf": [ 38 | { 39 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 40 | }, 41 | { 42 | "enum": [ 43 | "free", 44 | "bronze", 45 | "silver", 46 | "gold", 47 | "platinum", 48 | "premier" 49 | ] 50 | } 51 | ], 52 | "description": "Plan name" 53 | }, 54 | "publisher": { 55 | "oneOf": [ 56 | { 57 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 58 | }, 59 | { 60 | "enum": [ 61 | "Sendgrid" 62 | ] 63 | } 64 | ], 65 | "description": "Publisher name" 66 | }, 67 | "product": { 68 | "oneOf": [ 69 | { 70 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 71 | }, 72 | { 73 | "enum": [ 74 | "sendgrid_azure" 75 | ] 76 | } 77 | ], 78 | "description": "Plan id" 79 | }, 80 | "promotionCode": { 81 | "type": "string", 82 | "description": "Promotion code" 83 | } 84 | }, 85 | "required": [ 86 | "name", 87 | "publisher", 88 | "product" 89 | ], 90 | "description": "SendGrid plan" 91 | }, 92 | "properties": { 93 | "type": "object", 94 | "properties": { 95 | "password": { 96 | "anyOf": [ 97 | { 98 | "type": "string", 99 | "minLength": 8, 100 | "maxLength": 50 101 | }, 102 | { 103 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 104 | } 105 | ], 106 | "description": "The SendGrid account password" 107 | }, 108 | "acceptMarketingEmails": { 109 | "oneOf": [ 110 | { 111 | "type": "boolean" 112 | }, 113 | { 114 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 115 | } 116 | ], 117 | "description": "True if you want to accept Marketing Emails" 118 | }, 119 | "email": { 120 | "type": "string", 121 | "description": "The user's email address" 122 | } 123 | }, 124 | "required": [ 125 | "password", 126 | "acceptMarketingEmails", 127 | "email" 128 | ] 129 | } 130 | }, 131 | "required": [ 132 | "name", 133 | "type", 134 | "plan", 135 | "apiVersion", 136 | "properties" 137 | ], 138 | "description": "SendGrid resources that user purchases" 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2015-02-28-Microsoft.Search.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2015-02-28/Microsoft.Search.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.Search", 5 | "description": "Microsoft Search Resource Types", 6 | "resourceDefinitions": { 7 | "searchServices": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2015-02-28" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The geographic location of the Search service." 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the Search service to create or update." 23 | }, 24 | "properties": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/SearchServiceProperties" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ], 33 | "description": "Defines properties of an Azure Search service that can be modified." 34 | }, 35 | "tags": { 36 | "oneOf": [ 37 | { 38 | "type": "object", 39 | "additionalProperties": { 40 | "type": "string" 41 | }, 42 | "properties": {} 43 | }, 44 | { 45 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 46 | } 47 | ], 48 | "description": "Tags to help categorize the Search service in the Azure Portal." 49 | }, 50 | "type": { 51 | "type": "string", 52 | "enum": [ 53 | "Microsoft.Search/searchServices" 54 | ] 55 | } 56 | }, 57 | "required": [ 58 | "apiVersion", 59 | "name", 60 | "properties", 61 | "type" 62 | ], 63 | "description": "Microsoft.Search/searchServices" 64 | } 65 | }, 66 | "definitions": { 67 | "SearchServiceProperties": { 68 | "type": "object", 69 | "properties": { 70 | "partitionCount": { 71 | "oneOf": [ 72 | { 73 | "type": "integer" 74 | }, 75 | { 76 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 77 | } 78 | ], 79 | "description": "The number of partitions in the Search service; if specified, it can be 1, 2, 3, 4, 6, or 12." 80 | }, 81 | "replicaCount": { 82 | "oneOf": [ 83 | { 84 | "type": "integer" 85 | }, 86 | { 87 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 88 | } 89 | ], 90 | "description": "The number of replicas in the Search service. If specified, it must be a value between 1 and 6 inclusive." 91 | }, 92 | "sku": { 93 | "oneOf": [ 94 | { 95 | "$ref": "#/definitions/Sku" 96 | }, 97 | { 98 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 99 | } 100 | ], 101 | "description": "Defines the SKU of an Azure Search Service, which determines price tier and capacity limits." 102 | } 103 | }, 104 | "description": "Defines properties of an Azure Search service that can be modified." 105 | }, 106 | "Sku": { 107 | "type": "object", 108 | "properties": { 109 | "name": { 110 | "oneOf": [ 111 | { 112 | "type": "string", 113 | "enum": [ 114 | "free", 115 | "standard", 116 | "standard2" 117 | ] 118 | }, 119 | { 120 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 121 | } 122 | ], 123 | "description": "The SKU of the Search service." 124 | } 125 | }, 126 | "description": "Defines the SKU of an Azure Search Service, which determines price tier and capacity limits." 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2015-06-01-Microsoft.KeyVault.Secrets.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2015-06-01/Microsoft.KeyVault.Secrets.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.KeyVault", 5 | "description": "Microsoft KeyVault Resource Types", 6 | "resourceDefinitions": { 7 | "vaults_secrets": { 8 | "type": "object", 9 | "properties": { 10 | "type": { 11 | "enum": [ 12 | "Microsoft.KeyVault/vaults/secrets" 13 | ] 14 | }, 15 | "apiVersion": { 16 | "enum": [ 17 | "2015-06-01" 18 | ] 19 | }, 20 | "properties": { 21 | "type": "object", 22 | "properties": { 23 | "value": { 24 | "type": "string", 25 | "description": "Secret value" 26 | } 27 | }, 28 | "required": [ 29 | "value" 30 | ] 31 | } 32 | }, 33 | "required": [ 34 | "apiVersion", 35 | "properties", 36 | "type" 37 | ], 38 | "description": "Microsoft.KeyVault/vaults/secrets" 39 | } 40 | }, 41 | "definitions": {} 42 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2015-08-01-Microsoft.Storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2015-08-01/Microsoft.Storage.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.Storage", 5 | "description": "Microsoft Storage Resource Types", 6 | "resourceDefinitions": { 7 | "storageAccounts": { 8 | "type": "object", 9 | "properties": { 10 | "type": { 11 | "enum": [ 12 | "Microsoft.Storage/storageAccounts" 13 | ] 14 | }, 15 | "apiVersion": { 16 | "enum": [ 17 | "2015-05-01-preview", 18 | "2015-06-15" 19 | ] 20 | }, 21 | "properties": { 22 | "type": "object", 23 | "properties": { 24 | "accountType": { 25 | "type": "string", 26 | "minLength": 1, 27 | "description": "Microsoft.Storage/storageAccounts: The type of this account." 28 | } 29 | }, 30 | "required": [ 31 | "accountType" 32 | ] 33 | } 34 | }, 35 | "required": [ 36 | "type", 37 | "apiVersion", 38 | "properties", 39 | "location" 40 | ], 41 | "description": "Microsoft.Storage/storageAccounts" 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2015-08-31-preview-Microsoft.ManagedIdentity.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2015-08-31-preview/Microsoft.ManagedIdentity.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.ManagedIdentity", 5 | "description": "Microsoft ManagedIdentity Resource Types", 6 | "resourceDefinitions": { 7 | "userAssignedIdentities": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2015-08-31-preview" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The geo-location where the resource lives" 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the identity resource." 23 | }, 24 | "tags": { 25 | "oneOf": [ 26 | { 27 | "type": "object", 28 | "additionalProperties": { 29 | "type": "string" 30 | }, 31 | "properties": {} 32 | }, 33 | { 34 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 35 | } 36 | ], 37 | "description": "Resource tags." 38 | }, 39 | "type": { 40 | "type": "string", 41 | "enum": [ 42 | "Microsoft.ManagedIdentity/userAssignedIdentities" 43 | ] 44 | } 45 | }, 46 | "required": [ 47 | "apiVersion", 48 | "location", 49 | "name", 50 | "type" 51 | ], 52 | "description": "Microsoft.ManagedIdentity/userAssignedIdentities" 53 | } 54 | }, 55 | "definitions": {} 56 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2015-10-01-Microsoft.Media.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2015-10-01/Microsoft.Media.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.Media", 5 | "description": "Microsoft Media Resource Types", 6 | "resourceDefinitions": { 7 | "mediaservices": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2015-10-01" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The geographic location of the resource. This must be one of the supported and registered Azure Geo Regions (for example, West US, East US, Southeast Asia, and so forth)." 19 | }, 20 | "name": { 21 | "oneOf": [ 22 | { 23 | "type": "string", 24 | "pattern": "^[a-z0-9]{3,24}$", 25 | "minLength": 3, 26 | "maxLength": 24 27 | }, 28 | { 29 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 30 | } 31 | ], 32 | "description": "Name of the Media Service." 33 | }, 34 | "properties": { 35 | "oneOf": [ 36 | { 37 | "$ref": "#/definitions/MediaServiceProperties" 38 | }, 39 | { 40 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 41 | } 42 | ], 43 | "description": "The additional properties of a Media Service resource." 44 | }, 45 | "tags": { 46 | "oneOf": [ 47 | { 48 | "type": "object", 49 | "additionalProperties": { 50 | "type": "string" 51 | }, 52 | "properties": {} 53 | }, 54 | { 55 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 56 | } 57 | ], 58 | "description": "Tags to help categorize the resource in the Azure portal." 59 | }, 60 | "type": { 61 | "type": "string", 62 | "enum": [ 63 | "Microsoft.Media/mediaservices" 64 | ] 65 | } 66 | }, 67 | "required": [ 68 | "apiVersion", 69 | "name", 70 | "properties", 71 | "type" 72 | ], 73 | "description": "Microsoft.Media/mediaservices" 74 | } 75 | }, 76 | "definitions": { 77 | "MediaServiceProperties": { 78 | "type": "object", 79 | "properties": { 80 | "storageAccounts": { 81 | "oneOf": [ 82 | { 83 | "type": "array", 84 | "items": { 85 | "$ref": "#/definitions/StorageAccount" 86 | } 87 | }, 88 | { 89 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 90 | } 91 | ], 92 | "description": "The storage accounts for this resource." 93 | } 94 | }, 95 | "description": "The additional properties of a Media Service resource." 96 | }, 97 | "StorageAccount": { 98 | "type": "object", 99 | "properties": { 100 | "id": { 101 | "type": "string", 102 | "description": "The id of the storage account resource. Media Services relies on tables and queues as well as blobs, so the primary storage account must be a Standard Storage account (either Microsoft.ClassicStorage or Microsoft.Storage). Blob only storage accounts can be added as secondary storage accounts (isPrimary false)." 103 | }, 104 | "isPrimary": { 105 | "oneOf": [ 106 | { 107 | "type": "boolean" 108 | }, 109 | { 110 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 111 | } 112 | ], 113 | "description": "Is this storage account resource the primary storage account for the Media Service resource. Blob only storage must set this to false." 114 | } 115 | }, 116 | "required": [ 117 | "id", 118 | "isPrimary" 119 | ], 120 | "description": "The properties of a storage account associated with this resource." 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2016-01-29-Microsoft.PowerBI.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2016-01-29/Microsoft.PowerBI.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.PowerBI", 5 | "description": "Microsoft PowerBI Resource Types", 6 | "resourceDefinitions": { 7 | "workspaceCollections": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2016-01-29" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "Azure location" 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "Power BI Embedded Workspace Collection name" 23 | }, 24 | "sku": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/AzureSku" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ] 33 | }, 34 | "tags": { 35 | "oneOf": [ 36 | { 37 | "type": "object", 38 | "additionalProperties": { 39 | "type": "string" 40 | }, 41 | "properties": {} 42 | }, 43 | { 44 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 45 | } 46 | ] 47 | }, 48 | "type": { 49 | "type": "string", 50 | "enum": [ 51 | "Microsoft.PowerBI/workspaceCollections" 52 | ] 53 | } 54 | }, 55 | "required": [ 56 | "apiVersion", 57 | "name", 58 | "type" 59 | ], 60 | "description": "Microsoft.PowerBI/workspaceCollections" 61 | } 62 | }, 63 | "definitions": { 64 | "AzureSku": { 65 | "type": "object", 66 | "properties": { 67 | "name": { 68 | "oneOf": [ 69 | { 70 | "type": "string", 71 | "enum": [ 72 | "S1" 73 | ] 74 | }, 75 | { 76 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 77 | } 78 | ], 79 | "description": "SKU name" 80 | }, 81 | "tier": { 82 | "oneOf": [ 83 | { 84 | "type": "string", 85 | "enum": [ 86 | "Standard" 87 | ] 88 | }, 89 | { 90 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 91 | } 92 | ], 93 | "description": "SKU tier" 94 | } 95 | }, 96 | "required": [ 97 | "name", 98 | "tier" 99 | ] 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2016-04-01-Microsoft.MachineLearning.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2016-04-01/Microsoft.MachineLearning.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.MachineLearning", 5 | "description": "Microsoft MachineLearning Resource Types", 6 | "resourceDefinitions": { 7 | "workspaces": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2016-04-01" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The location of the resource. This cannot be changed after the resource is created." 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the machine learning workspace." 23 | }, 24 | "properties": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/WorkspaceProperties" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ], 33 | "description": "The properties of a machine learning workspace." 34 | }, 35 | "tags": { 36 | "oneOf": [ 37 | { 38 | "type": "object", 39 | "additionalProperties": { 40 | "type": "string" 41 | }, 42 | "properties": {} 43 | }, 44 | { 45 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 46 | } 47 | ], 48 | "description": "The tags of the resource." 49 | }, 50 | "type": { 51 | "type": "string", 52 | "enum": [ 53 | "Microsoft.MachineLearning/workspaces" 54 | ] 55 | } 56 | }, 57 | "required": [ 58 | "apiVersion", 59 | "location", 60 | "name", 61 | "properties", 62 | "type" 63 | ], 64 | "description": "Microsoft.MachineLearning/workspaces" 65 | } 66 | }, 67 | "definitions": { 68 | "WorkspaceProperties": { 69 | "type": "object", 70 | "properties": { 71 | "keyVaultIdentifierId": { 72 | "type": "string", 73 | "description": "The key vault identifier used for encrypted workspaces." 74 | }, 75 | "ownerEmail": { 76 | "type": "string", 77 | "description": "The email id of the owner for this workspace." 78 | }, 79 | "userStorageAccountId": { 80 | "type": "string", 81 | "description": "The fully qualified arm id of the storage account associated with this workspace." 82 | } 83 | }, 84 | "required": [ 85 | "ownerEmail", 86 | "userStorageAccountId" 87 | ], 88 | "description": "The properties of a machine learning workspace." 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2016-06-27-preview-Microsoft.ContainerRegistry.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2016-06-27-preview/Microsoft.ContainerRegistry.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.ContainerRegistry", 5 | "description": "Microsoft ContainerRegistry Resource Types", 6 | "resourceDefinitions": { 7 | "registries": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2016-06-27-preview" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The location of the resource. This cannot be changed after the resource is created." 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the container registry." 23 | }, 24 | "properties": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/RegistryProperties" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ], 33 | "description": "The properties of a container registry." 34 | }, 35 | "tags": { 36 | "oneOf": [ 37 | { 38 | "type": "object", 39 | "additionalProperties": { 40 | "type": "string" 41 | }, 42 | "properties": {} 43 | }, 44 | { 45 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 46 | } 47 | ], 48 | "description": "The tags of the resource." 49 | }, 50 | "type": { 51 | "type": "string", 52 | "enum": [ 53 | "Microsoft.ContainerRegistry/registries" 54 | ] 55 | } 56 | }, 57 | "required": [ 58 | "apiVersion", 59 | "location", 60 | "name", 61 | "properties", 62 | "type" 63 | ], 64 | "description": "Microsoft.ContainerRegistry/registries" 65 | } 66 | }, 67 | "definitions": { 68 | "RegistryProperties": { 69 | "type": "object", 70 | "properties": { 71 | "adminUserEnabled": { 72 | "oneOf": [ 73 | { 74 | "type": "boolean", 75 | "default": false 76 | }, 77 | { 78 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 79 | } 80 | ], 81 | "description": "The value that indicates whether the admin user is enabled. This value is false by default." 82 | }, 83 | "storageAccount": { 84 | "oneOf": [ 85 | { 86 | "$ref": "#/definitions/StorageAccountProperties" 87 | }, 88 | { 89 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 90 | } 91 | ], 92 | "description": "The properties of a storage account for a container registry." 93 | } 94 | }, 95 | "required": [ 96 | "storageAccount" 97 | ], 98 | "description": "The properties of a container registry." 99 | }, 100 | "StorageAccountProperties": { 101 | "type": "object", 102 | "properties": { 103 | "accessKey": { 104 | "type": "string", 105 | "description": "The access key to the storage account." 106 | }, 107 | "name": { 108 | "type": "string", 109 | "description": "The name of the storage account." 110 | } 111 | }, 112 | "required": [ 113 | "accessKey", 114 | "name" 115 | ], 116 | "description": "The properties of a storage account for a container registry." 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2017-01-01-preview-Microsoft.Maps.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2017-01-01-preview/Microsoft.Maps.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.Maps", 5 | "description": "Microsoft Maps Resource Types", 6 | "resourceDefinitions": { 7 | "accounts": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2017-01-01-preview" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The location of the resource." 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the Maps Account." 23 | }, 24 | "sku": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/Sku" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ], 33 | "description": "The SKU of the Maps Account." 34 | }, 35 | "tags": { 36 | "oneOf": [ 37 | { 38 | "type": "object", 39 | "additionalProperties": { 40 | "type": "string" 41 | }, 42 | "properties": {} 43 | }, 44 | { 45 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 46 | } 47 | ], 48 | "description": "Gets or sets a list of key value pairs that describe the resource. These tags can be used in viewing and grouping this resource (across resource groups). A maximum of 15 tags can be provided for a resource. Each tag must have a key no greater than 128 characters and value no greater than 256 characters." 49 | }, 50 | "type": { 51 | "type": "string", 52 | "enum": [ 53 | "Microsoft.Maps/accounts" 54 | ] 55 | } 56 | }, 57 | "required": [ 58 | "apiVersion", 59 | "location", 60 | "name", 61 | "sku", 62 | "type" 63 | ], 64 | "description": "Microsoft.Maps/accounts" 65 | } 66 | }, 67 | "definitions": { 68 | "Sku": { 69 | "type": "object", 70 | "properties": { 71 | "name": { 72 | "type": "string", 73 | "description": "The name of the SKU, in standard format (such as S0)." 74 | } 75 | }, 76 | "required": [ 77 | "name" 78 | ], 79 | "description": "The SKU of the Maps Account." 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2017-08-01-preview-Microsoft.Genomics.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2017-08-01-preview/Microsoft.Genomics.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.Genomics", 5 | "description": "Microsoft Genomics service", 6 | "resourceDefinitions": { 7 | "accounts": { 8 | "type": "object", 9 | "properties": { 10 | "name": { 11 | "type": "string", 12 | "minLength": 3, 13 | "maxLength": 64 14 | }, 15 | "type": { 16 | "type": "string", 17 | "enum": [ 18 | "Microsoft.Genomics/accounts" 19 | ] 20 | }, 21 | "apiVersion": { 22 | "type": "string", 23 | "enum": [ 24 | "2017-08-01-preview" 25 | ] 26 | }, 27 | "location": { 28 | "type": "string", 29 | "description": "Resource location." 30 | }, 31 | "tags": { 32 | "oneOf": [ 33 | { 34 | "type": "object", 35 | "additionalProperties": { 36 | "type": "string" 37 | } 38 | }, 39 | { 40 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 41 | } 42 | ], 43 | "description": "Resource tags." 44 | }, 45 | "etag": { 46 | "type": "string", 47 | "description": "A unique read-only string that changes whenever the resource is updated." 48 | }, 49 | "properties": { 50 | "oneOf": [ 51 | { 52 | "type": "object", 53 | "properties": {} 54 | }, 55 | { 56 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 57 | } 58 | ], 59 | "description": "Must exist in the request. Must not be null." 60 | } 61 | }, 62 | "required": [ 63 | "name", 64 | "type", 65 | "apiVersion", 66 | "location", 67 | "properties" 68 | ], 69 | "description": "Microsoft.Genomics/accounts" 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2017-10-01-Microsoft.Insights.Application.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2017-10-01/Microsoft.Insights.Application.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "microsoft.insights", 5 | "description": "microsoft insights Resource Types", 6 | "resourceDefinitions": { 7 | "components_pricingPlans": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2017-10-01" 14 | ] 15 | }, 16 | "name": { 17 | "oneOf": [ 18 | { 19 | "type": "string", 20 | "pattern": "^.*/current$" 21 | }, 22 | { 23 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 24 | } 25 | ] 26 | }, 27 | "properties": { 28 | "oneOf": [ 29 | { 30 | "$ref": "#/definitions/PricingPlanProperties" 31 | }, 32 | { 33 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 34 | } 35 | ], 36 | "description": "An Application Insights component daily data volume cap" 37 | }, 38 | "type": { 39 | "type": "string", 40 | "enum": [ 41 | "microsoft.insights/components/pricingPlans" 42 | ] 43 | } 44 | }, 45 | "required": [ 46 | "apiVersion", 47 | "name", 48 | "properties", 49 | "type" 50 | ], 51 | "description": "microsoft.insights/components/pricingPlans" 52 | } 53 | }, 54 | "definitions": { 55 | "PricingPlanProperties": { 56 | "type": "object", 57 | "properties": { 58 | "cap": { 59 | "oneOf": [ 60 | { 61 | "type": "number" 62 | }, 63 | { 64 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 65 | } 66 | ], 67 | "description": "Daily data volume cap in GB." 68 | }, 69 | "planType": { 70 | "type": "string", 71 | "description": "Pricing Plan Type Name." 72 | }, 73 | "stopSendNotificationWhenHitCap": { 74 | "oneOf": [ 75 | { 76 | "type": "boolean" 77 | }, 78 | { 79 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 80 | } 81 | ], 82 | "description": "Do not send a notification email when the daily data volume cap is met." 83 | }, 84 | "stopSendNotificationWhenHitThreshold": { 85 | "oneOf": [ 86 | { 87 | "type": "boolean" 88 | }, 89 | { 90 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 91 | } 92 | ], 93 | "description": "Reserved, not used for now." 94 | }, 95 | "warningThreshold": { 96 | "oneOf": [ 97 | { 98 | "type": "integer" 99 | }, 100 | { 101 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 102 | } 103 | ], 104 | "description": "Reserved, not used for now." 105 | } 106 | }, 107 | "description": "An Application Insights component daily data volume cap" 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2018-05-01-Microsoft.Maps.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2018-05-01/Microsoft.Maps.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.Maps", 5 | "description": "Microsoft Maps Resource Types", 6 | "resourceDefinitions": { 7 | "accounts": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2018-05-01" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The location of the resource." 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the Maps Account." 23 | }, 24 | "sku": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/Sku" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ], 33 | "description": "The SKU of the Maps Account." 34 | }, 35 | "tags": { 36 | "oneOf": [ 37 | { 38 | "type": "object", 39 | "additionalProperties": { 40 | "type": "string" 41 | }, 42 | "properties": {} 43 | }, 44 | { 45 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 46 | } 47 | ], 48 | "description": "Gets or sets a list of key value pairs that describe the resource. These tags can be used in viewing and grouping this resource (across resource groups). A maximum of 15 tags can be provided for a resource. Each tag must have a key no greater than 128 characters and value no greater than 256 characters." 49 | }, 50 | "type": { 51 | "type": "string", 52 | "enum": [ 53 | "Microsoft.Maps/accounts" 54 | ] 55 | } 56 | }, 57 | "required": [ 58 | "apiVersion", 59 | "location", 60 | "name", 61 | "sku", 62 | "type" 63 | ], 64 | "description": "Microsoft.Maps/accounts" 65 | } 66 | }, 67 | "definitions": { 68 | "Sku": { 69 | "type": "object", 70 | "properties": { 71 | "name": { 72 | "type": "string", 73 | "description": "The name of the SKU, in standard format (such as S0)." 74 | } 75 | }, 76 | "required": [ 77 | "name" 78 | ], 79 | "description": "The SKU of the Maps Account." 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2018-06-17-preview-Microsoft.Insights.Application.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2018-06-17-preview/Microsoft.Insights.Application.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "microsoft.insights", 5 | "description": "microsoft insights Resource Types", 6 | "resourceDefinitions": { 7 | "workbooks": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2018-06-17-preview" 14 | ] 15 | }, 16 | "kind": { 17 | "oneOf": [ 18 | { 19 | "type": "string", 20 | "enum": [ 21 | "user", 22 | "shared" 23 | ] 24 | }, 25 | { 26 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 27 | } 28 | ], 29 | "description": "The kind of workbook. Choices are user and shared." 30 | }, 31 | "location": { 32 | "type": "string", 33 | "description": "Resource location" 34 | }, 35 | "name": { 36 | "type": "string", 37 | "description": "The name of the Application Insights component resource." 38 | }, 39 | "properties": { 40 | "oneOf": [ 41 | { 42 | "$ref": "#/definitions/WorkbookProperties" 43 | }, 44 | { 45 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 46 | } 47 | ], 48 | "description": "Properties that contain a workbook." 49 | }, 50 | "tags": { 51 | "oneOf": [ 52 | { 53 | "type": "object", 54 | "additionalProperties": { 55 | "type": "string" 56 | }, 57 | "properties": {} 58 | }, 59 | { 60 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 61 | } 62 | ], 63 | "description": "Resource tags" 64 | }, 65 | "type": { 66 | "type": "string", 67 | "enum": [ 68 | "microsoft.insights/workbooks" 69 | ] 70 | } 71 | }, 72 | "required": [ 73 | "apiVersion", 74 | "location", 75 | "name", 76 | "properties", 77 | "type" 78 | ], 79 | "description": "microsoft.insights/workbooks" 80 | } 81 | }, 82 | "definitions": { 83 | "WorkbookProperties": { 84 | "type": "object", 85 | "properties": { 86 | "category": { 87 | "type": "string", 88 | "description": "Workbook category, as defined by the user at creation time." 89 | }, 90 | "displayName": { 91 | "type": "string", 92 | "description": "The user-defined name (display name) of the workbook." 93 | }, 94 | "serializedData": { 95 | "type": "string", 96 | "description": "Configuration of this particular workbook. Configuration data is a string containing valid JSON" 97 | }, 98 | "sourceId": { 99 | "type": "string", 100 | "description": "ResourceId for a source resource." 101 | }, 102 | "tags": { 103 | "oneOf": [ 104 | { 105 | "type": "array", 106 | "items": { 107 | "type": "string" 108 | } 109 | }, 110 | { 111 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 112 | } 113 | ], 114 | "description": "A list of 0 or more tags that are associated with this workbook definition" 115 | }, 116 | "version": { 117 | "type": "string", 118 | "description": "Workbook version" 119 | } 120 | }, 121 | "required": [ 122 | "category", 123 | "displayName", 124 | "serializedData" 125 | ], 126 | "description": "Properties that contain a workbook." 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2018-09-01-Microsoft.IotCentral.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2018-09-01/Microsoft.IotCentral.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.IoTCentral", 5 | "description": "Microsoft IoTCentral Resource Types", 6 | "resourceDefinitions": { 7 | "iotApps": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2018-09-01" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The resource location." 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The ARM resource name of the IoT Central application." 23 | }, 24 | "properties": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/AppProperties" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ], 33 | "description": "The properties of an IoT Central application." 34 | }, 35 | "sku": { 36 | "oneOf": [ 37 | { 38 | "$ref": "#/definitions/AppSkuInfo" 39 | }, 40 | { 41 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 42 | } 43 | ], 44 | "description": "Information about the SKU of the IoT Central application." 45 | }, 46 | "tags": { 47 | "oneOf": [ 48 | { 49 | "type": "object", 50 | "additionalProperties": { 51 | "type": "string" 52 | }, 53 | "properties": {} 54 | }, 55 | { 56 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 57 | } 58 | ], 59 | "description": "The resource tags." 60 | }, 61 | "type": { 62 | "type": "string", 63 | "enum": [ 64 | "Microsoft.IoTCentral/iotApps" 65 | ] 66 | } 67 | }, 68 | "required": [ 69 | "apiVersion", 70 | "location", 71 | "name", 72 | "properties", 73 | "sku", 74 | "type" 75 | ], 76 | "description": "Microsoft.IoTCentral/iotApps" 77 | } 78 | }, 79 | "definitions": { 80 | "AppProperties": { 81 | "type": "object", 82 | "properties": { 83 | "displayName": { 84 | "type": "string", 85 | "description": "The display name of the application." 86 | }, 87 | "subdomain": { 88 | "type": "string", 89 | "description": "The subdomain of the application." 90 | }, 91 | "template": { 92 | "type": "string", 93 | "description": "The ID of the application template, which is a blueprint that defines the characteristics and behaviors of an application. Optional; if not specified, defaults to a blank blueprint and allows the application to be defined from scratch." 94 | } 95 | }, 96 | "description": "The properties of an IoT Central application." 97 | }, 98 | "AppSkuInfo": { 99 | "type": "object", 100 | "properties": { 101 | "name": { 102 | "oneOf": [ 103 | { 104 | "type": "string", 105 | "enum": [ 106 | "F1", 107 | "S1", 108 | "ST0", 109 | "ST1", 110 | "ST2" 111 | ] 112 | }, 113 | { 114 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 115 | } 116 | ], 117 | "description": "The name of the SKU." 118 | } 119 | }, 120 | "required": [ 121 | "name" 122 | ], 123 | "description": "Information about the SKU of the IoT Central application." 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2018-09-01-preview-Microsoft.BareMetal.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2018-09-01-preview/Microsoft.BareMetal.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.BareMetal", 5 | "description": "BareMetal Resource Types", 6 | "resourceDefinitions": { 7 | "crayServers": { 8 | "type": "object", 9 | "properties": { 10 | "name": { 11 | "oneOf": [ 12 | { 13 | "type": "string" 14 | }, 15 | { 16 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 17 | } 18 | ], 19 | "description": "Resource name" 20 | }, 21 | "type": { 22 | "type": "string", 23 | "description": "The resource type.", 24 | "enum": [ 25 | "Microsoft.BareMetal/crayServers" 26 | ] 27 | }, 28 | "apiVersion": { 29 | "type": "string", 30 | "enum": [ 31 | "2018-09-01-preview" 32 | ] 33 | }, 34 | "location": { 35 | "oneOf": [ 36 | { 37 | "type": "string" 38 | }, 39 | { 40 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 41 | } 42 | ], 43 | "description": "Resource location." 44 | }, 45 | "tags": { 46 | "oneOf": [ 47 | { 48 | "type": "object", 49 | "additionalProperties": { 50 | "type": "string" 51 | } 52 | }, 53 | { 54 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 55 | } 56 | ], 57 | "description": "Resource tags." 58 | }, 59 | "properties": { 60 | "oneOf": [ 61 | { 62 | "$ref": "#/definitions/crayServersProperties" 63 | }, 64 | { 65 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 66 | } 67 | ] 68 | } 69 | }, 70 | "required": [ 71 | "name", 72 | "type", 73 | "location", 74 | "apiVersion", 75 | "properties" 76 | ], 77 | "description": "Cray Servers resource." 78 | } 79 | }, 80 | "definitions": { 81 | "crayServersProperties": { 82 | "type": "object", 83 | "properties": { 84 | "ipAddress": { 85 | "type": "string", 86 | "description": "Ip Address." 87 | }, 88 | "subnetResourceId": { 89 | "type": "string", 90 | "description": "Subnet resource ID." 91 | } 92 | }, 93 | "required": [ 94 | "ipAddress", 95 | "subnetResourceId" 96 | ], 97 | "description": "Cray Servers properties." 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2018-09-01-preview-Microsoft.ResourceGraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2018-09-01-preview/Microsoft.ResourceGraph.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.ResourceGraph", 5 | "description": "Microsoft ResourceGraph Resource Types", 6 | "resourceDefinitions": { 7 | "queries": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2018-09-01-preview" 14 | ] 15 | }, 16 | "etag": { 17 | "type": "string", 18 | "description": "This will be used to handle Optimistic Concurrency. If not present, it will always overwrite the existing resource without checking conflict." 19 | }, 20 | "location": { 21 | "type": "string", 22 | "description": "The location of the resource" 23 | }, 24 | "name": { 25 | "type": "string", 26 | "description": "The name of the Graph Query resource." 27 | }, 28 | "properties": { 29 | "oneOf": [ 30 | { 31 | "$ref": "#/definitions/GraphQueryProperties" 32 | }, 33 | { 34 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 35 | } 36 | ], 37 | "description": "Properties that contain a graph query." 38 | }, 39 | "tags": { 40 | "oneOf": [ 41 | { 42 | "type": "object", 43 | "additionalProperties": { 44 | "type": "string" 45 | }, 46 | "properties": {} 47 | }, 48 | { 49 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 50 | } 51 | ], 52 | "description": "Resource tags" 53 | }, 54 | "type": { 55 | "type": "string", 56 | "enum": [ 57 | "Microsoft.ResourceGraph/queries" 58 | ] 59 | } 60 | }, 61 | "required": [ 62 | "apiVersion", 63 | "name", 64 | "properties", 65 | "type" 66 | ], 67 | "description": "Microsoft.ResourceGraph/queries" 68 | } 69 | }, 70 | "definitions": { 71 | "GraphQueryProperties": { 72 | "type": "object", 73 | "properties": { 74 | "description": { 75 | "type": "string", 76 | "description": "The description of a graph query." 77 | }, 78 | "query": { 79 | "type": "string", 80 | "description": "KQL query that will be graph." 81 | } 82 | }, 83 | "required": [ 84 | "query" 85 | ], 86 | "description": "Properties that contain a graph query." 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2018-11-30-Microsoft.ManagedIdentity.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2018-11-30/Microsoft.ManagedIdentity.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.ManagedIdentity", 5 | "description": "Microsoft ManagedIdentity Resource Types", 6 | "resourceDefinitions": { 7 | "userAssignedIdentities": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2018-11-30" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The geo-location where the resource lives" 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the identity resource." 23 | }, 24 | "tags": { 25 | "oneOf": [ 26 | { 27 | "type": "object", 28 | "additionalProperties": { 29 | "type": "string" 30 | }, 31 | "properties": {} 32 | }, 33 | { 34 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 35 | } 36 | ], 37 | "description": "Resource tags." 38 | }, 39 | "type": { 40 | "type": "string", 41 | "enum": [ 42 | "Microsoft.ManagedIdentity/userAssignedIdentities" 43 | ] 44 | } 45 | }, 46 | "required": [ 47 | "apiVersion", 48 | "location", 49 | "name", 50 | "type" 51 | ], 52 | "description": "Microsoft.ManagedIdentity/userAssignedIdentities" 53 | } 54 | }, 55 | "definitions": {} 56 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2019-02-01-preview-Microsoft.AppConfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2019-02-01-preview/Microsoft.AppConfiguration.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.AppConfiguration", 5 | "description": "Microsoft AppConfiguration Resource Types", 6 | "resourceDefinitions": { 7 | "configurationStores": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2019-02-01-preview" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The location of the resource. This cannot be changed after the resource is created." 19 | }, 20 | "name": { 21 | "oneOf": [ 22 | { 23 | "type": "string", 24 | "pattern": "^[a-zA-Z0-9_-]*$", 25 | "minLength": 5, 26 | "maxLength": 50 27 | }, 28 | { 29 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 30 | } 31 | ], 32 | "description": "The name of the configuration store." 33 | }, 34 | "properties": { 35 | "oneOf": [ 36 | { 37 | "$ref": "#/definitions/ConfigurationStoreProperties" 38 | }, 39 | { 40 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 41 | } 42 | ], 43 | "description": "The properties of a configuration store." 44 | }, 45 | "tags": { 46 | "oneOf": [ 47 | { 48 | "type": "object", 49 | "additionalProperties": { 50 | "type": "string" 51 | }, 52 | "properties": {} 53 | }, 54 | { 55 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 56 | } 57 | ], 58 | "description": "The tags of the resource." 59 | }, 60 | "type": { 61 | "type": "string", 62 | "enum": [ 63 | "Microsoft.AppConfiguration/configurationStores" 64 | ] 65 | } 66 | }, 67 | "required": [ 68 | "apiVersion", 69 | "location", 70 | "name", 71 | "properties", 72 | "type" 73 | ], 74 | "description": "Microsoft.AppConfiguration/configurationStores" 75 | } 76 | }, 77 | "definitions": { 78 | "ConfigurationStoreProperties": { 79 | "type": "object", 80 | "properties": {}, 81 | "description": "The properties of a configuration store." 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2019-10-01-Microsoft.MachineLearning.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2019-10-01/Microsoft.MachineLearning.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.MachineLearning", 5 | "description": "Microsoft MachineLearning Resource Types", 6 | "resourceDefinitions": { 7 | "workspaces": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2019-10-01" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The location of the resource. This cannot be changed after the resource is created." 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the machine learning workspace." 23 | }, 24 | "properties": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/WorkspaceProperties" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ], 33 | "description": "The properties of a machine learning workspace." 34 | }, 35 | "sku": { 36 | "oneOf": [ 37 | { 38 | "$ref": "#/definitions/Sku" 39 | }, 40 | { 41 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 42 | } 43 | ], 44 | "description": "Sku of the resource" 45 | }, 46 | "tags": { 47 | "oneOf": [ 48 | { 49 | "type": "object", 50 | "additionalProperties": { 51 | "type": "string" 52 | }, 53 | "properties": {} 54 | }, 55 | { 56 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 57 | } 58 | ], 59 | "description": "The tags of the resource." 60 | }, 61 | "type": { 62 | "type": "string", 63 | "enum": [ 64 | "Microsoft.MachineLearning/workspaces" 65 | ] 66 | } 67 | }, 68 | "required": [ 69 | "apiVersion", 70 | "location", 71 | "name", 72 | "properties", 73 | "type" 74 | ], 75 | "description": "Microsoft.MachineLearning/workspaces" 76 | } 77 | }, 78 | "definitions": { 79 | "Sku": { 80 | "type": "object", 81 | "properties": { 82 | "name": { 83 | "type": "string", 84 | "description": "Name of the sku" 85 | }, 86 | "tier": { 87 | "type": "string", 88 | "description": "Tier of the sku like Basic or Enterprise" 89 | } 90 | }, 91 | "description": "Sku of the resource" 92 | }, 93 | "WorkspaceProperties": { 94 | "type": "object", 95 | "properties": { 96 | "keyVaultIdentifierId": { 97 | "type": "string", 98 | "description": "The key vault identifier used for encrypted workspaces." 99 | }, 100 | "ownerEmail": { 101 | "type": "string", 102 | "description": "The email id of the owner for this workspace." 103 | }, 104 | "userStorageAccountId": { 105 | "type": "string", 106 | "description": "The fully qualified arm id of the storage account associated with this workspace." 107 | } 108 | }, 109 | "required": [ 110 | "ownerEmail", 111 | "userStorageAccountId" 112 | ], 113 | "description": "The properties of a machine learning workspace." 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2020-03-01-preview-Microsoft.Insights.Application.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2020-03-01-preview/Microsoft.Insights.Application.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "microsoft.insights", 5 | "description": "microsoft insights Resource Types", 6 | "resourceDefinitions": { 7 | "components_linkedStorageAccounts": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2020-03-01-preview" 14 | ] 15 | }, 16 | "name": { 17 | "oneOf": [ 18 | { 19 | "type": "string", 20 | "pattern": "^.*/ServiceProfiler$" 21 | }, 22 | { 23 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 24 | } 25 | ], 26 | "description": "The type of the Application Insights component data source for the linked storage account." 27 | }, 28 | "properties": { 29 | "oneOf": [ 30 | { 31 | "$ref": "#/definitions/LinkedStorageAccountsProperties" 32 | }, 33 | { 34 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 35 | } 36 | ], 37 | "description": "An Application Insights component linked storage account" 38 | }, 39 | "type": { 40 | "type": "string", 41 | "enum": [ 42 | "microsoft.insights/components/linkedStorageAccounts" 43 | ] 44 | } 45 | }, 46 | "required": [ 47 | "apiVersion", 48 | "name", 49 | "properties", 50 | "type" 51 | ], 52 | "description": "microsoft.insights/components/linkedStorageAccounts" 53 | } 54 | }, 55 | "definitions": { 56 | "LinkedStorageAccountsProperties": { 57 | "type": "object", 58 | "properties": { 59 | "linkedStorageAccount": { 60 | "type": "string", 61 | "description": "Linked storage account resource ID" 62 | } 63 | }, 64 | "description": "An Application Insights component linked storage account" 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2020-07-01-preview-Microsoft.Authorization.Resources.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2020-07-01-preview/Microsoft.Authorization.Resources.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.Authorization", 5 | "description": "Microsoft Authorization Resource Types", 6 | "resourceDefinitions": {}, 7 | "unknown_resourceDefinitions": { 8 | "policyExemptions": { 9 | "type": "object", 10 | "properties": { 11 | "apiVersion": { 12 | "type": "string", 13 | "enum": [ 14 | "2020-07-01-preview" 15 | ] 16 | }, 17 | "name": { 18 | "type": "string", 19 | "description": "The name of the policy exemption to delete." 20 | }, 21 | "properties": { 22 | "oneOf": [ 23 | { 24 | "$ref": "#/definitions/PolicyExemptionProperties" 25 | }, 26 | { 27 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 28 | } 29 | ], 30 | "description": "The policy exemption properties." 31 | }, 32 | "type": { 33 | "type": "string", 34 | "enum": [ 35 | "Microsoft.Authorization/policyExemptions" 36 | ] 37 | } 38 | }, 39 | "required": [ 40 | "apiVersion", 41 | "name", 42 | "properties", 43 | "type" 44 | ], 45 | "description": "Microsoft.Authorization/policyExemptions" 46 | } 47 | }, 48 | "definitions": { 49 | "PolicyExemptionProperties": { 50 | "type": "object", 51 | "properties": { 52 | "description": { 53 | "type": "string", 54 | "description": "The description of the policy exemption." 55 | }, 56 | "displayName": { 57 | "type": "string", 58 | "description": "The display name of the policy exemption." 59 | }, 60 | "exemptionCategory": { 61 | "oneOf": [ 62 | { 63 | "type": "string", 64 | "enum": [ 65 | "Waiver", 66 | "Mitigated" 67 | ] 68 | }, 69 | { 70 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 71 | } 72 | ], 73 | "description": "The policy exemption category. Possible values are Waiver and Mitigated." 74 | }, 75 | "expiresOn": { 76 | "type": "string", 77 | "format": "date-time", 78 | "description": "The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption." 79 | }, 80 | "metadata": { 81 | "type": "object", 82 | "properties": {}, 83 | "description": "The policy exemption metadata. Metadata is an open ended object and is typically a collection of key value pairs." 84 | }, 85 | "policyAssignmentId": { 86 | "type": "string", 87 | "description": "The ID of the policy assignment that is being exempted." 88 | }, 89 | "policyDefinitionReferenceIds": { 90 | "oneOf": [ 91 | { 92 | "type": "array", 93 | "items": { 94 | "type": "string" 95 | } 96 | }, 97 | { 98 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 99 | } 100 | ], 101 | "description": "The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition." 102 | } 103 | }, 104 | "required": [ 105 | "exemptionCategory", 106 | "policyAssignmentId" 107 | ], 108 | "description": "The policy exemption properties." 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /test/__fixtures__/https---schema.management.azure.com-schemas-2020-08-20-preview-Microsoft.Communication.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https://schema.management.azure.com/schemas/2020-08-20-preview/Microsoft.Communication.json#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Microsoft.Communication", 5 | "description": "Microsoft Communication Resource Types", 6 | "resourceDefinitions": { 7 | "communicationServices": { 8 | "type": "object", 9 | "properties": { 10 | "apiVersion": { 11 | "type": "string", 12 | "enum": [ 13 | "2020-08-20-preview" 14 | ] 15 | }, 16 | "location": { 17 | "type": "string", 18 | "description": "The Azure location where the CommunicationService is running." 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "The name of the CommunicationService resource." 23 | }, 24 | "properties": { 25 | "oneOf": [ 26 | { 27 | "$ref": "#/definitions/CommunicationServiceProperties" 28 | }, 29 | { 30 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 31 | } 32 | ], 33 | "description": "A class that describes the properties of the CommunicationService." 34 | }, 35 | "tags": { 36 | "oneOf": [ 37 | { 38 | "type": "object", 39 | "additionalProperties": { 40 | "type": "string" 41 | }, 42 | "properties": {} 43 | }, 44 | { 45 | "$ref": "https://schema.management.azure.com/schemas/common/definitions.json#/definitions/expression" 46 | } 47 | ], 48 | "description": "Tags of the service which is a list of key value pairs that describe the resource." 49 | }, 50 | "type": { 51 | "type": "string", 52 | "enum": [ 53 | "Microsoft.Communication/communicationServices" 54 | ] 55 | } 56 | }, 57 | "required": [ 58 | "apiVersion", 59 | "name", 60 | "properties", 61 | "type" 62 | ], 63 | "description": "Microsoft.Communication/communicationServices" 64 | } 65 | }, 66 | "definitions": { 67 | "CommunicationServiceProperties": { 68 | "type": "object", 69 | "properties": { 70 | "dataLocation": { 71 | "type": "string", 72 | "description": "The location where the communication service stores its data at rest." 73 | } 74 | }, 75 | "required": [ 76 | "dataLocation" 77 | ], 78 | "description": "A class that describes the properties of the CommunicationService." 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /test/e2e.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable ts/no-require-imports */ 2 | 3 | import { readdirSync } from 'node:fs' 4 | import { join } from 'node:path' 5 | import { fileURLToPath } from 'node:url' 6 | import type { FileInfo } from '@apidevtools/json-schema-ref-parser' 7 | import { expect, it } from 'vitest' 8 | import find from 'lodash-es/find' 9 | import merge from 'lodash-es/merge' 10 | import type { JSONSchema, Options } from '../src' 11 | import { compile } from '../src' 12 | import { stripExtension } from '../src/utils' 13 | import { getWithCache } from './http' 14 | 15 | const dir = fileURLToPath(new URL('e2e', import.meta.url)) 16 | 17 | interface TestCase { 18 | input: JSONSchema 19 | error?: true 20 | exclude?: boolean 21 | only?: boolean 22 | options?: Options 23 | } 24 | 25 | export function hasOnly() { 26 | return readdirSync(dir) 27 | .filter(_ => /^.*\.js$/.test(_)) 28 | .map(_ => require(join(dir, _))) 29 | .some(_ => _.only) 30 | } 31 | 32 | export async function run() { 33 | // [filename, absolute dirname, contents][] 34 | const modules = await Promise.all( 35 | readdirSync(dir) 36 | .filter(_ => !_.includes('.ignore.')) 37 | .map(async _ => [_, await import(join(dir, _))] as [string, TestCase]), 38 | ) 39 | 40 | // exporting `const only=true` will only run that test 41 | // exporting `const exclude=true` will not run that test 42 | const only = find(modules, _ => Boolean(_[1].only)) 43 | if (only) 44 | runOne(only[1], only[0]) 45 | else 46 | modules.filter(_ => !_[1].exclude).forEach(_ => runOne(_[1], _[0])) 47 | } 48 | 49 | const httpWithCacheResolver = { 50 | order: 1, 51 | canRead: /^https?:/i, 52 | async read({ url }: FileInfo) { 53 | return await getWithCache(url) 54 | }, 55 | } 56 | 57 | function runOne(exports: TestCase, name: string) { 58 | // log('blue', 'Running test', name) 59 | 60 | const options = merge(exports.options, { $refOptions: { resolve: { http: httpWithCacheResolver } } }) 61 | 62 | it(name, async () => { 63 | if (exports.error) { 64 | try { 65 | await compile(exports.input, stripExtension(name), options) 66 | } 67 | catch (e) { 68 | expect(e instanceof Error).toBe(true) 69 | } 70 | } 71 | else { 72 | expect( 73 | await compile(exports.input, stripExtension(name), options), 74 | ).toMatchSnapshot() 75 | } 76 | }) 77 | } 78 | 79 | await run() 80 | -------------------------------------------------------------------------------- /test/e2e/JSONSchema.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | id: 'http://json-schema.org/draft-04/schema#', 3 | $schema: 'http://json-schema.org/draft-04/schema#', 4 | description: 'Core schema meta-schema', 5 | definitions: { 6 | schemaArray: { 7 | type: 'array', 8 | minItems: 1, 9 | items: { $ref: '#' }, 10 | }, 11 | positiveInteger: { 12 | type: 'integer', 13 | minimum: 0, 14 | }, 15 | positiveIntegerDefault0: { 16 | allOf: [{ $ref: '#/definitions/positiveInteger' }, { default: 0 }], 17 | }, 18 | simpleTypes: { 19 | enum: ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'], 20 | }, 21 | stringArray: { 22 | type: 'array', 23 | items: { type: 'string' }, 24 | minItems: 1, 25 | uniqueItems: true, 26 | }, 27 | }, 28 | type: 'object', 29 | properties: { 30 | id: { 31 | type: 'string', 32 | format: 'uri', 33 | }, 34 | $schema: { 35 | type: 'string', 36 | format: 'uri', 37 | }, 38 | title: { 39 | type: 'string', 40 | }, 41 | description: { 42 | type: 'string', 43 | }, 44 | default: {}, 45 | multipleOf: { 46 | type: 'number', 47 | minimum: 0, 48 | exclusiveMinimum: true, 49 | }, 50 | maximum: { 51 | type: 'number', 52 | }, 53 | exclusiveMaximum: { 54 | type: 'boolean', 55 | default: false, 56 | }, 57 | minimum: { 58 | type: 'number', 59 | }, 60 | exclusiveMinimum: { 61 | type: 'boolean', 62 | default: false, 63 | }, 64 | maxLength: { $ref: '#/definitions/positiveInteger' }, 65 | minLength: { $ref: '#/definitions/positiveIntegerDefault0' }, 66 | pattern: { 67 | type: 'string', 68 | format: 'regex', 69 | }, 70 | additionalItems: { 71 | anyOf: [{ type: 'boolean' }, { $ref: '#' }], 72 | default: {}, 73 | }, 74 | items: { 75 | anyOf: [{ $ref: '#' }, { $ref: '#/definitions/schemaArray' }], 76 | default: {}, 77 | }, 78 | maxItems: { $ref: '#/definitions/positiveInteger' }, 79 | minItems: { $ref: '#/definitions/positiveIntegerDefault0' }, 80 | uniqueItems: { 81 | type: 'boolean', 82 | default: false, 83 | }, 84 | maxProperties: { $ref: '#/definitions/positiveInteger' }, 85 | minProperties: { $ref: '#/definitions/positiveIntegerDefault0' }, 86 | required: { $ref: '#/definitions/stringArray' }, 87 | additionalProperties: { 88 | anyOf: [{ type: 'boolean' }, { $ref: '#' }], 89 | default: {}, 90 | }, 91 | definitions: { 92 | type: 'object', 93 | additionalProperties: { $ref: '#' }, 94 | default: {}, 95 | }, 96 | properties: { 97 | type: 'object', 98 | additionalProperties: { $ref: '#' }, 99 | default: {}, 100 | }, 101 | patternProperties: { 102 | type: 'object', 103 | additionalProperties: { $ref: '#' }, 104 | default: {}, 105 | }, 106 | dependencies: { 107 | type: 'object', 108 | additionalProperties: { 109 | anyOf: [{ $ref: '#' }, { $ref: '#/definitions/stringArray' }], 110 | }, 111 | }, 112 | enum: { 113 | type: 'array', 114 | minItems: 1, 115 | uniqueItems: true, 116 | }, 117 | type: { 118 | anyOf: [ 119 | { $ref: '#/definitions/simpleTypes' }, 120 | { 121 | type: 'array', 122 | items: { $ref: '#/definitions/simpleTypes' }, 123 | minItems: 1, 124 | uniqueItems: true, 125 | }, 126 | ], 127 | }, 128 | allOf: { $ref: '#/definitions/schemaArray' }, 129 | anyOf: { $ref: '#/definitions/schemaArray' }, 130 | oneOf: { $ref: '#/definitions/schemaArray' }, 131 | not: { $ref: '#' }, 132 | }, 133 | dependencies: { 134 | exclusiveMaximum: ['maximum'], 135 | exclusiveMinimum: ['minimum'], 136 | }, 137 | default: {}, 138 | } 139 | -------------------------------------------------------------------------------- /test/e2e/WithComment.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'WithComment', 3 | type: 'object', 4 | properties: { 5 | a: { 6 | type: 'object', 7 | description: '/* comment */', 8 | properties: { 9 | b: { 10 | type: 'string', 11 | description: '/* nested comment */', 12 | }, 13 | }, 14 | }, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /test/e2e/additionalProperties.1.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'AdditionalProperties', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | type: 'string', 7 | }, 8 | }, 9 | additionalProperties: { 10 | type: 'number', 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /test/e2e/additionalProperties.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'AdditionalProperties (configured to default to false)', 3 | type: 'object', 4 | definitions: { 5 | e: { 6 | type: 'object', 7 | }, 8 | }, 9 | properties: { 10 | a: { 11 | type: 'object', 12 | }, 13 | b: { 14 | type: 'object', 15 | additionalProperties: false, 16 | }, 17 | c: { 18 | type: 'object', 19 | additionalProperties: true, 20 | }, 21 | d: { 22 | type: 'object', 23 | additionalProperties: { 24 | type: 'number', 25 | }, 26 | }, 27 | e: { 28 | $ref: '#/definitions/e', 29 | }, 30 | }, 31 | } 32 | 33 | export const options = { 34 | additionalProperties: false, 35 | } 36 | -------------------------------------------------------------------------------- /test/e2e/additionalProperties.3.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'AdditionalProperties (default to true)', 3 | type: 'object', 4 | definitions: { 5 | e: { 6 | type: 'object', 7 | }, 8 | }, 9 | properties: { 10 | a: { 11 | type: 'object', 12 | }, 13 | b: { 14 | type: 'object', 15 | additionalProperties: false, 16 | }, 17 | c: { 18 | type: 'object', 19 | additionalProperties: true, 20 | }, 21 | d: { 22 | type: 'object', 23 | additionalProperties: { 24 | type: 'number', 25 | }, 26 | }, 27 | e: { 28 | $ref: '#/definitions/e', 29 | }, 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /test/e2e/allOf.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'AllOf', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | type: 'object', 7 | allOf: [{ $ref: '#/definitions/foo' }, { $ref: '#/definitions/bar' }], 8 | }, 9 | }, 10 | definitions: { 11 | foo: { 12 | properties: { 13 | a: { type: 'string' }, 14 | b: { type: 'integer' }, 15 | }, 16 | additionalProperties: false, 17 | required: ['a', 'b'], 18 | }, 19 | bar: { 20 | properties: { 21 | a: { type: 'string' }, 22 | }, 23 | additionalProperties: false, 24 | required: ['a', 'b'], 25 | }, 26 | }, 27 | required: ['foo', 'bar'], 28 | additionalProperties: false, 29 | } 30 | -------------------------------------------------------------------------------- /test/e2e/anyOf.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'AnyOf', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | type: 'object', 7 | anyOf: [{ $ref: '#/definitions/foo' }, { $ref: '#/definitions/bar' }, { $ref: '#/definitions/baz' }], 8 | }, 9 | }, 10 | definitions: { 11 | foo: { 12 | properties: { 13 | a: { type: 'string' }, 14 | b: { type: 'integer' }, 15 | }, 16 | additionalProperties: false, 17 | required: ['a'], 18 | }, 19 | bar: { 20 | properties: { 21 | a: { enum: ['a', 'b', 'c'] }, 22 | bam: { 23 | type: 'array', 24 | items: { 25 | anyOf: [ 26 | { 27 | enum: ['wam'], 28 | }, 29 | ], 30 | }, 31 | }, 32 | }, 33 | }, 34 | baz: { 35 | properties: { 36 | baz: { $ref: '#/definitions/bar' }, 37 | }, 38 | }, 39 | }, 40 | required: ['foo'], 41 | additionalProperties: false, 42 | } 43 | -------------------------------------------------------------------------------- /test/e2e/anyOfRoot.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'RootAnyOf', 3 | anyOf: [{ $ref: '#/definitions/foo' }, { $ref: '#/definitions/bar' }, { $ref: '#/definitions/baz' }], 4 | definitions: { 5 | foo: { 6 | properties: { 7 | a: { type: 'string' }, 8 | b: { type: 'integer' }, 9 | }, 10 | additionalProperties: false, 11 | required: ['a'], 12 | }, 13 | bar: { 14 | properties: { 15 | a: { enum: ['a', 'b', 'c'] }, 16 | }, 17 | }, 18 | baz: { 19 | properties: { 20 | baz: { $ref: '#/definitions/bar' }, 21 | }, 22 | }, 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /test/e2e/arrayOfEnum.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | properties: { 4 | namedEnum: { 5 | type: 'array', 6 | items: { 7 | enum: [1, 2, 3], 8 | title: 'NamedEnum', 9 | tsEnumNames: ['One', 'Two', 'Three'], 10 | }, 11 | }, 12 | tuples: { 13 | type: 'array', 14 | items: [ 15 | { type: 'string' }, 16 | { 17 | enum: [1, 2, 3], 18 | title: 'NamedEnum2', 19 | tsEnumNames: ['One', 'Two', 'Three'], 20 | }, 21 | ], 22 | }, 23 | }, 24 | required: ['namedEnum'], 25 | additionalProperties: false, 26 | } 27 | -------------------------------------------------------------------------------- /test/e2e/arrayOfSchema.2.ts: -------------------------------------------------------------------------------- 1 | // @see https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/43bfc6b/test-schema.json 2 | export const input = { 3 | $schema: 'http://json-schema.org/draft-04/schema#', 4 | type: 'array', 5 | items: { 6 | type: 'object', 7 | required: ['description', 'schema', 'tests'], 8 | properties: { 9 | description: { type: 'string' }, 10 | schema: {}, 11 | tests: { 12 | type: 'array', 13 | items: { 14 | type: 'object', 15 | required: ['description', 'data', 'valid'], 16 | properties: { 17 | description: { type: 'string' }, 18 | data: {}, 19 | valid: { type: 'boolean' }, 20 | }, 21 | additionalProperties: false, 22 | }, 23 | minItems: 1, 24 | }, 25 | }, 26 | additionalProperties: false, 27 | minItems: 1, 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /test/e2e/arrayOfSchema.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | name: 'Array of schema', 3 | type: 'object', 4 | properties: { 5 | countries: { 6 | id: 'Countries', 7 | type: 'array', 8 | items: { 9 | type: 'object', 10 | properties: { 11 | id: { 12 | type: 'string', 13 | minLength: 2, 14 | maxLength: 2, 15 | pattern: '[A-Z]+', 16 | }, 17 | name: { 18 | type: 'string', 19 | }, 20 | }, 21 | required: ['id', 'name'], 22 | }, 23 | }, 24 | tuple: { 25 | type: 'array', 26 | items: [ 27 | { 28 | type: 'object', 29 | properties: { 30 | foo: { type: 'string' }, 31 | }, 32 | }, 33 | { 34 | type: 'object', 35 | properties: { 36 | bar: { type: 'number' }, 37 | }, 38 | }, 39 | ], 40 | }, 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /test/e2e/arrayOfType.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Array of type', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | items: { 7 | type: 'string', 8 | }, 9 | type: 'array', 10 | }, 11 | bar: { 12 | items: { 13 | type: 'string', 14 | }, 15 | type: ['array'], 16 | }, 17 | baz: { 18 | items: { 19 | type: ['string', 'number'], 20 | }, 21 | type: ['array'], 22 | }, 23 | moo: { 24 | items: [{ type: 'integer' }, { type: 'string' }], 25 | }, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /test/e2e/basics.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Example Schema', 3 | type: 'object', 4 | properties: { 5 | firstName: { 6 | type: 'string', 7 | }, 8 | lastName: { 9 | id: 'lastName', 10 | type: 'string', 11 | }, 12 | age: { 13 | description: 'Age in years', 14 | type: 'integer', 15 | minimum: 0, 16 | }, 17 | height: { 18 | $id: 'height', 19 | type: 'number', 20 | }, 21 | favoriteFoods: { 22 | type: 'array', 23 | }, 24 | likesDogs: { 25 | type: 'boolean', 26 | }, 27 | }, 28 | required: ['firstName', 'lastName'], 29 | } 30 | -------------------------------------------------------------------------------- /test/e2e/booleanSchema.1.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Schema with boolean subschemas (unknownAny)', 3 | properties: { 4 | always: true, 5 | never: false, 6 | }, 7 | } 8 | 9 | export const options = { 10 | unknownAny: true, 11 | } 12 | -------------------------------------------------------------------------------- /test/e2e/booleanSchema.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Schema with boolean subschemas', 3 | properties: { 4 | always: true, 5 | never: false, 6 | }, 7 | } 8 | 9 | export const options = { 10 | unknownAny: false, 11 | } 12 | -------------------------------------------------------------------------------- /test/e2e/customName.1.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from '../../src/' 2 | 3 | export const input = { 4 | type: 'object', 5 | id: 'FooId', 6 | title: 'FooTitle', 7 | definitions: { 8 | defaa: { id: 'defaa-id' }, 9 | defab: { id: 'defab-id' }, 10 | }, 11 | properties: { 12 | propaaa: { $ref: '#/definitions/defaa' }, 13 | propaab: { $ref: '#/definitions/defaa' }, 14 | propbbb: { id: 'propbbb-id', title: 'propbbb-title' }, 15 | }, 16 | } 17 | 18 | export const options: Partial = { 19 | customName: (schema, keyName) => { 20 | return `CustomPrefix_${keyName || schema.$ref || 'FooRootName'}` 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /test/e2e/customType.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'CustomType', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | type: 'object', 7 | tsType: 'Set', 8 | }, 9 | bar: { 10 | description: 'Comparator function', 11 | instanceOf: 'Function', 12 | tsType: '(a: number, b: number) => number', 13 | }, 14 | foobar: { $ref: '#/definitions/foobar' }, 15 | }, 16 | definitions: { 17 | foobar: { 18 | description: 'Map from number to string', 19 | tsType: 'Map', 20 | }, 21 | }, 22 | additionalProperties: false, 23 | } 24 | -------------------------------------------------------------------------------- /test/e2e/deep.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | properties: { 4 | foo: { 5 | type: 'object', 6 | oneOf: [ 7 | { 8 | oneOf: [ 9 | { type: 'number' }, 10 | { $ref: '#/definitions/foo' }, 11 | { $ref: '#/definitions/bar' }, 12 | { 13 | properties: { 14 | baz: { type: 'number' }, 15 | }, 16 | }, 17 | ], 18 | }, 19 | { $ref: '#/definitions/bar' }, 20 | ], 21 | }, 22 | }, 23 | definitions: { 24 | foo: { 25 | properties: { 26 | a: { type: 'string' }, 27 | b: { type: 'integer' }, 28 | }, 29 | additionalProperties: false, 30 | required: ['a', 'b'], 31 | }, 32 | bar: { 33 | properties: { 34 | a: { type: 'string' }, 35 | }, 36 | required: ['a', 'b'], 37 | }, 38 | }, 39 | required: ['foo'], 40 | additionalProperties: false, 41 | } 42 | -------------------------------------------------------------------------------- /test/e2e/deprecated.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | $schema: 'http://json-schema.org/draft/2019-09/schema#', 3 | title: 'Example Schema', 4 | type: 'object', 5 | deprecated: true, 6 | description: 'comment', 7 | properties: { 8 | // https://github.com/bcherny/json-schema-to-typescript/issues/548 9 | firstName: { 10 | type: 'string', 11 | deprecated: true, 12 | }, 13 | middleName: { 14 | type: 'string', 15 | deprecated: true, 16 | description: 'Hi, my name\'s Doechii, this will be in a comment', 17 | }, 18 | lastName: { 19 | type: 'string', 20 | deprecated: false, 21 | description: 'nested comment', 22 | }, 23 | // https://github.com/bcherny/json-schema-to-typescript/issues/540 24 | description: { 25 | type: 'string', 26 | }, 27 | }, 28 | additionalProperties: false, 29 | required: ['firstName'], 30 | } 31 | -------------------------------------------------------------------------------- /test/e2e/disjointType.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Example Schema', 3 | description: 'My cool schema', 4 | type: 'object', 5 | properties: { 6 | value: { 7 | type: ['number', 'string'], 8 | }, 9 | anotherValue: { 10 | type: ['null', 'string'], 11 | }, 12 | nullableStringEnum: { 13 | type: ['null', 'string'], 14 | enum: ['foo', 'bar'], 15 | }, 16 | nullableObj: { 17 | type: ['null', 'object'], 18 | required: ['foo'], 19 | properties: { 20 | foo: { 21 | type: 'string', 22 | }, 23 | }, 24 | }, 25 | nullableArrayEnums: { 26 | type: ['null', 'array'], 27 | items: { 28 | type: 'string', 29 | enum: ['foo', 'bar'], 30 | }, 31 | }, 32 | }, 33 | required: ['value'], 34 | } 35 | -------------------------------------------------------------------------------- /test/e2e/emptyDefinition.ts: -------------------------------------------------------------------------------- 1 | // Reported in #326 2 | export const input = { 3 | additionalProperties: { 4 | $ref: '#/definitions/MyInterface', 5 | }, 6 | definitions: { 7 | MyInterface: {}, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /test/e2e/emptyProperties.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | definitions: { 3 | b: {}, 4 | }, 5 | properties: { 6 | a: {}, 7 | b: { $ref: '#/definitions/b' }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /test/e2e/emptySchema.ts: -------------------------------------------------------------------------------- 1 | export const input = {} 2 | -------------------------------------------------------------------------------- /test/e2e/enum.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | $schema: 'https://json-schema.org/draft/2019-09/schema', 3 | type: 'object', 4 | title: 'EntityObjectDefinition', 5 | properties: { 6 | definitions: { 7 | type: 'object', 8 | additionalProperties: false, 9 | minProperties: 1, 10 | patternProperties: { 11 | '^[a-zA-Z0-9_. /]+$': { 12 | type: 'object', 13 | $ref: '#/definitions/EntityObject', 14 | }, 15 | }, 16 | }, 17 | }, 18 | definitions: { 19 | EntityObject: { 20 | type: 'object', 21 | description: 'My example entity object definition', 22 | required: ['EntityDataCategory'], 23 | properties: { 24 | EntityDataCategory: { 25 | type: 'object', 26 | additionalProperties: false, 27 | properties: { 28 | APorpertyName: { 29 | $ref: '#/definitions/EntityDataCategory', 30 | }, 31 | }, 32 | }, 33 | }, 34 | }, 35 | EntityDataCategory: { 36 | type: 'string', 37 | enum: ['TABLE', 'OBJ', 'FUNC'], 38 | tsEnumNames: ['Table', 'Field', 'Func'], 39 | }, 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /test/e2e/enum.3.ts: -------------------------------------------------------------------------------- 1 | // Reported in #327 2 | export const input = { 3 | title: 'InterfaceWithTsEnumNames', 4 | anyOf: [{ $ref: '#/definitions/InterfaceWithTsEnumNames' }], 5 | definitions: { 6 | InterfaceWithTsEnumNames: { 7 | type: 'object', 8 | properties: { 9 | TsEnumNames: { $ref: '#/definitions/TsEnums' }, 10 | }, 11 | additionalProperties: false, 12 | }, 13 | TsEnums: { 14 | type: 'string', 15 | tsEnumNames: ['publish', 'draft'], 16 | enum: ['publish', 'draft'], 17 | }, 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /test/e2e/enum.4.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Enum with JSON literal', 3 | enum: [{ type: 'string' }, { foo: 3 }, 'foo', { foo: { bar: 'baz' } }, [2, 3, 4], [{ foo: [4, 6] }]], 4 | } 5 | -------------------------------------------------------------------------------- /test/e2e/enum.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Enum', 3 | type: 'object', 4 | definitions: { 5 | enumFromDefinition: { 6 | type: 'string', 7 | enum: ['a', 'b', 'c'], 8 | }, 9 | }, 10 | properties: { 11 | stringEnum: { 12 | type: 'string', 13 | enum: ['a', 'b', 'c'], 14 | }, 15 | impliedStringEnum: { 16 | enum: ['a', 'b', 'c'], 17 | }, 18 | booleanEnum: { 19 | type: 'boolean', 20 | enum: [true], 21 | }, 22 | impliedBooleanEnum: { 23 | enum: [true], 24 | }, 25 | integerEnum: { 26 | type: 'integer', 27 | enum: [-1, 0, 1], 28 | }, 29 | impliedIntegerEnum: { 30 | enum: [-1, 0, 1], 31 | }, 32 | numberEnum: { 33 | type: 'number', 34 | enum: [-1.1, 0, 1.2], 35 | }, 36 | namedIntegerEnum: { 37 | type: 'integer', 38 | enum: [1, 2, 3], 39 | tsEnumNames: ['One', 'Two', 'Three'], 40 | }, 41 | impliedNamedIntegerEnum: { 42 | enum: [4, 5, 6], 43 | tsEnumNames: ['Four', 'Five', 'Six'], 44 | }, 45 | impliedHeterogeneousEnum: { 46 | enum: [-20.1, null, 'foo', false], 47 | }, 48 | namedIntegerEnumTitle: { 49 | type: 'integer', 50 | enum: [1, 2, 3], 51 | title: 'NamedInteger', 52 | tsEnumNames: ['One', 'Two', 'Three'], 53 | }, 54 | impliedNamedIntegerEnumTitle: { 55 | enum: [4, 5, 6], 56 | title: 'ImpliedNamedInteger', 57 | tsEnumNames: ['Four', 'Five', 'Six'], 58 | }, 59 | enumThatComesFromADefinition: { 60 | $ref: '#/definitions/enumFromDefinition', 61 | }, 62 | propertyWithAnEnum: { 63 | type: 'object', 64 | properties: { 65 | enumThatComesFromADefinition: { 66 | $ref: '#/definitions/enumFromDefinition', 67 | }, 68 | }, 69 | }, 70 | }, 71 | required: [ 72 | 'stringEnum', 73 | 'impliedStringEnum', 74 | 'booleanEnum', 75 | 'impliedBooleanEnum', 76 | 'integerEnum', 77 | 'impliedIntegerEnum', 78 | 'impliedNamedIntegerEnum', 79 | 'namedIntegerEnumTitle', 80 | 'impliedNamedIntegerEnumTitle', 81 | ], 82 | additionalProperties: false, 83 | } 84 | -------------------------------------------------------------------------------- /test/e2e/enumInArray.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | properties: { 4 | foo: { 5 | type: 'array', 6 | items: { 7 | type: 'string', 8 | enum: ['BAR', 'BAZ'], 9 | enumNames: ['bar', 'baz'], 10 | tsEnumNames: ['BAR', 'BAZ'], 11 | }, 12 | }, 13 | title: 'foo', 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/enumValidation.1.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Enum', 3 | type: 'object', 4 | properties: { 5 | bar: { 6 | type: 'integer', 7 | enum: [1, 2, 3], 8 | tsEnumNames: ['One', 'Three'], 9 | }, 10 | }, 11 | required: ['bar'], 12 | additionalProperties: false, 13 | } 14 | 15 | export const options = { 16 | useTypescriptEnums: true, 17 | } 18 | 19 | export const error = true 20 | -------------------------------------------------------------------------------- /test/e2e/enumValidation.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Enum', 3 | type: 'object', 4 | properties: { 5 | bar: { 6 | type: 'integer', 7 | enum: [1, 2, 3], 8 | tsEnumNames: ['One', 2, 'Three'], 9 | }, 10 | }, 11 | required: ['bar'], 12 | additionalProperties: false, 13 | } 14 | 15 | export const error = true 16 | -------------------------------------------------------------------------------- /test/e2e/extends.1a.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Extends', 3 | type: 'object', 4 | extends: { 5 | $ref: 'test/resources/BaseType.1.json', 6 | }, 7 | properties: { 8 | foo: { 9 | type: 'string', 10 | }, 11 | }, 12 | required: ['foo'], 13 | additionalProperties: false, 14 | } 15 | 16 | export const options = { 17 | declareExternallyReferenced: true, 18 | } 19 | -------------------------------------------------------------------------------- /test/e2e/extends.1b.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Extends', 3 | type: 'object', 4 | extends: { 5 | $ref: 'test/resources/BaseType.1.json', 6 | }, 7 | properties: { 8 | foo: { 9 | type: 'string', 10 | }, 11 | }, 12 | required: ['foo'], 13 | additionalProperties: false, 14 | } 15 | 16 | export const options = { 17 | declareExternallyReferenced: false, 18 | } 19 | -------------------------------------------------------------------------------- /test/e2e/extends.2a.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Extends', 3 | type: 'object', 4 | extends: [ 5 | { 6 | $ref: 'test/resources/BaseType.1.json', 7 | }, 8 | { 9 | $ref: 'test/resources/BaseType.2.json', 10 | }, 11 | ], 12 | properties: { 13 | foo: { 14 | type: 'string', 15 | }, 16 | }, 17 | required: ['foo'], 18 | additionalProperties: false, 19 | } 20 | 21 | export const options = { 22 | declareExternallyReferenced: true, 23 | } 24 | -------------------------------------------------------------------------------- /test/e2e/extends.2b.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Extends', 3 | type: 'object', 4 | extends: [ 5 | { 6 | $ref: 'test/resources/BaseType.1.json', 7 | }, 8 | { 9 | $ref: 'test/resources/BaseType.2.json', 10 | }, 11 | ], 12 | properties: { 13 | foo: { 14 | type: 'string', 15 | }, 16 | }, 17 | required: ['foo'], 18 | additionalProperties: false, 19 | } 20 | 21 | export const options = { 22 | declareExternallyReferenced: false, 23 | } 24 | -------------------------------------------------------------------------------- /test/e2e/extends.3.ts: -------------------------------------------------------------------------------- 1 | // Reported in #322 2 | export const input = { 3 | type: 'object', 4 | title: 'Schema', 5 | description: 'Any Shape', 6 | additionalProperties: false, 7 | anyOf: [{ $ref: 'test/resources/extends/Circle.json' }, { $ref: 'test/resources/extends/Square.json' }], 8 | } 9 | -------------------------------------------------------------------------------- /test/e2e/intersection.1.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | additionalProperties: false, 4 | properties: { 5 | a: { 6 | additionalProperties: false, 7 | id: 'a', 8 | properties: { a: { type: 'string' } }, 9 | required: ['a'], 10 | }, 11 | b: { 12 | additionalProperties: false, 13 | id: 'b', 14 | properties: { b: { type: 'string' } }, 15 | required: ['b'], 16 | }, 17 | }, 18 | oneOf: [ 19 | { 20 | additionalProperties: false, 21 | id: 'c', 22 | properties: { c: { type: 'string' } }, 23 | required: ['c'], 24 | }, 25 | { 26 | additionalProperties: false, 27 | id: 'd', 28 | properties: { d: { type: 'string' } }, 29 | required: ['d'], 30 | }, 31 | ], 32 | required: ['a', 'b'], 33 | } 34 | -------------------------------------------------------------------------------- /test/e2e/intersection.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | properties: { 4 | c: { 5 | type: 'string', 6 | }, 7 | d: { 8 | type: 'string', 9 | }, 10 | }, 11 | patternProperties: { 12 | '^x-': {}, 13 | }, 14 | additionalProperties: false, 15 | required: ['c', 'd'], 16 | allOf: [ 17 | { 18 | $ref: '#/definitions/A', 19 | }, 20 | { 21 | $ref: '#/definitions/B', 22 | }, 23 | ], 24 | definitions: { 25 | A: { 26 | type: 'object', 27 | properties: { 28 | a: { type: 'string' }, 29 | }, 30 | }, 31 | B: { 32 | type: 'object', 33 | properties: { 34 | b: { type: 'string' }, 35 | }, 36 | }, 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /test/e2e/namedProperty.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Example Schema', 3 | description: 'My cool schema', 4 | type: 'object', 5 | properties: { 6 | users: { 7 | type: 'array', 8 | title: 'user id array', 9 | description: 'Array of authorized user ids.', 10 | items: { 11 | type: 'string', 12 | }, 13 | }, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/notExtensible.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Example Schema', 3 | type: 'object', 4 | properties: { 5 | firstName: { 6 | type: 'string', 7 | }, 8 | lastName: { 9 | type: 'string', 10 | }, 11 | age: { 12 | description: 'Age in years', 13 | type: 'integer', 14 | minimum: 0, 15 | }, 16 | }, 17 | required: ['firstName', 'lastName'], 18 | additionalProperties: false, 19 | } 20 | -------------------------------------------------------------------------------- /test/e2e/oneOf.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'OneOf', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | type: 'object', 7 | oneOf: [{ $ref: '#/definitions/foo' }, { $ref: '#/definitions/bar' }, { $ref: '#/definitions/baz' }], 8 | }, 9 | }, 10 | definitions: { 11 | foo: { 12 | properties: { 13 | a: { type: 'string' }, 14 | b: { type: 'integer' }, 15 | }, 16 | additionalProperties: false, 17 | required: ['a'], 18 | }, 19 | bar: { 20 | properties: { 21 | a: { enum: ['a', 'b', 'c'] }, 22 | }, 23 | }, 24 | baz: { 25 | properties: { 26 | baz: { $ref: '#/definitions/bar' }, 27 | }, 28 | }, 29 | }, 30 | required: ['foo'], 31 | additionalProperties: false, 32 | } 33 | -------------------------------------------------------------------------------- /test/e2e/oneOfWithDupes.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Test', 3 | type: 'object', 4 | definitions: { 5 | a: { 6 | type: 'object', 7 | title: 'a', 8 | properties: { 9 | name: { 10 | type: 'string', 11 | }, 12 | }, 13 | }, 14 | b: { 15 | type: 'object', 16 | title: 'b', 17 | properties: { 18 | name: { 19 | type: 'string', 20 | }, 21 | }, 22 | }, 23 | c: { 24 | type: 'number', 25 | title: 'c', 26 | }, 27 | d: { 28 | type: 'number', 29 | }, 30 | }, 31 | properties: { 32 | a: { 33 | oneOf: [{ $ref: '#/definitions/a' }], 34 | }, 35 | b: { 36 | oneOf: [{ $ref: '#/definitions/a' }, { $ref: '#/definitions/b' }], 37 | }, 38 | c: { 39 | oneOf: [{ $ref: '#/definitions/c' }, { $ref: '#/definitions/d' }], 40 | }, 41 | d: { 42 | oneOf: [{ $ref: '#/definitions/c' }, { $ref: '#/definitions/c' }, { $ref: '#/definitions/d' }], 43 | }, 44 | e: { 45 | oneOf: [{ $ref: '#/definitions/d' }, { $ref: '#/definitions/d' }], 46 | }, 47 | f: { 48 | oneOf: [ 49 | { $ref: '#/definitions/a' }, 50 | { $ref: '#/definitions/b' }, 51 | { $ref: '#/definitions/c' }, 52 | { $ref: '#/definitions/d' }, 53 | { $ref: '#/definitions/a' }, 54 | { $ref: '#/definitions/b' }, 55 | { $ref: '#/definitions/c' }, 56 | { $ref: '#/definitions/d' }, 57 | ], 58 | }, 59 | }, 60 | } 61 | -------------------------------------------------------------------------------- /test/e2e/optimize.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | definitions: { 3 | a: { 4 | type: 'string', 5 | additionalProperties: false, 6 | }, 7 | b: { 8 | type: 'object', 9 | }, 10 | c: { 11 | type: 'object', 12 | }, 13 | }, 14 | properties: { 15 | a: { 16 | anyOf: [{ type: 'string', additionalProperties: false }, { $ref: '#/definitions/a' }], 17 | }, 18 | b: { 19 | anyOf: [{ type: 'object' }, { type: 'object' }, { $ref: '#/definitions/b' }], 20 | }, 21 | c: { 22 | anyOf: [{ type: 'object' }, { $ref: '#/definitions/b' }, { $ref: '#/definitions/b' }, { $ref: '#/definitions/c' }], 23 | }, 24 | d: { 25 | allOf: [{ type: 'object' }, { type: 'object' }], 26 | }, 27 | e: { 28 | oneOf: [{ type: 'object' }, { allOf: [{ type: 'object' }, { type: 'object' }] }], 29 | }, 30 | }, 31 | required: ['a', 'b', 'c', 'd', 'e'], 32 | title: 'Optimizable Schema 2', 33 | type: 'object', 34 | additionalProperties: false, 35 | } 36 | -------------------------------------------------------------------------------- /test/e2e/optimize.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | properties: { 3 | bar: { 4 | anyOf: [{ type: 'string' }, { type: 'string' }, { type: 'integer' }, { type: 'string' }], 5 | }, 6 | foo: { 7 | anyOf: [{ type: 'string' }, { type: 'any' }, { type: 'integer' }], 8 | }, 9 | fooBar: { 10 | anyOf: [{ tsType: 'A' }, { tsType: 'A' }, { tsType: 'B' }, { tsType: 'A' }, { tsType: 'B' }], 11 | }, 12 | }, 13 | required: ['bar', 'foo', 'fooBar'], 14 | title: 'Optimizable Schema', 15 | type: 'object', 16 | } 17 | -------------------------------------------------------------------------------- /test/e2e/options.enableConstEnums.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Enum', 3 | type: 'object', 4 | properties: { 5 | stringEnum: { 6 | type: 'string', 7 | enum: ['a', 'b', 'c'], 8 | }, 9 | impliedStringEnum: { 10 | enum: ['a', 'b', 'c'], 11 | }, 12 | booleanEnum: { 13 | type: 'boolean', 14 | enum: [true], 15 | }, 16 | impliedBooleanEnum: { 17 | enum: [true], 18 | }, 19 | integerEnum: { 20 | type: 'integer', 21 | enum: [-1, 0, 1], 22 | }, 23 | impliedIntegerEnum: { 24 | enum: [-1, 0, 1], 25 | }, 26 | numberEnum: { 27 | type: 'number', 28 | enum: [-1.1, 0, 1.2], 29 | }, 30 | namedIntegerEnum: { 31 | type: 'integer', 32 | enum: [1, 2, 3], 33 | tsEnumNames: ['One', 'Two', 'Three'], 34 | }, 35 | impliedNamedIntegerEnum: { 36 | enum: [4, 5, 6], 37 | tsEnumNames: ['Four', 'Five', 'Six'], 38 | }, 39 | impliedHeterogeneousEnum: { 40 | enum: [-20.1, null, 'foo', false], 41 | }, 42 | namedIntegerEnumTitle: { 43 | type: 'integer', 44 | enum: [1, 2, 3], 45 | title: 'NamedInteger', 46 | tsEnumNames: ['One', 'Two', 'Three'], 47 | }, 48 | impliedNamedIntegerEnumTitle: { 49 | enum: [4, 5, 6], 50 | title: 'ImpliedNamedInteger', 51 | tsEnumNames: ['Four', 'Five', 'Six'], 52 | }, 53 | oneOfNamedEnum: { 54 | oneOf: [ 55 | { 56 | type: 'integer', 57 | enum: [1, 2, 3], 58 | title: 'IntegerOneOfNamedEnum', 59 | tsEnumNames: ['One', 'Two', 'Three'], 60 | }, 61 | { 62 | type: 'string', 63 | enum: ['four', 'five', 'six'], 64 | title: 'StringOneOfNamedEnum', 65 | tsEnumNames: ['Four', 'Five', 'Six'], 66 | }, 67 | ], 68 | }, 69 | anyOfNamedEnum: { 70 | anyOf: [ 71 | { 72 | type: 'integer', 73 | enum: [1, 2, 3], 74 | title: 'IntegerAnyOfNamedEnum', 75 | tsEnumNames: ['One', 'Two', 'Three'], 76 | }, 77 | { 78 | type: 'string', 79 | enum: ['four', 'five', 'six'], 80 | title: 'StringAnyOfNamedEnum', 81 | tsEnumNames: ['Four', 'Five', 'Six'], 82 | }, 83 | ], 84 | }, 85 | allOfNamedEnum: { 86 | allOf: [ 87 | { 88 | type: 'integer', 89 | enum: [1, 2, 3], 90 | title: 'IntegerAllOfNamedEnum', 91 | tsEnumNames: ['One', 'Two', 'Three'], 92 | }, 93 | { 94 | type: 'string', 95 | enum: ['four', 'five', 'six'], 96 | title: 'StringAllOfNamedEnum', 97 | tsEnumNames: ['Four', 'Five', 'Six'], 98 | }, 99 | ], 100 | }, 101 | }, 102 | required: [ 103 | 'stringEnum', 104 | 'impliedStringEnum', 105 | 'booleanEnum', 106 | 'impliedBooleanEnum', 107 | 'integerEnum', 108 | 'impliedIntegerEnum', 109 | 'impliedNamedIntegerEnum', 110 | 'namedIntegerEnumTitle', 111 | 'impliedNamedIntegerEnumTitle', 112 | 'oneOfNamedEnum', 113 | 'anyOfNamedEnum', 114 | 'allOfNamedEnum', 115 | ], 116 | additionalProperties: false, 117 | } 118 | 119 | export const options = { 120 | enableConstEnums: true, 121 | } 122 | -------------------------------------------------------------------------------- /test/e2e/options.format.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from '../../src' 2 | 3 | export const input = { 4 | title: 'Example Schema', 5 | type: 'object', 6 | properties: { 7 | firstName: { 8 | type: 'string', 9 | }, 10 | lastName: { 11 | id: 'lastName', 12 | type: 'string', 13 | }, 14 | }, 15 | required: ['firstName', 'lastName'], 16 | } 17 | 18 | export const options: Partial = { 19 | } 20 | -------------------------------------------------------------------------------- /test/e2e/options.strictIndexSignatures.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'StrictIndexSignatures', 3 | type: 'object', 4 | properties: { 5 | maybe: { 6 | type: 'string', 7 | }, 8 | complex: { 9 | type: 'object', 10 | properties: { 11 | maybe: { 12 | type: 'string', 13 | }, 14 | }, 15 | additionalProperties: { 16 | title: 'Leaf', 17 | type: 'object', 18 | properties: { 19 | maybe: { 20 | type: 'string', 21 | }, 22 | }, 23 | }, 24 | }, 25 | }, 26 | additionalProperties: { 27 | type: 'string', 28 | }, 29 | } 30 | 31 | export const options = { 32 | strictIndexSignatures: true, 33 | } 34 | -------------------------------------------------------------------------------- /test/e2e/options.style.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from '../../src' 2 | 3 | export const input = { 4 | title: 'Example Schema', 5 | type: 'object', 6 | properties: { 7 | firstName: { 8 | type: 'string', 9 | }, 10 | lastName: { 11 | id: 'lastName', 12 | type: 'string', 13 | }, 14 | }, 15 | required: ['firstName', 'lastName'], 16 | } 17 | 18 | export const options: Partial = { 19 | } 20 | -------------------------------------------------------------------------------- /test/e2e/options.unreachableDefinitions.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'UnreachableDefinition', 3 | type: 'object', 4 | definitions: { 5 | a: { 6 | properties: { 7 | firstName: { 8 | type: 'string', 9 | }, 10 | lastName: { 11 | id: 'lastName', 12 | type: 'string', 13 | }, 14 | }, 15 | }, 16 | }, 17 | properties: { 18 | b: { 19 | properties: { 20 | likesDogs: { 21 | type: 'boolean', 22 | }, 23 | }, 24 | }, 25 | }, 26 | } 27 | 28 | export const options = { 29 | unreachableDefinitions: true, 30 | } 31 | -------------------------------------------------------------------------------- /test/e2e/patternProperties.1.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | id: 'Parent', 3 | type: 'object', 4 | patternProperties: { 5 | '^[a-zA-Z]+': { 6 | id: 'Child', 7 | type: 'object', 8 | properties: { 9 | aProperty: { type: 'string' }, 10 | }, 11 | }, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /test/e2e/patternProperties.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | id: 'Parent', 3 | type: 'object', 4 | patternProperties: { 5 | '^[a-zA-Z]+': { 6 | id: 'Child', 7 | type: 'object', 8 | properties: { 9 | aProperty: { type: 'string' }, 10 | }, 11 | }, 12 | }, 13 | properties: { 14 | a: { type: 'number' }, 15 | b: { type: 'string' }, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /test/e2e/patternProperties.3.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | id: 'Parent', 3 | type: 'object', 4 | additionalProperties: false, 5 | patternProperties: { 6 | '^[a-zA-Z]+': { 7 | id: 'Child', 8 | type: 'object', 9 | properties: { 10 | aProperty: { type: 'string' }, 11 | }, 12 | }, 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /test/e2e/patternProperties.4.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | id: 'Parent', 3 | type: 'object', 4 | additionalProperties: false, 5 | patternProperties: { 6 | '^[0-9]+': { 7 | id: 'NumberChild', 8 | type: 'number', 9 | }, 10 | '^[a-zA-Z]+': { 11 | id: 'StringChild', 12 | type: 'string', 13 | }, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/patternProperties.5.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | id: 'Parent', 3 | type: 'object', 4 | additionalProperties: true, 5 | patternProperties: { 6 | '^[0-9]+': { 7 | id: 'NumberChild', 8 | type: 'number', 9 | }, 10 | '^[a-zA-Z]+': { 11 | id: 'StringChild', 12 | type: 'string', 13 | }, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/patternProperties.6.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | id: 'Parent', 3 | type: 'object', 4 | additionalProperties: { 5 | type: 'number', 6 | }, 7 | patternProperties: { 8 | '^[a-zA-Z]+': { 9 | id: 'StringChild', 10 | type: 'string', 11 | }, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /test/e2e/patternProperties.7.ts: -------------------------------------------------------------------------------- 1 | // @see https://github.com/bcherny/json-schema-to-typescript/issues/546 2 | export const input = { 3 | type: 'object', 4 | patternProperties: { 5 | '^[a-z][a-z0-9-]*/[a-z][a-z0-9-]*$': { 6 | type: 'string', 7 | }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /test/e2e/patternProperties.8.ts: -------------------------------------------------------------------------------- 1 | // @see https://github.com/bcherny/json-schema-to-typescript/issues/546 2 | export const input = { 3 | type: 'object', 4 | patternProperties: { 5 | '^[a-z][a-z0-9-]/*[a-z][a-z0-9-]*$': { 6 | type: 'string', 7 | }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /test/e2e/realWorld.jsonschema.ts: -------------------------------------------------------------------------------- 1 | // @see https://github.com/bcherny/json-schema-to-typescript/issues/228 2 | export const input = { 3 | $schema: 'http://json-schema.org/draft-07/schema#', 4 | $id: 'http://json-schema.org/draft-07/schema#', 5 | title: 'Core schema meta-schema', 6 | definitions: { 7 | schemaArray: { 8 | type: 'array', 9 | minItems: 1, 10 | items: { $ref: '#' }, 11 | }, 12 | nonNegativeInteger: { 13 | type: 'integer', 14 | minimum: 0, 15 | }, 16 | nonNegativeIntegerDefault0: { 17 | allOf: [{ $ref: '#/definitions/nonNegativeInteger' }, { default: 0 }], 18 | }, 19 | simpleTypes: { 20 | enum: ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'], 21 | }, 22 | stringArray: { 23 | type: 'array', 24 | items: { type: 'string' }, 25 | uniqueItems: true, 26 | default: [], 27 | }, 28 | }, 29 | type: ['object', 'boolean'], 30 | properties: { 31 | $id: { 32 | type: 'string', 33 | format: 'uri-reference', 34 | }, 35 | $schema: { 36 | type: 'string', 37 | format: 'uri', 38 | }, 39 | $ref: { 40 | type: 'string', 41 | format: 'uri-reference', 42 | }, 43 | $comment: { 44 | type: 'string', 45 | }, 46 | title: { 47 | type: 'string', 48 | }, 49 | description: { 50 | type: 'string', 51 | }, 52 | default: true, 53 | readOnly: { 54 | type: 'boolean', 55 | default: false, 56 | }, 57 | writeOnly: { 58 | type: 'boolean', 59 | default: false, 60 | }, 61 | examples: { 62 | type: 'array', 63 | items: true, 64 | }, 65 | multipleOf: { 66 | type: 'number', 67 | exclusiveMinimum: 0, 68 | }, 69 | maximum: { 70 | type: 'number', 71 | }, 72 | exclusiveMaximum: { 73 | type: 'number', 74 | }, 75 | minimum: { 76 | type: 'number', 77 | }, 78 | exclusiveMinimum: { 79 | type: 'number', 80 | }, 81 | maxLength: { $ref: '#/definitions/nonNegativeInteger' }, 82 | minLength: { $ref: '#/definitions/nonNegativeIntegerDefault0' }, 83 | pattern: { 84 | type: 'string', 85 | format: 'regex', 86 | }, 87 | additionalItems: { $ref: '#' }, 88 | items: { 89 | anyOf: [{ $ref: '#' }, { $ref: '#/definitions/schemaArray' }], 90 | default: true, 91 | }, 92 | maxItems: { $ref: '#/definitions/nonNegativeInteger' }, 93 | minItems: { $ref: '#/definitions/nonNegativeIntegerDefault0' }, 94 | uniqueItems: { 95 | type: 'boolean', 96 | default: false, 97 | }, 98 | contains: { $ref: '#' }, 99 | maxProperties: { $ref: '#/definitions/nonNegativeInteger' }, 100 | minProperties: { $ref: '#/definitions/nonNegativeIntegerDefault0' }, 101 | required: { $ref: '#/definitions/stringArray' }, 102 | additionalProperties: { $ref: '#' }, 103 | definitions: { 104 | type: 'object', 105 | additionalProperties: { $ref: '#' }, 106 | default: {}, 107 | }, 108 | properties: { 109 | type: 'object', 110 | additionalProperties: { $ref: '#' }, 111 | default: {}, 112 | }, 113 | patternProperties: { 114 | type: 'object', 115 | additionalProperties: { $ref: '#' }, 116 | propertyNames: { format: 'regex' }, 117 | default: {}, 118 | }, 119 | dependencies: { 120 | type: 'object', 121 | additionalProperties: { 122 | anyOf: [{ $ref: '#' }, { $ref: '#/definitions/stringArray' }], 123 | }, 124 | }, 125 | propertyNames: { $ref: '#' }, 126 | const: true, 127 | enum: { 128 | type: 'array', 129 | items: true, 130 | minItems: 1, 131 | uniqueItems: true, 132 | }, 133 | type: { 134 | anyOf: [ 135 | { $ref: '#/definitions/simpleTypes' }, 136 | { 137 | type: 'array', 138 | items: { $ref: '#/definitions/simpleTypes' }, 139 | minItems: 1, 140 | uniqueItems: true, 141 | }, 142 | ], 143 | }, 144 | format: { type: 'string' }, 145 | contentMediaType: { type: 'string' }, 146 | contentEncoding: { type: 'string' }, 147 | if: { $ref: '#' }, 148 | then: { $ref: '#' }, 149 | else: { $ref: '#' }, 150 | allOf: { $ref: '#/definitions/schemaArray' }, 151 | anyOf: { $ref: '#/definitions/schemaArray' }, 152 | oneOf: { $ref: '#/definitions/schemaArray' }, 153 | not: { $ref: '#' }, 154 | }, 155 | default: true, 156 | } 157 | -------------------------------------------------------------------------------- /test/e2e/realWorld.swagger.2.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Tests that $ref as key works as expected 3 | */ 4 | export const input = { 5 | type: 'object', 6 | properties: { 7 | definitions: { 8 | $ref: '#/definitions/definitions', 9 | }, 10 | }, 11 | definitions: { 12 | definitions: { 13 | $ref: '#/definitions/schema', 14 | }, 15 | schema: { 16 | type: 'object', 17 | properties: { 18 | additionalProperties: { 19 | anyOf: [ 20 | { 21 | $ref: '#/definitions/schema', 22 | }, 23 | ], 24 | }, 25 | }, 26 | }, 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /test/e2e/realWorld.swagger.ts: -------------------------------------------------------------------------------- 1 | export const exclude = true 2 | 3 | /** 4 | * @see https://github.com/bcherny/json-schema-to-typescript/issues/49 5 | */ 6 | export const input = { 7 | title: 'Referencing', 8 | type: 'object', 9 | properties: { 10 | foo: { 11 | $ref: 'https://raw.githubusercontent.com/bcherny/OpenAPI-Specification/ae9322eb2df1555acf3163e30cd84779d98afec5/schemas/v2.0/schema.json', 12 | }, 13 | }, 14 | required: ['foo'], 15 | additionalProperties: false, 16 | } 17 | -------------------------------------------------------------------------------- /test/e2e/ref.1a.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Referencing', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | $ref: 'test/resources/ReferencedType.json', 7 | }, 8 | }, 9 | required: ['foo'], 10 | additionalProperties: false, 11 | } 12 | 13 | export const options = { 14 | declareExternallyReferenced: true, 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/ref.1b.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Referencing', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | $ref: 'test/resources/ReferencedType.json', 7 | }, 8 | }, 9 | required: ['foo'], 10 | additionalProperties: false, 11 | } 12 | 13 | export const options = { 14 | declareExternallyReferenced: false, 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/ref.2.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://github.com/bcherny/json-schema-to-typescript/issues/56 3 | */ 4 | export const input = { 5 | $schema: 'http://json-schema.org/draft-04/schema#', 6 | title: 'Locally Referenced Manifest', 7 | type: 'object', 8 | definitions: { 9 | firstDefinition: { 10 | title: 'First Definition', 11 | description: 'Title matches definition key for kicks', 12 | type: 'object', 13 | properties: { 14 | name: { 15 | type: 'string', 16 | }, 17 | }, 18 | }, 19 | secondDefinition: { 20 | title: 'Unrelated Title', 21 | description: 'Title is unrelated to definition key and behaviour is the same', 22 | type: 'object', 23 | properties: { 24 | name: { 25 | type: 'string', 26 | }, 27 | }, 28 | }, 29 | thirdDefinition: { 30 | description: 'Definition has no title and produces no duplicate Interface', 31 | type: 'object', 32 | properties: { 33 | name: { 34 | type: 'string', 35 | }, 36 | }, 37 | }, 38 | fourthDefinition: { 39 | title: 'Fourth Definition Simple Object', 40 | description: 'A simple object type with title set and no properties defined produces no duplicate Interface', 41 | type: 'object', 42 | }, 43 | fifthDefinition: { 44 | title: 'Fifth Definition String', 45 | description: 'A string with title and enum defined does produce a duplicate Interface', 46 | type: 'string', 47 | enum: ['one', 'two', 'three'], 48 | }, 49 | sixthDefinition: { 50 | id: 'six', 51 | type: 'number', 52 | }, 53 | }, 54 | properties: { 55 | firstContainer: { 56 | description: 'Behaviour is the same if definition is referenced as prop within a container', 57 | type: 'object', 58 | properties: { 59 | first: { 60 | $ref: '#/definitions/firstDefinition', 61 | }, 62 | }, 63 | }, 64 | second: { 65 | $ref: '#/definitions/secondDefinition', 66 | }, 67 | third: { 68 | $ref: '#/definitions/thirdDefinition', 69 | }, 70 | fourth: { 71 | $ref: '#/definitions/fourthDefinition', 72 | }, 73 | fifth: { 74 | $ref: '#/definitions/fifthDefinition', 75 | }, 76 | sixth: { 77 | $ref: '#/definitions/sixthDefinition', 78 | }, 79 | }, 80 | } 81 | -------------------------------------------------------------------------------- /test/e2e/ref.3.ts: -------------------------------------------------------------------------------- 1 | // TODO: why does this test fail? do we need to send a specific User-Agent header to github? 2 | export const exclude = true 3 | 4 | export const input = { 5 | title: 'Referencing3', 6 | type: 'object', 7 | properties: { 8 | foo: { 9 | $ref: 'https://raw.githubusercontent.com/bcherny/json-schema-to-typescript/4531fd7da2c2dbed3b2887fd7035ff18573edb82/test/resources/ReferencedType.json', 10 | }, 11 | }, 12 | required: ['foo'], 13 | additionalProperties: false, 14 | } 15 | -------------------------------------------------------------------------------- /test/e2e/ref.4.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Referencing', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | $ref: 'ReferencedType.json', 7 | }, 8 | }, 9 | required: ['foo'], 10 | additionalProperties: false, 11 | } 12 | 13 | export const options = { 14 | cwd: 'test/resources/', 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/ref.5.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Referencing', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | $ref: 'ReferencedTypeWithoutID.json', 7 | }, 8 | bar: { 9 | $ref: 'ReferencedTypeWithoutIDConflict.json', 10 | }, 11 | }, 12 | required: ['foo', 'bar'], 13 | additionalProperties: false, 14 | } 15 | 16 | export const options = { 17 | cwd: 'test/resources/', 18 | } 19 | -------------------------------------------------------------------------------- /test/e2e/ref.6a.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Referencing Combined', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | $ref: 'test/resources/ReferencedCombinationType.json', 7 | }, 8 | }, 9 | required: ['foo'], 10 | additionalProperties: false, 11 | } 12 | 13 | export const options = { 14 | declareExternallyReferenced: true, 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/ref.6b.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Referencing Combined', 3 | type: 'object', 4 | properties: { 5 | foo: { 6 | $ref: 'test/resources/ReferencedCombinationType.json', 7 | }, 8 | }, 9 | required: ['foo'], 10 | additionalProperties: false, 11 | } 12 | 13 | export const options = { 14 | declareExternallyReferenced: false, 15 | } 16 | -------------------------------------------------------------------------------- /test/e2e/refWithCycle.1.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Local Cycle', 3 | properties: { 4 | foo: { 5 | $ref: '#', 6 | }, 7 | bar: { 8 | $ref: '#', 9 | }, 10 | }, 11 | required: ['foo'], 12 | additionalProperties: true, 13 | } 14 | -------------------------------------------------------------------------------- /test/e2e/refWithCycle.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Cycle (2)', 3 | properties: { 4 | foo: { 5 | $ref: 'test/resources/cycle.3.json', 6 | }, 7 | }, 8 | required: ['foo'], 9 | additionalProperties: true, 10 | } 11 | -------------------------------------------------------------------------------- /test/e2e/refWithCycle.3.ts: -------------------------------------------------------------------------------- 1 | export const exclude = true 2 | 3 | export const input = { 4 | additionalProperties: true, 5 | properties: { 6 | foo: { 7 | $ref: '#/definitions/bar', 8 | }, 9 | }, 10 | definitions: { 11 | bar: { 12 | $ref: '#/definitions/bar', 13 | }, 14 | }, 15 | required: ['foo'], 16 | title: 'Cycle (3)', 17 | } 18 | -------------------------------------------------------------------------------- /test/e2e/refWithCycle.4.ts: -------------------------------------------------------------------------------- 1 | // @see https://github.com/bcherny/json-schema-to-typescript/issues/355 2 | const idSchema = { type: 'integer', minimum: 1 } 3 | 4 | export const input = { 5 | type: 'object', 6 | properties: { 7 | id: idSchema, 8 | userId: idSchema, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /test/e2e/refWithCycle.5.ts: -------------------------------------------------------------------------------- 1 | // Cycle in referenced schema 2 | // @see https://github.com/bcherny/json-schema-to-typescript/issues/376 3 | export const input = { 4 | $schema: 'http://json-schema.org/draft-07/schema#', 5 | type: 'object', 6 | properties: { 7 | owner: { $ref: 'test/resources/Person.json' }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /test/e2e/referencesShouldBeNormalized.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Referencing', 3 | type: 'object', 4 | properties: { 5 | a: { 6 | $ref: 'test/resources/ReferencedTypeNotNormalized.json', 7 | }, 8 | }, 9 | required: ['a'], 10 | } 11 | -------------------------------------------------------------------------------- /test/e2e/required.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | additionalProperties: false, 3 | definitions: { 4 | location: { 5 | properties: { 6 | city: { type: 'string' }, 7 | postalCode: { type: 'number' }, 8 | }, 9 | }, 10 | }, 11 | properties: { 12 | location: { 13 | allOf: [{ $ref: '#/definitions/location' }, { required: ['postalCode'] }], 14 | }, 15 | name: { type: 'string' }, 16 | website: { type: 'string' }, 17 | }, 18 | required: ['name', 'location'], 19 | type: 'object', 20 | } 21 | -------------------------------------------------------------------------------- /test/e2e/reservedWords.1.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | definitions: { 3 | definitions: { 4 | type: 'integer', 5 | }, 6 | properties: { 7 | type: 'string', 8 | }, 9 | }, 10 | properties: { 11 | additionalProperties: { 12 | items: { 13 | type: 'number', 14 | }, 15 | type: 'array', 16 | }, 17 | definitions: { 18 | type: 'number', 19 | }, 20 | properties: { 21 | type: 'boolean', 22 | }, 23 | }, 24 | type: 'object', 25 | } 26 | -------------------------------------------------------------------------------- /test/e2e/reservedWords.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | properties: { 4 | definitions: { 5 | $ref: '#/definitions/definitions', 6 | }, 7 | }, 8 | definitions: { 9 | definitions: { 10 | $ref: '#/definitions/schema', 11 | }, 12 | schema: { 13 | type: 'object', 14 | properties: { 15 | additionalProperties: { 16 | anyOf: [ 17 | { 18 | $ref: '#/definitions/schema', 19 | }, 20 | ], 21 | }, 22 | }, 23 | }, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /test/e2e/safeTypeNames.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | definitions: { 3 | 'stra\'nge#name': { 4 | properties: { 5 | a: { type: 'string' }, 6 | b: { type: 'integer' }, 7 | }, 8 | additionalProperties: false, 9 | required: ['a'], 10 | }, 11 | 'keepUPPERCASE': { 12 | properties: { 13 | a: { enum: ['a', 'b', 'c'] }, 14 | }, 15 | }, 16 | 'snake_case': { 17 | properties: { 18 | a: { type: 'boolean' }, 19 | }, 20 | }, 21 | '_startsWithUnderscore': { 22 | properties: { 23 | a: { type: 'boolean' }, 24 | }, 25 | }, 26 | '_StartsWithUnderscoreUppercase': { 27 | properties: { 28 | a: { type: 'boolean' }, 29 | }, 30 | }, 31 | 'EndsWithUnderscore_': { 32 | properties: { 33 | a: { type: 'boolean' }, 34 | }, 35 | }, 36 | 'UPPER_CASE': { 37 | properties: { 38 | a: { type: 'boolean' }, 39 | }, 40 | }, 41 | '______________': { 42 | properties: { 43 | a: { type: 'boolean' }, 44 | }, 45 | }, 46 | 'camelCase': { 47 | properties: { 48 | a: { type: 'float' }, 49 | }, 50 | }, 51 | 'kebab-case': { 52 | properties: { 53 | a: { type: 'string' }, 54 | }, 55 | }, 56 | ' startsWithSpace': { 57 | properties: { 58 | a: { type: 'string' }, 59 | }, 60 | }, 61 | 'contains space': { 62 | properties: { 63 | a: { type: 'string' }, 64 | }, 65 | }, 66 | '5tartsWithDigit': { 67 | properties: { 68 | a: { type: 'string' }, 69 | }, 70 | }, 71 | ' 5tartsWithBlankAndDigit': { 72 | properties: { 73 | a: { type: 'string' }, 74 | }, 75 | }, 76 | 'endsWithDigi7': { 77 | properties: { 78 | a: { type: 'string' }, 79 | }, 80 | }, 81 | 'contains4digit': { 82 | properties: { 83 | a: { type: 'string' }, 84 | }, 85 | }, 86 | '.startsWithPeriod': { 87 | properties: { 88 | a: { type: 'string' }, 89 | }, 90 | }, 91 | 'endsWithPeriod.': { 92 | properties: { 93 | a: { type: 'string' }, 94 | }, 95 | }, 96 | 'contains...period': { 97 | properties: { 98 | a: { type: 'string' }, 99 | }, 100 | }, 101 | ',startsWithComma': { 102 | properties: { 103 | a: { type: 'string' }, 104 | }, 105 | }, 106 | 'endsWithComma,': { 107 | properties: { 108 | a: { type: 'string' }, 109 | }, 110 | }, 111 | 'contains,,Comma': { 112 | properties: { 113 | a: { type: 'string' }, 114 | }, 115 | }, 116 | '$startsWithDollar': { 117 | properties: { 118 | a: { type: 'string' }, 119 | }, 120 | }, 121 | 'endsWithDollar$': { 122 | properties: { 123 | a: { type: 'string' }, 124 | }, 125 | }, 126 | 'contains$Dollar': { 127 | properties: { 128 | a: { type: 'string' }, 129 | }, 130 | }, 131 | '$': { 132 | properties: { 133 | a: { type: 'string' }, 134 | }, 135 | }, 136 | 'UPPERCASE': { 137 | properties: { 138 | a: { type: 'string' }, 139 | }, 140 | }, 141 | 'Startsuppercase': { 142 | properties: { 143 | a: { type: 'string' }, 144 | }, 145 | }, 146 | }, 147 | } 148 | 149 | export const options = { 150 | unreachableDefinitions: true, 151 | } 152 | -------------------------------------------------------------------------------- /test/e2e/schemaTitleAsTypeName.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | $schema: 'http://json-schema.org/draft-03/schema', 3 | id: 'http://mycompany.com/api/referencing.json', 4 | title: 'Referencing', 5 | type: 'object', 6 | properties: { 7 | ref: { 8 | $ref: 'test/resources/ReferencedType.json', 9 | }, 10 | }, 11 | required: ['ref'], 12 | additionalProperties: false, 13 | } 14 | -------------------------------------------------------------------------------- /test/e2e/specialCharacters.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Example Schema', 3 | type: 'object', 4 | properties: { 5 | '`foo`': { 6 | type: 'string', 7 | }, 8 | '\'bar\'': { 9 | type: 'string', 10 | }, 11 | '"baz"': { 12 | type: 'string', 13 | }, 14 | '$zoo 2': { 15 | type: 'string', 16 | }, 17 | 'qux...': { 18 | type: 'number', 19 | }, 20 | }, 21 | required: ['`foo`', '\'bar\'', '"baz"'], 22 | } 23 | -------------------------------------------------------------------------------- /test/e2e/subSchema.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Schema with Subschema', 3 | properties: { 4 | firstName: { 5 | type: 'string', 6 | }, 7 | friend: { 8 | properties: { 9 | knowsFrom: { 10 | enum: ['work', 'school', 'other'], 11 | }, 12 | }, 13 | required: ['knowsFrom'], 14 | }, 15 | coworker: { 16 | properties: { 17 | company: { 18 | properties: { 19 | name: { 20 | type: 'string', 21 | }, 22 | }, 23 | required: ['name'], 24 | additionalProperties: false, 25 | }, 26 | }, 27 | additionalProperties: { 28 | enum: [10, 20, 30], 29 | tsEnumNames: ['red', 'green', 'blue'], 30 | }, 31 | }, 32 | }, 33 | required: ['firstName'], 34 | } 35 | -------------------------------------------------------------------------------- /test/e2e/topLevelUnion.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: ['number', 'string'], 3 | additionalProperties: false, 4 | } 5 | -------------------------------------------------------------------------------- /test/e2e/tupleItemsWithTitles.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | additionalItems: false, 3 | items: [ 4 | { 5 | properties: { 6 | foo: { 7 | type: 'string', 8 | }, 9 | }, 10 | title: 'Foo', 11 | type: 'object', 12 | }, 13 | { 14 | properties: { 15 | bar: { 16 | type: 'string', 17 | }, 18 | }, 19 | title: 'Bar', 20 | type: 'object', 21 | }, 22 | ], 23 | title: 'TupleFooBar', 24 | type: 'array', 25 | } 26 | -------------------------------------------------------------------------------- /test/e2e/tupleRef.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | additionalProperties: false, 3 | definitions: { 4 | a: { type: 'string' }, 5 | b: { type: 'number' }, 6 | }, 7 | properties: { 8 | c: { 9 | items: [{ $ref: '#/definitions/a' }, { $ref: '#/definitions/b' }], 10 | type: 'array', 11 | }, 12 | }, 13 | type: 'object', 14 | } 15 | -------------------------------------------------------------------------------- /test/e2e/typedArray.1.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | $schema: 'http://json-schema.org/draft-04/schema#', 3 | type: 'object', 4 | title: 'Object With Array Field', 5 | properties: { 6 | data: { 7 | type: 'array', 8 | title: 'Array Items', 9 | items: { $ref: '#/definitions/item' }, 10 | }, 11 | }, 12 | definitions: { 13 | item: { 14 | title: 'Array Item', 15 | type: 'object', 16 | properties: { 17 | prop: { 18 | type: 'string', 19 | }, 20 | }, 21 | required: ['prop'], 22 | additionalProperties: false, 23 | }, 24 | }, 25 | required: ['data'], 26 | additionalProperties: false, 27 | } 28 | -------------------------------------------------------------------------------- /test/e2e/typedArray.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | $schema: 'http://json-schema.org/draft-04/schema#', 3 | type: 'object', 4 | title: 'Object With Array Field', 5 | properties: { 6 | data: { 7 | type: 'array', 8 | title: 'Array Items', 9 | items: { $ref: '#/definitions/item' }, 10 | }, 11 | }, 12 | definitions: { 13 | item: { 14 | title: 'Array Item', 15 | type: 'object', 16 | properties: { 17 | id: { 18 | title: 'Id', 19 | type: 'string', 20 | }, 21 | }, 22 | required: ['id'], 23 | additionalProperties: false, 24 | }, 25 | }, 26 | required: ['data'], 27 | additionalProperties: false, 28 | } 29 | -------------------------------------------------------------------------------- /test/e2e/unicode.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | title: '呵呵', 4 | properties: { 5 | someKey: { 6 | type: 'string', 7 | title: '哈哈', 8 | }, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /test/e2e/union.2.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | properties: { 4 | input: { 5 | type: ['string', 'object', 'array'], 6 | items: { 7 | type: 'string', 8 | }, 9 | }, 10 | }, 11 | additionalProperties: false, 12 | } 13 | -------------------------------------------------------------------------------- /test/e2e/union.3.ts: -------------------------------------------------------------------------------- 1 | // @see https://github.com/bcherny/json-schema-to-typescript/issues/352 2 | export const input = { 3 | $schema: 'http://json-schema.org/draft-07/schema', 4 | title: 'my-schema', 5 | type: 'object', 6 | properties: { 7 | example: { 8 | type: ['boolean', 'string'], 9 | default: true, 10 | }, 11 | }, 12 | additionalProperties: false, 13 | } 14 | -------------------------------------------------------------------------------- /test/e2e/union.4.ts: -------------------------------------------------------------------------------- 1 | // @see https://github.com/bcherny/json-schema-to-typescript/issues/357 2 | export const input = { 3 | oneOf: [ 4 | { 5 | type: 'string', 6 | }, 7 | { 8 | enum: [false], 9 | }, 10 | ], 11 | default: 'foo', 12 | } 13 | -------------------------------------------------------------------------------- /test/e2e/union.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | $schema: 'http://json-schema.org/draft-07/schema', 3 | type: 'object', 4 | properties: { 5 | test: { 6 | type: 'object', 7 | properties: { 8 | test1: { 9 | type: ['boolean', 'array'], 10 | minItems: 1, 11 | items: { 12 | type: 'string', 13 | }, 14 | }, 15 | test2: { 16 | type: ['boolean', 'array'], 17 | minItems: 1, 18 | items: { 19 | type: 'string', 20 | }, 21 | }, 22 | }, 23 | }, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /test/e2e/unionWithProperties.ts: -------------------------------------------------------------------------------- 1 | // @see https://github.com/bcherny/json-schema-to-typescript/pull/328 2 | export const input = { 3 | title: 'UnionWithProps', 4 | type: 'object', 5 | anyOf: [ 6 | { 7 | type: 'object', 8 | required: ['obj_type', 'type'], 9 | properties: { 10 | obj_type: { type: 'string', enum: ['Foo'] }, 11 | foo_type: { type: 'string' }, 12 | }, 13 | }, 14 | { 15 | type: 'object', 16 | required: ['health', 'obj_type', 'team', 'type'], 17 | properties: { 18 | obj_type: { type: 'string', enum: ['Bar'] }, 19 | bar_type: { type: 'string' }, 20 | team: { type: 'string' }, 21 | health: { type: 'integer', format: 'uint', minimum: 0.0 }, 22 | }, 23 | }, 24 | ], 25 | required: ['coords', 'id'], 26 | properties: { 27 | coords: { type: 'number' }, 28 | id: { type: 'integer' }, 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /test/e2e/unnamedSchema.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | type: 'object', 3 | properties: { 4 | foo: { 5 | type: 'string', 6 | }, 7 | }, 8 | required: ['foo'], 9 | additionalProperties: false, 10 | } 11 | -------------------------------------------------------------------------------- /test/e2e/withDescription.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Example Schema', 3 | description: 'My cool schema', 4 | type: 'object', 5 | properties: { 6 | firstName: { 7 | type: 'string', 8 | }, 9 | lastName: { 10 | type: 'string', 11 | }, 12 | age: { 13 | description: 'Age in years', 14 | type: 'integer', 15 | minimum: 0, 16 | }, 17 | }, 18 | required: ['firstName', 'lastName'], 19 | } 20 | -------------------------------------------------------------------------------- /test/e2e/withDescriptionNewlines.ts: -------------------------------------------------------------------------------- 1 | export const input = { 2 | title: 'Example Schema', 3 | description: 'My cool schema', 4 | type: 'object', 5 | properties: { 6 | firstName: { 7 | description: 'first name single line description', 8 | type: 'string', 9 | }, 10 | lastName: { 11 | type: 'string', 12 | }, 13 | age: { 14 | description: 'Age description with\nmultiple lines', 15 | type: 'integer', 16 | minimum: 0, 17 | }, 18 | }, 19 | required: ['firstName', 'lastName'], 20 | } 21 | -------------------------------------------------------------------------------- /test/http.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, readFileSync, writeFileSync } from 'node:fs' 2 | import { get as httpGet } from 'node:http' 3 | import { get as httpsGet } from 'node:https' 4 | import { join } from 'node:path' 5 | 6 | const CACHE_DIR = 'test/__fixtures__' 7 | 8 | // Gets a URL, with read-through cache 9 | // TODO: Pull this out into its own NPM module 10 | export async function getWithCache(url: string): Promise { 11 | const resultFromFilesystem = getFromFilesystem(url) 12 | if (resultFromFilesystem) 13 | return resultFromFilesystem 14 | 15 | const resultFromNetwork = await getFromNetwork(url) 16 | writeToFilesystem(url, resultFromNetwork) 17 | return resultFromNetwork 18 | } 19 | 20 | function getFromFilesystem(url: string): object | undefined { 21 | const filepath = getFilepath(url) 22 | if (!existsSync(filepath)) 23 | return 24 | 25 | return JSON.parse(readFileSync(filepath, 'utf8')) 26 | } 27 | 28 | function getFilepath(url: string): string { 29 | return join(__dirname, '../../', CACHE_DIR, url.replace(/[:/\\]/g, '-')) 30 | } 31 | 32 | function writeToFilesystem(url: string, data: object): void { 33 | const filepath = getFilepath(url) 34 | // console.info(`Writing "${filepath} to filesystem...`) 35 | writeFileSync(filepath, JSON.stringify(data, null, 2)) 36 | } 37 | 38 | function getFromNetwork(url: string): Promise { 39 | const f = url.startsWith('https://') ? httpsGet : httpGet 40 | return new Promise((resolve, reject) => { 41 | f(url, (res) => { 42 | const contentType = res.headers['content-type'] 43 | if (res.statusCode !== 200) 44 | return reject(res) 45 | else if (contentType && !/^application\/json/.test(contentType)) 46 | return reject(new Error('Invalid content-type.\n' + `Expected application/json but received ${contentType}`)) 47 | 48 | res.setEncoding('utf8') 49 | let rawData = '' 50 | res.on('data', (chunk) => { 51 | rawData += chunk 52 | }) 53 | res.on('end', () => { 54 | try { 55 | resolve(JSON.parse(rawData)) 56 | } 57 | catch (e) { 58 | reject(e) 59 | } 60 | }) 61 | }).on('error', (e) => { 62 | reject(e) 63 | }) 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /test/idempotence.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import type { JSONSchema4 } from 'json-schema' 3 | import cloneDeep from 'lodash-es/cloneDeep' 4 | import { compile } from '../src' 5 | 6 | export function run() { 7 | const SCHEMA: JSONSchema4 = { 8 | type: 'object', 9 | properties: { 10 | firstName: { 11 | type: 'string', 12 | }, 13 | }, 14 | required: ['firstName'], 15 | } 16 | 17 | it('compile() should not mutate its input', async () => { 18 | const before = cloneDeep(SCHEMA) 19 | await compile(SCHEMA, 'A') 20 | expect(before).toEqual(SCHEMA) 21 | }) 22 | 23 | it('compile() should be idempotent', async () => { 24 | const a = await compile(SCHEMA, 'A') 25 | const b = await compile(SCHEMA, 'A') 26 | expect(a).toEqual(b) 27 | }) 28 | } 29 | 30 | await run() 31 | -------------------------------------------------------------------------------- /test/linker.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { link } from '../src/linker' 3 | import { Parent } from '../src/types/JSONSchema' 4 | import { input } from './e2e/basics' 5 | 6 | it('linker should link to each node\'s parent schema', () => { 7 | const schema = link(input) as any 8 | expect(schema[Parent]).toBe(null) 9 | expect(schema.properties[Parent]).toBe(schema) 10 | expect(schema.properties.firstName[Parent]).toBe(schema.properties) 11 | expect(schema.properties.lastName[Parent]).toBe(schema.properties) 12 | expect(schema.properties.age[Parent]).toBe(schema.properties) 13 | expect(schema.properties.height[Parent]).toBe(schema.properties) 14 | expect(schema.properties.favoriteFoods[Parent]).toBe(schema.properties) 15 | expect(schema.properties.likesDogs[Parent]).toBe(schema.properties) 16 | expect(schema.required[Parent]).toBe(schema) 17 | }) 18 | -------------------------------------------------------------------------------- /test/normalizer.test.ts: -------------------------------------------------------------------------------- 1 | import { readdirSync } from 'node:fs' 2 | import { join } from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | import { expect, it } from 'vitest' 5 | import type { JSONSchema, Options } from '../src' 6 | import { DEFAULT_OPTIONS } from '../src' 7 | import { link } from '../src/linker' 8 | import { normalize } from '../src/normalizer' 9 | 10 | interface JSONTestCase { 11 | name: string 12 | in: JSONSchema 13 | out: JSONSchema 14 | options?: Options 15 | } 16 | 17 | const normalizerDir = fileURLToPath(new URL('normalizer', import.meta.url)) 18 | 19 | const modules = await Promise.all( 20 | readdirSync(normalizerDir) 21 | .filter(_ => /^.*\.json$/.test(_)) 22 | .map(_ => join(normalizerDir, _)) 23 | .map(async _ => [_, await import(_).then(r => r.default)] as [string, JSONTestCase]), 24 | ) 25 | 26 | modules 27 | .forEach(([filename, json]: [string, JSONTestCase]) => { 28 | it(json.name, () => { 29 | const normalized = normalize(link(json.in), new WeakMap(), filename, json.options ?? DEFAULT_OPTIONS) 30 | expect(json.out).toEqual(normalized) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/normalizer/addEmptyRequiredProperty.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Add empty `required` property if none is defined", 3 | "in": { 4 | "$id": "foo", 5 | "type": [ 6 | "object" 7 | ], 8 | "properties": { 9 | "a": { 10 | "type": "integer", 11 | "$id": "a" 12 | } 13 | }, 14 | "additionalProperties": true 15 | }, 16 | "out": { 17 | "$id": "foo", 18 | "type": "object", 19 | "properties": { 20 | "a": { 21 | "type": "integer", 22 | "$id": "a" 23 | } 24 | }, 25 | "additionalProperties": true, 26 | "required": [] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/normalizer/constToEnum.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Normalize const to singleton enum", 3 | "in": { 4 | "$id": "foo", 5 | "const": "foobar" 6 | }, 7 | "out": { 8 | "$id": "foo", 9 | "enum": [ 10 | "foobar" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/normalizer/defaultAdditionalProperties.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Default additionalProperties to false", 3 | "in": { 4 | "$id": "foo", 5 | "type": [ 6 | "object" 7 | ], 8 | "properties": { 9 | "a": { 10 | "type": "integer", 11 | "$id": "a" 12 | }, 13 | "b": { 14 | "type": "object" 15 | } 16 | }, 17 | "required": [] 18 | }, 19 | "options": { 20 | "additionalProperties": false 21 | }, 22 | "out": { 23 | "$id": "foo", 24 | "type": "object", 25 | "properties": { 26 | "a": { 27 | "type": "integer", 28 | "$id": "a" 29 | }, 30 | "b": { 31 | "additionalProperties": false, 32 | "required": [], 33 | "type": "object" 34 | } 35 | }, 36 | "required": [], 37 | "additionalProperties": false 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/normalizer/defaultAdditionalProperties.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Default additionalProperties to true", 3 | "in": { 4 | "$id": "foo", 5 | "type": [ 6 | "object" 7 | ], 8 | "properties": { 9 | "a": { 10 | "type": "integer", 11 | "$id": "a" 12 | } 13 | }, 14 | "required": [] 15 | }, 16 | "out": { 17 | "$id": "foo", 18 | "type": "object", 19 | "properties": { 20 | "a": { 21 | "type": "integer", 22 | "$id": "a" 23 | } 24 | }, 25 | "required": [], 26 | "additionalProperties": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/normalizer/defaultID.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Default top level `id` (1)", 3 | "in": { 4 | "friend": { 5 | "properties": { 6 | "knowsFrom": { 7 | "enum": [ 8 | "work", 9 | "school", 10 | "other" 11 | ] 12 | } 13 | }, 14 | "additionalProperties": true, 15 | "required": [], 16 | "$id": "friend" 17 | }, 18 | "properties": { 19 | "firstName": { 20 | "type": "string" 21 | } 22 | }, 23 | "additionalProperties": true, 24 | "required": [ 25 | "firstName" 26 | ] 27 | }, 28 | "out": { 29 | "$id": "DefaultID1", 30 | "friend": { 31 | "properties": { 32 | "knowsFrom": { 33 | "enum": [ 34 | "work", 35 | "school", 36 | "other" 37 | ] 38 | } 39 | }, 40 | "additionalProperties": true, 41 | "required": [], 42 | "$id": "friend" 43 | }, 44 | "properties": { 45 | "firstName": { 46 | "type": "string" 47 | } 48 | }, 49 | "additionalProperties": true, 50 | "required": [ 51 | "firstName" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/normalizer/defaultID.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Default top level `id` (2)", 3 | "in": { 4 | "$id": "DefaultID", 5 | "friend": { 6 | "properties": { 7 | "knowsFrom": { 8 | "enum": [ 9 | "work", 10 | "school", 11 | "other" 12 | ] 13 | } 14 | }, 15 | "additionalProperties": true, 16 | "required": [], 17 | "$id": "friend" 18 | }, 19 | "properties": { 20 | "firstName": { 21 | "type": "string" 22 | } 23 | }, 24 | "additionalProperties": true, 25 | "required": [ 26 | "firstName" 27 | ] 28 | }, 29 | "out": { 30 | "$id": "DefaultID", 31 | "friend": { 32 | "properties": { 33 | "knowsFrom": { 34 | "enum": [ 35 | "work", 36 | "school", 37 | "other" 38 | ] 39 | } 40 | }, 41 | "additionalProperties": true, 42 | "required": [], 43 | "$id": "friend" 44 | }, 45 | "properties": { 46 | "firstName": { 47 | "type": "string" 48 | } 49 | }, 50 | "additionalProperties": true, 51 | "required": [ 52 | "firstName" 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/normalizer/destructureUnaryTypes.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Destructure unary types", 3 | "in": { 4 | "$id": "foo", 5 | "type": [ 6 | "object" 7 | ], 8 | "definitions": { 9 | "a": { 10 | "type": [ 11 | "integer" 12 | ], 13 | "$id": "a" 14 | } 15 | }, 16 | "properties": { 17 | "b": { 18 | "type": [ 19 | "string" 20 | ], 21 | "$id": "b" 22 | } 23 | }, 24 | "additionalProperties": true, 25 | "required": [] 26 | }, 27 | "out": { 28 | "$id": "foo", 29 | "$defs": { 30 | "a": { 31 | "type": "integer", 32 | "$id": "a" 33 | } 34 | }, 35 | "properties": { 36 | "b": { 37 | "type": "string", 38 | "$id": "b" 39 | } 40 | }, 41 | "type": "object", 42 | "additionalProperties": true, 43 | "required": [] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/normalizer/emptyStringConstToEnum.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Normalize empty const to singleton enum", 3 | "in": { 4 | "$id": "foo", 5 | "const": "" 6 | }, 7 | "out": { 8 | "$id": "foo", 9 | "enum": [ 10 | "" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/normalizer/nonObjectItems.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Non object items.items", 3 | "in": { 4 | "$id": "foo", 5 | "type": "object", 6 | "properties": { 7 | "myProperty": { 8 | "type": "array", 9 | "items": "string" 10 | } 11 | }, 12 | "additionalProperties": false 13 | }, 14 | "out": { 15 | "$id": "foo", 16 | "type": "object", 17 | "properties": { 18 | "myProperty": { 19 | "items": "string", 20 | "minItems": 0, 21 | "type": "array" 22 | } 23 | }, 24 | "additionalProperties": false, 25 | "required": [] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/normalizer/normalizeDefs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Normalize definitions to $defs", 3 | "in": { 4 | "$id": "foo", 5 | "definitions": { 6 | "bar": "baz" 7 | } 8 | }, 9 | "out": { 10 | "$id": "foo", 11 | "$defs": { 12 | "bar": "baz" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/normalizer/normalizeExtends.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Normalize extends to an array", 3 | "in": { 4 | "$id": "foo", 5 | "extends": "foo", 6 | "type": "object", 7 | "required": [], 8 | "additionalProperties": true 9 | }, 10 | "out": { 11 | "$id": "foo", 12 | "extends": [ 13 | "foo" 14 | ], 15 | "type": "object", 16 | "required": [], 17 | "additionalProperties": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/normalizer/normalizeID.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Transform id to $id", 3 | "in": { 4 | "properties": { 5 | "b": { 6 | "id": "b", 7 | "type": "object", 8 | "additionalProperties": false, 9 | "required": [] 10 | } 11 | }, 12 | "additionalProperties": false, 13 | "required": [], 14 | "id": "a" 15 | }, 16 | "out": { 17 | "properties": { 18 | "b": { 19 | "$id": "b", 20 | "type": "object", 21 | "additionalProperties": false, 22 | "required": [] 23 | } 24 | }, 25 | "additionalProperties": false, 26 | "required": [], 27 | "$id": "a" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/normalizer/redundantNull.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Remove `enum=[null]` if `type=['null']`", 3 | "in": { 4 | "type": "object", 5 | "properties": { 6 | "noopMissingType": { 7 | "enum": [ 8 | "foo", 9 | "bar" 10 | ] 11 | }, 12 | "noopMissingEnum": { 13 | "type": [ 14 | "null", 15 | "string" 16 | ] 17 | }, 18 | "noopNonNullableEnum": { 19 | "type": "string", 20 | "enum": [ 21 | "foo", 22 | "bar", 23 | null 24 | ] 25 | }, 26 | "dedupeNulls": { 27 | "type": [ 28 | "null", 29 | "string" 30 | ], 31 | "enum": [ 32 | "foo", 33 | "bar", 34 | null 35 | ] 36 | } 37 | } 38 | }, 39 | "out": { 40 | "additionalProperties": true, 41 | "$id": "RedundantNull", 42 | "required": [], 43 | "type": "object", 44 | "properties": { 45 | "noopMissingType": { 46 | "enum": [ 47 | "foo", 48 | "bar" 49 | ] 50 | }, 51 | "noopMissingEnum": { 52 | "type": [ 53 | "null", 54 | "string" 55 | ] 56 | }, 57 | "noopNonNullableEnum": { 58 | "type": "string", 59 | "enum": [ 60 | "foo", 61 | "bar", 62 | null 63 | ] 64 | }, 65 | "dedupeNulls": { 66 | "type": "string", 67 | "enum": [ 68 | "foo", 69 | "bar", 70 | null 71 | ] 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/normalizer/removeEmptyExtends.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Remove empty extends (1)", 3 | "in": { 4 | "$id": "foo", 5 | "extends": [], 6 | "type": "object", 7 | "required": [], 8 | "additionalProperties": true 9 | }, 10 | "out": { 11 | "$id": "foo", 12 | "type": "object", 13 | "required": [], 14 | "additionalProperties": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/normalizer/removeEmptyExtends.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Remove empty extends (2)", 3 | "in": { 4 | "$id": "foo", 5 | "extends": null, 6 | "type": "object", 7 | "required": [], 8 | "additionalProperties": true 9 | }, 10 | "out": { 11 | "$id": "foo", 12 | "type": "object", 13 | "required": [], 14 | "additionalProperties": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/normalizer/removeMaxItems.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Remove maxItems if it is big enough to likely cause OOMs (1)", 3 | "in": { 4 | "additionalProperties": false, 5 | "$id": "RemoveMaxItems", 6 | "properties": { 7 | "a": { 8 | "minItems": 5, 9 | "type": "array" 10 | }, 11 | "b": { 12 | "maxItems": 50, 13 | "minItems": 5, 14 | "type": "array" 15 | }, 16 | "c": { 17 | "maxItems": 50, 18 | "type": "array" 19 | }, 20 | "d": { 21 | "maxItems": 15, 22 | "minItems": 5, 23 | "type": "array" 24 | }, 25 | "e": { 26 | "maxItems": 15, 27 | "type": "array" 28 | } 29 | }, 30 | "required": [] 31 | }, 32 | "out": { 33 | "additionalProperties": false, 34 | "$id": "RemoveMaxItems", 35 | "properties": { 36 | "a": { 37 | "description": "@minItems 5", 38 | "minItems": 5, 39 | "type": "array" 40 | }, 41 | "b": { 42 | "description": "@minItems 5\n@maxItems 50", 43 | "minItems": 5, 44 | "type": "array" 45 | }, 46 | "c": { 47 | "description": "@maxItems 50", 48 | "minItems": 0, 49 | "type": "array" 50 | }, 51 | "d": { 52 | "description": "@minItems 5\n@maxItems 15", 53 | "maxItems": 15, 54 | "minItems": 5, 55 | "type": "array" 56 | }, 57 | "e": { 58 | "description": "@maxItems 15", 59 | "maxItems": 15, 60 | "minItems": 0, 61 | "type": "array" 62 | } 63 | }, 64 | "required": [] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/normalizer/removeMaxItems.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Remove maxItems if it is big enough to likely cause OOMs (2)", 3 | "in": { 4 | "additionalProperties": false, 5 | "$id": "RemoveMaxItems", 6 | "properties": { 7 | "a": { 8 | "minItems": 5, 9 | "type": "array" 10 | }, 11 | "b": { 12 | "maxItems": 50, 13 | "minItems": 5, 14 | "type": "array" 15 | }, 16 | "c": { 17 | "maxItems": 500, 18 | "type": "array" 19 | }, 20 | "d": { 21 | "maxItems": 15, 22 | "minItems": 5, 23 | "type": "array" 24 | }, 25 | "e": { 26 | "maxItems": 15, 27 | "type": "array" 28 | } 29 | }, 30 | "required": [] 31 | }, 32 | "out": { 33 | "additionalProperties": false, 34 | "$id": "RemoveMaxItems", 35 | "properties": { 36 | "a": { 37 | "description": "@minItems 5", 38 | "minItems": 5, 39 | "type": "array" 40 | }, 41 | "b": { 42 | "description": "@minItems 5\n@maxItems 50", 43 | "maxItems": 50, 44 | "minItems": 5, 45 | "type": "array" 46 | }, 47 | "c": { 48 | "description": "@maxItems 500", 49 | "maxItems": 500, 50 | "minItems": 0, 51 | "type": "array" 52 | }, 53 | "d": { 54 | "description": "@minItems 5\n@maxItems 15", 55 | "maxItems": 15, 56 | "minItems": 5, 57 | "type": "array" 58 | }, 59 | "e": { 60 | "description": "@maxItems 15", 61 | "maxItems": 15, 62 | "minItems": 0, 63 | "type": "array" 64 | } 65 | }, 66 | "required": [] 67 | }, 68 | "options": { 69 | "maxItems": 1000 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /test/normalizer/removeMaxItems.3.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Remove maxItems if it is big enough to likely cause OOMs (3)", 3 | "in": { 4 | "additionalProperties": false, 5 | "$id": "RemoveMaxItems", 6 | "properties": { 7 | "a": { 8 | "description": "Test", 9 | "minItems": 5, 10 | "type": "array" 11 | }, 12 | "b": { 13 | "description": "Test", 14 | "maxItems": 50, 15 | "minItems": 5, 16 | "type": "array" 17 | }, 18 | "c": { 19 | "maxItems": 50, 20 | "type": "array" 21 | }, 22 | "d": { 23 | "maxItems": 15, 24 | "minItems": 5, 25 | "type": "array" 26 | }, 27 | "e": { 28 | "maxItems": 15, 29 | "type": "array" 30 | } 31 | }, 32 | "required": [] 33 | }, 34 | "out": { 35 | "additionalProperties": false, 36 | "$id": "RemoveMaxItems", 37 | "properties": { 38 | "a": { 39 | "description": "Test\n\n@minItems 5", 40 | "minItems": 5, 41 | "type": "array" 42 | }, 43 | "b": { 44 | "description": "Test\n\n@minItems 5\n@maxItems 50", 45 | "minItems": 5, 46 | "type": "array" 47 | }, 48 | "c": { 49 | "description": "@maxItems 50", 50 | "minItems": 0, 51 | "type": "array" 52 | }, 53 | "d": { 54 | "description": "@minItems 5\n@maxItems 15", 55 | "minItems": 5, 56 | "type": "array" 57 | }, 58 | "e": { 59 | "description": "@maxItems 15", 60 | "minItems": 0, 61 | "type": "array" 62 | } 63 | }, 64 | "required": [] 65 | }, 66 | "options": { 67 | "maxItems": -1 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/resources/BaseType.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://dummy.com/api/base1", 3 | "title": "Base1", 4 | "type": "object", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | } 12 | }, 13 | "required": ["firstName", "lastName"], 14 | "additionalProperties": false 15 | } 16 | -------------------------------------------------------------------------------- /test/resources/BaseType.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://dummy.com/api/base2", 3 | "title": "Base2", 4 | "type": "object", 5 | "properties": { 6 | "age": { 7 | "type": "integer" 8 | } 9 | }, 10 | "required": ["age"], 11 | "additionalProperties": false 12 | } 13 | -------------------------------------------------------------------------------- /test/resources/DefinitionsOnly.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "DummyPerson": { 4 | "properties": { 5 | "age": { 6 | "type": "integer" 7 | } 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/resources/Enum.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "fstype": { 5 | "enum": ["ext3", "ext4", "btrfs"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/resources/MultiSchema/a.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "A schema", 3 | "type": "object", 4 | "properties": { 5 | "f": { "type": "string" }, 6 | "g": { "type": "integer" } 7 | }, 8 | "additionalProperties": false, 9 | "required": ["f"] 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/MultiSchema/b.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "B schema", 3 | "type": "object", 4 | "properties": { 5 | "x": { "type": "string" }, 6 | "y": { "type": "integer" } 7 | }, 8 | "additionalProperties": true, 9 | "required": ["y"] 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/MultiSchema2/bar/SameDirName/MultiSchema2/e.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "E schema", 3 | "type": "string" 4 | } 5 | -------------------------------------------------------------------------------- /test/resources/MultiSchema2/bar/b.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "B schema", 3 | "type": "object", 4 | "properties": { 5 | "x": { "type": "string" }, 6 | "y": { "type": "integer" } 7 | }, 8 | "additionalProperties": true, 9 | "required": ["y"] 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/MultiSchema2/foo/a.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "A schema", 3 | "type": "object", 4 | "properties": { 5 | "f": { "type": "string" }, 6 | "g": { "type": "integer" } 7 | }, 8 | "additionalProperties": false, 9 | "required": ["f"] 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/MultiSchema2/foobar/c.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "C schema", 3 | "type": "object", 4 | "properties": { 5 | "f": { "type": "string" }, 6 | "g": { "type": "integer" } 7 | }, 8 | "additionalProperties": false, 9 | "required": ["f"] 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/MultiSchema2/foobar/fuzz/d.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "D schema", 3 | "type": "object", 4 | "properties": { 5 | "f": { "type": "string" }, 6 | "g": { "type": "integer" } 7 | }, 8 | "additionalProperties": false, 9 | "required": ["f"] 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/Person.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "additionalProperties": false, 4 | "type": "object", 5 | "properties": { 6 | "name": { 7 | "type": "string" 8 | }, 9 | "children": { 10 | "$ref": "#" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/resources/ReferencedCombinationType.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://dummy.com/api/example-schema", 3 | "title": "Example Combined Schema", 4 | "type": "object", 5 | "oneOf": [ 6 | { "type": "string" }, 7 | { "type": "number" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /test/resources/ReferencedType.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://dummy.com/api/example-schema", 3 | "title": "Example Schema", 4 | "type": "object", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "description": "Age in years", 14 | "type": "integer", 15 | "minimum": 0 16 | }, 17 | "height": { 18 | "type": "number" 19 | }, 20 | "favoriteFoods": { 21 | "type": "array" 22 | }, 23 | "likesDogs": { 24 | "type": "boolean" 25 | } 26 | }, 27 | "required": ["firstName", "lastName"] 28 | } 29 | -------------------------------------------------------------------------------- /test/resources/ReferencedTypeNotNormalized.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Example Schema", 3 | "type": "object", 4 | "properties": { 5 | "b": { 6 | "items": { 7 | "type": "number" 8 | }, 9 | "maxItems": 5 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/resources/ReferencedTypeWithoutID.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Example Schema", 3 | "type": "object", 4 | "properties": { 5 | "firstName": { 6 | "type": "string" 7 | }, 8 | "lastName": { 9 | "type": "string" 10 | }, 11 | "age": { 12 | "description": "Age in years", 13 | "type": "integer", 14 | "minimum": 0 15 | }, 16 | "height": { 17 | "type": "number" 18 | }, 19 | "favoriteFoods": { 20 | "type": "array" 21 | }, 22 | "likesDogs": { 23 | "type": "boolean" 24 | } 25 | }, 26 | "required": ["firstName", "lastName"] 27 | } 28 | -------------------------------------------------------------------------------- /test/resources/ReferencedTypeWithoutIDConflict.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Example Schema", 3 | "properties": { 4 | "isConflict": { 5 | "type": "boolean" 6 | } 7 | }, 8 | "required": ["isConflict"], 9 | "additionalProperties": false 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/cycle.3.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Cycle (3)", 3 | "type": "object", 4 | "properties": { 5 | "foo": { 6 | "$ref": "./cycle.4.json" 7 | } 8 | }, 9 | "additionalProperties": false 10 | } 11 | -------------------------------------------------------------------------------- /test/resources/cycle.4.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Cycle (4)", 3 | "type": "object", 4 | "properties": { 5 | "foo": { 6 | "type": "integer" 7 | }, 8 | "bar": { 9 | "$ref": "./cycle.3.json" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/resources/extends/Circle.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Circle", 4 | "description": "A Circle", 5 | "additionalProperties": false, 6 | "extends": { 7 | "$ref": "Shape.json" 8 | }, 9 | "properties": { 10 | "type": { 11 | "enum": [ 12 | "circle" 13 | ] 14 | }, 15 | "radius": { 16 | "type": "number" 17 | } 18 | }, 19 | "required": [ 20 | "type", 21 | "radius" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /test/resources/extends/Shape.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Shape", 4 | "description": "A Shape", 5 | "additionalProperties": false, 6 | "properties": { 7 | "type": { 8 | "type": "string" 9 | }, 10 | "id": { 11 | "type": "string" 12 | } 13 | }, 14 | "required": [ 15 | "type", 16 | "id" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/resources/extends/Square.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "title": "Square", 4 | "description": "A Square", 5 | "additionalProperties": false, 6 | "extends": { 7 | "$ref": "Shape.json" 8 | }, 9 | "properties": { 10 | "type": { 11 | "enum": [ 12 | "square" 13 | ] 14 | }, 15 | "height": { 16 | "type": "number" 17 | }, 18 | "width": { 19 | "type": "number" 20 | } 21 | }, 22 | "required": [ 23 | "type", 24 | "height", 25 | "width" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /test/resources/other/ReferencingType.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Referencing", 3 | "type": "object", 4 | "properties": { 5 | "foo": { 6 | "$ref": "ReferencedType.json" 7 | } 8 | }, 9 | "required": ["foo"], 10 | "additionalProperties": false 11 | } 12 | -------------------------------------------------------------------------------- /test/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { link } from '../src/linker' 3 | import type { LinkedJSONSchema } from '../src/types/JSONSchema' 4 | import { generateName, isSchemaLike, pathTransform } from '../src/utils' 5 | 6 | it('pathTransform', () => { 7 | expect(pathTransform('types', 'schemas', 'schemas/foo/a.json')).toBe('types/foo') 8 | expect(pathTransform('./schemas/types', './schemas', 'schemas/foo/bar/a.json')).toBe('schemas/types/foo/bar') 9 | expect(pathTransform('types', './src/../types/../schemas', 'schemas/foo/a.json')).toBe('types/foo') 10 | }) 11 | it('generateName', () => { 12 | const usedNames = new Set() 13 | expect(generateName('a', usedNames)).toBe('A') 14 | expect(generateName('abc', usedNames)).toBe('Abc') 15 | expect(generateName('ABcd', usedNames)).toBe('ABcd') 16 | expect(generateName('$Abc_123', usedNames)).toBe('$Abc_123') 17 | expect(generateName('Abc-de-f', usedNames)).toBe('AbcDeF') 18 | 19 | // Index should increment: 20 | expect(generateName('a', usedNames)).toBe('A1') 21 | expect(generateName('a', usedNames)).toBe('A2') 22 | expect(generateName('a', usedNames)).toBe('A3') 23 | }) 24 | it('isSchemaLike', () => { 25 | const schema = link({ 26 | title: 'Example Schema', 27 | type: 'object', 28 | properties: { 29 | firstName: { 30 | type: 'string', 31 | }, 32 | lastName: { 33 | id: 'lastName', 34 | type: 'string', 35 | }, 36 | }, 37 | required: ['firstName', 'lastName'], 38 | }) 39 | expect(isSchemaLike(schema)).toBe(true) 40 | expect(isSchemaLike([] as any as LinkedJSONSchema)).toBe(false) 41 | expect(isSchemaLike(schema.properties as LinkedJSONSchema)).toBe(false) 42 | expect(isSchemaLike(schema.required as any as LinkedJSONSchema)).toBe(false) 43 | expect(isSchemaLike(schema.title as any as LinkedJSONSchema)).toBe(false) 44 | expect(isSchemaLike(schema.properties!.firstName)).toBe(true) 45 | expect(isSchemaLike(schema.properties!.lastName)).toBe(true) 46 | }) 47 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "strict": true, 7 | "noEmit": true 8 | } 9 | } 10 | --------------------------------------------------------------------------------