From 2dc461ae896913b21eb9405b87e2056ad478f4e2 Mon Sep 17 00:00:00 2001 From: Wian Drs Date: Sat, 20 Jun 2026 00:12:19 +0700 Subject: [PATCH] Ubah dari Swagger ke Dedoc lalu buat API Ticket dan TicketType --- app/Docs/OpenApiSpec.php | 20 - app/Http/Controllers/ApiController.php | 10 + app/Http/Controllers/AuthController.php | 30 +- .../Controllers/Ticket/TicketController.php | 70 + .../Ticket/TicketTypeController.php | 70 + .../Requests/Ticket/StoreTicketRequest.php | 33 + .../Requests/Ticket/UpdateTicketRequest.php | 34 + .../TicketType/StoreTicketTypeRequest.php | 32 + .../TicketType/UpdateTicketTypeRequest.php | 32 + app/Http/Resources/Ticket/TicketResource.php | 31 + .../TicketType/TicketTypeResource.php | 27 + app/Models/Ticket.php | 11 +- app/Models/TicketLog.php | 12 +- app/Models/TicketType.php | 13 +- app/Services/Ticket/TicketService.php | 79 + app/Services/Ticket/TicketTypeService.php | 24 + app/Traits/ApiResponse.php | 73 + bootstrap/app.php | 1 + .../Ticketing/Add Tickets.yml | 28 + .../Ticketing/Get List Tickets.yml | 17 + .../Ticketing/folder.yml | 7 + composer.json | 2 +- composer.lock | 699 +- config/l5-swagger.php | 333 - config/scramble.php | 180 + .../views/vendor/scramble}/.gitkeep | 0 .../views/vendor/scramble/docs.blade.php | 112 + .../views/vendor/scramble/scalar.blade.php | 32 + routes/api.php | 27 +- vendor/bin/openapi | 119 - vendor/bin/openapi.bat | 5 - vendor/composer/autoload_classmap.php | 661 +- vendor/composer/autoload_files.php | 5 +- vendor/composer/autoload_psr4.php | 7 +- vendor/composer/autoload_static.php | 704 +- vendor/composer/installed.json | 454 +- vendor/composer/installed.php | 79 +- .../l5-swagger/.claude/skills/write-tests.md | 273 - vendor/darkaonline/l5-swagger/CLAUDE.md | 83 - vendor/darkaonline/l5-swagger/composer.json | 65 - .../l5-swagger/config/l5-swagger.php | 333 - vendor/darkaonline/l5-swagger/phpstan.neon | 7 - .../resources/views/index.blade.php | 174 - .../l5-swagger/src/ConfigFactory.php | 71 - .../src/Console/GenerateDocsCommand.php | 75 - .../src/CustomGeneratorInterface.php | 10 - .../src/Exceptions/L5SwaggerException.php | 7 - .../darkaonline/l5-swagger/src/Generator.php | 359 - .../l5-swagger/src/GeneratorFactory.php | 43 - .../Controllers/SwaggerAssetController.php | 50 - .../Http/Controllers/SwaggerController.php | 201 - .../l5-swagger/src/Http/Middleware/Config.php | 38 - .../l5-swagger/src/L5SwaggerFacade.php | 20 - .../src/L5SwaggerServiceProvider.php | 75 - .../l5-swagger/src/SecurityDefinitions.php | 117 - vendor/darkaonline/l5-swagger/src/helpers.php | 66 - vendor/darkaonline/l5-swagger/src/routes.php | 66 - vendor/dedoc/scramble/CHANGELOG.md | 3 + .../LICENSE => dedoc/scramble/LICENSE.md} | 12 +- vendor/dedoc/scramble/README.md | 41 + vendor/dedoc/scramble/composer.json | 73 + vendor/dedoc/scramble/config/scramble.php | 174 + .../dedoc/scramble/dictionaries/classMap.php | 10 + .../dictionaries/scripts/generate.php | 46 + vendor/dedoc/scramble/phpstan.neon | 17 + vendor/dedoc/scramble/pint.json | 6 + .../scramble/resources/views/.gitkeep} | 0 .../scramble/resources/views/docs.blade.php | 112 + .../scramble/resources/views/scalar.blade.php | 32 + vendor/dedoc/scramble/routes/web.php | 7 + .../scramble/src/AbstractOpenApiVisitor.php | 10 + .../scramble/src/Attributes/BodyParameter.php | 24 + .../src/Attributes/CookieParameter.php | 24 + .../scramble/src/Attributes/Endpoint.php | 32 + .../dedoc/scramble/src/Attributes/Example.php | 26 + .../Attributes/ExcludeAllRoutesFromDocs.php | 11 + .../src/Attributes/ExcludeRouteFromDocs.php | 11 + .../dedoc/scramble/src/Attributes/Group.php | 23 + .../dedoc/scramble/src/Attributes/Header.php | 70 + .../src/Attributes/HeaderParameter.php | 24 + .../dedoc/scramble/src/Attributes/Hidden.php | 11 + .../scramble/src/Attributes/IgnoreParam.php | 17 + .../scramble/src/Attributes/MissingValue.php | 5 + .../scramble/src/Attributes/Parameter.php | 32 + .../scramble/src/Attributes/PathParameter.php | 24 + .../src/Attributes/QueryParameter.php | 24 + .../scramble/src/Attributes/Response.php | 118 + .../scramble/src/Attributes/SchemaName.php | 21 + .../dedoc/scramble/src/CacheableGenerator.php | 41 + .../scramble/src/Configuration/ApiPath.php | 96 + .../Configuration/DocumentTransformers.php | 56 + .../GeneratorConfigCollection.php | 53 + .../src/Configuration/InferConfig.php | 90 + .../src/Configuration/JsonApiConfig.php | 24 + .../Configuration/OperationTransformers.php | 69 + .../Configuration/ParametersExtractors.php | 88 + .../src/Configuration/RendererConfig.php | 32 + .../src/Configuration/RuleTransformers.php | 97 + .../SecurityDocumentationContext.php | 17 + .../src/Configuration/ServerVariables.php | 32 + .../Console/Commands/AnalyzeDocumentation.php | 141 + .../Console/Commands/CacheDocumentation.php | 41 + .../Commands/ClearDocumentationCache.php | 34 + .../src/Console/Commands/Components/Code.php | 21 + .../Console/Commands/Components/Component.php | 10 + .../Components/TermsOfContentItem.php | 40 + .../Concerns/ManagesDocumentationCache.php | 42 + .../Console/Commands/ExportDocumentation.php | 35 + .../dedoc/scramble/src/ContextReferences.php | 20 + .../src/ContextReferencesCollection.php | 79 + .../Contracts/AllRulesSchemasTransformer.php | 14 + .../src/Contracts/DocumentTransformer.php | 11 + .../src/Contracts/OperationTransformer.php | 11 + .../src/Contracts/RuleTransformer.php | 14 + .../SecurityDocumentationStrategy.php | 11 + .../DocumentTransformers/AddDocumentTags.php | 57 + ...nupUnusedResponseReferencesTransformer.php | 36 + .../src/Enums/JsonApiArraySerialization.php | 9 + .../src/Exceptions/ConsoleRenderable.php | 10 + .../scramble/src/Exceptions/InvalidSchema.php | 79 + ...penApiReferenceTargetNotFoundException.php | 5 + .../scramble/src/Exceptions/RouteAware.php | 14 + .../src/Exceptions/RouteAwareTrait.php | 30 + .../Exceptions/RulesEvaluationException.php | 64 + .../ExceptionToResponseExtension.php | 29 + .../src/Extensions/OperationExtension.php | 21 + .../src/Extensions/TypeToSchemaExtension.php | 34 + vendor/dedoc/scramble/src/Generator.php | 373 + vendor/dedoc/scramble/src/GeneratorConfig.php | 319 + .../Http/Middleware/RestrictedDocsAccess.php | 21 + vendor/dedoc/scramble/src/Infer.php | 32 + .../src/Infer/Analyzer/ClassAnalyzer.php | 109 + .../src/Infer/Analyzer/MethodAnalyzer.php | 46 + .../src/Infer/Analyzer/PropertyAnalyzer.php | 74 + .../Infer/AutoResolvingArgumentTypeBag.php | 53 + .../src/Infer/Configuration/ClassLike.php | 13 + .../Configuration/ClassLikeAndChildren.php | 13 + .../Infer/Configuration/DefinitionMatcher.php | 8 + vendor/dedoc/scramble/src/Infer/Context.php | 38 + .../src/Infer/Contracts/ArgumentTypeBag.php | 20 + .../src/Infer/Contracts/ClassDefinition.php | 13 + .../Contracts/ClassDefinitionBuilder.php | 8 + .../FunctionLikeDefinitionBuilder.php | 10 + .../scramble/src/Infer/Contracts/Index.php | 13 + .../Infer/Definition/AttributeDefinition.php | 32 + .../src/Infer/Definition/ClassDefinition.php | 477 + .../Definition/ClassPropertyDefinition.php | 51 + .../Definition/FunctionLikeAstDefinition.php | 129 + .../Definition/FunctionLikeDefinition.php | 61 + .../Definition/LazyShallowClassDefinition.php | 38 + .../Infer/Definition/PendingDocComment.php | 51 + .../Infer/Definition/PropertyVisibility.php | 21 + .../Definition/ShallowClassDefinition.php | 23 + .../FunctionLikeAstDefinitionBuilder.php | 376 + ...ionLikeDeclarationAstDefinitionBuilder.php | 100 + ...LikeDeclarationPhpDocDefinitionBuilder.php | 95 + ...unctionLikeReflectionDefinitionBuilder.php | 170 + .../LazyClassReflectionDefinitionBuilder.php | 83 + .../DefinitionBuilders/SelfOutTypeBuilder.php | 252 + ...hallowClassReflectionDefinitionBuilder.php | 69 + .../AfterClassDefinitionCreatedExtension.php | 12 + .../AfterSideEffectCallAnalyzed.php | 12 + .../AnyMethodReturnTypeExtension.php | 11 + .../Extensions/Event/AnyMethodCallEvent.php | 40 + .../Event/ClassDefinitionCreatedEvent.php | 13 + .../Event/Concerns/ArgumentTypesAware.php | 14 + .../Extensions/Event/FunctionCallEvent.php | 28 + .../Extensions/Event/MethodCallEvent.php | 36 + .../Extensions/Event/PropertyFetchEvent.php | 30 + .../Event/ReferenceResolutionEvent.php | 12 + .../Extensions/Event/SideEffectCallEvent.php | 22 + .../Event/StaticMethodCallEvent.php | 34 + .../ExpressionExceptionExtension.php | 19 + .../ExpressionTypeInferExtension.php | 12 + .../src/Infer/Extensions/ExtensionsBroker.php | 289 + .../FunctionReturnTypeExtension.php | 13 + .../Infer/Extensions/IndexBuildingBroker.php | 39 + .../src/Infer/Extensions/InferExtension.php | 5 + .../MethodCallExceptionsExtension.php | 16 + .../Extensions/MethodReturnTypeExtension.php | 14 + .../Extensions/PropertyTypeExtension.php | 14 + .../src/Infer/Extensions/ResolvingType.php | 12 + .../StaticMethodReturnTypeExtension.php | 13 + .../Extensions/TypeResolverExtension.php | 12 + .../scramble/src/Infer/Flow/AbstractNode.php | 68 + .../scramble/src/Infer/Flow/ConditionNode.php | 5 + vendor/dedoc/scramble/src/Infer/Flow/Edge.php | 203 + .../src/Infer/Flow/ExpressionTypeInferrer.php | 249 + .../scramble/src/Infer/Flow/MergeNode.php | 5 + vendor/dedoc/scramble/src/Infer/Flow/Node.php | 17 + .../dedoc/scramble/src/Infer/Flow/Nodes.php | 457 + .../scramble/src/Infer/Flow/StartNode.php | 5 + .../scramble/src/Infer/Flow/StatementNode.php | 15 + .../scramble/src/Infer/Flow/TerminateNode.php | 19 + .../src/Infer/Flow/TerminationKind.php | 9 + .../dedoc/scramble/src/Infer/FlowBuilder.php | 173 + .../src/Infer/Handler/ArrayHandler.php | 29 + .../src/Infer/Handler/ArrayItemHandler.php | 60 + .../src/Infer/Handler/AssignHandler.php | 140 + .../src/Infer/Handler/ClassHandler.php | 37 + .../src/Infer/Handler/CreatesScope.php | 11 + .../Handler/ExceptionInferringExtensions.php | 40 + .../ExpressionTypeInferringExtensions.php | 44 + .../src/Infer/Handler/FunctionLikeHandler.php | 247 + .../Infer/Handler/IndexBuildingHandler.php | 25 + .../src/Infer/Handler/PhpDocHandler.php | 59 + .../src/Infer/Handler/PropertyHandler.php | 76 + .../src/Infer/Handler/ReturnHandler.php | 32 + .../src/Infer/Handler/ThrowHandler.php | 35 + .../src/Infer/Handler/UnsetHandler.php | 73 + .../src/Infer/Reflector/ClassReflector.php | 71 + .../src/Infer/Reflector/ClosureReflector.php | 169 + .../src/Infer/Reflector/MethodReflector.php | 141 + .../src/Infer/Reflector/PropertyReflector.php | 238 + .../scramble/src/Infer/Scope/GlobalScope.php | 20 + .../dedoc/scramble/src/Infer/Scope/Index.php | 98 + .../Scope/LazyShallowReflectionIndex.php | 59 + .../src/Infer/Scope/NodeTypesResolver.php | 36 + .../dedoc/scramble/src/Infer/Scope/Scope.php | 190 + .../scramble/src/Infer/Scope/ScopeContext.php | 28 + .../Infer/Services/ConstFetchTypeGetter.php | 40 + .../src/Infer/Services/FileNameResolver.php | 71 + .../src/Infer/Services/FileParser.php | 40 + .../src/Infer/Services/FileParserResult.php | 47 + .../KeyedArrayUnpackingTypeVisitor.php | 20 + .../Services/LateTypeResolvingTypeVisitor.php | 25 + .../src/Infer/Services/RecursionGuard.php | 40 + .../Infer/Services/ReferenceTypeResolver.php | 669 + .../Infer/Services/ShallowTypeResolver.php | 236 + .../Infer/Services/TemplateTypesSolver.php | 209 + .../src/Infer/Services/TemplatesMap.php | 117 + .../Services/UnionNormalizingTypeVisitor.php | 20 + .../BooleanNotTypeGetter.php | 15 + .../SimpleTypeGetters/CastTypeGetter.php | 35 + .../ClassConstFetchTypeGetter.php | 89 + .../ConstFetchTypeGetter.php | 29 + .../SimpleTypeGetters/ScalarTypeGetter.php | 30 + .../dedoc/scramble/src/Infer/TypeInferer.php | 140 + .../src/Infer/UnresolvableArgumentTypeBag.php | 35 + .../src/Infer/Visitors/PhpDocResolver.php | 72 + vendor/dedoc/scramble/src/OpenApiContext.php | 21 + .../dedoc/scramble/src/OpenApiTraverser.php | 65 + vendor/dedoc/scramble/src/OpenApiVisitor.php | 22 + .../OpenApiVisitor/SchemaEnforceVisitor.php | 70 + .../src/PhpDoc/AbstractPhpDocTypeVisitor.php | 13 + .../scramble/src/PhpDoc/PhpDocParser.php | 23 + .../scramble/src/PhpDoc/PhpDocTypeHelper.php | 284 + .../scramble/src/PhpDoc/PhpDocTypeVisitor.php | 12 + .../scramble/src/PhpDoc/PhpDocTypeWalker.php | 80 + .../PhpDoc/ResolveFqnPhpDocTypeVisitor.php | 25 + .../src/Reflection/JsonApiRelationship.php | 16 + .../Reflection/ReflectionJsonApiResource.php | 385 + .../src/Reflection/ReflectionModel.php | 26 + .../src/Reflection/ReflectionRoute.php | 153 + .../src/RuleTransformers/ConfirmedRule.php | 27 + .../src/RuleTransformers/EnumRule.php | 79 + .../src/RuleTransformers/ExistsRule.php | 108 + .../scramble/src/RuleTransformers/InRule.php | 35 + .../src/RuleTransformers/RegexRule.php | 73 + vendor/dedoc/scramble/src/SchemaValidator.php | 51 + vendor/dedoc/scramble/src/Scramble.php | 200 + .../scramble/src/ScrambleServiceProvider.php | 333 + .../MiddlewareAuthSecurityStrategy.php | 60 + .../scramble/src/Support/ContainerUtils.php | 47 + ...enticationExceptionToResponseExtension.php | 45 + ...horizationExceptionToResponseExtension.php | 45 + .../HttpExceptionToResponseExtension.php | 127 + .../NotFoundExceptionToResponseExtension.php | 49 + ...ValidationExceptionToResponseExtension.php | 51 + .../JsonApiQueryParameterFactory.php | 40 + .../Support/Generator/ClassBasedReference.php | 30 + .../src/Support/Generator/Combined/AllOf.php | 56 + .../src/Support/Generator/Combined/AnyOf.php | 56 + .../src/Support/Generator/Components.php | 183 + .../src/Support/Generator/Encoding.php | 110 + .../src/Support/Generator/Example.php | 25 + .../scramble/src/Support/Generator/Header.php | 179 + .../src/Support/Generator/InfoObject.php | 46 + .../scramble/src/Support/Generator/Link.php | 116 + .../src/Support/Generator/MediaType.php | 130 + .../src/Support/Generator/MissingExample.php | 8 + .../src/Support/Generator/MissingValue.php | 5 + .../src/Support/Generator/OpenApi.php | 131 + .../src/Support/Generator/Operation.php | 222 + .../src/Support/Generator/Parameter.php | 149 + .../scramble/src/Support/Generator/Path.php | 60 + .../src/Support/Generator/Reference.php | 91 + .../Support/Generator/RequestBodyObject.php | 61 + .../src/Support/Generator/Response.php | 159 + .../scramble/src/Support/Generator/Schema.php | 77 + .../Support/Generator/SecurityRequirement.php | 28 + .../src/Support/Generator/SecurityScheme.php | 78 + .../SecuritySchemes/ApiKeySecurityScheme.php | 28 + .../SecuritySchemes/HttpSecurityScheme.php | 28 + .../Generator/SecuritySchemes/OAuthFlow.php | 56 + .../Generator/SecuritySchemes/OAuthFlows.php | 55 + .../SecuritySchemes/Oauth2SecurityScheme.php | 41 + .../OpenIdConnectUrlSecurityScheme.php | 24 + .../scramble/src/Support/Generator/Server.php | 53 + .../src/Support/Generator/ServerVariable.php | 42 + .../scramble/src/Support/Generator/Tag.php | 27 + .../src/Support/Generator/TypeTransformer.php | 559 + .../src/Support/Generator/Types/ArrayType.php | 96 + .../Support/Generator/Types/BooleanType.php | 11 + .../Support/Generator/Types/IntegerType.php | 11 + .../src/Support/Generator/Types/MixedType.php | 17 + .../src/Support/Generator/Types/NullType.php | 11 + .../Support/Generator/Types/NumberType.php | 37 + .../Support/Generator/Types/ObjectType.php | 98 + .../Support/Generator/Types/StringType.php | 37 + .../src/Support/Generator/Types/Type.php | 223 + .../Support/Generator/Types/UnknownType.php | 5 + .../Support/Generator/UniqueNameOptions.php | 25 + .../UniqueNamesOptionsCollection.php | 61 + .../src/Support/Generator/WithAttributes.php | 50 + .../src/Support/Generator/WithExtensions.php | 56 + .../src/Support/Helpers/ExamplesExtractor.php | 63 + .../Support/Helpers/JsonResourceHelper.php | 70 + .../src/Support/IndexBuilders/Bag.php | 30 + .../Support/IndexBuilders/IndexBuilder.php | 14 + .../PaginatorsCandidatesBuilder.php | 44 + .../RequestParametersBuilder.php | 238 + .../Support/IndexBuilders/ScopeCollector.php | 42 + .../AbortHelpersExceptionInfer.php | 57 + ...ceCollectionDefinitionCreatedExtension.php | 50 + ...ntCollectionDefinitionCreatedExtension.php | 81 + ...nApiResourceDefinitionCreatedExtension.php | 148 + ...JsonResourceDefinitionCreatedExtension.php | 125 + ...ceCollectionDefinitionCreatedExtension.php | 66 + ...fterResponseDefinitionCreatedExtension.php | 84 + .../ArrayMergeReturnTypeExtension.php | 36 + .../BinaryFileResponseTypeFactory.php | 116 + .../EloquentBuilderExtension.php | 121 + .../FacadeStaticMethodReturnTypeExtension.php | 65 + ...rceCollectionMethodReturnTypeExtension.php | 38 + ...onApiResourceMethodReturnTypeExtension.php | 38 + .../InferExtensions/JsonResourceExtension.php | 263 + .../JsonResponseMethodReturnTypeExtension.php | 28 + .../ModelCollectionTypeResolver.php | 45 + .../InferExtensions/ModelExtension.php | 380 + .../PaginateMethodsReturnTypeExtension.php | 110 + .../PossibleExceptionInfer.php | 67 + .../InferExtensions/RequestExtension.php | 44 + .../ResourceCollectionTypeInfer.php | 66 + ...ourceResponseMethodReturnTypeExtension.php | 40 + .../ResponseFactoryTypeInfer.php | 134 + .../ShallowFunctionDefinition.php | 34 + ...ransformsToResourceCollectionExtension.php | 158 + .../TranslationReturnTypeExtension.php | 30 + .../InferExtensions/TypeTraceInfer.php | 27 + .../InferExtensions/ValidatorTypeInfer.php | 31 + .../scramble/src/Support/OperationBuilder.php | 48 + .../DeprecationExtension.php | 80 + .../ErrorResponsesExtension.php | 135 + .../AttributesParametersExtractor.php | 265 + .../FormRequestParametersExtractor.php | 141 + .../ParameterExtractor/InferredParameter.php | 44 + .../JsonApiResourceParametersExtractor.php | 187 + .../MethodCallsParametersExtractor.php | 52 + .../ParameterExtractor/ParameterExtractor.php | 15 + .../PathParametersExtractor.php | 192 + .../RulesDocumentationRetriever.php | 16 + .../ParameterExtractor/RulesNodes.php | 88 + .../TypeBasedRulesDocumentationRetriever.php | 66 + .../ValidateCallParametersExtractor.php | 128 + .../RequestBodyExtension.php | 274 + .../RequestEssentialsExtension.php | 272 + .../OperationExtensions/ResponseExtension.php | 240 + .../ResponseHeadersExtension.php | 78 + .../ComposedFormRequestRulesEvaluator.php | 49 + .../RulesEvaluator/ConstFetchEvaluator.php | 45 + .../FormRequestRulesEvaluator.php | 43 + .../RulesEvaluator/NodeRulesEvaluator.php | 267 + .../RulesEvaluator/PublicProxy.php | 52 + .../RulesEvaluator/RulesEvaluator.php | 15 + .../RulesExtractor/DeepParametersMerger.php | 243 + .../GeneratesParametersFromRules.php | 21 + .../ParametersExtractionResult.php | 21 + .../PhpDocSchemaTransformer.php | 68 + .../QueryParametersConverter.php | 123 + .../RulesExtractor/RulesMapper.php | 246 + .../RulesExtractor/RulesToParameters.php | 142 + .../SchemaBagToParametersTransformer.php | 71 + vendor/dedoc/scramble/src/Support/PhpDoc.php | 88 + .../Support/ResponseExtractor/ModelInfo.php | 309 + .../dedoc/scramble/src/Support/RouteInfo.php | 227 + .../Support/RouteResponseTypeRetriever.php | 201 + .../RuleTransforming/NormalizedRule.php | 76 + .../RuleSetToSchemaTransformer.php | 204 + .../RuleTransformerContext.php | 34 + .../Support/RuleTransforming/SchemaBag.php | 70 + .../src/Support/SchemaClassDocReflector.php | 90 + .../scramble/src/Support/ServerFactory.php | 40 + .../src/Support/Type/AbstractType.php | 119 + .../src/Support/Type/AbstractTypeVisitor.php | 16 + .../src/Support/Type/ArrayItemType_.php | 40 + .../scramble/src/Support/Type/ArrayType.php | 48 + .../scramble/src/Support/Type/BooleanType.php | 16 + .../src/Support/Type/CallableStringType.php | 18 + .../src/Support/Type/CoalesceType.php | 48 + .../src/Support/Type/Contracts/Generic.php | 13 + .../Type/Contracts/LateResolvingType.php | 15 + .../Support/Type/Contracts/LiteralString.php | 8 + .../Support/Type/Contracts/LiteralType.php | 8 + .../src/Support/Type/EnumCaseType.php | 23 + .../scramble/src/Support/Type/FloatType.php | 16 + .../src/Support/Type/FunctionLikeType.php | 10 + .../src/Support/Type/FunctionType.php | 65 + .../scramble/src/Support/Type/Generic.php | 102 + .../Support/Type/GenericClassStringType.php | 36 + .../src/Support/Type/IntegerRangeType.php | 33 + .../scramble/src/Support/Type/IntegerType.php | 16 + .../src/Support/Type/IntersectionType.php | 30 + .../src/Support/Type/KeyedArrayType.php | 153 + .../Type/Literal/LiteralBooleanType.php | 34 + .../Support/Type/Literal/LiteralFloatType.php | 32 + .../Type/Literal/LiteralIntegerType.php | 32 + .../Type/Literal/LiteralStringType.php | 32 + .../scramble/src/Support/Type/MissingType.php | 18 + .../scramble/src/Support/Type/MixedType.php | 21 + .../scramble/src/Support/Type/NeverType.php | 21 + .../scramble/src/Support/Type/NullType.php | 16 + .../scramble/src/Support/Type/ObjectType.php | 103 + .../src/Support/Type/OffsetAccessType.php | 39 + .../src/Support/Type/OffsetSetType.php | 165 + .../src/Support/Type/OffsetUnsetType.php | 151 + .../Support/Type/RecursiveTemplateSolver.php | 155 + .../Type/Reference/AbstractReferenceType.php | 19 + .../Reference/CallableCallReferenceType.php | 31 + .../Reference/ConstFetchReferenceType.php | 18 + .../Reference/MethodCallReferenceType.php | 30 + .../Type/Reference/NewCallReferenceType.php | 36 + .../PotentialMethodMutatingCallType.php | 30 + .../Reference/PropertyFetchReferenceType.php | 23 + .../StaticMethodCallReferenceType.php | 32 + .../Type/Reference/StaticReference.php | 41 + .../scramble/src/Support/Type/SelfType.php | 16 + .../scramble/src/Support/Type/StringType.php | 22 + .../Support/Type/TemplatePlaceholderType.php | 19 + .../src/Support/Type/TemplateType.php | 41 + .../src/Support/Type/TranslatedStringType.php | 45 + .../dedoc/scramble/src/Support/Type/Type.php | 60 + .../src/Support/Type/TypeAttributes.php | 52 + .../scramble/src/Support/Type/TypeHelper.php | 316 + .../scramble/src/Support/Type/TypePath.php | 42 + .../Support/Type/TypePathFindingVisitor.php | 128 + .../src/Support/Type/TypePathItem.php | 55 + .../Support/Type/TypePathItemCondition.php | 24 + .../src/Support/Type/TypeTraverser.php | 99 + .../scramble/src/Support/Type/TypeVisitor.php | 15 + .../scramble/src/Support/Type/TypeWalker.php | 175 + .../scramble/src/Support/Type/TypeWidener.php | 111 + .../dedoc/scramble/src/Support/Type/Union.php | 110 + .../scramble/src/Support/Type/UnknownType.php | 25 + .../scramble/src/Support/Type/VoidType.php | 16 + .../CursorPaginatorTypeManager.php | 44 + .../JsonApiResourceTypeManager.php | 75 + .../LengthAwarePaginatorTypeManager.php | 56 + .../TypeManagers/ManagesProperties.php | 38 + .../TypeManagers/PaginatorTypeManager.php | 40 + .../ResourceCollectionTypeManager.php | 126 + ...nonymousResourceCollectionTypeToSchema.php | 62 + .../ArrayableToSchema.php | 68 + .../BinaryFileResponseToSchema.php | 148 + .../CarbonInterfaceToSchema.php | 21 + .../CollectionToSchema.php | 31 + .../CursorPaginatorTypeToSchema.php | 65 + .../EloquentCollectionToSchema.php | 34 + .../TypeToSchemaExtensions/EnumToSchema.php | 177 + .../FlattensMergeValues.php | 151 + .../HandlesJsonApiResourceResponse.php | 90 + ...JsonApiAnonymousCollectionTypeToSchema.php | 34 + ...natedResourceResponseToSchemaExtension.php | 17 + ...onApiResourceResponseToSchemaExtension.php | 21 + .../JsonApiResourceTypeToSchema.php | 151 + .../JsonResourceTypeToSchema.php | 95 + .../LengthAwarePaginatorTypeToSchema.php | 65 + .../MergesOpenApiObjects.php | 30 + .../TypeToSchemaExtensions/ModelToSchema.php | 65 + .../PaginatedResourceResponseTypeToSchema.php | 214 + .../PaginatorTypeToSchema.php | 65 + .../PlainObjectToSchema.php | 154 + .../ResourceCollectionTypeToSchema.php | 58 + .../ResourceResponseTypeToSchema.php | 306 + .../ResponsableTypeToSchema.php | 34 + .../ResponseTypeToSchema.php | 160 + .../StreamedResponseToSchema.php | 174 + .../VoidTypeToSchema.php | 25 + .../WithCollectedPaginatedItems.php | 22 + vendor/dedoc/scramble/stubs/DeepCopy.stub | 11 + vendor/radebatz/type-info-extras/LICENSE | 19 - vendor/radebatz/type-info-extras/Readme.md | 41 - .../radebatz/type-info-extras/composer.json | 65 - .../src/Type/ClassLikeType.php | 40 - .../src/Type/ExplicitType.php | 85 - .../src/Type/IntRangeType.php | 51 - .../type-info-extras/src/Type/Type.php | 11 - .../type-info-extras/src/TypeFactoryTrait.php | 30 - .../src/TypeResolver/ResolverExtrasTrait.php | 62 - .../src/TypeResolver/StringTypeResolver.php | 385 - .../laravel-package-tools/LICENSE.md} | 10 +- vendor/spatie/laravel-package-tools/README.md | 678 + .../laravel-package-tools/composer.json | 52 + .../Commands/Concerns/AskToRunMigrations.php | 28 + .../Concerns/AskToStarRepoOnGitHub.php | 39 + .../Commands/Concerns/PublishesResources.php | 49 + .../Concerns/SupportsServiceProviderInApp.php | 75 + .../Concerns/SupportsStartWithEndWith.php | 43 + .../src/Commands/InstallCommand.php | 48 + .../src/Concerns/Package/HasAssets.php | 15 + .../Concerns/Package/HasBladeComponents.php | 24 + .../src/Concerns/Package/HasCommands.php | 43 + .../src/Concerns/Package/HasConfigs.php | 21 + .../src/Concerns/Package/HasInertia.php | 17 + .../Concerns/Package/HasInstallCommand.php | 19 + .../src/Concerns/Package/HasMigrations.php | 46 + .../src/Concerns/Package/HasRoutes.php | 25 + .../Concerns/Package/HasServiceProviders.php | 15 + .../src/Concerns/Package/HasTranslations.php | 15 + .../src/Concerns/Package/HasViewComposers.php | 21 + .../Concerns/Package/HasViewSharedData.php | 15 + .../src/Concerns/Package/HasViews.php | 24 + .../PackageServiceProvider/ProcessAssets.php | 20 + .../ProcessBladeComponents.php | 26 + .../ProcessCommands.php | 28 + .../PackageServiceProvider/ProcessConfigs.php | 65 + .../PackageServiceProvider/ProcessInertia.php | 29 + .../ProcessMigrations.php | 109 + .../PackageServiceProvider/ProcessRoutes.php | 19 + .../ProcessServiceProviders.php | 21 + .../ProcessTranslations.php | 32 + .../ProcessViewComposers.php | 21 + .../ProcessViewSharedData.php | 21 + .../PackageServiceProvider/ProcessViews.php | 26 + .../src/Exceptions/InvalidPackage.php | 13 + .../laravel-package-tools/src/Package.php | 67 + .../src/PackageServiceProvider.php | 126 + vendor/swagger-api/swagger-ui/.agignore | 1 - vendor/swagger-api/swagger-ui/.browserslistrc | 29 - .../swagger-ui/.claude/SKILL_USAGE_EXAMPLE.md | 565 - .../swagger-ui/.claude/skills/README.md | 151 - .../.claude/skills/add-oas-support.md | 2333 -- .../swagger-api/swagger-ui/.commitlintrc.json | 25 - vendor/swagger-api/swagger-ui/.dockerignore | 8 - vendor/swagger-api/swagger-ui/.editorconfig | 10 - vendor/swagger-api/swagger-ui/.eslintignore | 3 - vendor/swagger-api/swagger-ui/.eslintrc.js | 78 - vendor/swagger-api/swagger-ui/.gitattributes | 4 - .../.github/ISSUE_TEMPLATE/Bug_report.md | 73 - .../.github/ISSUE_TEMPLATE/Feature_request.md | 42 - .../.github/ISSUE_TEMPLATE/Support.md | 46 - .../swagger-ui/.github/dependabot.yaml | 35 - .../swagger-api/swagger-ui/.github/lock.yml | 15 - .../.github/pull_request_template.md | 55 - .../swagger-ui/.github/workflows/codeql.yml | 63 - .../.github/workflows/dependabot-merge.yml | 40 - .../workflows/docker-build-push-unstable.yml | 52 - .../.github/workflows/docker-build-push.yml | 77 - .../.github/workflows/docker-image-check.yml | 23 - .../swagger-ui/.github/workflows/nodejs.yml | 86 - .../workflows/release-swagger-ui-dist.yml | 61 - .../release-swagger-ui-packagist.yml | 21 - .../workflows/release-swagger-ui-react.yml | 61 - .../.github/workflows/release-swagger-ui.yml | 95 - vendor/swagger-api/swagger-ui/.gitignore | 29 - .../swagger-api/swagger-ui/.husky/commit-msg | 1 - .../swagger-api/swagger-ui/.husky/pre-commit | 1 - vendor/swagger-api/swagger-ui/.lintstagedrc | 4 - vendor/swagger-api/swagger-ui/.npmignore | 16 - vendor/swagger-api/swagger-ui/.npmrc | 1 - vendor/swagger-api/swagger-ui/.nvmrc | 1 - .../swagger-api/swagger-ui/.prettierrc.yaml | 5 - vendor/swagger-api/swagger-ui/.releaserc | 26 - vendor/swagger-api/swagger-ui/CLAUDE.md | 1072 - vendor/swagger-api/swagger-ui/Dockerfile | 52 - vendor/swagger-api/swagger-ui/LICENSE | 202 - vendor/swagger-api/swagger-ui/NOTICE | 2 - vendor/swagger-api/swagger-ui/README.md | 118 - vendor/swagger-api/swagger-ui/SECURITY.md | 23 - vendor/swagger-api/swagger-ui/babel.config.js | 140 - vendor/swagger-api/swagger-ui/composer.json | 40 - .../swagger-api/swagger-ui/config/.eslintrc | 8 - .../config/jest/jest.artifact.config.js | 9 - .../config/jest/jest.unit.config.js | 24 - .../swagger-api/swagger-ui/cypress.config.js | 19 - .../dev-helpers/dev-helper-initializer.js | 33 - .../swagger-ui/dev-helpers/index.html | 22 - .../dev-helpers/oauth2-redirect.html | 6 - .../swagger-ui/dev-helpers/oauth2-redirect.js | 69 - .../swagger-ui/dev-helpers/style.css | 19 - .../swagger-ui/dist/favicon-16x16.png | Bin 665 -> 0 bytes .../swagger-ui/dist/favicon-32x32.png | Bin 628 -> 0 bytes vendor/swagger-api/swagger-ui/dist/index.css | 16 - vendor/swagger-api/swagger-ui/dist/index.html | 19 - .../swagger-ui/dist/oauth2-redirect.html | 6 - .../swagger-ui/dist/oauth2-redirect.js | 1 - .../swagger-ui/dist/swagger-initializer.js | 20 - .../swagger-ui/dist/swagger-ui-bundle.js | 2 - .../dist/swagger-ui-es-bundle-core.js | 3 - .../swagger-ui/dist/swagger-ui-es-bundle.js | 2 - .../dist/swagger-ui-standalone-preset.js | 2 - .../swagger-ui/dist/swagger-ui.css | 3 - .../swagger-api/swagger-ui/dist/swagger-ui.js | 2 - .../swagger-ui/docker/configurator/helpers.js | 13 - .../swagger-ui/docker/configurator/index.js | 52 - .../swagger-ui/docker/configurator/oauth.js | 55 - .../docker/configurator/translator.js | 111 - .../docker/configurator/variables.js | 125 - .../swagger-api/swagger-ui/docker/cors.conf | 14 - .../swagger-ui/docker/default.conf.template | 43 - .../docker-entrypoint.d/40-swagger-ui.sh | 52 - .../swagger-ui/docker/embedding.conf | 5 - .../flavors/swagger-ui-react/README.md | 227 - .../flavors/swagger-ui-react/index.jsx | 180 - .../release/create-manifest.js | 9 - .../flavors/swagger-ui-react/release/run.sh | 42 - .../swagger-ui-react/release/template.json | 53 - .../swagger-api/swagger-ui/package-lock.json | 28592 ---------------- vendor/swagger-api/swagger-ui/package.json | 213 - .../swagger-ui/release/.release-it.json | 28 - .../release/check-for-breaking-changes.sh | 14 - .../swagger-ui/release/get-changelog.sh | 5 - vendor/swagger-api/swagger-ui/snapcraft.yaml | 28 - vendor/swagger-api/swagger-ui/src/.eslintrc | 10 - .../src/core/assets/rolling-load.svg | 1 - .../swagger-ui/src/core/components/app.jsx | 30 - .../src/core/components/auth/api-key-auth.jsx | 93 - .../src/core/components/auth/auth-item.jsx | 60 - .../components/auth/authorization-popup.jsx | 59 - .../core/components/auth/authorize-btn.jsx | 30 - .../auth/authorize-operation-btn.jsx | 35 - .../src/core/components/auth/auths.jsx | 124 - .../src/core/components/auth/basic-auth.jsx | 110 - .../src/core/components/auth/error.jsx | 24 - .../src/core/components/auth/oauth2.jsx | 283 - .../swagger-ui/src/core/components/clear.jsx | 25 - .../src/core/components/contact.jsx | 44 - .../src/core/components/content-type.jsx | 64 - .../core/components/copy-to-clipboard-btn.jsx | 29 - .../swagger-ui/src/core/components/curl.jsx | 38 - .../swagger-ui/src/core/components/debug.jsx | 50 - .../src/core/components/deep-link.jsx | 20 - .../swagger-ui/src/core/components/errors.jsx | 132 - .../src/core/components/example.jsx | 43 - .../examples-select-value-retainer.jsx | 254 - .../src/core/components/examples-select.jsx | 139 - .../src/core/components/execute.jsx | 103 - .../swagger-ui/src/core/components/footer.jsx | 9 - .../src/core/components/headers.jsx | 58 - .../swagger-ui/src/core/components/info.jsx | 155 - .../src/core/components/initialized-input.jsx | 36 - .../src/core/components/jump-to-path.jsx | 9 - .../src/core/components/layout-utils.jsx | 265 - .../src/core/components/layouts/base.jsx | 154 - .../src/core/components/layouts/xpane.jsx | 73 - .../src/core/components/license.jsx | 40 - .../src/core/components/live-response.jsx | 133 - .../components/online-validator-badge.jsx | 120 - .../src/core/components/openapi-version.jsx | 15 - .../components/operation-extension-row.jsx | 17 - .../core/components/operation-extensions.jsx | 35 - .../components/operation-summary-method.jsx | 25 - .../components/operation-summary-path.jsx | 54 - .../src/core/components/operation-summary.jsx | 108 - .../src/core/components/operation-tag.jsx | 121 - .../src/core/components/operation.jsx | 257 - .../src/core/components/operations.jsx | 99 - .../src/core/components/overview.jsx | 119 - .../src/core/components/param-body.jsx | 156 - .../core/components/parameter-extension.jsx | 12 - .../components/parameter-include-empty.jsx | 59 - .../src/core/components/parameter-row.jsx | 438 - .../src/core/components/parameters/index.js | 1 - .../core/components/parameters/parameters.jsx | 286 - .../src/core/components/property.jsx | 17 - .../src/core/components/providers/README.md | 6 - .../core/components/providers/markdown.jsx | 72 - .../src/core/components/response-body.jsx | 170 - .../core/components/response-extension.jsx | 12 - .../src/core/components/response.jsx | 276 - .../src/core/components/responses.jsx | 170 - .../src/core/components/svg-assets.jsx | 48 - .../src/core/components/try-it-out-button.jsx | 41 - .../core/components/version-pragma-filter.jsx | 54 - .../src/core/components/version-stamp.jsx | 12 - .../swagger-ui/src/core/config/defaults.js | 101 - .../config/factorization/inline-plugin.js | 10 - .../src/core/config/factorization/system.js | 44 - .../swagger-ui/src/core/config/index.js | 12 - .../swagger-ui/src/core/config/merge.js | 58 - .../src/core/config/sources/query.js | 28 - .../src/core/config/sources/runtime.js | 18 - .../swagger-ui/src/core/config/sources/url.js | 38 - .../src/core/config/type-cast/index.js | 24 - .../src/core/config/type-cast/mappings.js | 139 - .../config/type-cast/type-casters/array.js | 7 - .../config/type-cast/type-casters/boolean.js | 11 - .../config/type-cast/type-casters/dom-node.js | 7 - .../config/type-cast/type-casters/filter.js | 11 - .../config/type-cast/type-casters/function.js | 7 - .../type-cast/type-casters/nullable-array.js | 6 - .../type-casters/nullable-function.js | 7 - .../type-cast/type-casters/nullable-string.js | 7 - .../config/type-cast/type-casters/number.js | 9 - .../config/type-cast/type-casters/object.js | 9 - .../config/type-cast/type-casters/sorter.js | 7 - .../config/type-cast/type-casters/string.js | 6 - .../type-casters/syntax-highlight.js | 14 - .../type-casters/undefined-string.js | 7 - .../core/containers/OperationContainer.jsx | 271 - .../src/core/containers/authorize-btn.jsx | 31 - .../swagger-ui/src/core/containers/filter.jsx | 44 - .../swagger-ui/src/core/containers/info.jsx | 34 - .../swagger-api/swagger-ui/src/core/index.js | 174 - .../swagger-ui/src/core/oauth2-authorize.js | 137 - .../src/core/plugins/auth/actions.js | 286 - .../auth/components/lock-auth-icon.jsx | 27 - .../auth/components/unlock-auth-icon.jsx | 27 - .../auth/configs-extensions/wrap-actions.js | 19 - .../swagger-ui/src/core/plugins/auth/index.js | 94 - .../src/core/plugins/auth/reducers.js | 77 - .../src/core/plugins/auth/selectors.js | 121 - .../auth/spec-extensions/wrap-actions.js | 10 - .../src/core/plugins/auth/wrap-actions.js | 66 - .../src/core/plugins/configs/actions.js | 58 - .../swagger-ui/src/core/plugins/configs/fn.js | 12 - .../src/core/plugins/configs/index.js | 16 - .../src/core/plugins/configs/reducers.js | 20 - .../src/core/plugins/configs/selectors.js | 4 - .../src/core/plugins/deep-linking/README.md | 1 - .../src/core/plugins/deep-linking/helpers.js | 7 - .../src/core/plugins/deep-linking/index.js | 24 - .../src/core/plugins/deep-linking/layout.js | 209 - .../deep-linking/operation-tag-wrapper.jsx | 25 - .../deep-linking/operation-wrapper.jsx | 27 - .../src/core/plugins/download-url/index.js | 126 - .../src/core/plugins/err/actions.js | 60 - .../plugins/err/error-transformers/README.md | 31 - .../plugins/err/error-transformers/hook.js | 38 - .../transformers/not-of-type.js | 29 - .../transformers/parameter-oneof.js | 59 - .../swagger-ui/src/core/plugins/err/index.js | 15 - .../src/core/plugins/err/reducers.js | 100 - .../src/core/plugins/err/selectors.js | 15 - .../src/core/plugins/filter/index.js | 9 - .../src/core/plugins/filter/opsFilter.js | 3 - .../plugins/icons/components/arrow-down.jsx | 28 - .../plugins/icons/components/arrow-up.jsx | 28 - .../core/plugins/icons/components/arrow.jsx | 28 - .../core/plugins/icons/components/close.jsx | 28 - .../core/plugins/icons/components/copy.jsx | 34 - .../core/plugins/icons/components/lock.jsx | 28 - .../core/plugins/icons/components/unlock.jsx | 28 - .../src/core/plugins/icons/index.js | 24 - .../fn/api/encoderAPI.js | 20 - .../fn/api/formatAPI.js | 20 - .../fn/api/mediaTypeAPI.js | 27 - .../fn/api/optionAPI.js | 17 - .../fn/class/EncoderRegistry.js | 33 - .../fn/class/FormatRegistry.js | 65 - .../fn/class/MediaTypeRegistry.js | 27 - .../fn/class/OptionRegistry.js | 16 - .../fn/class/Registry.js | 24 - .../fn/core/constants.js | 6 - .../fn/core/example.js | 57 - .../fn/core/merge.js | 83 - .../fn/core/predicates.js | 16 - .../fn/core/random.js | 46 - .../fn/core/type.js | 156 - .../fn/core/utils.js | 23 - .../fn/encoders/7bit.js | 6 - .../fn/encoders/8bit.js | 6 - .../fn/encoders/base16.js | 6 - .../fn/encoders/base32.js | 34 - .../fn/encoders/base64.js | 6 - .../fn/encoders/base64url.js | 6 - .../fn/encoders/binary.js | 6 - .../fn/encoders/quoted-printable.js | 38 - .../fn/generators/date-time.js | 6 - .../fn/generators/date.js | 6 - .../fn/generators/double.js | 6 - .../fn/generators/duration.js | 6 - .../fn/generators/email.js | 6 - .../fn/generators/float.js | 6 - .../fn/generators/hostname.js | 6 - .../fn/generators/idn-email.js | 6 - .../fn/generators/idn-hostname.js | 6 - .../fn/generators/int32.js | 6 - .../fn/generators/int64.js | 6 - .../fn/generators/ipv4.js | 6 - .../fn/generators/ipv6.js | 6 - .../fn/generators/iri-reference.js | 6 - .../fn/generators/iri.js | 6 - .../fn/generators/json-pointer.js | 6 - .../fn/generators/media-types/application.js | 17 - .../fn/generators/media-types/audio.js | 10 - .../fn/generators/media-types/image.js | 10 - .../fn/generators/media-types/text.js | 17 - .../fn/generators/media-types/video.js | 10 - .../fn/generators/password.js | 6 - .../fn/generators/regex.js | 6 - .../fn/generators/relative-json-pointer.js | 6 - .../fn/generators/time.js | 6 - .../fn/generators/uri-reference.js | 6 - .../fn/generators/uri-template.js | 7 - .../fn/generators/uri.js | 6 - .../fn/generators/uuid.js | 6 - .../fn/get-json-sample-schema.js | 36 - .../fn/get-sample-schema.js | 39 - .../fn/get-xml-sample-schema.js | 35 - .../fn/get-yaml-sample-schema.js | 34 - .../json-schema-2020-12-samples/fn/index.js | 16 - .../json-schema-2020-12-samples/fn/main.js | 562 - .../fn/types/array.js | 52 - .../fn/types/boolean.js | 9 - .../fn/types/index.js | 30 - .../fn/types/integer.js | 43 - .../fn/types/null.js | 9 - .../fn/types/number.js | 65 - .../fn/types/object.js | 9 - .../fn/types/string.js | 80 - .../json-schema-2020-12-samples/index.js | 51 - .../components/Accordion/Accordion.jsx | 45 - .../components/Accordion/_accordion.scss | 33 - .../ExpandDeepButton/ExpandDeepButton.jsx | 31 - .../ExpandDeepButton/_expand-deep-button.scss | 10 - .../components/JSONSchema/JSONSchema.jsx | 227 - .../components/JSONSchema/_json-schema.scss | 34 - .../components/JSONViewer/JSONViewer.jsx | 153 - .../components/JSONViewer/_json-viewer.scss | 11 - .../json-schema-2020-12/components/_all.scss | 4 - .../components/_mixins.scss | 82 - .../components/icons/ChevronRight.jsx | 17 - .../components/keywords/$anchor.jsx | 27 - .../components/keywords/$comment.jsx | 27 - .../components/keywords/$defs.jsx | 93 - .../components/keywords/$dynamicAnchor.jsx | 27 - .../components/keywords/$dynamicRef.jsx | 27 - .../components/keywords/$id.jsx | 27 - .../components/keywords/$ref.jsx | 27 - .../components/keywords/$schema.jsx | 27 - .../keywords/$vocabulary/$vocabulary.jsx | 66 - .../keywords/$vocabulary/_$vocabulary.scss | 17 - .../keywords/AdditionalProperties.jsx | 55 - .../components/keywords/AllOf.jsx | 105 - .../components/keywords/AnyOf.jsx | 105 - .../components/keywords/Const/Const.jsx | 28 - .../components/keywords/Const/_const.scss | 11 - .../keywords/Constraint/Constraint.jsx | 38 - .../keywords/Constraint/_constraint.scss | 16 - .../components/keywords/Contains.jsx | 35 - .../components/keywords/ContentSchema.jsx | 39 - .../components/keywords/Default/Default.jsx | 28 - .../components/keywords/Default/_default.scss | 11 - .../DependentRequired/DependentRequired.jsx | 37 - .../_dependent-required.scss | 12 - .../components/keywords/DependentSchemas.jsx | 97 - .../components/keywords/Deprecated.jsx | 22 - .../keywords/Description/Description.jsx | 24 - .../keywords/Description/_description.scss | 9 - .../components/keywords/Else.jsx | 35 - .../components/keywords/Enum/Enum.jsx | 27 - .../components/keywords/Enum/_enum.scss | 11 - .../components/keywords/Examples/Examples.jsx | 30 - .../keywords/Examples/_examples.scss | 11 - .../ExtensionKeywords/ExtensionKeywords.jsx | 103 - .../_extension-keywords.scss | 11 - .../components/keywords/If.jsx | 35 - .../components/keywords/Items.jsx | 35 - .../components/keywords/Not.jsx | 35 - .../components/keywords/OneOf.jsx | 105 - .../PatternProperties/PatternProperties.jsx | 41 - .../_pattern-properties.scss | 21 - .../components/keywords/PrefixItems.jsx | 105 - .../keywords/Properties/Properties.jsx | 61 - .../keywords/Properties/_properties.scss | 24 - .../components/keywords/PropertyNames.jsx | 38 - .../components/keywords/ReadOnly.jsx | 22 - .../components/keywords/Then.jsx | 35 - .../components/keywords/Title/Title.jsx | 24 - .../components/keywords/Title/_title.scss | 26 - .../components/keywords/Type.jsx | 27 - .../components/keywords/UnevaluatedItems.jsx | 39 - .../keywords/UnevaluatedProperties.jsx | 39 - .../components/keywords/WriteOnly.jsx | 22 - .../components/keywords/_all.scss | 43 - .../plugins/json-schema-2020-12/context.js | 14 - .../core/plugins/json-schema-2020-12/enum.js | 10 - .../core/plugins/json-schema-2020-12/fn.js | 527 - .../core/plugins/json-schema-2020-12/hoc.jsx | 296 - .../core/plugins/json-schema-2020-12/hooks.js | 144 - .../core/plugins/json-schema-2020-12/index.js | 153 - .../plugins/json-schema-2020-12/prop-types.js | 10 - .../fn/get-json-sample-schema.js | 32 - .../fn/get-sample-schema.js | 31 - .../fn/get-xml-sample-schema.js | 31 - .../fn/get-yaml-sample-schema.js | 34 - .../plugins/json-schema-5-samples/fn/index.js | 653 - .../plugins/json-schema-5-samples/index.js | 56 - .../json-schema-5/components/array-model.jsx | 80 - .../json-schema-5/components/enum-model.jsx | 19 - .../components/json-schema-components.jsx | 441 - .../components/model-collapse.jsx | 99 - .../components/model-example.jsx | 160 - .../components/model-extensions.jsx | 28 - .../components/model-wrapper.jsx | 44 - .../json-schema-5/components/model.jsx | 136 - .../json-schema-5/components/models.jsx | 139 - .../json-schema-5/components/object-model.jsx | 340 - .../components/primitive-model.jsx | 137 - .../json-schema-5/components/schemes.jsx | 53 - .../json-schema-5/containers/schemes.jsx | 30 - .../src/core/plugins/json-schema-5/fn.js | 51 - .../src/core/plugins/json-schema-5/index.js | 41 - .../src/core/plugins/layout/actions.js | 39 - .../src/core/plugins/layout/index.js | 19 - .../src/core/plugins/layout/reducers.js | 32 - .../src/core/plugins/layout/selectors.js | 24 - .../layout/spec-extensions/wrap-selector.js | 22 - .../swagger-ui/src/core/plugins/logs/index.js | 28 - .../src/core/plugins/oas3/actions.js | 99 - .../oas3/auth-extensions/wrap-selectors.js | 92 - .../oas3/components/auth/http-auth.jsx | 157 - .../plugins/oas3/components/callbacks.jsx | 51 - .../src/core/plugins/oas3/components/index.js | 19 - .../oas3/components/operation-link.jsx | 43 - .../oas3/components/operation-servers.jsx | 102 - .../oas3/components/request-body-editor.jsx | 103 - .../plugins/oas3/components/request-body.jsx | 338 - .../oas3/components/servers-container.jsx | 33 - .../core/plugins/oas3/components/servers.jsx | 153 - .../swagger-ui/src/core/plugins/oas3/fn.js | 35 - .../src/core/plugins/oas3/helpers.jsx | 48 - .../swagger-ui/src/core/plugins/oas3/index.js | 39 - .../src/core/plugins/oas3/reducers.js | 111 - .../src/core/plugins/oas3/selectors.js | 308 - .../plugins/oas3/spec-extensions/selectors.js | 100 - .../oas3/spec-extensions/wrap-selectors.js | 71 - .../oas3/wrap-components/auth/auth-item.jsx | 25 - .../plugins/oas3/wrap-components/index.js | 15 - .../wrap-components/json-schema-string.jsx | 27 - .../plugins/oas3/wrap-components/markdown.jsx | 45 - .../plugins/oas3/wrap-components/model.jsx | 40 - .../wrap-components/online-validator-badge.js | 5 - .../oas3/wrap-components/openapi-version.jsx | 7 - .../src/core/plugins/oas31/after-load.js | 72 - .../oas31/auth-extensions/wrap-selectors.js | 28 - .../core/plugins/oas31/components/_all.scss | 2 - .../plugins/oas31/components/auth/auths.jsx | 186 - .../oas31/components/auth/mutual-tls-auth.jsx | 32 - .../core/plugins/oas31/components/contact.jsx | 43 - .../core/plugins/oas31/components/info.jsx | 99 - .../oas31/components/json-schema-dialect.jsx | 60 - .../core/plugins/oas31/components/license.jsx | 38 - .../oas31/components/model/_model.scss | 23 - .../plugins/oas31/components/model/model.jsx | 54 - .../oas31/components/models/_models.scss | 11 - .../oas31/components/models/models.jsx | 141 - .../components/version-pragma-filter.jsx | 73 - .../plugins/oas31/components/webhooks.jsx | 49 - .../swagger-ui/src/core/plugins/oas31/fn.js | 124 - .../src/core/plugins/oas31/index.js | 166 - .../components/keywords/Description.jsx | 27 - .../keywords/Discriminator/Discriminator.jsx | 118 - .../Discriminator/DiscriminatorMapping.jsx | 32 - .../components/keywords/Example.jsx | 28 - .../components/keywords/ExternalDocs.jsx | 143 - .../components/keywords/OpenAPIExtensions.jsx | 33 - .../components/keywords/Properties.jsx | 60 - .../components/keywords/Xml.jsx | 162 - .../json-schema-2020-12-extensions/fn.js | 57 - .../wrap-components/keywords/Description.jsx | 9 - .../wrap-components/keywords/Examples.jsx | 31 - .../wrap-components/keywords/Properties.jsx | 9 - .../core/plugins/oas31/oas3-extensions/fn.js | 46 - .../src/core/plugins/oas31/selectors.js | 26 - .../oas31/spec-extensions/selectors.js | 198 - .../oas31/spec-extensions/wrap-selectors.js | 18 - .../oas31/wrap-components/auth/auth-item.jsx | 22 - .../plugins/oas31/wrap-components/auths.jsx | 17 - .../plugins/oas31/wrap-components/contact.jsx | 15 - .../plugins/oas31/wrap-components/info.jsx | 15 - .../plugins/oas31/wrap-components/license.jsx | 15 - .../plugins/oas31/wrap-components/model.jsx | 57 - .../plugins/oas31/wrap-components/models.jsx | 54 - .../wrap-components/version-pragma-filter.jsx | 16 - .../src/core/plugins/oas32/after-load.js | 58 - .../components/version-pragma-filter.jsx | 80 - .../swagger-ui/src/core/plugins/oas32/fn.js | 124 - .../src/core/plugins/oas32/index.js | 130 - .../components/keywords/Description.jsx | 27 - .../components/keywords/Properties.jsx | 60 - .../json-schema-2020-12-extensions/fn.js | 20 - .../wrap-components/keywords/Description.jsx | 9 - .../wrap-components/keywords/Examples.jsx | 31 - .../wrap-components/keywords/Properties.jsx | 9 - .../core/plugins/oas32/oas3-extensions/fn.js | 46 - .../src/core/plugins/oas32/selectors.js | 24 - .../oas32/spec-extensions/selectors.js | 86 - .../oas32/spec-extensions/wrap-selectors.js | 27 - .../plugins/oas32/wrap-components/contact.jsx | 13 - .../plugins/oas32/wrap-components/info.jsx | 15 - .../plugins/oas32/wrap-components/license.jsx | 13 - .../plugins/oas32/wrap-components/model.jsx | 57 - .../plugins/oas32/wrap-components/models.jsx | 54 - .../oas32/wrap-components/openapi-version.jsx | 11 - .../wrap-components/version-pragma-filter.jsx | 16 - .../src/core/plugins/on-complete/index.js | 28 - .../src/core/plugins/request-snippets/fn.js | 169 - .../core/plugins/request-snippets/index.js | 21 - .../request-snippets/request-snippets.jsx | 163 - .../plugins/request-snippets/selectors.js | 45 - .../safe-render/components/error-boundary.jsx | 52 - .../safe-render/components/fallback.jsx | 13 - .../src/core/plugins/safe-render/fn.jsx | 32 - .../src/core/plugins/safe-render/index.js | 42 - .../src/core/plugins/spec/actions.js | 531 - .../swagger-ui/src/core/plugins/spec/index.js | 20 - .../src/core/plugins/spec/reducers.js | 176 - .../src/core/plugins/spec/selectors.js | 576 - .../src/core/plugins/spec/wrap-actions.js | 39 - .../swagger-client/configs-wrap-actions.js | 6 - .../src/core/plugins/swagger-client/index.js | 54 - .../plugins/syntax-highlighting/after-load.js | 25 - .../components/HighlightCode.jsx | 107 - .../components/PlainTextViewer.jsx | 17 - .../components/SyntaxHighlighter.jsx | 42 - .../core/plugins/syntax-highlighting/index.js | 34 - .../syntax-highlighting/root-injects.js | 22 - .../wrap-components/SyntaxHighlighter.jsx | 31 - .../swagger-ui/src/core/plugins/util/index.js | 11 - .../src/core/plugins/versions/after-load.js | 19 - .../src/core/plugins/versions/index.js | 10 - .../src/core/plugins/view-legacy/index.js | 25 - .../core/plugins/view-legacy/root-injects.jsx | 12 - .../swagger-ui/src/core/plugins/view/fn.js | 1 - .../swagger-ui/src/core/plugins/view/index.js | 34 - .../src/core/plugins/view/root-injects.jsx | 118 - .../swagger-ui/src/core/presets/apis/index.js | 20 - .../swagger-ui/src/core/presets/base/index.js | 55 - .../base/plugins/core-components/index.js | 130 - .../base/plugins/form-components/index.js | 10 - .../swagger-api/swagger-ui/src/core/system.js | 511 - .../src/core/utils/create-html-ready-id.js | 10 - .../src/core/utils/get-parameter-schema.js | 94 - .../swagger-ui/src/core/utils/index.js | 845 - .../swagger-ui/src/core/utils/jsonParse.js | 15 - .../swagger-ui/src/core/utils/memoizeN.js | 48 - .../swagger-ui/src/core/utils/url.js | 79 - .../swagger-api/swagger-ui/src/core/window.js | 30 - vendor/swagger-api/swagger-ui/src/index.js | 3 - .../components/StandaloneLayout.jsx | 40 - .../plugins/stadalone-layout/index.js | 12 - .../plugins/top-bar/assets/lightbulb-off.svg | 1 - .../plugins/top-bar/assets/lightbulb.svg | 1 - .../plugins/top-bar/assets/logo_small.svg | 60 - .../top-bar/components/DarkModeToggle.jsx | 47 - .../plugins/top-bar/components/Logo.jsx | 9 - .../plugins/top-bar/components/TopBar.jsx | 183 - .../src/standalone/plugins/top-bar/index.js | 12 - .../standalone/presets/standalone/index.js | 19 - .../swagger-ui/src/style/_authorize.scss | 96 - .../swagger-ui/src/style/_buttons.scss | 199 - .../swagger-ui/src/style/_dark-mode.scss | 743 - .../swagger-ui/src/style/_errors.scss | 72 - .../swagger-ui/src/style/_form.scss | 214 - .../swagger-ui/src/style/_information.scss | 99 - .../swagger-ui/src/style/_layout.scss | 945 - .../swagger-ui/src/style/_markdown.scss | 34 - .../swagger-ui/src/style/_mixins.scss | 159 - .../swagger-ui/src/style/_modal.scss | 95 - .../swagger-ui/src/style/_models.scss | 330 - .../swagger-ui/src/style/_servers.scss | 66 - .../src/style/_split-pane-mode.scss | 3 - .../swagger-ui/src/style/_table.scss | 191 - .../swagger-ui/src/style/_topbar.scss | 126 - .../swagger-ui/src/style/_type.scss | 21 - .../swagger-ui/src/style/_variables.scss | 231 - .../swagger-ui/src/style/main.scss | 31 - .../swagger-ui/stylelint.config.js | 11 - .../swagger-ui-dist-package/.npmignore | 2 - .../swagger-ui/swagger-ui-dist-package/.npmrc | 1 - .../swagger-ui-dist-package/README.md | 40 - .../swagger-ui-dist-package/absolute-path.js | 14 - .../swagger-ui-dist-package/deploy.sh | 25 - .../swagger-ui-dist-package/index.js | 17 - .../swagger-ui-dist-package/package.json | 20 - .../swagger-ui/webpack/_config-builder.js | 170 - .../swagger-ui/webpack/_helpers.js | 27 - .../swagger-api/swagger-ui/webpack/bundle.js | 68 - vendor/swagger-api/swagger-ui/webpack/core.js | 29 - .../swagger-api/swagger-ui/webpack/dev-e2e.js | 79 - vendor/swagger-api/swagger-ui/webpack/dev.js | 122 - .../swagger-ui/webpack/es-bundle-core.js | 90 - .../swagger-ui/webpack/es-bundle.js | 54 - .../swagger-ui/webpack/standalone.js | 30 - .../swagger-ui/webpack/stylesheets.js | 82 - vendor/symfony/type-info/CHANGELOG.md | 40 - .../Exception/ExceptionInterface.php | 20 - .../Exception/InvalidArgumentException.php | 20 - .../type-info/Exception/LogicException.php | 20 - .../type-info/Exception/RuntimeException.php | 20 - .../Exception/UnsupportedException.php | 28 - vendor/symfony/type-info/README.md | 54 - vendor/symfony/type-info/Type.php | 108 - .../symfony/type-info/Type/ArrayShapeType.php | 144 - .../symfony/type-info/Type/BackedEnumType.php | 50 - vendor/symfony/type-info/Type/BuiltinType.php | 81 - .../symfony/type-info/Type/CollectionType.php | 217 - .../type-info/Type/CompositeTypeInterface.php | 34 - vendor/symfony/type-info/Type/EnumType.php | 24 - vendor/symfony/type-info/Type/GenericType.php | 80 - .../type-info/Type/IntersectionType.php | 90 - .../symfony/type-info/Type/NullableType.php | 67 - vendor/symfony/type-info/Type/ObjectType.php | 78 - .../symfony/type-info/Type/TemplateType.php | 64 - vendor/symfony/type-info/Type/UnionType.php | 110 - .../type-info/Type/WrappingTypeInterface.php | 34 - .../type-info/TypeContext/TypeContext.php | 118 - .../TypeContext/TypeContextFactory.php | 374 - vendor/symfony/type-info/TypeFactoryTrait.php | 451 - vendor/symfony/type-info/TypeIdentifier.php | 60 - .../PhpDocAwareReflectionTypeResolver.php | 104 - .../ReflectionParameterTypeResolver.php | 51 - .../ReflectionPropertyTypeResolver.php | 49 - .../ReflectionReturnTypeResolver.php | 51 - .../TypeResolver/ReflectionTypeResolver.php | 87 - .../TypeResolver/StringTypeResolver.php | 368 - .../type-info/TypeResolver/TypeResolver.php | 106 - .../TypeResolver/TypeResolverInterface.php | 33 - vendor/symfony/type-info/composer.json | 45 - vendor/zircote/swagger-php/CONTEXT.md | 93 - vendor/zircote/swagger-php/CONTRIBUTING.md | 83 - vendor/zircote/swagger-php/LICENSE | 202 - vendor/zircote/swagger-php/NOTICE | 2 - vendor/zircote/swagger-php/README.md | 152 - vendor/zircote/swagger-php/bin/openapi | 46 - vendor/zircote/swagger-php/composer.json | 136 - vendor/zircote/swagger-php/package.json | 13 - vendor/zircote/swagger-php/rector.php | 72 - .../src/Analysers/AnalyserInterface.php | 16 - .../Analysers/AnnotationFactoryInterface.php | 24 - .../Analysers/AttributeAnnotationFactory.php | 160 - .../Analysers/ComposerAutoloaderScanner.php | 53 - .../Analysers/DocBlockAnnotationFactory.php | 77 - .../src/Analysers/DocBlockParser.php | 89 - .../src/Analysers/ReflectionAnalyser.php | 186 - .../src/Analysers/TokenScanner.php | 161 - vendor/zircote/swagger-php/src/Analysis.php | 449 - .../src/Annotations/AbstractAnnotation.php | 712 - .../src/Annotations/AdditionalProperties.php | 39 - .../src/Annotations/Attachable.php | 75 - .../src/Annotations/Components.php | 177 - .../swagger-php/src/Annotations/Contact.php | 63 - .../src/Annotations/CookieParameter.php | 20 - .../swagger-php/src/Annotations/Delete.php | 25 - .../src/Annotations/Discriminator.php | 69 - .../swagger-php/src/Annotations/Encoding.php | 89 - .../swagger-php/src/Annotations/Examples.php | 102 - .../src/Annotations/ExternalDocumentation.php | 76 - .../swagger-php/src/Annotations/Flow.php | 102 - .../swagger-php/src/Annotations/Get.php | 25 - .../swagger-php/src/Annotations/Head.php | 25 - .../swagger-php/src/Annotations/Header.php | 102 - .../src/Annotations/HeaderParameter.php | 22 - .../swagger-php/src/Annotations/Info.php | 98 - .../swagger-php/src/Annotations/Items.php | 41 - .../src/Annotations/JsonContent.php | 60 - .../src/Annotations/JsonSchemaTrait.php | 223 - .../swagger-php/src/Annotations/License.php | 98 - .../swagger-php/src/Annotations/Link.php | 114 - .../swagger-php/src/Annotations/MediaType.php | 103 - .../swagger-php/src/Annotations/OpenApi.php | 307 - .../swagger-php/src/Annotations/Operation.php | 240 - .../swagger-php/src/Annotations/Options.php | 25 - .../swagger-php/src/Annotations/Parameter.php | 302 - .../swagger-php/src/Annotations/Patch.php | 25 - .../swagger-php/src/Annotations/PathItem.php | 195 - .../src/Annotations/PathParameter.php | 32 - .../swagger-php/src/Annotations/Post.php | 25 - .../swagger-php/src/Annotations/Property.php | 63 - .../swagger-php/src/Annotations/Put.php | 25 - .../swagger-php/src/Annotations/Query.php | 27 - .../src/Annotations/QueryParameter.php | 23 - .../src/Annotations/RequestBody.php | 107 - .../swagger-php/src/Annotations/Response.php | 129 - .../swagger-php/src/Annotations/Schema.php | 396 - .../src/Annotations/SecurityScheme.php | 138 - .../swagger-php/src/Annotations/Server.php | 87 - .../src/Annotations/ServerVariable.php | 87 - .../swagger-php/src/Annotations/Tag.php | 106 - .../swagger-php/src/Annotations/Trace.php | 25 - .../swagger-php/src/Annotations/Webhook.php | 45 - .../swagger-php/src/Annotations/Xml.php | 95 - .../src/Annotations/XmlContent.php | 47 - .../src/Attributes/AdditionalProperties.php | 138 - .../swagger-php/src/Attributes/Attachable.php | 18 - .../swagger-php/src/Attributes/Components.php | 49 - .../swagger-php/src/Attributes/Contact.php | 36 - .../src/Attributes/CookieParameter.php | 16 - .../swagger-php/src/Attributes/Delete.php | 15 - .../src/Attributes/Discriminator.php | 35 - .../swagger-php/src/Attributes/Encoding.php | 43 - .../swagger-php/src/Attributes/Examples.php | 43 - .../src/Attributes/ExternalDocumentation.php | 34 - .../swagger-php/src/Attributes/Flow.php | 41 - .../swagger-php/src/Attributes/Get.php | 15 - .../swagger-php/src/Attributes/Head.php | 15 - .../swagger-php/src/Attributes/Header.php | 44 - .../src/Attributes/HeaderParameter.php | 16 - .../swagger-php/src/Attributes/Info.php | 41 - .../swagger-php/src/Attributes/Items.php | 138 - .../src/Attributes/JsonContent.php | 162 - .../swagger-php/src/Attributes/License.php | 36 - .../swagger-php/src/Attributes/Link.php | 48 - .../swagger-php/src/Attributes/MediaType.php | 40 - .../swagger-php/src/Attributes/OpenApi.php | 46 - .../src/Attributes/OperationTrait.php | 55 - .../swagger-php/src/Attributes/Options.php | 15 - .../swagger-php/src/Attributes/Parameter.php | 15 - .../src/Attributes/ParameterTrait.php | 67 - .../swagger-php/src/Attributes/Patch.php | 15 - .../swagger-php/src/Attributes/PathItem.php | 53 - .../src/Attributes/PathParameter.php | 21 - .../swagger-php/src/Attributes/Post.php | 15 - .../swagger-php/src/Attributes/Property.php | 143 - .../swagger-php/src/Attributes/Put.php | 15 - .../swagger-php/src/Attributes/Query.php | 18 - .../src/Attributes/QueryParameter.php | 16 - .../src/Attributes/RequestBody.php | 42 - .../swagger-php/src/Attributes/Response.php | 44 - .../swagger-php/src/Attributes/Schema.php | 137 - .../src/Attributes/SecurityScheme.php | 53 - .../swagger-php/src/Attributes/Server.php | 37 - .../src/Attributes/ServerVariable.php | 41 - .../swagger-php/src/Attributes/Tag.php | 42 - .../swagger-php/src/Attributes/Trace.php | 15 - .../swagger-php/src/Attributes/Webhook.php | 55 - .../swagger-php/src/Attributes/Xml.php | 40 - .../swagger-php/src/Attributes/XmlContent.php | 141 - .../src/Console/GenerateCommand.php | 94 - .../src/Console/GenerateFormat.php | 29 - .../swagger-php/src/Console/GenerateInput.php | 66 - vendor/zircote/swagger-php/src/Context.php | 274 - vendor/zircote/swagger-php/src/Generator.php | 436 - .../src/GeneratorAwareInterface.php | 12 - .../swagger-php/src/GeneratorAwareTrait.php | 19 - .../swagger-php/src/Loggers/DefaultLogger.php | 25 - .../swagger-php/src/OpenApiException.php | 11 - vendor/zircote/swagger-php/src/Pipeline.php | 115 - .../src/Processors/AugmentDiscriminators.php | 32 - .../src/Processors/AugmentItems.php | 30 - .../src/Processors/AugmentMediaType.php | 54 - .../src/Processors/AugmentParameters.php | 163 - .../src/Processors/AugmentProperties.php | 78 - .../src/Processors/AugmentRefs.php | 104 - .../src/Processors/AugmentRequestBody.php | 67 - .../src/Processors/AugmentSchemas.php | 148 - .../src/Processors/AugmentTags.php | 114 - .../swagger-php/src/Processors/BuildPaths.php | 57 - .../src/Processors/CleanUnmerged.php | 35 - .../src/Processors/CleanUnusedComponents.php | 123 - .../Processors/Concerns/AnnotationTrait.php | 67 - .../src/Processors/Concerns/DocblockTrait.php | 275 - .../Concerns/MergePropertiesTrait.php | 65 - .../src/Processors/Concerns/RefTrait.php | 24 - .../src/Processors/DocBlockDescriptions.php | 105 - .../src/Processors/ExpandClasses.php | 48 - .../src/Processors/ExpandEnums.php | 141 - .../src/Processors/ExpandInterfaces.php | 54 - .../src/Processors/ExpandTraits.php | 79 - .../src/Processors/MergeIntoComponents.php | 37 - .../src/Processors/MergeIntoOpenApi.php | 111 - .../src/Processors/MergeJsonContent.php | 60 - .../src/Processors/MergeXmlContent.php | 57 - .../src/Processors/OperationId.php | 72 - .../swagger-php/src/Processors/PathFilter.php | 120 - vendor/zircote/swagger-php/src/Serializer.php | 215 - .../zircote/swagger-php/src/SourceFinder.php | 63 - .../src/Type/AbstractTypeResolver.php | 92 - .../src/Type/LegacyTypeResolver.php | 302 - .../src/Type/TypeInfoTypeResolver.php | 274 - .../swagger-php/src/TypeResolverInterface.php | 45 - 1283 files changed, 36405 insertions(+), 89470 deletions(-) delete mode 100644 app/Docs/OpenApiSpec.php create mode 100644 app/Http/Controllers/ApiController.php create mode 100644 app/Http/Controllers/Ticket/TicketController.php create mode 100644 app/Http/Controllers/Ticket/TicketTypeController.php create mode 100644 app/Http/Requests/Ticket/StoreTicketRequest.php create mode 100644 app/Http/Requests/Ticket/UpdateTicketRequest.php create mode 100644 app/Http/Requests/TicketType/StoreTicketTypeRequest.php create mode 100644 app/Http/Requests/TicketType/UpdateTicketTypeRequest.php create mode 100644 app/Http/Resources/Ticket/TicketResource.php create mode 100644 app/Http/Resources/TicketType/TicketTypeResource.php create mode 100644 app/Services/Ticket/TicketService.php create mode 100644 app/Services/Ticket/TicketTypeService.php create mode 100644 app/Traits/ApiResponse.php create mode 100644 bruno/Services Core API V1.0/Ticketing/Add Tickets.yml create mode 100644 bruno/Services Core API V1.0/Ticketing/Get List Tickets.yml create mode 100644 bruno/Services Core API V1.0/Ticketing/folder.yml delete mode 100644 config/l5-swagger.php create mode 100644 config/scramble.php rename {vendor/darkaonline/l5-swagger/resources/views => resources/views/vendor/scramble}/.gitkeep (100%) create mode 100644 resources/views/vendor/scramble/docs.blade.php create mode 100644 resources/views/vendor/scramble/scalar.blade.php delete mode 100644 vendor/bin/openapi delete mode 100644 vendor/bin/openapi.bat delete mode 100644 vendor/darkaonline/l5-swagger/.claude/skills/write-tests.md delete mode 100644 vendor/darkaonline/l5-swagger/CLAUDE.md delete mode 100644 vendor/darkaonline/l5-swagger/composer.json delete mode 100644 vendor/darkaonline/l5-swagger/config/l5-swagger.php delete mode 100644 vendor/darkaonline/l5-swagger/phpstan.neon delete mode 100644 vendor/darkaonline/l5-swagger/resources/views/index.blade.php delete mode 100644 vendor/darkaonline/l5-swagger/src/ConfigFactory.php delete mode 100644 vendor/darkaonline/l5-swagger/src/Console/GenerateDocsCommand.php delete mode 100644 vendor/darkaonline/l5-swagger/src/CustomGeneratorInterface.php delete mode 100644 vendor/darkaonline/l5-swagger/src/Exceptions/L5SwaggerException.php delete mode 100644 vendor/darkaonline/l5-swagger/src/Generator.php delete mode 100644 vendor/darkaonline/l5-swagger/src/GeneratorFactory.php delete mode 100644 vendor/darkaonline/l5-swagger/src/Http/Controllers/SwaggerAssetController.php delete mode 100644 vendor/darkaonline/l5-swagger/src/Http/Controllers/SwaggerController.php delete mode 100644 vendor/darkaonline/l5-swagger/src/Http/Middleware/Config.php delete mode 100644 vendor/darkaonline/l5-swagger/src/L5SwaggerFacade.php delete mode 100644 vendor/darkaonline/l5-swagger/src/L5SwaggerServiceProvider.php delete mode 100644 vendor/darkaonline/l5-swagger/src/SecurityDefinitions.php delete mode 100644 vendor/darkaonline/l5-swagger/src/helpers.php delete mode 100644 vendor/darkaonline/l5-swagger/src/routes.php create mode 100644 vendor/dedoc/scramble/CHANGELOG.md rename vendor/{symfony/type-info/LICENSE => dedoc/scramble/LICENSE.md} (82%) create mode 100644 vendor/dedoc/scramble/README.md create mode 100644 vendor/dedoc/scramble/composer.json create mode 100644 vendor/dedoc/scramble/config/scramble.php create mode 100644 vendor/dedoc/scramble/dictionaries/classMap.php create mode 100644 vendor/dedoc/scramble/dictionaries/scripts/generate.php create mode 100644 vendor/dedoc/scramble/phpstan.neon create mode 100644 vendor/dedoc/scramble/pint.json rename vendor/{swagger-api/swagger-ui/src/core/components/system-wrapper.jsx => dedoc/scramble/resources/views/.gitkeep} (100%) create mode 100644 vendor/dedoc/scramble/resources/views/docs.blade.php create mode 100644 vendor/dedoc/scramble/resources/views/scalar.blade.php create mode 100644 vendor/dedoc/scramble/routes/web.php create mode 100644 vendor/dedoc/scramble/src/AbstractOpenApiVisitor.php create mode 100644 vendor/dedoc/scramble/src/Attributes/BodyParameter.php create mode 100644 vendor/dedoc/scramble/src/Attributes/CookieParameter.php create mode 100644 vendor/dedoc/scramble/src/Attributes/Endpoint.php create mode 100644 vendor/dedoc/scramble/src/Attributes/Example.php create mode 100644 vendor/dedoc/scramble/src/Attributes/ExcludeAllRoutesFromDocs.php create mode 100644 vendor/dedoc/scramble/src/Attributes/ExcludeRouteFromDocs.php create mode 100644 vendor/dedoc/scramble/src/Attributes/Group.php create mode 100644 vendor/dedoc/scramble/src/Attributes/Header.php create mode 100644 vendor/dedoc/scramble/src/Attributes/HeaderParameter.php create mode 100644 vendor/dedoc/scramble/src/Attributes/Hidden.php create mode 100644 vendor/dedoc/scramble/src/Attributes/IgnoreParam.php create mode 100644 vendor/dedoc/scramble/src/Attributes/MissingValue.php create mode 100644 vendor/dedoc/scramble/src/Attributes/Parameter.php create mode 100644 vendor/dedoc/scramble/src/Attributes/PathParameter.php create mode 100644 vendor/dedoc/scramble/src/Attributes/QueryParameter.php create mode 100644 vendor/dedoc/scramble/src/Attributes/Response.php create mode 100644 vendor/dedoc/scramble/src/Attributes/SchemaName.php create mode 100644 vendor/dedoc/scramble/src/CacheableGenerator.php create mode 100644 vendor/dedoc/scramble/src/Configuration/ApiPath.php create mode 100644 vendor/dedoc/scramble/src/Configuration/DocumentTransformers.php create mode 100644 vendor/dedoc/scramble/src/Configuration/GeneratorConfigCollection.php create mode 100644 vendor/dedoc/scramble/src/Configuration/InferConfig.php create mode 100644 vendor/dedoc/scramble/src/Configuration/JsonApiConfig.php create mode 100644 vendor/dedoc/scramble/src/Configuration/OperationTransformers.php create mode 100644 vendor/dedoc/scramble/src/Configuration/ParametersExtractors.php create mode 100644 vendor/dedoc/scramble/src/Configuration/RendererConfig.php create mode 100644 vendor/dedoc/scramble/src/Configuration/RuleTransformers.php create mode 100644 vendor/dedoc/scramble/src/Configuration/SecurityDocumentationContext.php create mode 100644 vendor/dedoc/scramble/src/Configuration/ServerVariables.php create mode 100644 vendor/dedoc/scramble/src/Console/Commands/AnalyzeDocumentation.php create mode 100644 vendor/dedoc/scramble/src/Console/Commands/CacheDocumentation.php create mode 100644 vendor/dedoc/scramble/src/Console/Commands/ClearDocumentationCache.php create mode 100644 vendor/dedoc/scramble/src/Console/Commands/Components/Code.php create mode 100644 vendor/dedoc/scramble/src/Console/Commands/Components/Component.php create mode 100644 vendor/dedoc/scramble/src/Console/Commands/Components/TermsOfContentItem.php create mode 100644 vendor/dedoc/scramble/src/Console/Commands/Concerns/ManagesDocumentationCache.php create mode 100644 vendor/dedoc/scramble/src/Console/Commands/ExportDocumentation.php create mode 100644 vendor/dedoc/scramble/src/ContextReferences.php create mode 100644 vendor/dedoc/scramble/src/ContextReferencesCollection.php create mode 100644 vendor/dedoc/scramble/src/Contracts/AllRulesSchemasTransformer.php create mode 100644 vendor/dedoc/scramble/src/Contracts/DocumentTransformer.php create mode 100644 vendor/dedoc/scramble/src/Contracts/OperationTransformer.php create mode 100644 vendor/dedoc/scramble/src/Contracts/RuleTransformer.php create mode 100644 vendor/dedoc/scramble/src/Contracts/SecurityDocumentationStrategy.php create mode 100644 vendor/dedoc/scramble/src/DocumentTransformers/AddDocumentTags.php create mode 100644 vendor/dedoc/scramble/src/DocumentTransformers/CleanupUnusedResponseReferencesTransformer.php create mode 100644 vendor/dedoc/scramble/src/Enums/JsonApiArraySerialization.php create mode 100644 vendor/dedoc/scramble/src/Exceptions/ConsoleRenderable.php create mode 100644 vendor/dedoc/scramble/src/Exceptions/InvalidSchema.php create mode 100644 vendor/dedoc/scramble/src/Exceptions/OpenApiReferenceTargetNotFoundException.php create mode 100644 vendor/dedoc/scramble/src/Exceptions/RouteAware.php create mode 100644 vendor/dedoc/scramble/src/Exceptions/RouteAwareTrait.php create mode 100644 vendor/dedoc/scramble/src/Exceptions/RulesEvaluationException.php create mode 100644 vendor/dedoc/scramble/src/Extensions/ExceptionToResponseExtension.php create mode 100644 vendor/dedoc/scramble/src/Extensions/OperationExtension.php create mode 100644 vendor/dedoc/scramble/src/Extensions/TypeToSchemaExtension.php create mode 100644 vendor/dedoc/scramble/src/Generator.php create mode 100644 vendor/dedoc/scramble/src/GeneratorConfig.php create mode 100644 vendor/dedoc/scramble/src/Http/Middleware/RestrictedDocsAccess.php create mode 100644 vendor/dedoc/scramble/src/Infer.php create mode 100644 vendor/dedoc/scramble/src/Infer/Analyzer/ClassAnalyzer.php create mode 100644 vendor/dedoc/scramble/src/Infer/Analyzer/MethodAnalyzer.php create mode 100644 vendor/dedoc/scramble/src/Infer/Analyzer/PropertyAnalyzer.php create mode 100644 vendor/dedoc/scramble/src/Infer/AutoResolvingArgumentTypeBag.php create mode 100644 vendor/dedoc/scramble/src/Infer/Configuration/ClassLike.php create mode 100644 vendor/dedoc/scramble/src/Infer/Configuration/ClassLikeAndChildren.php create mode 100644 vendor/dedoc/scramble/src/Infer/Configuration/DefinitionMatcher.php create mode 100644 vendor/dedoc/scramble/src/Infer/Context.php create mode 100644 vendor/dedoc/scramble/src/Infer/Contracts/ArgumentTypeBag.php create mode 100644 vendor/dedoc/scramble/src/Infer/Contracts/ClassDefinition.php create mode 100644 vendor/dedoc/scramble/src/Infer/Contracts/ClassDefinitionBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/Contracts/FunctionLikeDefinitionBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/Contracts/Index.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/AttributeDefinition.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/ClassDefinition.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/ClassPropertyDefinition.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/FunctionLikeAstDefinition.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/FunctionLikeDefinition.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/LazyShallowClassDefinition.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/PendingDocComment.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/PropertyVisibility.php create mode 100644 vendor/dedoc/scramble/src/Infer/Definition/ShallowClassDefinition.php create mode 100644 vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeAstDefinitionBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationAstDefinitionBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationPhpDocDefinitionBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeReflectionDefinitionBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/DefinitionBuilders/LazyClassReflectionDefinitionBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/DefinitionBuilders/SelfOutTypeBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/DefinitionBuilders/ShallowClassReflectionDefinitionBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/AfterClassDefinitionCreatedExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/AfterSideEffectCallAnalyzed.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/AnyMethodReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/AnyMethodCallEvent.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/ClassDefinitionCreatedEvent.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/Concerns/ArgumentTypesAware.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/FunctionCallEvent.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/MethodCallEvent.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/PropertyFetchEvent.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/ReferenceResolutionEvent.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/SideEffectCallEvent.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/Event/StaticMethodCallEvent.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/ExpressionExceptionExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/ExpressionTypeInferExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/ExtensionsBroker.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/FunctionReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/IndexBuildingBroker.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/InferExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/MethodCallExceptionsExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/MethodReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/PropertyTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/ResolvingType.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/StaticMethodReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Extensions/TypeResolverExtension.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/AbstractNode.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/ConditionNode.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/Edge.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/ExpressionTypeInferrer.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/MergeNode.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/Node.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/Nodes.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/StartNode.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/StatementNode.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/TerminateNode.php create mode 100644 vendor/dedoc/scramble/src/Infer/Flow/TerminationKind.php create mode 100644 vendor/dedoc/scramble/src/Infer/FlowBuilder.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/ArrayHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/ArrayItemHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/AssignHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/ClassHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/CreatesScope.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/ExceptionInferringExtensions.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/ExpressionTypeInferringExtensions.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/FunctionLikeHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/IndexBuildingHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/PhpDocHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/PropertyHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/ReturnHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/ThrowHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Handler/UnsetHandler.php create mode 100644 vendor/dedoc/scramble/src/Infer/Reflector/ClassReflector.php create mode 100644 vendor/dedoc/scramble/src/Infer/Reflector/ClosureReflector.php create mode 100644 vendor/dedoc/scramble/src/Infer/Reflector/MethodReflector.php create mode 100644 vendor/dedoc/scramble/src/Infer/Reflector/PropertyReflector.php create mode 100644 vendor/dedoc/scramble/src/Infer/Scope/GlobalScope.php create mode 100644 vendor/dedoc/scramble/src/Infer/Scope/Index.php create mode 100644 vendor/dedoc/scramble/src/Infer/Scope/LazyShallowReflectionIndex.php create mode 100644 vendor/dedoc/scramble/src/Infer/Scope/NodeTypesResolver.php create mode 100644 vendor/dedoc/scramble/src/Infer/Scope/Scope.php create mode 100644 vendor/dedoc/scramble/src/Infer/Scope/ScopeContext.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/ConstFetchTypeGetter.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/FileNameResolver.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/FileParser.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/FileParserResult.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/KeyedArrayUnpackingTypeVisitor.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/LateTypeResolvingTypeVisitor.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/RecursionGuard.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/ReferenceTypeResolver.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/ShallowTypeResolver.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/TemplateTypesSolver.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/TemplatesMap.php create mode 100644 vendor/dedoc/scramble/src/Infer/Services/UnionNormalizingTypeVisitor.php create mode 100644 vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/BooleanNotTypeGetter.php create mode 100644 vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/CastTypeGetter.php create mode 100644 vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ClassConstFetchTypeGetter.php create mode 100644 vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ConstFetchTypeGetter.php create mode 100644 vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ScalarTypeGetter.php create mode 100644 vendor/dedoc/scramble/src/Infer/TypeInferer.php create mode 100644 vendor/dedoc/scramble/src/Infer/UnresolvableArgumentTypeBag.php create mode 100644 vendor/dedoc/scramble/src/Infer/Visitors/PhpDocResolver.php create mode 100644 vendor/dedoc/scramble/src/OpenApiContext.php create mode 100644 vendor/dedoc/scramble/src/OpenApiTraverser.php create mode 100644 vendor/dedoc/scramble/src/OpenApiVisitor.php create mode 100644 vendor/dedoc/scramble/src/OpenApiVisitor/SchemaEnforceVisitor.php create mode 100644 vendor/dedoc/scramble/src/PhpDoc/AbstractPhpDocTypeVisitor.php create mode 100644 vendor/dedoc/scramble/src/PhpDoc/PhpDocParser.php create mode 100644 vendor/dedoc/scramble/src/PhpDoc/PhpDocTypeHelper.php create mode 100644 vendor/dedoc/scramble/src/PhpDoc/PhpDocTypeVisitor.php create mode 100644 vendor/dedoc/scramble/src/PhpDoc/PhpDocTypeWalker.php create mode 100644 vendor/dedoc/scramble/src/PhpDoc/ResolveFqnPhpDocTypeVisitor.php create mode 100644 vendor/dedoc/scramble/src/Reflection/JsonApiRelationship.php create mode 100644 vendor/dedoc/scramble/src/Reflection/ReflectionJsonApiResource.php create mode 100644 vendor/dedoc/scramble/src/Reflection/ReflectionModel.php create mode 100644 vendor/dedoc/scramble/src/Reflection/ReflectionRoute.php create mode 100644 vendor/dedoc/scramble/src/RuleTransformers/ConfirmedRule.php create mode 100644 vendor/dedoc/scramble/src/RuleTransformers/EnumRule.php create mode 100644 vendor/dedoc/scramble/src/RuleTransformers/ExistsRule.php create mode 100644 vendor/dedoc/scramble/src/RuleTransformers/InRule.php create mode 100644 vendor/dedoc/scramble/src/RuleTransformers/RegexRule.php create mode 100644 vendor/dedoc/scramble/src/SchemaValidator.php create mode 100644 vendor/dedoc/scramble/src/Scramble.php create mode 100644 vendor/dedoc/scramble/src/ScrambleServiceProvider.php create mode 100644 vendor/dedoc/scramble/src/SecurityDocumentation/MiddlewareAuthSecurityStrategy.php create mode 100644 vendor/dedoc/scramble/src/Support/ContainerUtils.php create mode 100644 vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthenticationExceptionToResponseExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthorizationExceptionToResponseExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/HttpExceptionToResponseExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/NotFoundExceptionToResponseExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/ValidationExceptionToResponseExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/Factories/JsonApiQueryParameterFactory.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/ClassBasedReference.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Combined/AllOf.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Combined/AnyOf.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Components.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Encoding.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Example.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Header.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/InfoObject.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Link.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/MediaType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/MissingExample.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/MissingValue.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/OpenApi.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Operation.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Parameter.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Path.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Reference.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/RequestBodyObject.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Response.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Schema.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/SecurityRequirement.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/SecurityScheme.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/ApiKeySecurityScheme.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/HttpSecurityScheme.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlow.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlows.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/Oauth2SecurityScheme.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OpenIdConnectUrlSecurityScheme.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Server.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/ServerVariable.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Tag.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/TypeTransformer.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/ArrayType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/BooleanType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/IntegerType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/MixedType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/NullType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/NumberType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/ObjectType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/StringType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/Type.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/Types/UnknownType.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/UniqueNameOptions.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/UniqueNamesOptionsCollection.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/WithAttributes.php create mode 100644 vendor/dedoc/scramble/src/Support/Generator/WithExtensions.php create mode 100644 vendor/dedoc/scramble/src/Support/Helpers/ExamplesExtractor.php create mode 100644 vendor/dedoc/scramble/src/Support/Helpers/JsonResourceHelper.php create mode 100644 vendor/dedoc/scramble/src/Support/IndexBuilders/Bag.php create mode 100644 vendor/dedoc/scramble/src/Support/IndexBuilders/IndexBuilder.php create mode 100644 vendor/dedoc/scramble/src/Support/IndexBuilders/PaginatorsCandidatesBuilder.php create mode 100644 vendor/dedoc/scramble/src/Support/IndexBuilders/RequestParametersBuilder.php create mode 100644 vendor/dedoc/scramble/src/Support/IndexBuilders/ScopeCollector.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/AbortHelpersExceptionInfer.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/AfterAnonymousResourceCollectionDefinitionCreatedExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/AfterEloquentCollectionDefinitionCreatedExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/AfterJsonApiResourceDefinitionCreatedExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/AfterJsonResourceDefinitionCreatedExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/AfterResourceCollectionDefinitionCreatedExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/AfterResponseDefinitionCreatedExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/ArrayMergeReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/BinaryFileResponseTypeFactory.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/EloquentBuilderExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/FacadeStaticMethodReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceCollectionMethodReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceMethodReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/JsonResourceExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/JsonResponseMethodReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/ModelCollectionTypeResolver.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/ModelExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/PaginateMethodsReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/PossibleExceptionInfer.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/RequestExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/ResourceCollectionTypeInfer.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/ResourceResponseMethodReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/ResponseFactoryTypeInfer.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/ShallowFunctionDefinition.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/TransformsToResourceCollectionExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/TranslationReturnTypeExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/TypeTraceInfer.php create mode 100644 vendor/dedoc/scramble/src/Support/InferExtensions/ValidatorTypeInfer.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationBuilder.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/DeprecationExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ErrorResponsesExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/AttributesParametersExtractor.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/InferredParameter.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/JsonApiResourceParametersExtractor.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/MethodCallsParametersExtractor.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ParameterExtractor.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/PathParametersExtractor.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesDocumentationRetriever.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesNodes.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/TypeBasedRulesDocumentationRetriever.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RequestBodyExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RequestEssentialsExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ResponseExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/ResponseHeadersExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ConstFetchEvaluator.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/FormRequestRulesEvaluator.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/PublicProxy.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/RulesEvaluator.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/DeepParametersMerger.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/GeneratesParametersFromRules.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/ParametersExtractionResult.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/PhpDocSchemaTransformer.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/QueryParametersConverter.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesToParameters.php create mode 100644 vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/SchemaBagToParametersTransformer.php create mode 100644 vendor/dedoc/scramble/src/Support/PhpDoc.php create mode 100644 vendor/dedoc/scramble/src/Support/ResponseExtractor/ModelInfo.php create mode 100644 vendor/dedoc/scramble/src/Support/RouteInfo.php create mode 100644 vendor/dedoc/scramble/src/Support/RouteResponseTypeRetriever.php create mode 100644 vendor/dedoc/scramble/src/Support/RuleTransforming/NormalizedRule.php create mode 100644 vendor/dedoc/scramble/src/Support/RuleTransforming/RuleSetToSchemaTransformer.php create mode 100644 vendor/dedoc/scramble/src/Support/RuleTransforming/RuleTransformerContext.php create mode 100644 vendor/dedoc/scramble/src/Support/RuleTransforming/SchemaBag.php create mode 100644 vendor/dedoc/scramble/src/Support/SchemaClassDocReflector.php create mode 100644 vendor/dedoc/scramble/src/Support/ServerFactory.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/AbstractType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/AbstractTypeVisitor.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/ArrayItemType_.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/ArrayType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/BooleanType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/CallableStringType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/CoalesceType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Contracts/Generic.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Contracts/LateResolvingType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Contracts/LiteralString.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Contracts/LiteralType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/EnumCaseType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/FloatType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/FunctionLikeType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/FunctionType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Generic.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/GenericClassStringType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/IntegerRangeType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/IntegerType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/IntersectionType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/KeyedArrayType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Literal/LiteralBooleanType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Literal/LiteralFloatType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Literal/LiteralIntegerType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Literal/LiteralStringType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/MissingType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/MixedType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/NeverType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/NullType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/ObjectType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/OffsetAccessType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/OffsetSetType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/OffsetUnsetType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/RecursiveTemplateSolver.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/AbstractReferenceType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/CallableCallReferenceType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/ConstFetchReferenceType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/MethodCallReferenceType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/NewCallReferenceType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/PotentialMethodMutatingCallType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/PropertyFetchReferenceType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/StaticMethodCallReferenceType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Reference/StaticReference.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/SelfType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/StringType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TemplatePlaceholderType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TemplateType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TranslatedStringType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Type.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypeAttributes.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypeHelper.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypePath.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypePathFindingVisitor.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypePathItem.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypePathItemCondition.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypeTraverser.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypeVisitor.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypeWalker.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/TypeWidener.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/Union.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/UnknownType.php create mode 100644 vendor/dedoc/scramble/src/Support/Type/VoidType.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeManagers/CursorPaginatorTypeManager.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeManagers/JsonApiResourceTypeManager.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeManagers/LengthAwarePaginatorTypeManager.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeManagers/ManagesProperties.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeManagers/PaginatorTypeManager.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeManagers/ResourceCollectionTypeManager.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/AnonymousResourceCollectionTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ArrayableToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/BinaryFileResponseToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CarbonInterfaceToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CollectionToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CursorPaginatorTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/EloquentCollectionToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/EnumToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/FlattensMergeValues.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/HandlesJsonApiResourceResponse.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiAnonymousCollectionTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiPaginatedResourceResponseToSchemaExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceResponseToSchemaExtension.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonResourceTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/LengthAwarePaginatorTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/MergesOpenApiObjects.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ModelToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatedResourceResponseTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatorTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PlainObjectToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceCollectionTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceResponseTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponsableTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponseTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/StreamedResponseToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/VoidTypeToSchema.php create mode 100644 vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/WithCollectedPaginatedItems.php create mode 100644 vendor/dedoc/scramble/stubs/DeepCopy.stub delete mode 100644 vendor/radebatz/type-info-extras/LICENSE delete mode 100644 vendor/radebatz/type-info-extras/Readme.md delete mode 100644 vendor/radebatz/type-info-extras/composer.json delete mode 100644 vendor/radebatz/type-info-extras/src/Type/ClassLikeType.php delete mode 100644 vendor/radebatz/type-info-extras/src/Type/ExplicitType.php delete mode 100644 vendor/radebatz/type-info-extras/src/Type/IntRangeType.php delete mode 100644 vendor/radebatz/type-info-extras/src/Type/Type.php delete mode 100644 vendor/radebatz/type-info-extras/src/TypeFactoryTrait.php delete mode 100644 vendor/radebatz/type-info-extras/src/TypeResolver/ResolverExtrasTrait.php delete mode 100644 vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php rename vendor/{darkaonline/l5-swagger/LICENSE => spatie/laravel-package-tools/LICENSE.md} (88%) create mode 100644 vendor/spatie/laravel-package-tools/README.md create mode 100644 vendor/spatie/laravel-package-tools/composer.json create mode 100644 vendor/spatie/laravel-package-tools/src/Commands/Concerns/AskToRunMigrations.php create mode 100644 vendor/spatie/laravel-package-tools/src/Commands/Concerns/AskToStarRepoOnGitHub.php create mode 100644 vendor/spatie/laravel-package-tools/src/Commands/Concerns/PublishesResources.php create mode 100644 vendor/spatie/laravel-package-tools/src/Commands/Concerns/SupportsServiceProviderInApp.php create mode 100644 vendor/spatie/laravel-package-tools/src/Commands/Concerns/SupportsStartWithEndWith.php create mode 100644 vendor/spatie/laravel-package-tools/src/Commands/InstallCommand.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasAssets.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasBladeComponents.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasCommands.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasConfigs.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasInertia.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasInstallCommand.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasMigrations.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasRoutes.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasServiceProviders.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasTranslations.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViewComposers.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViewSharedData.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViews.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessAssets.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessBladeComponents.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessCommands.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessConfigs.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessInertia.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessMigrations.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessRoutes.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessServiceProviders.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessTranslations.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewComposers.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewSharedData.php create mode 100644 vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViews.php create mode 100644 vendor/spatie/laravel-package-tools/src/Exceptions/InvalidPackage.php create mode 100644 vendor/spatie/laravel-package-tools/src/Package.php create mode 100644 vendor/spatie/laravel-package-tools/src/PackageServiceProvider.php delete mode 100644 vendor/swagger-api/swagger-ui/.agignore delete mode 100644 vendor/swagger-api/swagger-ui/.browserslistrc delete mode 100644 vendor/swagger-api/swagger-ui/.claude/SKILL_USAGE_EXAMPLE.md delete mode 100644 vendor/swagger-api/swagger-ui/.claude/skills/README.md delete mode 100644 vendor/swagger-api/swagger-ui/.claude/skills/add-oas-support.md delete mode 100644 vendor/swagger-api/swagger-ui/.commitlintrc.json delete mode 100644 vendor/swagger-api/swagger-ui/.dockerignore delete mode 100644 vendor/swagger-api/swagger-ui/.editorconfig delete mode 100644 vendor/swagger-api/swagger-ui/.eslintignore delete mode 100644 vendor/swagger-api/swagger-ui/.eslintrc.js delete mode 100644 vendor/swagger-api/swagger-ui/.gitattributes delete mode 100644 vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Bug_report.md delete mode 100644 vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Feature_request.md delete mode 100644 vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Support.md delete mode 100644 vendor/swagger-api/swagger-ui/.github/dependabot.yaml delete mode 100644 vendor/swagger-api/swagger-ui/.github/lock.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/pull_request_template.md delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/codeql.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/dependabot-merge.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/docker-build-push-unstable.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/docker-build-push.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/docker-image-check.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/nodejs.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-dist.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-packagist.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-react.yml delete mode 100644 vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui.yml delete mode 100644 vendor/swagger-api/swagger-ui/.gitignore delete mode 100644 vendor/swagger-api/swagger-ui/.husky/commit-msg delete mode 100644 vendor/swagger-api/swagger-ui/.husky/pre-commit delete mode 100644 vendor/swagger-api/swagger-ui/.lintstagedrc delete mode 100644 vendor/swagger-api/swagger-ui/.npmignore delete mode 100644 vendor/swagger-api/swagger-ui/.npmrc delete mode 100644 vendor/swagger-api/swagger-ui/.nvmrc delete mode 100644 vendor/swagger-api/swagger-ui/.prettierrc.yaml delete mode 100644 vendor/swagger-api/swagger-ui/.releaserc delete mode 100644 vendor/swagger-api/swagger-ui/CLAUDE.md delete mode 100644 vendor/swagger-api/swagger-ui/Dockerfile delete mode 100644 vendor/swagger-api/swagger-ui/LICENSE delete mode 100644 vendor/swagger-api/swagger-ui/NOTICE delete mode 100644 vendor/swagger-api/swagger-ui/README.md delete mode 100644 vendor/swagger-api/swagger-ui/SECURITY.md delete mode 100644 vendor/swagger-api/swagger-ui/babel.config.js delete mode 100644 vendor/swagger-api/swagger-ui/composer.json delete mode 100644 vendor/swagger-api/swagger-ui/config/.eslintrc delete mode 100644 vendor/swagger-api/swagger-ui/config/jest/jest.artifact.config.js delete mode 100644 vendor/swagger-api/swagger-ui/config/jest/jest.unit.config.js delete mode 100644 vendor/swagger-api/swagger-ui/cypress.config.js delete mode 100644 vendor/swagger-api/swagger-ui/dev-helpers/dev-helper-initializer.js delete mode 100644 vendor/swagger-api/swagger-ui/dev-helpers/index.html delete mode 100644 vendor/swagger-api/swagger-ui/dev-helpers/oauth2-redirect.html delete mode 100644 vendor/swagger-api/swagger-ui/dev-helpers/oauth2-redirect.js delete mode 100644 vendor/swagger-api/swagger-ui/dev-helpers/style.css delete mode 100644 vendor/swagger-api/swagger-ui/dist/favicon-16x16.png delete mode 100644 vendor/swagger-api/swagger-ui/dist/favicon-32x32.png delete mode 100644 vendor/swagger-api/swagger-ui/dist/index.css delete mode 100644 vendor/swagger-api/swagger-ui/dist/index.html delete mode 100644 vendor/swagger-api/swagger-ui/dist/oauth2-redirect.html delete mode 100644 vendor/swagger-api/swagger-ui/dist/oauth2-redirect.js delete mode 100644 vendor/swagger-api/swagger-ui/dist/swagger-initializer.js delete mode 100644 vendor/swagger-api/swagger-ui/dist/swagger-ui-bundle.js delete mode 100644 vendor/swagger-api/swagger-ui/dist/swagger-ui-es-bundle-core.js delete mode 100644 vendor/swagger-api/swagger-ui/dist/swagger-ui-es-bundle.js delete mode 100644 vendor/swagger-api/swagger-ui/dist/swagger-ui-standalone-preset.js delete mode 100644 vendor/swagger-api/swagger-ui/dist/swagger-ui.css delete mode 100644 vendor/swagger-api/swagger-ui/dist/swagger-ui.js delete mode 100644 vendor/swagger-api/swagger-ui/docker/configurator/helpers.js delete mode 100644 vendor/swagger-api/swagger-ui/docker/configurator/index.js delete mode 100644 vendor/swagger-api/swagger-ui/docker/configurator/oauth.js delete mode 100644 vendor/swagger-api/swagger-ui/docker/configurator/translator.js delete mode 100644 vendor/swagger-api/swagger-ui/docker/configurator/variables.js delete mode 100644 vendor/swagger-api/swagger-ui/docker/cors.conf delete mode 100644 vendor/swagger-api/swagger-ui/docker/default.conf.template delete mode 100644 vendor/swagger-api/swagger-ui/docker/docker-entrypoint.d/40-swagger-ui.sh delete mode 100644 vendor/swagger-api/swagger-ui/docker/embedding.conf delete mode 100644 vendor/swagger-api/swagger-ui/flavors/swagger-ui-react/README.md delete mode 100644 vendor/swagger-api/swagger-ui/flavors/swagger-ui-react/index.jsx delete mode 100644 vendor/swagger-api/swagger-ui/flavors/swagger-ui-react/release/create-manifest.js delete mode 100644 vendor/swagger-api/swagger-ui/flavors/swagger-ui-react/release/run.sh delete mode 100644 vendor/swagger-api/swagger-ui/flavors/swagger-ui-react/release/template.json delete mode 100644 vendor/swagger-api/swagger-ui/package-lock.json delete mode 100644 vendor/swagger-api/swagger-ui/package.json delete mode 100644 vendor/swagger-api/swagger-ui/release/.release-it.json delete mode 100644 vendor/swagger-api/swagger-ui/release/check-for-breaking-changes.sh delete mode 100644 vendor/swagger-api/swagger-ui/release/get-changelog.sh delete mode 100644 vendor/swagger-api/swagger-ui/snapcraft.yaml delete mode 100644 vendor/swagger-api/swagger-ui/src/.eslintrc delete mode 100644 vendor/swagger-api/swagger-ui/src/core/assets/rolling-load.svg delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/app.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/api-key-auth.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/auth-item.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/authorization-popup.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/authorize-btn.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/authorize-operation-btn.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/auths.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/basic-auth.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/error.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/auth/oauth2.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/clear.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/contact.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/content-type.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/copy-to-clipboard-btn.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/curl.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/debug.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/deep-link.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/errors.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/example.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/examples-select-value-retainer.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/examples-select.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/execute.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/footer.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/headers.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/info.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/initialized-input.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/jump-to-path.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/layout-utils.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/layouts/base.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/layouts/xpane.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/license.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/live-response.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/online-validator-badge.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/openapi-version.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/operation-extension-row.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/operation-extensions.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/operation-summary-method.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/operation-summary-path.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/operation-summary.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/operation-tag.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/operation.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/operations.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/overview.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/param-body.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/parameter-extension.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/parameter-include-empty.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/parameter-row.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/parameters/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/parameters/parameters.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/property.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/providers/README.md delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/providers/markdown.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/response-body.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/response-extension.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/response.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/responses.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/svg-assets.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/try-it-out-button.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/version-pragma-filter.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/components/version-stamp.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/defaults.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/factorization/inline-plugin.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/factorization/system.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/merge.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/sources/query.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/sources/runtime.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/sources/url.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/mappings.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/array.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/boolean.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/dom-node.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/filter.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/function.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/nullable-array.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/nullable-function.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/nullable-string.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/number.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/object.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/sorter.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/string.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/syntax-highlight.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/config/type-cast/type-casters/undefined-string.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/containers/OperationContainer.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/containers/authorize-btn.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/containers/filter.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/containers/info.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/oauth2-authorize.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/components/lock-auth-icon.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/components/unlock-auth-icon.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/configs-extensions/wrap-actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/reducers.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/spec-extensions/wrap-actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/auth/wrap-actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/configs/actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/configs/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/configs/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/configs/reducers.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/configs/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/deep-linking/README.md delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/deep-linking/helpers.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/deep-linking/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/deep-linking/layout.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/deep-linking/operation-tag-wrapper.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/deep-linking/operation-wrapper.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/download-url/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/err/actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/err/error-transformers/README.md delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/err/error-transformers/hook.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/err/error-transformers/transformers/not-of-type.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/err/error-transformers/transformers/parameter-oneof.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/err/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/err/reducers.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/err/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/filter/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/filter/opsFilter.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/icons/components/arrow-down.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/icons/components/arrow-up.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/icons/components/arrow.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/icons/components/close.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/icons/components/copy.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/icons/components/lock.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/icons/components/unlock.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/icons/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/api/encoderAPI.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/api/formatAPI.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/api/mediaTypeAPI.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/api/optionAPI.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/class/EncoderRegistry.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/class/FormatRegistry.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/class/MediaTypeRegistry.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/class/OptionRegistry.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/class/Registry.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/core/constants.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/core/example.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/core/merge.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/core/predicates.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/core/random.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/core/type.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/core/utils.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/encoders/7bit.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/encoders/8bit.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/encoders/base16.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/encoders/base32.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/encoders/base64.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/encoders/base64url.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/encoders/binary.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/encoders/quoted-printable.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/date-time.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/date.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/double.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/duration.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/email.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/float.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/hostname.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/idn-email.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/idn-hostname.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/int32.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/int64.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/ipv4.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/ipv6.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/iri-reference.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/iri.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/json-pointer.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/media-types/application.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/media-types/audio.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/media-types/image.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/media-types/text.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/media-types/video.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/password.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/regex.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/relative-json-pointer.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/time.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/uri-reference.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/uri-template.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/uri.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/generators/uuid.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/get-json-sample-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/get-sample-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/get-xml-sample-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/get-yaml-sample-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/main.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/types/array.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/types/boolean.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/types/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/types/integer.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/types/null.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/types/number.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/types/object.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/fn/types/string.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12-samples/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/Accordion/Accordion.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/Accordion/_accordion.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/ExpandDeepButton/ExpandDeepButton.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/ExpandDeepButton/_expand-deep-button.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/JSONSchema/JSONSchema.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/JSONSchema/_json-schema.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/JSONViewer/JSONViewer.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/JSONViewer/_json-viewer.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/_all.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/_mixins.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/icons/ChevronRight.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$anchor.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$comment.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$defs.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$dynamicAnchor.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$dynamicRef.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$id.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$ref.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$schema.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$vocabulary/$vocabulary.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/$vocabulary/_$vocabulary.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/AdditionalProperties.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/AllOf.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/AnyOf.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Const/Const.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Const/_const.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Constraint/Constraint.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Constraint/_constraint.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Contains.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/ContentSchema.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Default/Default.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Default/_default.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/DependentRequired/DependentRequired.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/DependentRequired/_dependent-required.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/DependentSchemas.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Deprecated.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Description/Description.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Description/_description.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Else.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Enum/Enum.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Enum/_enum.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Examples/Examples.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Examples/_examples.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/ExtensionKeywords/ExtensionKeywords.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/ExtensionKeywords/_extension-keywords.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/If.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Items.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Not.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/OneOf.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/PatternProperties/PatternProperties.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/PatternProperties/_pattern-properties.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/PrefixItems.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Properties/Properties.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Properties/_properties.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/PropertyNames.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/ReadOnly.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Then.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Title/Title.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Title/_title.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/Type.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/UnevaluatedItems.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/UnevaluatedProperties.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/WriteOnly.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/components/keywords/_all.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/context.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/enum.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/hoc.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/hooks.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-2020-12/prop-types.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5-samples/fn/get-json-sample-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5-samples/fn/get-sample-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5-samples/fn/get-xml-sample-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5-samples/fn/get-yaml-sample-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5-samples/fn/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5-samples/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/array-model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/enum-model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/json-schema-components.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/model-collapse.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/model-example.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/model-extensions.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/model-wrapper.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/models.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/object-model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/primitive-model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/components/schemes.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/containers/schemes.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/json-schema-5/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/layout/actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/layout/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/layout/reducers.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/layout/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/layout/spec-extensions/wrap-selector.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/logs/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/auth-extensions/wrap-selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/auth/http-auth.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/callbacks.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/operation-link.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/operation-servers.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/request-body-editor.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/request-body.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/servers-container.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/components/servers.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/helpers.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/reducers.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/spec-extensions/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/spec-extensions/wrap-selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/wrap-components/auth/auth-item.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/wrap-components/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/wrap-components/json-schema-string.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/wrap-components/markdown.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/wrap-components/model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/wrap-components/online-validator-badge.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas3/wrap-components/openapi-version.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/after-load.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/auth-extensions/wrap-selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/_all.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/auth/auths.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/auth/mutual-tls-auth.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/contact.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/info.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/json-schema-dialect.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/license.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/model/_model.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/model/model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/models/_models.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/models/models.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/version-pragma-filter.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/components/webhooks.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/components/keywords/Description.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/components/keywords/Discriminator/Discriminator.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/components/keywords/Discriminator/DiscriminatorMapping.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/components/keywords/Example.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/components/keywords/ExternalDocs.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/components/keywords/OpenAPIExtensions.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/components/keywords/Properties.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/components/keywords/Xml.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/wrap-components/keywords/Description.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/wrap-components/keywords/Examples.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/json-schema-2020-12-extensions/wrap-components/keywords/Properties.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/oas3-extensions/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/spec-extensions/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/spec-extensions/wrap-selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/wrap-components/auth/auth-item.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/wrap-components/auths.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/wrap-components/contact.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/wrap-components/info.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/wrap-components/license.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/wrap-components/model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/wrap-components/models.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas31/wrap-components/version-pragma-filter.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/after-load.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/components/version-pragma-filter.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/json-schema-2020-12-extensions/components/keywords/Description.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/json-schema-2020-12-extensions/components/keywords/Properties.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/json-schema-2020-12-extensions/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/json-schema-2020-12-extensions/wrap-components/keywords/Description.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/json-schema-2020-12-extensions/wrap-components/keywords/Examples.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/json-schema-2020-12-extensions/wrap-components/keywords/Properties.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/oas3-extensions/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/spec-extensions/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/spec-extensions/wrap-selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/wrap-components/contact.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/wrap-components/info.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/wrap-components/license.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/wrap-components/model.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/wrap-components/models.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/wrap-components/openapi-version.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/oas32/wrap-components/version-pragma-filter.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/on-complete/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/request-snippets/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/request-snippets/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/request-snippets/request-snippets.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/request-snippets/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/safe-render/components/error-boundary.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/safe-render/components/fallback.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/safe-render/fn.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/safe-render/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/spec/actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/spec/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/spec/reducers.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/spec/selectors.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/spec/wrap-actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/swagger-client/configs-wrap-actions.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/swagger-client/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/syntax-highlighting/after-load.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/syntax-highlighting/components/HighlightCode.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/syntax-highlighting/components/PlainTextViewer.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/syntax-highlighting/components/SyntaxHighlighter.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/syntax-highlighting/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/syntax-highlighting/root-injects.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/syntax-highlighting/wrap-components/SyntaxHighlighter.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/util/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/versions/after-load.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/versions/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/view-legacy/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/view-legacy/root-injects.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/view/fn.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/view/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/plugins/view/root-injects.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/core/presets/apis/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/presets/base/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/presets/base/plugins/core-components/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/presets/base/plugins/form-components/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/system.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/utils/create-html-ready-id.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/utils/get-parameter-schema.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/utils/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/utils/jsonParse.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/utils/memoizeN.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/utils/url.js delete mode 100644 vendor/swagger-api/swagger-ui/src/core/window.js delete mode 100644 vendor/swagger-api/swagger-ui/src/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/stadalone-layout/components/StandaloneLayout.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/stadalone-layout/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/top-bar/assets/lightbulb-off.svg delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/top-bar/assets/lightbulb.svg delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/top-bar/assets/logo_small.svg delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/top-bar/components/DarkModeToggle.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/top-bar/components/Logo.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/top-bar/components/TopBar.jsx delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/plugins/top-bar/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/standalone/presets/standalone/index.js delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_authorize.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_buttons.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_dark-mode.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_errors.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_form.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_information.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_layout.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_markdown.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_mixins.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_modal.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_models.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_servers.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_split-pane-mode.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_table.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_topbar.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_type.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/_variables.scss delete mode 100644 vendor/swagger-api/swagger-ui/src/style/main.scss delete mode 100644 vendor/swagger-api/swagger-ui/stylelint.config.js delete mode 100644 vendor/swagger-api/swagger-ui/swagger-ui-dist-package/.npmignore delete mode 100644 vendor/swagger-api/swagger-ui/swagger-ui-dist-package/.npmrc delete mode 100644 vendor/swagger-api/swagger-ui/swagger-ui-dist-package/README.md delete mode 100644 vendor/swagger-api/swagger-ui/swagger-ui-dist-package/absolute-path.js delete mode 100644 vendor/swagger-api/swagger-ui/swagger-ui-dist-package/deploy.sh delete mode 100644 vendor/swagger-api/swagger-ui/swagger-ui-dist-package/index.js delete mode 100644 vendor/swagger-api/swagger-ui/swagger-ui-dist-package/package.json delete mode 100644 vendor/swagger-api/swagger-ui/webpack/_config-builder.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/_helpers.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/bundle.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/core.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/dev-e2e.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/dev.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/es-bundle-core.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/es-bundle.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/standalone.js delete mode 100644 vendor/swagger-api/swagger-ui/webpack/stylesheets.js delete mode 100644 vendor/symfony/type-info/CHANGELOG.md delete mode 100644 vendor/symfony/type-info/Exception/ExceptionInterface.php delete mode 100644 vendor/symfony/type-info/Exception/InvalidArgumentException.php delete mode 100644 vendor/symfony/type-info/Exception/LogicException.php delete mode 100644 vendor/symfony/type-info/Exception/RuntimeException.php delete mode 100644 vendor/symfony/type-info/Exception/UnsupportedException.php delete mode 100644 vendor/symfony/type-info/README.md delete mode 100644 vendor/symfony/type-info/Type.php delete mode 100644 vendor/symfony/type-info/Type/ArrayShapeType.php delete mode 100644 vendor/symfony/type-info/Type/BackedEnumType.php delete mode 100644 vendor/symfony/type-info/Type/BuiltinType.php delete mode 100644 vendor/symfony/type-info/Type/CollectionType.php delete mode 100644 vendor/symfony/type-info/Type/CompositeTypeInterface.php delete mode 100644 vendor/symfony/type-info/Type/EnumType.php delete mode 100644 vendor/symfony/type-info/Type/GenericType.php delete mode 100644 vendor/symfony/type-info/Type/IntersectionType.php delete mode 100644 vendor/symfony/type-info/Type/NullableType.php delete mode 100644 vendor/symfony/type-info/Type/ObjectType.php delete mode 100644 vendor/symfony/type-info/Type/TemplateType.php delete mode 100644 vendor/symfony/type-info/Type/UnionType.php delete mode 100644 vendor/symfony/type-info/Type/WrappingTypeInterface.php delete mode 100644 vendor/symfony/type-info/TypeContext/TypeContext.php delete mode 100644 vendor/symfony/type-info/TypeContext/TypeContextFactory.php delete mode 100644 vendor/symfony/type-info/TypeFactoryTrait.php delete mode 100644 vendor/symfony/type-info/TypeIdentifier.php delete mode 100644 vendor/symfony/type-info/TypeResolver/PhpDocAwareReflectionTypeResolver.php delete mode 100644 vendor/symfony/type-info/TypeResolver/ReflectionParameterTypeResolver.php delete mode 100644 vendor/symfony/type-info/TypeResolver/ReflectionPropertyTypeResolver.php delete mode 100644 vendor/symfony/type-info/TypeResolver/ReflectionReturnTypeResolver.php delete mode 100644 vendor/symfony/type-info/TypeResolver/ReflectionTypeResolver.php delete mode 100644 vendor/symfony/type-info/TypeResolver/StringTypeResolver.php delete mode 100644 vendor/symfony/type-info/TypeResolver/TypeResolver.php delete mode 100644 vendor/symfony/type-info/TypeResolver/TypeResolverInterface.php delete mode 100644 vendor/symfony/type-info/composer.json delete mode 100644 vendor/zircote/swagger-php/CONTEXT.md delete mode 100644 vendor/zircote/swagger-php/CONTRIBUTING.md delete mode 100644 vendor/zircote/swagger-php/LICENSE delete mode 100644 vendor/zircote/swagger-php/NOTICE delete mode 100644 vendor/zircote/swagger-php/README.md delete mode 100644 vendor/zircote/swagger-php/bin/openapi delete mode 100644 vendor/zircote/swagger-php/composer.json delete mode 100644 vendor/zircote/swagger-php/package.json delete mode 100644 vendor/zircote/swagger-php/rector.php delete mode 100644 vendor/zircote/swagger-php/src/Analysers/AnalyserInterface.php delete mode 100644 vendor/zircote/swagger-php/src/Analysers/AnnotationFactoryInterface.php delete mode 100644 vendor/zircote/swagger-php/src/Analysers/AttributeAnnotationFactory.php delete mode 100644 vendor/zircote/swagger-php/src/Analysers/ComposerAutoloaderScanner.php delete mode 100644 vendor/zircote/swagger-php/src/Analysers/DocBlockAnnotationFactory.php delete mode 100644 vendor/zircote/swagger-php/src/Analysers/DocBlockParser.php delete mode 100644 vendor/zircote/swagger-php/src/Analysers/ReflectionAnalyser.php delete mode 100644 vendor/zircote/swagger-php/src/Analysers/TokenScanner.php delete mode 100644 vendor/zircote/swagger-php/src/Analysis.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/AbstractAnnotation.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/AdditionalProperties.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Attachable.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Components.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Contact.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/CookieParameter.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Delete.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Discriminator.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Encoding.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Examples.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/ExternalDocumentation.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Flow.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Get.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Head.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Header.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/HeaderParameter.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Info.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Items.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/JsonContent.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/JsonSchemaTrait.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/License.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Link.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/MediaType.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/OpenApi.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Operation.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Options.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Parameter.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Patch.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/PathItem.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/PathParameter.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Post.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Property.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Put.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Query.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/QueryParameter.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/RequestBody.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Response.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Schema.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/SecurityScheme.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Server.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/ServerVariable.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Tag.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Trace.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Webhook.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/Xml.php delete mode 100644 vendor/zircote/swagger-php/src/Annotations/XmlContent.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/AdditionalProperties.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Attachable.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Components.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Contact.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/CookieParameter.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Delete.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Discriminator.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Encoding.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Examples.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/ExternalDocumentation.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Flow.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Get.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Head.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Header.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/HeaderParameter.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Info.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Items.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/JsonContent.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/License.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Link.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/MediaType.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/OpenApi.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/OperationTrait.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Options.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Parameter.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/ParameterTrait.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Patch.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/PathItem.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/PathParameter.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Post.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Property.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Put.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Query.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/QueryParameter.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/RequestBody.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Response.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Schema.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/SecurityScheme.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Server.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/ServerVariable.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Tag.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Trace.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Webhook.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/Xml.php delete mode 100644 vendor/zircote/swagger-php/src/Attributes/XmlContent.php delete mode 100644 vendor/zircote/swagger-php/src/Console/GenerateCommand.php delete mode 100644 vendor/zircote/swagger-php/src/Console/GenerateFormat.php delete mode 100644 vendor/zircote/swagger-php/src/Console/GenerateInput.php delete mode 100644 vendor/zircote/swagger-php/src/Context.php delete mode 100644 vendor/zircote/swagger-php/src/Generator.php delete mode 100644 vendor/zircote/swagger-php/src/GeneratorAwareInterface.php delete mode 100644 vendor/zircote/swagger-php/src/GeneratorAwareTrait.php delete mode 100644 vendor/zircote/swagger-php/src/Loggers/DefaultLogger.php delete mode 100644 vendor/zircote/swagger-php/src/OpenApiException.php delete mode 100644 vendor/zircote/swagger-php/src/Pipeline.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentDiscriminators.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentItems.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentMediaType.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentParameters.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentProperties.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentRefs.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentRequestBody.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentSchemas.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/AugmentTags.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/BuildPaths.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/CleanUnmerged.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/CleanUnusedComponents.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/Concerns/AnnotationTrait.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/Concerns/DocblockTrait.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/Concerns/MergePropertiesTrait.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/Concerns/RefTrait.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/DocBlockDescriptions.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/ExpandClasses.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/ExpandEnums.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/ExpandInterfaces.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/ExpandTraits.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/MergeIntoComponents.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/MergeIntoOpenApi.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/MergeJsonContent.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/MergeXmlContent.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/OperationId.php delete mode 100644 vendor/zircote/swagger-php/src/Processors/PathFilter.php delete mode 100644 vendor/zircote/swagger-php/src/Serializer.php delete mode 100644 vendor/zircote/swagger-php/src/SourceFinder.php delete mode 100644 vendor/zircote/swagger-php/src/Type/AbstractTypeResolver.php delete mode 100644 vendor/zircote/swagger-php/src/Type/LegacyTypeResolver.php delete mode 100644 vendor/zircote/swagger-php/src/Type/TypeInfoTypeResolver.php delete mode 100644 vendor/zircote/swagger-php/src/TypeResolverInterface.php diff --git a/app/Docs/OpenApiSpec.php b/app/Docs/OpenApiSpec.php deleted file mode 100644 index 98da9c1c..00000000 --- a/app/Docs/OpenApiSpec.php +++ /dev/null @@ -1,20 +0,0 @@ -validate([ diff --git a/app/Http/Controllers/Ticket/TicketController.php b/app/Http/Controllers/Ticket/TicketController.php new file mode 100644 index 00000000..eb833291 --- /dev/null +++ b/app/Http/Controllers/Ticket/TicketController.php @@ -0,0 +1,70 @@ +paginate( + $request->get('per_page', 10) + ); + + return $this->success($tickets); + } + + public function store(StoreTicketRequest $request) + { + $ticket = $this->service->create( + $request->validated() + ); + + return $this->created( + new TicketResource($ticket), + 'Ticket berhasil dibuat' + ); + } + + public function show(Ticket $ticket) + { + return $this->success( + new TicketResource($ticket) + ); + } + + public function update( + UpdateTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->update( + $ticket, + $request->validated() + ); + + return $this->updated( + new TicketResource($ticket), + 'Ticket berhasil diperbarui' + ); + } + + public function destroy(Ticket $ticket) + { + $this->service->delete($ticket); + + return $this->deleted( + 'Ticket berhasil dibatalkan' + ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Ticket/TicketTypeController.php b/app/Http/Controllers/Ticket/TicketTypeController.php new file mode 100644 index 00000000..c7e32cbb --- /dev/null +++ b/app/Http/Controllers/Ticket/TicketTypeController.php @@ -0,0 +1,70 @@ +paginate( + $request->get('per_page', 10) + ); + + return $this->success($data); + } + + public function store(StoreTicketTypeRequest $request) + { + $type = $this->service->create( + $request->validated() + ); + + return $this->created( + new TicketTypeResource($type), + 'Ticket Type berhasil dibuat' + ); + } + + public function show(TicketType $ticket_type) + { + return $this->success( + new TicketTypeResource($ticket_type) + ); + } + + public function update( + UpdateTicketTypeRequest $request, + TicketType $ticket_type + ) { + $type = $this->service->update( + $ticket_type, + $request->validated() + ); + + return $this->updated( + new TicketTypeResource($type), + 'Ticket Type berhasil diupdate' + ); + } + + public function destroy(TicketType $ticket_type) + { + $this->service->delete($ticket_type); + + return $this->deleted( + 'Ticket Type berhasil dihapus' + ); + } +} diff --git a/app/Http/Requests/Ticket/StoreTicketRequest.php b/app/Http/Requests/Ticket/StoreTicketRequest.php new file mode 100644 index 00000000..1abb88f9 --- /dev/null +++ b/app/Http/Requests/Ticket/StoreTicketRequest.php @@ -0,0 +1,33 @@ +|string> + */ + public function rules(): array + { + return [ + 'ticket_type_id' => 'required|integer', + 'customer_id' => 'nullable|integer', + 'title' => 'required|string|max:255', + 'description' => 'nullable|string', + 'priority' => 'required|in:low,medium,high,critical' + ]; + } +} diff --git a/app/Http/Requests/Ticket/UpdateTicketRequest.php b/app/Http/Requests/Ticket/UpdateTicketRequest.php new file mode 100644 index 00000000..aa9c28da --- /dev/null +++ b/app/Http/Requests/Ticket/UpdateTicketRequest.php @@ -0,0 +1,34 @@ +|string> + */ + public function rules(): array + { + return [ + 'ticket_type_id' => 'sometimes|integer', + 'customer_id' => 'nullable|integer', + 'title' => 'sometimes|string|max:255', + 'description' => 'nullable|string', + 'priority' => 'sometimes|in:low,medium,high,critical', + 'status' => 'sometimes|in:draft,open,approved,assigned,progress,pending,resolved,closed,cancelled,rejected' + ]; + } +} diff --git a/app/Http/Requests/TicketType/StoreTicketTypeRequest.php b/app/Http/Requests/TicketType/StoreTicketTypeRequest.php new file mode 100644 index 00000000..62766445 --- /dev/null +++ b/app/Http/Requests/TicketType/StoreTicketTypeRequest.php @@ -0,0 +1,32 @@ +|string> + */ + public function rules(): array + { + return [ + 'code' => 'required|string|max:50|unique:ticket_types,code', + 'name' => 'required|string|max:100', + 'need_approval' => 'boolean', + 'sla_minutes' => 'nullable|integer' + ]; + } +} diff --git a/app/Http/Requests/TicketType/UpdateTicketTypeRequest.php b/app/Http/Requests/TicketType/UpdateTicketTypeRequest.php new file mode 100644 index 00000000..9bcad7b7 --- /dev/null +++ b/app/Http/Requests/TicketType/UpdateTicketTypeRequest.php @@ -0,0 +1,32 @@ +|string> + */ + public function rules(): array + { + return [ + 'code' => 'sometimes|string|max:50|unique:ticket_types,code,' . $this->route('ticket_type'), + 'name' => 'sometimes|string|max:100', + 'need_approval' => 'sometimes|boolean', + 'sla_minutes' => 'nullable|integer' + ]; + } +} diff --git a/app/Http/Resources/Ticket/TicketResource.php b/app/Http/Resources/Ticket/TicketResource.php new file mode 100644 index 00000000..27a99689 --- /dev/null +++ b/app/Http/Resources/Ticket/TicketResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'ticket_no' => $this->ticket_no, + 'ticket_type_id' => $this->ticket_type_id, + 'customer_id' => $this->customer_id, + 'title' => $this->title, + 'description' => $this->description, + 'priority' => $this->priority, + 'status' => $this->status, + 'created_by' => $this->created_by, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/app/Http/Resources/TicketType/TicketTypeResource.php b/app/Http/Resources/TicketType/TicketTypeResource.php new file mode 100644 index 00000000..129fcf48 --- /dev/null +++ b/app/Http/Resources/TicketType/TicketTypeResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'code' => $this->code, + 'name' => $this->name, + 'need_approval' => $this->need_approval, + 'sla_minutes' => $this->sla_minutes, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index 9e9c06f0..a5fc0a7c 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -6,5 +6,14 @@ use Illuminate\Database\Eloquent\Model; class Ticket extends Model { - // + protected $fillable = [ + 'ticket_no', + 'ticket_type_id', + 'customer_id', + 'title', + 'description', + 'priority', + 'status', + 'created_by', + ]; } diff --git a/app/Models/TicketLog.php b/app/Models/TicketLog.php index 48e615c4..138e7316 100644 --- a/app/Models/TicketLog.php +++ b/app/Models/TicketLog.php @@ -6,5 +6,15 @@ use Illuminate\Database\Eloquent\Model; class TicketLog extends Model { - // + public $timestamps = false; + + protected $fillable = [ + 'ticket_id', + 'user_id', + 'activity', + 'notes', + 'latitude', + 'longitude', + 'created_at' + ]; } diff --git a/app/Models/TicketType.php b/app/Models/TicketType.php index 002e31e0..6a4a4583 100644 --- a/app/Models/TicketType.php +++ b/app/Models/TicketType.php @@ -6,5 +6,16 @@ use Illuminate\Database\Eloquent\Model; class TicketType extends Model { - // + protected $table = 'ticket_types'; + + protected $fillable = [ + 'code', + 'name', + 'need_approval', + 'sla_minutes' + ]; + + protected $casts = [ + 'need_approval' => 'boolean', + ]; } diff --git a/app/Services/Ticket/TicketService.php b/app/Services/Ticket/TicketService.php new file mode 100644 index 00000000..880ae091 --- /dev/null +++ b/app/Services/Ticket/TicketService.php @@ -0,0 +1,79 @@ + $this->generateTicketNo(), + 'ticket_type_id' => $data['ticket_type_id'], + 'customer_id' => $data['customer_id'] ?? null, + 'title' => $data['title'], + 'description' => $data['description'] ?? null, + 'priority' => $data['priority'], + 'status' => 'open', + 'created_by' => 1 + ]); + + TicketLog::create([ + 'ticket_id' => $ticket->id, + 'user_id' => 1, + 'activity' => 'Ticket Created', + 'notes' => 'Ticket dibuat oleh sistem', + 'created_at'=> now() + ]); + + return $ticket; + }); + } + + public function update(Ticket $ticket, array $data): Ticket + { + $ticket->update($data); + + return $ticket->fresh(); + } + + public function delete(Ticket $ticket): Ticket + { + $ticket->update([ + 'status' => 'cancelled' + ]); + + return $ticket; + } + + private function generateTicketNo(): string + { + $prefix = 'TCK-' . now()->format('Ym'); + + $lastTicket = Ticket::where( + 'ticket_no', + 'like', + $prefix . '%' + ) + ->latest('id') + ->first(); + + $number = 1; + + if ($lastTicket) { + $number = ((int) substr($lastTicket->ticket_no, -6)) + 1; + } + + return $prefix . '-' . str_pad( + $number, + 6, + '0', + STR_PAD_LEFT + ); + } +} \ No newline at end of file diff --git a/app/Services/Ticket/TicketTypeService.php b/app/Services/Ticket/TicketTypeService.php new file mode 100644 index 00000000..91fc34e0 --- /dev/null +++ b/app/Services/Ticket/TicketTypeService.php @@ -0,0 +1,24 @@ +update($data); + return $type->fresh(); + } + + public function delete(TicketType $type): bool + { + return $type->delete(); + } +} \ No newline at end of file diff --git a/app/Traits/ApiResponse.php b/app/Traits/ApiResponse.php new file mode 100644 index 00000000..b8f2774a --- /dev/null +++ b/app/Traits/ApiResponse.php @@ -0,0 +1,73 @@ +json([ + 'success' => true, + 'code' => $code, + 'message' => $message, + 'data' => $data + ], $code); + } + + protected function created($data = null, string $message = 'Data berhasil dibuat') + { + return response()->json([ + 'success' => true, + 'code' => 201, + 'message' => $message, + 'data' => $data + ], 201); + } + + protected function updated($data = null, string $message = 'Data berhasil diperbarui') + { + return response()->json([ + 'success' => true, + 'code' => 200, + 'message' => $message, + 'data' => $data + ]); + } + + protected function deleted(string $message = 'Data berhasil dihapus') + { + return response()->json([ + 'success' => true, + 'code' => 200, + 'message' => $message + ]); + } + + protected function error(string $message = 'Internal Server Error', int $code = 500) + { + return response()->json([ + 'success' => false, + 'code' => $code, + 'message' => $message + ], $code); + } + + protected function validationError($errors) + { + return response()->json([ + 'success' => false, + 'code' => 422, + 'message' => 'Validation Error', + 'errors' => $errors + ], 422); + } + + protected function notFound(string $message = 'Data tidak ditemukan') + { + return response()->json([ + 'success' => false, + 'code' => 404, + 'message' => $message + ], 404); + } +} \ No newline at end of file diff --git a/bootstrap/app.php b/bootstrap/app.php index d6542762..b4b52b40 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -11,6 +11,7 @@ return Application::configure(basePath: dirname(__DIR__)) commands: __DIR__.'/../routes/console.php', health: '/up', ) + ->withMiddleware(function (Middleware $middleware) { // }) diff --git a/bruno/Services Core API V1.0/Ticketing/Add Tickets.yml b/bruno/Services Core API V1.0/Ticketing/Add Tickets.yml new file mode 100644 index 00000000..c2a02a33 --- /dev/null +++ b/bruno/Services Core API V1.0/Ticketing/Add Tickets.yml @@ -0,0 +1,28 @@ +info: + name: Add Tickets + type: http + seq: 2 + +http: + method: POST + url: http://127.0.0.1:8000/api/tickets + body: + type: json + data: |- + { + "user_id": 1, + "ticket_type_id": 1, + "customer_id": 133, + "title": "Lampu PON modem merah", + "description": "Pelanggan mengeluhkan internet lambat", + "priority": "high" + } + auth: + type: bearer + token: ll4X9vVjWRRDGbGQyrzcAhWVHmlEbpmQ7odklBCh7423957d + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/bruno/Services Core API V1.0/Ticketing/Get List Tickets.yml b/bruno/Services Core API V1.0/Ticketing/Get List Tickets.yml new file mode 100644 index 00000000..a17ca0f4 --- /dev/null +++ b/bruno/Services Core API V1.0/Ticketing/Get List Tickets.yml @@ -0,0 +1,17 @@ +info: + name: Get List Tickets + type: http + seq: 2 + +http: + method: GET + url: http://127.0.0.1:8000/api/tickets + auth: + type: bearer + token: ll4X9vVjWRRDGbGQyrzcAhWVHmlEbpmQ7odklBCh7423957d + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/bruno/Services Core API V1.0/Ticketing/folder.yml b/bruno/Services Core API V1.0/Ticketing/folder.yml new file mode 100644 index 00000000..27fc7230 --- /dev/null +++ b/bruno/Services Core API V1.0/Ticketing/folder.yml @@ -0,0 +1,7 @@ +info: + name: Ticketing + type: folder + seq: 2 + +request: + auth: inherit diff --git a/composer.json b/composer.json index 2d05d389..46c7ec78 100644 --- a/composer.json +++ b/composer.json @@ -7,12 +7,12 @@ "license": "MIT", "require": { "php": "^8.2", - "darkaonline/l5-swagger": "^11.1", "laravel/framework": "^12.0", "laravel/sanctum": "^4.3", "laravel/tinker": "^2.10.1" }, "require-dev": { + "dedoc/scramble": "^0.13.28", "fakerphp/faker": "^1.23", "laravel/pail": "^1.2.2", "laravel/pint": "^1.13", diff --git a/composer.lock b/composer.lock index 516bedab..946f1f91 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "13ca615bebb1db69527a7039d10b8684", + "content-hash": "dc5dc467fbab2c5682c8e676f4c2ffb4", "packages": [ { "name": "brick/math", @@ -135,86 +135,6 @@ ], "time": "2024-02-09T16:56:22+00:00" }, - { - "name": "darkaonline/l5-swagger", - "version": "11.1.0", - "source": { - "type": "git", - "url": "https://github.com/DarkaOnLine/L5-Swagger.git", - "reference": "110b59478c9417c13794cef62a82b019433d642a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DarkaOnLine/L5-Swagger/zipball/110b59478c9417c13794cef62a82b019433d642a", - "reference": "110b59478c9417c13794cef62a82b019433d642a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "laravel/framework": "^13.0 || ^12.1 || ^11.44", - "php": "^8.2", - "swagger-api/swagger-ui": ">=5.18.3", - "symfony/yaml": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "zircote/swagger-php": "^6.0" - }, - "require-dev": { - "mockery/mockery": "1.*", - "orchestra/testbench": "^11.0 || ^10.0 || ^9.0 || ^8.0 || 7.* || ^6.15 || 5.*", - "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "laravel": { - "aliases": { - "L5Swagger": "L5Swagger\\L5SwaggerFacade" - }, - "providers": [ - "L5Swagger\\L5SwaggerServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "L5Swagger\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Darius Matulionis", - "email": "darius@matulionis.lt" - } - ], - "description": "OpenApi or Swagger integration to Laravel", - "keywords": [ - "api", - "documentation", - "laravel", - "openapi", - "specification", - "swagger", - "ui" - ], - "support": { - "issues": "https://github.com/DarkaOnLine/L5-Swagger/issues", - "source": "https://github.com/DarkaOnLine/L5-Swagger/tree/11.1.0" - }, - "funding": [ - { - "url": "https://github.com/DarkaOnLine", - "type": "github" - } - ], - "time": "2026-06-12T10:04:41+00:00" - }, { "name": "dflydev/dot-access-data", "version": "v3.0.3", @@ -2754,53 +2674,6 @@ ], "time": "2025-12-27T19:41:33+00:00" }, - { - "name": "phpstan/phpdoc-parser", - "version": "2.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" - }, - "time": "2026-01-25T14:56:51+00:00" - }, { "name": "psr/clock", "version": "1.0.0", @@ -3292,68 +3165,6 @@ }, "time": "2026-05-23T13:41:31+00:00" }, - { - "name": "radebatz/type-info-extras", - "version": "1.0.7", - "source": { - "type": "git", - "url": "https://github.com/DerManoMann/type-info-extras.git", - "reference": "95a524a74a61648b44e355cb33d38db4b17ef5ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DerManoMann/type-info-extras/zipball/95a524a74a61648b44e355cb33d38db4b17ef5ce", - "reference": "95a524a74a61648b44e355cb33d38db4b17ef5ce", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "phpstan/phpdoc-parser": "^2.0", - "symfony/type-info": "^7.3.8 || ^7.4.1 || ^8.0 || ^8.1-@dev" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.70", - "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Radebatz\\TypeInfoExtras\\": "src" - }, - "exclude-from-classmap": [ - "/tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Martin Rademacher", - "email": "mano@radebatz.org" - } - ], - "description": "Extras for symfony/type-info", - "homepage": "http://radebatz.net/mano/", - "keywords": [ - "component", - "symfony", - "type-info", - "types" - ], - "support": { - "issues": "https://github.com/DerManoMann/type-info-extras/issues", - "source": "https://github.com/DerManoMann/type-info-extras/tree/1.0.7" - }, - "time": "2026-03-06T22:40:29+00:00" - }, { "name": "ralouphie/getallheaders", "version": "3.0.3", @@ -3552,67 +3363,6 @@ }, "time": "2025-12-14T04:43:48+00:00" }, - { - "name": "swagger-api/swagger-ui", - "version": "v5.32.6", - "source": { - "type": "git", - "url": "https://github.com/swagger-api/swagger-ui.git", - "reference": "dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60", - "reference": "dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Anna Bodnia", - "email": "anna.bodnia@gmail.com" - }, - { - "name": "Buu Nguyen", - "email": "buunguyen@gmail.com" - }, - { - "name": "Josh Ponelat", - "email": "jponelat@gmail.com" - }, - { - "name": "Kyle Shockey", - "email": "kyleshockey1@gmail.com" - }, - { - "name": "Robert Barnwell", - "email": "robert@robertismy.name" - }, - { - "name": "Sahar Jafari", - "email": "shr.jafari@gmail.com" - } - ], - "description": " Swagger UI is a collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.", - "homepage": "http://swagger.io", - "keywords": [ - "api", - "documentation", - "openapi", - "specification", - "swagger", - "ui" - ], - "support": { - "issues": "https://github.com/swagger-api/swagger-ui/issues", - "source": "https://github.com/swagger-api/swagger-ui/tree/v5.32.6" - }, - "time": "2026-05-12T09:35:37+00:00" - }, { "name": "symfony/clock", "version": "v7.4.8", @@ -5957,89 +5707,6 @@ ], "time": "2026-01-05T13:30:16+00:00" }, - { - "name": "symfony/type-info", - "version": "v7.4.9", - "source": { - "type": "git", - "url": "https://github.com/symfony/type-info.git", - "reference": "cafeedbf157b890e94ac5b83eaed85595106d5d6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/cafeedbf157b890e94ac5b83eaed85595106d5d6", - "reference": "cafeedbf157b890e94ac5b83eaed85595106d5d6", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "phpstan/phpdoc-parser": "<1.30" - }, - "require-dev": { - "phpstan/phpdoc-parser": "^1.30|^2.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\TypeInfo\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mathias Arlaud", - "email": "mathias.arlaud@gmail.com" - }, - { - "name": "Baptiste LEDUC", - "email": "baptiste.leduc@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Extracts PHP types information.", - "homepage": "https://symfony.com", - "keywords": [ - "PHPStan", - "phpdoc", - "symfony", - "type" - ], - "support": { - "source": "https://github.com/symfony/type-info/tree/v7.4.9" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-04-22T15:21:55+00:00" - }, { "name": "symfony/uid", "version": "v7.4.9", @@ -6205,82 +5872,6 @@ ], "time": "2026-03-30T13:44:50+00:00" }, - { - "name": "symfony/yaml", - "version": "v7.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c", - "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-05-25T06:06:12+00:00" - }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "v2.4.0", @@ -6493,99 +6084,89 @@ } ], "time": "2026-04-26T05:33:54+00:00" - }, + } + ], + "packages-dev": [ { - "name": "zircote/swagger-php", - "version": "6.2.0", + "name": "dedoc/scramble", + "version": "v0.13.28", "source": { "type": "git", - "url": "https://github.com/zircote/swagger-php.git", - "reference": "060af3bb9c4cba6a5859aba2c51cd1c129479410" + "url": "https://github.com/dedoc/scramble.git", + "reference": "e50927c732a341bb743671066892eec6bd2e958b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/060af3bb9c4cba6a5859aba2c51cd1c129479410", - "reference": "060af3bb9c4cba6a5859aba2c51cd1c129479410", + "url": "https://api.github.com/repos/dedoc/scramble/zipball/e50927c732a341bb743671066892eec6bd2e958b", + "reference": "e50927c732a341bb743671066892eec6bd2e958b", "shasum": "" }, "require": { - "nikic/php-parser": "^4.19 || ^5.0", - "php": ">=8.2", - "phpstan/phpdoc-parser": "^2.0", - "psr/log": "^1.1 || ^2.0 || ^3.0", - "radebatz/type-info-extras": "^1.0.2", - "symfony/console": "^7.4 || ^8.0", - "symfony/deprecation-contracts": "^2 || ^3", - "symfony/finder": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" - }, - "conflict": { - "symfony/process": ">=6, <6.4.14" + "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0", + "myclabs/deep-copy": "^1.12", + "nikic/php-parser": "^5.0", + "php": "^8.1", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "spatie/laravel-package-tools": "^1.9.2" }, "require-dev": { - "composer/package-versions-deprecated": "^1.11", - "doctrine/annotations": "^2.0", - "friendsofphp/php-cs-fixer": "^3.62.0", - "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^11.5 || >=12.5.22", - "rector/rector": "^2.3.1" + "larastan/larastan": "^3.3", + "laravel/pint": "^v1.1.0", + "nunomaduro/collision": "^7.0|^8.0", + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.34|^3.7|^4.4", + "pestphp/pest-plugin-laravel": "^2.3|^3.1|^4.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5|^11.5.3|^12.5.12", + "spatie/laravel-permission": "^6.10|^7.2", + "spatie/pest-plugin-snapshots": "^2.1" }, - "bin": [ - "bin/openapi" - ], "type": "library", "extra": { - "branch-alias": { - "dev-master": "6.x-dev" + "laravel": { + "providers": [ + "Dedoc\\Scramble\\ScrambleServiceProvider" + ] } }, "autoload": { "psr-4": { - "OpenApi\\": "src" + "Dedoc\\Scramble\\": "src", + "Dedoc\\Scramble\\Database\\Factories\\": "database/factories" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { - "name": "Robert Allen", - "email": "zircote@gmail.com" - }, - { - "name": "Bob Fanger", - "email": "bfanger@gmail.com", - "homepage": "https://bfanger.nl" - }, - { - "name": "Martin Rademacher", - "email": "mano@radebatz.net", - "homepage": "https://radebatz.net" + "name": "Roman Lytvynenko", + "email": "litvinenko95@gmail.com", + "role": "Developer" } ], - "description": "Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations", - "homepage": "https://github.com/zircote/swagger-php", + "description": "Automatic generation of API documentation for Laravel applications.", + "homepage": "https://github.com/dedoc/scramble", "keywords": [ - "api", - "json", - "rest", - "service discovery" + "documentation", + "laravel", + "openapi" ], "support": { - "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/6.2.0" + "issues": "https://github.com/dedoc/scramble/issues", + "source": "https://github.com/dedoc/scramble/tree/v0.13.28" }, "funding": [ { - "url": "https://github.com/zircote", + "url": "https://github.com/romalytvynenko", "type": "github" } ], - "time": "2026-06-14T06:51:56+00:00" - } - ], - "packages-dev": [ + "time": "2026-06-14T18:21:12+00:00" + }, { "name": "fakerphp/faker", "version": "v1.24.1", @@ -7339,6 +6920,53 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" + }, + "time": "2026-01-25T14:56:51+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "11.0.12", @@ -8782,6 +8410,67 @@ ], "time": "2024-10-09T05:16:32+00:00" }, + { + "name": "spatie/laravel-package-tools", + "version": "1.93.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "d5552849801f2642aea710557463234b59ef65eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/d5552849801f2642aea710557463234b59ef65eb", + "reference": "d5552849801f2642aea710557463234b59ef65eb", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.1" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^8.0|^9.2|^10.0|^11.0", + "pestphp/pest": "^2.1|^3.1|^4.0", + "phpunit/php-code-coverage": "^10.0|^11.0|^12.0", + "phpunit/phpunit": "^10.5|^11.5|^12.5", + "spatie/pest-plugin-test-time": "^2.2|^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.93.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2026-05-19T14:06:37+00:00" + }, { "name": "staabm/side-effects-detector", "version": "1.0.5", @@ -8834,6 +8523,82 @@ ], "time": "2024-10-20T05:08:20+00:00" }, + { + "name": "symfony/yaml", + "version": "v7.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c", + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-25T06:06:12+00:00" + }, { "name": "theseer/tokenizer", "version": "1.3.1", diff --git a/config/l5-swagger.php b/config/l5-swagger.php deleted file mode 100644 index ff4c8a43..00000000 --- a/config/l5-swagger.php +++ /dev/null @@ -1,333 +0,0 @@ - 'default', - 'documentations' => [ - 'default' => [ - 'api' => [ - 'title' => 'L5 Swagger UI', - ], - - 'routes' => [ - /* - * Route for accessing api documentation interface - */ - 'api' => 'api/documentation', - ], - 'paths' => [ - /* - * Edit to include full URL in ui for assets - */ - 'use_absolute_path' => env('L5_SWAGGER_USE_ABSOLUTE_PATH', true), - - /* - * Edit to set path where swagger ui assets should be stored - */ - 'swagger_ui_assets_path' => env('L5_SWAGGER_UI_ASSETS_PATH', 'vendor/swagger-api/swagger-ui/dist/'), - - /* - * File name of the generated json documentation file - */ - 'docs_json' => 'api-docs.json', - - /* - * File name of the generated YAML documentation file - */ - 'docs_yaml' => 'api-docs.yaml', - - /* - * Set this to `json` or `yaml` to determine which documentation file to use in UI - */ - 'format_to_use_for_docs' => env('L5_FORMAT_TO_USE_FOR_DOCS', 'json'), - - /* - * Absolute paths to directory containing the swagger annotations are stored. - */ - 'annotations' => [ - base_path('app'), - ], - ], - ], - ], - 'defaults' => [ - 'routes' => [ - /* - * Route for accessing parsed swagger annotations. - */ - 'docs' => 'docs', - - /* - * Route for Oauth2 authentication callback. - */ - 'oauth2_callback' => 'api/oauth2-callback', - - /* - * Middleware allows to prevent unexpected access to API documentation - */ - 'middleware' => [ - 'api' => [], - 'asset' => [], - 'docs' => [], - 'oauth2_callback' => [], - ], - - /* - * Route Group options - */ - 'group_options' => [], - ], - - 'paths' => [ - /* - * Absolute path to location where parsed annotations will be stored - */ - 'docs' => storage_path('api-docs'), - - /* - * Absolute path to directory where to export views - */ - 'views' => base_path('resources/views/vendor/l5-swagger'), - - /* - * Edit to set the api's base path - */ - 'base' => env('L5_SWAGGER_BASE_PATH', null), - - /* - * Absolute path to directories that should be excluded from scanning - * @deprecated Please use `scanOptions.exclude` - * `scanOptions.exclude` overwrites this - */ - 'excludes' => [], - ], - - 'scanOptions' => [ - /** - * Optional CustomGeneratorInterface implementation that creates an OpenApi\Generator instance. - * Use this to provide a custom pre-configured generator. - * Accepts an instance or a class name (FQCN) implementing the interface. - * - * @see \L5Swagger\CustomGeneratorInterface - */ - 'generator_factory' => null, - - /** - * Configuration for default processors. Allows to pass processors configuration to swagger-php. - * - * @link https://zircote.github.io/swagger-php/reference/processors.html - */ - 'default_processors_configuration' => [ - /** Example */ - /** - * 'operationId.hash' => true, - * 'pathFilter' => [ - * 'tags' => [ - * '/pets/', - * '/store/', - * ], - * ],. - */ - ], - - /** - * analyser: defaults to \OpenApi\StaticAnalyser . - * - * @see \OpenApi\scan - */ - 'analyser' => null, - - /** - * analysis: defaults to a new \OpenApi\Analysis . - * - * @see \OpenApi\scan - */ - 'analysis' => null, - - /** - * Custom processors. - * - * Each entry can be: - * - A class name or instance (inserted after BuildPaths by default) - * - An array with 'class' and 'after' keys for precise positioning: - * ['class' => MyProcessor::class, 'after' => SomeProcessor::class] - * - * @link https://github.com/zircote/swagger-php/tree/master/Examples/processors/schema-query-parameter - * @see \OpenApi\scan - */ - 'processors' => [ - // \App\SwaggerProcessors\SchemaQueryParameter::class, - // ['class' => \App\SwaggerProcessors\Custom::class, 'after' => \OpenApi\Processors\AugmentSchemas::class], - ], - - /** - * pattern: string $pattern File pattern(s) to scan (default: *.php) . - * - * @see \OpenApi\scan - */ - 'pattern' => null, - - /* - * Absolute path to directories that should be excluded from scanning - * @note This option overwrites `paths.excludes` - * @see \OpenApi\scan - */ - 'exclude' => [], - - /* - * Allows to generate specs either for OpenAPI 3.0.0 or OpenAPI 3.1.0. - * By default the spec will be in version 3.0.0 - */ - 'open_api_spec_version' => env('L5_SWAGGER_OPEN_API_SPEC_VERSION', \L5Swagger\Generator::OPEN_API_DEFAULT_SPEC_VERSION), - ], - - /* - * API security definitions. Will be generated into documentation file. - */ - 'securityDefinitions' => [ - 'securitySchemes' => [ - /* - * Examples of Security schemes - */ - /* - 'api_key_security_example' => [ // Unique name of security - 'type' => 'apiKey', // The type of the security scheme. Valid values are "basic", "apiKey" or "oauth2". - 'description' => 'A short description for security scheme', - 'name' => 'api_key', // The name of the header or query parameter to be used. - 'in' => 'header', // The location of the API key. Valid values are "query" or "header". - ], - 'oauth2_security_example' => [ // Unique name of security - 'type' => 'oauth2', // The type of the security scheme. Valid values are "basic", "apiKey" or "oauth2". - 'description' => 'A short description for oauth2 security scheme.', - 'flow' => 'implicit', // The flow used by the OAuth2 security scheme. Valid values are "implicit", "password", "application" or "accessCode". - 'authorizationUrl' => 'http://example.com/auth', // The authorization URL to be used for (implicit/accessCode) - //'tokenUrl' => 'http://example.com/auth' // The authorization URL to be used for (password/application/accessCode) - 'scopes' => [ - 'read:projects' => 'read your projects', - 'write:projects' => 'modify projects in your account', - ] - ], - */ - - /* Open API 3.0 support - 'passport' => [ // Unique name of security - 'type' => 'oauth2', // The type of the security scheme. Valid values are "basic", "apiKey" or "oauth2". - 'description' => 'Laravel passport oauth2 security.', - 'in' => 'header', - 'scheme' => 'https', - 'flows' => [ - "password" => [ - "authorizationUrl" => config('app.url') . '/oauth/authorize', - "tokenUrl" => config('app.url') . '/oauth/token', - "refreshUrl" => config('app.url') . '/token/refresh', - "scopes" => [] - ], - ], - ], - 'sanctum' => [ // Unique name of security - 'type' => 'apiKey', // Valid values are "basic", "apiKey" or "oauth2". - 'description' => 'Enter token in format (Bearer )', - 'name' => 'Authorization', // The name of the header or query parameter to be used. - 'in' => 'header', // The location of the API key. Valid values are "query" or "header". - ], - */ - ], - 'security' => [ - /* - * Examples of Securities - */ - [ - /* - 'oauth2_security_example' => [ - 'read', - 'write' - ], - - 'passport' => [] - */ - ], - ], - ], - - /* - * Set this to `true` in development mode so that docs would be regenerated on each request - * Set this to `false` to disable swagger generation on production - */ - 'generate_always' => env('L5_SWAGGER_GENERATE_ALWAYS', false), - - /* - * Set this to `true` to generate a copy of documentation in yaml format - */ - 'generate_yaml_copy' => env('L5_SWAGGER_GENERATE_YAML_COPY', false), - - /* - * Edit to trust the proxy's ip address - needed for AWS Load Balancer - * string[] - */ - 'proxy' => false, - - /* - * Configs plugin allows to fetch external configs instead of passing them to SwaggerUIBundle. - * See more at: https://github.com/swagger-api/swagger-ui#configs-plugin - */ - 'additional_config_url' => null, - - /* - * Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), - * 'method' (sort by HTTP method). - * Default is the order returned by the server unchanged. - */ - 'operations_sort' => env('L5_SWAGGER_OPERATIONS_SORT', null), - - /* - * Pass the validatorUrl parameter to SwaggerUi init on the JS side. - * A null value here disables validation. - */ - 'validator_url' => null, - - /* - * Swagger UI configuration parameters - */ - 'ui' => [ - 'display' => [ - 'dark_mode' => env('L5_SWAGGER_UI_DARK_MODE', false), - /* - * Controls the default expansion setting for the operations and tags. It can be : - * 'list' (expands only the tags), - * 'full' (expands the tags and operations), - * 'none' (expands nothing). - */ - 'doc_expansion' => env('L5_SWAGGER_UI_DOC_EXPANSION', 'none'), - - /** - * If set, enables filtering. The top bar will show an edit box that - * you can use to filter the tagged operations that are shown. Can be - * Boolean to enable or disable, or a string, in which case filtering - * will be enabled using that string as the filter expression. Filtering - * is case-sensitive matching the filter expression anywhere inside - * the tag. - */ - 'filter' => env('L5_SWAGGER_UI_FILTERS', true), // true | false - ], - - 'authorization' => [ - /* - * If set to true, it persists authorization data, and it would not be lost on browser close/refresh - */ - 'persist_authorization' => env('L5_SWAGGER_UI_PERSIST_AUTHORIZATION', false), - - 'oauth2' => [ - /* - * If set to true, adds PKCE to AuthorizationCodeGrant flow - */ - 'use_pkce_with_authorization_code_grant' => false, - ], - ], - ], - /* - * Constants which can be used in annotations - */ - 'constants' => [ - 'L5_SWAGGER_CONST_HOST' => env('L5_SWAGGER_CONST_HOST', 'http://my-default-host.com'), - ], - ], -]; diff --git a/config/scramble.php b/config/scramble.php new file mode 100644 index 00000000..e2564533 --- /dev/null +++ b/config/scramble.php @@ -0,0 +1,180 @@ + [ + * 'include' => 'api', + * 'exclude' => ['api/internal'], + * ], + * + * Without *, patterns match path segments (api matches api and api/users, not apiary). + * With *, Str::is is used (e.g. api/v*). + * + * One static include → default server is /{include} and paths are stripped (/users). + * Multiple includes or wildcards → server defaults to / and paths stay full (/api/users). + * Override with `servers`, or use Scramble::registerApi() for separate bases. + */ + 'api_path' => 'api', + + /* + * Your API domain. By default, app domain is used. This is also a part of the default API routes + * matcher, so when implementing your own, make sure you use this config if needed. + */ + 'api_domain' => null, + + /* + * The path where your OpenAPI specification will be exported. + */ + 'export_path' => 'api.json', + + /* + * Cache configuration for the generated OpenAPI document. + * + * Use `scramble:cache` to warm the cache and `scramble:clear` to invalidate it. + */ + 'cache' => [ + 'key' => 'scramble.openapi', + 'store' => 'file', + ], + + 'info' => [ + /* + * API version. + */ + 'version' => env('API_VERSION', '0.0.1'), + + /* + * Description rendered on the home page of the API documentation (`/docs/api`). + */ + 'description' => '', + ], + + 'ui' => [ + 'title' => null, + ], + + 'renderer' => 'elements', + + 'renderers' => [ + /* + * Stoplight Elements config options: https://docs.stoplight.io/docs/elements/b074dc47b2826-elements-configuration-options + */ + 'elements' => [ + 'view' => 'scramble::docs', + 'theme' => 'light', + 'hideTryIt' => false, + 'hideSchemas' => false, + 'logo' => '', + 'tryItCredentialsPolicy' => 'include', + 'layout' => 'responsive', + 'router' => 'hash', + ], + /* + * Scalar API reference config options: https://scalar.com/products/api-references/configuration + */ + 'scalar' => [ + 'view' => 'scramble::scalar', + 'cdn' => 'https://cdn.jsdelivr.net/npm/@scalar/api-reference', + 'theme' => 'laravel', + 'proxyUrl' => 'https://proxy.scalar.com', + 'darkMode' => false, + 'showDeveloperTools' => 'never', + 'agent' => ['disabled' => true], + 'credentials' => 'include', + ], + ], + + /* + * The list of servers of the API. By default, when `null`, server URL will be created from + * `scramble.api_path` and `scramble.api_domain` config variables. When providing an array, you + * will need to specify the local server URL manually (if needed). + * + * Example of non-default config (final URLs are generated using Laravel `url` helper): + * + * ```php + * 'servers' => [ + * 'Live' => 'api', + * 'Prod' => 'https://scramble.dedoc.co/api', + * ], + * ``` + */ + 'servers' => null, + + /** + * Determines how Scramble stores the descriptions of enum cases. + * Available options: + * - 'description' – Case descriptions are stored as the enum schema's description using table formatting. + * - 'extension' – Case descriptions are stored in the `x-enumDescriptions` enum schema extension. + * + * @see https://redocly.com/docs-legacy/api-reference-docs/specification-extensions/x-enum-descriptions + * - false - Case descriptions are ignored. + */ + 'enum_cases_description_strategy' => 'description', + + /** + * Determines how Scramble stores the names of enum cases. + * Available options: + * - 'names' – Case names are stored in the `x-enumNames` enum schema extension. + * - 'varnames' - Case names are stored in the `x-enum-varnames` enum schema extension. + * - false - Case names are not stored. + */ + 'enum_cases_names_strategy' => false, + + /** + * When Scramble encounters deep objects in query parameters, it flattens the parameters so the generated + * OpenAPI document correctly describes the API. Flattening deep query parameters is relevant until + * OpenAPI 3.2 is released and query string structure can be described properly. + * + * For example, this nested validation rule describes the object with `bar` property: + * `['foo.bar' => ['required', 'int']]`. + * + * When `flatten_deep_query_parameters` is `true`, Scramble will document the parameter like so: + * `{"name":"foo[bar]", "schema":{"type":"int"}, "required":true}`. + * + * When `flatten_deep_query_parameters` is `false`, Scramble will document the parameter like so: + * `{"name":"foo", "schema": {"type":"object", "properties":{"bar":{"type": "int"}}, "required": ["bar"]}, "required":true}`. + */ + 'flatten_deep_query_parameters' => true, + + 'middleware' => [ + 'web', + RestrictedDocsAccess::class, + ], + + 'extensions' => [], + + /* + * Automatically document API security (OpenAPI `security` / `securitySchemes`) based on route + * middleware. + * + * Disabled by default. Uncomment the line below to enable `MiddlewareAuthSecurityStrategy`. + * When at least one documented route uses middleware matching the configured patterns (by default + * `auth` and `auth:*`), bearer auth is applied globally. Routes without matching middleware are + * marked as public (`security: []`). + * + * Set to `null` explicitly to disable. If you already configure security manually via + * `afterOpenApiGenerated` / `extendOpenApi`, keep this disabled to avoid duplicate schemes. + * + * Customize with a class-string or [class, options]: + * + * 'security_strategy' => [ + * \Dedoc\Scramble\SecurityDocumentation\MiddlewareAuthSecurityStrategy::class, + * [ + * 'middleware' => ['auth', 'auth:*'], + * 'scheme' => \Dedoc\Scramble\Support\Generator\SecurityScheme::http('bearer'), + * ], + * ], + */ + // 'security_strategy' => \Dedoc\Scramble\SecurityDocumentation\MiddlewareAuthSecurityStrategy::class, + 'security_strategy' => [ + \Dedoc\Scramble\SecurityDocumentation\MiddlewareAuthSecurityStrategy::class, + [ + 'middleware' => ['auth', 'auth:*'], + 'scheme' => \Dedoc\Scramble\Support\Generator\SecurityScheme::http('bearer'), + ], + ], +]; diff --git a/vendor/darkaonline/l5-swagger/resources/views/.gitkeep b/resources/views/vendor/scramble/.gitkeep similarity index 100% rename from vendor/darkaonline/l5-swagger/resources/views/.gitkeep rename to resources/views/vendor/scramble/.gitkeep diff --git a/resources/views/vendor/scramble/docs.blade.php b/resources/views/vendor/scramble/docs.blade.php new file mode 100644 index 00000000..166f5ab8 --- /dev/null +++ b/resources/views/vendor/scramble/docs.blade.php @@ -0,0 +1,112 @@ + + + + + + + {{ $config->get('ui.title') ?? config('app.name') . ' - API Docs' }} + + + + + + + + + +renderer()->all(except: ['theme']) as $key => $value) + @continue(! $value) + {{ $key }}="{{ $value === true ? 'true' : ($value === false ? 'false' : $value) }}" + @endforeach +/> + + +@if($config->renderer()->get('theme', 'light') === 'system') + +@endif + + diff --git a/resources/views/vendor/scramble/scalar.blade.php b/resources/views/vendor/scramble/scalar.blade.php new file mode 100644 index 00000000..b19878fe --- /dev/null +++ b/resources/views/vendor/scramble/scalar.blade.php @@ -0,0 +1,32 @@ + + + + + + {{ $config->get('ui.title') ?? config('app.name') . ' - API Docs' }} + + +
+ + + + + diff --git a/routes/api.php b/routes/api.php index ae9f9d0a..e9f173a9 100644 --- a/routes/api.php +++ b/routes/api.php @@ -3,13 +3,32 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use App\Http\Controllers\AuthController; +use App\Http\Controllers\Ticket\TicketController; +use App\Http\Controllers\Ticket\TicketTypeController; Route::post('/login', [AuthController::class, 'login']); + +// Route::prefix('tickets')->group(function () { +// Route::get('/', [TicketController::class, 'index']); +// Route::post('/', [TicketController::class, 'store']); +// }); + Route::middleware('auth:sanctum')->group(function () { - Route::get('/profile', function (Request $request) { - return response()->json($request->user()); - }); + Route::apiResource('tickets', TicketController::class); + Route::apiResource('ticket-types', TicketTypeController::class); -}); \ No newline at end of file +}); + + + +// Route::get('/generate-token', function () { + +// $user = \App\Models\User::find(1); + +// return $user->createToken( +// 'master-token' +// )->plainTextToken; + +// }); diff --git a/vendor/bin/openapi b/vendor/bin/openapi deleted file mode 100644 index 9f74b06f..00000000 --- a/vendor/bin/openapi +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env php -realpath = realpath($opened_path) ?: $opened_path; - $opened_path = $this->realpath; - $this->handle = fopen($this->realpath, $mode); - $this->position = 0; - - return (bool) $this->handle; - } - - public function stream_read($count) - { - $data = fread($this->handle, $count); - - if ($this->position === 0) { - $data = preg_replace('{^#!.*\r?\n}', '', $data); - } - - $this->position += strlen($data); - - return $data; - } - - public function stream_cast($castAs) - { - return $this->handle; - } - - public function stream_close() - { - fclose($this->handle); - } - - public function stream_lock($operation) - { - return $operation ? flock($this->handle, $operation) : true; - } - - public function stream_seek($offset, $whence) - { - if (0 === fseek($this->handle, $offset, $whence)) { - $this->position = ftell($this->handle); - return true; - } - - return false; - } - - public function stream_tell() - { - return $this->position; - } - - public function stream_eof() - { - return feof($this->handle); - } - - public function stream_stat() - { - return array(); - } - - public function stream_set_option($option, $arg1, $arg2) - { - return true; - } - - public function url_stat($path, $flags) - { - $path = substr($path, 17); - if (file_exists($path)) { - return stat($path); - } - - return false; - } - } - } - - if ( - (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) - || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) - ) { - return include("phpvfscomposer://" . __DIR__ . '/..'.'/zircote/swagger-php/bin/openapi'); - } -} - -return include __DIR__ . '/..'.'/zircote/swagger-php/bin/openapi'; diff --git a/vendor/bin/openapi.bat b/vendor/bin/openapi.bat deleted file mode 100644 index ae4fbbdd..00000000 --- a/vendor/bin/openapi.bat +++ /dev/null @@ -1,5 +0,0 @@ -@ECHO OFF -setlocal DISABLEDELAYEDEXPANSION -SET BIN_TARGET=%~dp0/openapi -SET COMPOSER_RUNTIME_BIN_DIR=%~dp0 -php "%BIN_TARGET%" %* diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index a18f742c..c22f3b4f 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -7,10 +7,25 @@ $baseDir = dirname($vendorDir); return array( 'App\\Docs\\OpenApiSpec' => $baseDir . '/app/Docs/OpenApiSpec.php', + 'App\\Http\\Controllers\\ApiController' => $baseDir . '/app/Http/Controllers/ApiController.php', 'App\\Http\\Controllers\\AuthController' => $baseDir . '/app/Http/Controllers/AuthController.php', 'App\\Http\\Controllers\\Controller' => $baseDir . '/app/Http/Controllers/Controller.php', + 'App\\Http\\Controllers\\TicketController' => $baseDir . '/app/Http/Controllers/TicketController.php', + 'App\\Http\\Controllers\\Ticket\\TicketController' => $baseDir . '/app/Http/Controllers/Ticket/TicketController.php', + 'App\\Http\\Requests\\Ticket\\StoreTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/StoreTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\UpdateTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/UpdateTicketRequest.php', + 'App\\Http\\Resources\\TicketResource' => $baseDir . '/app/Http/Resources/TicketResource.php', + 'App\\Models\\Ticket' => $baseDir . '/app/Models/Ticket.php', + 'App\\Models\\TicketApproval' => $baseDir . '/app/Models/TicketApproval.php', + 'App\\Models\\TicketAssignment' => $baseDir . '/app/Models/TicketAssignment.php', + 'App\\Models\\TicketFile' => $baseDir . '/app/Models/TicketFile.php', + 'App\\Models\\TicketLog' => $baseDir . '/app/Models/TicketLog.php', + 'App\\Models\\TicketMaterial' => $baseDir . '/app/Models/TicketMaterial.php', + 'App\\Models\\TicketType' => $baseDir . '/app/Models/TicketType.php', 'App\\Models\\User' => $baseDir . '/app/Models/User.php', 'App\\Providers\\AppServiceProvider' => $baseDir . '/app/Providers/AppServiceProvider.php', + 'App\\Services\\Ticket\\TicketService' => $baseDir . '/app/Services/Ticket/TicketService.php', + 'App\\Traits\\ApiResponse' => $baseDir . '/app/Traits/ApiResponse.php', 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Brick\\Math\\BigDecimal' => $vendorDir . '/brick/math/src/BigDecimal.php', 'Brick\\Math\\BigInteger' => $vendorDir . '/brick/math/src/BigInteger.php', @@ -145,6 +160,425 @@ return array( 'DateMalformedStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php', 'DateObjectError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php', 'DateRangeError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php', + 'Dedoc\\Scramble\\AbstractOpenApiVisitor' => $vendorDir . '/dedoc/scramble/src/AbstractOpenApiVisitor.php', + 'Dedoc\\Scramble\\Attributes\\BodyParameter' => $vendorDir . '/dedoc/scramble/src/Attributes/BodyParameter.php', + 'Dedoc\\Scramble\\Attributes\\CookieParameter' => $vendorDir . '/dedoc/scramble/src/Attributes/CookieParameter.php', + 'Dedoc\\Scramble\\Attributes\\Endpoint' => $vendorDir . '/dedoc/scramble/src/Attributes/Endpoint.php', + 'Dedoc\\Scramble\\Attributes\\Example' => $vendorDir . '/dedoc/scramble/src/Attributes/Example.php', + 'Dedoc\\Scramble\\Attributes\\ExcludeAllRoutesFromDocs' => $vendorDir . '/dedoc/scramble/src/Attributes/ExcludeAllRoutesFromDocs.php', + 'Dedoc\\Scramble\\Attributes\\ExcludeRouteFromDocs' => $vendorDir . '/dedoc/scramble/src/Attributes/ExcludeRouteFromDocs.php', + 'Dedoc\\Scramble\\Attributes\\Group' => $vendorDir . '/dedoc/scramble/src/Attributes/Group.php', + 'Dedoc\\Scramble\\Attributes\\Header' => $vendorDir . '/dedoc/scramble/src/Attributes/Header.php', + 'Dedoc\\Scramble\\Attributes\\HeaderParameter' => $vendorDir . '/dedoc/scramble/src/Attributes/HeaderParameter.php', + 'Dedoc\\Scramble\\Attributes\\Hidden' => $vendorDir . '/dedoc/scramble/src/Attributes/Hidden.php', + 'Dedoc\\Scramble\\Attributes\\IgnoreParam' => $vendorDir . '/dedoc/scramble/src/Attributes/IgnoreParam.php', + 'Dedoc\\Scramble\\Attributes\\MissingValue' => $vendorDir . '/dedoc/scramble/src/Attributes/MissingValue.php', + 'Dedoc\\Scramble\\Attributes\\Parameter' => $vendorDir . '/dedoc/scramble/src/Attributes/Parameter.php', + 'Dedoc\\Scramble\\Attributes\\PathParameter' => $vendorDir . '/dedoc/scramble/src/Attributes/PathParameter.php', + 'Dedoc\\Scramble\\Attributes\\QueryParameter' => $vendorDir . '/dedoc/scramble/src/Attributes/QueryParameter.php', + 'Dedoc\\Scramble\\Attributes\\Response' => $vendorDir . '/dedoc/scramble/src/Attributes/Response.php', + 'Dedoc\\Scramble\\Attributes\\SchemaName' => $vendorDir . '/dedoc/scramble/src/Attributes/SchemaName.php', + 'Dedoc\\Scramble\\CacheableGenerator' => $vendorDir . '/dedoc/scramble/src/CacheableGenerator.php', + 'Dedoc\\Scramble\\Configuration\\ApiPath' => $vendorDir . '/dedoc/scramble/src/Configuration/ApiPath.php', + 'Dedoc\\Scramble\\Configuration\\DocumentTransformers' => $vendorDir . '/dedoc/scramble/src/Configuration/DocumentTransformers.php', + 'Dedoc\\Scramble\\Configuration\\GeneratorConfigCollection' => $vendorDir . '/dedoc/scramble/src/Configuration/GeneratorConfigCollection.php', + 'Dedoc\\Scramble\\Configuration\\InferConfig' => $vendorDir . '/dedoc/scramble/src/Configuration/InferConfig.php', + 'Dedoc\\Scramble\\Configuration\\JsonApiConfig' => $vendorDir . '/dedoc/scramble/src/Configuration/JsonApiConfig.php', + 'Dedoc\\Scramble\\Configuration\\OperationTransformers' => $vendorDir . '/dedoc/scramble/src/Configuration/OperationTransformers.php', + 'Dedoc\\Scramble\\Configuration\\ParametersExtractors' => $vendorDir . '/dedoc/scramble/src/Configuration/ParametersExtractors.php', + 'Dedoc\\Scramble\\Configuration\\RendererConfig' => $vendorDir . '/dedoc/scramble/src/Configuration/RendererConfig.php', + 'Dedoc\\Scramble\\Configuration\\RuleTransformers' => $vendorDir . '/dedoc/scramble/src/Configuration/RuleTransformers.php', + 'Dedoc\\Scramble\\Configuration\\SecurityDocumentationContext' => $vendorDir . '/dedoc/scramble/src/Configuration/SecurityDocumentationContext.php', + 'Dedoc\\Scramble\\Configuration\\ServerVariables' => $vendorDir . '/dedoc/scramble/src/Configuration/ServerVariables.php', + 'Dedoc\\Scramble\\Console\\Commands\\AnalyzeDocumentation' => $vendorDir . '/dedoc/scramble/src/Console/Commands/AnalyzeDocumentation.php', + 'Dedoc\\Scramble\\Console\\Commands\\CacheDocumentation' => $vendorDir . '/dedoc/scramble/src/Console/Commands/CacheDocumentation.php', + 'Dedoc\\Scramble\\Console\\Commands\\ClearDocumentationCache' => $vendorDir . '/dedoc/scramble/src/Console/Commands/ClearDocumentationCache.php', + 'Dedoc\\Scramble\\Console\\Commands\\Components\\Code' => $vendorDir . '/dedoc/scramble/src/Console/Commands/Components/Code.php', + 'Dedoc\\Scramble\\Console\\Commands\\Components\\Component' => $vendorDir . '/dedoc/scramble/src/Console/Commands/Components/Component.php', + 'Dedoc\\Scramble\\Console\\Commands\\Components\\TermsOfContentItem' => $vendorDir . '/dedoc/scramble/src/Console/Commands/Components/TermsOfContentItem.php', + 'Dedoc\\Scramble\\Console\\Commands\\Concerns\\ManagesDocumentationCache' => $vendorDir . '/dedoc/scramble/src/Console/Commands/Concerns/ManagesDocumentationCache.php', + 'Dedoc\\Scramble\\Console\\Commands\\ExportDocumentation' => $vendorDir . '/dedoc/scramble/src/Console/Commands/ExportDocumentation.php', + 'Dedoc\\Scramble\\ContextReferences' => $vendorDir . '/dedoc/scramble/src/ContextReferences.php', + 'Dedoc\\Scramble\\ContextReferencesCollection' => $vendorDir . '/dedoc/scramble/src/ContextReferencesCollection.php', + 'Dedoc\\Scramble\\Contracts\\AllRulesSchemasTransformer' => $vendorDir . '/dedoc/scramble/src/Contracts/AllRulesSchemasTransformer.php', + 'Dedoc\\Scramble\\Contracts\\DocumentTransformer' => $vendorDir . '/dedoc/scramble/src/Contracts/DocumentTransformer.php', + 'Dedoc\\Scramble\\Contracts\\OperationTransformer' => $vendorDir . '/dedoc/scramble/src/Contracts/OperationTransformer.php', + 'Dedoc\\Scramble\\Contracts\\RuleTransformer' => $vendorDir . '/dedoc/scramble/src/Contracts/RuleTransformer.php', + 'Dedoc\\Scramble\\Contracts\\SecurityDocumentationStrategy' => $vendorDir . '/dedoc/scramble/src/Contracts/SecurityDocumentationStrategy.php', + 'Dedoc\\Scramble\\DocumentTransformers\\AddDocumentTags' => $vendorDir . '/dedoc/scramble/src/DocumentTransformers/AddDocumentTags.php', + 'Dedoc\\Scramble\\DocumentTransformers\\CleanupUnusedResponseReferencesTransformer' => $vendorDir . '/dedoc/scramble/src/DocumentTransformers/CleanupUnusedResponseReferencesTransformer.php', + 'Dedoc\\Scramble\\Enums\\JsonApiArraySerialization' => $vendorDir . '/dedoc/scramble/src/Enums/JsonApiArraySerialization.php', + 'Dedoc\\Scramble\\Exceptions\\ConsoleRenderable' => $vendorDir . '/dedoc/scramble/src/Exceptions/ConsoleRenderable.php', + 'Dedoc\\Scramble\\Exceptions\\InvalidSchema' => $vendorDir . '/dedoc/scramble/src/Exceptions/InvalidSchema.php', + 'Dedoc\\Scramble\\Exceptions\\OpenApiReferenceTargetNotFoundException' => $vendorDir . '/dedoc/scramble/src/Exceptions/OpenApiReferenceTargetNotFoundException.php', + 'Dedoc\\Scramble\\Exceptions\\RouteAware' => $vendorDir . '/dedoc/scramble/src/Exceptions/RouteAware.php', + 'Dedoc\\Scramble\\Exceptions\\RouteAwareTrait' => $vendorDir . '/dedoc/scramble/src/Exceptions/RouteAwareTrait.php', + 'Dedoc\\Scramble\\Exceptions\\RulesEvaluationException' => $vendorDir . '/dedoc/scramble/src/Exceptions/RulesEvaluationException.php', + 'Dedoc\\Scramble\\Extensions\\ExceptionToResponseExtension' => $vendorDir . '/dedoc/scramble/src/Extensions/ExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Extensions\\OperationExtension' => $vendorDir . '/dedoc/scramble/src/Extensions/OperationExtension.php', + 'Dedoc\\Scramble\\Extensions\\TypeToSchemaExtension' => $vendorDir . '/dedoc/scramble/src/Extensions/TypeToSchemaExtension.php', + 'Dedoc\\Scramble\\Generator' => $vendorDir . '/dedoc/scramble/src/Generator.php', + 'Dedoc\\Scramble\\GeneratorConfig' => $vendorDir . '/dedoc/scramble/src/GeneratorConfig.php', + 'Dedoc\\Scramble\\Http\\Middleware\\RestrictedDocsAccess' => $vendorDir . '/dedoc/scramble/src/Http/Middleware/RestrictedDocsAccess.php', + 'Dedoc\\Scramble\\Infer' => $vendorDir . '/dedoc/scramble/src/Infer.php', + 'Dedoc\\Scramble\\Infer\\Analyzer\\ClassAnalyzer' => $vendorDir . '/dedoc/scramble/src/Infer/Analyzer/ClassAnalyzer.php', + 'Dedoc\\Scramble\\Infer\\Analyzer\\MethodAnalyzer' => $vendorDir . '/dedoc/scramble/src/Infer/Analyzer/MethodAnalyzer.php', + 'Dedoc\\Scramble\\Infer\\Analyzer\\PropertyAnalyzer' => $vendorDir . '/dedoc/scramble/src/Infer/Analyzer/PropertyAnalyzer.php', + 'Dedoc\\Scramble\\Infer\\AutoResolvingArgumentTypeBag' => $vendorDir . '/dedoc/scramble/src/Infer/AutoResolvingArgumentTypeBag.php', + 'Dedoc\\Scramble\\Infer\\Configuration\\ClassLike' => $vendorDir . '/dedoc/scramble/src/Infer/Configuration/ClassLike.php', + 'Dedoc\\Scramble\\Infer\\Configuration\\ClassLikeAndChildren' => $vendorDir . '/dedoc/scramble/src/Infer/Configuration/ClassLikeAndChildren.php', + 'Dedoc\\Scramble\\Infer\\Configuration\\DefinitionMatcher' => $vendorDir . '/dedoc/scramble/src/Infer/Configuration/DefinitionMatcher.php', + 'Dedoc\\Scramble\\Infer\\Context' => $vendorDir . '/dedoc/scramble/src/Infer/Context.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\ArgumentTypeBag' => $vendorDir . '/dedoc/scramble/src/Infer/Contracts/ArgumentTypeBag.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\ClassDefinition' => $vendorDir . '/dedoc/scramble/src/Infer/Contracts/ClassDefinition.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\ClassDefinitionBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/Contracts/ClassDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\FunctionLikeDefinitionBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/Contracts/FunctionLikeDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\Index' => $vendorDir . '/dedoc/scramble/src/Infer/Contracts/Index.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\FunctionLikeAstDefinitionBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeAstDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\FunctionLikeDeclarationAstDefinitionBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationAstDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\FunctionLikeDeclarationPhpDocDefinitionBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationPhpDocDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\FunctionLikeReflectionDefinitionBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeReflectionDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\LazyClassReflectionDefinitionBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/DefinitionBuilders/LazyClassReflectionDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\SelfOutTypeBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/DefinitionBuilders/SelfOutTypeBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\ShallowClassReflectionDefinitionBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/DefinitionBuilders/ShallowClassReflectionDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\Definition\\AttributeDefinition' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/AttributeDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/ClassDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/ClassPropertyDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\FunctionLikeAstDefinition' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/FunctionLikeAstDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\FunctionLikeDefinition' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/FunctionLikeDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\LazyShallowClassDefinition' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/LazyShallowClassDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\PendingDocComment' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/PendingDocComment.php', + 'Dedoc\\Scramble\\Infer\\Definition\\PropertyVisibility' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/PropertyVisibility.php', + 'Dedoc\\Scramble\\Infer\\Definition\\ShallowClassDefinition' => $vendorDir . '/dedoc/scramble/src/Infer/Definition/ShallowClassDefinition.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\AfterClassDefinitionCreatedExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/AfterClassDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\AfterSideEffectCallAnalyzed' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/AfterSideEffectCallAnalyzed.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\AnyMethodReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/AnyMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\AnyMethodCallEvent' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/AnyMethodCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\ClassDefinitionCreatedEvent' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/ClassDefinitionCreatedEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\Concerns\\ArgumentTypesAware' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/Concerns/ArgumentTypesAware.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\FunctionCallEvent' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/FunctionCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\MethodCallEvent' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/MethodCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\PropertyFetchEvent' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/PropertyFetchEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\ReferenceResolutionEvent' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/ReferenceResolutionEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\SideEffectCallEvent' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/SideEffectCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\StaticMethodCallEvent' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/Event/StaticMethodCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\ExpressionExceptionExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/ExpressionExceptionExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\ExpressionTypeInferExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/ExpressionTypeInferExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\ExtensionsBroker' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/ExtensionsBroker.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\FunctionReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/FunctionReturnTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\IndexBuildingBroker' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/IndexBuildingBroker.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\InferExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/InferExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\MethodCallExceptionsExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/MethodCallExceptionsExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\MethodReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/MethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\PropertyTypeExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/PropertyTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\ResolvingType' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/ResolvingType.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\StaticMethodReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/StaticMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\TypeResolverExtension' => $vendorDir . '/dedoc/scramble/src/Infer/Extensions/TypeResolverExtension.php', + 'Dedoc\\Scramble\\Infer\\FlowBuilder' => $vendorDir . '/dedoc/scramble/src/Infer/FlowBuilder.php', + 'Dedoc\\Scramble\\Infer\\Flow\\AbstractNode' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/AbstractNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\ConditionNode' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/ConditionNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\Edge' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/Edge.php', + 'Dedoc\\Scramble\\Infer\\Flow\\ExpressionTypeInferrer' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/ExpressionTypeInferrer.php', + 'Dedoc\\Scramble\\Infer\\Flow\\MergeNode' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/MergeNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\Node' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/Node.php', + 'Dedoc\\Scramble\\Infer\\Flow\\Nodes' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/Nodes.php', + 'Dedoc\\Scramble\\Infer\\Flow\\StartNode' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/StartNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\StatementNode' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/StatementNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\TerminateNode' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/TerminateNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\TerminationKind' => $vendorDir . '/dedoc/scramble/src/Infer/Flow/TerminationKind.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ArrayHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/ArrayHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ArrayItemHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/ArrayItemHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\AssignHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/AssignHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ClassHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/ClassHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\CreatesScope' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/CreatesScope.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ExceptionInferringExtensions' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/ExceptionInferringExtensions.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ExpressionTypeInferringExtensions' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/ExpressionTypeInferringExtensions.php', + 'Dedoc\\Scramble\\Infer\\Handler\\FunctionLikeHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/FunctionLikeHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\IndexBuildingHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/IndexBuildingHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\PhpDocHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/PhpDocHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\PropertyHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/PropertyHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ReturnHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/ReturnHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ThrowHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/ThrowHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\UnsetHandler' => $vendorDir . '/dedoc/scramble/src/Infer/Handler/UnsetHandler.php', + 'Dedoc\\Scramble\\Infer\\Reflector\\ClassReflector' => $vendorDir . '/dedoc/scramble/src/Infer/Reflector/ClassReflector.php', + 'Dedoc\\Scramble\\Infer\\Reflector\\ClosureReflector' => $vendorDir . '/dedoc/scramble/src/Infer/Reflector/ClosureReflector.php', + 'Dedoc\\Scramble\\Infer\\Reflector\\MethodReflector' => $vendorDir . '/dedoc/scramble/src/Infer/Reflector/MethodReflector.php', + 'Dedoc\\Scramble\\Infer\\Reflector\\PropertyReflector' => $vendorDir . '/dedoc/scramble/src/Infer/Reflector/PropertyReflector.php', + 'Dedoc\\Scramble\\Infer\\Scope\\GlobalScope' => $vendorDir . '/dedoc/scramble/src/Infer/Scope/GlobalScope.php', + 'Dedoc\\Scramble\\Infer\\Scope\\Index' => $vendorDir . '/dedoc/scramble/src/Infer/Scope/Index.php', + 'Dedoc\\Scramble\\Infer\\Scope\\LazyShallowReflectionIndex' => $vendorDir . '/dedoc/scramble/src/Infer/Scope/LazyShallowReflectionIndex.php', + 'Dedoc\\Scramble\\Infer\\Scope\\NodeTypesResolver' => $vendorDir . '/dedoc/scramble/src/Infer/Scope/NodeTypesResolver.php', + 'Dedoc\\Scramble\\Infer\\Scope\\Scope' => $vendorDir . '/dedoc/scramble/src/Infer/Scope/Scope.php', + 'Dedoc\\Scramble\\Infer\\Scope\\ScopeContext' => $vendorDir . '/dedoc/scramble/src/Infer/Scope/ScopeContext.php', + 'Dedoc\\Scramble\\Infer\\Services\\ConstFetchTypeGetter' => $vendorDir . '/dedoc/scramble/src/Infer/Services/ConstFetchTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\Services\\FileNameResolver' => $vendorDir . '/dedoc/scramble/src/Infer/Services/FileNameResolver.php', + 'Dedoc\\Scramble\\Infer\\Services\\FileParser' => $vendorDir . '/dedoc/scramble/src/Infer/Services/FileParser.php', + 'Dedoc\\Scramble\\Infer\\Services\\FileParserResult' => $vendorDir . '/dedoc/scramble/src/Infer/Services/FileParserResult.php', + 'Dedoc\\Scramble\\Infer\\Services\\KeyedArrayUnpackingTypeVisitor' => $vendorDir . '/dedoc/scramble/src/Infer/Services/KeyedArrayUnpackingTypeVisitor.php', + 'Dedoc\\Scramble\\Infer\\Services\\LateTypeResolvingTypeVisitor' => $vendorDir . '/dedoc/scramble/src/Infer/Services/LateTypeResolvingTypeVisitor.php', + 'Dedoc\\Scramble\\Infer\\Services\\RecursionGuard' => $vendorDir . '/dedoc/scramble/src/Infer/Services/RecursionGuard.php', + 'Dedoc\\Scramble\\Infer\\Services\\ReferenceTypeResolver' => $vendorDir . '/dedoc/scramble/src/Infer/Services/ReferenceTypeResolver.php', + 'Dedoc\\Scramble\\Infer\\Services\\ShallowTypeResolver' => $vendorDir . '/dedoc/scramble/src/Infer/Services/ShallowTypeResolver.php', + 'Dedoc\\Scramble\\Infer\\Services\\TemplateTypesSolver' => $vendorDir . '/dedoc/scramble/src/Infer/Services/TemplateTypesSolver.php', + 'Dedoc\\Scramble\\Infer\\Services\\TemplatesMap' => $vendorDir . '/dedoc/scramble/src/Infer/Services/TemplatesMap.php', + 'Dedoc\\Scramble\\Infer\\Services\\UnionNormalizingTypeVisitor' => $vendorDir . '/dedoc/scramble/src/Infer/Services/UnionNormalizingTypeVisitor.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\BooleanNotTypeGetter' => $vendorDir . '/dedoc/scramble/src/Infer/SimpleTypeGetters/BooleanNotTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\CastTypeGetter' => $vendorDir . '/dedoc/scramble/src/Infer/SimpleTypeGetters/CastTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\ClassConstFetchTypeGetter' => $vendorDir . '/dedoc/scramble/src/Infer/SimpleTypeGetters/ClassConstFetchTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\ConstFetchTypeGetter' => $vendorDir . '/dedoc/scramble/src/Infer/SimpleTypeGetters/ConstFetchTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\ScalarTypeGetter' => $vendorDir . '/dedoc/scramble/src/Infer/SimpleTypeGetters/ScalarTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\TypeInferer' => $vendorDir . '/dedoc/scramble/src/Infer/TypeInferer.php', + 'Dedoc\\Scramble\\Infer\\UnresolvableArgumentTypeBag' => $vendorDir . '/dedoc/scramble/src/Infer/UnresolvableArgumentTypeBag.php', + 'Dedoc\\Scramble\\Infer\\Visitors\\PhpDocResolver' => $vendorDir . '/dedoc/scramble/src/Infer/Visitors/PhpDocResolver.php', + 'Dedoc\\Scramble\\OpenApiContext' => $vendorDir . '/dedoc/scramble/src/OpenApiContext.php', + 'Dedoc\\Scramble\\OpenApiTraverser' => $vendorDir . '/dedoc/scramble/src/OpenApiTraverser.php', + 'Dedoc\\Scramble\\OpenApiVisitor' => $vendorDir . '/dedoc/scramble/src/OpenApiVisitor.php', + 'Dedoc\\Scramble\\OpenApiVisitor\\SchemaEnforceVisitor' => $vendorDir . '/dedoc/scramble/src/OpenApiVisitor/SchemaEnforceVisitor.php', + 'Dedoc\\Scramble\\PhpDoc\\AbstractPhpDocTypeVisitor' => $vendorDir . '/dedoc/scramble/src/PhpDoc/AbstractPhpDocTypeVisitor.php', + 'Dedoc\\Scramble\\PhpDoc\\PhpDocParser' => $vendorDir . '/dedoc/scramble/src/PhpDoc/PhpDocParser.php', + 'Dedoc\\Scramble\\PhpDoc\\PhpDocTypeHelper' => $vendorDir . '/dedoc/scramble/src/PhpDoc/PhpDocTypeHelper.php', + 'Dedoc\\Scramble\\PhpDoc\\PhpDocTypeVisitor' => $vendorDir . '/dedoc/scramble/src/PhpDoc/PhpDocTypeVisitor.php', + 'Dedoc\\Scramble\\PhpDoc\\PhpDocTypeWalker' => $vendorDir . '/dedoc/scramble/src/PhpDoc/PhpDocTypeWalker.php', + 'Dedoc\\Scramble\\PhpDoc\\ResolveFqnPhpDocTypeVisitor' => $vendorDir . '/dedoc/scramble/src/PhpDoc/ResolveFqnPhpDocTypeVisitor.php', + 'Dedoc\\Scramble\\Reflection\\JsonApiRelationship' => $vendorDir . '/dedoc/scramble/src/Reflection/JsonApiRelationship.php', + 'Dedoc\\Scramble\\Reflection\\ReflectionJsonApiResource' => $vendorDir . '/dedoc/scramble/src/Reflection/ReflectionJsonApiResource.php', + 'Dedoc\\Scramble\\Reflection\\ReflectionModel' => $vendorDir . '/dedoc/scramble/src/Reflection/ReflectionModel.php', + 'Dedoc\\Scramble\\Reflection\\ReflectionRoute' => $vendorDir . '/dedoc/scramble/src/Reflection/ReflectionRoute.php', + 'Dedoc\\Scramble\\RuleTransformers\\ConfirmedRule' => $vendorDir . '/dedoc/scramble/src/RuleTransformers/ConfirmedRule.php', + 'Dedoc\\Scramble\\RuleTransformers\\EnumRule' => $vendorDir . '/dedoc/scramble/src/RuleTransformers/EnumRule.php', + 'Dedoc\\Scramble\\RuleTransformers\\ExistsRule' => $vendorDir . '/dedoc/scramble/src/RuleTransformers/ExistsRule.php', + 'Dedoc\\Scramble\\RuleTransformers\\InRule' => $vendorDir . '/dedoc/scramble/src/RuleTransformers/InRule.php', + 'Dedoc\\Scramble\\RuleTransformers\\RegexRule' => $vendorDir . '/dedoc/scramble/src/RuleTransformers/RegexRule.php', + 'Dedoc\\Scramble\\SchemaValidator' => $vendorDir . '/dedoc/scramble/src/SchemaValidator.php', + 'Dedoc\\Scramble\\Scramble' => $vendorDir . '/dedoc/scramble/src/Scramble.php', + 'Dedoc\\Scramble\\ScrambleServiceProvider' => $vendorDir . '/dedoc/scramble/src/ScrambleServiceProvider.php', + 'Dedoc\\Scramble\\SecurityDocumentation\\MiddlewareAuthSecurityStrategy' => $vendorDir . '/dedoc/scramble/src/SecurityDocumentation/MiddlewareAuthSecurityStrategy.php', + 'Dedoc\\Scramble\\Support\\ContainerUtils' => $vendorDir . '/dedoc/scramble/src/Support/ContainerUtils.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\AuthenticationExceptionToResponseExtension' => $vendorDir . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthenticationExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\AuthorizationExceptionToResponseExtension' => $vendorDir . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthorizationExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\HttpExceptionToResponseExtension' => $vendorDir . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/HttpExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\NotFoundExceptionToResponseExtension' => $vendorDir . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/NotFoundExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\ValidationExceptionToResponseExtension' => $vendorDir . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/ValidationExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\Factories\\JsonApiQueryParameterFactory' => $vendorDir . '/dedoc/scramble/src/Support/Factories/JsonApiQueryParameterFactory.php', + 'Dedoc\\Scramble\\Support\\Generator\\ClassBasedReference' => $vendorDir . '/dedoc/scramble/src/Support/Generator/ClassBasedReference.php', + 'Dedoc\\Scramble\\Support\\Generator\\Combined\\AllOf' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Combined/AllOf.php', + 'Dedoc\\Scramble\\Support\\Generator\\Combined\\AnyOf' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Combined/AnyOf.php', + 'Dedoc\\Scramble\\Support\\Generator\\Components' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Components.php', + 'Dedoc\\Scramble\\Support\\Generator\\Encoding' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Encoding.php', + 'Dedoc\\Scramble\\Support\\Generator\\Example' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Example.php', + 'Dedoc\\Scramble\\Support\\Generator\\Header' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Header.php', + 'Dedoc\\Scramble\\Support\\Generator\\InfoObject' => $vendorDir . '/dedoc/scramble/src/Support/Generator/InfoObject.php', + 'Dedoc\\Scramble\\Support\\Generator\\Link' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Link.php', + 'Dedoc\\Scramble\\Support\\Generator\\MediaType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/MediaType.php', + 'Dedoc\\Scramble\\Support\\Generator\\MissingExample' => $vendorDir . '/dedoc/scramble/src/Support/Generator/MissingExample.php', + 'Dedoc\\Scramble\\Support\\Generator\\MissingValue' => $vendorDir . '/dedoc/scramble/src/Support/Generator/MissingValue.php', + 'Dedoc\\Scramble\\Support\\Generator\\OpenApi' => $vendorDir . '/dedoc/scramble/src/Support/Generator/OpenApi.php', + 'Dedoc\\Scramble\\Support\\Generator\\Operation' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Operation.php', + 'Dedoc\\Scramble\\Support\\Generator\\Parameter' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Parameter.php', + 'Dedoc\\Scramble\\Support\\Generator\\Path' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Path.php', + 'Dedoc\\Scramble\\Support\\Generator\\Reference' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Reference.php', + 'Dedoc\\Scramble\\Support\\Generator\\RequestBodyObject' => $vendorDir . '/dedoc/scramble/src/Support/Generator/RequestBodyObject.php', + 'Dedoc\\Scramble\\Support\\Generator\\Response' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Response.php', + 'Dedoc\\Scramble\\Support\\Generator\\Schema' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Schema.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecurityRequirement' => $vendorDir . '/dedoc/scramble/src/Support/Generator/SecurityRequirement.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecurityScheme' => $vendorDir . '/dedoc/scramble/src/Support/Generator/SecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\ApiKeySecurityScheme' => $vendorDir . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/ApiKeySecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\HttpSecurityScheme' => $vendorDir . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/HttpSecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\OAuthFlow' => $vendorDir . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlow.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\OAuthFlows' => $vendorDir . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlows.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\Oauth2SecurityScheme' => $vendorDir . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/Oauth2SecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\OpenIdConnectUrlSecurityScheme' => $vendorDir . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/OpenIdConnectUrlSecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\Server' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Server.php', + 'Dedoc\\Scramble\\Support\\Generator\\ServerVariable' => $vendorDir . '/dedoc/scramble/src/Support/Generator/ServerVariable.php', + 'Dedoc\\Scramble\\Support\\Generator\\Tag' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Tag.php', + 'Dedoc\\Scramble\\Support\\Generator\\TypeTransformer' => $vendorDir . '/dedoc/scramble/src/Support/Generator/TypeTransformer.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\ArrayType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/ArrayType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\BooleanType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/BooleanType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\IntegerType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/IntegerType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\MixedType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/MixedType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\NullType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/NullType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\NumberType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/NumberType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\ObjectType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/ObjectType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\StringType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/StringType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\Type' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/Type.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\UnknownType' => $vendorDir . '/dedoc/scramble/src/Support/Generator/Types/UnknownType.php', + 'Dedoc\\Scramble\\Support\\Generator\\UniqueNameOptions' => $vendorDir . '/dedoc/scramble/src/Support/Generator/UniqueNameOptions.php', + 'Dedoc\\Scramble\\Support\\Generator\\UniqueNamesOptionsCollection' => $vendorDir . '/dedoc/scramble/src/Support/Generator/UniqueNamesOptionsCollection.php', + 'Dedoc\\Scramble\\Support\\Generator\\WithAttributes' => $vendorDir . '/dedoc/scramble/src/Support/Generator/WithAttributes.php', + 'Dedoc\\Scramble\\Support\\Generator\\WithExtensions' => $vendorDir . '/dedoc/scramble/src/Support/Generator/WithExtensions.php', + 'Dedoc\\Scramble\\Support\\Helpers\\ExamplesExtractor' => $vendorDir . '/dedoc/scramble/src/Support/Helpers/ExamplesExtractor.php', + 'Dedoc\\Scramble\\Support\\Helpers\\JsonResourceHelper' => $vendorDir . '/dedoc/scramble/src/Support/Helpers/JsonResourceHelper.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\Bag' => $vendorDir . '/dedoc/scramble/src/Support/IndexBuilders/Bag.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\IndexBuilder' => $vendorDir . '/dedoc/scramble/src/Support/IndexBuilders/IndexBuilder.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\PaginatorsCandidatesBuilder' => $vendorDir . '/dedoc/scramble/src/Support/IndexBuilders/PaginatorsCandidatesBuilder.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\RequestParametersBuilder' => $vendorDir . '/dedoc/scramble/src/Support/IndexBuilders/RequestParametersBuilder.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\ScopeCollector' => $vendorDir . '/dedoc/scramble/src/Support/IndexBuilders/ScopeCollector.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AbortHelpersExceptionInfer' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/AbortHelpersExceptionInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterAnonymousResourceCollectionDefinitionCreatedExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/AfterAnonymousResourceCollectionDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterEloquentCollectionDefinitionCreatedExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/AfterEloquentCollectionDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterJsonApiResourceDefinitionCreatedExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/AfterJsonApiResourceDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterJsonResourceDefinitionCreatedExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/AfterJsonResourceDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterResourceCollectionDefinitionCreatedExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/AfterResourceCollectionDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterResponseDefinitionCreatedExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/AfterResponseDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ArrayMergeReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/ArrayMergeReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\BinaryFileResponseTypeFactory' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/BinaryFileResponseTypeFactory.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\EloquentBuilderExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/EloquentBuilderExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\FacadeStaticMethodReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/FacadeStaticMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\JsonApiResourceCollectionMethodReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceCollectionMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\JsonApiResourceMethodReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\JsonResourceExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/JsonResourceExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\JsonResponseMethodReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/JsonResponseMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ModelCollectionTypeResolver' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/ModelCollectionTypeResolver.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ModelExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/ModelExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\PaginateMethodsReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/PaginateMethodsReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\PossibleExceptionInfer' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/PossibleExceptionInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\RequestExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/RequestExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ResourceCollectionTypeInfer' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/ResourceCollectionTypeInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ResourceResponseMethodReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/ResourceResponseMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ResponseFactoryTypeInfer' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/ResponseFactoryTypeInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ShallowFunctionDefinition' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/ShallowFunctionDefinition.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\TransformsToResourceCollectionExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/TransformsToResourceCollectionExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\TranslationReturnTypeExtension' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/TranslationReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\TypeTraceInfer' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/TypeTraceInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ValidatorTypeInfer' => $vendorDir . '/dedoc/scramble/src/Support/InferExtensions/ValidatorTypeInfer.php', + 'Dedoc\\Scramble\\Support\\OperationBuilder' => $vendorDir . '/dedoc/scramble/src/Support/OperationBuilder.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\DeprecationExtension' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/DeprecationExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ErrorResponsesExtension' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ErrorResponsesExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\AttributesParametersExtractor' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/AttributesParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\FormRequestParametersExtractor' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\InferredParameter' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/InferredParameter.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\JsonApiResourceParametersExtractor' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/JsonApiResourceParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\MethodCallsParametersExtractor' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/MethodCallsParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\ParameterExtractor' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ParameterExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\PathParametersExtractor' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/PathParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\RulesDocumentationRetriever' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesDocumentationRetriever.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\RulesNodes' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesNodes.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\TypeBasedRulesDocumentationRetriever' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/TypeBasedRulesDocumentationRetriever.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\ValidateCallParametersExtractor' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RequestBodyExtension' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RequestBodyExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RequestEssentialsExtension' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RequestEssentialsExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ResponseExtension' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ResponseExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ResponseHeadersExtension' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/ResponseHeadersExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\ComposedFormRequestRulesEvaluator' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\ConstFetchEvaluator' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ConstFetchEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\FormRequestRulesEvaluator' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/FormRequestRulesEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\NodeRulesEvaluator' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\PublicProxy' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/PublicProxy.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\RulesEvaluator' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/RulesEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\DeepParametersMerger' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/DeepParametersMerger.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\GeneratesParametersFromRules' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/GeneratesParametersFromRules.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\ParametersExtractionResult' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/ParametersExtractionResult.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\PhpDocSchemaTransformer' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/PhpDocSchemaTransformer.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\QueryParametersConverter' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/QueryParametersConverter.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\RulesMapper' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\RulesToParameters' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesToParameters.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\SchemaBagToParametersTransformer' => $vendorDir . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/SchemaBagToParametersTransformer.php', + 'Dedoc\\Scramble\\Support\\PhpDoc' => $vendorDir . '/dedoc/scramble/src/Support/PhpDoc.php', + 'Dedoc\\Scramble\\Support\\ResponseExtractor\\ModelInfo' => $vendorDir . '/dedoc/scramble/src/Support/ResponseExtractor/ModelInfo.php', + 'Dedoc\\Scramble\\Support\\RouteInfo' => $vendorDir . '/dedoc/scramble/src/Support/RouteInfo.php', + 'Dedoc\\Scramble\\Support\\RouteResponseTypeRetriever' => $vendorDir . '/dedoc/scramble/src/Support/RouteResponseTypeRetriever.php', + 'Dedoc\\Scramble\\Support\\RuleTransforming\\NormalizedRule' => $vendorDir . '/dedoc/scramble/src/Support/RuleTransforming/NormalizedRule.php', + 'Dedoc\\Scramble\\Support\\RuleTransforming\\RuleSetToSchemaTransformer' => $vendorDir . '/dedoc/scramble/src/Support/RuleTransforming/RuleSetToSchemaTransformer.php', + 'Dedoc\\Scramble\\Support\\RuleTransforming\\RuleTransformerContext' => $vendorDir . '/dedoc/scramble/src/Support/RuleTransforming/RuleTransformerContext.php', + 'Dedoc\\Scramble\\Support\\RuleTransforming\\SchemaBag' => $vendorDir . '/dedoc/scramble/src/Support/RuleTransforming/SchemaBag.php', + 'Dedoc\\Scramble\\Support\\SchemaClassDocReflector' => $vendorDir . '/dedoc/scramble/src/Support/SchemaClassDocReflector.php', + 'Dedoc\\Scramble\\Support\\ServerFactory' => $vendorDir . '/dedoc/scramble/src/Support/ServerFactory.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\CursorPaginatorTypeManager' => $vendorDir . '/dedoc/scramble/src/Support/TypeManagers/CursorPaginatorTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\JsonApiResourceTypeManager' => $vendorDir . '/dedoc/scramble/src/Support/TypeManagers/JsonApiResourceTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\LengthAwarePaginatorTypeManager' => $vendorDir . '/dedoc/scramble/src/Support/TypeManagers/LengthAwarePaginatorTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\ManagesProperties' => $vendorDir . '/dedoc/scramble/src/Support/TypeManagers/ManagesProperties.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\PaginatorTypeManager' => $vendorDir . '/dedoc/scramble/src/Support/TypeManagers/PaginatorTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\ResourceCollectionTypeManager' => $vendorDir . '/dedoc/scramble/src/Support/TypeManagers/ResourceCollectionTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\AnonymousResourceCollectionTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/AnonymousResourceCollectionTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ArrayableToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ArrayableToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\BinaryFileResponseToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/BinaryFileResponseToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\CarbonInterfaceToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/CarbonInterfaceToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\CollectionToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/CollectionToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\CursorPaginatorTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/CursorPaginatorTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\EloquentCollectionToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/EloquentCollectionToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\EnumToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/EnumToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\FlattensMergeValues' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/FlattensMergeValues.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\HandlesJsonApiResourceResponse' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/HandlesJsonApiResourceResponse.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonApiAnonymousCollectionTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiAnonymousCollectionTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonApiPaginatedResourceResponseToSchemaExtension' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiPaginatedResourceResponseToSchemaExtension.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonApiResourceResponseToSchemaExtension' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceResponseToSchemaExtension.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonApiResourceTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonResourceTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonResourceTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\LengthAwarePaginatorTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/LengthAwarePaginatorTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\MergesOpenApiObjects' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/MergesOpenApiObjects.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ModelToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ModelToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\PaginatedResourceResponseTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatedResourceResponseTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\PaginatorTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatorTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\PlainObjectToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/PlainObjectToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ResourceCollectionTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceCollectionTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ResourceResponseTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceResponseTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ResponsableTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponsableTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ResponseTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponseTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\StreamedResponseToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/StreamedResponseToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\VoidTypeToSchema' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/VoidTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\WithCollectedPaginatedItems' => $vendorDir . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/WithCollectedPaginatedItems.php', + 'Dedoc\\Scramble\\Support\\Type\\AbstractType' => $vendorDir . '/dedoc/scramble/src/Support/Type/AbstractType.php', + 'Dedoc\\Scramble\\Support\\Type\\AbstractTypeVisitor' => $vendorDir . '/dedoc/scramble/src/Support/Type/AbstractTypeVisitor.php', + 'Dedoc\\Scramble\\Support\\Type\\ArrayItemType_' => $vendorDir . '/dedoc/scramble/src/Support/Type/ArrayItemType_.php', + 'Dedoc\\Scramble\\Support\\Type\\ArrayType' => $vendorDir . '/dedoc/scramble/src/Support/Type/ArrayType.php', + 'Dedoc\\Scramble\\Support\\Type\\BooleanType' => $vendorDir . '/dedoc/scramble/src/Support/Type/BooleanType.php', + 'Dedoc\\Scramble\\Support\\Type\\CallableStringType' => $vendorDir . '/dedoc/scramble/src/Support/Type/CallableStringType.php', + 'Dedoc\\Scramble\\Support\\Type\\CoalesceType' => $vendorDir . '/dedoc/scramble/src/Support/Type/CoalesceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Contracts\\Generic' => $vendorDir . '/dedoc/scramble/src/Support/Type/Contracts/Generic.php', + 'Dedoc\\Scramble\\Support\\Type\\Contracts\\LateResolvingType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Contracts/LateResolvingType.php', + 'Dedoc\\Scramble\\Support\\Type\\Contracts\\LiteralString' => $vendorDir . '/dedoc/scramble/src/Support/Type/Contracts/LiteralString.php', + 'Dedoc\\Scramble\\Support\\Type\\Contracts\\LiteralType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Contracts/LiteralType.php', + 'Dedoc\\Scramble\\Support\\Type\\EnumCaseType' => $vendorDir . '/dedoc/scramble/src/Support/Type/EnumCaseType.php', + 'Dedoc\\Scramble\\Support\\Type\\FloatType' => $vendorDir . '/dedoc/scramble/src/Support/Type/FloatType.php', + 'Dedoc\\Scramble\\Support\\Type\\FunctionLikeType' => $vendorDir . '/dedoc/scramble/src/Support/Type/FunctionLikeType.php', + 'Dedoc\\Scramble\\Support\\Type\\FunctionType' => $vendorDir . '/dedoc/scramble/src/Support/Type/FunctionType.php', + 'Dedoc\\Scramble\\Support\\Type\\Generic' => $vendorDir . '/dedoc/scramble/src/Support/Type/Generic.php', + 'Dedoc\\Scramble\\Support\\Type\\GenericClassStringType' => $vendorDir . '/dedoc/scramble/src/Support/Type/GenericClassStringType.php', + 'Dedoc\\Scramble\\Support\\Type\\IntegerRangeType' => $vendorDir . '/dedoc/scramble/src/Support/Type/IntegerRangeType.php', + 'Dedoc\\Scramble\\Support\\Type\\IntegerType' => $vendorDir . '/dedoc/scramble/src/Support/Type/IntegerType.php', + 'Dedoc\\Scramble\\Support\\Type\\IntersectionType' => $vendorDir . '/dedoc/scramble/src/Support/Type/IntersectionType.php', + 'Dedoc\\Scramble\\Support\\Type\\KeyedArrayType' => $vendorDir . '/dedoc/scramble/src/Support/Type/KeyedArrayType.php', + 'Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralBooleanType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Literal/LiteralBooleanType.php', + 'Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralFloatType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Literal/LiteralFloatType.php', + 'Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralIntegerType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Literal/LiteralIntegerType.php', + 'Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Literal/LiteralStringType.php', + 'Dedoc\\Scramble\\Support\\Type\\MissingType' => $vendorDir . '/dedoc/scramble/src/Support/Type/MissingType.php', + 'Dedoc\\Scramble\\Support\\Type\\MixedType' => $vendorDir . '/dedoc/scramble/src/Support/Type/MixedType.php', + 'Dedoc\\Scramble\\Support\\Type\\NeverType' => $vendorDir . '/dedoc/scramble/src/Support/Type/NeverType.php', + 'Dedoc\\Scramble\\Support\\Type\\NullType' => $vendorDir . '/dedoc/scramble/src/Support/Type/NullType.php', + 'Dedoc\\Scramble\\Support\\Type\\ObjectType' => $vendorDir . '/dedoc/scramble/src/Support/Type/ObjectType.php', + 'Dedoc\\Scramble\\Support\\Type\\OffsetAccessType' => $vendorDir . '/dedoc/scramble/src/Support/Type/OffsetAccessType.php', + 'Dedoc\\Scramble\\Support\\Type\\OffsetSetType' => $vendorDir . '/dedoc/scramble/src/Support/Type/OffsetSetType.php', + 'Dedoc\\Scramble\\Support\\Type\\OffsetUnsetType' => $vendorDir . '/dedoc/scramble/src/Support/Type/OffsetUnsetType.php', + 'Dedoc\\Scramble\\Support\\Type\\RecursiveTemplateSolver' => $vendorDir . '/dedoc/scramble/src/Support/Type/RecursiveTemplateSolver.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\AbstractReferenceType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/AbstractReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\CallableCallReferenceType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/CallableCallReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\ConstFetchReferenceType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/ConstFetchReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\MethodCallReferenceType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/MethodCallReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\NewCallReferenceType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/NewCallReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\PotentialMethodMutatingCallType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/PotentialMethodMutatingCallType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\PropertyFetchReferenceType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/PropertyFetchReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\StaticMethodCallReferenceType' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/StaticMethodCallReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\StaticReference' => $vendorDir . '/dedoc/scramble/src/Support/Type/Reference/StaticReference.php', + 'Dedoc\\Scramble\\Support\\Type\\SelfType' => $vendorDir . '/dedoc/scramble/src/Support/Type/SelfType.php', + 'Dedoc\\Scramble\\Support\\Type\\StringType' => $vendorDir . '/dedoc/scramble/src/Support/Type/StringType.php', + 'Dedoc\\Scramble\\Support\\Type\\TemplatePlaceholderType' => $vendorDir . '/dedoc/scramble/src/Support/Type/TemplatePlaceholderType.php', + 'Dedoc\\Scramble\\Support\\Type\\TemplateType' => $vendorDir . '/dedoc/scramble/src/Support/Type/TemplateType.php', + 'Dedoc\\Scramble\\Support\\Type\\TranslatedStringType' => $vendorDir . '/dedoc/scramble/src/Support/Type/TranslatedStringType.php', + 'Dedoc\\Scramble\\Support\\Type\\Type' => $vendorDir . '/dedoc/scramble/src/Support/Type/Type.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeAttributes' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypeAttributes.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeHelper' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypeHelper.php', + 'Dedoc\\Scramble\\Support\\Type\\TypePath' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypePath.php', + 'Dedoc\\Scramble\\Support\\Type\\TypePathFindingVisitor' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypePathFindingVisitor.php', + 'Dedoc\\Scramble\\Support\\Type\\TypePathItem' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypePathItem.php', + 'Dedoc\\Scramble\\Support\\Type\\TypePathItemCondition' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypePathItemCondition.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeTraverser' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypeTraverser.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeVisitor' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypeVisitor.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeWalker' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypeWalker.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeWidener' => $vendorDir . '/dedoc/scramble/src/Support/Type/TypeWidener.php', + 'Dedoc\\Scramble\\Support\\Type\\Union' => $vendorDir . '/dedoc/scramble/src/Support/Type/Union.php', + 'Dedoc\\Scramble\\Support\\Type\\UnknownType' => $vendorDir . '/dedoc/scramble/src/Support/Type/UnknownType.php', + 'Dedoc\\Scramble\\Support\\Type\\VoidType' => $vendorDir . '/dedoc/scramble/src/Support/Type/VoidType.php', 'DeepCopy\\DeepCopy' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/DeepCopy.php', 'DeepCopy\\Exception\\CloneException' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php', 'DeepCopy\\Exception\\PropertyException' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php', @@ -2484,18 +2918,6 @@ return array( 'Illuminate\\View\\ViewFinderInterface' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewFinderInterface.php', 'Illuminate\\View\\ViewName' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewName.php', 'Illuminate\\View\\ViewServiceProvider' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewServiceProvider.php', - 'L5Swagger\\ConfigFactory' => $vendorDir . '/darkaonline/l5-swagger/src/ConfigFactory.php', - 'L5Swagger\\Console\\GenerateDocsCommand' => $vendorDir . '/darkaonline/l5-swagger/src/Console/GenerateDocsCommand.php', - 'L5Swagger\\CustomGeneratorInterface' => $vendorDir . '/darkaonline/l5-swagger/src/CustomGeneratorInterface.php', - 'L5Swagger\\Exceptions\\L5SwaggerException' => $vendorDir . '/darkaonline/l5-swagger/src/Exceptions/L5SwaggerException.php', - 'L5Swagger\\Generator' => $vendorDir . '/darkaonline/l5-swagger/src/Generator.php', - 'L5Swagger\\GeneratorFactory' => $vendorDir . '/darkaonline/l5-swagger/src/GeneratorFactory.php', - 'L5Swagger\\Http\\Controllers\\SwaggerAssetController' => $vendorDir . '/darkaonline/l5-swagger/src/Http/Controllers/SwaggerAssetController.php', - 'L5Swagger\\Http\\Controllers\\SwaggerController' => $vendorDir . '/darkaonline/l5-swagger/src/Http/Controllers/SwaggerController.php', - 'L5Swagger\\Http\\Middleware\\Config' => $vendorDir . '/darkaonline/l5-swagger/src/Http/Middleware/Config.php', - 'L5Swagger\\L5SwaggerFacade' => $vendorDir . '/darkaonline/l5-swagger/src/L5SwaggerFacade.php', - 'L5Swagger\\L5SwaggerServiceProvider' => $vendorDir . '/darkaonline/l5-swagger/src/L5SwaggerServiceProvider.php', - 'L5Swagger\\SecurityDefinitions' => $vendorDir . '/darkaonline/l5-swagger/src/SecurityDefinitions.php', 'Laravel\\Pail\\Console\\Commands\\PailCommand' => $vendorDir . '/laravel/pail/src/Console/Commands/PailCommand.php', 'Laravel\\Pail\\Contracts\\Printer' => $vendorDir . '/laravel/pail/src/Contracts/Printer.php', 'Laravel\\Pail\\File' => $vendorDir . '/laravel/pail/src/File.php', @@ -3378,149 +3800,6 @@ return array( 'NunoMaduro\\Collision\\Provider' => $vendorDir . '/nunomaduro/collision/src/Provider.php', 'NunoMaduro\\Collision\\SolutionsRepositories\\NullSolutionsRepository' => $vendorDir . '/nunomaduro/collision/src/SolutionsRepositories/NullSolutionsRepository.php', 'NunoMaduro\\Collision\\Writer' => $vendorDir . '/nunomaduro/collision/src/Writer.php', - 'OpenApi\\Analysers\\AnalyserInterface' => $vendorDir . '/zircote/swagger-php/src/Analysers/AnalyserInterface.php', - 'OpenApi\\Analysers\\AnnotationFactoryInterface' => $vendorDir . '/zircote/swagger-php/src/Analysers/AnnotationFactoryInterface.php', - 'OpenApi\\Analysers\\AttributeAnnotationFactory' => $vendorDir . '/zircote/swagger-php/src/Analysers/AttributeAnnotationFactory.php', - 'OpenApi\\Analysers\\ComposerAutoloaderScanner' => $vendorDir . '/zircote/swagger-php/src/Analysers/ComposerAutoloaderScanner.php', - 'OpenApi\\Analysers\\DocBlockAnnotationFactory' => $vendorDir . '/zircote/swagger-php/src/Analysers/DocBlockAnnotationFactory.php', - 'OpenApi\\Analysers\\DocBlockParser' => $vendorDir . '/zircote/swagger-php/src/Analysers/DocBlockParser.php', - 'OpenApi\\Analysers\\ReflectionAnalyser' => $vendorDir . '/zircote/swagger-php/src/Analysers/ReflectionAnalyser.php', - 'OpenApi\\Analysers\\TokenScanner' => $vendorDir . '/zircote/swagger-php/src/Analysers/TokenScanner.php', - 'OpenApi\\Analysis' => $vendorDir . '/zircote/swagger-php/src/Analysis.php', - 'OpenApi\\Annotations\\AbstractAnnotation' => $vendorDir . '/zircote/swagger-php/src/Annotations/AbstractAnnotation.php', - 'OpenApi\\Annotations\\AdditionalProperties' => $vendorDir . '/zircote/swagger-php/src/Annotations/AdditionalProperties.php', - 'OpenApi\\Annotations\\Attachable' => $vendorDir . '/zircote/swagger-php/src/Annotations/Attachable.php', - 'OpenApi\\Annotations\\Components' => $vendorDir . '/zircote/swagger-php/src/Annotations/Components.php', - 'OpenApi\\Annotations\\Contact' => $vendorDir . '/zircote/swagger-php/src/Annotations/Contact.php', - 'OpenApi\\Annotations\\CookieParameter' => $vendorDir . '/zircote/swagger-php/src/Annotations/CookieParameter.php', - 'OpenApi\\Annotations\\Delete' => $vendorDir . '/zircote/swagger-php/src/Annotations/Delete.php', - 'OpenApi\\Annotations\\Discriminator' => $vendorDir . '/zircote/swagger-php/src/Annotations/Discriminator.php', - 'OpenApi\\Annotations\\Encoding' => $vendorDir . '/zircote/swagger-php/src/Annotations/Encoding.php', - 'OpenApi\\Annotations\\Examples' => $vendorDir . '/zircote/swagger-php/src/Annotations/Examples.php', - 'OpenApi\\Annotations\\ExternalDocumentation' => $vendorDir . '/zircote/swagger-php/src/Annotations/ExternalDocumentation.php', - 'OpenApi\\Annotations\\Flow' => $vendorDir . '/zircote/swagger-php/src/Annotations/Flow.php', - 'OpenApi\\Annotations\\Get' => $vendorDir . '/zircote/swagger-php/src/Annotations/Get.php', - 'OpenApi\\Annotations\\Head' => $vendorDir . '/zircote/swagger-php/src/Annotations/Head.php', - 'OpenApi\\Annotations\\Header' => $vendorDir . '/zircote/swagger-php/src/Annotations/Header.php', - 'OpenApi\\Annotations\\HeaderParameter' => $vendorDir . '/zircote/swagger-php/src/Annotations/HeaderParameter.php', - 'OpenApi\\Annotations\\Info' => $vendorDir . '/zircote/swagger-php/src/Annotations/Info.php', - 'OpenApi\\Annotations\\Items' => $vendorDir . '/zircote/swagger-php/src/Annotations/Items.php', - 'OpenApi\\Annotations\\JsonContent' => $vendorDir . '/zircote/swagger-php/src/Annotations/JsonContent.php', - 'OpenApi\\Annotations\\JsonSchemaTrait' => $vendorDir . '/zircote/swagger-php/src/Annotations/JsonSchemaTrait.php', - 'OpenApi\\Annotations\\License' => $vendorDir . '/zircote/swagger-php/src/Annotations/License.php', - 'OpenApi\\Annotations\\Link' => $vendorDir . '/zircote/swagger-php/src/Annotations/Link.php', - 'OpenApi\\Annotations\\MediaType' => $vendorDir . '/zircote/swagger-php/src/Annotations/MediaType.php', - 'OpenApi\\Annotations\\OpenApi' => $vendorDir . '/zircote/swagger-php/src/Annotations/OpenApi.php', - 'OpenApi\\Annotations\\Operation' => $vendorDir . '/zircote/swagger-php/src/Annotations/Operation.php', - 'OpenApi\\Annotations\\Options' => $vendorDir . '/zircote/swagger-php/src/Annotations/Options.php', - 'OpenApi\\Annotations\\Parameter' => $vendorDir . '/zircote/swagger-php/src/Annotations/Parameter.php', - 'OpenApi\\Annotations\\Patch' => $vendorDir . '/zircote/swagger-php/src/Annotations/Patch.php', - 'OpenApi\\Annotations\\PathItem' => $vendorDir . '/zircote/swagger-php/src/Annotations/PathItem.php', - 'OpenApi\\Annotations\\PathParameter' => $vendorDir . '/zircote/swagger-php/src/Annotations/PathParameter.php', - 'OpenApi\\Annotations\\Post' => $vendorDir . '/zircote/swagger-php/src/Annotations/Post.php', - 'OpenApi\\Annotations\\Property' => $vendorDir . '/zircote/swagger-php/src/Annotations/Property.php', - 'OpenApi\\Annotations\\Put' => $vendorDir . '/zircote/swagger-php/src/Annotations/Put.php', - 'OpenApi\\Annotations\\Query' => $vendorDir . '/zircote/swagger-php/src/Annotations/Query.php', - 'OpenApi\\Annotations\\QueryParameter' => $vendorDir . '/zircote/swagger-php/src/Annotations/QueryParameter.php', - 'OpenApi\\Annotations\\RequestBody' => $vendorDir . '/zircote/swagger-php/src/Annotations/RequestBody.php', - 'OpenApi\\Annotations\\Response' => $vendorDir . '/zircote/swagger-php/src/Annotations/Response.php', - 'OpenApi\\Annotations\\Schema' => $vendorDir . '/zircote/swagger-php/src/Annotations/Schema.php', - 'OpenApi\\Annotations\\SecurityScheme' => $vendorDir . '/zircote/swagger-php/src/Annotations/SecurityScheme.php', - 'OpenApi\\Annotations\\Server' => $vendorDir . '/zircote/swagger-php/src/Annotations/Server.php', - 'OpenApi\\Annotations\\ServerVariable' => $vendorDir . '/zircote/swagger-php/src/Annotations/ServerVariable.php', - 'OpenApi\\Annotations\\Tag' => $vendorDir . '/zircote/swagger-php/src/Annotations/Tag.php', - 'OpenApi\\Annotations\\Trace' => $vendorDir . '/zircote/swagger-php/src/Annotations/Trace.php', - 'OpenApi\\Annotations\\Webhook' => $vendorDir . '/zircote/swagger-php/src/Annotations/Webhook.php', - 'OpenApi\\Annotations\\Xml' => $vendorDir . '/zircote/swagger-php/src/Annotations/Xml.php', - 'OpenApi\\Annotations\\XmlContent' => $vendorDir . '/zircote/swagger-php/src/Annotations/XmlContent.php', - 'OpenApi\\Attributes\\AdditionalProperties' => $vendorDir . '/zircote/swagger-php/src/Attributes/AdditionalProperties.php', - 'OpenApi\\Attributes\\Attachable' => $vendorDir . '/zircote/swagger-php/src/Attributes/Attachable.php', - 'OpenApi\\Attributes\\Components' => $vendorDir . '/zircote/swagger-php/src/Attributes/Components.php', - 'OpenApi\\Attributes\\Contact' => $vendorDir . '/zircote/swagger-php/src/Attributes/Contact.php', - 'OpenApi\\Attributes\\CookieParameter' => $vendorDir . '/zircote/swagger-php/src/Attributes/CookieParameter.php', - 'OpenApi\\Attributes\\Delete' => $vendorDir . '/zircote/swagger-php/src/Attributes/Delete.php', - 'OpenApi\\Attributes\\Discriminator' => $vendorDir . '/zircote/swagger-php/src/Attributes/Discriminator.php', - 'OpenApi\\Attributes\\Encoding' => $vendorDir . '/zircote/swagger-php/src/Attributes/Encoding.php', - 'OpenApi\\Attributes\\Examples' => $vendorDir . '/zircote/swagger-php/src/Attributes/Examples.php', - 'OpenApi\\Attributes\\ExternalDocumentation' => $vendorDir . '/zircote/swagger-php/src/Attributes/ExternalDocumentation.php', - 'OpenApi\\Attributes\\Flow' => $vendorDir . '/zircote/swagger-php/src/Attributes/Flow.php', - 'OpenApi\\Attributes\\Get' => $vendorDir . '/zircote/swagger-php/src/Attributes/Get.php', - 'OpenApi\\Attributes\\Head' => $vendorDir . '/zircote/swagger-php/src/Attributes/Head.php', - 'OpenApi\\Attributes\\Header' => $vendorDir . '/zircote/swagger-php/src/Attributes/Header.php', - 'OpenApi\\Attributes\\HeaderParameter' => $vendorDir . '/zircote/swagger-php/src/Attributes/HeaderParameter.php', - 'OpenApi\\Attributes\\Info' => $vendorDir . '/zircote/swagger-php/src/Attributes/Info.php', - 'OpenApi\\Attributes\\Items' => $vendorDir . '/zircote/swagger-php/src/Attributes/Items.php', - 'OpenApi\\Attributes\\JsonContent' => $vendorDir . '/zircote/swagger-php/src/Attributes/JsonContent.php', - 'OpenApi\\Attributes\\License' => $vendorDir . '/zircote/swagger-php/src/Attributes/License.php', - 'OpenApi\\Attributes\\Link' => $vendorDir . '/zircote/swagger-php/src/Attributes/Link.php', - 'OpenApi\\Attributes\\MediaType' => $vendorDir . '/zircote/swagger-php/src/Attributes/MediaType.php', - 'OpenApi\\Attributes\\OpenApi' => $vendorDir . '/zircote/swagger-php/src/Attributes/OpenApi.php', - 'OpenApi\\Attributes\\OperationTrait' => $vendorDir . '/zircote/swagger-php/src/Attributes/OperationTrait.php', - 'OpenApi\\Attributes\\Options' => $vendorDir . '/zircote/swagger-php/src/Attributes/Options.php', - 'OpenApi\\Attributes\\Parameter' => $vendorDir . '/zircote/swagger-php/src/Attributes/Parameter.php', - 'OpenApi\\Attributes\\ParameterTrait' => $vendorDir . '/zircote/swagger-php/src/Attributes/ParameterTrait.php', - 'OpenApi\\Attributes\\Patch' => $vendorDir . '/zircote/swagger-php/src/Attributes/Patch.php', - 'OpenApi\\Attributes\\PathItem' => $vendorDir . '/zircote/swagger-php/src/Attributes/PathItem.php', - 'OpenApi\\Attributes\\PathParameter' => $vendorDir . '/zircote/swagger-php/src/Attributes/PathParameter.php', - 'OpenApi\\Attributes\\Post' => $vendorDir . '/zircote/swagger-php/src/Attributes/Post.php', - 'OpenApi\\Attributes\\Property' => $vendorDir . '/zircote/swagger-php/src/Attributes/Property.php', - 'OpenApi\\Attributes\\Put' => $vendorDir . '/zircote/swagger-php/src/Attributes/Put.php', - 'OpenApi\\Attributes\\Query' => $vendorDir . '/zircote/swagger-php/src/Attributes/Query.php', - 'OpenApi\\Attributes\\QueryParameter' => $vendorDir . '/zircote/swagger-php/src/Attributes/QueryParameter.php', - 'OpenApi\\Attributes\\RequestBody' => $vendorDir . '/zircote/swagger-php/src/Attributes/RequestBody.php', - 'OpenApi\\Attributes\\Response' => $vendorDir . '/zircote/swagger-php/src/Attributes/Response.php', - 'OpenApi\\Attributes\\Schema' => $vendorDir . '/zircote/swagger-php/src/Attributes/Schema.php', - 'OpenApi\\Attributes\\SecurityScheme' => $vendorDir . '/zircote/swagger-php/src/Attributes/SecurityScheme.php', - 'OpenApi\\Attributes\\Server' => $vendorDir . '/zircote/swagger-php/src/Attributes/Server.php', - 'OpenApi\\Attributes\\ServerVariable' => $vendorDir . '/zircote/swagger-php/src/Attributes/ServerVariable.php', - 'OpenApi\\Attributes\\Tag' => $vendorDir . '/zircote/swagger-php/src/Attributes/Tag.php', - 'OpenApi\\Attributes\\Trace' => $vendorDir . '/zircote/swagger-php/src/Attributes/Trace.php', - 'OpenApi\\Attributes\\Webhook' => $vendorDir . '/zircote/swagger-php/src/Attributes/Webhook.php', - 'OpenApi\\Attributes\\Xml' => $vendorDir . '/zircote/swagger-php/src/Attributes/Xml.php', - 'OpenApi\\Attributes\\XmlContent' => $vendorDir . '/zircote/swagger-php/src/Attributes/XmlContent.php', - 'OpenApi\\Console\\GenerateCommand' => $vendorDir . '/zircote/swagger-php/src/Console/GenerateCommand.php', - 'OpenApi\\Console\\GenerateFormat' => $vendorDir . '/zircote/swagger-php/src/Console/GenerateFormat.php', - 'OpenApi\\Console\\GenerateInput' => $vendorDir . '/zircote/swagger-php/src/Console/GenerateInput.php', - 'OpenApi\\Context' => $vendorDir . '/zircote/swagger-php/src/Context.php', - 'OpenApi\\Generator' => $vendorDir . '/zircote/swagger-php/src/Generator.php', - 'OpenApi\\GeneratorAwareInterface' => $vendorDir . '/zircote/swagger-php/src/GeneratorAwareInterface.php', - 'OpenApi\\GeneratorAwareTrait' => $vendorDir . '/zircote/swagger-php/src/GeneratorAwareTrait.php', - 'OpenApi\\Loggers\\DefaultLogger' => $vendorDir . '/zircote/swagger-php/src/Loggers/DefaultLogger.php', - 'OpenApi\\OpenApiException' => $vendorDir . '/zircote/swagger-php/src/OpenApiException.php', - 'OpenApi\\Pipeline' => $vendorDir . '/zircote/swagger-php/src/Pipeline.php', - 'OpenApi\\Processors\\AugmentDiscriminators' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentDiscriminators.php', - 'OpenApi\\Processors\\AugmentItems' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentItems.php', - 'OpenApi\\Processors\\AugmentMediaType' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentMediaType.php', - 'OpenApi\\Processors\\AugmentParameters' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentParameters.php', - 'OpenApi\\Processors\\AugmentProperties' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentProperties.php', - 'OpenApi\\Processors\\AugmentRefs' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentRefs.php', - 'OpenApi\\Processors\\AugmentRequestBody' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentRequestBody.php', - 'OpenApi\\Processors\\AugmentSchemas' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentSchemas.php', - 'OpenApi\\Processors\\AugmentTags' => $vendorDir . '/zircote/swagger-php/src/Processors/AugmentTags.php', - 'OpenApi\\Processors\\BuildPaths' => $vendorDir . '/zircote/swagger-php/src/Processors/BuildPaths.php', - 'OpenApi\\Processors\\CleanUnmerged' => $vendorDir . '/zircote/swagger-php/src/Processors/CleanUnmerged.php', - 'OpenApi\\Processors\\CleanUnusedComponents' => $vendorDir . '/zircote/swagger-php/src/Processors/CleanUnusedComponents.php', - 'OpenApi\\Processors\\Concerns\\AnnotationTrait' => $vendorDir . '/zircote/swagger-php/src/Processors/Concerns/AnnotationTrait.php', - 'OpenApi\\Processors\\Concerns\\DocblockTrait' => $vendorDir . '/zircote/swagger-php/src/Processors/Concerns/DocblockTrait.php', - 'OpenApi\\Processors\\Concerns\\MergePropertiesTrait' => $vendorDir . '/zircote/swagger-php/src/Processors/Concerns/MergePropertiesTrait.php', - 'OpenApi\\Processors\\Concerns\\RefTrait' => $vendorDir . '/zircote/swagger-php/src/Processors/Concerns/RefTrait.php', - 'OpenApi\\Processors\\DocBlockDescriptions' => $vendorDir . '/zircote/swagger-php/src/Processors/DocBlockDescriptions.php', - 'OpenApi\\Processors\\ExpandClasses' => $vendorDir . '/zircote/swagger-php/src/Processors/ExpandClasses.php', - 'OpenApi\\Processors\\ExpandEnums' => $vendorDir . '/zircote/swagger-php/src/Processors/ExpandEnums.php', - 'OpenApi\\Processors\\ExpandInterfaces' => $vendorDir . '/zircote/swagger-php/src/Processors/ExpandInterfaces.php', - 'OpenApi\\Processors\\ExpandTraits' => $vendorDir . '/zircote/swagger-php/src/Processors/ExpandTraits.php', - 'OpenApi\\Processors\\MergeIntoComponents' => $vendorDir . '/zircote/swagger-php/src/Processors/MergeIntoComponents.php', - 'OpenApi\\Processors\\MergeIntoOpenApi' => $vendorDir . '/zircote/swagger-php/src/Processors/MergeIntoOpenApi.php', - 'OpenApi\\Processors\\MergeJsonContent' => $vendorDir . '/zircote/swagger-php/src/Processors/MergeJsonContent.php', - 'OpenApi\\Processors\\MergeXmlContent' => $vendorDir . '/zircote/swagger-php/src/Processors/MergeXmlContent.php', - 'OpenApi\\Processors\\OperationId' => $vendorDir . '/zircote/swagger-php/src/Processors/OperationId.php', - 'OpenApi\\Processors\\PathFilter' => $vendorDir . '/zircote/swagger-php/src/Processors/PathFilter.php', - 'OpenApi\\Serializer' => $vendorDir . '/zircote/swagger-php/src/Serializer.php', - 'OpenApi\\SourceFinder' => $vendorDir . '/zircote/swagger-php/src/SourceFinder.php', - 'OpenApi\\TypeResolverInterface' => $vendorDir . '/zircote/swagger-php/src/TypeResolverInterface.php', - 'OpenApi\\Type\\AbstractTypeResolver' => $vendorDir . '/zircote/swagger-php/src/Type/AbstractTypeResolver.php', - 'OpenApi\\Type\\LegacyTypeResolver' => $vendorDir . '/zircote/swagger-php/src/Type/LegacyTypeResolver.php', - 'OpenApi\\Type\\TypeInfoTypeResolver' => $vendorDir . '/zircote/swagger-php/src/Type/TypeInfoTypeResolver.php', 'Override' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/Override.php', 'PHPStan\\PhpDocParser\\Ast\\AbstractNodeVisitor' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php', 'PHPStan\\PhpDocParser\\Ast\\Attribute' => $vendorDir . '/phpstan/phpdoc-parser/src/Ast/Attribute.php', @@ -5302,13 +5581,6 @@ return array( 'Psy\\VersionUpdater\\IntervalChecker' => $vendorDir . '/psy/psysh/src/VersionUpdater/IntervalChecker.php', 'Psy\\VersionUpdater\\NoopChecker' => $vendorDir . '/psy/psysh/src/VersionUpdater/NoopChecker.php', 'Psy\\VersionUpdater\\SelfUpdate' => $vendorDir . '/psy/psysh/src/VersionUpdater/SelfUpdate.php', - 'Radebatz\\TypeInfoExtras\\TypeFactoryTrait' => $vendorDir . '/radebatz/type-info-extras/src/TypeFactoryTrait.php', - 'Radebatz\\TypeInfoExtras\\TypeResolver\\ResolverExtrasTrait' => $vendorDir . '/radebatz/type-info-extras/src/TypeResolver/ResolverExtrasTrait.php', - 'Radebatz\\TypeInfoExtras\\TypeResolver\\StringTypeResolver' => $vendorDir . '/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php', - 'Radebatz\\TypeInfoExtras\\Type\\ClassLikeType' => $vendorDir . '/radebatz/type-info-extras/src/Type/ClassLikeType.php', - 'Radebatz\\TypeInfoExtras\\Type\\ExplicitType' => $vendorDir . '/radebatz/type-info-extras/src/Type/ExplicitType.php', - 'Radebatz\\TypeInfoExtras\\Type\\IntRangeType' => $vendorDir . '/radebatz/type-info-extras/src/Type/IntRangeType.php', - 'Radebatz\\TypeInfoExtras\\Type\\Type' => $vendorDir . '/radebatz/type-info-extras/src/Type/Type.php', 'Ramsey\\Collection\\AbstractArray' => $vendorDir . '/ramsey/collection/src/AbstractArray.php', 'Ramsey\\Collection\\AbstractCollection' => $vendorDir . '/ramsey/collection/src/AbstractCollection.php', 'Ramsey\\Collection\\AbstractSet' => $vendorDir . '/ramsey/collection/src/AbstractSet.php', @@ -5660,6 +5932,40 @@ return array( 'SebastianBergmann\\Type\\UnknownType' => $vendorDir . '/sebastian/type/src/type/UnknownType.php', 'SebastianBergmann\\Type\\VoidType' => $vendorDir . '/sebastian/type/src/type/VoidType.php', 'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\AskToRunMigrations' => $vendorDir . '/spatie/laravel-package-tools/src/Commands/Concerns/AskToRunMigrations.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\AskToStarRepoOnGitHub' => $vendorDir . '/spatie/laravel-package-tools/src/Commands/Concerns/AskToStarRepoOnGitHub.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\PublishesResources' => $vendorDir . '/spatie/laravel-package-tools/src/Commands/Concerns/PublishesResources.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\SupportsServiceProviderInApp' => $vendorDir . '/spatie/laravel-package-tools/src/Commands/Concerns/SupportsServiceProviderInApp.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\SupportsStartWithEndWith' => $vendorDir . '/spatie/laravel-package-tools/src/Commands/Concerns/SupportsStartWithEndWith.php', + 'Spatie\\LaravelPackageTools\\Commands\\InstallCommand' => $vendorDir . '/spatie/laravel-package-tools/src/Commands/InstallCommand.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessAssets' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessAssets.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessBladeComponents' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessBladeComponents.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessCommands' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessCommands.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessConfigs' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessConfigs.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessInertia' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessInertia.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessMigrations' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessMigrations.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessRoutes' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessRoutes.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessServiceProviders' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessServiceProviders.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessTranslations' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessTranslations.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessViewComposers' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewComposers.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessViewSharedData' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewSharedData.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessViews' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViews.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasAssets' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasAssets.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasBladeComponents' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasBladeComponents.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasCommands' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasCommands.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasConfigs' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasConfigs.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasInertia' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasInertia.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasInstallCommand' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasInstallCommand.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasMigrations' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasMigrations.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasRoutes' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasRoutes.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasServiceProviders' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasServiceProviders.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasTranslations' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasTranslations.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasViewComposers' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasViewComposers.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasViewSharedData' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasViewSharedData.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasViews' => $vendorDir . '/spatie/laravel-package-tools/src/Concerns/Package/HasViews.php', + 'Spatie\\LaravelPackageTools\\Exceptions\\InvalidPackage' => $vendorDir . '/spatie/laravel-package-tools/src/Exceptions/InvalidPackage.php', + 'Spatie\\LaravelPackageTools\\Package' => $vendorDir . '/spatie/laravel-package-tools/src/Package.php', + 'Spatie\\LaravelPackageTools\\PackageServiceProvider' => $vendorDir . '/spatie/laravel-package-tools/src/PackageServiceProvider.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', 'Symfony\\Component\\Clock\\Clock' => $vendorDir . '/symfony/clock/Clock.php', 'Symfony\\Component\\Clock\\ClockAwareTrait' => $vendorDir . '/symfony/clock/ClockAwareTrait.php', @@ -6534,37 +6840,6 @@ return array( 'Symfony\\Component\\Translation\\Util\\XliffUtils' => $vendorDir . '/symfony/translation/Util/XliffUtils.php', 'Symfony\\Component\\Translation\\Writer\\TranslationWriter' => $vendorDir . '/symfony/translation/Writer/TranslationWriter.php', 'Symfony\\Component\\Translation\\Writer\\TranslationWriterInterface' => $vendorDir . '/symfony/translation/Writer/TranslationWriterInterface.php', - 'Symfony\\Component\\TypeInfo\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/type-info/Exception/ExceptionInterface.php', - 'Symfony\\Component\\TypeInfo\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/type-info/Exception/InvalidArgumentException.php', - 'Symfony\\Component\\TypeInfo\\Exception\\LogicException' => $vendorDir . '/symfony/type-info/Exception/LogicException.php', - 'Symfony\\Component\\TypeInfo\\Exception\\RuntimeException' => $vendorDir . '/symfony/type-info/Exception/RuntimeException.php', - 'Symfony\\Component\\TypeInfo\\Exception\\UnsupportedException' => $vendorDir . '/symfony/type-info/Exception/UnsupportedException.php', - 'Symfony\\Component\\TypeInfo\\Type' => $vendorDir . '/symfony/type-info/Type.php', - 'Symfony\\Component\\TypeInfo\\TypeContext\\TypeContext' => $vendorDir . '/symfony/type-info/TypeContext/TypeContext.php', - 'Symfony\\Component\\TypeInfo\\TypeContext\\TypeContextFactory' => $vendorDir . '/symfony/type-info/TypeContext/TypeContextFactory.php', - 'Symfony\\Component\\TypeInfo\\TypeFactoryTrait' => $vendorDir . '/symfony/type-info/TypeFactoryTrait.php', - 'Symfony\\Component\\TypeInfo\\TypeIdentifier' => $vendorDir . '/symfony/type-info/TypeIdentifier.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\PhpDocAwareReflectionTypeResolver' => $vendorDir . '/symfony/type-info/TypeResolver/PhpDocAwareReflectionTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\ReflectionParameterTypeResolver' => $vendorDir . '/symfony/type-info/TypeResolver/ReflectionParameterTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\ReflectionPropertyTypeResolver' => $vendorDir . '/symfony/type-info/TypeResolver/ReflectionPropertyTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\ReflectionReturnTypeResolver' => $vendorDir . '/symfony/type-info/TypeResolver/ReflectionReturnTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\ReflectionTypeResolver' => $vendorDir . '/symfony/type-info/TypeResolver/ReflectionTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\StringTypeResolver' => $vendorDir . '/symfony/type-info/TypeResolver/StringTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\TypeResolver' => $vendorDir . '/symfony/type-info/TypeResolver/TypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\TypeResolverInterface' => $vendorDir . '/symfony/type-info/TypeResolver/TypeResolverInterface.php', - 'Symfony\\Component\\TypeInfo\\Type\\ArrayShapeType' => $vendorDir . '/symfony/type-info/Type/ArrayShapeType.php', - 'Symfony\\Component\\TypeInfo\\Type\\BackedEnumType' => $vendorDir . '/symfony/type-info/Type/BackedEnumType.php', - 'Symfony\\Component\\TypeInfo\\Type\\BuiltinType' => $vendorDir . '/symfony/type-info/Type/BuiltinType.php', - 'Symfony\\Component\\TypeInfo\\Type\\CollectionType' => $vendorDir . '/symfony/type-info/Type/CollectionType.php', - 'Symfony\\Component\\TypeInfo\\Type\\CompositeTypeInterface' => $vendorDir . '/symfony/type-info/Type/CompositeTypeInterface.php', - 'Symfony\\Component\\TypeInfo\\Type\\EnumType' => $vendorDir . '/symfony/type-info/Type/EnumType.php', - 'Symfony\\Component\\TypeInfo\\Type\\GenericType' => $vendorDir . '/symfony/type-info/Type/GenericType.php', - 'Symfony\\Component\\TypeInfo\\Type\\IntersectionType' => $vendorDir . '/symfony/type-info/Type/IntersectionType.php', - 'Symfony\\Component\\TypeInfo\\Type\\NullableType' => $vendorDir . '/symfony/type-info/Type/NullableType.php', - 'Symfony\\Component\\TypeInfo\\Type\\ObjectType' => $vendorDir . '/symfony/type-info/Type/ObjectType.php', - 'Symfony\\Component\\TypeInfo\\Type\\TemplateType' => $vendorDir . '/symfony/type-info/Type/TemplateType.php', - 'Symfony\\Component\\TypeInfo\\Type\\UnionType' => $vendorDir . '/symfony/type-info/Type/UnionType.php', - 'Symfony\\Component\\TypeInfo\\Type\\WrappingTypeInterface' => $vendorDir . '/symfony/type-info/Type/WrappingTypeInterface.php', 'Symfony\\Component\\Uid\\AbstractUid' => $vendorDir . '/symfony/uid/AbstractUid.php', 'Symfony\\Component\\Uid\\BinaryUtil' => $vendorDir . '/symfony/uid/BinaryUtil.php', 'Symfony\\Component\\Uid\\Command\\GenerateUlidCommand' => $vendorDir . '/symfony/uid/Command/GenerateUlidCommand.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 979d77c4..b43cf8f9 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -19,11 +19,13 @@ return array( '662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php', '35a6ad97d21e794e7e22a17d806652e4' => $vendorDir . '/nunomaduro/termwind/src/Functions.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', + '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', '2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php', '09f6b20656683369174dd6fa83b7e5fb' => $vendorDir . '/symfony/polyfill-uuid/bootstrap.php', 'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '47e1160838b5e5a10346ac4084b58c23' => $vendorDir . '/laravel/prompts/src/helpers.php', + '801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php', 'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php', '9d2b9fc6db0f153a0a149fefb182415e' => $vendorDir . '/symfony/polyfill-php84/bootstrap.php', '476ca15b8d69b04665cd879be9cb4c68' => $vendorDir . '/laravel/framework/src/Illuminate/Collections/functions.php', @@ -35,9 +37,6 @@ return array( '91892b814db86b8442ad76273bb7aec5' => $vendorDir . '/laravel/framework/src/Illuminate/Reflection/helpers.php', '493c6aea52f6009bab023b26c21a386a' => $vendorDir . '/laravel/framework/src/Illuminate/Support/functions.php', '58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php', - '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', - '801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php', - 'ac0aa5b57142c92aeadc397fa46b9d39' => $vendorDir . '/darkaonline/l5-swagger/src/helpers.php', 'c72349b1fe8d0deeedd3a52e8aa814d8' => $vendorDir . '/mockery/mockery/library/helpers.php', 'ce9671a430e4846b44e1c68c7611f9f5' => $vendorDir . '/mockery/mockery/library/Mockery.php', 'a1cfe24d14977df6878b9bf804af2d1c' => $vendorDir . '/nunomaduro/collision/src/Adapters/Phpunit/Autoload.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 7d72c7dc..12ee813c 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -27,7 +27,6 @@ return array( 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'), 'Symfony\\Component\\Uid\\' => array($vendorDir . '/symfony/uid'), - 'Symfony\\Component\\TypeInfo\\' => array($vendorDir . '/symfony/type-info'), 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'), 'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'), 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), @@ -42,9 +41,9 @@ return array( 'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Symfony\\Component\\Clock\\' => array($vendorDir . '/symfony/clock'), + 'Spatie\\LaravelPackageTools\\' => array($vendorDir . '/spatie/laravel-package-tools/src'), 'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'), 'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'), - 'Radebatz\\TypeInfoExtras\\' => array($vendorDir . '/radebatz/type-info-extras/src'), 'Psy\\' => array($vendorDir . '/psy/psysh/src'), 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), @@ -56,7 +55,6 @@ return array( 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), 'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'), 'PHPStan\\PhpDocParser\\' => array($vendorDir . '/phpstan/phpdoc-parser/src'), - 'OpenApi\\' => array($vendorDir . '/zircote/swagger-php/src'), 'NunoMaduro\\Collision\\' => array($vendorDir . '/nunomaduro/collision/src'), 'Nette\\' => array($vendorDir . '/nette/schema/src', $vendorDir . '/nette/utils/src'), 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), @@ -73,7 +71,6 @@ return array( 'Laravel\\Sail\\' => array($vendorDir . '/laravel/sail/src'), 'Laravel\\Prompts\\' => array($vendorDir . '/laravel/prompts/src'), 'Laravel\\Pail\\' => array($vendorDir . '/laravel/pail/src'), - 'L5Swagger\\' => array($vendorDir . '/darkaonline/l5-swagger/src'), 'Illuminate\\Support\\' => array($vendorDir . '/laravel/framework/src/Illuminate/Macroable', $vendorDir . '/laravel/framework/src/Illuminate/Collections', $vendorDir . '/laravel/framework/src/Illuminate/Conditionable', $vendorDir . '/laravel/framework/src/Illuminate/Reflection'), 'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'), 'GuzzleHttp\\UriTemplate\\' => array($vendorDir . '/guzzlehttp/uri-template/src'), @@ -89,6 +86,8 @@ return array( 'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/src'), 'Dflydev\\DotAccessData\\' => array($vendorDir . '/dflydev/dot-access-data/src'), 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), + 'Dedoc\\Scramble\\Database\\Factories\\' => array($vendorDir . '/dedoc/scramble/database/factories'), + 'Dedoc\\Scramble\\' => array($vendorDir . '/dedoc/scramble/src'), 'Database\\Seeders\\' => array($baseDir . '/database/seeders', $vendorDir . '/laravel/pint/database/seeders'), 'Database\\Factories\\' => array($baseDir . '/database/factories', $vendorDir . '/laravel/pint/database/factories'), 'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 748fff55..b26ed395 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -20,11 +20,13 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 '662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php', '35a6ad97d21e794e7e22a17d806652e4' => __DIR__ . '/..' . '/nunomaduro/termwind/src/Functions.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', + '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', '2203a247e6fda86070a5e4e07aed533a' => __DIR__ . '/..' . '/symfony/clock/Resources/now.php', '09f6b20656683369174dd6fa83b7e5fb' => __DIR__ . '/..' . '/symfony/polyfill-uuid/bootstrap.php', 'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '47e1160838b5e5a10346ac4084b58c23' => __DIR__ . '/..' . '/laravel/prompts/src/helpers.php', + '801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php', 'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php', '9d2b9fc6db0f153a0a149fefb182415e' => __DIR__ . '/..' . '/symfony/polyfill-php84/bootstrap.php', '476ca15b8d69b04665cd879be9cb4c68' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Collections/functions.php', @@ -36,9 +38,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 '91892b814db86b8442ad76273bb7aec5' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Reflection/helpers.php', '493c6aea52f6009bab023b26c21a386a' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Support/functions.php', '58571171fd5812e6e447dce228f52f4d' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Support/helpers.php', - '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', - '801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php', - 'ac0aa5b57142c92aeadc397fa46b9d39' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/helpers.php', 'c72349b1fe8d0deeedd3a52e8aa814d8' => __DIR__ . '/..' . '/mockery/mockery/library/helpers.php', 'ce9671a430e4846b44e1c68c7611f9f5' => __DIR__ . '/..' . '/mockery/mockery/library/Mockery.php', 'a1cfe24d14977df6878b9bf804af2d1c' => __DIR__ . '/..' . '/nunomaduro/collision/src/Adapters/Phpunit/Autoload.php', @@ -78,7 +77,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Symfony\\Component\\Yaml\\' => 23, 'Symfony\\Component\\VarDumper\\' => 28, 'Symfony\\Component\\Uid\\' => 22, - 'Symfony\\Component\\TypeInfo\\' => 27, 'Symfony\\Component\\Translation\\' => 30, 'Symfony\\Component\\String\\' => 25, 'Symfony\\Component\\Routing\\' => 26, @@ -93,12 +91,12 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Symfony\\Component\\CssSelector\\' => 30, 'Symfony\\Component\\Console\\' => 26, 'Symfony\\Component\\Clock\\' => 24, + 'Spatie\\LaravelPackageTools\\' => 27, ), 'R' => array ( 'Ramsey\\Uuid\\' => 12, 'Ramsey\\Collection\\' => 18, - 'Radebatz\\TypeInfoExtras\\' => 24, ), 'P' => array ( @@ -114,10 +112,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'PhpOption\\' => 10, 'PHPStan\\PhpDocParser\\' => 21, ), - 'O' => - array ( - 'OpenApi\\' => 8, - ), 'N' => array ( 'NunoMaduro\\Collision\\' => 21, @@ -142,7 +136,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Laravel\\Sail\\' => 13, 'Laravel\\Prompts\\' => 16, 'Laravel\\Pail\\' => 13, - 'L5Swagger\\' => 10, ), 'I' => array ( @@ -173,6 +166,8 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Doctrine\\Common\\Lexer\\' => 22, 'Dflydev\\DotAccessData\\' => 22, 'DeepCopy\\' => 9, + 'Dedoc\\Scramble\\Database\\Factories\\' => 34, + 'Dedoc\\Scramble\\' => 15, 'Database\\Seeders\\' => 17, 'Database\\Factories\\' => 19, ), @@ -277,10 +272,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/symfony/uid', ), - 'Symfony\\Component\\TypeInfo\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/type-info', - ), 'Symfony\\Component\\Translation\\' => array ( 0 => __DIR__ . '/..' . '/symfony/translation', @@ -337,6 +328,10 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/symfony/clock', ), + 'Spatie\\LaravelPackageTools\\' => + array ( + 0 => __DIR__ . '/..' . '/spatie/laravel-package-tools/src', + ), 'Ramsey\\Uuid\\' => array ( 0 => __DIR__ . '/..' . '/ramsey/uuid/src', @@ -345,10 +340,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/ramsey/collection/src', ), - 'Radebatz\\TypeInfoExtras\\' => - array ( - 0 => __DIR__ . '/..' . '/radebatz/type-info-extras/src', - ), 'Psy\\' => array ( 0 => __DIR__ . '/..' . '/psy/psysh/src', @@ -394,10 +385,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src', ), - 'OpenApi\\' => - array ( - 0 => __DIR__ . '/..' . '/zircote/swagger-php/src', - ), 'NunoMaduro\\Collision\\' => array ( 0 => __DIR__ . '/..' . '/nunomaduro/collision/src', @@ -464,10 +451,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/laravel/pail/src', ), - 'L5Swagger\\' => - array ( - 0 => __DIR__ . '/..' . '/darkaonline/l5-swagger/src', - ), 'Illuminate\\Support\\' => array ( 0 => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Macroable', @@ -531,6 +514,14 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy', ), + 'Dedoc\\Scramble\\Database\\Factories\\' => + array ( + 0 => __DIR__ . '/..' . '/dedoc/scramble/database/factories', + ), + 'Dedoc\\Scramble\\' => + array ( + 0 => __DIR__ . '/..' . '/dedoc/scramble/src', + ), 'Database\\Seeders\\' => array ( 0 => __DIR__ . '/../..' . '/database/seeders', @@ -566,10 +557,25 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 public static $classMap = array ( 'App\\Docs\\OpenApiSpec' => __DIR__ . '/../..' . '/app/Docs/OpenApiSpec.php', + 'App\\Http\\Controllers\\ApiController' => __DIR__ . '/../..' . '/app/Http/Controllers/ApiController.php', 'App\\Http\\Controllers\\AuthController' => __DIR__ . '/../..' . '/app/Http/Controllers/AuthController.php', 'App\\Http\\Controllers\\Controller' => __DIR__ . '/../..' . '/app/Http/Controllers/Controller.php', + 'App\\Http\\Controllers\\TicketController' => __DIR__ . '/../..' . '/app/Http/Controllers/TicketController.php', + 'App\\Http\\Controllers\\Ticket\\TicketController' => __DIR__ . '/../..' . '/app/Http/Controllers/Ticket/TicketController.php', + 'App\\Http\\Requests\\Ticket\\StoreTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/StoreTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\UpdateTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/UpdateTicketRequest.php', + 'App\\Http\\Resources\\TicketResource' => __DIR__ . '/../..' . '/app/Http/Resources/TicketResource.php', + 'App\\Models\\Ticket' => __DIR__ . '/../..' . '/app/Models/Ticket.php', + 'App\\Models\\TicketApproval' => __DIR__ . '/../..' . '/app/Models/TicketApproval.php', + 'App\\Models\\TicketAssignment' => __DIR__ . '/../..' . '/app/Models/TicketAssignment.php', + 'App\\Models\\TicketFile' => __DIR__ . '/../..' . '/app/Models/TicketFile.php', + 'App\\Models\\TicketLog' => __DIR__ . '/../..' . '/app/Models/TicketLog.php', + 'App\\Models\\TicketMaterial' => __DIR__ . '/../..' . '/app/Models/TicketMaterial.php', + 'App\\Models\\TicketType' => __DIR__ . '/../..' . '/app/Models/TicketType.php', 'App\\Models\\User' => __DIR__ . '/../..' . '/app/Models/User.php', 'App\\Providers\\AppServiceProvider' => __DIR__ . '/../..' . '/app/Providers/AppServiceProvider.php', + 'App\\Services\\Ticket\\TicketService' => __DIR__ . '/../..' . '/app/Services/Ticket/TicketService.php', + 'App\\Traits\\ApiResponse' => __DIR__ . '/../..' . '/app/Traits/ApiResponse.php', 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Brick\\Math\\BigDecimal' => __DIR__ . '/..' . '/brick/math/src/BigDecimal.php', 'Brick\\Math\\BigInteger' => __DIR__ . '/..' . '/brick/math/src/BigInteger.php', @@ -704,6 +710,425 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'DateMalformedStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php', 'DateObjectError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php', 'DateRangeError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php', + 'Dedoc\\Scramble\\AbstractOpenApiVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/AbstractOpenApiVisitor.php', + 'Dedoc\\Scramble\\Attributes\\BodyParameter' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/BodyParameter.php', + 'Dedoc\\Scramble\\Attributes\\CookieParameter' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/CookieParameter.php', + 'Dedoc\\Scramble\\Attributes\\Endpoint' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/Endpoint.php', + 'Dedoc\\Scramble\\Attributes\\Example' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/Example.php', + 'Dedoc\\Scramble\\Attributes\\ExcludeAllRoutesFromDocs' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/ExcludeAllRoutesFromDocs.php', + 'Dedoc\\Scramble\\Attributes\\ExcludeRouteFromDocs' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/ExcludeRouteFromDocs.php', + 'Dedoc\\Scramble\\Attributes\\Group' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/Group.php', + 'Dedoc\\Scramble\\Attributes\\Header' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/Header.php', + 'Dedoc\\Scramble\\Attributes\\HeaderParameter' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/HeaderParameter.php', + 'Dedoc\\Scramble\\Attributes\\Hidden' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/Hidden.php', + 'Dedoc\\Scramble\\Attributes\\IgnoreParam' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/IgnoreParam.php', + 'Dedoc\\Scramble\\Attributes\\MissingValue' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/MissingValue.php', + 'Dedoc\\Scramble\\Attributes\\Parameter' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/Parameter.php', + 'Dedoc\\Scramble\\Attributes\\PathParameter' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/PathParameter.php', + 'Dedoc\\Scramble\\Attributes\\QueryParameter' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/QueryParameter.php', + 'Dedoc\\Scramble\\Attributes\\Response' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/Response.php', + 'Dedoc\\Scramble\\Attributes\\SchemaName' => __DIR__ . '/..' . '/dedoc/scramble/src/Attributes/SchemaName.php', + 'Dedoc\\Scramble\\CacheableGenerator' => __DIR__ . '/..' . '/dedoc/scramble/src/CacheableGenerator.php', + 'Dedoc\\Scramble\\Configuration\\ApiPath' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/ApiPath.php', + 'Dedoc\\Scramble\\Configuration\\DocumentTransformers' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/DocumentTransformers.php', + 'Dedoc\\Scramble\\Configuration\\GeneratorConfigCollection' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/GeneratorConfigCollection.php', + 'Dedoc\\Scramble\\Configuration\\InferConfig' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/InferConfig.php', + 'Dedoc\\Scramble\\Configuration\\JsonApiConfig' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/JsonApiConfig.php', + 'Dedoc\\Scramble\\Configuration\\OperationTransformers' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/OperationTransformers.php', + 'Dedoc\\Scramble\\Configuration\\ParametersExtractors' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/ParametersExtractors.php', + 'Dedoc\\Scramble\\Configuration\\RendererConfig' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/RendererConfig.php', + 'Dedoc\\Scramble\\Configuration\\RuleTransformers' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/RuleTransformers.php', + 'Dedoc\\Scramble\\Configuration\\SecurityDocumentationContext' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/SecurityDocumentationContext.php', + 'Dedoc\\Scramble\\Configuration\\ServerVariables' => __DIR__ . '/..' . '/dedoc/scramble/src/Configuration/ServerVariables.php', + 'Dedoc\\Scramble\\Console\\Commands\\AnalyzeDocumentation' => __DIR__ . '/..' . '/dedoc/scramble/src/Console/Commands/AnalyzeDocumentation.php', + 'Dedoc\\Scramble\\Console\\Commands\\CacheDocumentation' => __DIR__ . '/..' . '/dedoc/scramble/src/Console/Commands/CacheDocumentation.php', + 'Dedoc\\Scramble\\Console\\Commands\\ClearDocumentationCache' => __DIR__ . '/..' . '/dedoc/scramble/src/Console/Commands/ClearDocumentationCache.php', + 'Dedoc\\Scramble\\Console\\Commands\\Components\\Code' => __DIR__ . '/..' . '/dedoc/scramble/src/Console/Commands/Components/Code.php', + 'Dedoc\\Scramble\\Console\\Commands\\Components\\Component' => __DIR__ . '/..' . '/dedoc/scramble/src/Console/Commands/Components/Component.php', + 'Dedoc\\Scramble\\Console\\Commands\\Components\\TermsOfContentItem' => __DIR__ . '/..' . '/dedoc/scramble/src/Console/Commands/Components/TermsOfContentItem.php', + 'Dedoc\\Scramble\\Console\\Commands\\Concerns\\ManagesDocumentationCache' => __DIR__ . '/..' . '/dedoc/scramble/src/Console/Commands/Concerns/ManagesDocumentationCache.php', + 'Dedoc\\Scramble\\Console\\Commands\\ExportDocumentation' => __DIR__ . '/..' . '/dedoc/scramble/src/Console/Commands/ExportDocumentation.php', + 'Dedoc\\Scramble\\ContextReferences' => __DIR__ . '/..' . '/dedoc/scramble/src/ContextReferences.php', + 'Dedoc\\Scramble\\ContextReferencesCollection' => __DIR__ . '/..' . '/dedoc/scramble/src/ContextReferencesCollection.php', + 'Dedoc\\Scramble\\Contracts\\AllRulesSchemasTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/Contracts/AllRulesSchemasTransformer.php', + 'Dedoc\\Scramble\\Contracts\\DocumentTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/Contracts/DocumentTransformer.php', + 'Dedoc\\Scramble\\Contracts\\OperationTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/Contracts/OperationTransformer.php', + 'Dedoc\\Scramble\\Contracts\\RuleTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/Contracts/RuleTransformer.php', + 'Dedoc\\Scramble\\Contracts\\SecurityDocumentationStrategy' => __DIR__ . '/..' . '/dedoc/scramble/src/Contracts/SecurityDocumentationStrategy.php', + 'Dedoc\\Scramble\\DocumentTransformers\\AddDocumentTags' => __DIR__ . '/..' . '/dedoc/scramble/src/DocumentTransformers/AddDocumentTags.php', + 'Dedoc\\Scramble\\DocumentTransformers\\CleanupUnusedResponseReferencesTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/DocumentTransformers/CleanupUnusedResponseReferencesTransformer.php', + 'Dedoc\\Scramble\\Enums\\JsonApiArraySerialization' => __DIR__ . '/..' . '/dedoc/scramble/src/Enums/JsonApiArraySerialization.php', + 'Dedoc\\Scramble\\Exceptions\\ConsoleRenderable' => __DIR__ . '/..' . '/dedoc/scramble/src/Exceptions/ConsoleRenderable.php', + 'Dedoc\\Scramble\\Exceptions\\InvalidSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Exceptions/InvalidSchema.php', + 'Dedoc\\Scramble\\Exceptions\\OpenApiReferenceTargetNotFoundException' => __DIR__ . '/..' . '/dedoc/scramble/src/Exceptions/OpenApiReferenceTargetNotFoundException.php', + 'Dedoc\\Scramble\\Exceptions\\RouteAware' => __DIR__ . '/..' . '/dedoc/scramble/src/Exceptions/RouteAware.php', + 'Dedoc\\Scramble\\Exceptions\\RouteAwareTrait' => __DIR__ . '/..' . '/dedoc/scramble/src/Exceptions/RouteAwareTrait.php', + 'Dedoc\\Scramble\\Exceptions\\RulesEvaluationException' => __DIR__ . '/..' . '/dedoc/scramble/src/Exceptions/RulesEvaluationException.php', + 'Dedoc\\Scramble\\Extensions\\ExceptionToResponseExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Extensions/ExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Extensions\\OperationExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Extensions/OperationExtension.php', + 'Dedoc\\Scramble\\Extensions\\TypeToSchemaExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Extensions/TypeToSchemaExtension.php', + 'Dedoc\\Scramble\\Generator' => __DIR__ . '/..' . '/dedoc/scramble/src/Generator.php', + 'Dedoc\\Scramble\\GeneratorConfig' => __DIR__ . '/..' . '/dedoc/scramble/src/GeneratorConfig.php', + 'Dedoc\\Scramble\\Http\\Middleware\\RestrictedDocsAccess' => __DIR__ . '/..' . '/dedoc/scramble/src/Http/Middleware/RestrictedDocsAccess.php', + 'Dedoc\\Scramble\\Infer' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer.php', + 'Dedoc\\Scramble\\Infer\\Analyzer\\ClassAnalyzer' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Analyzer/ClassAnalyzer.php', + 'Dedoc\\Scramble\\Infer\\Analyzer\\MethodAnalyzer' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Analyzer/MethodAnalyzer.php', + 'Dedoc\\Scramble\\Infer\\Analyzer\\PropertyAnalyzer' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Analyzer/PropertyAnalyzer.php', + 'Dedoc\\Scramble\\Infer\\AutoResolvingArgumentTypeBag' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/AutoResolvingArgumentTypeBag.php', + 'Dedoc\\Scramble\\Infer\\Configuration\\ClassLike' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Configuration/ClassLike.php', + 'Dedoc\\Scramble\\Infer\\Configuration\\ClassLikeAndChildren' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Configuration/ClassLikeAndChildren.php', + 'Dedoc\\Scramble\\Infer\\Configuration\\DefinitionMatcher' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Configuration/DefinitionMatcher.php', + 'Dedoc\\Scramble\\Infer\\Context' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Context.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\ArgumentTypeBag' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Contracts/ArgumentTypeBag.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\ClassDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Contracts/ClassDefinition.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\ClassDefinitionBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Contracts/ClassDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\FunctionLikeDefinitionBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Contracts/FunctionLikeDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\Contracts\\Index' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Contracts/Index.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\FunctionLikeAstDefinitionBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeAstDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\FunctionLikeDeclarationAstDefinitionBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationAstDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\FunctionLikeDeclarationPhpDocDefinitionBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationPhpDocDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\FunctionLikeReflectionDefinitionBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeReflectionDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\LazyClassReflectionDefinitionBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/DefinitionBuilders/LazyClassReflectionDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\SelfOutTypeBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/DefinitionBuilders/SelfOutTypeBuilder.php', + 'Dedoc\\Scramble\\Infer\\DefinitionBuilders\\ShallowClassReflectionDefinitionBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/DefinitionBuilders/ShallowClassReflectionDefinitionBuilder.php', + 'Dedoc\\Scramble\\Infer\\Definition\\AttributeDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/AttributeDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/ClassDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/ClassPropertyDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\FunctionLikeAstDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/FunctionLikeAstDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\FunctionLikeDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/FunctionLikeDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\LazyShallowClassDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/LazyShallowClassDefinition.php', + 'Dedoc\\Scramble\\Infer\\Definition\\PendingDocComment' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/PendingDocComment.php', + 'Dedoc\\Scramble\\Infer\\Definition\\PropertyVisibility' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/PropertyVisibility.php', + 'Dedoc\\Scramble\\Infer\\Definition\\ShallowClassDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Definition/ShallowClassDefinition.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\AfterClassDefinitionCreatedExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/AfterClassDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\AfterSideEffectCallAnalyzed' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/AfterSideEffectCallAnalyzed.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\AnyMethodReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/AnyMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\AnyMethodCallEvent' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/AnyMethodCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\ClassDefinitionCreatedEvent' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/ClassDefinitionCreatedEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\Concerns\\ArgumentTypesAware' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/Concerns/ArgumentTypesAware.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\FunctionCallEvent' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/FunctionCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\MethodCallEvent' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/MethodCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\PropertyFetchEvent' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/PropertyFetchEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\ReferenceResolutionEvent' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/ReferenceResolutionEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\SideEffectCallEvent' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/SideEffectCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\Event\\StaticMethodCallEvent' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/Event/StaticMethodCallEvent.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\ExpressionExceptionExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/ExpressionExceptionExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\ExpressionTypeInferExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/ExpressionTypeInferExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\ExtensionsBroker' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/ExtensionsBroker.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\FunctionReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/FunctionReturnTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\IndexBuildingBroker' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/IndexBuildingBroker.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\InferExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/InferExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\MethodCallExceptionsExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/MethodCallExceptionsExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\MethodReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/MethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\PropertyTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/PropertyTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\ResolvingType' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/ResolvingType.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\StaticMethodReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/StaticMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Infer\\Extensions\\TypeResolverExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Extensions/TypeResolverExtension.php', + 'Dedoc\\Scramble\\Infer\\FlowBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/FlowBuilder.php', + 'Dedoc\\Scramble\\Infer\\Flow\\AbstractNode' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/AbstractNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\ConditionNode' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/ConditionNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\Edge' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/Edge.php', + 'Dedoc\\Scramble\\Infer\\Flow\\ExpressionTypeInferrer' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/ExpressionTypeInferrer.php', + 'Dedoc\\Scramble\\Infer\\Flow\\MergeNode' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/MergeNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\Node' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/Node.php', + 'Dedoc\\Scramble\\Infer\\Flow\\Nodes' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/Nodes.php', + 'Dedoc\\Scramble\\Infer\\Flow\\StartNode' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/StartNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\StatementNode' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/StatementNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\TerminateNode' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/TerminateNode.php', + 'Dedoc\\Scramble\\Infer\\Flow\\TerminationKind' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Flow/TerminationKind.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ArrayHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/ArrayHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ArrayItemHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/ArrayItemHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\AssignHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/AssignHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ClassHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/ClassHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\CreatesScope' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/CreatesScope.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ExceptionInferringExtensions' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/ExceptionInferringExtensions.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ExpressionTypeInferringExtensions' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/ExpressionTypeInferringExtensions.php', + 'Dedoc\\Scramble\\Infer\\Handler\\FunctionLikeHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/FunctionLikeHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\IndexBuildingHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/IndexBuildingHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\PhpDocHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/PhpDocHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\PropertyHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/PropertyHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ReturnHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/ReturnHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\ThrowHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/ThrowHandler.php', + 'Dedoc\\Scramble\\Infer\\Handler\\UnsetHandler' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Handler/UnsetHandler.php', + 'Dedoc\\Scramble\\Infer\\Reflector\\ClassReflector' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Reflector/ClassReflector.php', + 'Dedoc\\Scramble\\Infer\\Reflector\\ClosureReflector' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Reflector/ClosureReflector.php', + 'Dedoc\\Scramble\\Infer\\Reflector\\MethodReflector' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Reflector/MethodReflector.php', + 'Dedoc\\Scramble\\Infer\\Reflector\\PropertyReflector' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Reflector/PropertyReflector.php', + 'Dedoc\\Scramble\\Infer\\Scope\\GlobalScope' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Scope/GlobalScope.php', + 'Dedoc\\Scramble\\Infer\\Scope\\Index' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Scope/Index.php', + 'Dedoc\\Scramble\\Infer\\Scope\\LazyShallowReflectionIndex' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Scope/LazyShallowReflectionIndex.php', + 'Dedoc\\Scramble\\Infer\\Scope\\NodeTypesResolver' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Scope/NodeTypesResolver.php', + 'Dedoc\\Scramble\\Infer\\Scope\\Scope' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Scope/Scope.php', + 'Dedoc\\Scramble\\Infer\\Scope\\ScopeContext' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Scope/ScopeContext.php', + 'Dedoc\\Scramble\\Infer\\Services\\ConstFetchTypeGetter' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/ConstFetchTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\Services\\FileNameResolver' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/FileNameResolver.php', + 'Dedoc\\Scramble\\Infer\\Services\\FileParser' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/FileParser.php', + 'Dedoc\\Scramble\\Infer\\Services\\FileParserResult' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/FileParserResult.php', + 'Dedoc\\Scramble\\Infer\\Services\\KeyedArrayUnpackingTypeVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/KeyedArrayUnpackingTypeVisitor.php', + 'Dedoc\\Scramble\\Infer\\Services\\LateTypeResolvingTypeVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/LateTypeResolvingTypeVisitor.php', + 'Dedoc\\Scramble\\Infer\\Services\\RecursionGuard' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/RecursionGuard.php', + 'Dedoc\\Scramble\\Infer\\Services\\ReferenceTypeResolver' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/ReferenceTypeResolver.php', + 'Dedoc\\Scramble\\Infer\\Services\\ShallowTypeResolver' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/ShallowTypeResolver.php', + 'Dedoc\\Scramble\\Infer\\Services\\TemplateTypesSolver' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/TemplateTypesSolver.php', + 'Dedoc\\Scramble\\Infer\\Services\\TemplatesMap' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/TemplatesMap.php', + 'Dedoc\\Scramble\\Infer\\Services\\UnionNormalizingTypeVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Services/UnionNormalizingTypeVisitor.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\BooleanNotTypeGetter' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/SimpleTypeGetters/BooleanNotTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\CastTypeGetter' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/SimpleTypeGetters/CastTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\ClassConstFetchTypeGetter' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/SimpleTypeGetters/ClassConstFetchTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\ConstFetchTypeGetter' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/SimpleTypeGetters/ConstFetchTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\SimpleTypeGetters\\ScalarTypeGetter' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/SimpleTypeGetters/ScalarTypeGetter.php', + 'Dedoc\\Scramble\\Infer\\TypeInferer' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/TypeInferer.php', + 'Dedoc\\Scramble\\Infer\\UnresolvableArgumentTypeBag' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/UnresolvableArgumentTypeBag.php', + 'Dedoc\\Scramble\\Infer\\Visitors\\PhpDocResolver' => __DIR__ . '/..' . '/dedoc/scramble/src/Infer/Visitors/PhpDocResolver.php', + 'Dedoc\\Scramble\\OpenApiContext' => __DIR__ . '/..' . '/dedoc/scramble/src/OpenApiContext.php', + 'Dedoc\\Scramble\\OpenApiTraverser' => __DIR__ . '/..' . '/dedoc/scramble/src/OpenApiTraverser.php', + 'Dedoc\\Scramble\\OpenApiVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/OpenApiVisitor.php', + 'Dedoc\\Scramble\\OpenApiVisitor\\SchemaEnforceVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/OpenApiVisitor/SchemaEnforceVisitor.php', + 'Dedoc\\Scramble\\PhpDoc\\AbstractPhpDocTypeVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/PhpDoc/AbstractPhpDocTypeVisitor.php', + 'Dedoc\\Scramble\\PhpDoc\\PhpDocParser' => __DIR__ . '/..' . '/dedoc/scramble/src/PhpDoc/PhpDocParser.php', + 'Dedoc\\Scramble\\PhpDoc\\PhpDocTypeHelper' => __DIR__ . '/..' . '/dedoc/scramble/src/PhpDoc/PhpDocTypeHelper.php', + 'Dedoc\\Scramble\\PhpDoc\\PhpDocTypeVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/PhpDoc/PhpDocTypeVisitor.php', + 'Dedoc\\Scramble\\PhpDoc\\PhpDocTypeWalker' => __DIR__ . '/..' . '/dedoc/scramble/src/PhpDoc/PhpDocTypeWalker.php', + 'Dedoc\\Scramble\\PhpDoc\\ResolveFqnPhpDocTypeVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/PhpDoc/ResolveFqnPhpDocTypeVisitor.php', + 'Dedoc\\Scramble\\Reflection\\JsonApiRelationship' => __DIR__ . '/..' . '/dedoc/scramble/src/Reflection/JsonApiRelationship.php', + 'Dedoc\\Scramble\\Reflection\\ReflectionJsonApiResource' => __DIR__ . '/..' . '/dedoc/scramble/src/Reflection/ReflectionJsonApiResource.php', + 'Dedoc\\Scramble\\Reflection\\ReflectionModel' => __DIR__ . '/..' . '/dedoc/scramble/src/Reflection/ReflectionModel.php', + 'Dedoc\\Scramble\\Reflection\\ReflectionRoute' => __DIR__ . '/..' . '/dedoc/scramble/src/Reflection/ReflectionRoute.php', + 'Dedoc\\Scramble\\RuleTransformers\\ConfirmedRule' => __DIR__ . '/..' . '/dedoc/scramble/src/RuleTransformers/ConfirmedRule.php', + 'Dedoc\\Scramble\\RuleTransformers\\EnumRule' => __DIR__ . '/..' . '/dedoc/scramble/src/RuleTransformers/EnumRule.php', + 'Dedoc\\Scramble\\RuleTransformers\\ExistsRule' => __DIR__ . '/..' . '/dedoc/scramble/src/RuleTransformers/ExistsRule.php', + 'Dedoc\\Scramble\\RuleTransformers\\InRule' => __DIR__ . '/..' . '/dedoc/scramble/src/RuleTransformers/InRule.php', + 'Dedoc\\Scramble\\RuleTransformers\\RegexRule' => __DIR__ . '/..' . '/dedoc/scramble/src/RuleTransformers/RegexRule.php', + 'Dedoc\\Scramble\\SchemaValidator' => __DIR__ . '/..' . '/dedoc/scramble/src/SchemaValidator.php', + 'Dedoc\\Scramble\\Scramble' => __DIR__ . '/..' . '/dedoc/scramble/src/Scramble.php', + 'Dedoc\\Scramble\\ScrambleServiceProvider' => __DIR__ . '/..' . '/dedoc/scramble/src/ScrambleServiceProvider.php', + 'Dedoc\\Scramble\\SecurityDocumentation\\MiddlewareAuthSecurityStrategy' => __DIR__ . '/..' . '/dedoc/scramble/src/SecurityDocumentation/MiddlewareAuthSecurityStrategy.php', + 'Dedoc\\Scramble\\Support\\ContainerUtils' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/ContainerUtils.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\AuthenticationExceptionToResponseExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthenticationExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\AuthorizationExceptionToResponseExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthorizationExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\HttpExceptionToResponseExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/HttpExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\NotFoundExceptionToResponseExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/NotFoundExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\ExceptionToResponseExtensions\\ValidationExceptionToResponseExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/ExceptionToResponseExtensions/ValidationExceptionToResponseExtension.php', + 'Dedoc\\Scramble\\Support\\Factories\\JsonApiQueryParameterFactory' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Factories/JsonApiQueryParameterFactory.php', + 'Dedoc\\Scramble\\Support\\Generator\\ClassBasedReference' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/ClassBasedReference.php', + 'Dedoc\\Scramble\\Support\\Generator\\Combined\\AllOf' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Combined/AllOf.php', + 'Dedoc\\Scramble\\Support\\Generator\\Combined\\AnyOf' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Combined/AnyOf.php', + 'Dedoc\\Scramble\\Support\\Generator\\Components' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Components.php', + 'Dedoc\\Scramble\\Support\\Generator\\Encoding' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Encoding.php', + 'Dedoc\\Scramble\\Support\\Generator\\Example' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Example.php', + 'Dedoc\\Scramble\\Support\\Generator\\Header' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Header.php', + 'Dedoc\\Scramble\\Support\\Generator\\InfoObject' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/InfoObject.php', + 'Dedoc\\Scramble\\Support\\Generator\\Link' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Link.php', + 'Dedoc\\Scramble\\Support\\Generator\\MediaType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/MediaType.php', + 'Dedoc\\Scramble\\Support\\Generator\\MissingExample' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/MissingExample.php', + 'Dedoc\\Scramble\\Support\\Generator\\MissingValue' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/MissingValue.php', + 'Dedoc\\Scramble\\Support\\Generator\\OpenApi' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/OpenApi.php', + 'Dedoc\\Scramble\\Support\\Generator\\Operation' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Operation.php', + 'Dedoc\\Scramble\\Support\\Generator\\Parameter' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Parameter.php', + 'Dedoc\\Scramble\\Support\\Generator\\Path' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Path.php', + 'Dedoc\\Scramble\\Support\\Generator\\Reference' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Reference.php', + 'Dedoc\\Scramble\\Support\\Generator\\RequestBodyObject' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/RequestBodyObject.php', + 'Dedoc\\Scramble\\Support\\Generator\\Response' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Response.php', + 'Dedoc\\Scramble\\Support\\Generator\\Schema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Schema.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecurityRequirement' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/SecurityRequirement.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecurityScheme' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/SecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\ApiKeySecurityScheme' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/ApiKeySecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\HttpSecurityScheme' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/HttpSecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\OAuthFlow' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlow.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\OAuthFlows' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlows.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\Oauth2SecurityScheme' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/Oauth2SecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\SecuritySchemes\\OpenIdConnectUrlSecurityScheme' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/SecuritySchemes/OpenIdConnectUrlSecurityScheme.php', + 'Dedoc\\Scramble\\Support\\Generator\\Server' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Server.php', + 'Dedoc\\Scramble\\Support\\Generator\\ServerVariable' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/ServerVariable.php', + 'Dedoc\\Scramble\\Support\\Generator\\Tag' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Tag.php', + 'Dedoc\\Scramble\\Support\\Generator\\TypeTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/TypeTransformer.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\ArrayType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/ArrayType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\BooleanType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/BooleanType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\IntegerType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/IntegerType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\MixedType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/MixedType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\NullType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/NullType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\NumberType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/NumberType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\ObjectType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/ObjectType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\StringType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/StringType.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\Type' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/Type.php', + 'Dedoc\\Scramble\\Support\\Generator\\Types\\UnknownType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/Types/UnknownType.php', + 'Dedoc\\Scramble\\Support\\Generator\\UniqueNameOptions' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/UniqueNameOptions.php', + 'Dedoc\\Scramble\\Support\\Generator\\UniqueNamesOptionsCollection' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/UniqueNamesOptionsCollection.php', + 'Dedoc\\Scramble\\Support\\Generator\\WithAttributes' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/WithAttributes.php', + 'Dedoc\\Scramble\\Support\\Generator\\WithExtensions' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Generator/WithExtensions.php', + 'Dedoc\\Scramble\\Support\\Helpers\\ExamplesExtractor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Helpers/ExamplesExtractor.php', + 'Dedoc\\Scramble\\Support\\Helpers\\JsonResourceHelper' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Helpers/JsonResourceHelper.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\Bag' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/IndexBuilders/Bag.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\IndexBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/IndexBuilders/IndexBuilder.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\PaginatorsCandidatesBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/IndexBuilders/PaginatorsCandidatesBuilder.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\RequestParametersBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/IndexBuilders/RequestParametersBuilder.php', + 'Dedoc\\Scramble\\Support\\IndexBuilders\\ScopeCollector' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/IndexBuilders/ScopeCollector.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AbortHelpersExceptionInfer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/AbortHelpersExceptionInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterAnonymousResourceCollectionDefinitionCreatedExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/AfterAnonymousResourceCollectionDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterEloquentCollectionDefinitionCreatedExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/AfterEloquentCollectionDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterJsonApiResourceDefinitionCreatedExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/AfterJsonApiResourceDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterJsonResourceDefinitionCreatedExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/AfterJsonResourceDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterResourceCollectionDefinitionCreatedExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/AfterResourceCollectionDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\AfterResponseDefinitionCreatedExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/AfterResponseDefinitionCreatedExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ArrayMergeReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/ArrayMergeReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\BinaryFileResponseTypeFactory' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/BinaryFileResponseTypeFactory.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\EloquentBuilderExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/EloquentBuilderExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\FacadeStaticMethodReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/FacadeStaticMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\JsonApiResourceCollectionMethodReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceCollectionMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\JsonApiResourceMethodReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\JsonResourceExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/JsonResourceExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\JsonResponseMethodReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/JsonResponseMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ModelCollectionTypeResolver' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/ModelCollectionTypeResolver.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ModelExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/ModelExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\PaginateMethodsReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/PaginateMethodsReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\PossibleExceptionInfer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/PossibleExceptionInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\RequestExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/RequestExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ResourceCollectionTypeInfer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/ResourceCollectionTypeInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ResourceResponseMethodReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/ResourceResponseMethodReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ResponseFactoryTypeInfer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/ResponseFactoryTypeInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ShallowFunctionDefinition' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/ShallowFunctionDefinition.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\TransformsToResourceCollectionExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/TransformsToResourceCollectionExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\TranslationReturnTypeExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/TranslationReturnTypeExtension.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\TypeTraceInfer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/TypeTraceInfer.php', + 'Dedoc\\Scramble\\Support\\InferExtensions\\ValidatorTypeInfer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/InferExtensions/ValidatorTypeInfer.php', + 'Dedoc\\Scramble\\Support\\OperationBuilder' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationBuilder.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\DeprecationExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/DeprecationExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ErrorResponsesExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ErrorResponsesExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\AttributesParametersExtractor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/AttributesParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\FormRequestParametersExtractor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\InferredParameter' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/InferredParameter.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\JsonApiResourceParametersExtractor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/JsonApiResourceParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\MethodCallsParametersExtractor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/MethodCallsParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\ParameterExtractor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ParameterExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\PathParametersExtractor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/PathParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\RulesDocumentationRetriever' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesDocumentationRetriever.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\RulesNodes' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesNodes.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\TypeBasedRulesDocumentationRetriever' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/TypeBasedRulesDocumentationRetriever.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ParameterExtractor\\ValidateCallParametersExtractor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RequestBodyExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RequestBodyExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RequestEssentialsExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RequestEssentialsExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ResponseExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ResponseExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\ResponseHeadersExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/ResponseHeadersExtension.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\ComposedFormRequestRulesEvaluator' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\ConstFetchEvaluator' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ConstFetchEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\FormRequestRulesEvaluator' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/FormRequestRulesEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\NodeRulesEvaluator' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\PublicProxy' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/PublicProxy.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesEvaluator\\RulesEvaluator' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/RulesEvaluator.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\DeepParametersMerger' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/DeepParametersMerger.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\GeneratesParametersFromRules' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/GeneratesParametersFromRules.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\ParametersExtractionResult' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/ParametersExtractionResult.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\PhpDocSchemaTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/PhpDocSchemaTransformer.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\QueryParametersConverter' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/QueryParametersConverter.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\RulesMapper' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\RulesToParameters' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesToParameters.php', + 'Dedoc\\Scramble\\Support\\OperationExtensions\\RulesExtractor\\SchemaBagToParametersTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/SchemaBagToParametersTransformer.php', + 'Dedoc\\Scramble\\Support\\PhpDoc' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/PhpDoc.php', + 'Dedoc\\Scramble\\Support\\ResponseExtractor\\ModelInfo' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/ResponseExtractor/ModelInfo.php', + 'Dedoc\\Scramble\\Support\\RouteInfo' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/RouteInfo.php', + 'Dedoc\\Scramble\\Support\\RouteResponseTypeRetriever' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/RouteResponseTypeRetriever.php', + 'Dedoc\\Scramble\\Support\\RuleTransforming\\NormalizedRule' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/RuleTransforming/NormalizedRule.php', + 'Dedoc\\Scramble\\Support\\RuleTransforming\\RuleSetToSchemaTransformer' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/RuleTransforming/RuleSetToSchemaTransformer.php', + 'Dedoc\\Scramble\\Support\\RuleTransforming\\RuleTransformerContext' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/RuleTransforming/RuleTransformerContext.php', + 'Dedoc\\Scramble\\Support\\RuleTransforming\\SchemaBag' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/RuleTransforming/SchemaBag.php', + 'Dedoc\\Scramble\\Support\\SchemaClassDocReflector' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/SchemaClassDocReflector.php', + 'Dedoc\\Scramble\\Support\\ServerFactory' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/ServerFactory.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\CursorPaginatorTypeManager' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeManagers/CursorPaginatorTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\JsonApiResourceTypeManager' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeManagers/JsonApiResourceTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\LengthAwarePaginatorTypeManager' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeManagers/LengthAwarePaginatorTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\ManagesProperties' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeManagers/ManagesProperties.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\PaginatorTypeManager' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeManagers/PaginatorTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeManagers\\ResourceCollectionTypeManager' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeManagers/ResourceCollectionTypeManager.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\AnonymousResourceCollectionTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/AnonymousResourceCollectionTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ArrayableToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ArrayableToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\BinaryFileResponseToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/BinaryFileResponseToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\CarbonInterfaceToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/CarbonInterfaceToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\CollectionToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/CollectionToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\CursorPaginatorTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/CursorPaginatorTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\EloquentCollectionToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/EloquentCollectionToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\EnumToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/EnumToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\FlattensMergeValues' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/FlattensMergeValues.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\HandlesJsonApiResourceResponse' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/HandlesJsonApiResourceResponse.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonApiAnonymousCollectionTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiAnonymousCollectionTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonApiPaginatedResourceResponseToSchemaExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiPaginatedResourceResponseToSchemaExtension.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonApiResourceResponseToSchemaExtension' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceResponseToSchemaExtension.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonApiResourceTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\JsonResourceTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonResourceTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\LengthAwarePaginatorTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/LengthAwarePaginatorTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\MergesOpenApiObjects' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/MergesOpenApiObjects.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ModelToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ModelToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\PaginatedResourceResponseTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatedResourceResponseTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\PaginatorTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatorTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\PlainObjectToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/PlainObjectToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ResourceCollectionTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceCollectionTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ResourceResponseTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceResponseTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ResponsableTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponsableTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\ResponseTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponseTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\StreamedResponseToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/StreamedResponseToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\VoidTypeToSchema' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/VoidTypeToSchema.php', + 'Dedoc\\Scramble\\Support\\TypeToSchemaExtensions\\WithCollectedPaginatedItems' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/TypeToSchemaExtensions/WithCollectedPaginatedItems.php', + 'Dedoc\\Scramble\\Support\\Type\\AbstractType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/AbstractType.php', + 'Dedoc\\Scramble\\Support\\Type\\AbstractTypeVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/AbstractTypeVisitor.php', + 'Dedoc\\Scramble\\Support\\Type\\ArrayItemType_' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/ArrayItemType_.php', + 'Dedoc\\Scramble\\Support\\Type\\ArrayType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/ArrayType.php', + 'Dedoc\\Scramble\\Support\\Type\\BooleanType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/BooleanType.php', + 'Dedoc\\Scramble\\Support\\Type\\CallableStringType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/CallableStringType.php', + 'Dedoc\\Scramble\\Support\\Type\\CoalesceType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/CoalesceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Contracts\\Generic' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Contracts/Generic.php', + 'Dedoc\\Scramble\\Support\\Type\\Contracts\\LateResolvingType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Contracts/LateResolvingType.php', + 'Dedoc\\Scramble\\Support\\Type\\Contracts\\LiteralString' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Contracts/LiteralString.php', + 'Dedoc\\Scramble\\Support\\Type\\Contracts\\LiteralType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Contracts/LiteralType.php', + 'Dedoc\\Scramble\\Support\\Type\\EnumCaseType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/EnumCaseType.php', + 'Dedoc\\Scramble\\Support\\Type\\FloatType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/FloatType.php', + 'Dedoc\\Scramble\\Support\\Type\\FunctionLikeType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/FunctionLikeType.php', + 'Dedoc\\Scramble\\Support\\Type\\FunctionType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/FunctionType.php', + 'Dedoc\\Scramble\\Support\\Type\\Generic' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Generic.php', + 'Dedoc\\Scramble\\Support\\Type\\GenericClassStringType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/GenericClassStringType.php', + 'Dedoc\\Scramble\\Support\\Type\\IntegerRangeType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/IntegerRangeType.php', + 'Dedoc\\Scramble\\Support\\Type\\IntegerType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/IntegerType.php', + 'Dedoc\\Scramble\\Support\\Type\\IntersectionType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/IntersectionType.php', + 'Dedoc\\Scramble\\Support\\Type\\KeyedArrayType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/KeyedArrayType.php', + 'Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralBooleanType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Literal/LiteralBooleanType.php', + 'Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralFloatType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Literal/LiteralFloatType.php', + 'Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralIntegerType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Literal/LiteralIntegerType.php', + 'Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Literal/LiteralStringType.php', + 'Dedoc\\Scramble\\Support\\Type\\MissingType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/MissingType.php', + 'Dedoc\\Scramble\\Support\\Type\\MixedType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/MixedType.php', + 'Dedoc\\Scramble\\Support\\Type\\NeverType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/NeverType.php', + 'Dedoc\\Scramble\\Support\\Type\\NullType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/NullType.php', + 'Dedoc\\Scramble\\Support\\Type\\ObjectType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/ObjectType.php', + 'Dedoc\\Scramble\\Support\\Type\\OffsetAccessType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/OffsetAccessType.php', + 'Dedoc\\Scramble\\Support\\Type\\OffsetSetType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/OffsetSetType.php', + 'Dedoc\\Scramble\\Support\\Type\\OffsetUnsetType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/OffsetUnsetType.php', + 'Dedoc\\Scramble\\Support\\Type\\RecursiveTemplateSolver' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/RecursiveTemplateSolver.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\AbstractReferenceType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/AbstractReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\CallableCallReferenceType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/CallableCallReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\ConstFetchReferenceType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/ConstFetchReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\MethodCallReferenceType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/MethodCallReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\NewCallReferenceType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/NewCallReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\PotentialMethodMutatingCallType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/PotentialMethodMutatingCallType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\PropertyFetchReferenceType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/PropertyFetchReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\StaticMethodCallReferenceType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/StaticMethodCallReferenceType.php', + 'Dedoc\\Scramble\\Support\\Type\\Reference\\StaticReference' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Reference/StaticReference.php', + 'Dedoc\\Scramble\\Support\\Type\\SelfType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/SelfType.php', + 'Dedoc\\Scramble\\Support\\Type\\StringType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/StringType.php', + 'Dedoc\\Scramble\\Support\\Type\\TemplatePlaceholderType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TemplatePlaceholderType.php', + 'Dedoc\\Scramble\\Support\\Type\\TemplateType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TemplateType.php', + 'Dedoc\\Scramble\\Support\\Type\\TranslatedStringType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TranslatedStringType.php', + 'Dedoc\\Scramble\\Support\\Type\\Type' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Type.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeAttributes' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypeAttributes.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeHelper' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypeHelper.php', + 'Dedoc\\Scramble\\Support\\Type\\TypePath' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypePath.php', + 'Dedoc\\Scramble\\Support\\Type\\TypePathFindingVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypePathFindingVisitor.php', + 'Dedoc\\Scramble\\Support\\Type\\TypePathItem' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypePathItem.php', + 'Dedoc\\Scramble\\Support\\Type\\TypePathItemCondition' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypePathItemCondition.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeTraverser' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypeTraverser.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeVisitor' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypeVisitor.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeWalker' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypeWalker.php', + 'Dedoc\\Scramble\\Support\\Type\\TypeWidener' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/TypeWidener.php', + 'Dedoc\\Scramble\\Support\\Type\\Union' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/Union.php', + 'Dedoc\\Scramble\\Support\\Type\\UnknownType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/UnknownType.php', + 'Dedoc\\Scramble\\Support\\Type\\VoidType' => __DIR__ . '/..' . '/dedoc/scramble/src/Support/Type/VoidType.php', 'DeepCopy\\DeepCopy' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/DeepCopy.php', 'DeepCopy\\Exception\\CloneException' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php', 'DeepCopy\\Exception\\PropertyException' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php', @@ -3043,18 +3468,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Illuminate\\View\\ViewFinderInterface' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewFinderInterface.php', 'Illuminate\\View\\ViewName' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewName.php', 'Illuminate\\View\\ViewServiceProvider' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewServiceProvider.php', - 'L5Swagger\\ConfigFactory' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/ConfigFactory.php', - 'L5Swagger\\Console\\GenerateDocsCommand' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/Console/GenerateDocsCommand.php', - 'L5Swagger\\CustomGeneratorInterface' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/CustomGeneratorInterface.php', - 'L5Swagger\\Exceptions\\L5SwaggerException' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/Exceptions/L5SwaggerException.php', - 'L5Swagger\\Generator' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/Generator.php', - 'L5Swagger\\GeneratorFactory' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/GeneratorFactory.php', - 'L5Swagger\\Http\\Controllers\\SwaggerAssetController' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/Http/Controllers/SwaggerAssetController.php', - 'L5Swagger\\Http\\Controllers\\SwaggerController' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/Http/Controllers/SwaggerController.php', - 'L5Swagger\\Http\\Middleware\\Config' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/Http/Middleware/Config.php', - 'L5Swagger\\L5SwaggerFacade' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/L5SwaggerFacade.php', - 'L5Swagger\\L5SwaggerServiceProvider' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/L5SwaggerServiceProvider.php', - 'L5Swagger\\SecurityDefinitions' => __DIR__ . '/..' . '/darkaonline/l5-swagger/src/SecurityDefinitions.php', 'Laravel\\Pail\\Console\\Commands\\PailCommand' => __DIR__ . '/..' . '/laravel/pail/src/Console/Commands/PailCommand.php', 'Laravel\\Pail\\Contracts\\Printer' => __DIR__ . '/..' . '/laravel/pail/src/Contracts/Printer.php', 'Laravel\\Pail\\File' => __DIR__ . '/..' . '/laravel/pail/src/File.php', @@ -3937,149 +4350,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'NunoMaduro\\Collision\\Provider' => __DIR__ . '/..' . '/nunomaduro/collision/src/Provider.php', 'NunoMaduro\\Collision\\SolutionsRepositories\\NullSolutionsRepository' => __DIR__ . '/..' . '/nunomaduro/collision/src/SolutionsRepositories/NullSolutionsRepository.php', 'NunoMaduro\\Collision\\Writer' => __DIR__ . '/..' . '/nunomaduro/collision/src/Writer.php', - 'OpenApi\\Analysers\\AnalyserInterface' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysers/AnalyserInterface.php', - 'OpenApi\\Analysers\\AnnotationFactoryInterface' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysers/AnnotationFactoryInterface.php', - 'OpenApi\\Analysers\\AttributeAnnotationFactory' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysers/AttributeAnnotationFactory.php', - 'OpenApi\\Analysers\\ComposerAutoloaderScanner' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysers/ComposerAutoloaderScanner.php', - 'OpenApi\\Analysers\\DocBlockAnnotationFactory' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysers/DocBlockAnnotationFactory.php', - 'OpenApi\\Analysers\\DocBlockParser' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysers/DocBlockParser.php', - 'OpenApi\\Analysers\\ReflectionAnalyser' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysers/ReflectionAnalyser.php', - 'OpenApi\\Analysers\\TokenScanner' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysers/TokenScanner.php', - 'OpenApi\\Analysis' => __DIR__ . '/..' . '/zircote/swagger-php/src/Analysis.php', - 'OpenApi\\Annotations\\AbstractAnnotation' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/AbstractAnnotation.php', - 'OpenApi\\Annotations\\AdditionalProperties' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/AdditionalProperties.php', - 'OpenApi\\Annotations\\Attachable' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Attachable.php', - 'OpenApi\\Annotations\\Components' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Components.php', - 'OpenApi\\Annotations\\Contact' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Contact.php', - 'OpenApi\\Annotations\\CookieParameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/CookieParameter.php', - 'OpenApi\\Annotations\\Delete' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Delete.php', - 'OpenApi\\Annotations\\Discriminator' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Discriminator.php', - 'OpenApi\\Annotations\\Encoding' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Encoding.php', - 'OpenApi\\Annotations\\Examples' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Examples.php', - 'OpenApi\\Annotations\\ExternalDocumentation' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/ExternalDocumentation.php', - 'OpenApi\\Annotations\\Flow' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Flow.php', - 'OpenApi\\Annotations\\Get' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Get.php', - 'OpenApi\\Annotations\\Head' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Head.php', - 'OpenApi\\Annotations\\Header' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Header.php', - 'OpenApi\\Annotations\\HeaderParameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/HeaderParameter.php', - 'OpenApi\\Annotations\\Info' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Info.php', - 'OpenApi\\Annotations\\Items' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Items.php', - 'OpenApi\\Annotations\\JsonContent' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/JsonContent.php', - 'OpenApi\\Annotations\\JsonSchemaTrait' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/JsonSchemaTrait.php', - 'OpenApi\\Annotations\\License' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/License.php', - 'OpenApi\\Annotations\\Link' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Link.php', - 'OpenApi\\Annotations\\MediaType' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/MediaType.php', - 'OpenApi\\Annotations\\OpenApi' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/OpenApi.php', - 'OpenApi\\Annotations\\Operation' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Operation.php', - 'OpenApi\\Annotations\\Options' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Options.php', - 'OpenApi\\Annotations\\Parameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Parameter.php', - 'OpenApi\\Annotations\\Patch' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Patch.php', - 'OpenApi\\Annotations\\PathItem' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/PathItem.php', - 'OpenApi\\Annotations\\PathParameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/PathParameter.php', - 'OpenApi\\Annotations\\Post' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Post.php', - 'OpenApi\\Annotations\\Property' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Property.php', - 'OpenApi\\Annotations\\Put' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Put.php', - 'OpenApi\\Annotations\\Query' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Query.php', - 'OpenApi\\Annotations\\QueryParameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/QueryParameter.php', - 'OpenApi\\Annotations\\RequestBody' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/RequestBody.php', - 'OpenApi\\Annotations\\Response' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Response.php', - 'OpenApi\\Annotations\\Schema' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Schema.php', - 'OpenApi\\Annotations\\SecurityScheme' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/SecurityScheme.php', - 'OpenApi\\Annotations\\Server' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Server.php', - 'OpenApi\\Annotations\\ServerVariable' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/ServerVariable.php', - 'OpenApi\\Annotations\\Tag' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Tag.php', - 'OpenApi\\Annotations\\Trace' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Trace.php', - 'OpenApi\\Annotations\\Webhook' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Webhook.php', - 'OpenApi\\Annotations\\Xml' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/Xml.php', - 'OpenApi\\Annotations\\XmlContent' => __DIR__ . '/..' . '/zircote/swagger-php/src/Annotations/XmlContent.php', - 'OpenApi\\Attributes\\AdditionalProperties' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/AdditionalProperties.php', - 'OpenApi\\Attributes\\Attachable' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Attachable.php', - 'OpenApi\\Attributes\\Components' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Components.php', - 'OpenApi\\Attributes\\Contact' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Contact.php', - 'OpenApi\\Attributes\\CookieParameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/CookieParameter.php', - 'OpenApi\\Attributes\\Delete' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Delete.php', - 'OpenApi\\Attributes\\Discriminator' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Discriminator.php', - 'OpenApi\\Attributes\\Encoding' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Encoding.php', - 'OpenApi\\Attributes\\Examples' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Examples.php', - 'OpenApi\\Attributes\\ExternalDocumentation' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/ExternalDocumentation.php', - 'OpenApi\\Attributes\\Flow' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Flow.php', - 'OpenApi\\Attributes\\Get' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Get.php', - 'OpenApi\\Attributes\\Head' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Head.php', - 'OpenApi\\Attributes\\Header' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Header.php', - 'OpenApi\\Attributes\\HeaderParameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/HeaderParameter.php', - 'OpenApi\\Attributes\\Info' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Info.php', - 'OpenApi\\Attributes\\Items' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Items.php', - 'OpenApi\\Attributes\\JsonContent' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/JsonContent.php', - 'OpenApi\\Attributes\\License' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/License.php', - 'OpenApi\\Attributes\\Link' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Link.php', - 'OpenApi\\Attributes\\MediaType' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/MediaType.php', - 'OpenApi\\Attributes\\OpenApi' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/OpenApi.php', - 'OpenApi\\Attributes\\OperationTrait' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/OperationTrait.php', - 'OpenApi\\Attributes\\Options' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Options.php', - 'OpenApi\\Attributes\\Parameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Parameter.php', - 'OpenApi\\Attributes\\ParameterTrait' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/ParameterTrait.php', - 'OpenApi\\Attributes\\Patch' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Patch.php', - 'OpenApi\\Attributes\\PathItem' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/PathItem.php', - 'OpenApi\\Attributes\\PathParameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/PathParameter.php', - 'OpenApi\\Attributes\\Post' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Post.php', - 'OpenApi\\Attributes\\Property' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Property.php', - 'OpenApi\\Attributes\\Put' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Put.php', - 'OpenApi\\Attributes\\Query' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Query.php', - 'OpenApi\\Attributes\\QueryParameter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/QueryParameter.php', - 'OpenApi\\Attributes\\RequestBody' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/RequestBody.php', - 'OpenApi\\Attributes\\Response' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Response.php', - 'OpenApi\\Attributes\\Schema' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Schema.php', - 'OpenApi\\Attributes\\SecurityScheme' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/SecurityScheme.php', - 'OpenApi\\Attributes\\Server' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Server.php', - 'OpenApi\\Attributes\\ServerVariable' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/ServerVariable.php', - 'OpenApi\\Attributes\\Tag' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Tag.php', - 'OpenApi\\Attributes\\Trace' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Trace.php', - 'OpenApi\\Attributes\\Webhook' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Webhook.php', - 'OpenApi\\Attributes\\Xml' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/Xml.php', - 'OpenApi\\Attributes\\XmlContent' => __DIR__ . '/..' . '/zircote/swagger-php/src/Attributes/XmlContent.php', - 'OpenApi\\Console\\GenerateCommand' => __DIR__ . '/..' . '/zircote/swagger-php/src/Console/GenerateCommand.php', - 'OpenApi\\Console\\GenerateFormat' => __DIR__ . '/..' . '/zircote/swagger-php/src/Console/GenerateFormat.php', - 'OpenApi\\Console\\GenerateInput' => __DIR__ . '/..' . '/zircote/swagger-php/src/Console/GenerateInput.php', - 'OpenApi\\Context' => __DIR__ . '/..' . '/zircote/swagger-php/src/Context.php', - 'OpenApi\\Generator' => __DIR__ . '/..' . '/zircote/swagger-php/src/Generator.php', - 'OpenApi\\GeneratorAwareInterface' => __DIR__ . '/..' . '/zircote/swagger-php/src/GeneratorAwareInterface.php', - 'OpenApi\\GeneratorAwareTrait' => __DIR__ . '/..' . '/zircote/swagger-php/src/GeneratorAwareTrait.php', - 'OpenApi\\Loggers\\DefaultLogger' => __DIR__ . '/..' . '/zircote/swagger-php/src/Loggers/DefaultLogger.php', - 'OpenApi\\OpenApiException' => __DIR__ . '/..' . '/zircote/swagger-php/src/OpenApiException.php', - 'OpenApi\\Pipeline' => __DIR__ . '/..' . '/zircote/swagger-php/src/Pipeline.php', - 'OpenApi\\Processors\\AugmentDiscriminators' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentDiscriminators.php', - 'OpenApi\\Processors\\AugmentItems' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentItems.php', - 'OpenApi\\Processors\\AugmentMediaType' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentMediaType.php', - 'OpenApi\\Processors\\AugmentParameters' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentParameters.php', - 'OpenApi\\Processors\\AugmentProperties' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentProperties.php', - 'OpenApi\\Processors\\AugmentRefs' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentRefs.php', - 'OpenApi\\Processors\\AugmentRequestBody' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentRequestBody.php', - 'OpenApi\\Processors\\AugmentSchemas' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentSchemas.php', - 'OpenApi\\Processors\\AugmentTags' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/AugmentTags.php', - 'OpenApi\\Processors\\BuildPaths' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/BuildPaths.php', - 'OpenApi\\Processors\\CleanUnmerged' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/CleanUnmerged.php', - 'OpenApi\\Processors\\CleanUnusedComponents' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/CleanUnusedComponents.php', - 'OpenApi\\Processors\\Concerns\\AnnotationTrait' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/Concerns/AnnotationTrait.php', - 'OpenApi\\Processors\\Concerns\\DocblockTrait' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/Concerns/DocblockTrait.php', - 'OpenApi\\Processors\\Concerns\\MergePropertiesTrait' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/Concerns/MergePropertiesTrait.php', - 'OpenApi\\Processors\\Concerns\\RefTrait' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/Concerns/RefTrait.php', - 'OpenApi\\Processors\\DocBlockDescriptions' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/DocBlockDescriptions.php', - 'OpenApi\\Processors\\ExpandClasses' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/ExpandClasses.php', - 'OpenApi\\Processors\\ExpandEnums' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/ExpandEnums.php', - 'OpenApi\\Processors\\ExpandInterfaces' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/ExpandInterfaces.php', - 'OpenApi\\Processors\\ExpandTraits' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/ExpandTraits.php', - 'OpenApi\\Processors\\MergeIntoComponents' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/MergeIntoComponents.php', - 'OpenApi\\Processors\\MergeIntoOpenApi' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/MergeIntoOpenApi.php', - 'OpenApi\\Processors\\MergeJsonContent' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/MergeJsonContent.php', - 'OpenApi\\Processors\\MergeXmlContent' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/MergeXmlContent.php', - 'OpenApi\\Processors\\OperationId' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/OperationId.php', - 'OpenApi\\Processors\\PathFilter' => __DIR__ . '/..' . '/zircote/swagger-php/src/Processors/PathFilter.php', - 'OpenApi\\Serializer' => __DIR__ . '/..' . '/zircote/swagger-php/src/Serializer.php', - 'OpenApi\\SourceFinder' => __DIR__ . '/..' . '/zircote/swagger-php/src/SourceFinder.php', - 'OpenApi\\TypeResolverInterface' => __DIR__ . '/..' . '/zircote/swagger-php/src/TypeResolverInterface.php', - 'OpenApi\\Type\\AbstractTypeResolver' => __DIR__ . '/..' . '/zircote/swagger-php/src/Type/AbstractTypeResolver.php', - 'OpenApi\\Type\\LegacyTypeResolver' => __DIR__ . '/..' . '/zircote/swagger-php/src/Type/LegacyTypeResolver.php', - 'OpenApi\\Type\\TypeInfoTypeResolver' => __DIR__ . '/..' . '/zircote/swagger-php/src/Type/TypeInfoTypeResolver.php', 'Override' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/Override.php', 'PHPStan\\PhpDocParser\\Ast\\AbstractNodeVisitor' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php', 'PHPStan\\PhpDocParser\\Ast\\Attribute' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Ast/Attribute.php', @@ -5861,13 +6131,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Psy\\VersionUpdater\\IntervalChecker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/IntervalChecker.php', 'Psy\\VersionUpdater\\NoopChecker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/NoopChecker.php', 'Psy\\VersionUpdater\\SelfUpdate' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/SelfUpdate.php', - 'Radebatz\\TypeInfoExtras\\TypeFactoryTrait' => __DIR__ . '/..' . '/radebatz/type-info-extras/src/TypeFactoryTrait.php', - 'Radebatz\\TypeInfoExtras\\TypeResolver\\ResolverExtrasTrait' => __DIR__ . '/..' . '/radebatz/type-info-extras/src/TypeResolver/ResolverExtrasTrait.php', - 'Radebatz\\TypeInfoExtras\\TypeResolver\\StringTypeResolver' => __DIR__ . '/..' . '/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php', - 'Radebatz\\TypeInfoExtras\\Type\\ClassLikeType' => __DIR__ . '/..' . '/radebatz/type-info-extras/src/Type/ClassLikeType.php', - 'Radebatz\\TypeInfoExtras\\Type\\ExplicitType' => __DIR__ . '/..' . '/radebatz/type-info-extras/src/Type/ExplicitType.php', - 'Radebatz\\TypeInfoExtras\\Type\\IntRangeType' => __DIR__ . '/..' . '/radebatz/type-info-extras/src/Type/IntRangeType.php', - 'Radebatz\\TypeInfoExtras\\Type\\Type' => __DIR__ . '/..' . '/radebatz/type-info-extras/src/Type/Type.php', 'Ramsey\\Collection\\AbstractArray' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractArray.php', 'Ramsey\\Collection\\AbstractCollection' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractCollection.php', 'Ramsey\\Collection\\AbstractSet' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractSet.php', @@ -6219,6 +6482,40 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'SebastianBergmann\\Type\\UnknownType' => __DIR__ . '/..' . '/sebastian/type/src/type/UnknownType.php', 'SebastianBergmann\\Type\\VoidType' => __DIR__ . '/..' . '/sebastian/type/src/type/VoidType.php', 'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\AskToRunMigrations' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Commands/Concerns/AskToRunMigrations.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\AskToStarRepoOnGitHub' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Commands/Concerns/AskToStarRepoOnGitHub.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\PublishesResources' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Commands/Concerns/PublishesResources.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\SupportsServiceProviderInApp' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Commands/Concerns/SupportsServiceProviderInApp.php', + 'Spatie\\LaravelPackageTools\\Commands\\Concerns\\SupportsStartWithEndWith' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Commands/Concerns/SupportsStartWithEndWith.php', + 'Spatie\\LaravelPackageTools\\Commands\\InstallCommand' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Commands/InstallCommand.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessAssets' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessAssets.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessBladeComponents' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessBladeComponents.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessCommands' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessCommands.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessConfigs' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessConfigs.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessInertia' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessInertia.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessMigrations' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessMigrations.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessRoutes' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessRoutes.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessServiceProviders' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessServiceProviders.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessTranslations' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessTranslations.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessViewComposers' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewComposers.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessViewSharedData' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewSharedData.php', + 'Spatie\\LaravelPackageTools\\Concerns\\PackageServiceProvider\\ProcessViews' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViews.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasAssets' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasAssets.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasBladeComponents' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasBladeComponents.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasCommands' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasCommands.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasConfigs' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasConfigs.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasInertia' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasInertia.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasInstallCommand' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasInstallCommand.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasMigrations' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasMigrations.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasRoutes' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasRoutes.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasServiceProviders' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasServiceProviders.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasTranslations' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasTranslations.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasViewComposers' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasViewComposers.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasViewSharedData' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasViewSharedData.php', + 'Spatie\\LaravelPackageTools\\Concerns\\Package\\HasViews' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Concerns/Package/HasViews.php', + 'Spatie\\LaravelPackageTools\\Exceptions\\InvalidPackage' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Exceptions/InvalidPackage.php', + 'Spatie\\LaravelPackageTools\\Package' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Package.php', + 'Spatie\\LaravelPackageTools\\PackageServiceProvider' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/PackageServiceProvider.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', 'Symfony\\Component\\Clock\\Clock' => __DIR__ . '/..' . '/symfony/clock/Clock.php', 'Symfony\\Component\\Clock\\ClockAwareTrait' => __DIR__ . '/..' . '/symfony/clock/ClockAwareTrait.php', @@ -7093,37 +7390,6 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Symfony\\Component\\Translation\\Util\\XliffUtils' => __DIR__ . '/..' . '/symfony/translation/Util/XliffUtils.php', 'Symfony\\Component\\Translation\\Writer\\TranslationWriter' => __DIR__ . '/..' . '/symfony/translation/Writer/TranslationWriter.php', 'Symfony\\Component\\Translation\\Writer\\TranslationWriterInterface' => __DIR__ . '/..' . '/symfony/translation/Writer/TranslationWriterInterface.php', - 'Symfony\\Component\\TypeInfo\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/type-info/Exception/ExceptionInterface.php', - 'Symfony\\Component\\TypeInfo\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/type-info/Exception/InvalidArgumentException.php', - 'Symfony\\Component\\TypeInfo\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/type-info/Exception/LogicException.php', - 'Symfony\\Component\\TypeInfo\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/type-info/Exception/RuntimeException.php', - 'Symfony\\Component\\TypeInfo\\Exception\\UnsupportedException' => __DIR__ . '/..' . '/symfony/type-info/Exception/UnsupportedException.php', - 'Symfony\\Component\\TypeInfo\\Type' => __DIR__ . '/..' . '/symfony/type-info/Type.php', - 'Symfony\\Component\\TypeInfo\\TypeContext\\TypeContext' => __DIR__ . '/..' . '/symfony/type-info/TypeContext/TypeContext.php', - 'Symfony\\Component\\TypeInfo\\TypeContext\\TypeContextFactory' => __DIR__ . '/..' . '/symfony/type-info/TypeContext/TypeContextFactory.php', - 'Symfony\\Component\\TypeInfo\\TypeFactoryTrait' => __DIR__ . '/..' . '/symfony/type-info/TypeFactoryTrait.php', - 'Symfony\\Component\\TypeInfo\\TypeIdentifier' => __DIR__ . '/..' . '/symfony/type-info/TypeIdentifier.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\PhpDocAwareReflectionTypeResolver' => __DIR__ . '/..' . '/symfony/type-info/TypeResolver/PhpDocAwareReflectionTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\ReflectionParameterTypeResolver' => __DIR__ . '/..' . '/symfony/type-info/TypeResolver/ReflectionParameterTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\ReflectionPropertyTypeResolver' => __DIR__ . '/..' . '/symfony/type-info/TypeResolver/ReflectionPropertyTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\ReflectionReturnTypeResolver' => __DIR__ . '/..' . '/symfony/type-info/TypeResolver/ReflectionReturnTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\ReflectionTypeResolver' => __DIR__ . '/..' . '/symfony/type-info/TypeResolver/ReflectionTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\StringTypeResolver' => __DIR__ . '/..' . '/symfony/type-info/TypeResolver/StringTypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\TypeResolver' => __DIR__ . '/..' . '/symfony/type-info/TypeResolver/TypeResolver.php', - 'Symfony\\Component\\TypeInfo\\TypeResolver\\TypeResolverInterface' => __DIR__ . '/..' . '/symfony/type-info/TypeResolver/TypeResolverInterface.php', - 'Symfony\\Component\\TypeInfo\\Type\\ArrayShapeType' => __DIR__ . '/..' . '/symfony/type-info/Type/ArrayShapeType.php', - 'Symfony\\Component\\TypeInfo\\Type\\BackedEnumType' => __DIR__ . '/..' . '/symfony/type-info/Type/BackedEnumType.php', - 'Symfony\\Component\\TypeInfo\\Type\\BuiltinType' => __DIR__ . '/..' . '/symfony/type-info/Type/BuiltinType.php', - 'Symfony\\Component\\TypeInfo\\Type\\CollectionType' => __DIR__ . '/..' . '/symfony/type-info/Type/CollectionType.php', - 'Symfony\\Component\\TypeInfo\\Type\\CompositeTypeInterface' => __DIR__ . '/..' . '/symfony/type-info/Type/CompositeTypeInterface.php', - 'Symfony\\Component\\TypeInfo\\Type\\EnumType' => __DIR__ . '/..' . '/symfony/type-info/Type/EnumType.php', - 'Symfony\\Component\\TypeInfo\\Type\\GenericType' => __DIR__ . '/..' . '/symfony/type-info/Type/GenericType.php', - 'Symfony\\Component\\TypeInfo\\Type\\IntersectionType' => __DIR__ . '/..' . '/symfony/type-info/Type/IntersectionType.php', - 'Symfony\\Component\\TypeInfo\\Type\\NullableType' => __DIR__ . '/..' . '/symfony/type-info/Type/NullableType.php', - 'Symfony\\Component\\TypeInfo\\Type\\ObjectType' => __DIR__ . '/..' . '/symfony/type-info/Type/ObjectType.php', - 'Symfony\\Component\\TypeInfo\\Type\\TemplateType' => __DIR__ . '/..' . '/symfony/type-info/Type/TemplateType.php', - 'Symfony\\Component\\TypeInfo\\Type\\UnionType' => __DIR__ . '/..' . '/symfony/type-info/Type/UnionType.php', - 'Symfony\\Component\\TypeInfo\\Type\\WrappingTypeInterface' => __DIR__ . '/..' . '/symfony/type-info/Type/WrappingTypeInterface.php', 'Symfony\\Component\\Uid\\AbstractUid' => __DIR__ . '/..' . '/symfony/uid/AbstractUid.php', 'Symfony\\Component\\Uid\\BinaryUtil' => __DIR__ . '/..' . '/symfony/uid/BinaryUtil.php', 'Symfony\\Component\\Uid\\Command\\GenerateUlidCommand' => __DIR__ . '/..' . '/symfony/uid/Command/GenerateUlidCommand.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index ba9548c7..621a62b0 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -136,54 +136,56 @@ "install-path": "../carbonphp/carbon-doctrine-types" }, { - "name": "darkaonline/l5-swagger", - "version": "11.1.0", - "version_normalized": "11.1.0.0", + "name": "dedoc/scramble", + "version": "v0.13.28", + "version_normalized": "0.13.28.0", "source": { "type": "git", - "url": "https://github.com/DarkaOnLine/L5-Swagger.git", - "reference": "110b59478c9417c13794cef62a82b019433d642a" + "url": "https://github.com/dedoc/scramble.git", + "reference": "e50927c732a341bb743671066892eec6bd2e958b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DarkaOnLine/L5-Swagger/zipball/110b59478c9417c13794cef62a82b019433d642a", - "reference": "110b59478c9417c13794cef62a82b019433d642a", + "url": "https://api.github.com/repos/dedoc/scramble/zipball/e50927c732a341bb743671066892eec6bd2e958b", + "reference": "e50927c732a341bb743671066892eec6bd2e958b", "shasum": "" }, "require": { - "ext-json": "*", - "laravel/framework": "^13.0 || ^12.1 || ^11.44", - "php": "^8.2", - "swagger-api/swagger-ui": ">=5.18.3", - "symfony/yaml": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "zircote/swagger-php": "^6.0" + "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0", + "myclabs/deep-copy": "^1.12", + "nikic/php-parser": "^5.0", + "php": "^8.1", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "spatie/laravel-package-tools": "^1.9.2" }, "require-dev": { - "mockery/mockery": "1.*", - "orchestra/testbench": "^11.0 || ^10.0 || ^9.0 || ^8.0 || 7.* || ^6.15 || 5.*", - "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^11.0" + "larastan/larastan": "^3.3", + "laravel/pint": "^v1.1.0", + "nunomaduro/collision": "^7.0|^8.0", + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.34|^3.7|^4.4", + "pestphp/pest-plugin-laravel": "^2.3|^3.1|^4.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5|^11.5.3|^12.5.12", + "spatie/laravel-permission": "^6.10|^7.2", + "spatie/pest-plugin-snapshots": "^2.1" }, - "time": "2026-06-12T10:04:41+00:00", + "time": "2026-06-14T18:21:12+00:00", "type": "library", "extra": { "laravel": { - "aliases": { - "L5Swagger": "L5Swagger\\L5SwaggerFacade" - }, "providers": [ - "L5Swagger\\L5SwaggerServiceProvider" + "Dedoc\\Scramble\\ScrambleServiceProvider" ] } }, "installation-source": "dist", "autoload": { - "files": [ - "src/helpers.php" - ], "psr-4": { - "L5Swagger\\": "src" + "Dedoc\\Scramble\\": "src", + "Dedoc\\Scramble\\Database\\Factories\\": "database/factories" } }, "notification-url": "https://packagist.org/downloads/", @@ -192,31 +194,29 @@ ], "authors": [ { - "name": "Darius Matulionis", - "email": "darius@matulionis.lt" + "name": "Roman Lytvynenko", + "email": "litvinenko95@gmail.com", + "role": "Developer" } ], - "description": "OpenApi or Swagger integration to Laravel", + "description": "Automatic generation of API documentation for Laravel applications.", + "homepage": "https://github.com/dedoc/scramble", "keywords": [ - "api", "documentation", "laravel", - "openapi", - "specification", - "swagger", - "ui" + "openapi" ], "support": { - "issues": "https://github.com/DarkaOnLine/L5-Swagger/issues", - "source": "https://github.com/DarkaOnLine/L5-Swagger/tree/11.1.0" + "issues": "https://github.com/dedoc/scramble/issues", + "source": "https://github.com/dedoc/scramble/tree/v0.13.28" }, "funding": [ { - "url": "https://github.com/DarkaOnLine", + "url": "https://github.com/romalytvynenko", "type": "github" } ], - "install-path": "../darkaonline/l5-swagger" + "install-path": "../dedoc/scramble" }, { "name": "dflydev/dot-access-data", @@ -4676,71 +4676,6 @@ }, "install-path": "../psy/psysh" }, - { - "name": "radebatz/type-info-extras", - "version": "1.0.7", - "version_normalized": "1.0.7.0", - "source": { - "type": "git", - "url": "https://github.com/DerManoMann/type-info-extras.git", - "reference": "95a524a74a61648b44e355cb33d38db4b17ef5ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DerManoMann/type-info-extras/zipball/95a524a74a61648b44e355cb33d38db4b17ef5ce", - "reference": "95a524a74a61648b44e355cb33d38db4b17ef5ce", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "phpstan/phpdoc-parser": "^2.0", - "symfony/type-info": "^7.3.8 || ^7.4.1 || ^8.0 || ^8.1-@dev" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.70", - "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^11.0" - }, - "time": "2026-03-06T22:40:29+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Radebatz\\TypeInfoExtras\\": "src" - }, - "exclude-from-classmap": [ - "/tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Martin Rademacher", - "email": "mano@radebatz.org" - } - ], - "description": "Extras for symfony/type-info", - "homepage": "http://radebatz.net/mano/", - "keywords": [ - "component", - "symfony", - "type-info", - "types" - ], - "support": { - "issues": "https://github.com/DerManoMann/type-info-extras/issues", - "source": "https://github.com/DerManoMann/type-info-extras/tree/1.0.7" - }, - "install-path": "../radebatz/type-info-extras" - }, { "name": "ralouphie/getallheaders", "version": "3.0.3", @@ -5979,6 +5914,70 @@ ], "install-path": "../sebastian/version" }, + { + "name": "spatie/laravel-package-tools", + "version": "1.93.1", + "version_normalized": "1.93.1.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "d5552849801f2642aea710557463234b59ef65eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/d5552849801f2642aea710557463234b59ef65eb", + "reference": "d5552849801f2642aea710557463234b59ef65eb", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.1" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^8.0|^9.2|^10.0|^11.0", + "pestphp/pest": "^2.1|^3.1|^4.0", + "phpunit/php-code-coverage": "^10.0|^11.0|^12.0", + "phpunit/phpunit": "^10.5|^11.5|^12.5", + "spatie/pest-plugin-test-time": "^2.2|^3.0" + }, + "time": "2026-05-19T14:06:37+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.93.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "install-path": "../spatie/laravel-package-tools" + }, { "name": "staabm/side-effects-detector", "version": "1.0.5", @@ -6034,70 +6033,6 @@ ], "install-path": "../staabm/side-effects-detector" }, - { - "name": "swagger-api/swagger-ui", - "version": "v5.32.6", - "version_normalized": "5.32.6.0", - "source": { - "type": "git", - "url": "https://github.com/swagger-api/swagger-ui.git", - "reference": "dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60", - "reference": "dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60", - "shasum": "" - }, - "time": "2026-05-12T09:35:37+00:00", - "type": "library", - "installation-source": "dist", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Anna Bodnia", - "email": "anna.bodnia@gmail.com" - }, - { - "name": "Buu Nguyen", - "email": "buunguyen@gmail.com" - }, - { - "name": "Josh Ponelat", - "email": "jponelat@gmail.com" - }, - { - "name": "Kyle Shockey", - "email": "kyleshockey1@gmail.com" - }, - { - "name": "Robert Barnwell", - "email": "robert@robertismy.name" - }, - { - "name": "Sahar Jafari", - "email": "shr.jafari@gmail.com" - } - ], - "description": " Swagger UI is a collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.", - "homepage": "http://swagger.io", - "keywords": [ - "api", - "documentation", - "openapi", - "specification", - "swagger", - "ui" - ], - "support": { - "issues": "https://github.com/swagger-api/swagger-ui/issues", - "source": "https://github.com/swagger-api/swagger-ui/tree/v5.32.6" - }, - "install-path": "../swagger-api/swagger-ui" - }, { "name": "symfony/clock", "version": "v7.4.8", @@ -8526,92 +8461,6 @@ ], "install-path": "../symfony/translation-contracts" }, - { - "name": "symfony/type-info", - "version": "v7.4.9", - "version_normalized": "7.4.9.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/type-info.git", - "reference": "cafeedbf157b890e94ac5b83eaed85595106d5d6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/cafeedbf157b890e94ac5b83eaed85595106d5d6", - "reference": "cafeedbf157b890e94ac5b83eaed85595106d5d6", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "phpstan/phpdoc-parser": "<1.30" - }, - "require-dev": { - "phpstan/phpdoc-parser": "^1.30|^2.0" - }, - "time": "2026-04-22T15:21:55+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\TypeInfo\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mathias Arlaud", - "email": "mathias.arlaud@gmail.com" - }, - { - "name": "Baptiste LEDUC", - "email": "baptiste.leduc@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Extracts PHP types information.", - "homepage": "https://symfony.com", - "keywords": [ - "PHPStan", - "phpdoc", - "symfony", - "type" - ], - "support": { - "source": "https://github.com/symfony/type-info/tree/v7.4.9" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "install-path": "../symfony/type-info" - }, { "name": "symfony/uid", "version": "v7.4.9", @@ -9136,103 +8985,11 @@ } ], "install-path": "../voku/portable-ascii" - }, - { - "name": "zircote/swagger-php", - "version": "6.2.0", - "version_normalized": "6.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/zircote/swagger-php.git", - "reference": "060af3bb9c4cba6a5859aba2c51cd1c129479410" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/060af3bb9c4cba6a5859aba2c51cd1c129479410", - "reference": "060af3bb9c4cba6a5859aba2c51cd1c129479410", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.19 || ^5.0", - "php": ">=8.2", - "phpstan/phpdoc-parser": "^2.0", - "psr/log": "^1.1 || ^2.0 || ^3.0", - "radebatz/type-info-extras": "^1.0.2", - "symfony/console": "^7.4 || ^8.0", - "symfony/deprecation-contracts": "^2 || ^3", - "symfony/finder": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" - }, - "conflict": { - "symfony/process": ">=6, <6.4.14" - }, - "require-dev": { - "composer/package-versions-deprecated": "^1.11", - "doctrine/annotations": "^2.0", - "friendsofphp/php-cs-fixer": "^3.62.0", - "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^11.5 || >=12.5.22", - "rector/rector": "^2.3.1" - }, - "time": "2026-06-14T06:51:56+00:00", - "bin": [ - "bin/openapi" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "OpenApi\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Robert Allen", - "email": "zircote@gmail.com" - }, - { - "name": "Bob Fanger", - "email": "bfanger@gmail.com", - "homepage": "https://bfanger.nl" - }, - { - "name": "Martin Rademacher", - "email": "mano@radebatz.net", - "homepage": "https://radebatz.net" - } - ], - "description": "Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations", - "homepage": "https://github.com/zircote/swagger-php", - "keywords": [ - "api", - "json", - "rest", - "service discovery" - ], - "support": { - "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/6.2.0" - }, - "funding": [ - { - "url": "https://github.com/zircote", - "type": "github" - } - ], - "install-path": "../zircote/swagger-php" } ], "dev": true, "dev-package-names": [ + "dedoc/scramble", "fakerphp/faker", "filp/whoops", "hamcrest/hamcrest-php", @@ -9244,6 +9001,7 @@ "nunomaduro/collision", "phar-io/manifest", "phar-io/version", + "phpstan/phpdoc-parser", "phpunit/php-code-coverage", "phpunit/php-file-iterator", "phpunit/php-invoker", @@ -9265,7 +9023,9 @@ "sebastian/recursion-context", "sebastian/type", "sebastian/version", + "spatie/laravel-package-tools", "staabm/side-effects-detector", + "symfony/yaml", "theseer/tokenizer" ] } diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 7acebcf2..b231f72a 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,9 +1,9 @@ array( 'name' => 'laravel/laravel', - 'pretty_version' => '1.0.0+no-version-set', - 'version' => '1.0.0.0', - 'reference' => null, + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '2f805a233dbd854665db766b12582701833e8622', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -34,21 +34,21 @@ 0 => '*', ), ), - 'darkaonline/l5-swagger' => array( - 'pretty_version' => '11.1.0', - 'version' => '11.1.0.0', - 'reference' => '110b59478c9417c13794cef62a82b019433d642a', - 'type' => 'library', - 'install_path' => __DIR__ . '/../darkaonline/l5-swagger', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'davedevelopment/hamcrest-php' => array( 'dev_requirement' => true, 'replaced' => array( 0 => '*', ), ), + 'dedoc/scramble' => array( + 'pretty_version' => 'v0.13.28', + 'version' => '0.13.28.0', + 'reference' => 'e50927c732a341bb743671066892eec6bd2e958b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../dedoc/scramble', + 'aliases' => array(), + 'dev_requirement' => true, + ), 'dflydev/dot-access-data' => array( 'pretty_version' => 'v3.0.3', 'version' => '3.0.3.0', @@ -407,9 +407,9 @@ 'dev_requirement' => false, ), 'laravel/laravel' => array( - 'pretty_version' => '1.0.0+no-version-set', - 'version' => '1.0.0.0', - 'reference' => null, + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '2f805a233dbd854665db766b12582701833e8622', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -662,7 +662,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../phpstan/phpdoc-parser', 'aliases' => array(), - 'dev_requirement' => false, + 'dev_requirement' => true, ), 'phpunit/php-code-coverage' => array( 'pretty_version' => '11.0.12', @@ -848,15 +848,6 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'radebatz/type-info-extras' => array( - 'pretty_version' => '1.0.7', - 'version' => '1.0.7.0', - 'reference' => '95a524a74a61648b44e355cb33d38db4b17ef5ce', - 'type' => 'library', - 'install_path' => __DIR__ . '/../radebatz/type-info-extras', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'ralouphie/getallheaders' => array( 'pretty_version' => '3.0.3', 'version' => '3.0.3.0', @@ -1025,6 +1016,15 @@ 'aliases' => array(), 'dev_requirement' => true, ), + 'spatie/laravel-package-tools' => array( + 'pretty_version' => '1.93.1', + 'version' => '1.93.1.0', + 'reference' => 'd5552849801f2642aea710557463234b59ef65eb', + 'type' => 'library', + 'install_path' => __DIR__ . '/../spatie/laravel-package-tools', + 'aliases' => array(), + 'dev_requirement' => true, + ), 'spatie/once' => array( 'dev_requirement' => false, 'replaced' => array( @@ -1040,15 +1040,6 @@ 'aliases' => array(), 'dev_requirement' => true, ), - 'swagger-api/swagger-ui' => array( - 'pretty_version' => 'v5.32.6', - 'version' => '5.32.6.0', - 'reference' => 'dcdca62c8b64a0a54e4decd4e1a6c3c712fdcc60', - 'type' => 'library', - 'install_path' => __DIR__ . '/../swagger-api/swagger-ui', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'symfony/clock' => array( 'pretty_version' => 'v7.4.8', 'version' => '7.4.8.0', @@ -1313,15 +1304,6 @@ 0 => '2.3|3.0', ), ), - 'symfony/type-info' => array( - 'pretty_version' => 'v7.4.9', - 'version' => '7.4.9.0', - 'reference' => 'cafeedbf157b890e94ac5b83eaed85595106d5d6', - 'type' => 'library', - 'install_path' => __DIR__ . '/../symfony/type-info', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'symfony/uid' => array( 'pretty_version' => 'v7.4.9', 'version' => '7.4.9.0', @@ -1347,7 +1329,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/yaml', 'aliases' => array(), - 'dev_requirement' => false, + 'dev_requirement' => true, ), 'theseer/tokenizer' => array( 'pretty_version' => '1.3.1', @@ -1385,14 +1367,5 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'zircote/swagger-php' => array( - 'pretty_version' => '6.2.0', - 'version' => '6.2.0.0', - 'reference' => '060af3bb9c4cba6a5859aba2c51cd1c129479410', - 'type' => 'library', - 'install_path' => __DIR__ . '/../zircote/swagger-php', - 'aliases' => array(), - 'dev_requirement' => false, - ), ), ); diff --git a/vendor/darkaonline/l5-swagger/.claude/skills/write-tests.md b/vendor/darkaonline/l5-swagger/.claude/skills/write-tests.md deleted file mode 100644 index 33b0d3b8..00000000 --- a/vendor/darkaonline/l5-swagger/.claude/skills/write-tests.md +++ /dev/null @@ -1,273 +0,0 @@ ---- -name: write-tests -description: Write PHPUnit tests for the L5-Swagger Laravel package using Orchestra Testbench, following project conventions for mocking, config manipulation, and PHPUnit attributes -allowed-tools: Read, Edit, Write, Bash(vendor/bin/phpunit *), Bash(composer run-script phpunit), Bash(composer run-script analyse), Bash(grep *), Bash(find *), Bash(ls *), Bash(diff *), Bash(cat *) ---- - -# Write Tests for L5-Swagger - -Generate PHPUnit tests for the L5-Swagger package following its established patterns. - -## Context - -- Test runner: !`composer run-script phpunit -- --version 2>/dev/null | head -1` -- Existing test files: !`ls tests/Unit/*.php 2>/dev/null | xargs -I{} basename {}` -- Source files: !`ls src/*.php src/**/*.php 2>/dev/null | grep -v vendor` - -## Project Test Architecture - -All tests extend `Tests\Unit\TestCase` which extends Orchestra Testbench's `OrchestraTestCase`. This gives every test a full Laravel application with the `L5SwaggerServiceProvider` registered. - -### Base TestCase provides: - -| Property / Method | Purpose | -|---|---| -| `$this->configFactory` | `ConfigFactory` instance resolved from the app container | -| `$this->generator` | `Generator` instance resolved from the app container | -| `$this->fileSystem` | PHPUnit mock of `Illuminate\Filesystem\Filesystem` (created via `createMock`) | -| `setAnnotationsPath()` | Points config to `tests/storage/annotations/OpenApi/` fixtures, enables `generate_always` and `generate_yaml_copy`, sets `L5_SWAGGER_CONST_HOST` constant, rebuilds the generator | -| `makeGeneratorWithMockedFileSystem()` | Injects `$this->fileSystem` mock into the generator via reflection | -| `setCustomDocsFileName($name, $type)` | Overrides docs filename in config for JSON or YAML | -| `crateJsonDocumentationFile()` | Creates a minimal `{}` JSON docs file | -| `createYamlDocumentationFile()` | Creates an empty YAML docs file | -| `jsonDocsFile()` | Returns absolute path to the JSON docs file (creates dir if needed) | -| `yamlDocsFile()` | Returns absolute path to the YAML docs file (creates dir if needed) | -| `copyAssets()` | Copies swagger-ui dist into testbench vendor dir (runs in setUp) | -| `deleteAssets()` | Removes the copied swagger-ui assets | - -### tearDown cleanup - -The base TestCase automatically deletes generated JSON/YAML docs files and the docs directory in `tearDown()`. You do NOT need to clean up generated files. - -## Instructions - -### Step 1: Determine what to test - -Read the source file(s) the user wants tested. Identify: -- Public methods and their behavior -- Error/exception paths -- Config-driven behavior branches -- Interactions with the filesystem or external dependencies - -### Step 2: Create or edit the test file - -**File location**: `tests/Unit/{ClassName}Test.php` - -**Required class-level attributes** (PHPUnit 11 attributes, not annotations): -```php -#[TestDox('Human readable class description')] -#[CoversClass(FullyQualifiedClassName::class)] -``` - -**Required test method conventions**: -- Method names: `testItDoesX` or `testCanDoX` (camelCase, descriptive) -- Visibility: `public function testXxx(): void` -- Add `@throws` docblock for expected exceptions -- Use `expectException()` and `expectExceptionMessage()` for exception tests - -### Step 3: Follow these patterns based on what you're testing - -#### Pattern A: Testing Generator behavior with mocked filesystem - -Use when testing `Generator` methods that interact with the filesystem (directory creation, file writing, permission checks). - -```php -public function testItThrowsExceptionIfSomethingFails(): void -{ - $this->setAnnotationsPath(); - - $config = $this->configFactory->documentationConfig(); - $docs = $config['paths']['docs']; - - // Set up filesystem mock expectations - $this->fileSystem - ->expects($this->once()) - ->method('exists') - ->with($docs) - ->willReturn(true); - - // ... more mock setup ... - - $this->expectException(L5SwaggerException::class); - $this->expectExceptionMessage('Expected error message'); - - // IMPORTANT: call makeGeneratorWithMockedFileSystem() AFTER mock setup - $this->makeGeneratorWithMockedFileSystem(); - $this->generator->generateDocs(); -} -``` - -#### Pattern B: Testing full generation pipeline (integration) - -Use when testing that docs generate correctly with specific config. - -```php -public function testCanGenerateWithSpecificConfig(): void -{ - $this->setAnnotationsPath(); - - // Optionally override config - $cfg = config('l5-swagger.documentations.default'); - $cfg['paths']['base'] = 'https://custom-server.url'; - config(['l5-swagger' => [ - 'default' => 'default', - 'documentations' => ['default' => $cfg], - 'defaults' => config('l5-swagger.defaults'), - ]]); - - $this->generator->generateDocs(); - - $this->assertFileExists($this->jsonDocsFile()); - - // Verify via HTTP response - $this->get(route('l5-swagger.default.docs')) - ->assertSee('expected content') - ->assertStatus(200); -} -``` - -#### Pattern C: Testing routes and HTTP responses - -Use when testing controller behavior, middleware, or route registration. - -```php -public function testRouteReturnsExpectedResponse(): void -{ - // For tests needing generated docs, call setAnnotationsPath() first - // For tests checking behavior without docs, don't call it - - $this->get(route('l5-swagger.default.docs')) - ->assertStatus(200) - ->assertSee('expected') - ->assertHeader('Content-Type', 'application/json'); -} -``` - -#### Pattern D: Testing config merging - -Use when testing `ConfigFactory` behavior. - -```php -#[DataProvider('configDataProvider')] -public function testConfigMergesCorrectly(array $data, array $expected): void -{ - config(['l5-swagger' => array_merge($data, [ - 'defaults' => [/* base defaults */], - ])]); - - $config = $this->configFactory->documentationConfig(); - - $this->assertSame($expected['key'], $config['key']); -} - -public static function configDataProvider(): \Generator -{ - yield 'descriptive case name' => [ - 'data' => [/* input */], - 'expected' => [/* expected output */], - ]; -} -``` - -#### Pattern E: Mocking the Generator via GeneratorFactory - -Use when testing controllers/routes that call `generateDocs()` and you want to control generator behavior without actual generation. - -```php -public function testBehaviorWhenGenerationFails(): void -{ - $mockGenerator = $this->createMock(Generator::class); - $mockGeneratorFactory = $this->createMock(GeneratorFactory::class); - $mockGeneratorFactory->method('make')->willReturn($mockGenerator); - app()->extend(GeneratorFactory::class, function () use ($mockGeneratorFactory) { - return $mockGeneratorFactory; - }); - - $mockGenerator->expects($this->once()) - ->method('generateDocs') - ->willThrowException(new L5SwaggerException()); - - $this->get(route('l5-swagger.default.docs'))->assertNotFound(); -} -``` - -### Step 4: Config manipulation pattern - -When overriding config, always preserve the full structure: - -```php -config(['l5-swagger' => [ - 'default' => 'default', - 'documentations' => [ - 'default' => $cfg, // your modified config - ], - 'defaults' => config('l5-swagger.defaults'), -]]); -``` - -After changing config that affects the generator, call `$this->makeGenerator()` to rebuild it. - -### Step 5: Run and verify with coverage gate - -New tests must never decrease code coverage. Follow this sequence: - -#### 5a. Capture baseline coverage BEFORE writing tests - -```bash -vendor/bin/phpunit --coverage-text --only-summary-for-coverage-text 2>&1 | tee /tmp/l5-coverage-before.txt -``` - -Extract the baseline percentages: - -```bash -grep -E 'Lines:|Methods:|Classes:' /tmp/l5-coverage-before.txt -``` - -Record both **Lines** and **Methods** percentages — these are the numbers that must not drop. - -#### 5b. Run the new/modified tests in isolation - -```bash -vendor/bin/phpunit tests/Unit/YourNewTest.php --testdox -``` - -#### 5c. Run the full suite with coverage AFTER adding tests - -```bash -vendor/bin/phpunit --coverage-text --only-summary-for-coverage-text 2>&1 | tee /tmp/l5-coverage-after.txt -``` - -#### 5d. Compare coverage - -```bash -diff /tmp/l5-coverage-before.txt /tmp/l5-coverage-after.txt -``` - -Verify: -- **Lines coverage**: must be >= baseline -- **Methods coverage**: must be >= baseline - -If coverage decreased, identify the cause: -1. A new test file with `#[CoversClass]` pulled in a class that was previously uncovered — add tests for the uncovered methods -2. A test is covering code paths that were already covered but skipping others — add assertions for the missing branches -3. A new fixture or helper class landed under `src/` — it needs its own tests - -**Do not proceed until coverage is equal to or higher than the baseline.** - -#### 5e. Run static analysis - -```bash -composer run-script analyse -``` - -## Important Rules - -- **Coverage must not decrease.** Always capture baseline coverage before writing tests and verify it afterwards. If a `#[CoversClass]` attribute pulls in a class with uncovered methods, you must add tests for those methods too — not just remove the attribute. This is a hard gate: do not report the task as complete until coverage is verified equal or higher. -- Use PHP 8.2+ features: constructor property promotion, union types, named arguments, match expressions -- Use PHPUnit 11 **attributes** (`#[TestDox]`, `#[CoversClass]`, `#[DataProvider]`), NOT docblock annotations (`@testdox`, `@covers`, `@dataProvider`) -- Data providers must be `public static` methods returning `\Generator` (using `yield`) -- Fixture annotations live in `tests/storage/annotations/OpenApi/` — read existing ones before creating new fixtures -- The `$this->fileSystem` mock is created fresh via `#[Before]` attribute before each test — no shared mock state between tests -- Route names follow the pattern `l5-swagger.{documentation}.{type}` where type is `api`, `docs`, `asset`, or `oauth2_callback` -- The test environment sets `SWAGGER_VERSION=3.0` and `APP_KEY` via `phpunit.xml` -- StyleCI enforces Laravel preset — don't fight the code style diff --git a/vendor/darkaonline/l5-swagger/CLAUDE.md b/vendor/darkaonline/l5-swagger/CLAUDE.md deleted file mode 100644 index ba4d6f0c..00000000 --- a/vendor/darkaonline/l5-swagger/CLAUDE.md +++ /dev/null @@ -1,83 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -L5-Swagger is a Laravel package that integrates swagger-php and swagger-ui into Laravel. It does NOT implement the OpenAPI spec — it wraps [swagger-php](https://github.com/zircote/swagger-php) for annotation scanning and [swagger-ui](https://github.com/swagger-api/swagger-ui) for the documentation frontend. - -Supports multiple independent documentation sets, each with its own routes, paths, and settings via a two-level config: `defaults` (shared) merged with `documentations.{name}` (per-doc overrides). `ConfigFactory::mergeConfig()` recursively merges associative arrays. - -## Development Commands - -```bash -# Run all tests -composer run-script phpunit - -# Run a single test file -vendor/bin/phpunit tests/Unit/GeneratorTest.php - -# Run a single test method -vendor/bin/phpunit --filter testCanGenerateApiJsonFile - -# Static analysis (PHPStan level 8) -composer run-script analyse -``` - -## Architecture - -### Generation Pipeline - -`GeneratorFactory::make(documentation)` → creates `Generator` with merged config → `Generator::generateDocs()` chains: - -1. `prepareDirectory()` — ensure output dir exists/writable -2. `defineConstants()` — define PHP constants from config (usable in annotations) -3. `scanFilesForDocumentation()` — swagger-php scans PHP files using `ReflectionAnalyser` + `AttributeAnnotationFactory` -4. `populateServers()` — injects base path as a Server URL -5. `saveJson()` — writes JSON, then `SecurityDefinitions::generate()` injects security schemes -6. `makeYamlCopy()` — optional YAML conversion via `symfony/yaml` - -Custom processors can be positioned relative to any existing processor via `scanOptions.processors` using `['class' => ..., 'after' => ...]` syntax. A `CustomGeneratorInterface` implementation can be provided via `scanOptions.generator_factory` to supply a pre-configured `OpenApi\Generator`. - -### Service Container Bindings - -`L5SwaggerServiceProvider` binds `Generator::class` as a factory — each resolution creates a new Generator via `GeneratorFactory::make()` using the `l5-swagger.default` documentation name. The `GenerateDocsCommand` is registered as a singleton under `command.l5-swagger.generate`. - -### Routing - -Routes are registered dynamically in `src/routes.php` by iterating all documentation sets. Each set gets up to 4 routes (api UI, docs JSON/YAML, assets, OAuth2 callback), all wrapped with the `Config` middleware that sets the active documentation context. Route names follow `l5-swagger.{documentation}.{type}`. - -### Helpers - -`swagger_ui_dist_path()` resolves swagger-ui asset paths with an allowlist of permitted files. `l5_swagger_asset()` generates versioned (cache-busted) URLs for those assets. - -## Testing - -Tests use Orchestra Testbench (`Tests\Unit\TestCase` extends `OrchestraTestCase`) for a full Laravel environment. - -Key patterns: -- `$this->fileSystem` is a PHPUnit mock of `Filesystem`, injected into `Generator` via reflection in `makeGeneratorWithMockedFileSystem()` -- `setAnnotationsPath()` reconfigures the app to use `tests/storage/annotations/OpenApi/` fixtures and rebuilds the generator -- `setCustomDocsFileName()` overrides the output filename for JSON/YAML format tests -- `copyAssets()` copies swagger-ui dist into testbench's vendor directory in setUp; `tearDown` cleans up generated docs -- PHPUnit attributes: `#[TestDox('...')]`, `#[CoversClass(...)]` - -Test fixtures live in `tests/storage/annotations/OpenApi/` with sample PHP attribute annotations. - -## Code Style - -- **StyleCI**: `preset: laravel` — enforced automatically -- **PHPStan**: Level 8, analyzes `src/` and `tests/Unit/`, ignores `argument.templateType` -- PHP 8.2+ required; uses constructor property promotion, union types, named arguments -- Supports Laravel 11.44+, 12.1+, and 13.0+ - -## Environment Variables - -| Variable | Purpose | -|---|---| -| `L5_SWAGGER_GENERATE_ALWAYS` | Regenerate docs on every request (dev mode) | -| `L5_SWAGGER_GENERATE_YAML_COPY` | Also create YAML version | -| `L5_SWAGGER_CONST_HOST` | Default host constant for annotations | -| `L5_SWAGGER_OPEN_API_SPEC_VERSION` | `3.0.0` (default) or `3.1.0` | -| `L5_SWAGGER_UI_DARK_MODE` | Dark mode for Swagger UI | -| `SWAGGER_VERSION` | Used in tests to set OpenAPI version | diff --git a/vendor/darkaonline/l5-swagger/composer.json b/vendor/darkaonline/l5-swagger/composer.json deleted file mode 100644 index 5bf108c0..00000000 --- a/vendor/darkaonline/l5-swagger/composer.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "darkaonline/l5-swagger", - "description": "OpenApi or Swagger integration to Laravel", - "keywords": [ - "laravel", - "swagger", - "api", - "OpenApi", - "specification", - "documentation", - "API", - "UI" - ], - "license": "MIT", - "authors": [ - { - "name": "Darius Matulionis", - "email": "darius@matulionis.lt" - } - ], - "autoload": { - "psr-4": { - "L5Swagger\\": "src" - }, - "files": [ - "src/helpers.php" - ] - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests" - } - }, - "require": { - "php": "^8.2", - "laravel/framework": "^13.0 || ^12.1 || ^11.44", - "zircote/swagger-php": "^6.0", - "swagger-api/swagger-ui": ">=5.18.3", - "symfony/yaml": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "ext-json": "*" - }, - "require-dev": { - "phpunit/phpunit": "^11.0", - "mockery/mockery": "1.*", - "orchestra/testbench": "^11.0 || ^10.0 || ^9.0 || ^8.0 || 7.* || ^6.15 || 5.*", - "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": "^2.1" - }, - "extra": { - "laravel": { - "providers": [ - "L5Swagger\\L5SwaggerServiceProvider" - ], - "aliases": { - "L5Swagger": "L5Swagger\\L5SwaggerFacade" - } - } - }, - "minimum-stability": "dev", - "prefer-stable": true, - "scripts": { - "phpunit": "vendor/bin/phpunit --testdox", - "analyse": "vendor/bin/phpstan analyse --memory-limit=256M" - } -} diff --git a/vendor/darkaonline/l5-swagger/config/l5-swagger.php b/vendor/darkaonline/l5-swagger/config/l5-swagger.php deleted file mode 100644 index ff4c8a43..00000000 --- a/vendor/darkaonline/l5-swagger/config/l5-swagger.php +++ /dev/null @@ -1,333 +0,0 @@ - 'default', - 'documentations' => [ - 'default' => [ - 'api' => [ - 'title' => 'L5 Swagger UI', - ], - - 'routes' => [ - /* - * Route for accessing api documentation interface - */ - 'api' => 'api/documentation', - ], - 'paths' => [ - /* - * Edit to include full URL in ui for assets - */ - 'use_absolute_path' => env('L5_SWAGGER_USE_ABSOLUTE_PATH', true), - - /* - * Edit to set path where swagger ui assets should be stored - */ - 'swagger_ui_assets_path' => env('L5_SWAGGER_UI_ASSETS_PATH', 'vendor/swagger-api/swagger-ui/dist/'), - - /* - * File name of the generated json documentation file - */ - 'docs_json' => 'api-docs.json', - - /* - * File name of the generated YAML documentation file - */ - 'docs_yaml' => 'api-docs.yaml', - - /* - * Set this to `json` or `yaml` to determine which documentation file to use in UI - */ - 'format_to_use_for_docs' => env('L5_FORMAT_TO_USE_FOR_DOCS', 'json'), - - /* - * Absolute paths to directory containing the swagger annotations are stored. - */ - 'annotations' => [ - base_path('app'), - ], - ], - ], - ], - 'defaults' => [ - 'routes' => [ - /* - * Route for accessing parsed swagger annotations. - */ - 'docs' => 'docs', - - /* - * Route for Oauth2 authentication callback. - */ - 'oauth2_callback' => 'api/oauth2-callback', - - /* - * Middleware allows to prevent unexpected access to API documentation - */ - 'middleware' => [ - 'api' => [], - 'asset' => [], - 'docs' => [], - 'oauth2_callback' => [], - ], - - /* - * Route Group options - */ - 'group_options' => [], - ], - - 'paths' => [ - /* - * Absolute path to location where parsed annotations will be stored - */ - 'docs' => storage_path('api-docs'), - - /* - * Absolute path to directory where to export views - */ - 'views' => base_path('resources/views/vendor/l5-swagger'), - - /* - * Edit to set the api's base path - */ - 'base' => env('L5_SWAGGER_BASE_PATH', null), - - /* - * Absolute path to directories that should be excluded from scanning - * @deprecated Please use `scanOptions.exclude` - * `scanOptions.exclude` overwrites this - */ - 'excludes' => [], - ], - - 'scanOptions' => [ - /** - * Optional CustomGeneratorInterface implementation that creates an OpenApi\Generator instance. - * Use this to provide a custom pre-configured generator. - * Accepts an instance or a class name (FQCN) implementing the interface. - * - * @see \L5Swagger\CustomGeneratorInterface - */ - 'generator_factory' => null, - - /** - * Configuration for default processors. Allows to pass processors configuration to swagger-php. - * - * @link https://zircote.github.io/swagger-php/reference/processors.html - */ - 'default_processors_configuration' => [ - /** Example */ - /** - * 'operationId.hash' => true, - * 'pathFilter' => [ - * 'tags' => [ - * '/pets/', - * '/store/', - * ], - * ],. - */ - ], - - /** - * analyser: defaults to \OpenApi\StaticAnalyser . - * - * @see \OpenApi\scan - */ - 'analyser' => null, - - /** - * analysis: defaults to a new \OpenApi\Analysis . - * - * @see \OpenApi\scan - */ - 'analysis' => null, - - /** - * Custom processors. - * - * Each entry can be: - * - A class name or instance (inserted after BuildPaths by default) - * - An array with 'class' and 'after' keys for precise positioning: - * ['class' => MyProcessor::class, 'after' => SomeProcessor::class] - * - * @link https://github.com/zircote/swagger-php/tree/master/Examples/processors/schema-query-parameter - * @see \OpenApi\scan - */ - 'processors' => [ - // \App\SwaggerProcessors\SchemaQueryParameter::class, - // ['class' => \App\SwaggerProcessors\Custom::class, 'after' => \OpenApi\Processors\AugmentSchemas::class], - ], - - /** - * pattern: string $pattern File pattern(s) to scan (default: *.php) . - * - * @see \OpenApi\scan - */ - 'pattern' => null, - - /* - * Absolute path to directories that should be excluded from scanning - * @note This option overwrites `paths.excludes` - * @see \OpenApi\scan - */ - 'exclude' => [], - - /* - * Allows to generate specs either for OpenAPI 3.0.0 or OpenAPI 3.1.0. - * By default the spec will be in version 3.0.0 - */ - 'open_api_spec_version' => env('L5_SWAGGER_OPEN_API_SPEC_VERSION', \L5Swagger\Generator::OPEN_API_DEFAULT_SPEC_VERSION), - ], - - /* - * API security definitions. Will be generated into documentation file. - */ - 'securityDefinitions' => [ - 'securitySchemes' => [ - /* - * Examples of Security schemes - */ - /* - 'api_key_security_example' => [ // Unique name of security - 'type' => 'apiKey', // The type of the security scheme. Valid values are "basic", "apiKey" or "oauth2". - 'description' => 'A short description for security scheme', - 'name' => 'api_key', // The name of the header or query parameter to be used. - 'in' => 'header', // The location of the API key. Valid values are "query" or "header". - ], - 'oauth2_security_example' => [ // Unique name of security - 'type' => 'oauth2', // The type of the security scheme. Valid values are "basic", "apiKey" or "oauth2". - 'description' => 'A short description for oauth2 security scheme.', - 'flow' => 'implicit', // The flow used by the OAuth2 security scheme. Valid values are "implicit", "password", "application" or "accessCode". - 'authorizationUrl' => 'http://example.com/auth', // The authorization URL to be used for (implicit/accessCode) - //'tokenUrl' => 'http://example.com/auth' // The authorization URL to be used for (password/application/accessCode) - 'scopes' => [ - 'read:projects' => 'read your projects', - 'write:projects' => 'modify projects in your account', - ] - ], - */ - - /* Open API 3.0 support - 'passport' => [ // Unique name of security - 'type' => 'oauth2', // The type of the security scheme. Valid values are "basic", "apiKey" or "oauth2". - 'description' => 'Laravel passport oauth2 security.', - 'in' => 'header', - 'scheme' => 'https', - 'flows' => [ - "password" => [ - "authorizationUrl" => config('app.url') . '/oauth/authorize', - "tokenUrl" => config('app.url') . '/oauth/token', - "refreshUrl" => config('app.url') . '/token/refresh', - "scopes" => [] - ], - ], - ], - 'sanctum' => [ // Unique name of security - 'type' => 'apiKey', // Valid values are "basic", "apiKey" or "oauth2". - 'description' => 'Enter token in format (Bearer )', - 'name' => 'Authorization', // The name of the header or query parameter to be used. - 'in' => 'header', // The location of the API key. Valid values are "query" or "header". - ], - */ - ], - 'security' => [ - /* - * Examples of Securities - */ - [ - /* - 'oauth2_security_example' => [ - 'read', - 'write' - ], - - 'passport' => [] - */ - ], - ], - ], - - /* - * Set this to `true` in development mode so that docs would be regenerated on each request - * Set this to `false` to disable swagger generation on production - */ - 'generate_always' => env('L5_SWAGGER_GENERATE_ALWAYS', false), - - /* - * Set this to `true` to generate a copy of documentation in yaml format - */ - 'generate_yaml_copy' => env('L5_SWAGGER_GENERATE_YAML_COPY', false), - - /* - * Edit to trust the proxy's ip address - needed for AWS Load Balancer - * string[] - */ - 'proxy' => false, - - /* - * Configs plugin allows to fetch external configs instead of passing them to SwaggerUIBundle. - * See more at: https://github.com/swagger-api/swagger-ui#configs-plugin - */ - 'additional_config_url' => null, - - /* - * Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), - * 'method' (sort by HTTP method). - * Default is the order returned by the server unchanged. - */ - 'operations_sort' => env('L5_SWAGGER_OPERATIONS_SORT', null), - - /* - * Pass the validatorUrl parameter to SwaggerUi init on the JS side. - * A null value here disables validation. - */ - 'validator_url' => null, - - /* - * Swagger UI configuration parameters - */ - 'ui' => [ - 'display' => [ - 'dark_mode' => env('L5_SWAGGER_UI_DARK_MODE', false), - /* - * Controls the default expansion setting for the operations and tags. It can be : - * 'list' (expands only the tags), - * 'full' (expands the tags and operations), - * 'none' (expands nothing). - */ - 'doc_expansion' => env('L5_SWAGGER_UI_DOC_EXPANSION', 'none'), - - /** - * If set, enables filtering. The top bar will show an edit box that - * you can use to filter the tagged operations that are shown. Can be - * Boolean to enable or disable, or a string, in which case filtering - * will be enabled using that string as the filter expression. Filtering - * is case-sensitive matching the filter expression anywhere inside - * the tag. - */ - 'filter' => env('L5_SWAGGER_UI_FILTERS', true), // true | false - ], - - 'authorization' => [ - /* - * If set to true, it persists authorization data, and it would not be lost on browser close/refresh - */ - 'persist_authorization' => env('L5_SWAGGER_UI_PERSIST_AUTHORIZATION', false), - - 'oauth2' => [ - /* - * If set to true, adds PKCE to AuthorizationCodeGrant flow - */ - 'use_pkce_with_authorization_code_grant' => false, - ], - ], - ], - /* - * Constants which can be used in annotations - */ - 'constants' => [ - 'L5_SWAGGER_CONST_HOST' => env('L5_SWAGGER_CONST_HOST', 'http://my-default-host.com'), - ], - ], -]; diff --git a/vendor/darkaonline/l5-swagger/phpstan.neon b/vendor/darkaonline/l5-swagger/phpstan.neon deleted file mode 100644 index 3b8f5e3a..00000000 --- a/vendor/darkaonline/l5-swagger/phpstan.neon +++ /dev/null @@ -1,7 +0,0 @@ -parameters: - level: 8 - paths: - - src - - tests/Unit - ignoreErrors: - - identifier: argument.templateType diff --git a/vendor/darkaonline/l5-swagger/resources/views/index.blade.php b/vendor/darkaonline/l5-swagger/resources/views/index.blade.php deleted file mode 100644 index 4f570408..00000000 --- a/vendor/darkaonline/l5-swagger/resources/views/index.blade.php +++ /dev/null @@ -1,174 +0,0 @@ - - - - - {{ $documentationTitle }} - - - - - @if(config('l5-swagger.defaults.ui.display.dark_mode')) - - @endif - - - -
- - - - - - diff --git a/vendor/darkaonline/l5-swagger/src/ConfigFactory.php b/vendor/darkaonline/l5-swagger/src/ConfigFactory.php deleted file mode 100644 index b50ad7f8..00000000 --- a/vendor/darkaonline/l5-swagger/src/ConfigFactory.php +++ /dev/null @@ -1,71 +0,0 @@ - The merged configuration for the specified documentation. - * - * @throws L5SwaggerException If the specified documentation configuration is not found. - */ - public function documentationConfig(?string $documentation = null): array - { - if ($documentation === null) { - $documentation = config('l5-swagger.default'); - } - - $defaults = config('l5-swagger.defaults', []); - $documentations = config('l5-swagger.documentations', []); - - if (! isset($documentations[$documentation])) { - throw new L5SwaggerException('Documentation config not found'); - } - - return $this->mergeConfig($defaults, $documentations[$documentation]); - } - - /** - * Merges two configuration arrays recursively, with the values from the second array - * overriding those in the first array when keys overlap. - * - * @param array $defaults The default configuration array. - * @param array $config The configuration array to merge into the defaults. - * @return array The merged configuration array. - */ - private function mergeConfig(array $defaults, array $config): array - { - $merged = $defaults; - - foreach ($config as $key => &$value) { - if (isset($defaults[$key]) - && $this->isAssociativeArray($defaults[$key]) - && $this->isAssociativeArray($value) - ) { - $merged[$key] = $this->mergeConfig($defaults[$key], $value); - continue; - } - - $merged[$key] = $value; - } - - return $merged; - } - - /** - * Determines whether a given value is an associative array. - * - * @param mixed $value The value to be checked. - * @return bool True if the value is an associative array, false otherwise. - */ - private function isAssociativeArray(mixed $value): bool - { - return is_array($value) && count(array_filter(array_keys($value), 'is_string')) > 0; - } -} diff --git a/vendor/darkaonline/l5-swagger/src/Console/GenerateDocsCommand.php b/vendor/darkaonline/l5-swagger/src/Console/GenerateDocsCommand.php deleted file mode 100644 index 89890b21..00000000 --- a/vendor/darkaonline/l5-swagger/src/Console/GenerateDocsCommand.php +++ /dev/null @@ -1,75 +0,0 @@ -option('all'); - - if ($all) { - /** @var array $documentations */ - $documentations = array_keys(config('l5-swagger.documentations', [])); - - foreach ($documentations as $documentation) { - $this->generateDocumentation($generatorFactory, $documentation); - } - - return; - } - - $documentation = $this->argument('documentation'); - - if (! $documentation) { - $documentation = config('l5-swagger.default'); - } - - $this->generateDocumentation($generatorFactory, $documentation); - } - - /** - * Generates documentation using the specified generator factory. - * - * @param GeneratorFactory $generatorFactory The factory used to create the documentation generator. - * @param string $documentation The name or identifier of the documentation to be generated. - * @return void - * - * @throws L5SwaggerException - */ - private function generateDocumentation(GeneratorFactory $generatorFactory, string $documentation): void - { - $this->info('Regenerating docs '.$documentation); - - $generator = $generatorFactory->make($documentation); - $generator->generateDocs(); - } -} diff --git a/vendor/darkaonline/l5-swagger/src/CustomGeneratorInterface.php b/vendor/darkaonline/l5-swagger/src/CustomGeneratorInterface.php deleted file mode 100644 index eb3f126b..00000000 --- a/vendor/darkaonline/l5-swagger/src/CustomGeneratorInterface.php +++ /dev/null @@ -1,10 +0,0 @@ - - */ - protected string|array $annotationsDir; - - protected string $docDir; - - protected string $docsFile; - - protected string $yamlDocsFile; - - /** - * @var array - */ - protected array $excludedDirs; - - /** - * @var array - */ - protected array $constants; - - protected ?OpenApi $openApi; - - protected bool $yamlCopyRequired; - - protected ?string $basePath; - - protected SecurityDefinitions $security; - - /** - * @var array - */ - protected array $scanOptions; - - protected Filesystem $fileSystem; - - /** - * Constructor to initialize documentation generation settings and dependencies. - * - * @param array $paths Array of paths including annotations, docs, excluded directories, and base path. - * @param array $constants Array of constants to be used during documentation generation. - * @param bool $yamlCopyRequired Determines if a YAML copy of the documentation is required. - * @param SecurityDefinitions $security Security definitions for the documentation. - * @param array $scanOptions Additional options for scanning files or directories. - * @param Filesystem|null $filesystem Filesystem instance, optional, defaults to a new Filesystem. - * @return void - */ - public function __construct( - array $paths, - array $constants, - bool $yamlCopyRequired, - SecurityDefinitions $security, - array $scanOptions, - ?Filesystem $filesystem = null - ) { - $this->annotationsDir = $paths['annotations']; - $this->docDir = $paths['docs']; - $this->docsFile = $this->docDir.DIRECTORY_SEPARATOR.($paths['docs_json'] ?? 'api-docs.json'); - $this->yamlDocsFile = $this->docDir.DIRECTORY_SEPARATOR.($paths['docs_yaml'] ?? 'api-docs.yaml'); - $this->excludedDirs = $paths['excludes']; - $this->basePath = $paths['base']; - $this->constants = $constants; - $this->yamlCopyRequired = $yamlCopyRequired; - $this->security = $security; - $this->scanOptions = $scanOptions; - - $this->fileSystem = $filesystem ?? new Filesystem(); - } - - /** - * Generate necessary documentation files by scanning and processing the required data. - * - * @return void - * - * @throws L5SwaggerException - * @throws Exception - */ - public function generateDocs(): void - { - $this->prepareDirectory() - ->defineConstants() - ->scanFilesForDocumentation() - ->populateServers() - ->saveJson() - ->makeYamlCopy(); - } - - /** - * Prepares the directory for storing documentation by ensuring it exists and is writable. - * - * @return self - * - * @throws L5SwaggerException If the directory is not writable or cannot be created. - */ - protected function prepareDirectory(): self - { - if ($this->fileSystem->exists($this->docDir) && ! $this->fileSystem->isWritable($this->docDir)) { - throw new L5SwaggerException('Documentation storage directory is not writable'); - } - - if (! $this->fileSystem->exists($this->docDir)) { - $this->fileSystem->makeDirectory($this->docDir); - } - - if (! $this->fileSystem->exists($this->docDir)) { - throw new L5SwaggerException('Documentation storage directory could not be created'); - } - - return $this; - } - - /** - * Define and set constants if not already defined. - * - * @return self - */ - protected function defineConstants(): self - { - if (! empty($this->constants)) { - foreach ($this->constants as $key => $value) { - defined($key) || define($key, $value); - } - } - - return $this; - } - - /** - * Scans files to generate documentation. - * - * @return self - */ - protected function scanFilesForDocumentation(): self - { - $generator = $this->createOpenApiGenerator(); - $finder = $this->createScanFinder(); - - // Analysis. - $analysis = Arr::get($this->scanOptions, self::SCAN_OPTION_ANALYSIS); - - $this->openApi = $generator->generate($finder, $analysis); - - return $this; - } - - /** - * Create and configure an instance of OpenApiGenerator. - * - * @return OpenApiGenerator - */ - protected function createOpenApiGenerator(): OpenApiGenerator - { - $factory = $this->scanOptions['generator_factory'] ?? null; - - if (is_string($factory) && is_subclass_of($factory, CustomGeneratorInterface::class)) { - $factory = new $factory(); - } - - $generator = $factory instanceof CustomGeneratorInterface - ? $factory->create() - : new OpenApiGenerator(); - - if (! empty($this->scanOptions['default_processors_configuration']) - && is_array($this->scanOptions['default_processors_configuration']) - ) { - $generator->setConfig($this->scanOptions['default_processors_configuration']); - } - - $generator->setVersion( - $this->scanOptions['open_api_spec_version'] ?? self::OPEN_API_DEFAULT_SPEC_VERSION - ); - - // Processors. - $this->setProcessors($generator); - - // Analyser. - $this->setAnalyser($generator); - - return $generator; - } - - /** - * Set the processors for the OpenAPI generator. - * - * @param OpenApiGenerator $generator The OpenAPI generator instance to configure. - * @return void - */ - protected function setProcessors(OpenApiGenerator $generator): void - { - $processorConfigs = Arr::get($this->scanOptions, self::SCAN_OPTION_PROCESSORS, []); - - if (empty($processorConfigs)) { - return; - } - - $normalizedConfigs = []; - foreach ($processorConfigs as $config) { - $normalizedConfigs[] = is_array($config) - ? $config - : ['class' => $config, 'after' => \OpenApi\Processors\BuildPaths::class]; - } - - $newPipeLine = []; - $generator->getProcessorPipeline()->walk( - function (callable $pipe) use ($normalizedConfigs, &$newPipeLine) { - $newPipeLine[] = $pipe; - foreach ($normalizedConfigs as $entry) { - $after = $entry['after']; - if ($pipe instanceof $after) { - $processor = $entry['class']; - $newPipeLine[] = is_string($processor) ? new $processor() : $processor; - } - } - } - ); - - $generator->setProcessorPipeline(new Pipeline($newPipeLine)); - } - - /** - * Set the analyzer for the OpenAPI generator based on scan options. - * - * @param OpenApiGenerator $generator The OpenAPI generator instance. - * @return void - */ - protected function setAnalyser(OpenApiGenerator $generator): void - { - $analyser = Arr::get($this->scanOptions, self::SCAN_OPTION_ANALYSER); - - if (! empty($analyser)) { - $generator->setAnalyser($analyser); - - return; - } - - // Use AttributeAnnotationFactory for PHP 8.1+ native attributes - $generator->setAnalyser(new \OpenApi\Analysers\ReflectionAnalyser([ - new \OpenApi\Analysers\AttributeAnnotationFactory(), - ])); - } - - /** - * Create and return a Finder instance configured for scanning directories. - * - * @return Finder - */ - protected function createScanFinder(): Finder - { - $pattern = Arr::get($this->scanOptions, self::SCAN_OPTION_PATTERN) ?: '*.php'; - $exclude = Arr::get($this->scanOptions, self::SCAN_OPTION_EXCLUDE); - - $exclude = ! empty($exclude) ? $exclude : $this->excludedDirs; - - return new SourceFinder($this->annotationsDir, $exclude, $pattern); - } - - /** - * Populate the servers list in the OpenAPI configuration using the base path. - * - * @return self - */ - protected function populateServers(): self - { - if ($this->basePath !== null && $this->openApi !== null) { - if ( - $this->openApi->servers === OpenApiGenerator::UNDEFINED // @phpstan-ignore-line - || is_array($this->openApi->servers) === false // @phpstan-ignore-line - ) { - $this->openApi->servers = []; - } - - $this->openApi->servers[] = new Server(['url' => $this->basePath]); - } - - return $this; - } - - /** - * Saves the JSON data and applies security measures to the file. - * - * @return self - * - * @throws FileNotFoundException - * @throws OpenApiException - */ - protected function saveJson(): self - { - if ($this->openApi !== null) { - $this->openApi->saveAs($this->docsFile); - } - - $this->security->generate($this->docsFile); - - return $this; - } - - /** - * Creates a YAML copy of the OpenAPI documentation if required. - * - * This method converts the JSON documentation file to YAML format and saves it - * to the specified file path when the YAML copy requirement is enabled. - * - * @return void - * - * @throws FileNotFoundException - */ - protected function makeYamlCopy(): void - { - if ($this->yamlCopyRequired) { - $yamlDocs = (new YamlDumper(2))->dump( - json_decode($this->fileSystem->get($this->docsFile), true), - 20, - 0, - Yaml::DUMP_OBJECT_AS_MAP ^ Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE - ); - - $this->fileSystem->put( - $this->yamlDocsFile, - $yamlDocs - ); - } - } -} diff --git a/vendor/darkaonline/l5-swagger/src/GeneratorFactory.php b/vendor/darkaonline/l5-swagger/src/GeneratorFactory.php deleted file mode 100644 index 5c665c73..00000000 --- a/vendor/darkaonline/l5-swagger/src/GeneratorFactory.php +++ /dev/null @@ -1,43 +0,0 @@ -configFactory->documentationConfig($documentation); - - $paths = $config['paths']; - $scanOptions = $config['scanOptions'] ?? []; - $constants = $config['constants'] ?? []; - $yamlCopyRequired = $config['generate_yaml_copy'] ?? false; - - $secSchemesConfig = $config['securityDefinitions']['securitySchemes'] ?? []; - $secConfig = $config['securityDefinitions']['security'] ?? []; - - $security = new SecurityDefinitions($secSchemesConfig, $secConfig); - - return new Generator( - $paths, - $constants, - $yamlCopyRequired, - $security, - $scanOptions - ); - } -} diff --git a/vendor/darkaonline/l5-swagger/src/Http/Controllers/SwaggerAssetController.php b/vendor/darkaonline/l5-swagger/src/Http/Controllers/SwaggerAssetController.php deleted file mode 100644 index fbbeafff..00000000 --- a/vendor/darkaonline/l5-swagger/src/Http/Controllers/SwaggerAssetController.php +++ /dev/null @@ -1,50 +0,0 @@ -offsetGet('documentation'); - $asset = $request->offsetGet('asset'); - - try { - $path = swagger_ui_dist_path($documentation, $asset); - - return (new Response( - $fileSystem->get($path), - 200, - [ - 'Content-Type' => (isset(pathinfo($asset)['extension']) && pathinfo($asset)['extension'] === 'css') - ? 'text/css' - : 'application/javascript', - ] - ))->setSharedMaxAge(31536000) - ->setMaxAge(31536000) - ->setExpires(new \DateTime('+1 year')); - } catch (L5SwaggerException $exception) { - return abort(404, $exception->getMessage()); - } - } -} diff --git a/vendor/darkaonline/l5-swagger/src/Http/Controllers/SwaggerController.php b/vendor/darkaonline/l5-swagger/src/Http/Controllers/SwaggerController.php deleted file mode 100644 index 4dca3083..00000000 --- a/vendor/darkaonline/l5-swagger/src/Http/Controllers/SwaggerController.php +++ /dev/null @@ -1,201 +0,0 @@ -offsetGet('documentation'); - $config = $request->offsetGet('config'); - - $formatToUseForDocs = $config['paths']['format_to_use_for_docs'] ?? 'json'; - $yamlFormat = ($formatToUseForDocs === 'yaml'); - - $filePath = sprintf( - '%s/%s', - $config['paths']['docs'], - $yamlFormat ? $config['paths']['docs_yaml'] : $config['paths']['docs_json'] - ); - - if ($config['generate_always']) { - $generator = $this->generatorFactory->make($documentation); - - try { - $generator->generateDocs(); - } catch (Exception $e) { - Log::error($e); - - abort( - 404, - sprintf( - 'Unable to generate documentation file to: "%s". Please make sure directory is writable. Error: %s', - $filePath, - $e->getMessage() - ) - ); - } - } - - if (! $fileSystem->exists($filePath)) { - abort(404, sprintf('Unable to locate documentation file at: "%s"', $filePath)); - } - - $content = $fileSystem->get($filePath); - - if ($yamlFormat) { - return response($content, 200, [ - 'Content-Type' => 'application/yaml', - 'Content-Disposition' => 'inline', - ]); - } - - return response($content, 200, [ - 'Content-Type' => 'application/json', - ]); - } - - /** - * Handles the API request and renders the Swagger documentation view. - * - * @param Request $request The HTTP request containing necessary parameters such as documentation and configuration details. - * @return Response The HTTP response rendering the Swagger documentation view. - */ - public function api(Request $request): Response - { - $documentation = $request->offsetGet('documentation'); - $config = $request->offsetGet('config'); - $proxy = $config['proxy']; - - if ($proxy) { - if (! is_array($proxy)) { - $proxy = [$proxy]; - } - Request::setTrustedProxies( - $proxy, - Request::HEADER_X_FORWARDED_FOR | - Request::HEADER_X_FORWARDED_HOST | - Request::HEADER_X_FORWARDED_PORT | - Request::HEADER_X_FORWARDED_PROTO | - Request::HEADER_X_FORWARDED_AWS_ELB - ); - } - - $urlToDocs = $this->generateDocumentationFileURL($documentation, $config); - $urlsToDocs = $this->getAllDocumentationUrls(); - $useAbsolutePath = config('l5-swagger.documentations.'.$documentation.'.paths.use_absolute_path', true); - - // Need the / at the end to avoid CORS errors on Homestead systems. - return response( - view('l5-swagger::index', [ - 'documentation' => $documentation, - 'documentationTitle' => $config['api']['title'] ?? $documentation, - 'secure' => RequestFacade::secure(), - 'urlToDocs' => $urlToDocs, // Is not used in the view, but still passed for backwards compatibility - 'urlsToDocs' => $urlsToDocs, - 'operationsSorter' => $config['operations_sort'], - 'configUrl' => $config['additional_config_url'], - 'validatorUrl' => $config['validator_url'], - 'useAbsolutePath' => $useAbsolutePath, - ]), - 200 - ); - } - - /** - * Handles the OAuth2 callback and retrieves the required file for the redirect. - * - * @param Request $request The HTTP request containing necessary parameters. - * @return string The content of the OAuth2 redirect file. - * - * @throws FileNotFoundException - * @throws L5SwaggerException - */ - public function oauth2Callback(Request $request): string - { - $fileSystem = new Filesystem(); - $documentation = $request->offsetGet('documentation'); - - return $fileSystem->get(swagger_ui_dist_path($documentation, 'oauth2-redirect.html')); - } - - /** - * Generate the URL for accessing the documentation file based on the provided configuration. - * - * @param string $documentation The name of the documentation instance. - * @param array $config The configuration settings for generating the documentation URL. - * @return string The generated URL for the documentation file. - */ - protected function generateDocumentationFileURL(string $documentation, array $config): string - { - $fileUsedForDocs = $config['paths']['docs_json'] ?? 'api-docs.json'; - - if (! empty($config['paths']['format_to_use_for_docs']) - && $config['paths']['format_to_use_for_docs'] === 'yaml' - && $config['paths']['docs_yaml'] - ) { - $fileUsedForDocs = $config['paths']['docs_yaml']; - } - - $useAbsolutePath = config('l5-swagger.documentations.'.$documentation.'.paths.use_absolute_path', true); - - return route( - 'l5-swagger.'.$documentation.'.docs', - $fileUsedForDocs, - $useAbsolutePath - ); - } - - /** - * Retrieves all available documentation URLs with their corresponding titles. - * - * @return array An associative array where the keys are documentation titles - * and the values are the corresponding URLs. - * - * @throws L5SwaggerException - */ - protected function getAllDocumentationUrls(): array - { - /** @var array $documentations */ - $documentations = array_keys(config('l5-swagger.documentations', [])); - - $urlsToDocs = []; - - foreach ($documentations as $documentationName) { - $config = $this->configFactory->documentationConfig($documentationName); - $title = $config['api']['title'] ?? $documentationName; - - $urlsToDocs[$title] = $this->generateDocumentationFileURL($documentationName, $config); - } - - return $urlsToDocs; - } -} diff --git a/vendor/darkaonline/l5-swagger/src/Http/Middleware/Config.php b/vendor/darkaonline/l5-swagger/src/Http/Middleware/Config.php deleted file mode 100644 index 2bb935e3..00000000 --- a/vendor/darkaonline/l5-swagger/src/Http/Middleware/Config.php +++ /dev/null @@ -1,38 +0,0 @@ -route()->getAction(); - - $documentation = $actions['l5-swagger.documentation']; - - $config = $this->configFactory->documentationConfig($documentation); - - $request->offsetSet('documentation', $documentation); - $request->offsetSet('config', $config); - - return $next($request); - } -} diff --git a/vendor/darkaonline/l5-swagger/src/L5SwaggerFacade.php b/vendor/darkaonline/l5-swagger/src/L5SwaggerFacade.php deleted file mode 100644 index 4249d63f..00000000 --- a/vendor/darkaonline/l5-swagger/src/L5SwaggerFacade.php +++ /dev/null @@ -1,20 +0,0 @@ -loadViewsFrom($viewPath, 'l5-swagger'); - - // Publish a config file - $configPath = __DIR__.'/../config/l5-swagger.php'; - $this->publishes([ - $configPath => config_path('l5-swagger.php'), - ], 'config'); - - //Publish views - $this->publishes([ - __DIR__.'/../resources/views' => config('l5-swagger.defaults.paths.views'), - ], 'views'); - - //Include routes - $this->loadRoutesFrom(__DIR__.'/routes.php'); - - //Register commands - $this->commands([GenerateDocsCommand::class]); - } - - /** - * Register the service provider. - * - * @return void - */ - public function register(): void - { - $configPath = __DIR__.'/../config/l5-swagger.php'; - $this->mergeConfigFrom($configPath, 'l5-swagger'); - - $this->app->singleton('command.l5-swagger.generate', function ($app) { - return $app->make(GenerateDocsCommand::class); - }); - - $this->app->bind(Generator::class, function ($app) { - $documentation = config('l5-swagger.default'); - - /** @var GeneratorFactory $factory */ - $factory = $app->make(GeneratorFactory::class); - - return $factory->make($documentation); - }); - } - - /** - * Get the services provided by the provider. - * - * @codeCoverageIgnore - * - * @return array - */ - public function provides(): array - { - return [ - 'command.l5-swagger.generate', - ]; - } -} diff --git a/vendor/darkaonline/l5-swagger/src/SecurityDefinitions.php b/vendor/darkaonline/l5-swagger/src/SecurityDefinitions.php deleted file mode 100644 index f4bc3869..00000000 --- a/vendor/darkaonline/l5-swagger/src/SecurityDefinitions.php +++ /dev/null @@ -1,117 +0,0 @@ - $schemasConfig - * @param array $securityConfig - */ - public function __construct( - private readonly array $schemasConfig = [], - private readonly array $securityConfig = [] - ) { - } - - /** - * Reads in the l5-swagger configuration and appends security settings to documentation. - * - * @param string $filename The path to the generated json documentation - * - * @throws FileNotFoundException - */ - public function generate(string $filename): void - { - $fileSystem = new Filesystem(); - - $documentation = collect( - json_decode($fileSystem->get($filename)) - ); - - if (! empty($this->schemasConfig)) { - $documentation = $this->injectSecuritySchemes($documentation, $this->schemasConfig); - } - - if (! empty($this->securityConfig)) { - $documentation = $this->injectSecurity($documentation, $this->securityConfig); - } - - $fileSystem->put( - $filename, - $documentation->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) - ); - } - - /** - * Inject security schemes settings. - * - * @param Collection $documentation The parse json - * @param array $config The securityScheme settings from l5-swagger - * @return Collection - */ - protected function injectSecuritySchemes(Collection $documentation, array $config): Collection - { - $components = collect(); - if ($documentation->has('components')) { - $components = collect($documentation->get('components')); - } - - $securitySchemes = collect(); - if ($components->has('securitySchemes')) { - $securitySchemes = collect($components->get('securitySchemes')); - } - - foreach ($config as $key => $cfg) { - $securitySchemes->offsetSet($key, $this->arrayToObject($cfg)); - } - - $components->offsetSet('securitySchemes', $securitySchemes); - - $documentation->offsetSet('components', $components); - - return $documentation; - } - - /** - * Inject security settings. - * - * @param Collection $documentation The parse json - * @param array $config The security settings from l5-swagger - * @return Collection - */ - protected function injectSecurity(Collection $documentation, array $config): Collection - { - $security = collect(); - if ($documentation->has('security')) { - $security = collect($documentation->get('security')); - } - - foreach ($config as $key => $cfg) { - if (! empty($cfg)) { - $security->put($key, $this->arrayToObject($cfg)); - } - } - - if ($security->count()) { - $documentation->offsetSet('security', $security); - } - - return $documentation; - } - - /** - * Converts an array to an object. - * - * @param $array - * @return object - */ - protected function arrayToObject(mixed $array): mixed - { - return json_decode(json_encode($array) ?: ''); - } -} diff --git a/vendor/darkaonline/l5-swagger/src/helpers.php b/vendor/darkaonline/l5-swagger/src/helpers.php deleted file mode 100644 index 33b94983..00000000 --- a/vendor/darkaonline/l5-swagger/src/helpers.php +++ /dev/null @@ -1,66 +0,0 @@ - 'L5Swagger'], static function (Router $router) { - $configFactory = resolve(ConfigFactory::class); - - /** @var array $documentations */ - $documentations = config('l5-swagger.documentations', []); - - foreach (array_keys($documentations) as $name) { - $config = $configFactory->documentationConfig($name); - - if (! isset($config['routes'])) { - continue; - } - - $groupOptions = $config['routes']['group_options'] ?? []; - - if (! isset($groupOptions['middleware'])) { - $groupOptions['middleware'] = []; - } - - if (is_string($groupOptions['middleware'])) { - $groupOptions['middleware'] = [$groupOptions['middleware']]; - } - - $groupOptions['l5-swagger.documentation'] = $name; - $groupOptions['middleware'][] = L5SwaggerConfig::class; - - Route::group($groupOptions, static function (Router $router) use ($name, $config) { - if (isset($config['routes']['api'])) { - $router->get($config['routes']['api'], [ - 'as' => 'l5-swagger.'.$name.'.api', - 'middleware' => $config['routes']['middleware']['api'] ?? [], - 'uses' => '\L5Swagger\Http\Controllers\SwaggerController@api', - ]); - } - - if (isset($config['routes']['docs'])) { - $router->get($config['routes']['docs'], [ - 'as' => 'l5-swagger.'.$name.'.docs', - 'middleware' => $config['routes']['middleware']['docs'] ?? [], - 'uses' => '\L5Swagger\Http\Controllers\SwaggerController@docs', - ]); - - $router->get($config['routes']['docs'].'/asset/{asset}', [ - 'as' => 'l5-swagger.'.$name.'.asset', - 'middleware' => $config['routes']['middleware']['asset'] ?? [], - 'uses' => '\L5Swagger\Http\Controllers\SwaggerAssetController@index', - ]); - } - - if (isset($config['routes']['oauth2_callback'])) { - $router->get($config['routes']['oauth2_callback'], [ - 'as' => 'l5-swagger.'.$name.'.oauth2_callback', - 'middleware' => $config['routes']['middleware']['oauth2_callback'] ?? [], - 'uses' => '\L5Swagger\Http\Controllers\SwaggerController@oauth2Callback', - ]); - } - }); - } -}); diff --git a/vendor/dedoc/scramble/CHANGELOG.md b/vendor/dedoc/scramble/CHANGELOG.md new file mode 100644 index 00000000..23eb4851 --- /dev/null +++ b/vendor/dedoc/scramble/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +All notable changes to `scramble` will be documented in this file. diff --git a/vendor/symfony/type-info/LICENSE b/vendor/dedoc/scramble/LICENSE.md similarity index 82% rename from vendor/symfony/type-info/LICENSE rename to vendor/dedoc/scramble/LICENSE.md index e374a5c8..6b9d8554 100644 --- a/vendor/symfony/type-info/LICENSE +++ b/vendor/dedoc/scramble/LICENSE.md @@ -1,14 +1,16 @@ -Copyright (c) 2024-present Fabien Potencier +The MIT License (MIT) + +Copyright (c) dedoc 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: +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/vendor/dedoc/scramble/README.md b/vendor/dedoc/scramble/README.md new file mode 100644 index 00000000..a7be5466 --- /dev/null +++ b/vendor/dedoc/scramble/README.md @@ -0,0 +1,41 @@ +

+ + Scramble – Laravel API documentation generator + +

+ +# Scramble + +Scramble generates API documentation for Laravel project. Without requiring you to manually write PHPDoc annotations. Docs are generated in OpenAPI 3.1.0 format. + +## Documentation + +You can find full documentation at [scramble.dedoc.co](https://scramble.dedoc.co). + +## Introduction + +The main motto of the project is generating your API documentation without requiring you to annotate your code. + +This allows you to focus on code and avoid annotating every possible param/field as it may result in outdated documentation. By generating docs automatically from the code your API will always have up-to-date docs which you can trust. + +## Installation +You can install the package via composer: +```shell +composer require dedoc/scramble +``` + +## Usage +After install you will have 2 routes added to your application: + +- `/docs/api` - UI viewer for your documentation +- `/docs/api.json` - Open API document in JSON format describing your API. + +By default, these routes are available only in `local` environment. You can change this behavior [by defining `viewApiDocs` gate](https://scramble.dedoc.co/usage/getting-started#docs-authorization). + +--- + +

+ + Donate + +

diff --git a/vendor/dedoc/scramble/composer.json b/vendor/dedoc/scramble/composer.json new file mode 100644 index 00000000..b45b3cb1 --- /dev/null +++ b/vendor/dedoc/scramble/composer.json @@ -0,0 +1,73 @@ +{ + "name": "dedoc/scramble", + "description": "Automatic generation of API documentation for Laravel applications.", + "keywords": [ + "laravel", + "documentation", + "openapi" + ], + "homepage": "https://github.com/dedoc/scramble", + "license": "MIT", + "authors": [ + { + "name": "Roman Lytvynenko", + "email": "litvinenko95@gmail.com", + "role": "Developer" + } + ], + "require": { + "php": "^8.1", + "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0", + "myclabs/deep-copy": "^1.12", + "nikic/php-parser": "^5.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "spatie/laravel-package-tools": "^1.9.2" + }, + "require-dev": { + "larastan/larastan": "^3.3", + "laravel/pint": "^v1.1.0", + "nunomaduro/collision": "^7.0|^8.0", + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.34|^3.7|^4.4", + "pestphp/pest-plugin-laravel": "^2.3|^3.1|^4.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5|^11.5.3|^12.5.12", + "spatie/laravel-permission": "^6.10|^7.2", + "spatie/pest-plugin-snapshots": "^2.1" + }, + "autoload": { + "psr-4": { + "Dedoc\\Scramble\\": "src", + "Dedoc\\Scramble\\Database\\Factories\\": "database/factories" + } + }, + "autoload-dev": { + "psr-4": { + "Dedoc\\Scramble\\Tests\\": "tests" + } + }, + "scripts": { + "analyse": "vendor/bin/phpstan analyse", + "test": "vendor/bin/pest", + "test-coverage": "vendor/bin/pest --coverage", + "format": "vendor/bin/pint" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "phpstan/extension-installer": true + } + }, + "extra": { + "laravel": { + "providers": [ + "Dedoc\\Scramble\\ScrambleServiceProvider" + ] + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/vendor/dedoc/scramble/config/scramble.php b/vendor/dedoc/scramble/config/scramble.php new file mode 100644 index 00000000..9001932c --- /dev/null +++ b/vendor/dedoc/scramble/config/scramble.php @@ -0,0 +1,174 @@ + [ + * 'include' => 'api', + * 'exclude' => ['api/internal'], + * ], + * + * Without *, patterns match path segments (api matches api and api/users, not apiary). + * With *, Str::is is used (e.g. api/v*). + * + * One static include → default server is /{include} and paths are stripped (/users). + * Multiple includes or wildcards → server defaults to / and paths stay full (/api/users). + * Override with `servers`, or use Scramble::registerApi() for separate bases. + */ + 'api_path' => 'api', + + /* + * Your API domain. By default, app domain is used. This is also a part of the default API routes + * matcher, so when implementing your own, make sure you use this config if needed. + */ + 'api_domain' => null, + + /* + * The path where your OpenAPI specification will be exported. + */ + 'export_path' => 'api.json', + + /* + * Cache configuration for the generated OpenAPI document. + * + * Use `scramble:cache` to warm the cache and `scramble:clear` to invalidate it. + */ + 'cache' => [ + 'key' => 'scramble.openapi', + 'store' => 'file', + ], + + 'info' => [ + /* + * API version. + */ + 'version' => env('API_VERSION', '0.0.1'), + + /* + * Description rendered on the home page of the API documentation (`/docs/api`). + */ + 'description' => '', + ], + + 'ui' => [ + 'title' => null, + ], + + 'renderer' => 'elements', + + 'renderers' => [ + /* + * Stoplight Elements config options: https://docs.stoplight.io/docs/elements/b074dc47b2826-elements-configuration-options + */ + 'elements' => [ + 'view' => 'scramble::docs', + 'theme' => 'light', + 'hideTryIt' => false, + 'hideSchemas' => false, + 'logo' => '', + 'tryItCredentialsPolicy' => 'include', + 'layout' => 'responsive', + 'router' => 'hash', + ], + /* + * Scalar API reference config options: https://scalar.com/products/api-references/configuration + */ + 'scalar' => [ + 'view' => 'scramble::scalar', + 'cdn' => 'https://cdn.jsdelivr.net/npm/@scalar/api-reference', + 'theme' => 'laravel', + 'proxyUrl' => 'https://proxy.scalar.com', + 'darkMode' => false, + 'showDeveloperTools' => 'never', + 'agent' => ['disabled' => true], + 'credentials' => 'include', + ], + ], + + /* + * The list of servers of the API. By default, when `null`, server URL will be created from + * `scramble.api_path` and `scramble.api_domain` config variables. When providing an array, you + * will need to specify the local server URL manually (if needed). + * + * Example of non-default config (final URLs are generated using Laravel `url` helper): + * + * ```php + * 'servers' => [ + * 'Live' => 'api', + * 'Prod' => 'https://scramble.dedoc.co/api', + * ], + * ``` + */ + 'servers' => null, + + /** + * Determines how Scramble stores the descriptions of enum cases. + * Available options: + * - 'description' – Case descriptions are stored as the enum schema's description using table formatting. + * - 'extension' – Case descriptions are stored in the `x-enumDescriptions` enum schema extension. + * + * @see https://redocly.com/docs-legacy/api-reference-docs/specification-extensions/x-enum-descriptions + * - false - Case descriptions are ignored. + */ + 'enum_cases_description_strategy' => 'description', + + /** + * Determines how Scramble stores the names of enum cases. + * Available options: + * - 'names' – Case names are stored in the `x-enumNames` enum schema extension. + * - 'varnames' - Case names are stored in the `x-enum-varnames` enum schema extension. + * - false - Case names are not stored. + */ + 'enum_cases_names_strategy' => false, + + /** + * When Scramble encounters deep objects in query parameters, it flattens the parameters so the generated + * OpenAPI document correctly describes the API. Flattening deep query parameters is relevant until + * OpenAPI 3.2 is released and query string structure can be described properly. + * + * For example, this nested validation rule describes the object with `bar` property: + * `['foo.bar' => ['required', 'int']]`. + * + * When `flatten_deep_query_parameters` is `true`, Scramble will document the parameter like so: + * `{"name":"foo[bar]", "schema":{"type":"int"}, "required":true}`. + * + * When `flatten_deep_query_parameters` is `false`, Scramble will document the parameter like so: + * `{"name":"foo", "schema": {"type":"object", "properties":{"bar":{"type": "int"}}, "required": ["bar"]}, "required":true}`. + */ + 'flatten_deep_query_parameters' => true, + + 'middleware' => [ + 'web', + RestrictedDocsAccess::class, + ], + + 'extensions' => [], + + /* + * Automatically document API security (OpenAPI `security` / `securitySchemes`) based on route + * middleware. + * + * Disabled by default. Uncomment the line below to enable `MiddlewareAuthSecurityStrategy`. + * When at least one documented route uses middleware matching the configured patterns (by default + * `auth` and `auth:*`), bearer auth is applied globally. Routes without matching middleware are + * marked as public (`security: []`). + * + * Set to `null` explicitly to disable. If you already configure security manually via + * `afterOpenApiGenerated` / `extendOpenApi`, keep this disabled to avoid duplicate schemes. + * + * Customize with a class-string or [class, options]: + * + * 'security_strategy' => [ + * \Dedoc\Scramble\SecurityDocumentation\MiddlewareAuthSecurityStrategy::class, + * [ + * 'middleware' => ['auth', 'auth:*'], + * 'scheme' => \Dedoc\Scramble\Support\Generator\SecurityScheme::http('bearer'), + * ], + * ], + */ + // 'security_strategy' => \Dedoc\Scramble\SecurityDocumentation\MiddlewareAuthSecurityStrategy::class, + 'security_strategy' => null, +]; diff --git a/vendor/dedoc/scramble/dictionaries/classMap.php b/vendor/dedoc/scramble/dictionaries/classMap.php new file mode 100644 index 00000000..6886be86 --- /dev/null +++ b/vendor/dedoc/scramble/dictionaries/classMap.php @@ -0,0 +1,10 @@ + 'O:47:"Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition":8:{s:68:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'propagatesTemplates";b:0;s:16:"'."\0".'*'."\0".'loadedMethods";a:0:{}s:54:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'index";O:32:"Dedoc\\Scramble\\Infer\\Scope\\Index":2:{s:18:"classesDefinitions";a:1:{s:9:"Exception";r:1;}s:20:"functionsDefinitions";a:0:{}}s:4:"name";s:9:"Exception";s:13:"templateTypes";a:7:{i:0;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:8:"TMessage";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\UnknownType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:48:"'."\0".'Dedoc\\Scramble\\Support\\Type\\UnknownType'."\0".'comment";s:0:"";}s:7:"default";N;}i:1;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:7:"TString";s:2:"is";O:38:"Dedoc\\Scramble\\Support\\Type\\StringType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:2;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TCode";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\UnknownType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:48:"'."\0".'Dedoc\\Scramble\\Support\\Type\\UnknownType'."\0".'comment";s:0:"";}s:7:"default";N;}i:3;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TFile";s:2:"is";O:38:"Dedoc\\Scramble\\Support\\Type\\StringType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:4;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TLine";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\IntegerType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:5;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:6:"TTrace";s:2:"is";O:37:"Dedoc\\Scramble\\Support\\Type\\ArrayType":4:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";O:37:"Dedoc\\Scramble\\Support\\Type\\MixedType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:3:"key";O:39:"Dedoc\\Scramble\\Support\\Type\\IntegerType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}}s:7:"default";N;}i:6;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:9:"TPrevious";s:2:"is";O:33:"Dedoc\\Scramble\\Support\\Type\\Union":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"types";a:2:{i:0;O:36:"Dedoc\\Scramble\\Support\\Type\\NullType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}i:1;O:38:"Dedoc\\Scramble\\Support\\Type\\ObjectType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:9:"Throwable";}}}s:7:"default";N;}}s:10:"properties";a:7:{s:7:"message";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:10;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";E:60:"Dedoc\\Scramble\\Infer\\Definition\\PropertyVisibility:Protected";s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:6:"string";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:19;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";E:58:"Dedoc\\Scramble\\Infer\\Definition\\PropertyVisibility:Private";s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"code";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:27;s:11:"defaultType";O:54:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralIntegerType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";i:0;}s:8:"isStatic";b:0;s:10:"visibility";r:91;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"file";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:36;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";r:91;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"line";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:44;s:11:"defaultType";O:54:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralIntegerType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";i:0;}s:8:"isStatic";b:0;s:10:"visibility";r:91;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:5:"trace";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:52;s:11:"defaultType";O:42:"Dedoc\\Scramble\\Support\\Type\\KeyedArrayType":4:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:6:"isList";b:1;s:5:"items";a:0:{}}s:8:"isStatic";b:0;s:10:"visibility";r:102;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:8:"previous";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:66;s:11:"defaultType";O:36:"Dedoc\\Scramble\\Support\\Type\\NullType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:8:"isStatic";b:0;s:10:"visibility";r:102;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}}s:7:"methods";a:0:{}s:9:"parentFqn";N;}', + 'RuntimeException' => 'O:47:"Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition":8:{s:68:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'propagatesTemplates";b:0;s:16:"'."\0".'*'."\0".'loadedMethods";a:0:{}s:54:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'index";O:32:"Dedoc\\Scramble\\Infer\\Scope\\Index":2:{s:18:"classesDefinitions";a:2:{s:9:"Exception";O:47:"Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition":8:{s:68:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'propagatesTemplates";b:0;s:16:"'."\0".'*'."\0".'loadedMethods";a:0:{}s:54:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'index";r:4;s:4:"name";s:9:"Exception";s:13:"templateTypes";a:7:{i:0;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:8:"TMessage";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\UnknownType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:48:"'."\0".'Dedoc\\Scramble\\Support\\Type\\UnknownType'."\0".'comment";s:0:"";}s:7:"default";N;}i:1;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:7:"TString";s:2:"is";O:38:"Dedoc\\Scramble\\Support\\Type\\StringType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:2;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TCode";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\UnknownType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:48:"'."\0".'Dedoc\\Scramble\\Support\\Type\\UnknownType'."\0".'comment";s:0:"";}s:7:"default";N;}i:3;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TFile";s:2:"is";O:38:"Dedoc\\Scramble\\Support\\Type\\StringType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:4;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TLine";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\IntegerType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:5;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:6:"TTrace";s:2:"is";O:37:"Dedoc\\Scramble\\Support\\Type\\ArrayType":4:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";O:37:"Dedoc\\Scramble\\Support\\Type\\MixedType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:3:"key";O:39:"Dedoc\\Scramble\\Support\\Type\\IntegerType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}}s:7:"default";N;}i:6;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:9:"TPrevious";s:2:"is";O:33:"Dedoc\\Scramble\\Support\\Type\\Union":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"types";a:2:{i:0;O:36:"Dedoc\\Scramble\\Support\\Type\\NullType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}i:1;O:38:"Dedoc\\Scramble\\Support\\Type\\ObjectType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:9:"Throwable";}}}s:7:"default";N;}}s:10:"properties";a:7:{s:7:"message";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:12;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";E:60:"Dedoc\\Scramble\\Infer\\Definition\\PropertyVisibility:Protected";s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:6:"string";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:21;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";E:58:"Dedoc\\Scramble\\Infer\\Definition\\PropertyVisibility:Private";s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"code";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:29;s:11:"defaultType";O:54:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralIntegerType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";i:0;}s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"file";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:38;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"line";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:46;s:11:"defaultType";O:54:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralIntegerType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";i:0;}s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:5:"trace";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:54;s:11:"defaultType";O:42:"Dedoc\\Scramble\\Support\\Type\\KeyedArrayType":4:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:6:"isList";b:1;s:5:"items";a:0:{}}s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:8:"previous";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:68;s:11:"defaultType";O:36:"Dedoc\\Scramble\\Support\\Type\\NullType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}}s:7:"methods";a:0:{}s:9:"parentFqn";N;}s:16:"RuntimeException";r:1;}s:20:"functionsDefinitions";a:0:{}}s:4:"name";s:16:"RuntimeException";s:13:"templateTypes";a:7:{i:0;r:12;i:1;r:21;i:2;r:29;i:3;r:38;i:4;r:46;i:5;r:54;i:6;r:68;}s:10:"properties";a:7:{s:7:"message";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:12;s:11:"defaultType";r:88;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:6:"string";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:21;s:11:"defaultType";r:99;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"code";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:29;s:11:"defaultType";r:110;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"file";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:38;s:11:"defaultType";r:121;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"line";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:46;s:11:"defaultType";r:132;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:5:"trace";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:54;s:11:"defaultType";r:143;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:8:"previous";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:68;s:11:"defaultType";r:155;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}}s:7:"methods";a:0:{}s:9:"parentFqn";s:9:"Exception";}', + 'Symfony\\Component\\HttpKernel\\Exception\\HttpException' => 'O:47:"Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition":8:{s:68:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'propagatesTemplates";b:0;s:16:"'."\0".'*'."\0".'loadedMethods";a:0:{}s:54:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'index";O:32:"Dedoc\\Scramble\\Infer\\Scope\\Index":2:{s:18:"classesDefinitions";a:3:{s:9:"Exception";O:47:"Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition":8:{s:68:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'propagatesTemplates";b:0;s:16:"'."\0".'*'."\0".'loadedMethods";a:0:{}s:54:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'index";r:4;s:4:"name";s:9:"Exception";s:13:"templateTypes";a:7:{i:0;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:8:"TMessage";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\UnknownType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:48:"'."\0".'Dedoc\\Scramble\\Support\\Type\\UnknownType'."\0".'comment";s:0:"";}s:7:"default";N;}i:1;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:7:"TString";s:2:"is";O:38:"Dedoc\\Scramble\\Support\\Type\\StringType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:2;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TCode";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\UnknownType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:48:"'."\0".'Dedoc\\Scramble\\Support\\Type\\UnknownType'."\0".'comment";s:0:"";}s:7:"default";N;}i:3;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TFile";s:2:"is";O:38:"Dedoc\\Scramble\\Support\\Type\\StringType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:4;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:5:"TLine";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\IntegerType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:5;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:6:"TTrace";s:2:"is";O:37:"Dedoc\\Scramble\\Support\\Type\\ArrayType":4:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";O:37:"Dedoc\\Scramble\\Support\\Type\\MixedType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:3:"key";O:39:"Dedoc\\Scramble\\Support\\Type\\IntegerType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}}s:7:"default";N;}i:6;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:9:"TPrevious";s:2:"is";O:33:"Dedoc\\Scramble\\Support\\Type\\Union":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"types";a:2:{i:0;O:36:"Dedoc\\Scramble\\Support\\Type\\NullType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}i:1;O:38:"Dedoc\\Scramble\\Support\\Type\\ObjectType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:9:"Throwable";}}}s:7:"default";N;}}s:10:"properties";a:7:{s:7:"message";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:12;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";E:60:"Dedoc\\Scramble\\Infer\\Definition\\PropertyVisibility:Protected";s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:6:"string";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:21;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";E:58:"Dedoc\\Scramble\\Infer\\Definition\\PropertyVisibility:Private";s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"code";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:29;s:11:"defaultType";O:54:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralIntegerType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";i:0;}s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"file";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:38;s:11:"defaultType";O:53:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralStringType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";s:0:"";}s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"line";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:46;s:11:"defaultType";O:54:"Dedoc\\Scramble\\Support\\Type\\Literal\\LiteralIntegerType":3:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";i:0;}s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:5:"trace";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:54;s:11:"defaultType";O:42:"Dedoc\\Scramble\\Support\\Type\\KeyedArrayType":4:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:6:"isList";b:1;s:5:"items";a:0:{}}s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:8:"previous";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:68;s:11:"defaultType";O:36:"Dedoc\\Scramble\\Support\\Type\\NullType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}}s:7:"methods";a:0:{}s:9:"parentFqn";N;}s:16:"RuntimeException";O:47:"Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition":8:{s:68:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'propagatesTemplates";b:0;s:16:"'."\0".'*'."\0".'loadedMethods";a:0:{}s:54:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition'."\0".'index";r:4;s:4:"name";s:16:"RuntimeException";s:13:"templateTypes";a:7:{i:0;r:12;i:1;r:21;i:2;r:29;i:3;r:38;i:4;r:46;i:5;r:54;i:6;r:68;}s:10:"properties";a:7:{s:7:"message";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:12;s:11:"defaultType";r:88;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:6:"string";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:21;s:11:"defaultType";r:99;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"code";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:29;s:11:"defaultType";r:110;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"file";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:38;s:11:"defaultType";r:121;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"line";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:46;s:11:"defaultType";r:132;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:5:"trace";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:54;s:11:"defaultType";r:143;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:8:"previous";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:68;s:11:"defaultType";r:155;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}}s:7:"methods";a:0:{}s:9:"parentFqn";s:9:"Exception";}s:52:"Symfony\\Component\\HttpKernel\\Exception\\HttpException";r:1;}s:20:"functionsDefinitions";a:0:{}}s:4:"name";s:52:"Symfony\\Component\\HttpKernel\\Exception\\HttpException";s:13:"templateTypes";a:9:{i:0;r:12;i:1;r:21;i:2;r:29;i:3;r:38;i:4;r:46;i:5;r:54;i:6;r:68;i:7;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:11:"TStatusCode";s:2:"is";O:39:"Dedoc\\Scramble\\Support\\Type\\IntegerType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:7:"default";N;}i:8;O:40:"Dedoc\\Scramble\\Support\\Type\\TemplateType":5:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:4:"name";s:8:"THeaders";s:2:"is";O:37:"Dedoc\\Scramble\\Support\\Type\\ArrayType":4:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}s:5:"value";O:37:"Dedoc\\Scramble\\Support\\Type\\MixedType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}s:3:"key";O:39:"Dedoc\\Scramble\\Support\\Type\\IntegerType":2:{s:8:"original";N;s:52:"'."\0".'Dedoc\\Scramble\\Support\\Type\\AbstractType'."\0".'attributes";a:0:{}}}s:7:"default";N;}}s:10:"properties";a:9:{s:7:"message";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:12;s:11:"defaultType";r:88;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:6:"string";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:21;s:11:"defaultType";r:99;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"code";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:29;s:11:"defaultType";r:110;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"file";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:38;s:11:"defaultType";r:121;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:4:"line";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:46;s:11:"defaultType";r:132;s:8:"isStatic";b:0;s:10:"visibility";r:93;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:5:"trace";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:54;s:11:"defaultType";r:143;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:8:"previous";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:68;s:11:"defaultType";r:155;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:10:"statusCode";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:247;s:11:"defaultType";N;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}s:7:"headers";O:55:"Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition":7:{s:64:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'docNode";N;s:4:"type";r:255;s:11:"defaultType";N;s:8:"isStatic";b:0;s:10:"visibility";r:104;s:10:"attributes";a:0:{}s:74:"'."\0".'Dedoc\\Scramble\\Infer\\Definition\\ClassPropertyDefinition'."\0".'pendingDocComment";N;}}s:7:"methods";a:0:{}s:9:"parentFqn";s:16:"RuntimeException";}', +]; diff --git a/vendor/dedoc/scramble/dictionaries/scripts/generate.php b/vendor/dedoc/scramble/dictionaries/scripts/generate.php new file mode 100644 index 00000000..f2685b10 --- /dev/null +++ b/vendor/dedoc/scramble/dictionaries/scripts/generate.php @@ -0,0 +1,46 @@ +singleton(FileParser::class, function () { + return new FileParser( + (new ParserFactory)->createForHostVersion() + ); +}); +app()->singleton(Index::class); + +$classesDefinitions = []; +foreach ($classes as $className) { + $classesDefinitions[$className] = generateClassDefinitionInitialization($className); +} + +function generateClassDefinitionInitialization(string $name) +{ + $classAnalyzer = app(\Dedoc\Scramble\Infer\Analyzer\ClassAnalyzer::class); + + $classDefinition = $classAnalyzer->analyze($name); + foreach ($classDefinition->methods as $methodName => $method) { + $classDefinition->getMethodDefinition($methodName); + } + + return serialize($classAnalyzer->analyze($name)); +} + +$def = var_export($classesDefinitions, true); +file_put_contents(__DIR__.'/../classMap.php', << + + + + + + {{ $config->get('ui.title') ?? config('app.name') . ' - API Docs' }} + + + + + + + + + +renderer()->all(except: ['theme']) as $key => $value) + @continue(! $value) + {{ $key }}="{{ $value === true ? 'true' : ($value === false ? 'false' : $value) }}" + @endforeach +/> + + +@if($config->renderer()->get('theme', 'light') === 'system') + +@endif + + diff --git a/vendor/dedoc/scramble/resources/views/scalar.blade.php b/vendor/dedoc/scramble/resources/views/scalar.blade.php new file mode 100644 index 00000000..b19878fe --- /dev/null +++ b/vendor/dedoc/scramble/resources/views/scalar.blade.php @@ -0,0 +1,32 @@ + + + + + + {{ $config->get('ui.title') ?? config('app.name') . ' - API Docs' }} + + +
+ + + + + diff --git a/vendor/dedoc/scramble/routes/web.php b/vendor/dedoc/scramble/routes/web.php new file mode 100644 index 00000000..cbd3fc5e --- /dev/null +++ b/vendor/dedoc/scramble/routes/web.php @@ -0,0 +1,7 @@ +name('scramble.docs.ui'); + +Scramble::registerJsonSpecificationRoute(path: 'docs/api.json')->name('scramble.docs.document'); diff --git a/vendor/dedoc/scramble/src/AbstractOpenApiVisitor.php b/vendor/dedoc/scramble/src/AbstractOpenApiVisitor.php new file mode 100644 index 00000000..cd548f6a --- /dev/null +++ b/vendor/dedoc/scramble/src/AbstractOpenApiVisitor.php @@ -0,0 +1,10 @@ +value instanceof MissingValue ? new OpenApiMissingValue : $example->value, + summary: $example->summary, + description: $example->description, + externalValue: $example->externalValue, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Attributes/ExcludeAllRoutesFromDocs.php b/vendor/dedoc/scramble/src/Attributes/ExcludeAllRoutesFromDocs.php new file mode 100644 index 00000000..a1f19d4a --- /dev/null +++ b/vendor/dedoc/scramble/src/Attributes/ExcludeAllRoutesFromDocs.php @@ -0,0 +1,11 @@ +|object|MissingValue $example + */ + public function __construct( + public readonly string $name, + public readonly ?string $description = null, + public readonly ?string $type = null, + public readonly ?string $format = null, + public readonly ?bool $required = null, + /** @var array|scalar|null|MissingValue */ + public readonly mixed $default = new MissingValue, + public readonly mixed $example = new MissingValue, + /** @var array $examples */ + public readonly array $examples = [], + public readonly ?bool $deprecated = null, + public readonly ?bool $explode = null, + public readonly int|string $status = 200, + ) {} + + public static function toOpenApiHeader(Header $header, TypeTransformer $openApiTransformer): OpenApiHeader + { + $type = self::getType($header, $openApiTransformer); + $default = $header->default instanceof MissingValue ? new OpenApiMissingValue : $header->default; + $format = $header->format ?: ''; + + if ($type instanceof MixedType && ($format || ! $default instanceof OpenApiMissingValue)) { + $type = new StringType; + } + + return new OpenApiHeader( + description: $header->description, + required: $header->required, + deprecated: $header->deprecated, + explode: $header->explode, + schema: Schema::fromType( + $type->default($default)->format($format) + ), + example: $header->example instanceof MissingValue ? new OpenApiMissingValue : $header->example, + examples: array_map(fn ($e) => Example::toOpenApiExample($e), $header->examples), + ); + } + + public static function getType(Header $header, TypeTransformer $openApiTransformer): OpenApiType + { + return $header->type ? $openApiTransformer->transform( + PhpDocTypeHelper::toType( + PhpDoc::parse("/** @return $header->type */")->getReturnTagValues()[0]->type ?? new IdentifierTypeNode('mixed') + ) + ) : new MixedType; + } +} diff --git a/vendor/dedoc/scramble/src/Attributes/HeaderParameter.php b/vendor/dedoc/scramble/src/Attributes/HeaderParameter.php new file mode 100644 index 00000000..d5c29f48 --- /dev/null +++ b/vendor/dedoc/scramble/src/Attributes/HeaderParameter.php @@ -0,0 +1,24 @@ + $examples The key is a distinct name and the value is an example object. + */ + public function __construct( + public readonly string $in, + public readonly string $name, + public readonly ?string $description = null, + ?bool $required = null, + public bool $deprecated = false, + public ?string $type = null, + public ?string $format = null, + public bool $infer = true, + public mixed $default = new MissingValue, + public mixed $example = new MissingValue, + public array $examples = [], + ) { + $this->required = $required !== null ? $required : $this->in === 'path'; + } +} diff --git a/vendor/dedoc/scramble/src/Attributes/PathParameter.php b/vendor/dedoc/scramble/src/Attributes/PathParameter.php new file mode 100644 index 00000000..8d3e14b7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Attributes/PathParameter.php @@ -0,0 +1,24 @@ +|scalar|null + */ +#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION)] +class Response +{ + public function __construct( + public readonly int|string $status = 200, + public readonly ?string $description = null, + public readonly string $mediaType = 'application/json', + public readonly ?string $type = null, + public readonly ?string $format = null, + /** @var BaseExample|BaseExample[] $examples */ + public readonly mixed $examples = [], + ) {} + + public static function toOpenApiResponse(Response $responseAttribute, ?OpenApiResponse $originalResponse, TypeTransformer $openApiTransformer, ?FileNameResolver $nameResolver): OpenApiResponse + { + $response = $originalResponse ? deep_copy($originalResponse) : OpenApiResponse::make($responseAttribute->status); + + $response = self::applyResponseMediaType($responseAttribute, $response, $openApiTransformer, $nameResolver); + + $response->setDescription(self::getDescription($responseAttribute, $response)); + + return $response; + } + + private static function applyResponseMediaType(Response $responseAttribute, OpenApiResponse $response, TypeTransformer $openApiTransformer, ?FileNameResolver $nameResolver): OpenApiResponse + { + if (! $responseAttribute->type) { + return $response + ->setContent( + $responseAttribute->mediaType, + self::getMediaType($responseAttribute, $response), + ); + } + + $responseFromType = $openApiTransformer->toResponse( + PhpDocTypeHelper::toType( + PhpDoc::parse("/** @return $responseAttribute->type */", $nameResolver)->getReturnTagValues()[0]->type ?? new IdentifierTypeNode('mixed') + ) + ); + + if ($responseFromType instanceof Reference) { + $responseFromType = deep_copy($responseFromType->resolve()); + } + + /** @var OpenApiResponse|null $responseFromType */ + if (! $responseFromType) { + return $response->setContent( + $responseAttribute->mediaType, + self::getMediaType($responseAttribute, $response), + ); + } + + return $response + ->setDescription($responseFromType->description ?: self::getDescription($responseAttribute, $response)) + ->setContent( + $responseAttribute->mediaType, + self::getMediaType($responseAttribute, $responseFromType), + ); + } + + private static function getDescription(Response $responseAttribute, OpenApiResponse $response): string + { + if ($responseAttribute->description === null) { + return $response->description; + } + + return Str::replace('$0', $response->description, $responseAttribute->description); + } + + private static function getMediaType(Response $responseAttribute, OpenApiResponse $response): Schema|Reference + { + return self::getSchema($responseAttribute, $response->content[$responseAttribute->mediaType] ?? null); + } + + private static function getSchema(Response $responseAttribute, Schema|Reference|null $schema): Schema|Reference + { + if (! $responseAttribute->format && ! $responseAttribute->examples) { + return $schema ?: Schema::fromType(new StringType); + } + + $schemaType = $schema + ? clone ($schema instanceof Reference ? $schema->resolve()->type : $schema->type) + : new StringType; + + /** @var Type $schemaType */ + if ($responseAttribute->format) { + $schemaType->format($responseAttribute->format); + } + + if ($responseAttribute->examples) { + $schemaType->examples($responseAttribute->examples); // @phpstan-ignore argument.type + } + + return Schema::fromType($schemaType); + } +} diff --git a/vendor/dedoc/scramble/src/Attributes/SchemaName.php b/vendor/dedoc/scramble/src/Attributes/SchemaName.php new file mode 100644 index 00000000..6cafca7f --- /dev/null +++ b/vendor/dedoc/scramble/src/Attributes/SchemaName.php @@ -0,0 +1,21 @@ +generator)($config); + } + + $key = $keyBase.':'.$this->resolveApi($config); + + if ($cached = cache()->store($store)->get($key)) { + return $cached; + } + + return ($this->generator)($config); + } + + private function resolveApi(GeneratorConfig $config): string + { + foreach (Scramble::getConfigurationsInstance()->all() as $api => $generatorConfig) { + if ($generatorConfig === $config) { + return $api; + } + } + + return Scramble::DEFAULT_API; + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/ApiPath.php b/vendor/dedoc/scramble/src/Configuration/ApiPath.php new file mode 100644 index 00000000..e6711eff --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/ApiPath.php @@ -0,0 +1,96 @@ + $includes + * @param list $excludes + */ + private function __construct( + private readonly array $includes, + private readonly array $excludes, + ) {} + + /** + * @param string|array{include?: string|string[], exclude?: string|string[]}|null $config + */ + public static function from(mixed $config, string $default = 'api'): self + { + if ($config === null || is_string($config)) { + return new self(self::normalizePrefixes($config ?? $default), []); + } + + if (is_array($config) && (array_key_exists('include', $config) || array_key_exists('exclude', $config))) { + return new self( + self::normalizePrefixes($config['include'] ?? $default), + self::normalizePrefixes($config['exclude'] ?? []), + ); + } + + throw new InvalidArgumentException( + 'Invalid scramble.api_path config. Expected a string or an array with `include` and/or `exclude` keys.' + ); + } + + /** + * @return list + */ + private static function normalizePrefixes(mixed $prefixes): array + { + if ($prefixes === '' || $prefixes === null) { + return []; + } + + return array_values(array_filter(Arr::wrap($prefixes), fn ($prefix) => is_string($prefix) && $prefix !== '')); + } + + public function matches(string $uri): bool + { + if ($this->includes !== [] && ! $this->matchesAny($uri, $this->includes)) { + return false; + } + + return ! $this->matchesAny($uri, $this->excludes); + } + + public function stripPrefix(string $uri): string + { + if (! $this->usesSingleBase()) { + return $uri; + } + + return (string) Str::of($uri) + ->replaceStart($this->includes[0], '') + ->trim('/'); + } + + public function serverPath(): string + { + return $this->usesSingleBase() ? $this->includes[0] : ''; + } + + private function usesSingleBase(): bool + { + return count($this->includes) === 1 && ! str_contains($this->includes[0], '*'); + } + + /** + * @param list $patterns + */ + private function matchesAny(string $uri, array $patterns): bool + { + foreach ($patterns as $pattern) { + if (str_contains($pattern, '*') ? Str::is($pattern, $uri) : ($uri === $pattern || Str::startsWith($uri, $pattern.'/'))) { + return true; + } + } + + return false; + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/DocumentTransformers.php b/vendor/dedoc/scramble/src/Configuration/DocumentTransformers.php new file mode 100644 index 00000000..cdcdc90e --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/DocumentTransformers.php @@ -0,0 +1,56 @@ +appends = array_merge( + $this->appends, + Arr::wrap($transformers) + ); + + return $this; + } + + public function prepend(array|callable|string $transformers) + { + $this->prepends = array_merge( + $this->prepends, + Arr::wrap($transformers) + ); + + return $this; + } + + public function use(array $transformers) + { + $this->transformers = $transformers; + + return $this; + } + + /** + * @return (callable|class-string)[] + */ + public function all(): array + { + $base = $this->transformers; + + return array_values(array_unique([ + ...$this->prepends, + ...$base, + ...$this->appends, + ], SORT_REGULAR)); + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/GeneratorConfigCollection.php b/vendor/dedoc/scramble/src/Configuration/GeneratorConfigCollection.php new file mode 100644 index 00000000..64fcc67c --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/GeneratorConfigCollection.php @@ -0,0 +1,53 @@ + + */ + private array $apis = []; + + public function __construct() + { + $this->apis[Scramble::DEFAULT_API] = $this->buildDefaultApiConfiguration(); + } + + private function buildDefaultApiConfiguration(): GeneratorConfig + { + return (new GeneratorConfig) + ->expose( + ui: fn (Router $router, $action) => $router->get('docs/api', $action)->name('scramble.docs.ui'), + document: fn (Router $router, $action) => $router->get('docs/api.json', $action)->name('scramble.docs.document'), + ); + } + + public function get(string $name): GeneratorConfig + { + if (! array_key_exists($name, $this->apis)) { + throw new LogicException("$name API is not registered. Register the API using `Scramble::registerApi` first."); + } + + return $this->apis[$name]; + } + + public function register(string $name, array $config): GeneratorConfig + { + $this->apis[$name] = $generatorConfig = $this->apis[Scramble::DEFAULT_API] + ->cloneWithoutExposing() + ->useConfig(array_merge(config('scramble') ?: [], $config)); + + return $generatorConfig; + } + + public function all(): array + { + return $this->apis; + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/InferConfig.php b/vendor/dedoc/scramble/src/Configuration/InferConfig.php new file mode 100644 index 00000000..e06ebb6a --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/InferConfig.php @@ -0,0 +1,90 @@ +forcedAstSourcedDefinitionsMatchers = [ + ...$this->forcedAstSourcedDefinitionsMatchers, + ...array_map( + fn ($item) => is_string($item) ? new ClassLike($item) : $item, + Arr::wrap($items), + ), + ]; + + return $this; + } + + /** + * @param (string|DefinitionMatcher)[] $items + * @return $this + */ + public function buildDefinitionsUsingReflectionFor(array $items): static + { + $this->forcedReflectionSourcedDefinitionsMatchers = [ + ...$this->forcedReflectionSourcedDefinitionsMatchers, + ...array_map( + fn ($item) => is_string($item) ? new ClassLike($item) : $item, + Arr::wrap($items), + ), + ]; + + return $this; + } + + /** + * @param DefinitionMatcher[] $matchers + */ + private function matchesAnyDefinitionMatcher(string $class, array $matchers): bool + { + foreach ($matchers as $item) { + if ($item->matches($class)) { + return true; + } + } + + return false; + } + + public function shouldAnalyzeAst(string $class): bool + { + if ($this->matchesAnyDefinitionMatcher($class, $this->forcedReflectionSourcedDefinitionsMatchers)) { + return false; + } + + if ($this->matchesAnyDefinitionMatcher($class, $this->forcedAstSourcedDefinitionsMatchers)) { + return true; + } + + // Ignoring static analysis error due to it is fine to exception to be thrown here if bad input. + $reflection = new \ReflectionClass($class); // @phpstan-ignore argument.type + + $path = $reflection->getFileName(); + + if (! $path) { + return true; // Keep in mind the internal classes are analyzed via AST analyzer + } + + return ! Str::contains($path, DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR); + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/JsonApiConfig.php b/vendor/dedoc/scramble/src/Configuration/JsonApiConfig.php new file mode 100644 index 00000000..78747c6c --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/JsonApiConfig.php @@ -0,0 +1,24 @@ +maxRelationshipDepth === null) { + return JsonApiResource::$maxRelationshipDepth; // @phpstan-ignore return.type + } + + return max(0, min($this->maxRelationshipDepth, JsonApiResource::$maxRelationshipDepth)); + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/OperationTransformers.php b/vendor/dedoc/scramble/src/Configuration/OperationTransformers.php new file mode 100644 index 00000000..43f860fe --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/OperationTransformers.php @@ -0,0 +1,69 @@ +appends = array_merge( + $this->appends, + Arr::wrap($transformers) + ); + + return $this; + } + + public function prepend(array|callable|string $transformers) + { + $this->prepends = array_merge( + $this->prepends, + Arr::wrap($transformers) + ); + + return $this; + } + + public function use(array $transformers) + { + $this->transformers = $transformers; + + return $this; + } + + /** + * @return list> + */ + public function all(): array + { + $base = $this->transformers ?: [ + RequestEssentialsExtension::class, + RequestBodyExtension::class, + ErrorResponsesExtension::class, + ResponseExtension::class, + ResponseHeadersExtension::class, + DeprecationExtension::class, + ]; + + return array_values(array_unique([ + ...$this->prepends, + ...$base, + ...$this->appends, + ], SORT_REGULAR)); + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/ParametersExtractors.php b/vendor/dedoc/scramble/src/Configuration/ParametersExtractors.php new file mode 100644 index 00000000..5ec6be32 --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/ParametersExtractors.php @@ -0,0 +1,88 @@ +[] */ + protected array $extractors = []; + + /** @var class-string[] */ + protected array $appends = []; + + /** @var class-string[] */ + protected array $prepends = []; + + /** + * @param class-string[]|class-string $extractor + * @return $this + */ + public function append(array|string $extractor): self + { + $this->appends = array_merge( + $this->appends, + Arr::wrap($extractor) + ); + + return $this; + } + + /** + * @param class-string[]|class-string $extractor + * @return $this + */ + public function prepend(array|string $extractor): self + { + $this->prepends = array_merge( + $this->prepends, + Arr::wrap($extractor) + ); + + return $this; + } + + /** + * @param class-string[] $extractors + * @return $this + */ + public function use(array $extractors): self + { + $this->extractors = $extractors; + + return $this; + } + + /** + * @return class-string[] + */ + public function all(): array + { + $base = $this->extractors ?: [ + PathParametersExtractor::class, + FormRequestParametersExtractor::class, + ValidateCallParametersExtractor::class, + JsonApiResourceParametersExtractor::class, + ]; + + $defaultAppends = [ + MethodCallsParametersExtractor::class, + AttributesParametersExtractor::class, + ]; + + return array_values(array_unique([ + ...$this->prepends, + ...$base, + ...$this->appends, + ...$defaultAppends, + ])); + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/RendererConfig.php b/vendor/dedoc/scramble/src/Configuration/RendererConfig.php new file mode 100644 index 00000000..af54d810 --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/RendererConfig.php @@ -0,0 +1,32 @@ +config = Arr::except($config, ['view']); + $this->view = $config['view']; + } + + public function get(string $key, mixed $default = null): mixed + { + return Arr::get($this->config, $key, $default); + } + + public function all(array $except = []): array + { + return Arr::except($this->config, $except); + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/RuleTransformers.php b/vendor/dedoc/scramble/src/Configuration/RuleTransformers.php new file mode 100644 index 00000000..5068b39b --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/RuleTransformers.php @@ -0,0 +1,97 @@ +> */ + protected array $transformers = []; + + /** @var list> */ + protected array $appends = []; + + /** @var list> */ + protected array $prepends = []; + + /** + * @param class-string|list> $transformers + */ + public function append(array|string $transformers): self + { + $this->appends = array_values(array_merge( // @phpstan-ignore arrayValues.list + $this->appends, + Arr::wrap($transformers) + )); + + return $this; + } + + /** + * @param class-string|list> $transformers + */ + public function prepend(array|string $transformers): self + { + $this->prepends = array_values(array_merge( // @phpstan-ignore arrayValues.list + $this->prepends, + Arr::wrap($transformers) + )); + + return $this; + } + + /** + * @param list> $transformers + */ + public function use(array $transformers): self + { + $this->transformers = $transformers; + + return $this; + } + + /** + * @template TExtensionType of object + * + * @param class-string $type + * @param array $contextfulBindings + * @return Collection + */ + public function instances(string $type, array $contextfulBindings): Collection + { + return collect($this->all()) // @phpstan-ignore return.type + ->filter(fn ($class) => is_a($class, $type, true)) + ->map(fn ($class) => ContainerUtils::makeContextable($class, $contextfulBindings)) + ->values(); + } + + /** + * @return list> + */ + public function all(): array + { + $base = $this->transformers ?: [ + EnumRule::class, + InRule::class, + ConfirmedRule::class, + ExistsRule::class, + RegexRule::class, + ]; + + return array_values(array_unique([ + ...$this->prepends, + ...$base, + ...$this->appends, + ], SORT_REGULAR)); + } +} diff --git a/vendor/dedoc/scramble/src/Configuration/SecurityDocumentationContext.php b/vendor/dedoc/scramble/src/Configuration/SecurityDocumentationContext.php new file mode 100644 index 00000000..1b4ef4af --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/SecurityDocumentationContext.php @@ -0,0 +1,17 @@ + $routes + */ + public function __construct( + public readonly Collection $routes, + public readonly GeneratorConfig $config, + ) {} +} diff --git a/vendor/dedoc/scramble/src/Configuration/ServerVariables.php b/vendor/dedoc/scramble/src/Configuration/ServerVariables.php new file mode 100644 index 00000000..b830a720 --- /dev/null +++ b/vendor/dedoc/scramble/src/Configuration/ServerVariables.php @@ -0,0 +1,32 @@ + $items + */ + public function __construct(public array $items = []) {} + + public function use(array $items) + { + $this->items = $items; + + return $this; + } + + public function all() + { + return $this->items; + } + + public function set(string $name, ServerVariable $serverVariable) + { + $this->items[$name] = $serverVariable; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Console/Commands/AnalyzeDocumentation.php b/vendor/dedoc/scramble/src/Console/Commands/AnalyzeDocumentation.php new file mode 100644 index 00000000..c3b78c0b --- /dev/null +++ b/vendor/dedoc/scramble/src/Console/Commands/AnalyzeDocumentation.php @@ -0,0 +1,141 @@ +setThrowExceptions(false); + + $generator(Scramble::getGeneratorConfig($this->option('api'))); + + $i = 1; + $this->groupExceptions($generator->exceptions)->each(function (Collection $exceptions, string $group) use (&$i) { + $this->renderExceptionsGroup($exceptions, $group, $i); + }); + + if (count($generator->exceptions)) { + $this->error('[ERROR] Found '.count($generator->exceptions).' errors.'); + + return static::FAILURE; + } + + $this->info('Everything is fine! Documentation is generated without any errors 🍻'); + + return static::SUCCESS; + } + + /** + * @return Collection> + */ + private function groupExceptions(array $exceptions): Collection + { + return collect($exceptions) + ->groupBy(fn ($e) => $e instanceof RouteAware ? $this->getRouteKey($e->getRoute()) : ''); + } + + /** + * @param Collection $exceptions + */ + private function renderExceptionsGroup(Collection $exceptions, string $group, int &$i): void + { + // when route key is set, then the exceptions in the group are route aware. + if ($group) { + $this->renderRouteExceptionsGroupLine($exceptions); + } + + $exceptions->each(function ($exception) use (&$i) { + $this->renderException($exception, $i); + $i++; + $this->line(''); + }); + } + + private function getRouteKey(?Route $route): string + { + if (! $route) { + return ''; + } + + $method = implode('|', $route->methods()); + $action = $route->getAction('uses'); + + return "$method.$action"; + } + + /** + * @param Collection $exceptions + */ + private function renderRouteExceptionsGroupLine(Collection $exceptions): void + { + $firstException = $exceptions->first(); + $route = $firstException->getRoute(); + + $method = implode('|', $route->methods()); + $errorsMessage = ($count = $exceptions->count()).' '.Str::plural('error', $count); + + $tocComponent = new TermsOfContentItem( + right: 'getHttpMethodColor($method).'>'.$method." $route->uri $errorsMessage", + left: $this->getRouteAction($route), + ); + + $tocComponent->render($this->output); + + $this->line(''); + } + + private function getHttpMethodColor(string $method): string + { + return match ($method) { + 'POST', 'PUT' => 'blue', + 'DELETE' => 'red', + default => 'yellow', + }; + } + + public function getRouteAction(?Route $route): ?string + { + if (! $uses = $route->getAction('uses')) { + return null; + } + + if (count($parts = explode('@', $uses)) !== 2 || ! method_exists(...$parts)) { + return null; + } + + [$class, $method] = $parts; + + $eloquentClassName = Str::replace(['App\Http\Controllers\\', 'App\Http\\'], '', $class); + + return "{$eloquentClassName}@{$method}"; + } + + private function renderException(Throwable $exception, int $i): void + { + $message = Str::replace('Dedoc\Scramble\Support\Generator\Types\\', '', property_exists($exception, 'originalMessage') ? $exception->originalMessage : $exception->getMessage()); // @phpstan-ignore argument.templateType + + $this->output->writeln("$i. {$message}"); + + if ($exception instanceof ConsoleRenderable) { + $exception->renderInConsole($this->output); + } + } +} diff --git a/vendor/dedoc/scramble/src/Console/Commands/CacheDocumentation.php b/vendor/dedoc/scramble/src/Console/Commands/CacheDocumentation.php new file mode 100644 index 00000000..924982db --- /dev/null +++ b/vendor/dedoc/scramble/src/Console/Commands/CacheDocumentation.php @@ -0,0 +1,41 @@ +ensureDocumentationCacheConfigured()) { + return self::SUCCESS; + } + + $store = config('scramble.cache.store'); + + foreach ($this->resolveApis() as $api) { + $config = Scramble::getGeneratorConfig($api); + + cache()->store($store)->forever( + $this->cacheKey($api), + $generator($config), + ); + + $this->info("OpenAPI document cached for [{$api}] API."); + } + + return self::SUCCESS; + } +} diff --git a/vendor/dedoc/scramble/src/Console/Commands/ClearDocumentationCache.php b/vendor/dedoc/scramble/src/Console/Commands/ClearDocumentationCache.php new file mode 100644 index 00000000..d0cad05b --- /dev/null +++ b/vendor/dedoc/scramble/src/Console/Commands/ClearDocumentationCache.php @@ -0,0 +1,34 @@ +ensureDocumentationCacheConfigured()) { + return self::SUCCESS; + } + + $store = config('scramble.cache.store'); + + foreach ($this->resolveApis() as $api) { + cache()->store($store)->forget($this->cacheKey($api)); + + $this->info("OpenAPI document cache cleared for [{$api}] API."); + } + + return self::SUCCESS; + } +} diff --git a/vendor/dedoc/scramble/src/Console/Commands/Components/Code.php b/vendor/dedoc/scramble/src/Console/Commands/Components/Code.php new file mode 100644 index 00000000..c9a95c88 --- /dev/null +++ b/vendor/dedoc/scramble/src/Console/Commands/Components/Code.php @@ -0,0 +1,21 @@ +highlight(file_get_contents($this->filePath), $this->line); + + $style->writeln($code); + } +} diff --git a/vendor/dedoc/scramble/src/Console/Commands/Components/Component.php b/vendor/dedoc/scramble/src/Console/Commands/Components/Component.php new file mode 100644 index 00000000..163e8f73 --- /dev/null +++ b/vendor/dedoc/scramble/src/Console/Commands/Components/Component.php @@ -0,0 +1,10 @@ +getWidth(); + + $rightWidth = $this->getLineWidth($this->right ?: ''); + $leftWidth = $this->getLineWidth($this->left ?: ''); + + if (! $leftWidth) { + $style->writeln($this->right); + + return; + } + + $dotsCount = max($width - $rightWidth - $leftWidth - 2, 0); + + $style->writeln("{$this->right} ".Str::repeat('.', $dotsCount)." {$this->left}"); + } + + private function getLineWidth(string $string) + { + $re = '/<.*?>/m'; + + return mb_strlen(preg_replace($re, '', $string)); + } +} diff --git a/vendor/dedoc/scramble/src/Console/Commands/Concerns/ManagesDocumentationCache.php b/vendor/dedoc/scramble/src/Console/Commands/Concerns/ManagesDocumentationCache.php new file mode 100644 index 00000000..fa46c71d --- /dev/null +++ b/vendor/dedoc/scramble/src/Console/Commands/Concerns/ManagesDocumentationCache.php @@ -0,0 +1,42 @@ +info('Documentation cache store is not configured. Set `scramble.cache.store` in your config.'); + + return false; + } + + if (config('scramble.cache.key') === null) { + $this->info('Documentation cache key is not configured. Set `scramble.cache.key` in your config.'); + + return false; + } + + return true; + } + + /** @return string[] */ + protected function resolveApis(): array + { + $apis = array_filter($this->option('api') ?: []); + + if ($apis === [] || $apis === ['*']) { + return array_keys(Scramble::getConfigurationsInstance()->all()); + } + + return $apis; + } + + protected function cacheKey(string $api): string + { + return config('scramble.cache.key').':'.$api; + } +} diff --git a/vendor/dedoc/scramble/src/Console/Commands/ExportDocumentation.php b/vendor/dedoc/scramble/src/Console/Commands/ExportDocumentation.php new file mode 100644 index 00000000..46ac51ca --- /dev/null +++ b/vendor/dedoc/scramble/src/Console/Commands/ExportDocumentation.php @@ -0,0 +1,35 @@ +option('api'); + $path = $this->option('path'); + + $config = Scramble::getGeneratorConfig($api); + + $specification = json_encode($generator($config), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + + /** @var string $filename */ + $filename = $path ?: $config->get('export_path') ?? 'api'.($api === 'default' ? '' : "-$api").'.json'; + + File::put($filename, $specification); + + $this->info("OpenAPI document exported to {$filename}."); + } +} diff --git a/vendor/dedoc/scramble/src/ContextReferences.php b/vendor/dedoc/scramble/src/ContextReferences.php new file mode 100644 index 00000000..9d06e764 --- /dev/null +++ b/vendor/dedoc/scramble/src/ContextReferences.php @@ -0,0 +1,20 @@ + $items The key is reference ID and the value is a list of such references. + */ + public function __construct( + public array $items = [], + ) {} + + public function has(string $referenceId): bool + { + return array_key_exists($referenceId, $this->items); + } + + /** + * @return Reference[] + */ + public function get(string $referenceId): array + { + return $this->items[$referenceId] ?? []; + } + + public function add(string $referenceId, Reference $reference): Reference + { + $this->items[$referenceId] ??= []; + $this->items[$referenceId][] = $this->setUniqueName($reference); + + return $reference; + } + + public function setUniqueName(Reference $reference): Reference + { + $reference->fullName = $reference->shortName ?: $this->uniqueSchemaName($reference->fullName); + + return $reference; + } + + public function uniqueName(string $referenceId): string + { + if ($this->has($referenceId)) { + $reference = $this->get($referenceId)[0]; + + return $reference->shortName ?: $reference->fullName; + } + + return $this->uniqueSchemaName($referenceId); + } + + private function uniqueSchemaName(string $fullName) + { + $shortestPossibleName = class_basename($fullName); + + if ( + ($this->tempNames[$shortestPossibleName] ?? null) === null + || ($this->tempNames[$shortestPossibleName] ?? null) === $fullName + ) { + $this->tempNames[$shortestPossibleName] = $fullName; + + return static::slug($shortestPossibleName); + } + + return static::slug($fullName); + } + + private static function slug(string $name) + { + return Str::replace('\\', '.', $name); + } +} diff --git a/vendor/dedoc/scramble/src/Contracts/AllRulesSchemasTransformer.php b/vendor/dedoc/scramble/src/Contracts/AllRulesSchemasTransformer.php new file mode 100644 index 00000000..bc5f4734 --- /dev/null +++ b/vendor/dedoc/scramble/src/Contracts/AllRulesSchemasTransformer.php @@ -0,0 +1,14 @@ +tags = $this->makeTagsFromGroupAttributes($context->groups); + } + + /** + * @param Collection> $groupsAttributes + * @return Tag[] + */ + private function makeTagsFromGroupAttributes(Collection $groupsAttributes) + { + /** @var Collection $tags */ + $tags = $groupsAttributes->reduce(function (Collection $acc, ReflectionAttribute $attribute) { + $arguments = $attribute->getArguments(); + + $name = $arguments['name'] ?? $arguments[0] ?? null; + + if (! $name) { + return $acc; + } + + $description = $arguments['description'] ?? $arguments[1] ?? null; + $weight = $arguments['weight'] ?? $arguments[2] ?? null; + + /** @var Tag $tag */ + $tag = $acc->get($name, new Tag($name)); + + if ($description !== null && $tag->description === null) { + $tag->description = $description; + } + + if ($weight !== null && $tag->getAttribute('weight') === null) { + $tag->setAttribute('weight', $weight); + } + + $acc->offsetSet($name, $tag); + + return $acc; + }, collect()); + + return $tags->sortBy(fn (Tag $t) => $t->getAttribute('weight', INF))->values()->all(); + } +} diff --git a/vendor/dedoc/scramble/src/DocumentTransformers/CleanupUnusedResponseReferencesTransformer.php b/vendor/dedoc/scramble/src/DocumentTransformers/CleanupUnusedResponseReferencesTransformer.php new file mode 100644 index 00000000..5211d95f --- /dev/null +++ b/vendor/dedoc/scramble/src/DocumentTransformers/CleanupUnusedResponseReferencesTransformer.php @@ -0,0 +1,36 @@ +components; + $responses = $components->responses; + + foreach ($responses as $responseName => $reference) { + if (! $this->isResponseReferenceUsed($document, $context->references->responses->uniqueName($responseName))) { + $components->removeResponse($responseName); + } + } + } + + private function isResponseReferenceUsed(OpenApi $document, string $responseName): bool + { + if (! isset($this->serializedDocumentJson)) { + $this->serializedDocumentJson = json_encode($document->toArray(), JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); + } + + $referencePath = "#/components/responses/{$responseName}"; + + return Str::contains($this->serializedDocumentJson, $referencePath); + } +} diff --git a/vendor/dedoc/scramble/src/Enums/JsonApiArraySerialization.php b/vendor/dedoc/scramble/src/Enums/JsonApiArraySerialization.php new file mode 100644 index 00000000..06041f28 --- /dev/null +++ b/vendor/dedoc/scramble/src/Enums/JsonApiArraySerialization.php @@ -0,0 +1,9 @@ +getAttribute('file'); + /** @var int|null $line */ + $line = $schema->getAttribute('line'); + + $originalMessage = $message; + if ($file) { + $message = rtrim($message, '.').'. Got when analyzing an expression in file ['.$file.'] on line '.$line; + } + + $exception = new static($path.': '.$message); + + $exception->originalMessage = $originalMessage; + $exception->originFile = $file; + $exception->originLine = $line; + $exception->jsonPointer = $path; + + return $exception; + } + + public function getRouteAwareMessage(Route $route, string $msg): string + { + $method = $route->methods()[0]; + $action = $route->getAction('uses'); + + return "'$method $route->uri' ($action): ".$msg; + } + + public function renderInConsole(OutputStyle $outputStyle): void + { + $codeSample = null; + $tableCells = []; + if ($this->jsonPointer) { + $tableCells[] = ['Found at', $this->jsonPointer]; + } + + if ($this->originFile) { + $line = $this->originLine ? ":$this->originLine" : ''; + + $tableCells[] = ['Inferred at', "{$this->originFile}{$line}"]; + + if ($line) { + $path = class_exists($this->originFile) + ? (new \ReflectionClass($this->originFile))->getFileName() + : $this->originFile; + + $codeSample = new Code($path, $this->originLine); + } + } + + $outputStyle->createTable()->setRows($tableCells)->setStyle('compact')->render(); + + $codeSample?->render($outputStyle); + } +} diff --git a/vendor/dedoc/scramble/src/Exceptions/OpenApiReferenceTargetNotFoundException.php b/vendor/dedoc/scramble/src/Exceptions/OpenApiReferenceTargetNotFoundException.php new file mode 100644 index 00000000..92bc9982 --- /dev/null +++ b/vendor/dedoc/scramble/src/Exceptions/OpenApiReferenceTargetNotFoundException.php @@ -0,0 +1,5 @@ +route = $route; + + if (method_exists($this, 'getRouteAwareMessage')) { + $this->message = $this->getRouteAwareMessage($route, $this->getMessage()); + } + + return $this; + } + + public function getRoute(): ?Route + { + return $this->route; + } +} diff --git a/vendor/dedoc/scramble/src/Exceptions/RulesEvaluationException.php b/vendor/dedoc/scramble/src/Exceptions/RulesEvaluationException.php new file mode 100644 index 00000000..9d2d4bdc --- /dev/null +++ b/vendor/dedoc/scramble/src/Exceptions/RulesEvaluationException.php @@ -0,0 +1,64 @@ + */ + public array $exceptions = []; + + /** + * @param array $exceptions + */ + public static function fromExceptions(array $exceptions): self + { + $exceptions = collect(Arr::wrap($exceptions)) + ->reduce(function (array $acc, Throwable $e, string $key) { + if ($e instanceof self) { + return array_merge($acc, $e->exceptions); + } + + return [...$acc, $key => $e]; + }, []); + + $previous = reset($exceptions) ?: null; + + $exception = new self(self::buildMessage($exceptions), previous: $previous); + $exception->exceptions = $exceptions; + + return $exception; + } + + /** + * @param array $exceptions + */ + private static function buildMessage(array $exceptions): string + { + $lines = [ + 'Cannot evaluate validation rules ('.count($exceptions).' evaluators failed):', + ]; + + foreach ($exceptions as $key => $e) { + $name = class_basename($key); + + $context = self::extractFileLocation($e); + + $lines[] = " [$name] {$e->getMessage()}".($context ? " {$context}" : ''); + } + + return implode("\n", $lines); + } + + private static function extractFileLocation(Throwable $e): ?string + { + if ($e->getFile() && $e->getLine()) { + return "(at {$e->getFile()}:{$e->getLine()})"; + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Extensions/ExceptionToResponseExtension.php b/vendor/dedoc/scramble/src/Extensions/ExceptionToResponseExtension.php new file mode 100644 index 00000000..70ef38b4 --- /dev/null +++ b/vendor/dedoc/scramble/src/Extensions/ExceptionToResponseExtension.php @@ -0,0 +1,29 @@ +infer = $infer; + $this->openApiTransformer = $openApiTransformer; + $this->components = $components; + } + + public function toResponse(Type $type) + { + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Extensions/OperationExtension.php b/vendor/dedoc/scramble/src/Extensions/OperationExtension.php new file mode 100644 index 00000000..5950a9a8 --- /dev/null +++ b/vendor/dedoc/scramble/src/Extensions/OperationExtension.php @@ -0,0 +1,21 @@ +throwExceptions = $throwExceptions; + + return $this; + } + + public function __invoke(?GeneratorConfig $config = null) + { + $config ??= Scramble::getGeneratorConfig(Scramble::DEFAULT_API); + + $routes = $this->getRoutes($config); + $config = $this->configureSecurityStrategy($routes, $config); + + $openApi = $this->makeOpenApi($config); + $context = new OpenApiContext($openApi, $config); + + $typeTransformer = $this->buildTypeTransformer($context); + + $routes + ->flatMap(function (Route $route, int $index) use ($openApi, $config, $typeTransformer) { + try { + $operations = $this->routeToOperations($openApi, $route, $config, $typeTransformer); + + foreach ($operations as $i => $operation) { + if ($route->getAction('uses') instanceof Closure) { + $operation->setAttribute('isClosure', true); + } + + $operation->setAttribute('index', $index + $i); + } + + return $operations; + } catch (Throwable $e) { + if ($e instanceof RouteAware) { + $e->setRoute($route); + } + + if (config('app.debug', false)) { + $method = implode('|', $route->methods()); + $action = $route->getAction('uses'); + if ($action instanceof Closure) { + $action = '{closure}'; + } + + dump("Error when analyzing route '$method $route->uri' ($action): {$e->getMessage()} – ".($e->getFile().' on line '.$e->getLine())); + logger()->error("Error when analyzing route '$method $route->uri' ($action): {$e->getMessage()} – ".($e->getFile().' on line '.$e->getLine())); + } + + throw $e; + } + }) + ->filter() + ->sortBy($this->createOperationsSorter()) + ->each(fn (Operation $operation) => $openApi->addPath( + Path::make( + $config->apiPath()->stripPrefix($operation->path) + )->addOperation($operation) + )) + ->toArray(); + + $this->setUniqueOperationId($openApi); + + $this->moveSameAlternativeServersToPath($openApi); + + foreach ($config->documentTransformers->all() as $openApiTransformer) { + $openApiTransformer = is_callable($openApiTransformer) + ? $openApiTransformer + : ContainerUtils::makeContextable($openApiTransformer, [ + TypeTransformer::class => $typeTransformer, + ]); + + if (is_callable($openApiTransformer)) { + $openApiTransformer($openApi, $context); + + continue; + } + + if ($openApiTransformer instanceof DocumentTransformer) { + $openApiTransformer->handle($openApi, $context); + + continue; + } + + // @phpstan-ignore deadCode.unreachable + throw new InvalidArgumentException('(callable(OpenApi, OpenApiContext): void)|DocumentTransformer type for document transformer expected, received '.$openApiTransformer::class); + } + + return $openApi->toArray(); + } + + private function configureSecurityStrategy(Collection $routes, GeneratorConfig $config): GeneratorConfig + { + $strategy = $config->securityStrategy(); + + if (! $strategy) { + return $config; + } + + return $strategy->configure(new SecurityDocumentationContext($routes, $config->cloneWithoutExposing())); + } + + private function createOperationsSorter(): array + { + $defaultSortValue = fn (Operation $o) => $o->tags[0] ?? null; + + return [ + fn (Operation $a, Operation $b) => $a->getAttribute('groupWeight', INF) <=> $b->getAttribute('groupWeight', INF), + fn (Operation $a, Operation $b) => $a->getAttribute('weight', INF) <=> $b->getAttribute('weight', INF), // @todo manual endpoint sorting + fn (Operation $a, Operation $b) => $defaultSortValue($a) <=> $defaultSortValue($b), + fn (Operation $a, Operation $b) => $a->getAttribute('index', INF) <=> $b->getAttribute('index', INF), + ]; + } + + private function makeOpenApi(GeneratorConfig $config) + { + $openApi = OpenApi::make('3.1.0') + ->setComponents(new Components) + ->setInfo( + InfoObject::make($config->get('ui.title', $default = config('app.name')) ?: $default) + ->setVersion($config->get('info.version', '0.0.1')) + ->setDescription($config->get('info.description', '')) + ); + + [$defaultProtocol] = explode('://', url('/')); + $servers = $config->get('servers') ?: [ + '' => ($domain = $config->get('api_domain')) + ? $defaultProtocol.'://'.$domain.'/'.$config->apiPath()->serverPath() + : $config->apiPath()->serverPath(), + ]; + foreach ($servers as $description => $url) { + $openApi->addServer( + (new ServerFactory($config->serverVariables->all()))->make(url($url ?: '/'), $description) + ); + } + + return $openApi; + } + + /** + * @return Collection + */ + private function getRoutes(GeneratorConfig $config): Collection + { + return collect(RouteFacade::getRoutes()) + ->pipe(function (Collection $c) { + $onlyRoutes = $c->filter(function (Route $route) { + + if (! is_string($route->getAction('controller'))) { + return false; + } + + if (! is_string($route->getAction('uses'))) { + return false; + } + + try { + $reflection = new ReflectionMethod(...explode('@', $route->getAction('uses'))); + + if (str_contains($reflection->getDocComment() ?: '', '@only-docs')) { + return true; + } + } catch (Throwable) { + } + + return false; + }); + + return $onlyRoutes->count() ? $onlyRoutes : $c; + }) + ->filter(function (Route $route) { + return ! ($name = $route->getAction('as')) || ! Str::startsWith($name, 'scramble'); + }) + ->filter($config->routes()) + ->filter(function (Route $route) { + if (! is_string($route->getAction('uses'))) { + return true; + } + + try { + $reflection = new ReflectionMethod(...explode('@', $route->getAction('uses'))); + } catch (ReflectionException) { + /* + * If route is registered but route method doesn't exist, it will not be included + * in the resulting documentation. + */ + return false; + } + + if (count($reflection->getAttributes(ExcludeRouteFromDocs::class))) { + return false; + } + + if (count($reflection->getDeclaringClass()->getAttributes(ExcludeAllRoutesFromDocs::class))) { + return false; + } + + return true; + }) + ->values(); + } + + private function buildTypeTransformer(OpenApiContext $context): TypeTransformer + { + return app()->make(TypeTransformer::class, [ + 'context' => $context, + ]); + } + + /** @return Operation[] */ + private function routeToOperations(OpenApi $openApi, Route $route, GeneratorConfig $config, TypeTransformer $typeTransformer): array + { + $methods = array_map('strtolower', Arr::wrap(($config->operationMethodsResolver)($route))); + + $operations = []; + foreach ($methods as $method) { + $routeInfo = new RouteInfo($route, $method); + + $operation = $this->operationBuilder->build($routeInfo, $openApi, $config, $typeTransformer); + + $this->ensureSchemaTypes($route, $operation); + + $operations[] = $operation; + } + + return $operations; + } + + private function ensureSchemaTypes(Route $route, Operation $operation): void + { + if (! Scramble::getSchemaValidator()->hasRules()) { + return; + } + + [$traverser, $visitor] = $this->createSchemaEnforceTraverser($route); + + $traverser->traverse($operation, ['', 'paths', $operation->path, $operation->method]); + $references = $visitor->popReferences(); + + /** @var Reference $ref */ + foreach ($references as $ref) { + if ($resolvedType = $ref->resolve()) { + $traverser->traverse($resolvedType, ['', 'components', $ref->referenceType, $ref->getUniqueName()]); + } + } + } + + private function createSchemaEnforceTraverser(Route $route) + { + $traverser = new OpenApiTraverser([$visitor = new SchemaEnforceVisitor($route, $this->throwExceptions, $this->exceptions)]); + + return [$traverser, $visitor]; + } + + private function moveSameAlternativeServersToPath(OpenApi $openApi) + { + foreach (collect($openApi->paths)->groupBy('path') as $pathsGroup) { + if ($pathsGroup->isEmpty()) { + continue; + } + + $operations = collect($pathsGroup->pluck('operations')->flatten()); + + $operationsHaveSameAlternativeServers = $operations->count() + && $operations->every(fn (Operation $o) => count($o->servers)) + && $operations->unique(function (Operation $o) { + return collect($o->servers)->map(fn (Server $s) => $s->url)->join('.'); + })->count() === 1; + + if (! $operationsHaveSameAlternativeServers) { + continue; + } + + $pathsGroup->every->servers($operations->first()->servers); + + foreach ($operations as $operation) { + $operation->servers([]); + } + } + } + + private function setUniqueOperationId(OpenApi $openApi) + { + $names = new UniqueNamesOptionsCollection; + + $this->foreachOperation($openApi, function (Operation $operation) use ($names) { + if ($operation->operationId) { + return; + } + + $names->push($operation->getAttribute('operationId')); // @phpstan-ignore argument.type + }); + + $this->foreachOperation($openApi, function (Operation $operation, $index) use ($names) { + if ($operation->operationId) { + return; + } + + $name = $operation->getAttribute('operationId'); + if (! $name instanceof UniqueNameOptions) { + return; + } + + if (! $name->eloquent && $operation->getAttribute('isClosure')) { + return; + } + + $operation->setOperationId($names->getUniqueName($name, function (string $fallback) use ($index) { + return "{$fallback}_{$index}"; + })); + }); + } + + private function foreachOperation(OpenApi $openApi, callable $callback) + { + foreach (collect($openApi->paths)->groupBy('path') as $pathsGroup) { + if ($pathsGroup->isEmpty()) { + continue; + } + + $operations = collect($pathsGroup->pluck('operations')->flatten()); + + foreach ($operations as $index => $operation) { + $callback($operation, $index); + } + } + } +} diff --git a/vendor/dedoc/scramble/src/GeneratorConfig.php b/vendor/dedoc/scramble/src/GeneratorConfig.php new file mode 100644 index 00000000..2b7160d8 --- /dev/null +++ b/vendor/dedoc/scramble/src/GeneratorConfig.php @@ -0,0 +1,319 @@ +operationMethodsResolver = $operationMethodsResolver ?: fn (Route $r) => $r->methods()[0]; + } + + public function config(array $config) + { + $this->config = $config; + $this->resolvedApiPath = null; + + return $this; + } + + public function routes(?Closure $routeResolver = null) + { + if (count(func_get_args()) === 0) { + return $this->routeResolver ?: $this->defaultRoutesFilter(...); + } + + if ($routeResolver) { + $this->routeResolver = $routeResolver; + } + + return $this; + } + + public function renderer(): RendererConfig + { + if (Arr::has($this->config, 'ui.logo')) { + return new RendererConfig(array_merge( + $this->get('renderers.elements', []), + $this->get('ui'), + )); + } + + return new RendererConfig($this->get('renderers.'.$this->get('renderer'), [])); + } + + /** + * @param (Closure(Router, mixed): Route)|string|false $ui + * @param (Closure(Router, mixed): Route)|string|false $document + */ + public function expose(Closure|string|false $ui = false, Closure|string|false $document = false): static + { + if (count(func_get_args()) === 1 && isset(func_get_args()[0]) && func_get_args()[0] === false) { + $this->uiRoute = null; + $this->documentRoute = null; + + return $this; + } + + $this->uiRoute = $ui ?: null; + $this->documentRoute = $document ?: null; + + return $this; + } + + private function defaultRoutesFilter(Route $route) + { + $expectedDomain = $this->get('api_domain'); + + return $this->apiPath()->matches($route->uri) + && (! $expectedDomain || $route->getDomain() === $expectedDomain); + } + + public function apiPath(): ApiPath + { + return $this->resolvedApiPath ??= ApiPath::from($this->get('api_path'), 'api'); + } + + public function afterOpenApiGenerated(?callable $afterOpenApiGenerated = null) + { + if (count(func_get_args()) === 0) { + return $this->documentTransformers->all(); + } + + if ($afterOpenApiGenerated) { + $this->documentTransformers->append($afterOpenApiGenerated); + } + + return $this; + } + + public function useConfig(array $config): static + { + $this->config = $config; + $this->resolvedApiPath = null; + + return $this; + } + + public function cloneWithoutExposing(): static + { + return new GeneratorConfig( + config: $this->config, + routeResolver: $this->routeResolver, + parametersExtractors: clone $this->parametersExtractors, + operationTransformers: clone $this->operationTransformers, + documentTransformers: clone $this->documentTransformers, + ruleTransformers: clone $this->ruleTransformers, + serverVariables: clone $this->serverVariables, + operationMethodsResolver: $this->operationMethodsResolver, + jsonApi: clone $this->jsonApi, + ); + } + + public function withParametersExtractors(callable $callback): static + { + $callback($this->parametersExtractors); + + return $this; + } + + public function withOperationTransformers(array|string|callable $cb): static + { + if ($this->isOperationTransformerMapper($cb)) { + $cb($this->operationTransformers); + + return $this; + } + + $this->operationTransformers->append($cb); + + return $this; + } + + /** + * @param list>|class-string|(callable(RuleTransformers): void) $cb + */ + public function withRuleTransformers(array|string|callable $cb): self + { + if (is_callable($cb)) { + $cb($this->ruleTransformers); + + return $this; + } + + $this->ruleTransformers->append($cb); + + return $this; + } + + private function isOperationTransformerMapper($cb): bool + { + if (! $cb instanceof Closure) { + return false; + } + + $reflection = new ReflectionFunction($cb); + + return count($reflection->getParameters()) === 1 + && $reflection->getParameters()[0]->getType() instanceof ReflectionNamedType + && is_a($reflection->getParameters()[0]->getType()->getName(), OperationTransformers::class, true); + } + + public function withDocumentTransformers(array|string|callable $cb): static + { + if ($this->isDocumentTransformerMapper($cb)) { + $cb($this->documentTransformers); + + return $this; + } + + $this->documentTransformers->append($cb); + + return $this; + } + + private function isDocumentTransformerMapper($cb): bool + { + if (! $cb instanceof Closure) { + return false; + } + + $reflection = new ReflectionFunction($cb); + + return count($reflection->getParameters()) === 1 + && $reflection->getParameters()[0]->getType() instanceof ReflectionNamedType + && is_a($reflection->getParameters()[0]->getType()->getName(), DocumentTransformers::class, true); + } + + /** + * @param (callable(ServerVariables): void)|array $variables + */ + public function withServerVariables(callable|array $variables) + { + if (is_callable($variables)) { + $variables($this->serverVariables); + + return $this; + } + + $this->serverVariables->use($variables); + + return $this; + } + + /** + * Force Scramble to use `PATCH` for `PUT|PATCH` routes. + */ + public function preferPatchMethod(): static + { + return $this->resolveOperationMethodsUsing(function (Route $route): string { + $methods = array_map('strtolower', $route->methods()); + + return in_array('patch', $methods) && in_array('put', $methods) ? 'patch' : $methods[0]; + }); + } + + /** + * @param Closure(Route): (string|string[]) $resolver + */ + public function resolveOperationMethodsUsing(Closure $resolver): static + { + $this->operationMethodsResolver = $resolver; + + return $this; + } + + public function jsonApi( + JsonApiArraySerialization $arraySerialization = JsonApiArraySerialization::Comma, + ?int $maxRelationshipDepth = null, + ): static { + $this->jsonApi = new JsonApiConfig( + arraySerialization: $arraySerialization, + maxRelationshipDepth: $maxRelationshipDepth, + ); + + return $this; + } + + public function get(string $key, mixed $default = null) + { + return Arr::get($this->config, $key, $default); + } + + public function securityStrategy(): ?SecurityDocumentationStrategy + { + $value = $this->get('security_strategy'); + + if ($value === null) { + return null; + } + + [$class, $options] = is_string($value) + ? [$value, []] + : (is_array($value) && count($value) === 2 && is_string($value[0]) + ? [$value[0], $value[1]] + : [null, []]); + + if ($class === null) { + throw new InvalidArgumentException( + 'Invalid scramble.security_strategy config. Expected null, a class-string, or [class-string, options array].' + ); + } + + $strategy = app($class, $options); + + if (! $strategy instanceof SecurityDocumentationStrategy) { + throw new InvalidArgumentException( + "Security strategy [{$class}] must implement ".SecurityDocumentationStrategy::class.'.' + ); + } + + return $strategy; + } +} diff --git a/vendor/dedoc/scramble/src/Http/Middleware/RestrictedDocsAccess.php b/vendor/dedoc/scramble/src/Http/Middleware/RestrictedDocsAccess.php new file mode 100644 index 00000000..9aef88c8 --- /dev/null +++ b/vendor/dedoc/scramble/src/Http/Middleware/RestrictedDocsAccess.php @@ -0,0 +1,21 @@ +environment('local')) { + return $next($request); + } + + if (Gate::allows('viewApiDocs')) { + return $next($request); + } + + abort(403); + } +} diff --git a/vendor/dedoc/scramble/src/Infer.php b/vendor/dedoc/scramble/src/Infer.php new file mode 100644 index 00000000..1c10476a --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer.php @@ -0,0 +1,32 @@ +index->getClassDefinition($class)) { + $this->index->registerClassDefinition( + (new ClassAnalyzer($this->index))->analyze($class) + ); + } + + return $this->index->getClassDefinition($class); + } + + public function configure(): InferConfig + { + return $this->config; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Analyzer/ClassAnalyzer.php b/vendor/dedoc/scramble/src/Infer/Analyzer/ClassAnalyzer.php new file mode 100644 index 00000000..a6af7f98 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Analyzer/ClassAnalyzer.php @@ -0,0 +1,109 @@ +getParentClass() ?: null)?->name; + + $parentDefinition = $parentName ? $this->index->getClass($parentName) : null; + + $classDefinition = new ClassDefinition( + name: $name, + templateTypes: $parentDefinition?->templateTypes ?: [], + properties: array_map(fn ($pd) => clone $pd, $parentDefinition?->properties ?: []), + methods: array_map(fn ($md) => $md->copyFromParent(), $parentDefinition?->methods ?: []), + parentFqn: $parentName, + ); + + /* + * Traits get analyzed by embracing default behavior of PHP reflection: reflection properties and + * reflection methods get copied into the class that uses the trait. + */ + + foreach ($classReflection->getProperties() as $reflectionProperty) { + if ($reflectionProperty->class !== $name) { + continue; + } + + if (array_key_exists($reflectionProperty->name, $classDefinition->properties)) { + $classDefinition->properties[$reflectionProperty->name]->defaultType = $reflectionProperty->hasDefaultValue() + ? PropertyAnalyzer::from($reflectionProperty)->getDefaultType() + : null; + + continue; + } + + if ($reflectionProperty->isStatic()) { + $classDefinition->properties[$reflectionProperty->name] = new ClassPropertyDefinition( + type: $reflectionProperty->hasDefaultValue() + ? (TypeHelper::createTypeFromValue($reflectionProperty->getDefaultValue()) ?: new UnknownType) + : new UnknownType, + isStatic: $reflectionProperty->isStatic(), + visibility: PropertyVisibility::fromReflectionProperty($reflectionProperty), + attributes: AttributeDefinition::fromReflectionAttributesArray($reflectionProperty->getAttributes()), + pendingDocComment: ($docComment = $reflectionProperty->getDocComment() ?: null) + ? new PendingDocComment($docComment, declaringClass: $name) + : null, + ); + } else { + $expectedTemplateTypeName = 'T'.Str::studly($reflectionProperty->name); + + $existingPropertyTemplateType = collect($classDefinition->templateTypes) + ->first(fn (TemplateType $t) => $t->name === $expectedTemplateTypeName); + + $propertyTemplateType = $existingPropertyTemplateType ?: new TemplateType( + $expectedTemplateTypeName, + is: ($reflectionPropertyType = $reflectionProperty->getType()) ? TypeHelper::createTypeFromReflectionType($reflectionPropertyType) : new UnknownType, + ); + + $classDefinition->properties[$reflectionProperty->name] = new ClassPropertyDefinition( + type: $propertyTemplateType, + defaultType: $reflectionProperty->hasDefaultValue() + ? PropertyAnalyzer::from($reflectionProperty)->getDefaultType() + : null, + visibility: PropertyVisibility::fromReflectionProperty($reflectionProperty), + attributes: AttributeDefinition::fromReflectionAttributesArray($reflectionProperty->getAttributes()), + pendingDocComment: ($docComment = $reflectionProperty->getDocComment() ?: null) + ? new PendingDocComment($docComment, declaringClass: $name) + : null, + ); + + if (! $existingPropertyTemplateType) { + $classDefinition->templateTypes[] = $propertyTemplateType; + } + } + } + + $classDefinition->setIndex($this->index); + + $this->index->registerClassDefinition($classDefinition); + + Context::getInstance()->extensionsBroker->afterClassDefinitionCreated(new ClassDefinitionCreatedEvent($classDefinition->name, $classDefinition)); + + return $classDefinition; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Analyzer/MethodAnalyzer.php b/vendor/dedoc/scramble/src/Infer/Analyzer/MethodAnalyzer.php new file mode 100644 index 00000000..f196ec77 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Analyzer/MethodAnalyzer.php @@ -0,0 +1,46 @@ +getClassReflector()->getMethod($methodDefinition->type->name)->getAstNode(); + } catch (\ReflectionException) { + return null; + } + + if (! $node) { + return $methodDefinition; + } + + return $this->classDefinition->methods[$methodDefinition->type->name] = (new FunctionLikeAstDefinitionBuilder( + $methodDefinition->type->name, + $node, + $this->index, + new FileNameResolver($this->getClassReflector()->getNameContext()), + $this->classDefinition, + $indexBuilders, + $withSideEffects + ))->build(); + } + + private function getClassReflector(): ClassReflector + { + return ClassReflector::make($this->classDefinition->name); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Analyzer/PropertyAnalyzer.php b/vendor/dedoc/scramble/src/Infer/Analyzer/PropertyAnalyzer.php new file mode 100644 index 00000000..69daa7b3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Analyzer/PropertyAnalyzer.php @@ -0,0 +1,74 @@ +reflectionProperty->hasDefaultValue()) { + return null; + } + + if ($astType = $this->getDefaultTypeFromAst()) { + return $astType; + } + + return TypeHelper::createTypeFromValue($this->reflectionProperty->getDefaultValue()); + } + + private function getDefaultTypeFromAst(): ?Type + { + $reflector = PropertyReflector::make($this->reflectionProperty->getDeclaringClass()->name, $this->reflectionProperty->name); + + $astNode = $reflector->getAstNode(); + if (! $astNode) { + return null; + } + + $propertyItem = collect($astNode->props)->first(fn (PropertyItem $p) => $p->name->name === $this->reflectionProperty->name); + if (! $propertyItem instanceof PropertyItem) { + return null; + } + + $index = app(Index::class); + $traverser = new NodeTraverser; + $nameResolver = new FileNameResolver($reflector->getClassReflector()->getNameContext()); + $traverser->addVisitor($inferer = new TypeInferer( + $index, + $nameResolver, + $scope = new Scope($index, new NodeTypesResolver, new ScopeContext(new ClassDefinition($this->reflectionProperty->getDeclaringClass()->name)), $nameResolver), + Context::getInstance()->extensionsBroker->extensions, + )); + $traverser->traverse(Arr::wrap($propertyItem)); + + if (! $propertyItem->default) { + return null; + } + + return $scope->getType($propertyItem->default); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/AutoResolvingArgumentTypeBag.php b/vendor/dedoc/scramble/src/Infer/AutoResolvingArgumentTypeBag.php new file mode 100644 index 00000000..35bacafd --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/AutoResolvingArgumentTypeBag.php @@ -0,0 +1,53 @@ + $arguments + */ + public function __construct(private Scope $scope, private array $arguments) {} + + public function get(string $name, int $position, ?Type $default = new UnknownType): ?Type + { + if ($argumentType = $this->arguments[$name] ?? $this->arguments[$position] ?? null) { + return ReferenceTypeResolver::getInstance()->resolve($this->scope, $argumentType); + } + + return $default; + } + + public function map(callable $cb): ArgumentTypeBag + { + return new self( + $this->scope, + collect($this->arguments)->map(fn ($t, $key) => $cb($t, $key))->all(), + ); + } + + public function all(): array + { + return array_map( + fn ($t) => ReferenceTypeResolver::getInstance()->resolve($this->scope, $t), + $this->arguments, + ); + } + + public function count(): int + { + return count($this->arguments); + } + + /** @return array */ + public function allUnresolved(): array + { + return $this->arguments; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Configuration/ClassLike.php b/vendor/dedoc/scramble/src/Infer/Configuration/ClassLike.php new file mode 100644 index 00000000..b5a85198 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Configuration/ClassLike.php @@ -0,0 +1,13 @@ +class; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Configuration/ClassLikeAndChildren.php b/vendor/dedoc/scramble/src/Infer/Configuration/ClassLikeAndChildren.php new file mode 100644 index 00000000..51ce92c3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Configuration/ClassLikeAndChildren.php @@ -0,0 +1,13 @@ +class, true); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Configuration/DefinitionMatcher.php b/vendor/dedoc/scramble/src/Infer/Configuration/DefinitionMatcher.php new file mode 100644 index 00000000..0b467504 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Configuration/DefinitionMatcher.php @@ -0,0 +1,8 @@ + */ + public function all(): array; + + /** + * @param callable(Type, string|int): Type $cb + */ + public function map(callable $cb): self; +} diff --git a/vendor/dedoc/scramble/src/Infer/Contracts/ClassDefinition.php b/vendor/dedoc/scramble/src/Infer/Contracts/ClassDefinition.php new file mode 100644 index 00000000..19d38562 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Contracts/ClassDefinition.php @@ -0,0 +1,13 @@ +getName(), + arguments: $reflectionAttribute->getArguments(), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Definition/ClassDefinition.php b/vendor/dedoc/scramble/src/Infer/Definition/ClassDefinition.php new file mode 100644 index 00000000..e5142591 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Definition/ClassDefinition.php @@ -0,0 +1,477 @@ + */ + protected array $loadedMethods = []; + + /** @var Collection> */ + private Collection $classContexts; + + private Index $index; + + public function __construct( + // FQ name + public string $name, + /** @var TemplateType[] $templateTypes */ + public array $templateTypes = [], + /** @var array $properties */ + public array $properties = [], + /** @var array $methods */ + public array $methods = [], + public ?string $parentFqn = null, + ) {} + + public function propagatesTemplates(?bool $propagatesTemplates = null): bool + { + if ($propagatesTemplates === null) { + return $this->propagatesTemplates; + } + + return $this->propagatesTemplates = $propagatesTemplates; + } + + public function isInstanceOf(string $className) + { + return is_a($this->name, $className, true); + } + + public function isChildOf(string $className) + { + return $this->isInstanceOf($className) && $this->name !== $className; + } + + public function hasMethodDefinition(string $name): bool + { + return $this->lazilyLoadMethodDefinition($name) || $this->findReflectionMethod($name); + } + + public function getMethodDefinitionWithoutAnalysis(string $name): ?FunctionLikeDefinition + { + return $this->lazilyLoadMethodDefinition($name); + } + + protected function lazilyLoadMethodDefinition(string $name): ?FunctionLikeDefinition + { + if (array_key_exists($name, $this->loadedMethods)) { + return $this->methods[$name] ?? null; + } + + $this->loadedMethods[$name] = true; + + /** @var \ReflectionMethod|null $reflectionMethod */ + $reflectionMethod = rescue( + fn () => (new \ReflectionClass($this->name))->getMethod($name), // @phpstan-ignore argument.type + report: false, + ); + + if (! $reflectionMethod) { + return $this->methods[$name] ?? null; + } + + if ($this->isMethodDefinedInNonAstAnalyzableTrait($name)) { + return $this->methods[$name] ?? null; + } + + if ( + $reflectionMethod->class === $this->name + || Scramble::infer()->config->shouldAnalyzeAst($reflectionMethod->class) + ) { + return $this->methods[$reflectionMethod->name] = new FunctionLikeDefinition( + new FunctionType( + $reflectionMethod->name, + arguments: [], + returnType: new UnknownType, + ), + definingClassName: $reflectionMethod->class, + isStatic: $reflectionMethod->isStatic(), + ); + } + + return $this->methods[$name] ?? null; + } + + public function getMethodDefiningClassName(string $name, Index $index) + { + $lastLookedUpClassName = $this->name; + while ($lastLookedUpClassDefinition = $index->getClass($lastLookedUpClassName)) { + if ($methodDefinition = $lastLookedUpClassDefinition->getMethodDefinitionWithoutAnalysis($name)) { + return $methodDefinition->definingClassName; + } + + if ($lastLookedUpClassDefinition->parentFqn) { + $lastLookedUpClassName = $lastLookedUpClassDefinition->parentFqn; + + continue; + } + + break; + } + + return $lastLookedUpClassName; + } + + /** + * @param IndexBuilder>[] $indexBuilders + */ + public function getMethodDefinition(string $name, Scope $scope = new GlobalScope, array $indexBuilders = [], bool $withSideEffects = false): ?FunctionLikeDefinition + { + if (! $methodDefinition = $this->lazilyLoadMethodDefinition($name)) { + return $this->getFunctionLikeDefinitionBuiltFromReflection($name); + } + + return $this->getFunctionLikeDefinitionBuiltFromAst($methodDefinition, $name, $scope, $indexBuilders, $withSideEffects); + } + + protected function isMethodDefinedInNonAstAnalyzableTrait(string $name): bool + { + if (! $reflectionMethod = $this->findReflectionMethod($name)) { + return false; + } + /** @var \ReflectionClass|null $classReflection */ + $classReflection = rescue(fn () => new \ReflectionClass($this->name), report: false); // @phpstan-ignore argument.type + + if (! $classReflection) { + return false; + } + + if ($this->isDeclaredIn($reflectionMethod, $classReflection)) { + return false; + } + + foreach (class_uses_recursive($classReflection->name) as $traitName) { + $traitReflection = rescue( + fn () => new \ReflectionClass($traitName), // @phpstan-ignore argument.type + report: false, + ); + + if (! $traitReflection) { + continue; + } + + if (! $this->isDeclaredIn($reflectionMethod, $traitReflection)) { + return false; + } + + return ! Scramble::infer()->config->shouldAnalyzeAst($traitName); + } + + return false; + } + + /** + * @param \ReflectionClass $class + */ + private function isDeclaredIn(\ReflectionMethod $reflectionMethod, \ReflectionClass $class): bool + { + $reflectionMethodFileName = $reflectionMethod->getFileName(); + $reflectionMethodStartLine = $reflectionMethod->getStartLine(); + + $classFileName = $class->getFileName(); + $classStartLine = $class->getStartLine(); + $classEndLine = $class->getEndLine(); + + if (! $reflectionMethodFileName || ! $classFileName) { + return false; + } + + if ($reflectionMethodFileName !== $classFileName) { + return false; + } + + return $reflectionMethodStartLine > $classStartLine && $reflectionMethodStartLine < $classEndLine; + } + + private function findReflectionMethod(string $name): ?\ReflectionMethod + { + /** @var \ReflectionClass|null $classReflection */ + $classReflection = rescue(fn () => new \ReflectionClass($this->name), report: false); // @phpstan-ignore argument.type + /** @var \ReflectionMethod|null $methodReflection */ + $methodReflection = rescue(fn () => $classReflection?->getMethod($name), report: false); + + // The case when method is defined in the class or its parents. + if ($methodReflection && $this->isDeclaredIn($methodReflection, $methodReflection->getDeclaringClass())) { + return $methodReflection; + } + + foreach ($this->getClassContexts()->keys() as $class) { + /** @var \ReflectionClass|null $classReflection */ + $classReflection = rescue(fn () => new \ReflectionClass($class), report: false); // @phpstan-ignore argument.type + /** @var \ReflectionMethod|null $traitMethodReflection */ + $traitMethodReflection = rescue(fn () => $classReflection?->getMethod($name), report: false); + + if ($traitMethodReflection) { + return $traitMethodReflection; + } + } + + return $methodReflection; + } + + protected function getFunctionLikeDefinitionBuiltFromReflection(string $name): ?FunctionLikeDefinition + { + if (array_key_exists($name, $this->methods)) { + return $this->methods[$name]; + } + + if (! $methodReflection = $this->findReflectionMethod($name)) { + return null; + } + + $definition = (new FunctionLikeReflectionDefinitionBuilder( + $name, + $methodReflection, + collect($this->templateTypes)->keyBy->name + ->merge($this->getMethodContextTemplates($methodReflection)), + ))->build(); + + return $this->methods[$name] = $definition; + } + + /** + * @param IndexBuilder>[] $indexBuilders + */ + protected function getFunctionLikeDefinitionBuiltFromAst( + FunctionLikeDefinition $methodDefinition, + string $name, + Scope $scope = new GlobalScope, + array $indexBuilders = [], + bool $withSideEffects = false, + ): ?FunctionLikeDefinition { + if (! $methodDefinition->isFullyAnalyzed()) { + $this->methods[$name] = (new MethodAnalyzer( + $scope->index, + $this, + ))->analyze($methodDefinition, $indexBuilders, $withSideEffects); + } + + if (! $this->methods[$name]) { // @phpstan-ignore booleanNot.alwaysFalse + return $this->methods[$name]; + } + + if (! $this->methods[$name]->referencesResolved) { + $methodScope = new Scope( + $scope->index, + new NodeTypesResolver, + new ScopeContext($this, $methodDefinition), + new FileNameResolver( + class_exists($this->name) + ? ClassReflector::make($this->name)->getNameContext() + : tap(new NameContext(new Throwing), fn (NameContext $nc) => $nc->startNamespace()), + ), + ); + + FunctionLikeAstDefinitionBuilder::resolveFunctionParameterDefaults($methodScope, $this->methods[$name]); + + FunctionLikeAstDefinitionBuilder::resolveFunctionReturnReferences($methodScope, $this->methods[$name]); + + FunctionLikeAstDefinitionBuilder::resolveFunctionExceptions($methodScope, $this->methods[$name]); + + $this->methods[$name]->referencesResolved = true; + } + + return $this->methods[$name]; + } + + /** + * @return Collection + */ + private function getMethodContextTemplates(\ReflectionMethod $methodReflection): Collection + { + $classContexts = $this->getClassContexts(); + + return $classContexts->get($methodReflection->class, collect()); + } + + /** + * @param string[] $ignoreClasses + * @return Collection> + */ + public function getClassContexts(array $ignoreClasses = []): Collection + { + if (isset($this->classContexts)) { + return $this->classContexts; + } + + if (in_array($this->name, $ignoreClasses, true)) { + return collect(); + } + + $reflector = ClassReflector::make($this->name); + + $docComment = rescue(fn () => $reflector->getReflection()->getDocComment(), report: false) ?: ''; + + $classSource = $docComment."\n".rescue($reflector->getSource(...), '', report: false); + + /** @var Collection $tagsDoc */ + $tagsDoc = Str::matchAll('/@(?:use|extends|mixin)\s+[^\r\n*]+/', $classSource); + $contextPhpDoc = $tagsDoc->map(fn ($s) => " * $s")->prepend('/**')->push('*/')->join("\n"); + + $nameContext = rescue($reflector->getNameContext(...), report: false); + + $phpDoc = PhpDoc::parse( + $contextPhpDoc, + $nameContext ? new FileNameResolver($nameContext) : null, + ); + + /** @var (ExtendsTagValueNode|UsesTagValueNode|MixinTagValueNode)[] $tags */ + $tags = [ + ...array_values($phpDoc->getExtendsTagValues()), + ...array_values($phpDoc->getUsesTagValues()), + ...array_values($phpDoc->getMixinTagValues()), + ]; + + $classTemplatesByName = collect($this->templateTypes)->keyBy->name; + + $types = collect($tags) + ->map(fn ($tag) => PhpDocTypeHelper::toType($tag->type)) + ->filter(fn ($type) => $type instanceof ObjectType); + + if ($this->parentFqn && ! $types->firstWhere('name', $this->parentFqn)) { + $types->push(new ObjectType($this->parentFqn)); + } + + $classContexts = collect(); + + foreach ($types as $type) { + $classContext = collect(); + $type = ! $type instanceof Generic ? new Generic($type->name) : $type; + + if ($definition = $this->getIndex()->getClass($type->name)) { + foreach ($definition->templateTypes as $i => $templateType) { + $concreteType = (new TypeWalker)->map( + $type->templateTypes[$i] + ?? $this->templateTypes[$i] + ?? $templateType->default + ?? new UnknownType, + fn ($t) => $t instanceof ObjectType ? $classTemplatesByName->get($t->name, $t) : $t, + ); + + $classContext->offsetSet($templateType->name, $concreteType); + } + } + + $classContexts->offsetSet($type->name, $classContext); + } + + foreach ($classContexts as $class => $localContext) { + if (in_array($class, $ignoreClasses, true)) { + continue; + } + + if (! $classDef = $this->getIndex()->getClass($class)) { + continue; + } + + $classContext = $classDef->getClassContexts([...$ignoreClasses, $this->name]); + + $localizedClassContext = $classContext->map->map(function ($type) use ($localContext) { + return (new TypeWalker)->map( + $type, + fn ($t) => $type instanceof TemplateType + ? $localContext->get($type->name, new UnknownType) + : $type, + ); + }); + + $classContexts = $localizedClassContext->merge($classContexts); + } + + // $this->dumpContext($this, $classContexts); + + return $this->classContexts = $classContexts; + } + + /** + * @param Collection> $classContexts + */ + private function dumpContext(ClassDefinition $def, Collection $classContexts): void // @phpstan-ignore method.unused + { + $className = $def->name.($def->templateTypes ? '<'.implode(',', array_map($this->dumpTemplateName(...), $def->templateTypes)).'>' : ''); + + $contextNames = $classContexts->map(function ($contexts, $name) { + $templates = $contexts->map(fn ($t) => $t instanceof TemplateType ? $this->dumpTemplateName($t) : $t->toString()); + + return $name.($templates->count() ? '<'.$templates->join(',').'>' : ''); + })->values()->toArray(); + + dump([ + $className => $contextNames, + ]); + } + + private function dumpTemplateName(TemplateType $tt): string + { + return $tt->name.'#'.spl_object_id($tt); + } + + public function setIndex(Index $index): void + { + $this->index = $index; + } + + protected function getIndex(): Index + { + return isset($this->index) ? $this->index : app(Index::class); + } + + public function getPropertyDefinition($name) + { + return $this->properties[$name] ?? null; + } + + public function hasPropertyDefinition(string $name): bool + { + return array_key_exists($name, $this->properties); + } + + public function getMethodCallType(string $name) + { + return $this->getMethodDefinition($name)?->getReturnType() + ?: new UnknownType("Cannot get type of calling method [$name] on object [$this->name]"); + } + + public function getMethod(string $name): ?FunctionLikeDefinition + { + return $this->getMethodDefinition($name); + } + + public function getData(): ClassDefinition + { + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Definition/ClassPropertyDefinition.php b/vendor/dedoc/scramble/src/Infer/Definition/ClassPropertyDefinition.php new file mode 100644 index 00000000..e5a0f5c7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Definition/ClassPropertyDefinition.php @@ -0,0 +1,51 @@ +pendingDocComment) { + return null; + } + + return $this->docNode ??= $this->pendingDocComment->resolve(); + } + + /** + * @return AttributeDefinition[] + */ + public function getAttributes(?string $name = null, int $flags = 0): array + { + if ($name === null) { + return $this->attributes; + } + + return array_values(array_filter( + $this->attributes, + function (AttributeDefinition $attribute) use ($name, $flags) { + if ($flags & AttributeDefinition::IS_INSTANCEOF) { + return is_a($attribute->name, $name, true); + } + + return $attribute->name === $name; + }, + )); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Definition/FunctionLikeAstDefinition.php b/vendor/dedoc/scramble/src/Infer/Definition/FunctionLikeAstDefinition.php new file mode 100644 index 00000000..4b345e87 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Definition/FunctionLikeAstDefinition.php @@ -0,0 +1,129 @@ +declarationDefinition = $declarationDefinition; + + return $this; + } + + public function getDeclarationDefinition(): ?FunctionLikeDefinition + { + return $this->declarationDefinition; + } + + public function setAstNode(FunctionLike $astNode): self + { + $this->astNode = $astNode; + + return $this; + } + + public function getAstNode(): FunctionLike + { + if (! $this->astNode) { + /** + * @see \Dedoc\Scramble\Infer\DefinitionBuilders\FunctionLikeAstDefinitionBuilder + */ + throw new \LogicException('AST node must be set before accessing it on FunctionLikeAstDefinition'); + } + + return $this->astNode; + } + + public function setScope(Scope $scope): self + { + $this->scope = $scope; + + return $this; + } + + public function getScope(): Scope + { + if (! $this->scope) { + /** + * @see \Dedoc\Scramble\Infer\DefinitionBuilders\FunctionLikeAstDefinitionBuilder + */ + throw new \LogicException('Scope must be set before accessing it on FunctionLikeAstDefinition'); + } + + return $this->scope; + } + + public function getFlowContainer(): Nodes + { + if ($this->flowContainer) { + return $this->flowContainer; + } + + $traverser = new NodeTraverser; + + $traverser->addVisitor($flowBuilder = new FlowBuilder( + $this->type->arguments, // parameters + $this->getScope(), + )); + + $traverser->traverse([$this->getAstNode()]); + + return $this->flowContainer = $flowBuilder->flowNodes; + } + + public function getInferredReturnType(): Type + { + return $this->type->getReturnType(); + } + + public function getReturnType(): Type + { + $inferredReturnType = $this->type->getReturnType(); + + if ($inferredReturnType->getAttribute('fromScrambleReturn') === true) { + return $inferredReturnType; + } + + if (! $returnDeclarationType = $this->getDeclarationDefinition()?->getReturnType()) { + return $inferredReturnType; + } + + return $this->prefersInferredReturnType($returnDeclarationType, $inferredReturnType) + ? $inferredReturnType + : $returnDeclarationType; + } + + private function prefersInferredReturnType(?Type $declarationType, Type $inferredType): bool + { + if (! $declarationType || $declarationType instanceof UnknownType) { + return true; + } + + if ($declarationType->accepts($inferredType)) { + return true; + } + + if ($inferredType->acceptedBy($declarationType)) { + return true; + } + + return false; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Definition/FunctionLikeDefinition.php b/vendor/dedoc/scramble/src/Infer/Definition/FunctionLikeDefinition.php new file mode 100644 index 00000000..657cf465 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Definition/FunctionLikeDefinition.php @@ -0,0 +1,61 @@ + $argumentsDefaults A map where the key is arg name and value is a default type. + */ + public function __construct( + public FunctionType $type, + public array $argumentsDefaults = [], + public ?string $definingClassName = null, + public bool $isStatic = false, + public ?SelfOutTypeBuilder $selfOutTypeBuilder = null, + ) {} + + public function isFullyAnalyzed(): bool + { + return $this->isFullyAnalyzed; + } + + public function addArgumentDefault(string $paramName, Type $type): self + { + $this->argumentsDefaults[$paramName] = $type; + + return $this; + } + + public function getSelfOutType(): ?Generic + { + return $this->selfOutType ??= $this->selfOutTypeBuilder?->build(); + } + + public function getReturnType(): Type + { + return $this->type->getReturnType(); + } + + /** + * When analyzing parent classes, function like definitions are "copied" from parent class. When function + * like is sourced from AST, we don't want to make a deep clone to save some memory (is the difference really makes sense?) + * as the definition will be re-build to the specifics of a given class. However, other types of fn definitions + * may override this method and do a deeper cloning (or cloning at all!). + */ + public function copyFromParent(): self + { + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Definition/LazyShallowClassDefinition.php b/vendor/dedoc/scramble/src/Infer/Definition/LazyShallowClassDefinition.php new file mode 100644 index 00000000..b73b8e74 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Definition/LazyShallowClassDefinition.php @@ -0,0 +1,38 @@ +getData(); + + if (isset($data->methods[$name])) { + return $data->methods[$name]; + } + + try { + $reflection = (new ReflectionClass($data->name))->getMethod($name); + } catch (ReflectionException) { + return null; + } + + return $data->methods[$name] = (new FunctionLikeReflectionDefinitionBuilder($name, $reflection))->build(); + } + + public function getData(): ClassDefinitionData + { + return $this->definition->getData(); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Definition/PendingDocComment.php b/vendor/dedoc/scramble/src/Infer/Definition/PendingDocComment.php new file mode 100644 index 00000000..e45d76ac --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Definition/PendingDocComment.php @@ -0,0 +1,51 @@ +resolveFileName(); + + return PhpDoc::parse( + $this->docComment, + $fileName ? FileNameResolver::createForFile($fileName) : null, + ); + } + + private function resolveFileName(): ?string + { + if ($this->fileName) { + return $this->fileName; + } + + if ($this->declaringClass) { + return $this->getDeclaringClassFile($this->declaringClass); + } + + return null; + } + + private function getDeclaringClassFile(string $declaringClass): ?string + { + try { + $reflectionClass = new \ReflectionClass($declaringClass); + + return $reflectionClass->getFileName() ?: null; + } catch (\Throwable) { + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Definition/PropertyVisibility.php b/vendor/dedoc/scramble/src/Infer/Definition/PropertyVisibility.php new file mode 100644 index 00000000..a6c61e08 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Definition/PropertyVisibility.php @@ -0,0 +1,21 @@ +isPrivate() => self::Private, + $property->isProtected() => self::Protected, + default => self::Public, + }; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Definition/ShallowClassDefinition.php b/vendor/dedoc/scramble/src/Infer/Definition/ShallowClassDefinition.php new file mode 100644 index 00000000..2981d1d3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Definition/ShallowClassDefinition.php @@ -0,0 +1,23 @@ +methods[$name] ?? null; + } + + public function getMethodDefinition(string $name, Scope $scope = new GlobalScope, array $indexBuilders = [], bool $withSideEffects = false): ?FunctionLikeDefinition + { + if ($this->isMethodDefinedInNonAstAnalyzableTrait($name)) { + return $this->getFunctionLikeDefinitionBuiltFromReflection($name); + } + + return $this->getMethodDefinitionWithoutAnalysis($name) ?: $this->getFunctionLikeDefinitionBuiltFromReflection($name); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeAstDefinitionBuilder.php b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeAstDefinitionBuilder.php new file mode 100644 index 00000000..3db5067e --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeAstDefinitionBuilder.php @@ -0,0 +1,376 @@ +>[] $indexBuilders + */ + public function __construct( + public string $name, + public FunctionLike $functionLike, + public Index $index, + public FileNameResolver $fileNameResolver, + public ?ClassDefinition $classDefinition = null, + public array $indexBuilders = [], + public bool $withSideEffects = false, + ) { + $this->shallowIndex = app(LazyShallowReflectionIndex::class); + } + + public function build(): FunctionLikeAstDefinition + { + $inferrer = $this->traverseAstNode($this->functionLike); + + $scope = $inferrer->getFunctionLikeScope($this->functionLike); + + if (! $scope) { + throw new LogicException('Scope must be available when building FunctionLikeAstDefinition'); + } + + $definition = $scope->context->functionDefinition; + + if (! $definition instanceof FunctionLikeAstDefinition) { + throw new LogicException('Definition must be an instance of FunctionLikeAstDefinition'); + } + + $definition + ->setAstNode($this->functionLike) + ->setScope($scope); + + if ($this->functionLike instanceof ClassMethod) { + $definition->selfOutTypeBuilder = new SelfOutTypeBuilder($scope, $this->functionLike); + } + + $definition->setDeclarationDefinition($this->buildDeclarationDefinition()); + + $this->overrideInferredReturnTypeWithManualAnnotation($definition); + + if ($this->withSideEffects) { + $this->analyzeSideEffects($definition, $inferrer); + } + + $definition->isFullyAnalyzed = true; + + return $definition; + } + + private function traverseAstNode(Node $node): TypeInferer + { + $traverser = new NodeTraverser; + + $traverser->addVisitor($inferrer = new TypeInferer( + $this->index, + $this->fileNameResolver, + new Scope($this->index, new NodeTypesResolver, new ScopeContext($this->classDefinition), $this->fileNameResolver), + Context::getInstance()->extensionsBroker->extensions, + [new IndexBuildingHandler($this->indexBuilders)], + )); + + $traverser->traverse(Arr::wrap($node)); + + return $inferrer; + } + + private function analyzeSideEffects(FunctionLikeDefinition $methodDefinition, TypeInferer $inferrer): void + { + $fnScope = $inferrer->getFunctionLikeScope($this->functionLike); + + if (! $fnScope) { + return; + } + + foreach ($fnScope->getMethodCalls() as $methodCall) { + match (true) { + $methodCall instanceof MethodCall || $methodCall instanceof NullsafeMethodCall => $this->analyzeMethodCall($methodDefinition, $fnScope, $methodCall), + $methodCall instanceof StaticCall => $this->analyzeStaticMethodCall($methodDefinition, $fnScope, $methodCall), + $methodCall instanceof FuncCall => $this->analyzeFuncCall($methodDefinition, $fnScope, $methodCall), + $methodCall instanceof New_ => null, + default => null, + }; + } + } + + private function analyzeMethodCall(FunctionLikeDefinition $methodDefinition, Scope $fnScope, MethodCall|NullsafeMethodCall $methodCall): void + { + // 1. ensure method call should be handled + /* + * Only explicit method calls are supported. So the following is supported: + * $this->foo() + * But when the expression is in place, we skip analysis: + * $this->{$var}() + */ + $this->applyExceptionsFromMethodCall($methodDefinition, $fnScope, $methodCall); + + if (! $methodCall->name instanceof Identifier) { + return; + } + + // 2. get called method definition and if not yet analyzed, analyze shallowly (PHPDoc, type hints) + + // get shallow method definition (get shallow callee type, get the shallow definition) + $calleeType = (new ShallowTypeResolver($this->shallowIndex))->resolve($fnScope, $fnScope->getType($methodCall->var)); + if ($calleeType instanceof TemplateType && $calleeType->is) { + $calleeType = $calleeType->is; + } + if (! $calleeType instanceof ObjectType) { + return; + } + + $definition = $this->shallowIndex->getClass($calleeType->name); + if (! $definition) { + return; + } + + $shallowMethodDefinition = $definition->getMethod($methodCall->name->name); + if (! $shallowMethodDefinition) { + return; + } + + $this->applySideEffectsFromCall(new SideEffectCallEvent( + definition: $methodDefinition, + calledDefinition: $shallowMethodDefinition, + node: $methodCall, + scope: $fnScope, + arguments: new UnresolvableArgumentTypeBag($fnScope->getArgsTypes($methodCall->args)), + )); + } + + private function applyExceptionsFromMethodCall(FunctionLikeDefinition $methodDefinition, Scope $fnScope, MethodCall|NullsafeMethodCall $methodCall): void + { + if (! $methodCall->name instanceof Identifier) { + return; + } + + $calleeType = $fnScope->getType($methodCall->var); + + if (! $calleeType instanceof ObjectType) { + return; + } + + $event = new MethodCallEvent( + $calleeType, + $methodCall->name->name, + $fnScope, + new UnresolvableArgumentTypeBag($fnScope->getArgsTypes($methodCall->args)), + $calleeType->name + ); + + $exceptions = app(ExtensionsBroker::class)->getMethodCallExceptions($event); + + if (empty($exceptions)) { + return; + } + + $methodDefinition->type->exceptions = array_merge( + $methodDefinition->type->exceptions, + $exceptions, + ); + } + + private function analyzeStaticMethodCall(FunctionLikeDefinition $methodDefinition, Scope $fnScope, StaticCall $methodCall): void + { + if (! $methodCall->name instanceof Identifier) { + return; + } + + if (! $methodCall->class instanceof Name) { + return; + } + + $class = ReferenceTypeResolver::resolveClassName($fnScope, $methodCall->class->name); + if (! $class) { + return; + } + + $definition = $this->shallowIndex->getClass($class); + if (! $definition) { + return; + } + + $shallowMethodDefinition = $definition->getMethod($methodCall->name->name); + if (! $shallowMethodDefinition) { + return; + } + + $this->applySideEffectsFromCall(new SideEffectCallEvent( + definition: $methodDefinition, + calledDefinition: $shallowMethodDefinition, + node: $methodCall, + scope: $fnScope, + arguments: new UnresolvableArgumentTypeBag($fnScope->getArgsTypes($methodCall->args)), + )); + } + + private function analyzeFuncCall(FunctionLikeDefinition $methodDefinition, Scope $fnScope, FuncCall $call): void + { + $name = $call->name->getAttribute('namespacedName', $call->name); + if (! $name instanceof Name) { + return; + } + + $functionDefinition = $this->shallowIndex->getFunction($name); + if (! $functionDefinition) { + return; + } + + $this->applySideEffectsFromCall(new SideEffectCallEvent( + definition: $methodDefinition, + calledDefinition: $functionDefinition, + node: $call, + scope: $fnScope, + arguments: new UnresolvableArgumentTypeBag($fnScope->getArgsTypes($call->args)), + )); + } + + private function applySideEffectsFromCall(SideEffectCallEvent $event): void + { + foreach ($event->calledDefinition->type->exceptions as $exception) { + $event->definition->type->exceptions[] = $exception; + } + + Context::getInstance()->extensionsBroker->afterSideEffectCallAnalyzed($event); + } + + private function overrideInferredReturnTypeWithManualAnnotation(FunctionLikeDefinition $definition): void + { + if (! $type = $this->getExplicitScrambleReturnType()) { + return; + } + + $definition->type->setReturnType($type); + } + + protected function buildDeclarationDefinition(): ?FunctionLikeDefinition + { + $definition = (new FunctionLikeDeclarationAstDefinitionBuilder( + $this->functionLike, + $this->classDefinition, + ))->build(); + + $phpDocNode = $this->functionLike->getAttribute('parsedPhpDoc'); + if (! $phpDocNode instanceof PhpDocNode) { + return $definition; + } + + return (new FunctionLikeDeclarationPhpDocDefinitionBuilder( + $definition, + $phpDocNode, + $this->classDefinition, + ))->build(); + } + + protected function getReflection(): ReflectionFunction|ReflectionMethod|null + { + try { + if ($this->classDefinition) { + return (new ReflectionClass($this->classDefinition->name))->getMethod($this->name); // @phpstan-ignore argument.type + } + + return new ReflectionFunction($this->name); + } catch (\ReflectionException) { + } + + return null; + } + + private function getExplicitScrambleReturnType(): ?Type + { + $phpDoc = $this->functionLike->getAttribute('parsedPhpDoc'); + if (! $phpDoc instanceof PhpDocNode) { + return null; + } + + if (! $scrambleReturn = Arr::first($phpDoc->getReturnTagValues('@scramble-return'))) { + return null; + } + + /** @var ReturnTagValueNode $scrambleReturn */ + $type = PhpDocTypeHelper::toType($scrambleReturn->type); + foreach (($this->classDefinition?->templateTypes ?: []) as $template) { + $type = (new TypeWalker)->map($type, fn ($t) => $t instanceof ObjectType && $t->name === $template->name ? $template : $t); + } + + $type->setAttribute('fromScrambleReturn', true); + + return $type; + } + + public static function resolveFunctionExceptions(Scope $scope, FunctionLikeDefinition $functionLikeDefinition): void + { + $functionType = $functionLikeDefinition->type; + + foreach ($functionType->exceptions as $i => $exceptionType) { + $exception = (new ReferenceTypeResolver($scope->index))->resolve($scope, $exceptionType); + if (! $exception instanceof ObjectType) { + continue; + } + $functionType->exceptions[$i] = $exception; + } + } + + public static function resolveFunctionReturnReferences(Scope $scope, FunctionLikeDefinition $functionLikeDefinition): void + { + $functionType = $functionLikeDefinition->type; + + $returnType = $functionType->getReturnType(); + $resolvedReference = ReferenceTypeResolver::getInstance()->resolve($scope, $returnType); + $functionType->setReturnType($resolvedReference); + } + + public static function resolveFunctionParameterDefaults(Scope $scope, FunctionLikeDefinition $functionLikeDefinition): void + { + $referenceTypeResolver = ReferenceTypeResolver::getInstance(); + + foreach ($functionLikeDefinition->argumentsDefaults as $name => $argumentDefault) { + $functionLikeDefinition->argumentsDefaults[$name] = $referenceTypeResolver->resolve($scope, $argumentDefault); + } + } +} diff --git a/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationAstDefinitionBuilder.php b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationAstDefinitionBuilder.php new file mode 100644 index 00000000..457105f4 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationAstDefinitionBuilder.php @@ -0,0 +1,100 @@ +buildType(), + argumentsDefaults: $this->getArgumentDefaults(), + definingClassName: $this->classDefinition?->name, + isStatic: $this->node instanceof Node\Stmt\ClassMethod ? $this->node->isStatic() : false, + ); + } + + private function buildType(): FunctionType + { + return new FunctionType( + $this->getName(), + arguments: $this->getArgumentTypes(), + returnType: $this->getReturnType(), + ); + } + + private function getReturnType(): Type + { + if (! $returnType = $this->node->getReturnType()) { + return new UnknownType; + } + + return TypeHelper::createTypeFromTypeNode($returnType); + } + + /** @return array */ + private function getArgumentTypes(): array + { + return collect($this->node->getParams()) + ->mapWithKeys(function (Node\Param $param) { + if (! $param->var instanceof Node\Expr\Variable || ! is_string($param->var->name)) { + return []; + } + + $type = $param->type + ? TypeHelper::createTypeFromTypeNode($param->type) + : new MixedType; + + return [$param->var->name => $type]; + }) + ->all(); + } + + /** @return array */ + private function getArgumentDefaults(): array + { + return collect($this->node->getParams()) + ->mapWithKeys(function (Node\Param $param) { + if (! $param->default) { + return []; + } + + if (! $param->var instanceof Node\Expr\Variable || ! is_string($param->var->name)) { + return []; + } + + return [ + $param->var->name => (new GlobalScope)->getType($param->default), + ]; + }) + ->filter() + ->all(); + } + + private function getName(): string + { + if ($this->node instanceof ClassMethod || $this->node instanceof Node\Stmt\Function_) { + return $this->node->name->name; + } + + return '{anonymous}'; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationPhpDocDefinitionBuilder.php b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationPhpDocDefinitionBuilder.php new file mode 100644 index 00000000..3ff2439a --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeDeclarationPhpDocDefinitionBuilder.php @@ -0,0 +1,95 @@ +phpDocNode = $phpDocNode ?: new PhpDocNode([]); + } + + public function build(): FunctionLikeDefinition + { + $definition = clone $this->definition; + $definition->type = $definition->type->clone(); + + $phpDoc = $this->phpDocNode; + + foreach ($phpDoc->getTemplateTagValues() as $templateTagValue) { + $definition->type->templates[] = new TemplateType( + name: $templateTagValue->name, + ); + } + + foreach ($phpDoc->getParamTagValues() as $paramTagValue) { + $name = Str::replaceFirst('$ ', '', $paramTagValue->parameterName); + if (! array_key_exists($name, $definition->type->arguments)) { + continue; + } + $definition->type->arguments[$name] = $this->handleStatic( + PhpDocTypeHelper::toType($paramTagValue->type), + $definition->type->templates, + ); + } + + foreach ($phpDoc->getThrowsTagValues() as $throwsTagValue) { + $definition->type->exceptions[] = $this->handleStatic( + PhpDocTypeHelper::toType($throwsTagValue->type), + $definition->type->templates, + ); + } + + if ($returnTagValues = array_values($phpDoc->getReturnTagValues())) { + $definition->type->returnType = $this->handleStatic( + PhpDocTypeHelper::toType($returnTagValues[0]->type), + $definition->type->templates, + ); + } + + return $definition; + } + + /** + * @param TemplateType[] $functionTemplates + */ + private function handleStatic(Type $type, array $functionTemplates): Type + { + $classTemplates = collect($this->classDefinition?->templateTypes ?: [])->keyBy->name; + $functionTemplatesByKeys = collect($functionTemplates)->keyBy->name; + + return (new TypeWalker)->map($type, function (Type $t) use ($functionTemplatesByKeys, $classTemplates) { + if (! $t instanceof ObjectType) { + return $t; + } + + $t->name = ltrim($t->name, '\\'); + + if ($definedTemplate = $classTemplates->get($t->name)) { + return $definedTemplate; + } + + if ($definedFnTemplate = $functionTemplatesByKeys->get($t->name)) { + return $definedFnTemplate; + } + + return $t; + }); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeReflectionDefinitionBuilder.php b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeReflectionDefinitionBuilder.php new file mode 100644 index 00000000..fc410434 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/FunctionLikeReflectionDefinitionBuilder.php @@ -0,0 +1,170 @@ + */ + private Collection $classTemplates; + + /** + * @param Collection|null $classTemplates + */ + public function __construct( + public string $name, + ReflectionFunction|ReflectionMethod|null $reflection = null, + ?Collection $classTemplates = null, + ) { + $this->reflection = $reflection ?: new ReflectionFunction($this->name); + $this->classTemplates = $classTemplates ?: collect(); + } + + public function build(): FunctionLikeDefinition + { + $functionDefinition = $this->buildFunctionDefinitionFromTypeHints(); + + $this->applyPhpDoc( + $functionDefinition, + PhpDoc::parse($this->reflection->getDocComment() ?: '/** */', FileNameResolver::createForFile($this->reflection->getFileName())), + ); + + $functionDefinition->isFullyAnalyzed = true; + + return $functionDefinition; + } + + private function buildFunctionDefinitionFromTypeHints(): FunctionLikeDefinition + { + $parameters = collect($this->reflection->getParameters()) + ->mapWithKeys(fn (ReflectionParameter $p) => [ + $p->name => ($paramType = $p->getType()) + ? TypeHelper::createTypeFromReflectionType($paramType) + : new MixedType, + ]) + ->all(); + + $argumentDefaults = collect($this->reflection->getParameters()) + ->mapWithKeys(fn (ReflectionParameter $p) => [ + $p->name => rescue(function () use ($p) { + return TypeHelper::createTypeFromValue($p->getDefaultValue()); + }, report: false), + ]) + ->filter() + ->all(); + + $returnType = ($retType = $this->reflection->getReturnType()) + ? TypeHelper::createTypeFromReflectionType($retType) + : new UnknownType; + + $type = new FunctionType($this->name, $parameters, $returnType); + + return new FunctionLikeDefinition( + $type, + argumentsDefaults: $argumentDefaults, + definingClassName: $this->reflection instanceof ReflectionMethod ? $this->reflection->class : null, + ); + } + + private function applyPhpDoc(FunctionLikeDefinition $definition, PhpDocNode $phpDoc): void + { + foreach ($phpDoc->getTemplateTagValues() as $templateTagValue) { + $definition->type->templates[] = new TemplateType( + name: $templateTagValue->name, + ); + } + + foreach ($phpDoc->getParamTagValues() as $paramTagValue) { + $name = Str::replaceFirst('$', '', $paramTagValue->parameterName); + if (! array_key_exists($name, $definition->type->arguments)) { + continue; + } + $definition->type->arguments[$name] = $this->handleStatic( + PhpDocTypeHelper::toType($paramTagValue->type), + $definition->type->templates, + ); + } + + foreach ($phpDoc->getThrowsTagValues() as $throwsTagValue) { + $definition->type->exceptions[] = $this->handleStatic( + PhpDocTypeHelper::toType($throwsTagValue->type), + $definition->type->templates, + ); + } + + if ($returnTagValues = array_values($phpDoc->getReturnTagValues())) { + $definition->type->returnType = $this->handleStatic( + PhpDocTypeHelper::toType($returnTagValues[0]->type), + $definition->type->templates, + ); + } + + if (method_exists($phpDoc, 'getSelfOutTypeTagValues')) { // @phpstan-ignore function.alreadyNarrowedType + foreach ($phpDoc->getSelfOutTypeTagValues() as $selfOutTypeTagValue) { + $selfOutType = $this->handleStatic( + PhpDocTypeHelper::toType($selfOutTypeTagValue->type), + $definition->type->templates, + ); + + if (! $selfOutType instanceof Generic) { + continue; + } + + $definition->selfOutType = $this->handleSelfOutType($selfOutType); + } + } + } + + /** + * @param TemplateType[] $functionTemplates + */ + private function handleStatic(Type $type, array $functionTemplates): Type + { + $functionTemplatesByKeys = collect($functionTemplates)->keyBy->name; + + return (new TypeWalker)->map($type, function (Type $t) use ($functionTemplatesByKeys) { + if (! $t instanceof ObjectType) { + return $t; + } + + $t->name = ltrim($t->name, '\\'); + + if ($definedTemplate = $this->classTemplates->get($t->name)) { + return $definedTemplate; + } + + if ($definedFnTemplate = $functionTemplatesByKeys->get($t->name)) { + return $definedFnTemplate; + } + + return $t; + }); + } + + private function handleSelfOutType(Generic $type): Generic + { + return new Generic('self', $type->templateTypes); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/LazyClassReflectionDefinitionBuilder.php b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/LazyClassReflectionDefinitionBuilder.php new file mode 100644 index 00000000..d95bd3ce --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/LazyClassReflectionDefinitionBuilder.php @@ -0,0 +1,83 @@ +reflection->getParentClass() ?: null)?->name) + ? ($this->index->getClass($parentName)?->getData() ?? new ClassDefinition(name: '')) + : new ClassDefinition(name: ''); + + $classDefinitionData = new ClassDefinition( + name: $this->reflection->name, + templateTypes: $parentDefinition->templateTypes, + properties: array_map(fn ($pd) => clone $pd, $parentDefinition->properties ?: []), + methods: $parentDefinition->methods ?: [], + parentFqn: $parentName ?? null, + ); + + /* + * Traits get analyzed by embracing default behavior of PHP reflection: reflection properties and + * reflection methods get copied into the class that uses the trait. + */ + + foreach ($this->reflection->getProperties() as $reflectionProperty) { + if ($reflectionProperty->class !== $this->reflection->name) { + continue; + } + + if ($reflectionProperty->isStatic()) { + $classDefinitionData->properties[$reflectionProperty->name] = new ClassPropertyDefinition( + type: $reflectionProperty->hasDefaultValue() + ? (TypeHelper::createTypeFromValue($reflectionProperty->getDefaultValue()) ?: new UnknownType) + : new UnknownType, + isStatic: $reflectionProperty->isStatic(), + visibility: PropertyVisibility::fromReflectionProperty($reflectionProperty), + attributes: AttributeDefinition::fromReflectionAttributesArray($reflectionProperty->getAttributes()), + pendingDocComment: ($docComment = $reflectionProperty->getDocComment() ?: null) + ? new PendingDocComment($docComment, declaringClass: $this->reflection->name) + : null, + ); + } else { + $classDefinitionData->properties[$reflectionProperty->name] = new ClassPropertyDefinition( + type: $t = new TemplateType( + 'T'.Str::studly($reflectionProperty->name), + is: $reflectionProperty->hasType() ? TypeHelper::createTypeFromReflectionType($reflectionProperty->getType()) : new UnknownType, + ), + defaultType: $reflectionProperty->hasDefaultValue() + ? TypeHelper::createTypeFromValue($reflectionProperty->getDefaultValue()) + : null, + visibility: PropertyVisibility::fromReflectionProperty($reflectionProperty), + attributes: AttributeDefinition::fromReflectionAttributesArray($reflectionProperty->getAttributes()), + pendingDocComment: ($docComment = $reflectionProperty->getDocComment() ?: null) + ? new PendingDocComment($docComment, declaringClass: $this->reflection->name) + : null, + ); + $classDefinitionData->templateTypes[] = $t; + } + } + + return new LazyShallowClassDefinition($classDefinitionData); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/SelfOutTypeBuilder.php b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/SelfOutTypeBuilder.php new file mode 100644 index 00000000..cf5a74f0 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/SelfOutTypeBuilder.php @@ -0,0 +1,252 @@ +scope->context->classDefinition) { + return null; + } + + if (! $functionDefinition = $this->scope->context->functionDefinition) { + return null; + } + + $expectedTemplatesMap = collect($classDefinition->templateTypes) + ->mapWithKeys(fn (TemplateType $t) => [$t->name => null]) + ->all(); + + $templateDefiningStatements = (new NodeFinder)->find( + $this->node->stmts ?: [], + fn ($n) => $this->isThisPropertyAssignment($n) // Direct assignments of something on `$this`, like `$this->foo = 42`. + || ($functionDefinition->type->name === '__construct' && $this->isParentConstructCall($n)) // Calls to `parent::__construct` if is in constructor + || ($this->isPotentialSetterCall($n) && $this->isSelfTypeOrCallOnSelfType($this->scope->getType($n->var)))// just any method call on $this (self type!) + ); + + foreach (array_reverse($templateDefiningStatements) as $statement) { + if ($this->isThisPropertyAssignment($statement)) { + $thisPropertiesAssignment = $statement; + + $propertyName = $thisPropertiesAssignment->var->name->name; // @phpstan-ignore property.notFound + + if (! array_key_exists($propertyName, $classDefinition->properties)) { + continue; + } + + // if property name is not template type - skip + $propertyType = $classDefinition->properties[$propertyName]->type; + if (! $propertyType instanceof TemplateType || ! array_key_exists($propertyType->name, $expectedTemplatesMap)) { + continue; + } + + // if property's template type is defined - skip + if ($expectedTemplatesMap[$propertyType->name] !== null) { + continue; + } + + // if property template type equals to assigned expression template type - skip + $assignedType = $this->scope->getType($thisPropertiesAssignment->expr); + if ($propertyType === $assignedType) { + continue; + } + + // define template + $expectedTemplatesMap[$propertyType->name] = $assignedType; + + continue; + } + + if ($this->isParentConstructCall($statement)) { + $parentConstructCall = $statement; + + if (! $classDefinition->parentFqn) { + continue; + } + + if (! $parentDefinition = $this->scope->index->getClass($classDefinition->parentFqn)) { + continue; + } + + $parentConstructor = $parentDefinition->getMethodDefinition('__construct'); + if (! $constructorSelfOutType = $parentConstructor?->getSelfOutType()) { + continue; + } + + $parentCallContextTemplates = (new TemplateTypesSolver)->getClassConstructorContextTemplates( + $parentDefinition, + $parentDefinition->getMethodDefinition('__construct'), + $arguments = new UnresolvableArgumentTypeBag($this->scope->getArgsTypes($parentConstructCall->args)), + ); + + foreach ($constructorSelfOutType->templateTypes as $index => $genericSelfOutTypePart) { + if (! $definedParentTemplateType = ($parentDefinition->templateTypes[$index] ?? null)) { + continue; + } + + // if property's template type is defined - skip + if (($expectedTemplatesMap[$definedParentTemplateType->name] ?? null) !== null) { + continue; + } + + $concreteSelfOutTypePart = $genericSelfOutTypePart instanceof TemplatePlaceholderType && $parentCallContextTemplates->has($definedParentTemplateType->name) + ? $parentCallContextTemplates->get($definedParentTemplateType->name) + : (new TypeWalker)->map( + $genericSelfOutTypePart, + fn ($t) => $t instanceof TemplateType ? $parentCallContextTemplates->get($t->name, $t) : $t, + ); + + $expectedTemplatesMap[$definedParentTemplateType->name] = $concreteSelfOutTypePart; + } + + continue; + } + + // Potential setter calls analysis requires reference resolution! + if ($this->isPotentialSetterCall($statement)) { + $potentialSetterCall = $statement; + + /* + * When getting statements we made sure that `var` is either `$this`, or a call to + * a method on `$this` so resolving potential setter calls should not trigger entire codebase + * analysis (which would be slow). + */ + $var = ReferenceTypeResolver::getInstance()->resolve( + $this->scope, + $this->scope->getType($potentialSetterCall->var)->clone(), + ); + + if (! $var instanceof SelfType) { + continue; + } + + if (! $methodDefinition = $classDefinition->getMethodDefinition($potentialSetterCall->name->name)) { // @phpstan-ignore property.notFound + continue; + } + + if (! $methodSelfOutType = $this->getMethodSelfOutType($methodDefinition)) { + continue; + } + + $methodCallContextTemplates = (new TemplateTypesSolver)->getFunctionContextTemplates( + $methodDefinition, + $arguments = new UnresolvableArgumentTypeBag($this->scope->getArgsTypes($potentialSetterCall->args)), + ); + + foreach ($methodSelfOutType->templateTypes as $index => $genericSelfOutTypePart) { + if ($genericSelfOutTypePart instanceof TemplatePlaceholderType) { + continue; + } + + if (! $definedTemplateType = ($classDefinition->templateTypes[$index] ?? null)) { + continue; + } + + // if property's template type is defined - skip + if (($expectedTemplatesMap[$definedTemplateType->name] ?? null) !== null) { + continue; + } + + $concreteSelfOutTypePart = (new TypeWalker)->map( + $genericSelfOutTypePart, + fn ($t) => $t instanceof TemplateType ? $methodCallContextTemplates->get($t->name, $t) : $t, + ); + + $expectedTemplatesMap[$definedTemplateType->name] = $concreteSelfOutTypePart; + } + + continue; + } + } + + return new Generic( + 'self', + array_values(array_map(fn ($type) => $type ?: new TemplatePlaceholderType, $expectedTemplatesMap)) + ); + } + + private function getMethodSelfOutType(FunctionLikeDefinition $methodDefinition): ?Generic + { + return RecursionGuard::run( + $methodDefinition, + fn () => $methodDefinition->getSelfOutType(), + fn () => null, + ); + } + + /** + * @phpstan-assert-if-true Assign $n + */ + private function isThisPropertyAssignment(Node $n): bool + { + return $n instanceof Assign + && $n->var instanceof PropertyFetch + && $n->var->var instanceof Variable + && $n->var->var->name === 'this' + && $n->var->name instanceof Identifier; + } + + /** + * @phpstan-assert-if-true StaticCall $n + */ + private function isParentConstructCall(Node $n): bool + { + return $n instanceof StaticCall + && $n->class instanceof Name + && $n->class->name === 'parent' + && $n->name instanceof Identifier + && $n->name->name === '__construct'; + } + + /** + * @phpstan-assert-if-true MethodCall $n + */ + private function isPotentialSetterCall(Node $n): bool + { + return $n instanceof MethodCall + && $n->name instanceof Identifier; + } + + private function isSelfTypeOrCallOnSelfType(Type $t): bool + { + if ($t instanceof SelfType) { + return true; + } + + if (! $t instanceof MethodCallReferenceType) { + return false; + } + + return $this->isSelfTypeOrCallOnSelfType($t->callee); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/ShallowClassReflectionDefinitionBuilder.php b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/ShallowClassReflectionDefinitionBuilder.php new file mode 100644 index 00000000..13a0221c --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/DefinitionBuilders/ShallowClassReflectionDefinitionBuilder.php @@ -0,0 +1,69 @@ + $reflection + */ + public function __construct( + private Index $index, + private ReflectionClass $reflection + ) {} + + public function build(): ClassDefinition + { + $parentName = ($this->reflection->getParentClass() ?: null)?->name; + + $parentDefinition = $parentName ? $this->index->getClass($parentName) : null; + + $classPhpDoc = (($comment = $this->reflection->getDocComment()) && ($path = $this->reflection->getFileName())) + ? PhpDoc::parse($comment, FileNameResolver::createForFile($path)) + : new PhpDocNode([]); + + $classTemplates = collect($classPhpDoc->getTemplateTagValues()) + ->merge($classPhpDoc->getTemplateTagValues('@template-covariant')) + ->values() + ->map(fn (TemplateTagValueNode $n) => new TemplateType( + name: $n->name, + is: $n->bound ? PhpDocTypeHelper::toType($n->bound) : null, + )) + ->keyBy('name'); + + /* + * @todo consider more advanced cloning implementation. + * Currently just cloning property definition feels alright as only its `defaultType` may change. + */ + $classDefinition = new ShallowClassDefinition( + name: $this->reflection->name, + templateTypes: $parentDefinition?->propagatesTemplates() + ? array_merge($classTemplates->values()->all(), $parentDefinition->templateTypes ?? []) + : $classTemplates->values()->all(), + properties: array_map(fn ($pd) => clone $pd, $parentDefinition?->properties ?: []), + methods: $parentDefinition?->methods ?: [], + parentFqn: $parentName, + ); + $classDefinition->propagatesTemplates($parentDefinition?->propagatesTemplates() ?? false); + + $classDefinition->setIndex($this->index); + + Context::getInstance()->extensionsBroker->afterClassDefinitionCreated(new ClassDefinitionCreatedEvent($classDefinition->name, $classDefinition)); + + return $classDefinition; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/AfterClassDefinitionCreatedExtension.php b/vendor/dedoc/scramble/src/Infer/Extensions/AfterClassDefinitionCreatedExtension.php new file mode 100644 index 00000000..9810c46a --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/AfterClassDefinitionCreatedExtension.php @@ -0,0 +1,12 @@ +instance instanceof ObjectType + ? $this->scope->index->getClass($this->instance->name) + : null; + } + + public function getInstance(): Type + { + return $this->instance; + } + + public function getName(): string + { + return $this->name; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/Event/ClassDefinitionCreatedEvent.php b/vendor/dedoc/scramble/src/Infer/Extensions/Event/ClassDefinitionCreatedEvent.php new file mode 100644 index 00000000..04792be6 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/Event/ClassDefinitionCreatedEvent.php @@ -0,0 +1,13 @@ +arguments->get($name, $position, $default) ?: $default; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/Event/FunctionCallEvent.php b/vendor/dedoc/scramble/src/Infer/Extensions/Event/FunctionCallEvent.php new file mode 100644 index 00000000..3dc7f58b --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/Event/FunctionCallEvent.php @@ -0,0 +1,28 @@ +scope->index->getFunctionDefinition($this->name); + } + + public function getName() + { + return $this->name; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/Event/MethodCallEvent.php b/vendor/dedoc/scramble/src/Infer/Extensions/Event/MethodCallEvent.php new file mode 100644 index 00000000..d8fd8b61 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/Event/MethodCallEvent.php @@ -0,0 +1,36 @@ +scope->index->getClass($this->getInstance()->name); + } + + public function getInstance() + { + return $this->instance; + } + + public function getName() + { + return $this->name; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/Event/PropertyFetchEvent.php b/vendor/dedoc/scramble/src/Infer/Extensions/Event/PropertyFetchEvent.php new file mode 100644 index 00000000..94464ab9 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/Event/PropertyFetchEvent.php @@ -0,0 +1,30 @@ +scope->index->getClass($this->getInstance()->name); + } + + public function getInstance() + { + return $this->instance; + } + + public function getName() + { + return $this->name; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/Event/ReferenceResolutionEvent.php b/vendor/dedoc/scramble/src/Infer/Extensions/Event/ReferenceResolutionEvent.php new file mode 100644 index 00000000..0b578d88 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/Event/ReferenceResolutionEvent.php @@ -0,0 +1,12 @@ +scope->index->getClass($this->getCallee()); + } + + public function getCallee() + { + return $this->callee; + } + + public function getName() + { + return $this->name; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/ExpressionExceptionExtension.php b/vendor/dedoc/scramble/src/Infer/Extensions/ExpressionExceptionExtension.php new file mode 100644 index 00000000..a5e08761 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/ExpressionExceptionExtension.php @@ -0,0 +1,19 @@ + Possible extensions returned from an expression. + */ + public function getException(Expr $node, Scope $scope): array; +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/ExpressionTypeInferExtension.php b/vendor/dedoc/scramble/src/Infer/Extensions/ExpressionTypeInferExtension.php new file mode 100644 index 00000000..e1fcec1b --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/ExpressionTypeInferExtension.php @@ -0,0 +1,12 @@ +[] + */ + private array $priorities = []; + + public function __construct(public readonly array $extensions = []) + { + $this->buildExtensions(); + } + + /** + * @param class-string[] $priority + */ + public function priority(array $priority): self + { + $this->priorities = array_values(array_unique(array_merge($this->priorities, $priority))); + + $this->buildExtensions(); + + return $this; + } + + private function buildExtensions(): void + { + $extensions = $this->sortExtensionsInOrder($this->extensions, $this->priorities); + + $this->propertyTypeExtensions = array_filter($extensions, function ($e) { + return $e instanceof PropertyTypeExtension; + }); + + $this->methodReturnTypeExtensions = array_filter($extensions, function ($e) { + return $e instanceof MethodReturnTypeExtension; + }); + + $this->anyMethodReturnTypeExtensions = array_filter($extensions, function ($e) { + return $e instanceof AnyMethodReturnTypeExtension; + }); + + $this->methodCallExceptionsExtensions = array_filter($extensions, function ($e) { + return $e instanceof MethodCallExceptionsExtension; + }); + + $this->staticMethodReturnTypeExtensions = array_filter($extensions, function ($e) { + return $e instanceof StaticMethodReturnTypeExtension; + }); + + $this->functionReturnTypeExtensions = array_filter($extensions, function ($e) { + return $e instanceof FunctionReturnTypeExtension; + }); + + $this->afterClassDefinitionCreatedExtensions = array_filter($extensions, function ($e) { + return $e instanceof AfterClassDefinitionCreatedExtension; + }); + + $this->afterSideEffectCallAnalyzedExtensions = array_filter($extensions, function ($e) { + return $e instanceof AfterSideEffectCallAnalyzed; + }); + + $this->typeResolverExtensions = array_filter($extensions, function ($e) { + return $e instanceof TypeResolverExtension; + }); + } + + /** + * @param InferExtension[] $arrayToSort + * @param class-string[] $arrayToSortWithItems + * @return InferExtension[] + */ + private function sortExtensionsInOrder(array $arrayToSort, array $arrayToSortWithItems): array + { + // 1) Figure out which items match any of the given “order” patterns + $isMatched = []; // parallel boolean array + $matchedItems = []; // will collect items for sorting + foreach ($arrayToSort as $item) { + $found = false; + foreach ($arrayToSortWithItems as $pattern) { + if ($item::class === $pattern) { + $found = true; + break; + } + } + $isMatched[] = $found; + if ($found) { + $matchedItems[] = $item; + } + } + + // 2) Sort the matched-items list by the order of patterns + usort($matchedItems, function ($a, $b) use ($arrayToSortWithItems) { + $rank = array_flip($arrayToSortWithItems); + // Find the first pattern each item matches + $getRank = function ($item) use ($rank) { + foreach ($rank as $pattern => $idx) { + if ($item::class === $pattern) { + return $idx; + } + } + + return PHP_INT_MAX; // fallback (should not happen) + }; + + return $getRank($a) <=> $getRank($b); + }); + + // 3) Rebuild the final array + $result = []; + $mIndex = 0; + foreach ($arrayToSort as $i => $item) { + if ($isMatched[$i]) { + // pull from the sorted‐matches list + $result[] = $matchedItems[$mIndex++]; + } else { + // untouched item + $result[] = $item; + } + } + + return $result; + } + + public function getPropertyType($event): ?Type + { + foreach ($this->propertyTypeExtensions as $extension) { + if (! $extension->shouldHandle($event->getInstance())) { + continue; + } + + if ($propertyType = $extension->getPropertyType($event)) { + return $propertyType; + } + } + + return null; + } + + public function getMethodReturnType($event): ?Type + { + foreach ($this->methodReturnTypeExtensions as $extension) { + if (! $extension->shouldHandle($event->getInstance())) { + continue; + } + + if ($returnType = $extension->getMethodReturnType($event)) { + return $returnType; + } + } + + return null; + } + + /** + * @return ObjectType[] + */ + public function getMethodCallExceptions($event): array + { + $exceptions = []; + + foreach ($this->methodCallExceptionsExtensions as $extension) { + if (! $extension->shouldHandle($event->getInstance())) { + continue; + } + + if ($extensionExceptions = $extension->getMethodCallExceptions($event)) { + $exceptions = array_merge($exceptions, $extensionExceptions); + } + } + + return $exceptions; + } + + public function getStaticMethodReturnType($event): ?Type + { + foreach ($this->staticMethodReturnTypeExtensions as $extension) { + if (! $extension->shouldHandle($event->getCallee())) { + continue; + } + + if ($returnType = $extension->getStaticMethodReturnType($event)) { + return $returnType; + } + } + + return null; + } + + public function getFunctionReturnType($event): ?Type + { + foreach ($this->functionReturnTypeExtensions as $extension) { + if (! $extension->shouldHandle($event->getName())) { + continue; + } + + if ($returnType = $extension->getFunctionReturnType($event)) { + return $returnType; + } + } + + return null; + } + + public function getResolvedType(ReferenceResolutionEvent $event): ?Type + { + if ($event->type instanceof ObjectType && is_a($event->type->name, ResolvingType::class, true)) { + if ($type = app($event->type->name)->resolve($event)) { + return $type; + } + } + + foreach ($this->typeResolverExtensions as $extension) { + if ($type = $extension->resolve($event)) { + return $type; + } + } + + return $event->type; + } + + public function afterClassDefinitionCreated($event): void + { + foreach ($this->afterClassDefinitionCreatedExtensions as $extension) { + if (! $extension->shouldHandle($event->name)) { + continue; + } + + $extension->afterClassDefinitionCreated($event); + } + } + + public function afterSideEffectCallAnalyzed(SideEffectCallEvent $event): void + { + foreach ($this->afterSideEffectCallAnalyzedExtensions as $extension) { + if (! $extension->shouldHandle($event)) { + continue; + } + + $extension->afterSideEffectCallAnalyzed($event); + } + } + + public function getAnyMethodReturnType(AnyMethodCallEvent $event): ?Type + { + foreach ($this->anyMethodReturnTypeExtensions as $extension) { + if ($returnType = $extension->getMethodReturnType($event)) { + return $returnType; + } + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/FunctionReturnTypeExtension.php b/vendor/dedoc/scramble/src/Infer/Extensions/FunctionReturnTypeExtension.php new file mode 100644 index 00000000..c93a0628 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/FunctionReturnTypeExtension.php @@ -0,0 +1,13 @@ + + * + * @param class-string> $builderClassName + * @return Bag + */ + public function getIndex(string $builderClassName): Bag + { + foreach ($this->indexBuilders as $indexBuilder) { + if (is_a($indexBuilder, $builderClassName)) { + return $indexBuilder->bag; // @phpstan-ignore-line + } + } + + return new Bag; // @phpstan-ignore-line + } + + public function handleEvent($event) + { + foreach ($this->indexBuilders as $indexBuilder) { + if ($indexBuilder instanceof IndexBuilder) { + $indexBuilder->handleEvent($event); + } + } + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/InferExtension.php b/vendor/dedoc/scramble/src/Infer/Extensions/InferExtension.php new file mode 100644 index 00000000..a2812bd9 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/InferExtension.php @@ -0,0 +1,5 @@ + + */ + public function getMethodCallExceptions(MethodCallEvent $event): array; +} diff --git a/vendor/dedoc/scramble/src/Infer/Extensions/MethodReturnTypeExtension.php b/vendor/dedoc/scramble/src/Infer/Extensions/MethodReturnTypeExtension.php new file mode 100644 index 00000000..33979bae --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Extensions/MethodReturnTypeExtension.php @@ -0,0 +1,14 @@ +parserNode instanceof \PhpParser\Node\Stmt\Expression + && $this->parserNode->expr instanceof \PhpParser\Node\Expr\Assign + && $this->parserNode->expr->var instanceof \PhpParser\Node\Expr\Variable + && $this->parserNode->expr->var->name === $varName; + } + + public function toDotId(Nodes $nodes): string + { + $index = array_search($this, $nodes->nodes, true); + + // @phpstan-ignore match.unhandled + return match ($this::class) { + StartNode::class => 'S', + TerminateNode::class => match ($this->kind) { + TerminationKind::RETURN => 'Ret', + TerminationKind::THROW => 'Throw', + }, + StatementNode::class => 'Stmt', + MergeNode::class => 'M', + ConditionNode::class => 'If', + }.'_'.$index; + } + + public function toDot(Nodes $nodes): string + { + $dot = $this->toDotId($nodes); + + $phpParserExpressionPrinter = app(PrettyPrinter::class); + + $empty = new \stdClass; + + $parserNode = $this->getParserNode(); + + $label = match ($this::class) { + TerminateNode::class => match ($this->kind) { + TerminationKind::RETURN => 'Return', + TerminationKind::THROW => 'Throw', + }.($parserNode ? ' '.$phpParserExpressionPrinter->prettyPrint([$parserNode]) : ' VOID'), + ConditionNode::class => 'If', + StatementNode::class => $parserNode ? $phpParserExpressionPrinter->prettyPrint([$parserNode]) : '', + default => $empty, + }; + + if ($label !== $empty && is_string($label)) { + $dot .= '[label="'.Str::replace('"', '\"', $label).'"]'; + } + + return $dot; + } + + public function getParserNode(): ?PhpParserNode + { + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Flow/ConditionNode.php b/vendor/dedoc/scramble/src/Infer/Flow/ConditionNode.php new file mode 100644 index 00000000..5228ecab --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Flow/ConditionNode.php @@ -0,0 +1,5 @@ +conditions) { + return null; + } + + if ($this->isNegated) { // not yet + return null; + } + + $condition = $this->conditions[0] ?? null; + if (! $condition) { + return null; + } + + if ($condition instanceof Identical) { + return $this->getRefinedVariableTypeFromIdentical($condition, $nodes, $varName); + } + + return null; + } + + private function getRefinedVariableTypeFromIdentical(Identical $condition, Nodes $nodes, string $varName): ?Type + { + [$expressionBeingNarrowed, $expr] = $this->normalizeEqualityExpression($condition); + if (! $expressionBeingNarrowed || ! $expr) { + return null; + } + + if ($expressionBeingNarrowed instanceof Expr\Variable) { + return $this->narrowByVariableEquality($expressionBeingNarrowed, $expr, $nodes, $varName); + } + + if ($expressionBeingNarrowed instanceof Expr\ArrayDimFetch) { // @phpstan-ignore instanceof.alwaysTrue + return $this->narrowByArrayDimFetchEquality($expressionBeingNarrowed, $expr, $nodes, $varName); + } + + return null; // @phpstan-ignore deadCode.unreachable + } + + /** + * @return array{0: Expr\Variable|Expr\ArrayDimFetch, 1: Expr}|array{0: null, 1: null} + */ + private function normalizeEqualityExpression(Identical $node): array + { + [$narrowable, $expr] = $this->isNarrowableExpression($node->left) + ? [$node->left, $node->right] + : [$node->right, $node->left]; + + if (! $this->isNarrowableExpression($narrowable)) { + return [null, null]; + } + + return [$narrowable, $expr]; + } + + /** + * @phpstan-assert-if-true Expr\Variable|Expr\ArrayDimFetch $expr + */ + private function isNarrowableExpression(Expr $expr): bool + { + if ($expr instanceof Expr\Variable) { + return true; + } + + // Array dim fetch is also narrowable ($foo['bar']['baz'] + if ($expr instanceof Expr\ArrayDimFetch) { + return $this->isNarrowableArrayDimFetchExpression($expr); + } + + return false; + } + + /** + * Array dim fetch is narrowable when dimensions that are fetched are string literals. This supports nested + * array dim fetches as well for cases like `$foo['bar']['baz']`. + */ + private function isNarrowableArrayDimFetchExpression(Expr\ArrayDimFetch $expr): bool + { + return ($expr->dim instanceof String_ || $expr->dim instanceof Int_) + && $this->isNarrowableExpression($expr->var); + } + + private function narrowByVariableEquality(Expr\Variable $varBeingNarrowed, Expr $expr, Nodes $nodes, string $varName): ?Type + { + if ($varBeingNarrowed->name !== $varName) { + return null; + } + + return $nodes->getTypeAt($expr, $this->from); + } + + private function narrowByArrayDimFetchEquality(Expr\ArrayDimFetch $narrowedExpr, Expr $expr, Nodes $nodes, string $varName): ?Type + { + [$var, $path] = $this->parseNarrowableArrayDimFetch($narrowedExpr); + + if ($var->name !== $varName) { + return null; + } + + $narrowedIncompleteArray = null; + + foreach (array_reverse($path) as $pathItem) { + $narrowedIncompleteArray = new KeyedArrayType([ + new ArrayItemType_( + key: $pathItem, + value: $narrowedIncompleteArray ?? $nodes->getTypeAt($expr, $this->from), + isOptional: false, + ), + ]); + + $narrowedIncompleteArray->setAttribute('isIncomplete', true); + } + + return $narrowedIncompleteArray; + } + + /** + * @return array{0: Expr\Variable, 1: non-empty-array} + */ + private function parseNarrowableArrayDimFetch(Expr\ArrayDimFetch $narrowedExpr): array + { + $dims = []; + $expr = $narrowedExpr; + + while ($expr instanceof Expr\ArrayDimFetch) { + if (! $expr->dim) { + throw new \LogicException('Array dim fetch without dim is not narrowable.'); + } + + $dim = $expr->dim; + + if ($dim instanceof String_) { + $dims[] = $dim->value; + } elseif ($dim instanceof Int_) { + $dims[] = $dim->value; + } else { + throw new \LogicException('Only string or int array keys are narrowable.'); + } + + $expr = $expr->var; + } + + if (! $expr instanceof Expr\Variable) { + throw new \LogicException('Array dim fetch root must be a variable.'); + } + + return [$expr, array_reverse($dims)]; + } + + public function toDot(Nodes $nodes): string + { + if (! $this->to) { + throw new \Exception('Incomplete edge, should not happen'); + } + + $phpParserExpressionPrinter = app(PrettyPrinter::class); + + $dot = $this->from->toDotId($nodes).' -> '.$this->to->toDotId($nodes); + + $label = null; + if ($this->conditions || $this->isNegated) { + $label = implode(' AND ', array_map( + fn ($expr) => $phpParserExpressionPrinter->prettyPrint([$expr]), + $this->conditions, + )); + + if ($this->isNegated) { + $label = '!('.$label.')'; + } + } + + if ($label) { + $dot .= ' [label="'.Str::replace('"', '\"', $label).'"]'; + } + + return $dot; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Flow/ExpressionTypeInferrer.php b/vendor/dedoc/scramble/src/Infer/Flow/ExpressionTypeInferrer.php new file mode 100644 index 00000000..7e8a5d73 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Flow/ExpressionTypeInferrer.php @@ -0,0 +1,249 @@ +scope, new NodeTypesResolver); + } + + /** + * Ideally, `infer` should accept not Node but just expressions. @todo + */ + public function infer(PhpParserNode $expr, Closure $variableTypeGetter): Type + { + if ($expr instanceof Expr\Variable && $expr->name === 'this') { + return new SelfType($this->scope->classDefinition()?->name ?: 'unknown'); + } + + if ($expr instanceof Expr\Variable) { + return $variableTypeGetter($expr); + } + + $type = $this->nodeTypesResolver->getType($expr); + + if (! $type instanceof UnknownType) { + return $type; + } + + if ($this->nodeTypesResolver->hasType($expr)) { // For case when the unknown type was in node type resolver. + return $type; + } + + $nonCacheableType = match (true) { + $expr instanceof PhpParserNode\Scalar => (new ScalarTypeGetter)($expr), + $expr instanceof Expr\Cast => (new CastTypeGetter)($expr), + $expr instanceof Expr\ConstFetch => (new ConstFetchTypeGetter)($expr), + $expr instanceof Expr\Throw_ => new NeverType, + $expr instanceof Expr\Ternary => Union::wrap([ + $this->infer($expr->if ?? $expr->cond, $variableTypeGetter), + $this->infer($expr->else, $variableTypeGetter), + ]), + $expr instanceof Expr\BinaryOp\Coalesce => new CoalesceType( + $this->infer($expr->left, $variableTypeGetter), + $this->infer($expr->right, $variableTypeGetter), + ), + $expr instanceof Expr\Match_ => Union::wrap(array_map( + fn (PhpParserNode\MatchArm $arm) => $this->infer($arm->body, $variableTypeGetter), + $expr->arms, + )), + $expr instanceof Expr\ClassConstFetch => (new ClassConstFetchTypeGetter)($expr, $this->scope), + $expr instanceof Expr\BinaryOp\Equal + || $expr instanceof Expr\BinaryOp\Identical + || $expr instanceof Expr\BinaryOp\NotEqual + || $expr instanceof Expr\BinaryOp\NotIdentical + || $expr instanceof Expr\BinaryOp\Greater + || $expr instanceof Expr\BinaryOp\GreaterOrEqual + || $expr instanceof Expr\BinaryOp\Smaller + || $expr instanceof Expr\BinaryOp\SmallerOrEqual => new BooleanType, + $expr instanceof Expr\BooleanNot => (new BooleanNotTypeGetter)($expr), + default => null, + }; + + if ($nonCacheableType) { + return $nonCacheableType; + } + + $type = match (true) { + $expr instanceof Expr\New_ => $this->inferNewCall($expr, $variableTypeGetter), + $expr instanceof Expr\MethodCall => $this->inferMethodCall($expr, $variableTypeGetter), + $expr instanceof Expr\StaticCall => $this->inferStaticCall($expr, $variableTypeGetter), + $expr instanceof Expr\FuncCall => $this->inferFuncCall($expr, $variableTypeGetter), + $expr instanceof Expr\PropertyFetch => $this->inferPropertyFetch($expr, $variableTypeGetter), + /** + * When `dim` is empty, it means that the context is setting – handling in AssignHandler. + * + * @see AssignHandler + */ + $expr instanceof Expr\ArrayDimFetch && $expr->dim => new OffsetAccessType( + $this->infer($expr->var, $variableTypeGetter), + $this->infer($expr->dim, $variableTypeGetter), + ), + default => $type, + }; + + if (! $type instanceof UnknownType) { + $this->nodeTypesResolver->setType($expr, $type); + } + + return $type; + } + + private function inferNewCall(Expr\New_ $expr, Closure $variableTypeGetter): Type + { + if (! $expr->class instanceof PhpParserNode\Name) { + return new NewCallReferenceType( + $this->infer($expr->class, $variableTypeGetter), + $this->inferArgsTypes($expr->args, $variableTypeGetter), + ); + } + + return new NewCallReferenceType( + $expr->class->toString(), + $this->inferArgsTypes($expr->args, $variableTypeGetter), + ); + } + + private function inferMethodCall(Expr\MethodCall $expr, Closure $variableTypeGetter): Type + { + // Only string method names support. + if (! $expr->name instanceof PhpParserNode\Identifier) { + return new UnknownType; + } + + $calleeType = $this->infer($expr->var, $variableTypeGetter); + + return new MethodCallReferenceType( + $calleeType, + $expr->name->name, + $this->inferArgsTypes($expr->args, $variableTypeGetter), + ); + } + + private function inferStaticCall(Expr\StaticCall $expr, Closure $variableTypeGetter): Type + { + // Only string method names support. + if (! $expr->name instanceof PhpParserNode\Identifier) { + return new UnknownType; + } + + if (! $expr->class instanceof PhpParserNode\Name) { + return new StaticMethodCallReferenceType( + $this->infer($expr->class, $variableTypeGetter), + $expr->name->name, + $this->inferArgsTypes($expr->args, $variableTypeGetter), + ); + } + + return new StaticMethodCallReferenceType( + $expr->class->toString(), + $expr->name->name, + $this->inferArgsTypes($expr->args, $variableTypeGetter), + ); + } + + private function inferFuncCall(Expr\FuncCall $expr, Closure $variableTypeGetter): Type + { + if ($expr->name instanceof PhpParserNode\Name) { + return new CallableCallReferenceType( + new CallableStringType($expr->name->toString()), + $this->inferArgsTypes($expr->args, $variableTypeGetter), + ); + } + + return new CallableCallReferenceType( + $this->infer($expr->name, $variableTypeGetter), + $this->inferArgsTypes($expr->args, $variableTypeGetter), + ); + } + + private function inferPropertyFetch(Expr\PropertyFetch $expr, Closure $variableTypeGetter): Type + { + // Only string prop names support. + if (! $name = ($expr->name->name ?? null)) { + return new UnknownType('Cannot infer type of property fetch: not supported yet.'); + } + + return new PropertyFetchReferenceType( + $this->infer($expr->var, $variableTypeGetter), + $name, + ); + } + + /** + * @param array $args + * @return array + */ + private function inferArgsTypes(array $args, Closure $variableTypeGetter): array + { + return collect($args) + ->filter(fn ($arg) => $arg instanceof PhpParserNode\Arg) + ->mapWithKeys(function (PhpParserNode\Arg $arg, $index) use ($variableTypeGetter) { + $type = $this->infer($arg->value, $variableTypeGetter); + if ($parsedPhpDoc = $arg->getAttribute('parsedPhpDoc')) { + $type->setAttribute('docNode', $parsedPhpDoc); + } + + if (! $arg->unpack) { + return [$arg->name ? $arg->name->name : $index => $type]; + } + + if (! $type instanceof ArrayType && ! $type instanceof KeyedArrayType) { + return [$arg->name ? $arg->name->name : $index => $type]; // falling back, but not sure if we should. Maybe some DTO is needed to represent unpacked arg type? + } + + if ($type instanceof ArrayType) { + /* + * For example, when passing something that is array, but shape is unknown + * $a = foo(...array_keys($bar)); + */ + return [$arg->name ? $arg->name->name : $index => $type]; // falling back, but not sure if we should. Maybe some DTO is needed to represent unpacked arg type? + } + + return collect($type->items) + ->mapWithKeys(fn (ArrayItemType_ $item, $i) => [ + $item->key ?: $index + $i => $item->value, + ]) + ->all(); + }) + ->all(); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Flow/MergeNode.php b/vendor/dedoc/scramble/src/Infer/Flow/MergeNode.php new file mode 100644 index 00000000..78f3de45 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Flow/MergeNode.php @@ -0,0 +1,5 @@ +getParserNode() */ + public function definesVariable(string $varName): bool; + + public function getParserNode(): ?PhpParserNode; +} diff --git a/vendor/dedoc/scramble/src/Infer/Flow/Nodes.php b/vendor/dedoc/scramble/src/Infer/Flow/Nodes.php new file mode 100644 index 00000000..ef80f0f4 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Flow/Nodes.php @@ -0,0 +1,457 @@ + */ + protected array $resolvingVariables = []; + + /** + * @param array $entryBindings + */ + public function __construct(private array $entryBindings, private ExpressionTypeInferrer $expressionTypeInferrer) + { + $this->head = new StartNode; + $this->nodes[] = $this->head; + } + + /** + * @param array $entryBindings + */ + public function withEntryBindings(array $entryBindings): self + { + $clone = clone $this; + + $clone->entryBindings = $entryBindings; + $clone->expressionTypeInferrer = $this->expressionTypeInferrer->resetCache(); + + return $clone; + } + + protected function pushNode(Node $node): ?Edge + { + $this->nodes[] = $node; + + if ($this->conditionEdge) { + $this->conditionEdge->to = $node; + $edge = $this->edges[] = $this->conditionEdge; + $this->conditionEdge = null; + + return $edge; + } + + if ($this->head) { + return $this->edges[] = new Edge(from: $this->head, to: $node); + } + + return null; + } + + public function push(Node $node): self + { + $this->pushNode($node); + + $this->head = $node; + + return $this; + } + + public function pushTerminate(Node $node): self + { + $this->pushNode($node); + + $this->head = null; + + return $this; + } + + public function pushCondition(?Expr $condition = null): self + { + $node = new ConditionNode; + + $this->pushNode($node); + + $this->conditionNodesStack[] = $node; + + $this->head = $node; + + $this->conditionEdge = new Edge(from: $node, conditions: $condition ? [$condition] : []); + + return $this; + } + + public function pushConditionBranch(?Expr $condition = null): self + { + $this->head = $this->conditionNodesStack[count($this->conditionNodesStack) - 1]; + + $this->conditionEdge = new Edge(from: $this->head, conditions: $condition ? [$condition] : [], isNegated: ! $condition); + + return $this; + } + + public function exitCondition(): self + { + $conditionNode = array_pop($this->conditionNodesStack); + if (! $conditionNode) { + throw new \Exception('Should not happen'); + } + + $conditionEdges = collect($this->edges)->filter(fn (Edge $e) => $e->from === $conditionNode); + [$negatedEdges, $otherEdges] = $conditionEdges->partition(fn (Edge $e) => $e->isNegated)->all(); + + if ($negatedEdge = $negatedEdges->first()) { + $negatedEdge->conditions = $otherEdges->map->conditions->flatten()->values()->all(); // @phpstan-ignore assign.propertyType + } + + $leafNodes = []; + $nodesToTraverse = [$conditionNode]; + while ($nodesToTraverse) { + $traversingNode = array_pop($nodesToTraverse); + if (! ($traversingNodeSuccessors = $this->successors($traversingNode))) { + $leafNodes[] = $traversingNode; + } else { + $nodesToTraverse = array_merge($nodesToTraverse, $traversingNodeSuccessors); + } + } + + $heads = collect($leafNodes) + ->reject(fn (Node $n) => $n instanceof TerminateNode) + ->unique(strict: true) + ->values() + ->all(); + + if (! $heads && $negatedEdge) { + $this->head = null; + + return $this; + } + + $this->nodes[] = $mergeNode = new MergeNode; + + foreach ($heads as $head) { + $this->edges[] = new Edge(from: $head, to: $mergeNode); + } + + if (! $negatedEdge) { // no else! + $this->edges[] = new Edge( + from: $conditionNode, + to: $mergeNode, + conditions: $otherEdges->map->conditions->flatten()->values()->all(), // @phpstan-ignore argument.type + isNegated: true, + ); + } + + $this->head = $mergeNode; + + return $this; + } + + /** @return Node[] */ + private function successors(Node $node): array + { + return collect($this->edges) + ->filter(fn (Edge $e) => $e->from === $node) + ->values() + ->map(fn (Edge $e): Node => $e->to) // @phpstan-ignore return.type + ->all(); + } + + /** @return Node[] */ + public function getReachableNodes(Closure $cb): array + { + return collect($this->nodes) + ->filter($cb) + ->filter(fn (Node $n) => $this->getRootNode($n) instanceof StartNode) + ->values() + ->all(); + } + + private function getRootNode(Node $node): Node + { + $currentNode = $node; + + /** @var ?Edge $checkingEdge */ + $checkingEdge = collect($this->edges) + ->first(fn (Edge $e) => $e->to === $node); + + while ($checkingEdge) { + $currentNode = $checkingEdge->from; + + $checkingEdge = collect($this->edges) + ->first(fn (Edge $e) => $e->to === $currentNode); + } + + return $currentNode; + } + + /** + * @param Closure(Type $t): bool $cb + * @return list + */ + public function findValueOriginsByExitType(Closure $cb): array + { + /** @var TerminateNode[] $nodes */ + $nodes = $this + ->getReachableNodes(fn (Node $n) => $n instanceof TerminateNode && $n->kind === TerminationKind::RETURN); + + $origins = []; + foreach ($nodes as $node) { + $nodeValueOrigins = $this->findValueOrigins($node); + + foreach ($nodeValueOrigins as $nodeValueOrigin) { + $expression = $nodeValueOrigin instanceof TerminateNode + ? $nodeValueOrigin->value + : ($nodeValueOrigin instanceof StatementNode + && $nodeValueOrigin->parserNode instanceof Expression + && $nodeValueOrigin->parserNode->expr instanceof Expr\Assign ? $nodeValueOrigin->parserNode->expr->expr : null); + + $type = $expression ? $this->getTypeAt($expression, $nodeValueOrigin) : new VoidType; + + if ($cb($type)) { + $origins[] = $nodeValueOrigin; + } + } + } + + return $origins; + } + + /** @return list */ + protected function findValueOrigins(TerminateNode $node): array + { + if (! $node->value) { + return [$node]; + } + + if (! $node->value instanceof Variable || ! is_string($node->value->name)) { + return [$node]; + } + + $origins = []; + $stack = [$node]; + $visited = new WeakMap; + while ($stack) { + /** @var Node $current */ + $current = array_pop($stack); + + if (isset($visited[$current])) { + continue; + } + $visited[$current] = true; + + foreach ($this->incomingEdges($current) as $edge) { + $prev = $edge->from; + + if ($prev->definesVariable($node->value->name)) { + $origins[] = $prev; + + continue; + } + + $stack[] = $prev; + } + } + + return $origins; + } + + /** @return Edge[] */ + private function incomingEdges(Node $node): array + { + return collect($this->edges) + ->filter(fn (Edge $e) => $e->to === $node) + ->values() + ->all(); + } + + public function getTypeAt(Expr $expr, Node $node): Type + { + return $this->expressionTypeInferrer->infer( + expr: $expr, + variableTypeGetter: fn (Expr\Variable $n) => $this->resolveNarrowedVariableTypeAtLocation($n, $node), + ); + } + + private function resolveNarrowedVariableTypeAtLocation(Expr\Variable $var, Node $node): Type + { + $definedTypeAtLocation = $this->resolveVariableTypeAt($var, $node); + $narrowedType = $this->narrowType($definedTypeAtLocation, $var, $node); + + // @todo checking for `isIncomplete` is incredibly important: we don't want to report that some variables has some type based ONLY on narrowing. + if ($narrowedType/* && ! $narrowedType->getAttribute('isIncomplete') */) { + return $narrowedType; + } + + return $definedTypeAtLocation; + } + + private function resolveVariableTypeAt(Expr\Variable $var, Node $node): Type + { + $varName = $var->name; + if (! is_string($varName)) { + return new UnknownType; + } + + if ($node instanceof StartNode) { + if (array_key_exists($varName, $this->entryBindings)) { + return $this->entryBindings[$varName]; + } + + return new UnknownType; + } + + if ($node->definesVariable($varName)) { + // When the variable in question is being resolved (for example, `$a = $a`), the following call will + // return `null`. In such cases we just want to keep looking up, so the execution doesn't stuck here. + $type = $this->resolveVariableTypeAtAssignmentNode($varName, $node); + + if ($type) { + return $type; + } + } + + $types = []; + foreach ($this->incomingEdges($node) as $incomingEdge) { + $types[] = $this->resolveVariableTypeAt($var, $incomingEdge->from); + } + + return Union::wrap($types); + } + + private function resolveVariableTypeAtAssignmentNode(string $varName, Node $node): ?Type + { + if (! $node->definesVariable($varName)) { + throw new \Exception('Should not happen'); + } + + $key = $varName.'@'.spl_object_id($node); + + $isAlreadyResolvingVariable = array_key_exists($key, $this->resolvingVariables); + + if ($isAlreadyResolvingVariable) { + return null; + } + + /** @var Expr\Assign $assignment */ + $assignment = $node->getParserNode()->expr; + + try { + $this->resolvingVariables[$key] = true; + + return $this->getTypeAt($assignment->expr, $node); + } finally { + unset($this->resolvingVariables[$key]); + } + } + + private function narrowType(Type $type, Expr\Variable $var, Node $node): ?Type + { + $varName = $var->name; + if (! is_string($varName)) { + return null; + } + + if ($node instanceof MergeNode || $node instanceof StartNode || $node->definesVariable($varName)) { + return null; + } + + $incomingEdge = $this->incomingEdges($node)[0] ?? null; + if (! $incomingEdge) { + return null; + } + + if ($t = $incomingEdge->getRefinedVariableType($this, $varName)) { + return $t; + } + + $fromNode = $incomingEdge->from; + + if ($narrowed = $this->narrowVariableTypeFromSelfOutCall($var, $fromNode)) { + return $narrowed; + } + + return $this->narrowType($type, $var, $fromNode); + } + + private function narrowVariableTypeFromSelfOutCall(Expr\Variable $var, Node $node): ?PotentialMethodMutatingCallType + { + if (! $node instanceof StatementNode) { + return null; + } + + $parserNode = $node->parserNode; + if (! $parserNode instanceof Expression) { + return null; + } + + $expr = $parserNode->expr; + if (! $expr instanceof Expr\MethodCall) { + return null; + } + + if (! $expr->var instanceof Expr\Variable || $expr->var->name !== $var->name) { + return null; + } + + if (! $expr->name instanceof Identifier) { + return null; + } + + $type = $this->getTypeAt($expr, $node); + if (! $type instanceof MethodCallReferenceType) { + return null; + } + + return new PotentialMethodMutatingCallType( + $type->callee, + $type->methodName, + $type->arguments, + ); + } + + public function toDot(bool $indent = false): string + { + $dotGraph = 'digraph Flow {'.($indent ? "\n" : ' '); + + $dotEdges = collect($this->edges) + ->map(fn (Edge $e, $i) => $e->toDot($this)) + ->map(fn (string $d) => $indent ? ' '.$d : $d) + ->join(';'.($indent ? "\n" : ' ')); + $dotGraph .= $dotEdges.';'.($indent ? "\n" : ' '); + + $dotNodes = collect($this->nodes) + ->map(fn (Node $n, $i) => $n->toDot($this)) + ->map(fn (string $d) => $indent ? ' '.$d : $d) + ->join(';'.($indent ? "\n" : ' ')); + $dotGraph .= $dotNodes.';'.($indent ? "\n" : ' '); + + return $dotGraph.'}'; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Flow/StartNode.php b/vendor/dedoc/scramble/src/Infer/Flow/StartNode.php new file mode 100644 index 00000000..5791b6b7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Flow/StartNode.php @@ -0,0 +1,5 @@ +parserNode; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Flow/TerminateNode.php b/vendor/dedoc/scramble/src/Infer/Flow/TerminateNode.php new file mode 100644 index 00000000..0800f729 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Flow/TerminateNode.php @@ -0,0 +1,19 @@ +value; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Flow/TerminationKind.php b/vendor/dedoc/scramble/src/Infer/Flow/TerminationKind.php new file mode 100644 index 00000000..70dea737 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Flow/TerminationKind.php @@ -0,0 +1,9 @@ + $parameters + */ + public function __construct(private array $parameters, private Scope $scope) + { + $this->flowNodes = new Nodes( + $this->parameters, + new ExpressionTypeInferrer($this->scope, $this->scope->nodeTypesResolver), + ); + } + + public function shouldHandle(Node $node): bool + { + return $node instanceof Node\Stmt + && ! $node instanceof Node\Stmt\Function_; + } + + public function enterNode(Node $node) + { + if (! $this->shouldHandle($node)) { + return null; + } + + $flow = $this->flowNodes; + + if ( + $node instanceof Node\Stmt\Expression + && $node->expr instanceof Node\Expr\Assign + && $node->expr->var instanceof Node\Expr\Variable + ) { + if ($node->expr->expr instanceof Node\Expr\Match_) { + $this->pushAssignMatch($flow, $node->expr->var, $node->expr->expr); + + return null; + } + } + + if ($node instanceof Node\Stmt\Return_) { + if ($node->expr instanceof Node\Expr\Match_) { + $this->pushTerminateMatch($flow, $node->expr); + + return null; + } + + $flow->pushTerminate(new TerminateNode(TerminationKind::RETURN, $node->expr)); + + return null; + } + + if ($node instanceof Node\Stmt\If_) { + $flow->pushCondition(condition: $node->cond); // pushes node, makes "yes" branch head + + return null; + } + + if ($node instanceof Node\Stmt\ElseIf_) { + $flow->pushConditionBranch(condition: $node->cond); // goes back to condition node, pushes the new branch + + return null; + } + + if ($node instanceof Node\Stmt\Else_) { + $flow->pushConditionBranch(); // goes back to condition node, pushes the new branch + + return null; + } + + $flow->push(new StatementNode($node)); // pushes node, make the node head + + return null; + } + + public function leaveNode(Node $node) + { + if ($node instanceof Node\FunctionLike) { + $this->ensureTerminated(); + + return null; + } + + if (! $this->shouldHandle($node)) { + return null; + } + + $flow = $this->flowNodes; + + if ($node instanceof Node\Stmt\If_) { + $flow->exitCondition(); // pushes node, makes "yes" branch head + } + + return null; + } + + private function ensureTerminated(): void + { + if ($this->flowNodes->head === null) { + return; + } + + $this->flowNodes->pushTerminate(new TerminateNode( + kind: TerminationKind::RETURN, + value: null, + )); + } + + private function pushTerminateMatch(Nodes $flow, Node\Expr\Match_ $match): void + { + $flow->pushCondition(); + + foreach ($match->arms as $arm) { + if ($arm->conds === null) { // default arm + $flow->pushConditionBranch(); // negated / else + + $flow->pushTerminate(new TerminateNode(TerminationKind::RETURN, $arm->body)); + + continue; + } + + foreach ($arm->conds as $cond) { + $flow->pushConditionBranch(new Expr\BinaryOp\Identical($match->cond, $cond)); + + $flow->pushTerminate(new TerminateNode(TerminationKind::RETURN, $arm->body)); + } + } + + $flow->exitCondition(); + } + + private function pushAssignMatch(Nodes $flow, Variable $variable, Match_ $match): void + { + $flow->pushCondition(); + + foreach ($match->arms as $arm) { + if ($arm->conds === null) { // default arm + $flow->pushConditionBranch(); // negated / else + + $flow->push(new StatementNode(new Expression(new Expr\Assign($variable, $arm->body)))); + + continue; + } + + foreach ($arm->conds as $cond) { + $flow->pushConditionBranch(new Expr\BinaryOp\Identical($match->cond, $cond)); + + $flow->push(new StatementNode(new Expression(new Expr\Assign($variable, $arm->body)))); + } + } + + $flow->exitCondition(); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/ArrayHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/ArrayHandler.php new file mode 100644 index 00000000..f6504169 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/ArrayHandler.php @@ -0,0 +1,29 @@ +items) + ->filter() + ->map(fn (Node\Expr\ArrayItem $arrayItem) => $scope->getType($arrayItem)) + ->all(); + + $scope->setType( + $node, + TypeHelper::unpackIfArray(new KeyedArrayType($arrayItems)) + ); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/ArrayItemHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/ArrayItemHandler.php new file mode 100644 index 00000000..7dfe26e1 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/ArrayItemHandler.php @@ -0,0 +1,60 @@ +key ? $scope->getType($node->key) : null; + + $scope->setType( + $node, + new ArrayItemType_( + $this->evaluateKeyNode($node->key, $scope), // @todo handle cases when key is something dynamic + $scope->getType($node->value), + isOptional: false, + shouldUnpack: $node->unpack, + keyType: $keyType, + ) + ); + } + + private function evaluateKeyNode(?Node\Expr $key, Scope $scope): int|string|null + { + if (! $key) { + return null; + } + + $evaluator = new ConstExprEvaluator(function (Node\Expr $node) use ($scope) { + if ($node instanceof Node\Expr\ClassConstFetch) { + return (new ConstFetchEvaluator([ + 'self' => $scope->classDefinition()?->name, + 'static' => $scope->classDefinition()?->name, + ]))->evaluate($node); + } + + return null; + }); + + try { + $result = $evaluator->evaluateSilently($key); + + return is_string($result) || is_int($result) ? $result : null; + } catch (ConstExprEvaluationException) { + return null; + } + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/AssignHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/AssignHandler.php new file mode 100644 index 00000000..db55c2e3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/AssignHandler.php @@ -0,0 +1,140 @@ +var instanceof Node\Expr\Variable) { + $this->handleVarAssignment($node, $node->var, $scope); + } + + if ($node->var instanceof Node\Expr\ArrayDimFetch) { + $this->handleArrayKeyAssignment($node, $node->var, $scope); + } + + if ($node->var instanceof Node\Expr\Array_ || $node->var instanceof Node\Expr\List_) { + $this->handleDestructuringAssignment($node, $node->var, $scope); + } + } + + private function handleVarAssignment(Node\Expr\Assign $node, Node\Expr\Variable $var, Scope $scope): void + { + $scope->addVariableType( + $node->getAttribute('startLine'), + (string) $var->name, + $type = $scope->getType($node->expr), + ); + + $scope->setType($node, $type); + } + + private function handleDestructuringAssignment( + Node\Expr\Assign $node, + Node\Expr\Array_|Node\Expr\List_ $pattern, + Scope $scope, + ): void { + $rhsType = $scope->getType($node->expr); + $line = $node->getAttribute('startLine'); + + $this->assignFromPattern($rhsType, $pattern, $line, $scope); + + $scope->setType($node, $rhsType); + } + + private function assignFromPattern( + Type $sourceType, + Node\Expr\Array_|Node\Expr\List_ $pattern, + int $line, + Scope $scope, + ): void { + $position = 0; + + foreach ($pattern->items as $item) { + if (! $item) { + $position++; + + continue; + } + + if ($item->unpack) { + if ($item->key === null) { + $position++; + } + + continue; + } + + $offsetType = $item->key === null + ? new LiteralIntegerType($position) + : $scope->getType($item->key); + + $elementType = new OffsetAccessType($sourceType, $offsetType); + + if ($item->value instanceof Node\Expr\Variable && is_string($item->value->name)) { + $scope->addVariableType($line, $item->value->name, $elementType); + } elseif ($item->value instanceof Node\Expr\Array_ || $item->value instanceof Node\Expr\List_) { + $this->assignFromPattern($elementType, $item->value, $line, $scope); + } + + if ($item->key === null) { + $position++; + } + } + } + + private function handleArrayKeyAssignment(Node\Expr\Assign $node, Node\Expr\ArrayDimFetch $targetNode, Scope $scope): void + { + /** @var (?Expr)[] $path */ + $path = [$targetNode->dim]; + $var = $targetNode->var; + + while ($var instanceof Node\Expr\ArrayDimFetch) { + $path = Arr::prepend($path, $var->dim); + $var = $var->var; + } + + if (! $var instanceof Node\Expr\Variable) { + return; + } + + if (! is_string($var->name)) { + return; + } + + $varType = new OffsetSetType( + $scope->getType($var), + new KeyedArrayType(array_map( + fn ($pathExpression) => new ArrayItemType_(null, value: $pathExpression === null ? new TemplatePlaceholderType : $scope->getType($pathExpression)), + $path, + )), + $scope->getType($node->expr), + ); + + $scope->addVariableType( + $node->getAttribute('startLine'), // @phpstan-ignore argument.type + $var->name, + $varType, + ); + + $scope->setType($node, $varType); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/ClassHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/ClassHandler.php new file mode 100644 index 00000000..958818ec --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/ClassHandler.php @@ -0,0 +1,37 @@ +createChildScope(clone $scope->context); + } + + public function shouldHandle($node) + { + return $node instanceof Node\Stmt\Class_; + } + + public function enter(Node\Stmt\Class_ $node, Scope $scope) + { + $parentDefinition = $node->extends + ? $scope->index->getClass($node->extends->toString()) + : null; + + $scope->context->setClassDefinition($classDefinition = new ClassDefinition( + name: $node->namespacedName ? $node->namespacedName->toString() : 'anonymous@class', + templateTypes: $parentDefinition?->templateTypes ?: [], + properties: $parentDefinition?->properties ?: [], + methods: $parentDefinition?->methods ?: [], + parentFqn: $node->extends ? $node->extends->toString() : null, + )); + + $scope->index->registerClassDefinition($classDefinition); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/CreatesScope.php b/vendor/dedoc/scramble/src/Infer/Handler/CreatesScope.php new file mode 100644 index 00000000..3e97d1dd --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/CreatesScope.php @@ -0,0 +1,11 @@ +extensions = $extensions; + } + + public function shouldHandle($node) + { + return $node instanceof Node\Expr; + } + + public function leave(Node\Expr $node, Scope $scope) + { + if (! $scope->isInFunction()) { + return; + } + + $fnDefinition = $scope->functionDefinition(); + + foreach ($this->extensions as $extension) { + if (! count($exceptions = $extension->getException($node, $scope))) { + continue; + } + + $fnDefinition->type->exceptions = array_merge($fnDefinition->type->exceptions, $exceptions); + } + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/ExpressionTypeInferringExtensions.php b/vendor/dedoc/scramble/src/Infer/Handler/ExpressionTypeInferringExtensions.php new file mode 100644 index 00000000..ad052af2 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/ExpressionTypeInferringExtensions.php @@ -0,0 +1,44 @@ +extensions = $extensions; + } + + public function shouldHandle($node) + { + return $node instanceof Node\Expr; + } + + public function leave(Node\Expr $node, Scope $scope) + { + $type = array_reduce( + $this->extensions, + function ($acc, ExpressionTypeInferExtension $extension) use ($node, $scope) { + $type = $extension->getType($node, $scope); + if ($type) { + $scope->setType($node, $type); + + return $type; + } + + return $acc; + }, + ); + + if ($type) { + $scope->setType($node, $type); + } + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/FunctionLikeHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/FunctionLikeHandler.php new file mode 100644 index 00000000..d9313d1f --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/FunctionLikeHandler.php @@ -0,0 +1,247 @@ +createChildScope(clone $scope->context); + + if ($node instanceof Node\Expr\ArrowFunction) { + $fnScope->variables = $scope->variables; + } + + if ($node instanceof Node\Expr\Closure) { + foreach ($node->uses as $use) { + $fnScope->variables[$use->var->name] = $scope->variables[$use->var->name] ?? []; + } + } + + return $fnScope; + } + + public function enter(FunctionLike $node, Scope $scope) + { + // when entering function node, the only thing we need/want to do + // is to set node param types to scope. + // Also, if here we add a reference to the function node type, it may allow us to + // set function return types not in leave function, but in the return handlers. + $scope->context->setFunctionDefinition($fnDefinition = new FunctionLikeAstDefinition( + type: $fnType = new FunctionType($node->name->name ?? 'anonymous'), + definingClassName: $scope->context->classDefinition?->name, + isStatic: $node instanceof Node\Stmt\ClassMethod ? $node->isStatic() : false, + )); + + $fnDefinition + ->setAstNode($node) + ->setScope($scope) + ->setDeclarationDefinition($this->buildDeclarationDefinition($node, $scope)); + + $fnDefinition->isFullyAnalyzed = true; + + if ($node instanceof Node\Expr\ArrowFunction || $node instanceof Node\Expr\Closure) { + $scope->setType($node, $fnType); + } + + if (isset($node->name->name) && $node instanceof Node\Stmt\Function_) { + $scope->index->registerFunctionDefinition($fnDefinition); + } + + // If the function is __construct and we're in the class context, we want to handle + // simple assigning of args to props ("simple" - assigning is in the fn's statements, means + // it is not in if or other block) by setting args types to be prop's ones. + // Also, here we find calls to `parent::__construct` and infer args class' templates types from there. + $classDefinitionTemplatesTypes = $this->findPropertyAssignedArgs($node, $scope, $fnType); + + $localTemplates = []; + $fnType->arguments = collect($node->getParams()) + ->mapWithKeys(function (Node\Param $param) use ($scope, $classDefinitionTemplatesTypes, &$localTemplates) { + if (! $param->var instanceof Node\Expr\Variable || ! is_string($param->var->name)) { + return []; + } + + if (array_key_exists($param->var->name, $classDefinitionTemplatesTypes)) { + return [$param->var->name => $classDefinitionTemplatesTypes[$param->var->name]]; + } + + $annotatedType = isset($param->type) + ? TypeHelper::createTypeFromTypeNode($param->type) + : null; + + $type = new TemplateType( + $scope->makeConflictFreeTemplateName('T'.Str::studly($param->var->name)), + $annotatedType, + ); + + $localTemplates[] = $type; + + return [$param->var->name => $type]; + }) + ->all(); + + $fnType->templates = $localTemplates; + + foreach ($node->getParams() as $param) { + if (! $param->var instanceof Node\Expr\Variable) { + continue; + } + + $scope->addVariableType( + $param->getAttribute('startLine'), + $paramName = (string) $param->var->name, + $fnType->arguments[$paramName], + ); + + if (isset($param->default)) { + $fnDefinition->addArgumentDefault($paramName, $scope->getType($param->default)); + } + } + + if ($scope->isInClass() && $node instanceof Node\Stmt\ClassMethod) { + $scope->classDefinition()->methods[$fnType->name] = $fnDefinition; + } + } + + public function leave(FunctionLike $node, Scope $scope) + { + // Simple way of handling the arrow functions, as they do not have a return statement. + // So here we just create a "virtual" return and processing it as by default. + if ($node instanceof Node\Expr\ArrowFunction) { + (new ReturnHandler)->leave( + new Node\Stmt\Return_($node->expr, $node->getAttributes()), + $scope, + ); + } + } + + private function findPropertyAssignedArgs(FunctionLike $node, Scope $scope, FunctionType $fnType) + { + if (! $scope->isInClass()) { + return []; + } + + if ($fnType->name !== '__construct') { + return []; + } + + $argumentsByKeys = collect($node->getParams()) + ->mapWithKeys(function (Node\Param $param) { + return $param->var instanceof Node\Expr\Variable && is_string($param->var->name) ? [ + $param->var->name => true, + ] : []; + }) + ->toArray(); + + $argumentsAssignedToProperties = []; + + $parentFqn = $scope->classDefinition()->parentFqn; + + $callToParentConstruct = $parentFqn ? array_filter( + $node->getStmts() ?: [], + fn (Node\Stmt $s) => $s instanceof Node\Stmt\Expression + && $s->expr instanceof Node\Expr\StaticCall + && $s->expr->class instanceof Node\Name + && $s->expr->class->toString() === 'parent' + && $s->expr->name instanceof Node\Identifier + && $s->expr->name->toString() === '__construct', + )[0] ?? null : null; + + if ( + $callToParentConstruct + && $parentFqn // @phpstan-ignore booleanAnd.rightAlwaysTrue + && ($parentDefinition = $scope->index->getClass($parentFqn)) + && ($parentConstructorDefinition = $parentDefinition->getMethodDefinition('__construct')) + ) { + $parentConstructorArguments = $parentConstructorDefinition->type->arguments; + + foreach ($callToParentConstruct->expr->args as $index => $arg) { + if (! $arg->value instanceof Node\Expr\Variable) { + continue; + } + + $correspondingParentArgumentType = $arg->name + ? ($parentConstructorArguments[$arg->name->toString()] ?? null) + : (array_values($parentConstructorArguments)[$index] ?? null); + + if (! $correspondingParentArgumentType) { + continue; + } + + $argumentsAssignedToProperties[$arg->value->name] = $correspondingParentArgumentType; + } + } + + $assignPropertiesToThisNodes = array_filter( + $node->getStmts() ?: [], + fn (Node\Stmt $s) => $s instanceof Node\Stmt\Expression + && $s->expr instanceof Node\Expr\Assign + && $s->expr->var instanceof Node\Expr\PropertyFetch + && $s->expr->var->var instanceof Node\Expr\Variable + && $s->expr->var->var->name === 'this' + && $s->expr->var->name instanceof Node\Identifier + && $s->expr->expr instanceof Node\Expr\Variable + && is_string($s->expr->expr->name) + && ($argumentsByKeys[$s->expr->expr->name] ?? false), + ); + + // Variable type becomes a property type. + $assignPropertiesToThisNodes = array_reduce($assignPropertiesToThisNodes, function ($acc, Node\Stmt\Expression $s) use ($scope) { + $propName = $s->expr->var->name->name; + + if (! array_key_exists($propName, $scope->classDefinition()->properties)) { + return $acc; + } + + $acc[$s->expr->expr->name] = $scope->classDefinition()->properties[$propName]->type; + + return $acc; + }, $argumentsAssignedToProperties); + + $promotedProperties = collect($node->getParams()) + ->filter(fn (Node\Param $p) => $p->isPromoted()) + ->mapWithKeys(fn (Node\Param $param) => $param->var instanceof Node\Expr\Variable && is_string($param->var->name) ? [ + $param->var->name => $scope->classDefinition()->properties[$param->var->name]->type, + ] : []) + ->toArray(); + + return array_merge($assignPropertiesToThisNodes, $promotedProperties); + } + + private function buildDeclarationDefinition(FunctionLike $node, Scope $scope): FunctionLikeDefinition + { + $definition = (new FunctionLikeDeclarationAstDefinitionBuilder( + $node, + $scope->context->classDefinition, + ))->build(); + + $phpDocNode = $node->getAttribute('parsedPhpDoc'); + if (! $phpDocNode instanceof PhpDocNode) { + return $definition; + } + + return (new FunctionLikeDeclarationPhpDocDefinitionBuilder( + $definition, + $phpDocNode, + $scope->context->classDefinition + ))->build(); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/IndexBuildingHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/IndexBuildingHandler.php new file mode 100644 index 00000000..1bf84c41 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/IndexBuildingHandler.php @@ -0,0 +1,25 @@ +indexBuilders as $indexBuilder) { + $indexBuilder->afterAnalyzedNode($scope, $node); + } + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/PhpDocHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/PhpDocHandler.php new file mode 100644 index 00000000..80447970 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/PhpDocHandler.php @@ -0,0 +1,59 @@ +getDocComment() || count($node->getComments()); + } + + public function leave(Node $node, Scope $scope): ?Type + { + if (! $parsedPhpDoc = $node->getAttribute('parsedPhpDoc')) { + return null; + } + + /** @var PhpDocNode $parsedPhpDoc */ + $parsedPhpDoc->setAttribute('sourceClass', $scope->classDefinition()?->name); + + $scope->getType($node)->setAttribute('docNode', $parsedPhpDoc); + + if ($node instanceof Node\Stmt\Return_ && $node->expr) { + $scope->getType($node->expr)->setAttribute('docNode', $parsedPhpDoc); + } + + if ($node instanceof Node\Stmt\ClassMethod && ($methodType = $scope->functionDefinition()?->type)) { + $thrownExceptions = collect($parsedPhpDoc->getThrowsTagValues()) + ->flatMap(function (ThrowsTagValueNode $t) { + $type = PhpDocTypeHelper::toType($t->type); + + if ($type instanceof Union) { + return $type->types; + } + + return [$type]; + }); + + $methodType->exceptions = [ + ...$methodType->exceptions, + ...$thrownExceptions, + ]; + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/PropertyHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/PropertyHandler.php new file mode 100644 index 00000000..1550ecce --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/PropertyHandler.php @@ -0,0 +1,76 @@ +classDefinition()) { + // Anonymous class - not implemented yet. + return; + } + + $ownProperties = []; + foreach ($node->props as $prop) { + $annotatedType = isset($node->type) + ? TypeHelper::createTypeFromTypeNode($node->type) + : null; + + $attributes = []; + $docComment = $node->getDocComment()?->getText(); + + try { + $reflectionProperty = new ReflectionProperty($classDefinition->name, $prop->name->name); + $attributes = AttributeDefinition::fromReflectionAttributesArray($reflectionProperty->getAttributes()); + + if (! $docComment) { + $docComment = $reflectionProperty->getDocComment() ?: null; + } + } catch (\ReflectionException) { + } + + $propertyDefinition = new ClassPropertyDefinition( + type: new TemplateType($scope->makeConflictFreeTemplateName('T'.Str::studly($prop->name->name)), $annotatedType), + defaultType: $prop->default ? $scope->getType($prop->default) : null, + isStatic: $node->isStatic(), + visibility: match (true) { + $node->isPrivate() => PropertyVisibility::Private, + $node->isProtected() => PropertyVisibility::Protected, + default => PropertyVisibility::Public, + }, + attributes: $attributes, + pendingDocComment: $docComment + ? new PendingDocComment($docComment, declaringClass: $classDefinition->name) + : null, + ); + $ownProperties[$prop->name->name] = $propertyDefinition; + } + + $classDefinition->properties = array_merge($classDefinition->properties, $ownProperties); + + $classDefinition->templateTypes = array_merge( + $classDefinition->templateTypes, + array_values(array_filter(array_map( + fn (ClassPropertyDefinition $p) => $p->type instanceof TemplateType ? $p->type : null, + $ownProperties, + ))), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/ReturnHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/ReturnHandler.php new file mode 100644 index 00000000..95a16c13 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/ReturnHandler.php @@ -0,0 +1,32 @@ +isInFunction()) { + return; + } + + $fnDefinition = $scope->functionDefinition(); + + $fnDefinition->type->setReturnType( + TypeHelper::mergeTypes( + $node->expr ? $scope->getType($node->expr) : new VoidType, + $fnDefinition->type->getReturnType(), + ) + ); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/ThrowHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/ThrowHandler.php new file mode 100644 index 00000000..70570fed --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/ThrowHandler.php @@ -0,0 +1,35 @@ +isInFunction()) { + return; + } + + $exceptionType = $scope->getType($node->expr); + + if (! $exceptionType->isInstanceOf(Throwable::class)) { + return; + } + + $fnDefinition = $scope->functionDefinition(); + + $fnDefinition->type->exceptions = [ + ...$fnDefinition->type->exceptions, + $exceptionType, + ]; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Handler/UnsetHandler.php b/vendor/dedoc/scramble/src/Infer/Handler/UnsetHandler.php new file mode 100644 index 00000000..6381803e --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Handler/UnsetHandler.php @@ -0,0 +1,73 @@ +vars as $var) { + if ($var instanceof Node\Expr\ArrayDimFetch) { + if (! $var->dim) { + continue; + } + + $this->handleArrayKeyUnsetting($node, $var, $scope); + } + } + } + + private function handleArrayKeyUnsetting(Node\Stmt\Unset_ $node, Node\Expr\ArrayDimFetch $targetNode, Scope $scope): void + { + /** @var (Expr)[] $path */ + $path = [$targetNode->dim]; + $var = $targetNode->var; + + while ($var instanceof Node\Expr\ArrayDimFetch) { + if (! $var->dim) { + return; + } + + $path = Arr::prepend($path, $var->dim); + $var = $var->var; + } + + if (! $var instanceof Node\Expr\Variable) { + return; + } + + if (! is_string($var->name)) { + return; + } + + $varType = new OffsetUnsetType( + $scope->getType($var), + new KeyedArrayType(array_map( + fn ($pathExpression) => new ArrayItemType_(null, value: $scope->getType($pathExpression)), + $path, + )), + ); + + $scope->addVariableType( + $node->getAttribute('startLine'), // @phpstan-ignore argument.type + $var->name, + $varType, + ); + + $scope->setType($node, new VoidType); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Reflector/ClassReflector.php b/vendor/dedoc/scramble/src/Infer/Reflector/ClassReflector.php new file mode 100644 index 00000000..94edf50d --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Reflector/ClassReflector.php @@ -0,0 +1,71 @@ +methods[$name] ??= MethodReflector::make($this->className, $name); + } + + public static function make(string $className): static + { + return static::$cache[$className] ??= new static( + FileParser::getInstance(), + $className, + ); + } + + public function getReflection(): ReflectionClass + { + return new ReflectionClass($this->className); + } + + public function getNameContext(): NameContext + { + return $this->nameContext ??= FileNameResolver::createForFile($this->getReflection()->getFileName())->nameContext; + } + + /** + * @throws RuntimeException + */ + public function getSource(): string + { + $refClass = $this->getReflection(); + + $fileName = $refClass->getFileName(); + if ($fileName === false) { + throw new RuntimeException("Class {$refClass->getName()} is internal or not user-defined."); + } + + $startLine = $refClass->getStartLine(); + $endLine = $refClass->getEndLine(); + + $lines = file($fileName); + if ($lines === false) { + throw new RuntimeException("Unable to read file: $fileName"); + } + + $classLines = array_slice($lines, $startLine - 1, $endLine - $startLine + 1); + + return implode('', $classLines); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Reflector/ClosureReflector.php b/vendor/dedoc/scramble/src/Infer/Reflector/ClosureReflector.php new file mode 100644 index 00000000..a6742fbd --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Reflector/ClosureReflector.php @@ -0,0 +1,169 @@ + + */ + private static WeakMap $cache; + + private ?NameContext $nameContext = null; + + private ?Node\FunctionLike $astNode = null; + + private function __construct( + private FileParser $parser, + public Closure $closure, + ) {} + + /** + * @return WeakMap + */ + private static function getCache(): WeakMap + { + /** @var WeakMap $default */ + $default = new WeakMap; + + return self::$cache ??= $default; + } + + public static function make(Closure $closure): self + { + if (self::getCache()->offsetExists($closure)) { + return self::getCache()->offsetGet($closure); + } + + $reflector = new self(app(FileParser::class), $closure); + + self::getCache()->offsetSet($closure, $reflector); + + return $reflector; + } + + public function getCode(): string + { + return ($this->getReflection()->getDocComment() ?: '')."\n".$this->getReflection()->getCode(); + } + + public function getNameContext(): NameContext + { + if (! $path = $this->getReflection()->getFileName()) { + throw new \LogicException('Cannot find file name for closure'); + } + + return $this->nameContext ??= FileNameResolver::createForFile($path)->nameContext; + } + + public function getReflection(): ReflectionClosure + { + return new ReflectionClosure($this->closure); + } + + public function getAstNode(): ?Node\FunctionLike + { + if ($this->astNode) { + return $this->astNode; + } + + $code = 'getCode().';'; + + $node = (new NodeFinder) + ->findFirst( + $this->parser->parseContent($code)->getStatements(), + fn (Node $n) => $n instanceof Node\Stmt\Expression && $n->expr instanceof Node\FunctionLike, + ); + + /** @var Node\Stmt\Expression|null $node */ + if (! $node) { + return null; + } + + /* + * We first find statement containing function like expression and not function like directly, + * due to the PHPDoc. When PHPDoc is attached to the closure, it will be contained in the statement + * node, not the function like expression node. This way we attach this PHPDoc to the expression so + * the rest of the codebase can handle it correctly. + */ + $attrs = $node->getAttributes(); + /** @var Node\FunctionLike $node */ + $node = $node->expr; + $node->setAttributes($attrs); + + if (! $path = $this->getReflection()->getFileName()) { + return null; + } + + $fileNameContext = FileNameResolver::createForFile($path); + + $traverser = new NodeTraverser( + new class($fileNameContext->nameContext) extends NameResolver + { + public function __construct(NameContext $nameContext) + { + parent::__construct(); + $this->nameContext = $nameContext; + } + + public function beforeTraverse(array $nodes): ?array + { + return null; + } + }, + new PhpDocResolver($fileNameContext), + ); + $traverser->traverse([$node]); + + return $this->astNode = $node; + } + + /** + * @param IndexBuilder>[] $indexBuilders + */ + public function getFunctionLikeDefinition(array $indexBuilders = [], bool $withSideEffects = false): FunctionLikeDefinition + { + if (! $functionLikeNode = $this->getAstNode()) { + throw new \LogicException('Cannot get AST node of closure'); + } + + $scopeCollector = new ScopeCollector; + + $closureDefinition = (new Infer\DefinitionBuilders\FunctionLikeAstDefinitionBuilder( + '{closure}', + $functionLikeNode, + app(Infer::class)->index, + new FileNameResolver($this->getNameContext()), + indexBuilders: [...$indexBuilders, $scopeCollector], + withSideEffects: $withSideEffects, + ))->build(); + + if (! $scope = $scopeCollector->getScope($closureDefinition)) { + throw new \LogicException('Cannot get scope of closure'); + } + + Infer\DefinitionBuilders\FunctionLikeAstDefinitionBuilder::resolveFunctionParameterDefaults($scope, $closureDefinition); + Infer\DefinitionBuilders\FunctionLikeAstDefinitionBuilder::resolveFunctionReturnReferences($scope, $closureDefinition); + Infer\DefinitionBuilders\FunctionLikeAstDefinitionBuilder::resolveFunctionExceptions($scope, $closureDefinition); + + $closureDefinition->referencesResolved = true; + + return $closureDefinition; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Reflector/MethodReflector.php b/vendor/dedoc/scramble/src/Infer/Reflector/MethodReflector.php new file mode 100644 index 00000000..f89583ae --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Reflector/MethodReflector.php @@ -0,0 +1,141 @@ +getClassReflector()->getNameContext(); + } + + public function getMethodCode(): string + { + $reflection = $this->getReflection(); + + // The class may be a part of standard PHP classes. + if (! $path = $reflection->getFileName()) { + return ''; + } + + return implode("\n", array_slice( + preg_split('/\r\n|\r|\n/', file_get_contents($path)), + $reflection->getStartLine() - 1, + $reflection->getStartLine() === $reflection->getEndLine() ? 1 : max($reflection->getEndLine() - $reflection->getStartLine(), 1) + 1, + )); + } + + public function getReflection(): ReflectionMethod + { + /** + * \ReflectionMethod could've been used here, but for `\Closure::__invoke` it fails when constructed manually + */ + return (new \ReflectionClass($this->className))->getMethod($this->name); + } + + /** + * @todo: Think if this method can actually return `null` or it should fail. + */ + public function getAstNode(): ?ClassMethod + { + if (! $this->methodNode) { + $className = class_basename($this->className); + $methodReflection = $this->getReflection(); + + $methodDoc = $methodReflection->getDocComment() ?: ''; + $lines = $methodReflection->getStartLine(); + + $lines = str_repeat("\n", max($lines - 3 - substr_count($methodDoc, "\n"), 1)); + + $partialClass = "getMethodCode()."\n}"; + + $node = (new NodeFinder) + ->findFirst( + $this->parser->parseContent($partialClass)->getStatements(), + fn (Node $node) => $node instanceof Node\Stmt\ClassMethod && $node->name->name === $this->name, + ); + + if (! $path = $this->getReflection()->getFileName()) { + return null; + } + $fileNameContext = FileNameResolver::createForFile($path); + + $traverser = new NodeTraverser( + new class($fileNameContext->nameContext) extends NameResolver + { + public function __construct(NameContext $nameContext) + { + parent::__construct(); + $this->nameContext = $nameContext; + } + + public function beforeTraverse(array $nodes): ?array + { + return null; + } + }, + new PhpDocResolver($fileNameContext), + ); + $traverser->traverse([$node]); + + $this->methodNode = $node; + } + + return $this->methodNode; + } + + public function getClassReflector(): ClassReflector + { + return ClassReflector::make($this->getReflection()->class); + } + + /** + * @param IndexBuilder>[] $indexBuilders + */ + public function getFunctionLikeDefinition(array $indexBuilders = [], bool $withSideEffects = false): FunctionLikeDefinition + { + $def = app(Infer::class)->analyzeClass($this->getReflection()->class); + + $methodDefinition = $def->getMethodDefinition( + $this->getReflection()->name, + indexBuilders: $indexBuilders, + withSideEffects: $withSideEffects, + ); + + if (! $methodDefinition) { + throw new LogicException("Method [$this->name] is not found on class [$this->className]"); + } + + return $methodDefinition; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Reflector/PropertyReflector.php b/vendor/dedoc/scramble/src/Infer/Reflector/PropertyReflector.php new file mode 100644 index 00000000..b336b7b0 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Reflector/PropertyReflector.php @@ -0,0 +1,238 @@ + + */ + private static array $cache = []; + + private ?Property $properyNode = null; + + /** + * @param class-string $className + */ + private function __construct( + private FileParser $parser, + public string $className, + public string $name, + ) {} + + /** + * @param class-string $className + */ + public static function make(string $className, string $name): self + { + return self::$cache["$className@$name"] ??= new self( + app(FileParser::class), // ? + $className, + $name, + ); + } + + public function getPropertyCode(): string + { + return $this->getPropertyNodeDeclarationSource(); + } + + public function getReflection(): ReflectionProperty + { + /** + * \ReflectionMethod could've been used here, but for `\Closure::__invoke` it fails when constructed manually + */ + return (new \ReflectionClass($this->className))->getProperty($this->name); + } + + private function getPropertyNodeDeclarationSource(): string + { + try { + $classSource = $this->getClassReflector()->getSource(); + } catch (RuntimeException) { + return ''; + } + + $code = "name + && $braceLevel === 1 + ) { + $collect = true; + $result .= $text; + + continue; + } + + if ($collect) { + // collect everything (comments, whitespace, punctuation, etc.) + $result .= $text; + + // stop collecting at the semicolon that closes this property + if ($text === ';' && $braceLevel === 1) { + break; + } + } + } + + return $result; + } + + /** + * @todo: Think if this method can actually return `null` or it should fail. + */ + public function getAstNode(): ?Node\Stmt\Property + { + if ($this->properyNode) { + return $this->properyNode; + } + + $propertySource = $this->getPropertyNodeDeclarationSource(); + + if (! $propertySource) { + return null; + } + + $partialClass = <<<"EOD" +parser->parseContent($partialClass)->getStatements(); + + /** @var Property|null $node */ + $node = (new NodeFinder) + ->findFirst( + $statements, + fn (Node $node) => $node instanceof Property && (bool) collect($node->props)->first(fn (Node\PropertyItem $p) => $p->name->name === $this->name), + ); + + if (! $node) { + return null; + } + + $traverser = new NodeTraverser; + + $traverser->addVisitor(new class($this->getClassReflector()->getNameContext()) extends NameResolver + { + public function __construct(NameContext $nameContext) + { + parent::__construct(); + $this->nameContext = $nameContext; + } + + public function beforeTraverse(array $nodes): ?array + { + return null; + } + }); + $traverser->addVisitor(new PhpDocResolver( + new FileNameResolver($this->getClassReflector()->getNameContext()), + )); + + $traverser->traverse([$node]); + + $this->properyNode = $node; + + return $node; + } + + public function getClassReflector(): ClassReflector + { + return ClassReflector::make($this->getReflection()->class); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Scope/GlobalScope.php b/vendor/dedoc/scramble/src/Infer/Scope/GlobalScope.php new file mode 100644 index 00000000..c8a5ed53 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Scope/GlobalScope.php @@ -0,0 +1,20 @@ +make(Index::class), // ??? + new NodeTypesResolver, + new ScopeContext, + new FileNameResolver(tap(new NameContext(new Throwing), fn (NameContext $nc) => $nc->startNamespace())), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Scope/Index.php b/vendor/dedoc/scramble/src/Infer/Scope/Index.php new file mode 100644 index 00000000..0ab090b2 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Scope/Index.php @@ -0,0 +1,98 @@ + + */ + public array $classesDefinitions = []; + + /** + * @var array + */ + public array $functionsDefinitions = []; + + public function getClass(string $className): ?ClassDefinition + { + if (isset($this->classesDefinitions[$className])) { + return $this->classesDefinitions[$className]; + } + + $reflection = $this->createClassReflection($className); + + if (! $reflection) { + return null; + } + + if (! Scramble::infer()->config->shouldAnalyzeAst($className)) { + return $this->classesDefinitions[$className] = (new ShallowClassReflectionDefinitionBuilder($this, $reflection))->build(); + } + + return (new ClassAnalyzer($this))->analyze($className); + } + + public function registerClassDefinition(ClassDefinition $classDefinition): void + { + $this->classesDefinitions[$classDefinition->name] = $classDefinition; + } + + public function getClassDefinition(string $className): ?ClassDefinition + { + return $this->classesDefinitions[$className] ?? null; + } + + public function registerFunctionDefinition(FunctionLikeDefinition $fnDefinition): void + { + $this->functionsDefinitions[$fnDefinition->type->name] = $fnDefinition; + } + + public function getFunctionDefinition(string $fnName): ?FunctionLikeDefinition + { + if (array_key_exists($fnName, $this->functionsDefinitions)) { + return $this->functionsDefinitions[$fnName]; + } + + /** @var \ReflectionFunction|null $reflection */ + $reflection = rescue(fn () => new \ReflectionFunction($fnName), report: false); + if (! $reflection) { + return null; + } + + return $this->functionsDefinitions[$fnName] = (new FunctionLikeReflectionDefinitionBuilder( + $fnName, + $reflection, + ))->build(); + } + + /** @return ?ReflectionClass */ + private function createClassReflection(string $className): ?ReflectionClass + { + try { + return new ReflectionClass($className); // @phpstan-ignore argument.type + } catch (ReflectionException) { + return null; + } + } + + public function getFunction(string $name): ?FunctionLikeDefinition + { + return $this->getFunctionDefinition($name); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Scope/LazyShallowReflectionIndex.php b/vendor/dedoc/scramble/src/Infer/Scope/LazyShallowReflectionIndex.php new file mode 100644 index 00000000..59edf3f4 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Scope/LazyShallowReflectionIndex.php @@ -0,0 +1,59 @@ + $classes + * @param array $functions + */ + public function __construct( + public array $classes = [], + public array $functions = [], + ) {} + + public function getClass(string $name): ?ClassDefinitionContract + { + if (isset($this->classes[$name])) { + return $this->classes[$name]; + } + + try { + $reflection = new ReflectionClass($name); + } catch (ReflectionException) { + return null; + } + + return $this->classes[$name] = (new LazyClassReflectionDefinitionBuilder($this, $reflection))->build(); + } + + public function getFunction(string $name): ?FunctionLikeDefinition + { + if (isset($this->functions[$name])) { + return $this->functions[$name]; + } + + try { + $reflection = new ReflectionFunction($name); + } catch (ReflectionException) { + return null; + } + + return $this->functions[$name] = (new FunctionLikeReflectionDefinitionBuilder($name, $reflection))->build(); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Scope/NodeTypesResolver.php b/vendor/dedoc/scramble/src/Infer/Scope/NodeTypesResolver.php new file mode 100644 index 00000000..3b57e11c --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Scope/NodeTypesResolver.php @@ -0,0 +1,36 @@ +nodeTypes); + } + + public function getType(Node $node): Type + { + $nodeId = spl_object_id($node); + + return $this->nodeTypes[$nodeId] ?? new UnknownType; + } + + public function setType(Node $node, Type $type): void + { + $nodeId = spl_object_id($node); + + $this->nodeTypes[$nodeId] = $type; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Scope/Scope.php b/vendor/dedoc/scramble/src/Infer/Scope/Scope.php new file mode 100644 index 00000000..8dd05ffb --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Scope/Scope.php @@ -0,0 +1,190 @@ + + */ + public array $variables = []; + + /** + * @internal + * + * @var Node\Expr\CallLike[] + */ + public array $calls = []; + + public function __construct( + public Index $index, + public NodeTypesResolver $nodeTypesResolver, + public ScopeContext $context, + public FileNameResolver $nameResolver, + public ?Scope $parentScope = null, + ) {} + + public function getType(Node $node): Type + { + return (new ExpressionTypeInferrer($this, $this->nodeTypesResolver))->infer( + expr: $node, + variableTypeGetter: fn ($expr) => $this->getVariableType($expr), + ); + } + + // @todo: Move to some helper, Scope should be passed as a dependency. + /** + * @param array $args + * @return array + */ + public function getArgsTypes(array $args) + { + return collect($args) + ->filter(fn ($arg) => $arg instanceof Node\Arg) + ->mapWithKeys(function (Node\Arg $arg, $index) { + $type = $this->getType($arg->value); + if ($parsedPhpDoc = $arg->getAttribute('parsedPhpDoc')) { + $type->setAttribute('docNode', $parsedPhpDoc); + } + + if (! $arg->unpack) { + return [$arg->name ? $arg->name->name : $index => $type]; + } + + if (! $type instanceof ArrayType && ! $type instanceof KeyedArrayType) { + return [$arg->name ? $arg->name->name : $index => $type]; // falling back, but not sure if we should. Maybe some DTO is needed to represent unpacked arg type? + } + + if ($type instanceof ArrayType) { + /* + * For example, when passing something that is array, but shape is unknown + * $a = foo(...array_keys($bar)); + */ + return [$arg->name ? $arg->name->name : $index => $type]; // falling back, but not sure if we should. Maybe some DTO is needed to represent unpacked arg type? + } + + return collect($type->items) + ->mapWithKeys(fn (ArrayItemType_ $item, $i) => [ + $item->key ?: $index + $i => $item->value, + ]) + ->all(); + }) + ->all(); + } + + public function setType(Node $node, Type $type): Type + { + $this->nodeTypesResolver->setType($node, $type); + + return $type; + } + + public function createChildScope(?ScopeContext $context = null): Scope + { + return new Scope( + $this->index, + $this->nodeTypesResolver, + $context ?: $this->context, + $this->nameResolver, + $this, + ); + } + + /** @return TemplateType[] */ + public function getContextTemplates(): array + { + return [ + ...($this->classDefinition()?->templateTypes ?: []), + ...($this->functionDefinition()?->type->templates ?: []), + ...($this->parentScope?->getContextTemplates() ?: []), + ]; + } + + public function makeConflictFreeTemplateName(string $name): string + { + $scopeDuplicateTemplates = collect($this->getContextTemplates()) + ->pluck('name') + ->unique() + ->values() + ->filter(fn ($n) => preg_match('/^'.$name.'(\d*)?$/m', $n) === 1) // @phpstan-ignore argument.type + ->all(); + + return $name.($scopeDuplicateTemplates ? count($scopeDuplicateTemplates) : ''); + } + + /** @phpstan-assert-if-true !null $this->classDefinition() */ + public function isInClass(): bool + { + return (bool) $this->context->classDefinition; + } + + public function classDefinition(): ?ClassDefinition + { + return $this->context->classDefinition; + } + + public function functionDefinition(): ?FunctionLikeDefinition + { + return $this->context->functionDefinition; + } + + /** @phpstan-assert-if-true !null $this->functionDefinition() */ + public function isInFunction(): bool + { + return (bool) $this->context->functionDefinition; + } + + public function addVariableType(int $line, string $name, Type $type): void + { + if (! isset($this->variables[$name])) { + $this->variables[$name] = []; + } + + $this->variables[$name][] = compact('line', 'type'); + } + + private function getVariableType(Node\Expr\Variable $node): Type + { + if (! is_string($node->name)) { + return new UnknownType('Cannot infer type of variable: non-string variable name not supported yet.'); + } + + $line = $node->getAttribute('startLine', 0); + + $definitions = $this->variables[$node->name] ?? []; + + $type = new UnknownType; + foreach ($definitions as $definition) { + if ($definition['line'] > $line) { + return $type; + } + $type = $definition['type']; + } + + return $type; + } + + /** + * @internal + * + * @return Node\Expr\CallLike[] + */ + public function getMethodCalls(): array + { + return $this->calls; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Scope/ScopeContext.php b/vendor/dedoc/scramble/src/Infer/Scope/ScopeContext.php new file mode 100644 index 00000000..80cb9b5d --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Scope/ScopeContext.php @@ -0,0 +1,28 @@ +classDefinition = $classDefinition; + + return $this; + } + + public function setFunctionDefinition(FunctionLikeDefinition $functionDefinition) + { + $this->functionDefinition = $functionDefinition; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/ConstFetchTypeGetter.php b/vendor/dedoc/scramble/src/Infer/Services/ConstFetchTypeGetter.php new file mode 100644 index 00000000..97e6da2c --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/ConstFetchTypeGetter.php @@ -0,0 +1,40 @@ +getValue(); + + if ($constantReflection->isEnumCase()) { + return new EnumCaseType($className, $constName); + } + + $type = TypeHelper::createTypeFromValue($constantValue); + + if ($type) { + return $type; + } + } catch (\ReflectionException $e) { + return new UnknownType('Cannot get const value'); + } + + return new UnknownType('ConstFetchTypeGetter is not yet implemented fully for non-class const fetches.'); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/FileNameResolver.php b/vendor/dedoc/scramble/src/Infer/Services/FileNameResolver.php new file mode 100644 index 00000000..b28c4b9d --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/FileNameResolver.php @@ -0,0 +1,71 @@ +parseContent($code)->getStatements(); + + $traverser = new NodeTraverser; + $traverser->addVisitor($nameResolver = new NameResolver); + $traverser->traverse($nodes); + + return new self(static::$nameContextCache[$fileName] = $nameResolver->getNameContext()); + } + + public function __invoke(string $shortName): string + { + // If the name is prefixed with `\\`, it is already FQN + $name = str_starts_with($shortName, '\\') + ? ltrim($shortName, '\\') + : $this->nameContext->getResolvedName(new Name([$shortName]), Use_::TYPE_NORMAL)->toString(); + + $classLikeExists = class_exists($name) + || interface_exists($name) + || trait_exists($name) + || enum_exists($name); + + // By definition, the returned class name here is FQN, so like *::class or get_class(*) + // invoking name resolver returns the class name without leading slash. + return ltrim($classLikeExists ? $name : $shortName, '\\'); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/FileParser.php b/vendor/dedoc/scramble/src/Infer/Services/FileParser.php new file mode 100644 index 00000000..6e385289 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/FileParser.php @@ -0,0 +1,40 @@ + + */ + private array $cache = []; + + private Parser $parser; + + public function __construct(Parser $parser) + { + $this->parser = $parser; + } + + public static function getInstance(): static + { + return app(static::class); + } + + public function parseContent(string $content): FileParserResult + { + return $this->cache[md5($content)] ??= new FileParserResult( + $statements = Arr::wrap($this->parser->parse($content)), + new FileNameResolver(tap(new NameContext(new Throwing), fn (NameContext $nc) => $nc->startNamespace())) + ); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/FileParserResult.php b/vendor/dedoc/scramble/src/Infer/Services/FileParserResult.php new file mode 100644 index 00000000..8d1b4a12 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/FileParserResult.php @@ -0,0 +1,47 @@ +nameResolver; + } + + public function getStatements(): array + { + return $this->statements; + } + + public function findFirstClass(string $class) + { + return (new NodeFinder)->findFirst( + $this->getStatements(), + fn (Node $node) => $node instanceof Node\Stmt\Class_ + && ($node->namespacedName ?? $node->name)->toString() === ltrim($class, '\\'), + ); + } + + public function findMethod(string $classMethod) + { + [$class, $method] = explode('@', $classMethod); + + $classAst = $this->findFirstClass($class); + + return (new NodeFinder) + ->findFirst( + Arr::wrap($classAst), + fn (Node $node) => $node instanceof Node\Stmt\ClassMethod && $node->name->name === $method, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/KeyedArrayUnpackingTypeVisitor.php b/vendor/dedoc/scramble/src/Infer/Services/KeyedArrayUnpackingTypeVisitor.php new file mode 100644 index 00000000..a3eb2d61 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/KeyedArrayUnpackingTypeVisitor.php @@ -0,0 +1,20 @@ +isResolvable()) { + return $type->resolve(); + } + + if ($newType = Context::getInstance()->extensionsBroker->getResolvedType(new ReferenceResolutionEvent($type))) { + return $newType; + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/RecursionGuard.php b/vendor/dedoc/scramble/src/Infer/Services/RecursionGuard.php new file mode 100644 index 00000000..0ea0605f --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/RecursionGuard.php @@ -0,0 +1,40 @@ +callIdsMap)) { + return $onInfiniteRecursion(); + } + + try { + $this->callIdsMap[$id] = true; + + return $callback(); + } finally { + unset($this->callIdsMap[$id]); + } + } + + public static function run($obj, callable $callback, callable $onInfiniteRecursion) + { + if (array_key_exists($id = spl_object_id($obj), static::$callIds)) { + return $onInfiniteRecursion(); + } + + try { + static::$callIds[$id] = true; + + return $callback(); + } finally { + unset(static::$callIds[$id]); + } + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/ReferenceTypeResolver.php b/vendor/dedoc/scramble/src/Infer/Services/ReferenceTypeResolver.php new file mode 100644 index 00000000..0aea33ca --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/ReferenceTypeResolver.php @@ -0,0 +1,669 @@ + (new TypeWalker)->map($type, fn (Type $t) => $this->doResolve($t, $type, $scope)), + onInfiniteRecursion: fn () => new UnknownType('really bad self reference'), + ); + + // Type finalization: removing duplicates from union, unpacking array items (inside `replace`), calling resolving extensions. + return $this->finalizeType($resolvedType, $originalType); + } + + private function doResolve(Type $t, Type $type, Scope $scope): Type + { + $resolved = match ($t::class) { + ConstFetchReferenceType::class => $this->resolveConstFetchReferenceType($scope, $t), + MethodCallReferenceType::class => $this->resolveMethodCallReferenceType($scope, $t), + StaticMethodCallReferenceType::class => $this->resolveStaticMethodCallReferenceType($scope, $t), + CallableCallReferenceType::class => $this->resolveCallableCallReferenceType($scope, $t), + NewCallReferenceType::class => $this->resolveNewCallReferenceType($scope, $t), + PropertyFetchReferenceType::class => $this->resolvePropertyFetchReferenceType($scope, $t), + PotentialMethodMutatingCallType::class => $this->resolvePotentialMethodMutatingCallType($scope, $t), + default => null, + }; + + if (! $resolved) { + return $t; + } + + if ($resolved === $type || $resolved === $t) { + return new UnknownType('self reference'); + } + + return $resolved; + } + + /** + * When a parent method returns `static`, chained calls on that value must still resolve + * methods on the current class (late static binding), without widening the return type. + */ + private function resolveStaticCalleeForMethodLookup(Scope $scope, Type $calleeType): Type + { + if ( + $calleeType instanceof ObjectType + && $calleeType->name === StaticReference::STATIC + && $scope->context->classDefinition + ) { + return new SelfType($scope->context->classDefinition->name); + } + + return $calleeType; + } + + private function finalizeStatic(Type $type, Type $staticType): Type + { + return (new TypeWalker)->map($type, function (Type $t) use ($staticType) { + if ($t instanceof Generic && $staticType instanceof ObjectType && $t->name === StaticReference::STATIC) { + $t->name = $staticType->name; + + return $t; + } + + if ($staticType instanceof ObjectType && $t instanceof ObjectType && $t->name === StaticReference::SELF) { + $t->name = $staticType->name; + + return $t; + } + + return $t instanceof ObjectType && $t->name === StaticReference::STATIC ? $staticType : $t; + }); + } + + private function finalizeSelf(Type $type, Type $concreteSelfType): Type + { + return (new TypeWalker)->map($type, function (Type $t) use ($concreteSelfType) { + return $t instanceof SelfType ? $concreteSelfType : $t; + }); + } + + /** + * Replaces `SelfType` to `$calledOnType` only if it is coming from PHPDoc annotation (empty name). For now this + * happens only when argument type is augmented by {@see TemplateTypesSolver::addContextTypesToTypelessParametersOfCallableArgument}. + */ + private function finalizeSelfForCallableArguments(Type $type, Type $calledOnType): Type + { + return (new TypeWalker)->map($type, function (Type $t) use ($calledOnType) { + if (! $t instanceof SelfType || $t->name !== '') { + return $t; + } + + return $calledOnType; + }); + } + + private function finalizeType(Type $type, Type $originalType): Type + { + $attributes = $type->attributes(); + + $traverser = new TypeTraverser([ + new KeyedArrayUnpackingTypeVisitor, + new LateTypeResolvingTypeVisitor, + new UnionNormalizingTypeVisitor, + ]); + + return $traverser + ->traverse($type) + ->mergeAttributes($attributes) + ->setOriginal($originalType); + } + + private function resolveConstFetchReferenceType(Scope $scope, ConstFetchReferenceType $type): Type + { + $contextualCalleeName = $type->callee; + + if ($contextualCalleeName instanceof StaticReference) { + $contextualCalleeName = static::resolveClassName($scope, $contextualCalleeName->keyword); + + // This can only happen if any of static reserved keyword used in non-class context – hence considering not possible for now. + if (! $contextualCalleeName) { + return new UnknownType("Cannot properly analyze [{$type->toString()}] reference type as static keyword used in non-class context, or current class scope has no parent."); + } + } + + return (new ConstFetchTypeGetter)($scope, $contextualCalleeName, $type->constName); + } + + private function resolveMethodCallReferenceType(Scope $scope, MethodCallReferenceType $type): Type + { + $calleeType = $this->resolveAndNormalizeCallee($scope, $type->callee); + $arguments = new AutoResolvingArgumentTypeBag($scope, $type->arguments); + + $calleeAllTypes = $calleeType instanceof Union + ? $calleeType->types + : [$calleeType]; + + return Union::wrap(array_map(function (Type $calleeType) use ($scope, $type, $arguments) { + $calleeType = $this->resolveStaticCalleeForMethodLookup($scope, $calleeType); + + $classDefinition = $calleeType instanceof ObjectType + ? $this->index->getClass($calleeType->name) + : null; + + if (! ($calleeType instanceof TemplateType) && $returnType = Context::getInstance()->extensionsBroker->getAnyMethodReturnType(new AnyMethodCallEvent( + instance: $calleeType, + name: $type->methodName, + scope: $scope, + arguments: $arguments, + methodDefiningClassName: $classDefinition + ? $classDefinition->getMethodDefiningClassName($type->methodName, $scope->index) + : ($calleeType instanceof ObjectType ? $calleeType->name : null), + ))) { + return $this->finalizeStatic($returnType, $calleeType); + } + + if ($calleeType instanceof MixedType) { + return new UnknownType; + } + if ( + ! $calleeType instanceof ObjectType + && ! $calleeType instanceof UnknownType + && ! $calleeType instanceof TemplateType + ) { + return new NeverType; + } + if (! $calleeType instanceof ObjectType) { + return new UnknownType; + } + + if ($returnType = Context::getInstance()->extensionsBroker->getMethodReturnType(new MethodCallEvent( + instance: $calleeType, + name: $type->methodName, + scope: $scope, + arguments: $arguments, + methodDefiningClassName: $classDefinition ? $classDefinition->getMethodDefiningClassName($type->methodName, $scope->index) : $calleeType->name, + ))) { + return $this->finalizeStatic($returnType, $calleeType); + } + + if (! $classDefinition) { + return new UnknownType; + } + + if (! $methodDefinition = $calleeType->getMethodDefinition($type->methodName, $scope)) { + return new NeverType; + } + + $resultingType = $this->getFunctionCallResult($methodDefinition, new AutoResolvingArgumentTypeBag($scope, $type->arguments), $calleeType); + + if ($calleeType instanceof SelfType) { + return $resultingType; + } + + // @todo resolve template type? + $resultingType = $resultingType instanceof TemplateType + ? ($resultingType->is ?: new UnknownType) + : $resultingType; + + return $this->finalizeStatic($resultingType, $calleeType); + }, $calleeAllTypes)); + } + + private function resolveStaticMethodCallReferenceType(Scope $scope, StaticMethodCallReferenceType $type): Type + { + if (! $contextualClassName = $this->resolveContextualClassName($scope, $type->callee)) { + return new UnknownType; + } + + $arguments = new AutoResolvingArgumentTypeBag($scope, $type->arguments); + + $isStaticCall = ! in_array($type->callee, StaticReference::KEYWORDS) + || (in_array($type->callee, StaticReference::KEYWORDS) && $scope->context->functionDefinition?->isStatic); + + // Assuming callee here can be only string of known name. Reality is more complex than + // that, but it is fine for now. + + // Attempting extensions broker before potentially giving up on type inference + if ($isStaticCall && $returnType = Context::getInstance()->extensionsBroker->getStaticMethodReturnType(new StaticMethodCallEvent( + callee: $contextualClassName, + name: $type->methodName, + scope: $scope, + arguments: $arguments, + ))) { + return $returnType; + } + + // Attempting extensions broker before potentially giving up on type inference + if (! $isStaticCall && $scope->context->classDefinition) { + $definingMethodName = ($definingClass = $scope->index->getClass($contextualClassName)) + ? $definingClass->getMethodDefiningClassName($type->methodName, $scope->index) + : $contextualClassName; + + $returnType = Context::getInstance()->extensionsBroker->getMethodReturnType(new MethodCallEvent( + instance: new SelfType($scope->context->classDefinition->name), + name: $type->methodName, + scope: $scope, + arguments: $arguments, + methodDefiningClassName: $definingMethodName, + )); + + if ($returnType) { + return $returnType; + } + } + + if (! $calleeDefinition = $this->index->getClass($contextualClassName)) { + return new UnknownType; + } + + if (! $methodDefinition = $calleeDefinition->getMethodDefinition($type->methodName, $scope)) { + return new NeverType; + } + + return $this->finalizeStatic( + $this->getFunctionCallResult($methodDefinition, $arguments), + new ObjectType($contextualClassName), // @todo Generic can be here. + ); + } + + private function resolveCallableCallReferenceType(Scope $scope, CallableCallReferenceType $type): Type + { + $callee = $this->resolveAndNormalizeCallee($scope, $type->callee); + $arguments = new AutoResolvingArgumentTypeBag($scope, $type->arguments); + + $calleeAllTypes = $callee instanceof Union + ? $callee->types + : [$callee]; + + return Union::wrap(array_map(function (Type $callee) use ($scope, $type, $arguments) { + /* + * String can be callable, and object with `__invoke` can be callable as well. + */ + if ( + $callee instanceof IntegerType + || $callee instanceof FloatType + || $callee instanceof BooleanType + ) { + return new NeverType; + } + + if ($callee instanceof CallableStringType) { + $returnType = Context::getInstance()->extensionsBroker->getFunctionReturnType(new FunctionCallEvent( + name: $callee->name, + scope: $scope, + arguments: $arguments, + )); + + if ($returnType) { + return $returnType; + } + } + + if ($callee instanceof ObjectType) { + return $this->resolve( + $scope, + new MethodCallReferenceType($callee, '__invoke', $type->arguments), + ); + } + + $calleeType = $callee instanceof CallableStringType + ? $this->index->getFunctionDefinition($callee->name) + : $callee; + + if (! $calleeType) { + return new UnknownType; + } + + if ($calleeType instanceof FunctionType) { // When resolving into a closure. + $calleeType = new FunctionLikeDefinition($calleeType); + } + + // @todo: callee now can be either in index or not, add support for other cases. + if (! $calleeType instanceof FunctionLikeDefinition) { + return new UnknownType; + } + + return $this->getFunctionCallResult($calleeType, $arguments); + }, $calleeAllTypes)); + } + + private function resolveNewCallReferenceType(Scope $scope, NewCallReferenceType $type): Type + { + if (! $contextualClassName = $this->resolveContextualClassName($scope, $type->name)) { + return new UnknownType; + } + + $arguments = new AutoResolvingArgumentTypeBag($scope, $type->arguments); + + if (! $classDefinition = $this->index->getClass($contextualClassName)) { + /* + * Usually in this case we want to return UnknownType. But we certainly know that using `new` will produce + * an object of a type being created. + */ + return new ObjectType($contextualClassName); + } + + $typeBeingConstructed = ! $classDefinition->templateTypes + ? new ObjectType($contextualClassName) + : new Generic($contextualClassName, array_map(fn () => new MixedType, $classDefinition->templateTypes)); + + if (Context::getInstance()->extensionsBroker->getMethodReturnType(new MethodCallEvent( + instance: $typeBeingConstructed, + name: '__construct', + scope: $scope, + arguments: $arguments, + methodDefiningClassName: $contextualClassName, + )) instanceof VoidType) { + return $typeBeingConstructed; + } + + if (! $classDefinition->templateTypes) { + return new ObjectType($contextualClassName); + } + + $propertyDefaultTemplateTypes = (new TemplateTypesSolver) + ->inferTemplatesFromPropertyDefaults( + $classDefinition->templateTypes, + $classDefinition->properties, + ); + + $constructorDefinition = $classDefinition->getMethodDefinition('__construct', $scope); + + $templatesMap = (new TemplateTypesSolver) + ->getClassConstructorContextTemplates( + $classDefinition, + $constructorDefinition, + new AutoResolvingArgumentTypeBag($scope, $type->arguments), + ) + ->prepend($propertyDefaultTemplateTypes); + + $resultingTemplatesMap = (new TemplateTypesSolver) + ->getGenericCreationTemplatesWithDefaults($classDefinition->templateTypes, $templatesMap); + + $resultingTemplatesMap = $this->applySelfOutType( + $resultingTemplatesMap, + $constructorDefinition?->getSelfOutType(), + $templatesMap, + ); + + return new Generic($classDefinition->name, $resultingTemplatesMap); + } + + private function resolvePotentialMethodMutatingCallType(Scope $scope, PotentialMethodMutatingCallType $type): Type + { + $callee = $this->resolveAndNormalizeCallee($scope, $type->callee); + $arguments = new AutoResolvingArgumentTypeBag($scope, $type->arguments); + + $calleeAllTypes = $callee instanceof Union + ? $callee->types + : [$callee]; + + return Union::wrap(array_map(function (Type $callee) use ($scope, $type, $arguments) { + $callee = $this->resolveStaticCalleeForMethodLookup($scope, $callee); + + if (! $callee instanceof Generic) { + return $callee; + } + + if (! $calleeDefinition = $this->index->getClass($callee->name)) { + return $callee; + } + + if (! $methodDefinition = $calleeDefinition->getMethodDefinition($type->methodName, $scope)) { + return $callee; + } + + $selfOutType = $methodDefinition->getSelfOutType(); + if (! $selfOutType instanceof Generic) { + return $callee; + } + + $classContextTemplates = (new TemplateTypesSolver)->getClassContextTemplates($callee, $calleeDefinition); + + $arguments = $arguments->map(fn (Type $t, string|int $key) => (new TemplateTypesSolver)->addContextTypesToTypelessParametersOfCallableArgument( + $t, + $key, + $methodDefinition, + $classContextTemplates, + )); + + $arguments = $arguments->map(fn (Type $argType) => $this->finalizeSelfForCallableArguments($argType, $callee)); + + $templatesMap = (new TemplateTypesSolver) + ->getFunctionContextTemplates($methodDefinition, $arguments) + ->prepend($classContextTemplates); + + $newTemplateTypes = $this->applySelfOutType( + [...$callee->templateTypes], + $selfOutType, + $templatesMap, + ); + + return new Generic($callee->name, $newTemplateTypes); + }, $calleeAllTypes)); + } + + private function resolvePropertyFetchReferenceType(Scope $scope, PropertyFetchReferenceType $type): Type + { + $objectType = $this->resolveAndNormalizeCallee($scope, $type->object); + $objectAllTypes = $objectType instanceof Union + ? $objectType->types + : [$objectType]; + + return Union::wrap(array_map(function (Type $objectType) use ($scope, $type) { + $objectType = $this->resolveStaticCalleeForMethodLookup($scope, $objectType); + + if ($objectType instanceof MixedType) { + return new UnknownType; + } + if ( + ! $objectType instanceof ObjectType + && ! $objectType instanceof UnknownType + && ! $objectType instanceof TemplateType + ) { + return new NeverType; + } + if (! $objectType instanceof ObjectType) { + return new UnknownType; + } + + if ($propertyType = Context::getInstance()->extensionsBroker->getPropertyType(new PropertyFetchEvent( + instance: $objectType, + name: $type->propertyName, + scope: $scope, + ))) { + return $propertyType; + } + + $classDefinition = $this->index->getClass($objectType->name); + + if (! $classDefinition) { + return new UnknownType("Cannot get property [$type->propertyName] type on [$objectType->name]"); + } + + $propertyType = $objectType->getPropertyType($type->propertyName, $scope); + + if ($objectType instanceof SelfType) { + return $propertyType; + } + + // @todo resolve template type? + return $propertyType instanceof TemplateType + ? ($propertyType->is ?: new UnknownType) + : $propertyType; + }, $objectAllTypes)); + } + + /** + * Prepares the type of the value a method will be called on or a property will be fetched on. This includes + * resolving the reference type and using the lower bound of if the callee is a template type. + */ + private function resolveAndNormalizeCallee(Scope $scope, Type $callee): Type + { + $resolved = $this->resolve($scope, $callee); + + if ($resolved instanceof TemplateType && $resolved->is) { + return $resolved->is; + } + + return $resolved; + } + + /** + * Resolves the name of the type for static context (when creating new objects and calling static methods): + * For example: new static/new Object/static::X/Object::X/('Object')::X + */ + private function resolveContextualClassName(Scope $scope, Type|string $type): ?string + { + if ($type instanceof Type) { + $resolvedNameType = $this->resolve($scope, $type); + + if ($resolvedNameType instanceof LiteralStringType) { + return $resolvedNameType->value; + } + + return null; + } + + return $this->resolveClassName($scope, $type); + } + + /** + * @param Type[] $resultingTemplatesMap + * @return Type[] + */ + private function applySelfOutType(array $resultingTemplatesMap, ?Type $selfOutType, TemplatesMap $inferredTemplates): array + { + if (! $selfOutType instanceof Generic) { + return $resultingTemplatesMap; + } + + foreach ($selfOutType->templateTypes as $index => $genericSelfOutTypePart) { + if ($genericSelfOutTypePart instanceof TemplatePlaceholderType) { + continue; + } + + $resultingTemplatesMap[$index] = (new TypeWalker)->map( + $genericSelfOutTypePart, + fn ($t) => $t instanceof TemplateType ? $inferredTemplates->get($t->name, $t) : $t, + ); + } + + return $resultingTemplatesMap; + } + + private function getFunctionCallResult( + FunctionLikeDefinition $callee, + ArgumentTypeBag $arguments, + /* When this is a handling for method call */ + ObjectType|SelfType|null $calledOnType = null, + ): Type { + $classDefinition = $calledOnType instanceof ObjectType ? $this->index->getClass($calledOnType->name) : null; + + $classContextTemplates = $calledOnType && $classDefinition + ? (new TemplateTypesSolver)->getClassContextTemplates($calledOnType, $classDefinition) + : []; + + $arguments = $arguments + ->map(fn ($t, $nameOrPosition) => (new TemplateTypesSolver)->addContextTypesToTypelessParametersOfCallableArgument( + $t, + $nameOrPosition, + $callee, + $classContextTemplates, + )); + + $returnType = $callee->getReturnType(); + + /* + * Return type: PhpDoc `self` / `$this` placeholders bind to the method receiver (`$calledOnType`). + * Arguments: same for PhpDoc placeholders (`SelfType` with empty name); expression-inferred `$this` + * already uses `SelfType` with the lexical class name and must not be overwritten by the receiver. + */ + if ($calledOnType) { + $returnType = $this->finalizeSelf($returnType, $calledOnType); + + $arguments = $arguments->map(fn ($argType) => $this->finalizeSelfForCallableArguments($argType, $calledOnType)); + } + + $templatesMap = (new TemplateTypesSolver) + ->getFunctionContextTemplates($callee, $arguments) + ->prepend($classContextTemplates); + + $returnType = (new TypeWalker)->map( + $returnType, + fn (Type $t) => $t instanceof TemplateType ? $templatesMap->get($t->name, $t) : $t, + ); + + if ($returnType instanceof Generic && ($selfOutType = $callee->getSelfOutType())) { + $returnType->templateTypes = $this->applySelfOutType( + $returnType->templateTypes, + $selfOutType, + $templatesMap, + ); + } + + // void (unresolved) template types that are still present in the type, as this is probably an error + // @todo maybe better way to handle? just replacing to unknown (even taking is into account breaks a LOT, these templates will probably make sense later) + + return $returnType; + } + + public static function resolveClassName(Scope $scope, string $name): ?string + { + if (! in_array($name, StaticReference::KEYWORDS)) { + return $name; + } + + return match ($name) { + StaticReference::SELF => $scope->context->functionDefinition?->definingClassName, + StaticReference::STATIC => $scope->context->classDefinition?->name, + StaticReference::PARENT => $scope->context->classDefinition?->parentFqn, + }; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/ShallowTypeResolver.php b/vendor/dedoc/scramble/src/Infer/Services/ShallowTypeResolver.php new file mode 100644 index 00000000..e3606957 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/ShallowTypeResolver.php @@ -0,0 +1,236 @@ +map( + $type, + fn ($t) => $this->doResolve($scope, $t), + function (Type $t) { + $nodes = $t->nodes(); + /* + * When mapping function type, we don't want to affect arguments of the function types, just the return type. + */ + if ($t instanceof FunctionType) { + return []; + } + + return $nodes; + }, + ); + } + + private function doResolve(Scope $scope, Type $type): Type + { + return match (true) { + $type instanceof SelfType => $type, + $type instanceof TemplateType => $type->is ?: new UnknownType, + $type instanceof PropertyFetchReferenceType => $this->resolvePropertyFetchReferenceType($scope, $type), + $type instanceof MethodCallReferenceType => $this->resolveMethodCallReferenceType($scope, $type), + $type instanceof StaticMethodCallReferenceType => $this->resolveStaticMethodCallReferenceType($scope, $type), + $type instanceof NewCallReferenceType => $this->resolveNewCallReferenceType($scope, $type), + $type instanceof ConstFetchReferenceType => $this->resolveConstFetchReferenceType($scope, $type), + $type instanceof CallableCallReferenceType => $this->resolveCallableCallReferenceType($scope, $type), + $type instanceof AbstractReferenceType => new UnknownType($type::class.' reference type is not handled '.$type->toString()), + default => $type, + }; + } + + private function resolvePropertyFetchReferenceType(Scope $scope, PropertyFetchReferenceType $type): Type + { + $callee = $this->resolve($scope, $type->object); + if (! $callee instanceof ObjectType) { + return new UnknownType('fetching a property on a non-object'); + } + + $definition = $this->index->getClass($callee->name); + if (! $definition) { + return new UnknownType('cannot find a definition of '.$callee->name); + } + + $propertyDefinition = $definition->getData()->properties[$type->propertyName] ?? null; + if (! $propertyDefinition) { + return new UnknownType("property [{$type->propertyName}] is not found on object [{$callee->name}]"); + } + + $propertyType = $propertyDefinition->type ?: $propertyDefinition->defaultType; + if ($propertyType instanceof TemplateType) { + $propertyType = $propertyType->is; + } + + if (! $propertyType) { + return new MixedType; + } + + return $propertyType; + } + + private function resolveMethodCallReferenceType(Scope $scope, MethodCallReferenceType $type): Type + { + $callee = $this->resolve($scope, $type->callee); + if (! $callee instanceof ObjectType) { + return new UnknownType('calling a method on a non-object'); + } + + $definition = $this->index->getClass($callee->name); + if (! $definition) { + return new UnknownType('cannot find a definition of '.$callee->name); + } + + $methodDefinition = $definition->getMethod($type->methodName); + if (! $methodDefinition) { + return new UnknownType("method [{$type->methodName}] is not found on object [{$callee->name}]"); + } + + return $this->resolveStaticBinding($definition->getData(), $methodDefinition, $methodDefinition->type->returnType); + } + + private function resolveNewCallReferenceType(Scope $scope, NewCallReferenceType $type): Type + { + $class = $type->name instanceof Type + ? $this->resolve($scope, $type->name) + : $type->name; + + if ($class instanceof LiteralStringType) { + $class = $class->value; + } + + if (! is_string($class)) { + return new UnknownType; + } + + $definition = $this->index->getClass($class); + if (! $definition) { + return new UnknownType('cannot find a definition of '.$class); + } + + return new ObjectType($class); + } + + private function resolveConstFetchReferenceType(Scope $scope, ConstFetchReferenceType $type): Type + { + $callee = $type->callee; + + if ($type->callee instanceof StaticReference) { + $contextualCalleeName = match ($type->callee->keyword) { + StaticReference::SELF => $scope->context->functionDefinition?->definingClassName, + StaticReference::STATIC => $scope->context->classDefinition?->name, + StaticReference::PARENT => $scope->context->classDefinition?->parentFqn, + }; + + // This can only happen if any of static reserved keyword used in non-class context – hence considering not possible for now. + if (! $contextualCalleeName) { + return new UnknownType("Cannot properly analyze [{$type->toString()}] reference type as static keyword used in non-class context, or current class scope has no parent."); + } + + $callee = $contextualCalleeName; + } + + return (new ConstFetchTypeGetter)($scope, $callee, $type->constName); + } + + private function resolveCallableCallReferenceType(Scope $scope, CallableCallReferenceType $type): Type + { + $callee = $this->resolve($scope, $type->callee); + + $function = match ($callee::class) { + CallableStringType::class => $callee->name, + LiteralStringType::class => $callee->value, + ObjectType::class => $callee, + default => null, + }; + + if (! $function) { + return new UnknownType; + } + + $functionDefinition = is_string($function) + ? $this->index->getFunction($function) + : $this->index->getClass($function->name)->getMethod('__invoke'); + + if (! $functionDefinition) { + return new UnknownType('cannot find a function definition of '.$callee->toString()); + } + + return $functionDefinition->type->returnType; + } + + private function resolveStaticMethodCallReferenceType(Scope $scope, StaticMethodCallReferenceType $type): Type + { + $class = $type->callee instanceof Type + ? $this->resolve($scope, $type->callee) + : $type->callee; + + if ($class instanceof LiteralStringType) { + $class = $class->value; + } + + if (! is_string($class)) { + return new UnknownType; + } + + $definition = $this->index->getClass($class); + if (! $definition) { + return new UnknownType('cannot find a definition of '.$class); + } + + $methodDefinition = $definition->getMethod($type->methodName); + if (! $methodDefinition) { + return new UnknownType("method [{$type->methodName}] is not found on object [{$class}]"); + } + + return $this->resolveStaticBinding($definition->getData(), $methodDefinition, $methodDefinition->type->returnType); + } + + /** + * When passed an object with `self`, `static`, or `parent` name resolves the correct class name. + */ + private function resolveStaticBinding( + ClassDefinition $classDefinitionData, + FunctionLikeDefinition $methodDefinition, + Type $type, + ): Type { + if (! $type instanceof ObjectType) { + return $type; + } + + return match ($type->name) { + StaticReference::SELF => tap(clone $type, fn ($t) => $t->name = $methodDefinition->definingClassName ?: $classDefinitionData->name), + StaticReference::STATIC => tap(clone $type, fn ($t) => $t->name = $classDefinitionData->name), + StaticReference::PARENT => $classDefinitionData->parentFqn + ? tap(clone $type, fn ($t) => $t->name = $classDefinitionData->parentFqn) + : new UnknownType, + default => $type, + }; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/TemplateTypesSolver.php b/vendor/dedoc/scramble/src/Infer/Services/TemplateTypesSolver.php new file mode 100644 index 00000000..29df1653 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/TemplateTypesSolver.php @@ -0,0 +1,209 @@ + */ + public function getClassContextTemplates(ObjectType $type, ClassDefinition $classDefinition): array + { + if (! $type instanceof Generic) { + return []; + } + + return collect($classDefinition->templateTypes)->mapWithKeys(fn ($t, $index) => [ + $t->name => $type->templateTypes[$index] ?? new UnknownType, + ])->all(); + } + + public function getFunctionContextTemplates(FunctionLikeDefinition $functionLikeDefinition, ArgumentTypeBag $arguments): TemplatesMap + { + return new TemplatesMap( + templates: $functionLikeDefinition->type->templates, + parameters: $functionLikeDefinition->type->arguments, + arguments: $arguments, + defaults: $functionLikeDefinition->argumentsDefaults, + ); + } + + public function getClassConstructorContextTemplates(ClassDefinition $classDefinition, ?FunctionLikeDefinition $functionLikeDefinition, ArgumentTypeBag $arguments): TemplatesMap + { + return new TemplatesMap( + templates: ($functionLikeDefinition->type->templates ?? []) + $classDefinition->templateTypes, + parameters: $functionLikeDefinition->type->arguments ?? [], + arguments: $arguments, + defaults: $functionLikeDefinition->argumentsDefaults ?? [], + ); + } + + /** + * @param TemplateType[] $templateTypes + * @return Type[] + */ + public function getGenericCreationTemplatesWithDefaults(array $templateTypes, TemplatesMap $templatesMap): array + { + $mappedTypes = collect($templateTypes) + ->map(function (TemplateType $t) use ($templatesMap) { + $type = $templatesMap->get($t->name, new MissingType); + + if ($type instanceof MissingType) { + return $t->default ? $type : new UnknownType; + } + + return $type; + }) + ->all(); + + $nonMissingTypeSeen = false; + foreach (array_reverse($mappedTypes, preserve_keys: true) as $key => $type) { + if (! $type instanceof MissingType) { + $nonMissingTypeSeen = true; + } + + if ($nonMissingTypeSeen && $type instanceof MissingType) { + $mappedTypes[$key] = ($templateTypes[$key]->default ?? new UnknownType('Should have template default here but doesnt have for some reason')); + + continue; + } + + if ($type instanceof MissingType) { + unset($mappedTypes[$key]); + } + } + + return $mappedTypes; + } + + /** + * @param TemplateType[] $classTemplateTypes + * @param ClassPropertyDefinition[] $properties + * @return array The key is template name and the value is the inferred type. + */ + public function inferTemplatesFromPropertyDefaults(array $classTemplateTypes, array $properties): array + { + $inferredTemplates = []; + + foreach ($classTemplateTypes as $template) { + foreach ($properties as $property) { + if (! $property->defaultType) { + continue; + } + + if ($inferredType = $this->inferTemplate($template, $property->type, $property->defaultType)) { + $inferredTemplates[$template->name] = $inferredType; + + break; + } + } + + } + + return $inferredTemplates; + } + + private function inferTemplate(TemplateType $template, Type $typeWithTemplate, Type $type): ?Type + { + return (new RecursiveTemplateSolver)->solve($typeWithTemplate, $type, $template); + } + + /** + * @param array $templates + */ + public function addContextTypesToTypelessParametersOfCallableArgument( + Type $argument, + string|int $nameOrPosition, + FunctionLikeDefinition $definition, + array $templates, + ): Type { + if (! $argument instanceof FunctionType) { + return $argument; + } + + $correspondingParameterType = is_string($nameOrPosition) + ? ($definition->type->arguments[$nameOrPosition] ?? null) + : (array_values($definition->type->arguments)[$nameOrPosition] ?? null); + + // @todo + // This is an entire area for improvement: + // 1) what if multiple callables are there, + // 2) parameter structure MAY be complex! It is not really specific to function - it may be keyed array, + // etc - we still may need to contextualize things + // if parameter typed is union, we pick just the first function (this is far from accurate in case it is union of multiple functions (1)!) + $correspondingParameterType = collect($correspondingParameterType instanceof Union ? $correspondingParameterType->types : [$correspondingParameterType]) + ->first(fn ($t) => $t instanceof FunctionType); + + if (! $correspondingParameterType instanceof FunctionType) { + return $argument; + } + + $argument = $argument->clone(); + $replacedTemplates = []; + + $i = -1; + foreach ($argument->arguments as $name => $arg) { + $i++; + + if (! $arg instanceof TemplateType) { + continue; + } + + /** @var Type|null $inferredTypeForReplacement */ + $inferredTypeForReplacement = $correspondingParameterType->arguments[$i] ?? null; + if (! $inferredTypeForReplacement) { + continue; + } + + // Allow replacing object/array-typed params when the call-site context provides a compatible, + // more specific type (e.g. Collection), instead of always keeping the user's type. + $inferredConcreteTypeForReplacement = $inferredTypeForReplacement instanceof TemplateType && array_key_exists($inferredTypeForReplacement->name, $templates) + ? $templates[$inferredTypeForReplacement->name] + : null; + + $argShouldBeReplaced = ! $arg->is + || $arg->is instanceof ArrayType + || $arg->is instanceof ObjectType && ( + $inferredConcreteTypeForReplacement?->accepts($arg->is) || $inferredTypeForReplacement->accepts($arg->is) + ); + + $param = $argShouldBeReplaced ? $inferredTypeForReplacement : $arg->is; + + if (! $param) { // @phpstan-ignore booleanNot.alwaysFalse + continue; + } + + $replacedTemplates[$arg->name] = $param; + $argument->arguments[$name] = $param; + } + + $argument->templates = array_filter( + $argument->templates, + fn (TemplateType $tt) => ! array_key_exists($tt->name, $replacedTemplates), + ); + + $argument = (new TypeWalker)->map( + $argument, + fn ($t) => $t instanceof TemplateType ? $replacedTemplates[$t->name] ?? $t : $t, + ); + + return (new TypeWalker)->map( + $argument, + fn ($t) => $t instanceof TemplateType ? $templates[$t->name] ?? $t : $t, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/TemplatesMap.php b/vendor/dedoc/scramble/src/Infer/Services/TemplatesMap.php new file mode 100644 index 00000000..223ac761 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/TemplatesMap.php @@ -0,0 +1,117 @@ + */ + public array $additional = []; + + /** @var array */ + private array $cache = []; + + /** + * @param TemplateType[] $templates + * @param array $parameters + * @param array $defaults + */ + public function __construct( + public array $templates, + public array $parameters, + public ArgumentTypeBag $arguments, + public array $defaults, + ) {} + + /** + * @param array $additional + * @return $this + */ + public function prepend(array $additional): self + { + $this->additional = array_merge($additional, $this->additional); + + return $this; + } + + public function get(string $name, Type $defaultType = new UnknownType): Type + { + return $this->cache[$name] ??= $this->doGet($name, $defaultType); + } + + private function doGet(string $name, Type $defaultType = new UnknownType): Type + { + if ($name === self::ARGUMENTS) { + return new KeyedArrayType(collect($this->arguments->all())->map(fn ($t, $k) => new ArrayItemType_($k, $t))->all()); + } + + return $this->getSingle($name) ?? $this->additional[$name] ?? $defaultType; + } + + protected function getSingle(string $name): ?Type + { + $template = collect($this->templates)->first(fn (TemplateType $t) => $t->name === $name); + + if (! $template) { + return null; + } + + $inferredTypes = []; + foreach (array_values($this->parameters) as $i => $parameterType) { + if (! $this->hasTemplateIn($parameterType, $template)) { + continue; + } + + $name = array_keys($this->parameters)[$i]; + + $argumentType = $this->arguments->get($name, $i, $this->defaults[$name] ?? null) ?: new UnknownType; + + if ($inferredType = $this->inferTemplate($template, $parameterType, $argumentType)) { + $inferredTypes[] = $inferredType; + } + } + + return count($inferredTypes) ? Union::wrap($inferredTypes) : null; + } + + private function hasTemplateIn(Type $parameterType, TemplateType $templateType): bool + { + return (bool) (new TypeWalker)->first($parameterType, fn ($t) => $t === $templateType); + } + + private function hasTemplateInAnyParameter(string $name): bool + { + $template = collect($this->templates)->first(fn (TemplateType $t) => $t->name === $name); + + if (! $template) { + return false; + } + + return collect($this->parameters)->some(function ($pt) use ($template) { + return $this->hasTemplateIn($pt, $template); + }); + } + + public function has(string $name): bool + { + return $name === self::ARGUMENTS + || array_key_exists($name, $this->additional) + || $this->hasTemplateInAnyParameter($name); + } + + private function inferTemplate(TemplateType $template, Type $typeWithTemplate, Type $type): ?Type + { + return (new RecursiveTemplateSolver)->solve($typeWithTemplate, $type, $template); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Services/UnionNormalizingTypeVisitor.php b/vendor/dedoc/scramble/src/Infer/Services/UnionNormalizingTypeVisitor.php new file mode 100644 index 00000000..236f39e4 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Services/UnionNormalizingTypeVisitor.php @@ -0,0 +1,20 @@ +types)->widen(); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/BooleanNotTypeGetter.php b/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/BooleanNotTypeGetter.php new file mode 100644 index 00000000..f1aa8477 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/BooleanNotTypeGetter.php @@ -0,0 +1,15 @@ +name instanceof Node\Identifier && $node->name->toString() === 'class') { + if ($node->class instanceof Node\Name) { + if (in_array($node->class->toString(), StaticReference::KEYWORDS)) { + return new ConstFetchReferenceType( + new StaticReference($node->class->toString()), + $node->name->toString(), + ); + } + + return new GenericClassStringType(new ObjectType($node->class->toString())); + } + + $type = $scope->getType($node->class); + + if ( + ($type instanceof ObjectType || $type instanceof NewCallReferenceType) + && $className = $this->getClassName($type) + ) { + return new GenericClassStringType(new ObjectType($className)); + } + } + + if ( + $node->class instanceof Node\Name + && $node->name instanceof Node\Identifier + ) { + $className = in_array($node->class->toString(), StaticReference::KEYWORDS) + ? new StaticReference($node->class->toString()) + : $node->class->toString(); + + return new ConstFetchReferenceType( + $className, + $node->name->toString(), + ); + } + + // In case we're here, it means that we were unable to infer the type from the const fetch. So we rollback to the + // string type. + if ($node->name instanceof Node\Identifier && $node->name->toString() === 'class') { + return new StringType; + } + + return new UnknownType('Cannot get type from class const fetch'); + } + + private function getClassName(ObjectType|NewCallReferenceType $type): ?string + { + if ($type instanceof ObjectType) { + return $type->name; + } + + if (is_string($type->name)) { + return $type->name; + } + + $typeName = $type->name; + + if ($typeName instanceof LiteralStringType) { + return $typeName->value; + } + + if ($typeName instanceof TemplateType && $typeName->is instanceof ObjectType) { + return $typeName->is->name; + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ConstFetchTypeGetter.php b/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ConstFetchTypeGetter.php new file mode 100644 index 00000000..405fad00 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ConstFetchTypeGetter.php @@ -0,0 +1,29 @@ +name->toString() === 'null') { + return new NullType; + } + + if ($node->name->toString() === 'true') { + return new LiteralBooleanType(true); + } + + if ($node->name->toString() === 'false') { + return new LiteralBooleanType(false); + } + + return new UnknownType('Cannot get type from constant fetch'); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ScalarTypeGetter.php b/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ScalarTypeGetter.php new file mode 100644 index 00000000..01cfa2e6 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/SimpleTypeGetters/ScalarTypeGetter.php @@ -0,0 +1,30 @@ +value); + } + + if ($node instanceof Node\Scalar\LNumber) { + return new LiteralIntegerType($node->value); + } + + if ($node instanceof Node\Scalar\DNumber) { + return new LiteralFloatType($node->value); + } + + return new UnknownType('Cannot get type from scalar'); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/TypeInferer.php b/vendor/dedoc/scramble/src/Infer/TypeInferer.php new file mode 100644 index 00000000..36dabf73 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/TypeInferer.php @@ -0,0 +1,140 @@ +handlers = [ + new FunctionLikeHandler, + new AssignHandler, + new UnsetHandler, + new ClassHandler, + new PropertyHandler, + new ArrayHandler, + new ArrayItemHandler, + new ReturnHandler, + new ThrowHandler, + new ExpressionTypeInferringExtensions(array_values(array_filter( + $extensions, + fn ($ext) => $ext instanceof ExpressionTypeInferExtension, + ))), + new ExceptionInferringExtensions(array_values(array_filter( + $extensions, + fn ($ext) => $ext instanceof ExpressionExceptionExtension, + ))), + new PhpDocHandler, + ...$handlers, + ]; + + $this->scopes = new WeakMap; + } + + public function enterNode(Node $node) + { + $scope = $this->getOrCreateScope(); + + foreach ($this->handlers as $handler) { + if (! $handler->shouldHandle($node)) { + continue; + } + + if ($handler instanceof CreatesScope) { + $this->scope = $handler->createScope($scope, $node); + $this->scopes->offsetSet($node, $this->scope); + } + + if (method_exists($handler, 'enter')) { + $handler->enter($node, $this->scope); + } + } + + return null; + } + + public function leaveNode(Node $node) + { + if ($node instanceof Node\Expr\CallLike && $this->scope) { + $this->scope->calls[] = $node; + } + + $shouldLeaveScope = false; + + foreach ($this->handlers as $handler) { + if (! $handler->shouldHandle($node)) { + continue; + } + + if (method_exists($handler, 'leave')) { + $handler->leave($node, $this->scope); + } + + if ($handler instanceof CreatesScope) { + $shouldLeaveScope = true; + } + } + + if ($shouldLeaveScope) { + $this->scope = $this->scope->parentScope; + } + + return null; + } + + private function getOrCreateScope() + { + if (! isset($this->scope)) { + $this->scope = new Scope( + $this->index, + new NodeTypesResolver, + new ScopeContext, + $this->nameResolver, + ); + } + + return $this->scope; + } + + public function getFunctionLikeScope(?Node $node): ?Scope + { + return $node ? $this->scopes->offsetGet($node) : null; + } +} diff --git a/vendor/dedoc/scramble/src/Infer/UnresolvableArgumentTypeBag.php b/vendor/dedoc/scramble/src/Infer/UnresolvableArgumentTypeBag.php new file mode 100644 index 00000000..3e261951 --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/UnresolvableArgumentTypeBag.php @@ -0,0 +1,35 @@ + $arguments + */ + public function __construct(private array $arguments) {} + + public function get(string $name, int $position, ?Type $default = new UnknownType): ?Type + { + return $this->arguments[$name] ?? $this->arguments[$position] ?? $default; + } + + public function map(callable $cb): ArgumentTypeBag + { + return new self(collect($this->arguments)->map(fn ($t, $key) => $cb($t, $key))->all()); + } + + public function all(): array + { + return $this->arguments; + } + + public function count(): int + { + return count($this->arguments); + } +} diff --git a/vendor/dedoc/scramble/src/Infer/Visitors/PhpDocResolver.php b/vendor/dedoc/scramble/src/Infer/Visitors/PhpDocResolver.php new file mode 100644 index 00000000..654fd73d --- /dev/null +++ b/vendor/dedoc/scramble/src/Infer/Visitors/PhpDocResolver.php @@ -0,0 +1,72 @@ +nameResolver = $nameResolver; + } + + public function enterNode(Node $node) + { + if (! $node->getDocComment() && empty($node->getComments())) { + return null; + } + + $doc = $node->getDocComment(); + + if ( + ($node instanceof Expr\ArrayItem + || $node instanceof Node\Stmt\Return_) + && ! $doc + ) { + $docText = collect($node->getComments()) + ->map(fn (Comment $c) => $c->getReformattedText()) + ->join("\n"); + + $docText = (string) Str::of($docText)->replace(['//', ' * ', '/*', '*/'], '')->trim(); + + $doc = new Doc("/** $docText */"); + } + + if (! $doc) { + return null; + } + + $parsedDoc = $this->parseDocs($doc); + + $node->setAttribute('parsedPhpDoc', $parsedDoc); + + /* + * Parsed doc is propagated to the child expressions nodes, so it is easier for the consumer + * to get to the php doc when needed. For example, when some method call is annotated with phpdoc, + * we'd want to get this doc from the method call node, not an expression one. + */ + if ($node instanceof Node\Stmt\Expression) { + $node->expr->setAttribute('parsedPhpDoc', $parsedDoc); + } + + if ($node instanceof Node\Stmt\Expression && $node->expr instanceof Expr\Assign) { + $node->expr->expr->setAttribute('parsedPhpDoc', $parsedDoc); + } + } + + private function parseDocs(Doc $doc): PhpDocNode + { + return PhpDoc::parse($doc->getText(), $this->nameResolver); + } +} diff --git a/vendor/dedoc/scramble/src/OpenApiContext.php b/vendor/dedoc/scramble/src/OpenApiContext.php new file mode 100644 index 00000000..085d9803 --- /dev/null +++ b/vendor/dedoc/scramble/src/OpenApiContext.php @@ -0,0 +1,21 @@ +> + */ + public Collection $groups = new Collection, + ) {} +} diff --git a/vendor/dedoc/scramble/src/OpenApiTraverser.php b/vendor/dedoc/scramble/src/OpenApiTraverser.php new file mode 100644 index 00000000..687e9afb --- /dev/null +++ b/vendor/dedoc/scramble/src/OpenApiTraverser.php @@ -0,0 +1,65 @@ +enterType($type, $path); + + $propertiesWithNodes = $this->getNodes($type); + + foreach ($propertiesWithNodes as $propertyWithNode) { + $node = $type->$propertyWithNode; + + if (! is_array($node)) { + $this->traverse($node, [...$path, $propertyWithNode]); + } else { + foreach ($node as $i => $item) { + $this->traverse($item, [...$path, $propertyWithNode, $i]); + } + } + } + + $this->leaveType($type, $path); + } + + private function enterType($type, $path): void + { + foreach ($this->visitors as $visitor) { + $visitor->enter($type, $path); + } + } + + private function leaveType($type, $path): void + { + foreach ($this->visitors as $visitor) { + $visitor->leave($type, $path); + } + } + + private function getNodes($instance) + { + if (! is_object($instance)) { + return []; + } + + return array_keys(get_object_vars($instance)); + } + + public static function normalizeJsonPointerReferenceToken(string $referenceToken) + { + return Str::replace(['~', '/'], ['~0', '~1'], addslashes($referenceToken)); + } +} diff --git a/vendor/dedoc/scramble/src/OpenApiVisitor.php b/vendor/dedoc/scramble/src/OpenApiVisitor.php new file mode 100644 index 00000000..f0c6a2af --- /dev/null +++ b/vendor/dedoc/scramble/src/OpenApiVisitor.php @@ -0,0 +1,22 @@ +operationReferences, fn () => $this->operationReferences = []); + } + + public function enter($object, array $path = []) + { + if ($object instanceof Reference) { + if (array_key_exists($object->fullName, static::$handledReferences)) { + return; + } + $this->operationReferences[] = $object; + static::$handledReferences[$object->fullName] = true; + } + + if ($object instanceof Type) { + $this->validateSchema($object, $path); + } + } + + protected function validateSchema($object, $path) + { + $exceptions = []; + try { + $exceptions = Scramble::getSchemaValidator()->validate( + $object, + implode('/', array_map(OpenApiTraverser::normalizeJsonPointerReferenceToken(...), $path)), + ); + } catch (InvalidSchema $e) { + $e->setRoute($this->route); + + if ($this->throwExceptions) { + throw $e; + } + + $this->exceptions[] = $e; + } + foreach ($exceptions as $exception) { + if ($exception instanceof RouteAware) { + $exception->setRoute($this->route); + } + } + $this->exceptions = array_merge($this->exceptions, $exceptions); + } +} diff --git a/vendor/dedoc/scramble/src/PhpDoc/AbstractPhpDocTypeVisitor.php b/vendor/dedoc/scramble/src/PhpDoc/AbstractPhpDocTypeVisitor.php new file mode 100644 index 00000000..172a9ef0 --- /dev/null +++ b/vendor/dedoc/scramble/src/PhpDoc/AbstractPhpDocTypeVisitor.php @@ -0,0 +1,13 @@ +type->name === 'int') { + return self::handleGenericInteger($type->genericTypes); + } + + if ($type instanceof IdentifierTypeNode) { + return self::handleIdentifierNode($type); + } + + if ($type instanceof ArrayShapeNode) { + return new KeyedArrayType(array_map( + function (ArrayShapeItemNode $t) { + $keyName = $t->keyName instanceof IdentifierTypeNode + ? $t->keyName->name + : $t->keyName->value ?? null; + + return new ArrayItemType_( + $keyName, + static::toType($t->valueType), + $t->optional, + ); + }, + $type->items, + )); + } + + if ($type instanceof ArrayTypeNode) { + return new ArrayType( + value: static::toType($type->type), + ); + } + + if ($type instanceof GenericTypeNode) { + if ($type->type->name === 'array') { + if (count($type->genericTypes) === 1) { + return new ArrayType( + value: static::toType($type->genericTypes[0]), + ); + } + + return new ArrayType( + value: static::toType($type->genericTypes[1]), + key: static::toType($type->genericTypes[0]), + ); + } + + if ($type->type->name === 'class-string') { + return new GenericClassStringType(static::toType($type->genericTypes[0])); + } + + if ($type->type->name === 'list') { + $valueType = isset($type->genericTypes[0]) ? static::toType($type->genericTypes[0]) : new MixedType; + + return new ArrayType(value: $valueType); + } + + if (! ($typeObject = static::toType($type->type)) instanceof ObjectType) { + return $typeObject; + } + + return new Generic( + $typeObject->name, + array_map( + fn ($type) => static::toType($type), + $type->genericTypes, + ) + ); + } + + if ($type instanceof IntersectionTypeNode) { + return new IntersectionType(array_map( + fn ($t) => static::toType($t), + $type->types, + )); + } + + if ($type instanceof ThisTypeNode) { + return new SelfType(''/** ??? */); + } + + if ($type instanceof UnionTypeNode) { + return new Union(array_map( + fn ($t) => static::toType($t), + $type->types, + )); + } + + if ($type instanceof ConstTypeNode) { + if ($type->constExpr instanceof ConstExprStringNode) { + return new LiteralStringType($type->constExpr->value); + } + + if ($type->constExpr instanceof ConstExprIntegerNode) { + return new LiteralIntegerType($type->constExpr->value); + } + + if ($type->constExpr instanceof ConstExprFloatNode) { + return new FloatType; // todo: float literal? + } + } + + if ($type instanceof CallableTypeNode) { + return new FunctionType( + name: '{closure}', + arguments: array_map( + fn (CallableTypeParameterNode $n) => self::toType($n->type), + $type->parameters, + ), + returnType: self::toType($type->returnType), + ); + } + + return new UnknownType('Unknown phpDoc type ['.$type.']'); + } + + private static function handleIdentifierNode(IdentifierTypeNode $type) + { + if (in_array($type->name, [ + 'string', + 'non-empty-string', + 'callable-string', + 'numeric-string', + 'non-falsy-string', + 'truthy-string', + 'literal-string', + 'lowercase-string', + 'uppercase-string', + 'non-empty-lowercase-string', + 'non-empty-uppercase-string', + 'non-empty-literal-string', + ])) { + return new StringType; + } + if (in_array($type->name, ['float', 'double'])) { + return new FloatType; + } + if (in_array($type->name, [ + 'int', + 'integer', + 'positive-int', + 'negative-int', + 'non-positive-int', + 'non-negative-int', + 'non-zero-int', + ])) { + return match ($type->name) { + 'int', 'integer', 'non-zero-int' => new IntegerType, + 'positive-int' => new IntegerRangeType(min: 1), + 'negative-int' => new IntegerRangeType(max: -1), + 'non-positive-int' => new IntegerRangeType(max: 0), + 'non-negative-int' => new IntegerRangeType(min: 0), + }; + } + if (in_array($type->name, ['bool', 'boolean'])) { + return new BooleanType; + } + if ($type->name === 'true') { + return new LiteralBooleanType(true); + } + if ($type->name === 'false') { + return new LiteralBooleanType(false); + } + if ($type->name === 'scalar') { + // @todo: Scalar variables are those containing an int, float, string or bool. + return new StringType; + } + if ($type->name === 'array') { + return new ArrayType; + } + if ($type->name === 'array-key') { + return new Union([ + new IntegerType, + new StringType, + ]); + } + if ($type->name === 'list') { + return new ArrayType; + } + if ($type->name === 'object') { + return new ObjectType('\stdClass'); + } + if ($type->name === 'null') { + return new NullType; + } + if ($type->name === 'mixed') { + return new MixedType; + } + + return new ObjectType($type->name); + } + + /** + * @param TypeNode[] $genericTypes + */ + private static function handleGenericInteger(array $genericTypes): IntegerType + { + if (count($genericTypes) !== 2) { + return new IntegerType; + } + + if (! ($genericTypes[0] instanceof ConstTypeNode || $genericTypes[0] instanceof IdentifierTypeNode)) { + return new IntegerType; + } + + if ($genericTypes[0] instanceof ConstTypeNode && ! $genericTypes[0]->constExpr instanceof ConstExprIntegerNode) { + return new IntegerType; + } + + if ($genericTypes[0] instanceof IdentifierTypeNode && $genericTypes[0]->name !== 'min') { + return new IntegerType; + } + + if (! ($genericTypes[1] instanceof ConstTypeNode || $genericTypes[1] instanceof IdentifierTypeNode)) { + return new IntegerType; + } + + if ($genericTypes[1] instanceof ConstTypeNode && ! $genericTypes[1]->constExpr instanceof ConstExprIntegerNode) { + return new IntegerType; + } + + if ($genericTypes[1] instanceof IdentifierTypeNode && $genericTypes[1]->name !== 'max') { + return new IntegerType; + } + + $min = match (true) { + $genericTypes[0] instanceof ConstTypeNode => $genericTypes[0]->constExpr->value, // @phpstan-ignore property.notFound + $genericTypes[0] instanceof IdentifierTypeNode => null, + }; + + $max = match (true) { + $genericTypes[1] instanceof ConstTypeNode => $genericTypes[1]->constExpr->value, // @phpstan-ignore property.notFound + $genericTypes[1] instanceof IdentifierTypeNode => null, + }; + + return new IntegerRangeType( + min: $min, + max: $max, + ); + } +} diff --git a/vendor/dedoc/scramble/src/PhpDoc/PhpDocTypeVisitor.php b/vendor/dedoc/scramble/src/PhpDoc/PhpDocTypeVisitor.php new file mode 100644 index 00000000..912f35ad --- /dev/null +++ b/vendor/dedoc/scramble/src/PhpDoc/PhpDocTypeVisitor.php @@ -0,0 +1,12 @@ +$method($node); + } + }; + + if ($type instanceof IdentifierTypeNode) { + $callVisitors($type, 'enter'); + $callVisitors($type, 'leave'); + } + + if ($type instanceof GenericTypeNode) { + $callVisitors($type, 'enter'); + static::traverse($type->type, $visitors); + foreach ($type->genericTypes as $genericType) { + static::traverse($genericType, $visitors); + } + $callVisitors($type, 'leave'); + } + + if ($type instanceof ArrayShapeNode) { + $callVisitors($type, 'enter'); + foreach ($type->items as $itemType) { + static::traverse($itemType, $visitors); + } + $callVisitors($type, 'leave'); + } + + if ($type instanceof ArrayShapeItemNode) { + $callVisitors($type, 'enter'); + if ($type->valueType) { + static::traverse($type->valueType, $visitors); + } + $callVisitors($type, 'leave'); + } + + if ($type instanceof ArrayTypeNode) { + $callVisitors($type, 'enter'); + static::traverse($type->type, $visitors); + $callVisitors($type, 'leave'); + } + + if ($type instanceof IntersectionTypeNode) { + $callVisitors($type, 'enter'); + foreach ($type->types as $itemType) { + static::traverse($itemType, $visitors); + } + $callVisitors($type, 'leave'); + } + + if ($type instanceof UnionTypeNode) { + $callVisitors($type, 'enter'); + foreach ($type->types as $itemType) { + static::traverse($itemType, $visitors); + } + $callVisitors($type, 'leave'); + } + } +} diff --git a/vendor/dedoc/scramble/src/PhpDoc/ResolveFqnPhpDocTypeVisitor.php b/vendor/dedoc/scramble/src/PhpDoc/ResolveFqnPhpDocTypeVisitor.php new file mode 100644 index 00000000..efd67368 --- /dev/null +++ b/vendor/dedoc/scramble/src/PhpDoc/ResolveFqnPhpDocTypeVisitor.php @@ -0,0 +1,25 @@ +nameResolver = $nameResolver; + } + + public function enter(TypeNode|Node $type): void + { + if ($type instanceof IdentifierTypeNode) { + $type->name = ($this->nameResolver)($type->name); + } + } +} diff --git a/vendor/dedoc/scramble/src/Reflection/JsonApiRelationship.php b/vendor/dedoc/scramble/src/Reflection/JsonApiRelationship.php new file mode 100644 index 00000000..5dc38448 --- /dev/null +++ b/vendor/dedoc/scramble/src/Reflection/JsonApiRelationship.php @@ -0,0 +1,16 @@ +analyzeClass($class)); + } + + public function getAttributesType(): ?InferType\KeyedArrayType + { + $propertiesAttributesType = $this->getPropertyDefaultType('attributes'); + $propertiesAttributesType = $propertiesAttributesType instanceof KeyedArrayType ? $propertiesAttributesType : null; + + $toAttributesReturnType = $this->getMethodReturnType('toAttributes'); + $toAttributesReturnType = $toAttributesReturnType instanceof KeyedArrayType ? $toAttributesReturnType : null; + + if ( + ! $propertiesAttributesType instanceof InferType\KeyedArrayType + && ! $toAttributesReturnType instanceof InferType\KeyedArrayType + ) { + return null; + } + + return $this->normalizeAttributesType($toAttributesReturnType ?: $propertiesAttributesType); + } + + public function getRelationshipsType(): ?KeyedArrayType + { + $propertiesRelationshipsType = $this->getPropertyDefaultType('relationships'); + $propertiesRelationshipsType = $propertiesRelationshipsType instanceof KeyedArrayType ? $propertiesRelationshipsType : null; + + $toRelationshipsReturnType = $this->getMethodReturnType('toRelationships'); + $toRelationshipsReturnType = $toRelationshipsReturnType instanceof KeyedArrayType ? $toRelationshipsReturnType : null; + + if ( + ! $propertiesRelationshipsType instanceof InferType\KeyedArrayType + && ! $toRelationshipsReturnType instanceof InferType\KeyedArrayType + ) { + return null; + } + + return $this->normalizeRelationshipsType($toRelationshipsReturnType ?: $propertiesRelationshipsType); + } + + /** + * @return JsonApiRelationship[] + */ + public function getRelationshipItems(): array + { + $type = $this->getRelationshipsType(); + + if (! $type) { + return []; + } + + return array_values(array_filter(array_map( + fn ($item) => $this->decodeRelationshipItem($item), + $type->items, + ))); + } + + /** + * Returns a flat list of all relationships at every nesting level, with dotted names. + * + * @return JsonApiRelationship[] + */ + public function getNestedRelationshipItems(int $maxRelationshipDepth, string $prefix = ''): array + { + if (! $maxRelationshipDepth) { + return []; + } + + $result = []; + foreach ($this->getRelationshipItems() as $relationship) { + $fullName = implode('.', array_filter([$prefix, $relationship->name])); + + $result[] = new JsonApiRelationship($fullName, $relationship->resourceType, $relationship->isCollection); + + $result = array_merge( + $result, + ReflectionJsonApiResource::createForClass($relationship->resourceType->name)->getNestedRelationshipItems( + $maxRelationshipDepth - 1, + $fullName, + ), + ); + } + + return $result; + } + + private function decodeRelationshipItem(InferType\ArrayItemType_ $item): ?JsonApiRelationship + { + if (! is_string($item->key) || ! $item->value instanceof ObjectType) { + return null; + } + + if ($item->value->isInstanceOf(JsonApiAnonymousResourceCollection::class)) { + $resourceType = ResourceCollectionTypeManager::make($item->value)->getCollectedType(); + $isCollection = true; + } elseif ($item->value->isInstanceOf(JsonApiResource::class)) { + $resourceType = $item->value; + $isCollection = false; + } else { + return null; + } + + if (! $resourceType instanceof ObjectType || ! $resourceType->isInstanceOf(JsonApiResource::class)) { + return null; + } + + return new JsonApiRelationship($item->key, $resourceType, $isCollection, $item->getAttribute('docNode')); // @phpstan-ignore argument.type + } + + public function getLinksType(): ?KeyedArrayType + { + $linksType = $this->getMethodReturnType('toLinks'); + + return $linksType instanceof KeyedArrayType ? $linksType : null; + } + + public function getMetaType(): ?KeyedArrayType + { + $metaType = $this->getMethodReturnType('toMeta'); + + return $metaType instanceof KeyedArrayType ? $metaType : null; + } + + public function getIdType(InferType\Generic $type): InferType\Type + { + if (! $modelType = $this->getModelTypeFromInstanceOrDeclaration($type)) { + return new InferType\StringType; + } + + return tap(new InferType\StringType, function ($t) use ($modelType) { + $isUuid = ReflectionModel::createForClass($modelType->name)->isKeyUuid(); + + if ($isUuid) { + $t->setAttribute('format', 'uuid'); + } + }); + } + + /** + * @see ResolvesJsonApiElements::resolveResourceType + */ + public function getTypeType(InferType\Generic $type): InferType\Type + { + $toTypeReturn = $this->getMethodReturnType('toType'); + if ($toTypeReturn instanceof InferType\Contracts\LiteralString) { + return new LiteralStringType($toTypeReturn->getValue()); + } + + if ($this->name !== JsonApiResource::class) { + $value = Str::of($this->name)->classBasename()->beforeLast('Resource')->snake()->pluralStudly()->toString(); + + return new LiteralStringType($value); + } + + if ($modelType = $this->getModelTypeFromInstanceOrDeclaration($type)) { + /** @var string $morphAlias */ + $morphAlias = Relation::getMorphAlias($modelType->name); // @phpstan-ignore argument.type + $base = $morphAlias !== $modelType->name ? $morphAlias : class_basename($modelType->name); + $value = Str::of($base)->snake()->pluralStudly()->toString(); + + return new LiteralStringType($value); + } + + return new InferType\StringType; + } + + private function getModelTypeFromInstanceOrDeclaration(InferType\Generic $type): ?ObjectType + { + $instanceModel = $type->templateTypes[0] ?? new InferType\UnknownType; + if ($instanceModel->isInstanceOf(Model::class)) { + $instanceModel = $instanceModel instanceof InferType\TemplateType ? $instanceModel->is : $instanceModel; + + return $instanceModel instanceof InferType\ObjectType ? $instanceModel : null; + } + + return $this->getModelType(); + } + + private function normalizeRelationshipsType(?Type $type): ?InferType\KeyedArrayType + { + if (! $type instanceof InferType\KeyedArrayType) { + return null; + } + + $modelType = $this->getModelType(); + + $arrayType = clone $type; + $arrayType->items = collect($arrayType->items) + ->map(function (InferType\ArrayItemType_ $t) use ($modelType) { + $newType = clone $t; + $newType->value = $newType->value instanceof InferType\FunctionType + ? $newType->value->getReturnType() + : $newType->value; + + if ($newType->isNumericKey() && $this->isLiteralString($newType->value)) { + $relationshipName = $this->getLiteralStringValue($newType->value); + + $relationshipType = $modelType?->getPropertyType($relationshipName) ?? new InferType\UnknownType; + $relationshipIsMany = $relationshipType->isInstanceOf(Collection::class); + + $guessedClass = $this->guessResourceClassFromRelationship($relationshipType); + + $newType->key = $relationshipName; + $newType->value = $relationshipIsMany + ? new InferType\Generic(JsonApiAnonymousResourceCollection::class, [new InferType\UnknownType, new InferType\UnknownType, new ObjectType($guessedClass)]) + : new ObjectType($guessedClass); + + return $newType; + } + + if ($this->isLiteralString($t->value)) { + $relationshipType = ($newType->key ? $modelType?->getPropertyType((string) $newType->key) : null) ?? new InferType\UnknownType; + $relationshipIsMany = $relationshipType->isInstanceOf(Collection::class); + + $newType->value = $relationshipIsMany + ? new InferType\Generic(JsonApiAnonymousResourceCollection::class, [new InferType\UnknownType, new InferType\UnknownType, new ObjectType($this->getLiteralStringValue($t->value))]) + : new ObjectType($this->getLiteralStringValue($t->value)); + + return $newType; + } + + return $newType; + }) + ->filter() + ->values() + ->all(); + $arrayType->isList = KeyedArrayType::checkIsList($arrayType->items); + + return $arrayType; + } + + /** + * Attempts to find the related model's resource class name from a relationship type. + * In case resource name cannot be found, falls back to {@see JsonApiResource::class} {@see ResolvesJsonApiElements::compileResourceRelationshipUsingResolver() Line 256} + */ + private function guessResourceClassFromRelationship(Type $relationshipType): string + { + $relatedModel = (new InferType\TypeWalker)->first($relationshipType, fn (Type $t) => $t->isInstanceOf(Model::class)); + if ($relatedModel instanceof InferType\TemplateType) { + $relatedModel = $relatedModel->is; + } + if (! $relatedModel instanceof ObjectType) { + return JsonApiResource::class; + } + + $guessedRelatedResource = $this->guessResourceClass($relatedModel->name); + /** + * If $guessedRelatedResource is JsonResource::class, it means that the type of `toResponse` was + * inferred from Model's `toResponse` definition (not via {@see ModelExtension}). In runtime this + * is causing the failure, but `JsonApiResource` wraps this call in `rescue`, and in case + * of exception, returns `JsonApiResource` {@see ResolvesJsonApiElements::compileResourceRelationshipUsingResolver() Line 256} + */ + if (! $guessedRelatedResource || $guessedRelatedResource === JsonResource::class) { + return JsonApiResource::class; + } + + return $guessedRelatedResource; + } + + private function guessResourceClass(string $modelClass): ?string + { + $modelType = new ObjectType($modelClass); + + $resourceType = ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new MethodCallReferenceType($modelType, 'toResource', []), + ); + + return $resourceType instanceof ObjectType ? $resourceType->name : null; + } + + private function normalizeAttributesType(?Type $type): ?KeyedArrayType + { + if (! $type instanceof InferType\KeyedArrayType) { + return null; + } + + $modelType = $this->getModelType(); + + $arrayType = clone $type; + $arrayType->items = collect($arrayType->items) + ->map(function (InferType\ArrayItemType_ $t) use ($modelType) { + $newType = clone $t; + + if ($t->isNumericKey() && $this->isLiteralString($t->value)) { + $newType->key = $propertyName = $this->getLiteralStringValue($t->value); + $newType->value = $modelType?->getPropertyType($propertyName) ?? new InferType\StringType; + + return $newType; + } + + return $newType; + }) + ->all(); + $arrayType->isList = KeyedArrayType::checkIsList($arrayType->items); + + return $arrayType; + } + + private function getModelType(): ?ObjectType + { + try { + $type = JsonResourceHelper::modelType($this->definition); + + if (! $type instanceof ObjectType) { + return null; + } + + return $type; + } catch (Throwable) { + // Anything may go wrong here, but that is fine, we'll just fall back to string. + } + + return null; + } + + private function getPropertyDefaultType(string $name): ?Type + { + return $this->definition->getPropertyDefinition($name)?->defaultType; + } + + private function getMethodReturnType(string $method): ?Type + { + if (! $this->definition->hasMethodDefinition($method)) { + return null; + } + + return ReferenceTypeResolver::getInstance()->resolve( + new GlobalScope, + (new MethodCallReferenceType(new ObjectType($this->name), $method, arguments: [])) + ); + } + + /** + * @phpstan-assert-if-true InferType\Contracts\LiteralString $value + */ + private function isLiteralString(Type $value): bool + { + return $value instanceof InferType\Contracts\LiteralString; + } + + private function getLiteralStringValue(InferType\Contracts\LiteralString $value): string + { + return $value->getValue(); + } +} diff --git a/vendor/dedoc/scramble/src/Reflection/ReflectionModel.php b/vendor/dedoc/scramble/src/Reflection/ReflectionModel.php new file mode 100644 index 00000000..a80e9dc4 --- /dev/null +++ b/vendor/dedoc/scramble/src/Reflection/ReflectionModel.php @@ -0,0 +1,26 @@ +analyzeClass($class)); + } + + public function isKeyUuid(): bool + { + $modelTraits = class_uses_recursive($this->name); + + return Arr::hasAny($modelTraits, [HasUuids::class, HasVersion4Uuids::class]); + } +} diff --git a/vendor/dedoc/scramble/src/Reflection/ReflectionRoute.php b/vendor/dedoc/scramble/src/Reflection/ReflectionRoute.php new file mode 100644 index 00000000..dc98e7e7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Reflection/ReflectionRoute.php @@ -0,0 +1,153 @@ + 'emailId', 'recipient_id' => 'recipientId']`. + * + * The trick is to avoid mapping parameters like `Request $request`, but to correctly map the model bindings + * (and other potential kind of bindings). + * + * During this method implementation, Laravel implicit binding checks against snake cased parameters. + * + * @see ImplicitRouteBinding::getParameterName + */ + public function getSignatureParametersMap(): array + { + $paramNames = $this->route->parameterNames(); + + $paramBoundTypes = $this->getBoundParametersTypes(); + + $checkingRouteSignatureParameters = $this->route->signatureParameters(); + $paramsToSignatureParametersNameMap = collect($paramNames) + ->mapWithKeys(function ($name) use ($paramBoundTypes, &$checkingRouteSignatureParameters) { + $boundParamType = $paramBoundTypes[$name]; + $mappedParameterReflection = collect($checkingRouteSignatureParameters) + ->first(function (ReflectionParameter $rp) use ($boundParamType) { + $type = $rp->getType(); + + if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) { + return true; + } + + $className = Reflector::getParameterClassName($rp); + + return is_a($boundParamType, $className, true); + }); + + if ($mappedParameterReflection) { + $checkingRouteSignatureParameters = array_filter($checkingRouteSignatureParameters, fn ($v) => $v !== $mappedParameterReflection); + } + + return [ + $name => $mappedParameterReflection, + ]; + }); + + $paramsWithRealNames = $paramsToSignatureParametersNameMap + ->mapWithKeys(fn (?ReflectionParameter $reflectionParameter, $name) => [$name => $reflectionParameter?->name ?: $name]) + ->values(); + + return collect($paramNames)->mapWithKeys(fn ($name, $i) => [$name => $paramsWithRealNames[$i]])->all(); + } + + /** + * Get bound parameters types – these are the name of classes that can be bound to the parameters. + * This includes implicitly bound types (UrlRoutable, backedEnum) and explicitly bound parameters. + * + * @return array + */ + public function getBoundParametersTypes(): array + { + $paramNames = $this->route->parameterNames(); + + $implicitlyBoundReflectionParams = collect() + ->union($this->route->signatureParameters(UrlRoutable::class)) + ->union($this->route->signatureParameters(['backedEnum' => true])) + ->keyBy('name'); + + return collect($paramNames) + ->mapWithKeys(function ($name) use ($implicitlyBoundReflectionParams) { + if ($explicitlyBoundParamType = $this->getExplicitlyBoundParamType($name)) { + return [$name => $explicitlyBoundParamType]; + } + + /** @var ReflectionParameter $implicitlyBoundParam */ + $implicitlyBoundParam = $implicitlyBoundReflectionParams->first( + fn (ReflectionParameter $p) => $p->name === $name || Str::snake($p->name) === $name, + ); + + if ($implicitlyBoundParam) { + return [$name => Reflector::getParameterClassName($implicitlyBoundParam)]; + } + + return [ + $name => null, + ]; + }) + ->all(); + } + + private function getExplicitlyBoundParamType(string $name): ?string + { + if (! $binder = app(Router::class)->getBindingCallback($name)) { + return null; + } + + try { + $reflection = new ReflectionFunction($binder); + } catch (ReflectionException) { + return null; + } + + if ($returnType = $reflection->getReturnType()) { + return $returnType instanceof ReflectionNamedType && ! $returnType->isBuiltin() + ? $returnType->getName() + : null; + } + + // in case this is a model binder + if ( + ($modelClass = $reflection->getClosureUsedVariables()['class'] ?? null) + && is_string($modelClass) + ) { + return $modelClass; + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/RuleTransformers/ConfirmedRule.php b/vendor/dedoc/scramble/src/RuleTransformers/ConfirmedRule.php new file mode 100644 index 00000000..1cf6f9e1 --- /dev/null +++ b/vendor/dedoc/scramble/src/RuleTransformers/ConfirmedRule.php @@ -0,0 +1,27 @@ +is('confirmed'); + } + + public function transformAll(SchemaBag $schemaBag, NormalizedRule $rule, RuleTransformerContext $context): void + { + $explicitConfirmationName = $rule->parameters[0] ?? null; + $confirmationField = is_string($explicitConfirmationName) ? $explicitConfirmationName : "{$context->field}_confirmation"; + + $schemaBag->set( + $confirmationField, + clone $schemaBag->getOrFail($context->field), + ); + } +} diff --git a/vendor/dedoc/scramble/src/RuleTransformers/EnumRule.php b/vendor/dedoc/scramble/src/RuleTransformers/EnumRule.php new file mode 100644 index 00000000..c1c47530 --- /dev/null +++ b/vendor/dedoc/scramble/src/RuleTransformers/EnumRule.php @@ -0,0 +1,79 @@ +is(Enum::class); + } + + /** + * @param NormalizedRule $rule + */ + public function toSchema(Type $previous, NormalizedRule $rule, RuleTransformerContext $context): Type + { + $rule = $rule->getRule(); + + /** @var class-string $enumName */ + $enumName = $this->getProtectedValue($rule, 'type'); + + $objectType = new ObjectType($enumName); + + /** @var BackedEnum[] $except */ + $except = method_exists(Enum::class, 'except') ? $this->getProtectedValue($rule, 'except') : []; // @phpstan-ignore function.alreadyNarrowedType + /** @var BackedEnum[] $only */ + $only = method_exists(Enum::class, 'only') ? $this->getProtectedValue($rule, 'only') : []; // @phpstan-ignore function.alreadyNarrowedType + + if ($except || $only) { + return $this->createPartialEnum($enumName, $only, $except); + } + + return $this->openApiTransformer->transform($objectType); + } + + /** + * @param class-string $enumName + * @param BackedEnum[] $only + * @param BackedEnum[] $except + */ + private function createPartialEnum(string $enumName, array $only, array $except): Type + { + $cases = collect($enumName::cases()) + ->reject(fn ($case) => in_array($case, $except)) + ->filter(fn ($case) => ! $only || in_array($case, $only)); + + if (! isset($cases->first()->value)) { + return new UnknownType; // $enumName enum doesnt have values (only/except context) + } + + return $this->openApiTransformer->transform(Union::wrap( + $cases->map(fn ($c) => TypeHelper::createTypeFromValue($c->value))->all() + )); + } + + private function getProtectedValue(object $obj, string $name): mixed + { + $array = (array) $obj; + $prefix = chr(0).'*'.chr(0); + + return $array[$prefix.$name]; + } +} diff --git a/vendor/dedoc/scramble/src/RuleTransformers/ExistsRule.php b/vendor/dedoc/scramble/src/RuleTransformers/ExistsRule.php new file mode 100644 index 00000000..dd9245cd --- /dev/null +++ b/vendor/dedoc/scramble/src/RuleTransformers/ExistsRule.php @@ -0,0 +1,108 @@ +is('exists'); + } + + public function toSchema(Type $previous, NormalizedRule $rule, RuleTransformerContext $context): Type + { + if (! $previous instanceof UnknownType) { + return $previous; + } + + if ($schema = $this->getSchemaUsingDbConnection($previous, $rule, $context)) { + return $schema; + } + + // fallback to guessing using "id" in column name + if (Str::is(['id', '*_id'], $this->getColumnName($rule, $context))) { + return $this->rulesMapper->int($previous); + } + + return $previous; + } + + private function getSchemaUsingDbConnection(Type $previous, NormalizedRule $rule, RuleTransformerContext $context): ?Type + { + /** @var list $params */ + $params = $rule->getParameters(); + + try { + [$connection, $table] = $this->getConnection(tableOrModel: $params[0]); + + $columns = $connection->getSchemaBuilder()->getColumns($table); + } catch (Throwable) { + // @todo collect the error ErrorCollector + return null; + } + + $columnData = collect($columns)->firstWhere('name', $this->getColumnName($rule, $context)); + if (! $columnData) { + return null; + } + + /** @see ModelExtension */ + $typeName = str($columnData['type']) + ->before(' ') // strip modifiers from a type name such as `bigint unsigned` + ->before('(') // strip the length from a type name such as `tinyint(4)` + ->toString(); + + if (in_array($typeName, ['int', 'integer', 'tinyint', 'smallint', 'mediumint', 'bigint'])) { + return $this->rulesMapper->int($previous); + } + + return $this->rulesMapper->string($previous); + } + + private function getColumnName(NormalizedRule $rule, RuleTransformerContext $context): string + { + /** @var list $params */ + $params = $rule->getParameters(); + + $column = $params[1] ?? 'NULL'; + + return $column === 'NULL' ? $context->field : $column; + } + + /** + * @return array{0: Connection, 1: string} + */ + private function getConnection(string $tableOrModel): array + { + if (str_contains($tableOrModel, '\\') && class_exists($tableOrModel) && is_a($tableOrModel, Model::class, true)) { + /** + * @see ValidatesAttributes::parseTable + * + * @var Model $model + */ + $model = new $tableOrModel; + + return [$model->getConnection(), $model->getTable()]; + } + + return [DB::connection(), $tableOrModel]; + } +} diff --git a/vendor/dedoc/scramble/src/RuleTransformers/InRule.php b/vendor/dedoc/scramble/src/RuleTransformers/InRule.php new file mode 100644 index 00000000..eb5e9b01 --- /dev/null +++ b/vendor/dedoc/scramble/src/RuleTransformers/InRule.php @@ -0,0 +1,35 @@ +is('in'); + } + + public function toSchema(Type $previous, NormalizedRule $rule, RuleTransformerContext $context): Type + { + $enum = collect($rule->parameters) + ->mapInto(Stringable::class) + ->map(fn (Stringable $v) => (string) $v->trim('"')->replace('""', '"')) + ->values() + ->all(); + + if ($previous instanceof ArrayType) { + $previous->items->enum($enum); + + return $previous; + } + + return $previous->enum($enum); + } +} diff --git a/vendor/dedoc/scramble/src/RuleTransformers/RegexRule.php b/vendor/dedoc/scramble/src/RuleTransformers/RegexRule.php new file mode 100644 index 00000000..b1e30598 --- /dev/null +++ b/vendor/dedoc/scramble/src/RuleTransformers/RegexRule.php @@ -0,0 +1,73 @@ +is('regex'); + } + + public function toSchema(Type $previous, NormalizedRule $rule, RuleTransformerContext $context): Type + { + if (! is_string($regex = $rule->getParameters()[0])) { + return $previous; + } + + if (! $this->isRegexConvertableToEcma262($regex)) { + // @todo collect the error ErrorCollector + return $previous; + } + + if (! $normalizedRegex = $this->normalize($regex)) { + // @todo collect the error ErrorCollector + return $previous; + } + + return $previous->pattern($normalizedRegex); + } + + private function normalize(string $pattern): ?string + { + // remove regex delimiters (assumes leading / and optional trailing /flags) + $pattern = preg_replace('#^/(.*?)/[a-zA-Z]*$#', '$1', $pattern); + + if (! $pattern) { + return null; + } + + return strtr($pattern, [ + '\z' => '$', + '\A' => '^', + ]); + } + + protected function isRegexConvertableToEcma262(string $pattern): bool + { + $unsupported = [ + '(?<=', '(? $rules + */ + public function __construct( + private array $rules, + ) {} + + public function hasRules(): bool + { + return (bool) count($this->rules); + } + + /** + * @return InvalidSchema[] + * + * @throws InvalidSchema + */ + public function validate(OpenApiType $type, string $path): array + { + $exceptions = []; + + foreach ($this->rules as [$ruleCb, $errorMessageGetter, $ignorePaths, $throw]) { + if (Str::is($ignorePaths, $path)) { + continue; + } + + if ($ruleCb($type, $path)) { + continue; + } + + throw_if( + $throw, + $exception = InvalidSchema::createForSchema(value($errorMessageGetter, $type, $path), $path, $type), + ); + + $exceptions[] = $exception; + } + + return $exceptions; + } +} diff --git a/vendor/dedoc/scramble/src/Scramble.php b/vendor/dedoc/scramble/src/Scramble.php new file mode 100644 index 00000000..4da59df9 --- /dev/null +++ b/vendor/dedoc/scramble/src/Scramble.php @@ -0,0 +1,200 @@ + + */ + public static array $enforceSchemaRules = []; + + public static bool $defaultRoutesIgnored = false; + + /** + * Extensions registered using programmatic API. + * + * @var class-string[] + */ + public static array $extensions = []; + + /** + * Whether to throw an exception during docs generation. When `false`, + * documentation will be generated and issues added to the endpoint description + * that failed generation. When `true`, the exception will be thrown and docs + * generation will fail. + */ + public static bool $throwOnError = false; + + /** + * Disables registration of default API documentation routes. + */ + public static function ignoreDefaultRoutes(): void + { + static::$defaultRoutesIgnored = true; + } + + public static function registerApi(string $name, array $config = []): GeneratorConfig + { + return static::getConfigurationsInstance()->register($name, $config); + } + + public static function configure(string $api = self::DEFAULT_API): GeneratorConfig + { + return static::getGeneratorConfig($api); + } + + /** + * Update open api document before finally rendering it. + * + * @deprecated + */ + public static function extendOpenApi(callable $openApiExtender) + { + static::afterOpenApiGenerated($openApiExtender); + } + + /** + * Update Open API document before finally rendering it. + */ + public static function afterOpenApiGenerated(callable $afterOpenApiGenerated) + { + static::configure()->withDocumentTransformers($afterOpenApiGenerated); + } + + public static function routes(callable $routeResolver) + { + static::configure()->routes($routeResolver); + } + + /** + * @param class-string $extensionClassName + */ + public static function registerExtension(string $extensionClassName): void + { + static::$extensions = array_values(array_unique(array_merge(static::$extensions, [$extensionClassName]))); + } + + /** + * @param class-string[] $extensionClassNames + */ + public static function registerExtensions(array $extensionClassNames): void + { + static::$extensions = array_values(array_unique(array_merge(static::$extensions, $extensionClassNames))); + } + + /** + * Modify tag generation behaviour + * + * @param callable(RouteInfo, Operation): string[] $tagResolver + */ + public static function resolveTagsUsing(callable $tagResolver) + { + static::$tagResolver = $tagResolver; + } + + /** + * @param bool $throw When `true` documentation won't be generated in case of the error. When `false`, + * documentation will be generated but errors will be available in `scramble:analyze` command. + */ + public static function enforceSchema(callable $cb, string|callable $errorMessageGetter, array $ignorePaths = [], bool $throw = true) + { + static::$enforceSchemaRules[] = [$cb, $errorMessageGetter, $ignorePaths, $throw]; + } + + public static function preventSchema(string|array $schemaTypes, array $ignorePaths = [], bool $throw = true) + { + $forbiddenSchemas = Arr::wrap($schemaTypes); + + static::enforceSchema( + fn ($schema, $path) => ! in_array($schema::class, $forbiddenSchemas), + fn ($schema) => 'Schema ['.$schema::class.'] is not allowed.', + $ignorePaths, + $throw, + ); + } + + public static function getSchemaValidator(): SchemaValidator + { + return new SchemaValidator(static::$enforceSchemaRules); + } + + /** + * @param array $variables + */ + public static function defineServerVariables(array $variables) + { + static::configure()->withServerVariables($variables); + } + + public static function registerUiRoute(string $path, string $api = 'default'): Route + { + $config = static::getGeneratorConfig($api); + + return RouteFacade::get($path, function (CacheableGenerator $generator) use ($api) { + $config = static::getGeneratorConfig($api); + + return view('scramble::docs', [ + 'spec' => $generator($config), + 'config' => $config, + ]); + }) + ->middleware($config->get('middleware', [RestrictedDocsAccess::class])); + } + + public static function registerJsonSpecificationRoute(string $path, string $api = 'default'): Route + { + $config = static::getGeneratorConfig($api); + + return RouteFacade::get($path, function (CacheableGenerator $generator) use ($api) { + $config = static::getGeneratorConfig($api); + + return response()->json($generator($config), options: JSON_PRETTY_PRINT); + }) + ->middleware($config->get('middleware', [RestrictedDocsAccess::class])); + } + + public static function getGeneratorConfig(string $api): GeneratorConfig + { + return static::getConfigurationsInstance()->get($api); + } + + public static function infer(): Infer + { + return app(Infer::class); + } + + /** @internal */ + public static function getConfigurationsInstance(): GeneratorConfigCollection + { + return app(GeneratorConfigCollection::class); + } + + public static function throwOnError(bool $throw = true): void + { + static::$throwOnError = $throw; + } + + public static function shouldThrowOnError(): bool + { + return static::$throwOnError; + } +} diff --git a/vendor/dedoc/scramble/src/ScrambleServiceProvider.php b/vendor/dedoc/scramble/src/ScrambleServiceProvider.php new file mode 100644 index 00000000..fddd711b --- /dev/null +++ b/vendor/dedoc/scramble/src/ScrambleServiceProvider.php @@ -0,0 +1,333 @@ + PrettyPrinter\Standard::class, + GeneratorConfigCollection::class => GeneratorConfigCollection::class, + CacheableGenerator::class => CacheableGenerator::class, + ]; + + public function configurePackage(Package $package): void + { + $package + ->name('scramble') + ->hasConfigFile() + ->hasCommand(ExportDocumentation::class) + ->hasCommand(AnalyzeDocumentation::class) + ->hasCommand(CacheDocumentation::class) + ->hasCommand(ClearDocumentationCache::class) + ->hasViews('scramble'); + + $this->app->singleton(FileParser::class, function () { + return new FileParser( + (new ParserFactory)->createForHostVersion() + ); + }); + + $this->app->singleton(LazyShallowReflectionIndex::class, function () { + return new LazyShallowReflectionIndex( + // Abort helpers are handled in the extension and these definitions are needed to avoid leaking the + // annotated exceptions to the caller's definitions. + functions: [ + 'abort' => $abortType = new FunctionLikeDefinition(type: new FunctionType('abort', returnType: new VoidType)), + 'abort_if' => $abortType, + 'abort_unless' => $abortType, + 'throw_if' => $throwType = new FunctionLikeDefinition(type: new FunctionType('throw_if', returnType: new VoidType)), + 'throw_unless' => $throwType, + ] + ); + }); + + $this->app->singleton(Index::class, function () { + $index = new Index; + foreach ((require __DIR__.'/../dictionaries/classMap.php') ?: [] as $className => $serializedClassDefinition) { + $index->classesDefinitions[$className] = unserialize($serializedClassDefinition); + } + + $templates = [$tValue = new TemplateType('TValue')]; + $index->functionsDefinitions['tap'] = new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'tap', + arguments: [ + 'value' => $tValue, + ], + returnType: $tValue, + ), function (FunctionType $type) use ($templates) { + $type->templates = $templates; + }) + ); + + return $index; + }); + + $this->app->singleton(Infer::class); + + $this->app->when(ExtensionsBroker::class) + ->needs('$extensions') + ->give(function ($app) { + $extensions = array_merge(config('scramble.extensions', []), Scramble::$extensions); + + $inferExtensionsClasses = array_values(array_filter( + $extensions, + fn ($e) => is_a($e, InferExtension::class, true), + )); + + $inferExtensionsClasses = array_merge($inferExtensionsClasses, [ + JsonApiResourceMethodReturnTypeExtension::class, + JsonApiResourceCollectionMethodReturnTypeExtension::class, + AfterJsonApiResourceDefinitionCreatedExtension::class, + AfterEloquentCollectionDefinitionCreatedExtension::class, + JsonResourceExtension::class, + ResourceResponseMethodReturnTypeExtension::class, + JsonResponseMethodReturnTypeExtension::class, + ModelExtension::class, + TransformsToResourceCollectionExtension::class, + EloquentBuilderExtension::class, + RequestExtension::class, + AfterJsonResourceDefinitionCreatedExtension::class, + AfterResourceCollectionDefinitionCreatedExtension::class, + AfterAnonymousResourceCollectionDefinitionCreatedExtension::class, + AfterResponseDefinitionCreatedExtension::class, + ]); + + return array_merge( + [ + new PossibleExceptionInfer, + new AbortHelpersExceptionInfer, + + new PaginateMethodsReturnTypeExtension, + new FacadeStaticMethodReturnTypeExtension, + + new ValidatorTypeInfer, + new ResourceCollectionTypeInfer, + new ResponseFactoryTypeInfer, + + new ArrayMergeReturnTypeExtension, + $app->make(TranslationReturnTypeExtension::class), + + /* Keep this extension last, so the trace info is preserved. */ + new TypeTraceInfer, + ], + array_map(function ($class) { + return app($class); + }, $inferExtensionsClasses) + ); + }); + + $this->app->when(IndexBuildingBroker::class) + ->needs('$indexBuilders') + ->give(function () { + $extensions = array_merge(config('scramble.extensions', []), Scramble::$extensions); + + $indexBuilders = array_values(array_filter( + $extensions, + fn ($e) => is_a($e, IndexBuilder::class, true), + )); + + return array_map(function ($class) { + return app($class); + }, array_merge([PaginatorsCandidatesBuilder::class], $indexBuilders)); + }); + + $this->app->bind(TypeTransformer::class, function (Application $application, array $parameters) { + $extensions = array_merge(config('scramble.extensions', []), Scramble::$extensions); + + $typesToSchemaExtensions = array_values(array_filter( + $extensions, + fn ($e) => is_a($e, TypeToSchemaExtension::class, true), + )); + + $exceptionToResponseExtensions = array_values(array_filter( + $extensions, + fn ($e) => is_a($e, ExceptionToResponseExtension::class, true), + )); + + return new TypeTransformer( + $parameters['infer'] ?? $application->make(Infer::class), + $parameters['context'], + typeToSchemaExtensionsClasses: $parameters['typeToSchemaExtensions'] ?? array_merge([ + PlainObjectToSchema::class, + ResponsableTypeToSchema::class, + ArrayableToSchema::class, + EnumToSchema::class, + JsonResourceTypeToSchema::class, + JsonApiResourceTypeToSchema::class, + CollectionToSchema::class, + EloquentCollectionToSchema::class, + ResourceCollectionTypeToSchema::class, + CursorPaginatorTypeToSchema::class, + PaginatorTypeToSchema::class, + LengthAwarePaginatorTypeToSchema::class, + ResponseTypeToSchema::class, + BinaryFileResponseToSchema::class, + StreamedResponseToSchema::class, + ResourceResponseTypeToSchema::class, + PaginatedResourceResponseTypeToSchema::class, + JsonApiAnonymousCollectionTypeToSchema::class, + JsonApiResourceResponseToSchemaExtension::class, + JsonApiPaginatedResourceResponseToSchemaExtension::class, + CarbonInterfaceToSchema::class, + VoidTypeToSchema::class, + ], $typesToSchemaExtensions), + exceptionToResponseExtensionsClasses: $parameters['exceptionToResponseExtensions'] ?? array_merge([ + ValidationExceptionToResponseExtension::class, + AuthorizationExceptionToResponseExtension::class, + AuthenticationExceptionToResponseExtension::class, + HttpExceptionToResponseExtension::class, + NotFoundExceptionToResponseExtension::class, + ], $exceptionToResponseExtensions), + ); + }); + } + + public function bootingPackage() + { + Scramble::configure() + ->useConfig(config('scramble')) + ->withOperationTransformers(function (OperationTransformers $transformers) { + $extensions = array_merge(config('scramble.extensions', []), Scramble::$extensions); + + $operationExtensions = array_values(array_filter( + $extensions, + fn ($e) => is_a($e, OperationExtension::class, true), + )); + + $transformers->append($operationExtensions); + }) + ->withDocumentTransformers([ + AddDocumentTags::class, + CleanupUnusedResponseReferencesTransformer::class, + ]); + + if (Scramble::$defaultRoutesIgnored) { + Scramble::configure()->expose(false); + } + + $this->app->booted(function () { + $this->registerRoutes(); + }); + } + + private function registerRoutes(): void + { + foreach (Scramble::getConfigurationsInstance()->all() as $api => $generatorConfig) { + /** @var Router $router */ + $router = $this->app->get(Router::class); + + if ($generatorConfig->uiRoute) { + $cb = is_callable($generatorConfig->uiRoute) + ? $generatorConfig->uiRoute + : fn ($router, $action) => $router->get($generatorConfig->uiRoute, $action); + + $cb($router, function (CacheableGenerator $generator) use ($api) { + $config = Scramble::getGeneratorConfig($api); + + return view($config->renderer()->view, [ + 'spec' => $generator($config), + 'config' => $config, + ]); + })->middleware($generatorConfig->get('middleware', [RestrictedDocsAccess::class])); + } + + if ($generatorConfig->documentRoute) { + $cb = is_callable($generatorConfig->documentRoute) + ? $generatorConfig->documentRoute + : fn ($router, $action) => $router->get($generatorConfig->documentRoute, $action); + + $cb($router, function (CacheableGenerator $generator) use ($api) { + $config = Scramble::getGeneratorConfig($api); + + return response()->json($generator($config), options: JSON_PRETTY_PRINT); + })->middleware($generatorConfig->get('middleware', [RestrictedDocsAccess::class])); + } + } + } +} diff --git a/vendor/dedoc/scramble/src/SecurityDocumentation/MiddlewareAuthSecurityStrategy.php b/vendor/dedoc/scramble/src/SecurityDocumentation/MiddlewareAuthSecurityStrategy.php new file mode 100644 index 00000000..b77d5f43 --- /dev/null +++ b/vendor/dedoc/scramble/src/SecurityDocumentation/MiddlewareAuthSecurityStrategy.php @@ -0,0 +1,60 @@ + $middleware + */ + public function __construct( + private array $middleware = ['auth', 'auth:*'], + ?SecurityScheme $scheme = null, + ) { + $this->scheme = $scheme ?? SecurityScheme::http('bearer'); + } + + public function configure(SecurityDocumentationContext $context): GeneratorConfig + { + $hasAuthenticatedRoutes = $context->routes->contains( + fn (Route $route) => $this->routeHasMiddleware($route), + ); + + if (! $hasAuthenticatedRoutes) { + return $context->config; + } + + return $context->config + ->withDocumentTransformers(function (OpenApi $openApi) { + $openApi->secure($this->scheme); + }) + ->withOperationTransformers(function (OperationTransformers $transformers) { + $transformers->prepend(function (Operation $operation, RouteInfo $routeInfo): void { + if ($this->routeHasMiddleware($routeInfo->route)) { + return; + } + + $operation->security = []; + }); + }); + } + + private function routeHasMiddleware(Route $route): bool + { + return collect($route->gatherMiddleware()) + ->some(fn (string $middleware) => Str::is($this->middleware, $middleware)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/ContainerUtils.php b/vendor/dedoc/scramble/src/Support/ContainerUtils.php new file mode 100644 index 00000000..76c8c8a7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/ContainerUtils.php @@ -0,0 +1,47 @@ + $class + * @return T + */ + public static function makeContextable(string $class, array $contextfulBindings = []) + { + $reflectionClass = new ReflectionClass($class); + + $parameters = $reflectionClass->getConstructor()?->getParameters() ?? []; + + $contextfulArguments = collect($parameters) + ->mapWithKeys(function (ReflectionParameter $p) use ($contextfulBindings) { + $parameterClass = $p->getType() instanceof ReflectionNamedType + ? $p->getType()->getName() + : null; + + $missingValue = new \stdClass; + + $parameterValue = ($parameterClass ? $contextfulBindings[$parameterClass] ?? null : null) + ?? $contextfulBindings[$p->name] + ?? $missingValue; + + if ($parameterValue === $missingValue) { + return []; + } + + return [ + $p->name => $parameterValue, + ]; + }) + ->all(); + + return app()->make($class, $contextfulArguments); + } +} diff --git a/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthenticationExceptionToResponseExtension.php b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthenticationExceptionToResponseExtension.php new file mode 100644 index 00000000..6200dd49 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthenticationExceptionToResponseExtension.php @@ -0,0 +1,45 @@ +isInstanceOf(AuthenticationException::class); + } + + public function toResponse(Type $type) + { + $responseBodyType = (new OpenApiTypes\ObjectType) + ->addProperty( + 'message', + (new OpenApiTypes\StringType) + ->setDescription('Error overview.') + ) + ->setRequired(['message']); + + return Response::make(401) + ->setDescription('Unauthenticated') + ->setContent( + 'application/json', + Schema::fromType($responseBodyType), + ); + } + + public function reference(ObjectType $type) + { + return new Reference('responses', Str::start($type->name, '\\'), $this->components); + } +} diff --git a/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthorizationExceptionToResponseExtension.php b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthorizationExceptionToResponseExtension.php new file mode 100644 index 00000000..0c6c3213 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/AuthorizationExceptionToResponseExtension.php @@ -0,0 +1,45 @@ +isInstanceOf(AuthorizationException::class); + } + + public function toResponse(Type $type) + { + $validationResponseBodyType = (new OpenApiTypes\ObjectType) + ->addProperty( + 'message', + (new OpenApiTypes\StringType) + ->setDescription('Error overview.') + ) + ->setRequired(['message']); + + return Response::make(403) + ->setDescription('Authorization error') + ->setContent( + 'application/json', + Schema::fromType($validationResponseBodyType), + ); + } + + public function reference(ObjectType $type) + { + return new Reference('responses', Str::start($type->name, '\\'), $this->components); + } +} diff --git a/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/HttpExceptionToResponseExtension.php b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/HttpExceptionToResponseExtension.php new file mode 100644 index 00000000..003f153a --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/HttpExceptionToResponseExtension.php @@ -0,0 +1,127 @@ +isInstanceOf(HttpException::class); + } + + /** + * @param ObjectType $type + */ + public function toResponse(Type $type) + { + /* + * So you (Roman from future) are wondering what 7 or 0 is. + * When index is 7 – the type is honestly inferred – this the index of `TCode` template. + * When index is 0 - the type is manually constructed in other extensions. + */ + $codeType = count($type->templateTypes ?? []) > 3 + ? ($type->templateTypes[7] ?? null) + : ($type->templateTypes[0] ?? null); + + $responseCode = $this->getResponseCode($codeType, $type); + if ($responseCode === null) { + return null; + } + + $responseBodyType = (new OpenApiTypes\ObjectType) + ->addProperty( + 'message', + tap((new OpenApiTypes\StringType)->setDescription('Error overview.'), function (OpenApiTypes\StringType $t) use ($type) { + $messageType = $type->templateTypes[1] ?? null; + if (! $messageType instanceof LiteralStringType) { + return; + } + $t->example($messageType->value); + }) + ) + ->setRequired(['message']); + + return Response::make($responseCode) + ->setDescription($this->getDescription($type)) + ->setContent( + 'application/json', + Schema::fromType($responseBodyType), + ); + } + + protected function getResponseCode(?Type $codeType, Type $type): ?int + { + if (! $codeType instanceof LiteralIntegerType) { + return match (true) { + $type->isInstanceOf(AccessDeniedHttpException::class) => 403, + $type->isInstanceOf(BadRequestHttpException::class) => 400, + $type->isInstanceOf(ConflictHttpException::class) => 409, + $type->isInstanceOf(GoneHttpException::class) => 410, + $type->isInstanceOf(LengthRequiredHttpException::class) => 411, + $type->isInstanceOf(LockedHttpException::class) => 423, + $type->isInstanceOf(MethodNotAllowedHttpException::class) => 405, + $type->isInstanceOf(NotAcceptableHttpException::class) => 406, + $type->isInstanceOf(PreconditionFailedHttpException::class) => 412, + $type->isInstanceOf(PreconditionRequiredHttpException::class) => 428, + $type->isInstanceOf(ServiceUnavailableHttpException::class) => 503, + $type->isInstanceOf(TooManyRequestsHttpException::class) => 429, + $type->isInstanceOf(UnauthorizedHttpException::class) => 401, + $type->isInstanceOf(UnprocessableEntityHttpException::class) => 422, + $type->isInstanceOf(UnsupportedMediaTypeHttpException::class) => 415, + default => null, + }; + } + + return $codeType->value; + } + + protected function getDescription(Type $type): string + { + return match (true) { + $type->isInstanceOf(AccessDeniedHttpException::class) => 'Access denied', + $type->isInstanceOf(BadRequestHttpException::class) => 'Bad request', + $type->isInstanceOf(ConflictHttpException::class) => 'Conflict', + $type->isInstanceOf(GoneHttpException::class) => 'Gone', + $type->isInstanceOf(LengthRequiredHttpException::class) => 'Length required', + $type->isInstanceOf(LockedHttpException::class) => 'Locked', + $type->isInstanceOf(MethodNotAllowedHttpException::class) => 'Method not allowed', + $type->isInstanceOf(NotAcceptableHttpException::class) => 'Not acceptable', + $type->isInstanceOf(NotFoundHttpException::class) => 'Not found', + $type->isInstanceOf(PreconditionFailedHttpException::class) => 'Precondition failed', + $type->isInstanceOf(PreconditionRequiredHttpException::class) => 'Precondition required', + $type->isInstanceOf(ServiceUnavailableHttpException::class) => 'Service unavailable', + $type->isInstanceOf(TooManyRequestsHttpException::class) => 'Too many requests', + $type->isInstanceOf(UnauthorizedHttpException::class) => 'Unauthorized', + $type->isInstanceOf(UnprocessableEntityHttpException::class) => 'Unprocessable entity', + $type->isInstanceOf(UnsupportedMediaTypeHttpException::class) => 'Unsupported media type', + default => 'An error', + }; + } +} diff --git a/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/NotFoundExceptionToResponseExtension.php b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/NotFoundExceptionToResponseExtension.php new file mode 100644 index 00000000..8f192302 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/NotFoundExceptionToResponseExtension.php @@ -0,0 +1,49 @@ +isInstanceOf(RecordsNotFoundException::class) + || $type->isInstanceOf(NotFoundHttpException::class) + ); + } + + public function toResponse(Type $type) + { + $validationResponseBodyType = (new OpenApiTypes\ObjectType) + ->addProperty( + 'message', + (new OpenApiTypes\StringType) + ->setDescription('Error overview.') + ) + ->setRequired(['message']); + + return Response::make(404) + ->setDescription('Not found') + ->setContent( + 'application/json', + Schema::fromType($validationResponseBodyType), + ); + } + + public function reference(ObjectType $type) + { + return new Reference('responses', Str::start($type->name, '\\'), $this->components); + } +} diff --git a/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/ValidationExceptionToResponseExtension.php b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/ValidationExceptionToResponseExtension.php new file mode 100644 index 00000000..1ab2df65 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/ExceptionToResponseExtensions/ValidationExceptionToResponseExtension.php @@ -0,0 +1,51 @@ +isInstanceOf(ValidationException::class); + } + + public function toResponse(Type $type) + { + $validationResponseBodyType = (new OpenApiTypes\ObjectType) + ->addProperty( + 'message', + (new OpenApiTypes\StringType) + ->setDescription('Errors overview.') + ) + ->addProperty( + 'errors', + (new OpenApiTypes\ObjectType) + ->setDescription('A detailed description of each field that failed validation.') + ->additionalProperties((new OpenApiTypes\ArrayType)->setItems(new OpenApiTypes\StringType)) + ) + ->setRequired(['message', 'errors']); + + return Response::make(422) + ->setDescription('Validation error') + ->setContent( + 'application/json', + Schema::fromType($validationResponseBodyType), + ); + } + + public function reference(ObjectType $type) + { + return new Reference('responses', Str::start($type->name, '\\'), $this->components); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Factories/JsonApiQueryParameterFactory.php b/vendor/dedoc/scramble/src/Support/Factories/JsonApiQueryParameterFactory.php new file mode 100644 index 00000000..205bd7b7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Factories/JsonApiQueryParameterFactory.php @@ -0,0 +1,40 @@ +arraySerialization === JsonApiArraySerialization::String) { + $possibleOptionsDescriptions = array_map(fn ($value) => '`'.$value.'`', $values); + + return Parameter::make($name, 'query') + ->description('Available values are '.implode(', ', $possibleOptionsDescriptions).'. You can include multiple values by separating them with a comma.') + ->setSchema(Schema::fromType(new StringType)); + } + + return tap( + Parameter::make($name, 'query') + ->setExplode(false) + ->setSchema(Schema::fromType( + (new ArrayType)->setItems((new StringType)->enum($values)), + )), + /** Setting `isFlat` prevents QueryParametersConverter to append `[]` to parameter {@see QueryParametersConverter::shouldAdaptParameterForQuery} */ + fn (Parameter $p) => $p->setAttribute('isFlat', true), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/ClassBasedReference.php b/vendor/dedoc/scramble/src/Support/Generator/ClassBasedReference.php new file mode 100644 index 00000000..57068dee --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/ClassBasedReference.php @@ -0,0 +1,30 @@ +getAttributes(SchemaName::class)[0] ?? null)?->newInstance(); + + return $schemaNameAttribute + ? ($input && $schemaNameAttribute->input ? $schemaNameAttribute->input : $schemaNameAttribute->name) + : null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Combined/AllOf.php b/vendor/dedoc/scramble/src/Support/Generator/Combined/AllOf.php new file mode 100644 index 00000000..8cf606cd --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Combined/AllOf.php @@ -0,0 +1,56 @@ +items = [new StringType]; + } + + public function clone(): static + { + $clone = parent::clone(); + $clone->items = array_map( + fn (Type $item) => $item->clone(), + $clone->items, + ); + + return $clone; + } + + public function toArray() + { + $parentArray = parent::toArray(); + + unset($parentArray['type']); + + return [ + ...$parentArray, + 'allOf' => array_map( + fn ($item) => $item->toArray(), + $this->items, + ), + ]; + } + + public function setItems($items) + { + if (collect($items)->contains(fn ($item) => ! $item instanceof Type)) { + throw new InvalidArgumentException('All items should be instances of '.Type::class); + } + + $this->items = $items; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Combined/AnyOf.php b/vendor/dedoc/scramble/src/Support/Generator/Combined/AnyOf.php new file mode 100644 index 00000000..16f57b54 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Combined/AnyOf.php @@ -0,0 +1,56 @@ +items = [new StringType]; + } + + public function clone(): static + { + $clone = parent::clone(); + $clone->items = array_map( + fn (Type $item) => $item->clone(), + $clone->items, + ); + + return $clone; + } + + public function toArray() + { + $parentArray = parent::toArray(); + + unset($parentArray['type']); + + return [ + ...$parentArray, + 'anyOf' => array_map( + fn ($item) => $item->toArray(), + $this->items, + ), + ]; + } + + public function setItems($items) + { + if (collect($items)->contains(fn ($item) => ! $item instanceof Type)) { + throw new InvalidArgumentException('All items should be instances of '.Type::class); + } + + $this->items = $items; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Components.php b/vendor/dedoc/scramble/src/Support/Generator/Components.php new file mode 100644 index 00000000..4d94df84 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Components.php @@ -0,0 +1,183 @@ + */ + public array $schemas = []; + + /** @var array */ + public array $responses = []; + + /** @var array */ + public array $securitySchemes = []; + + // @todo: figure out how to solve the problem of duplicating resource names better + public array $tempNames = []; + + public function addSecurityScheme(string $name, SecurityScheme $securityScheme) + { + $this->securitySchemes[$name] = $securityScheme; + + return $this; + } + + public function hasSchema(string $schemaName): bool + { + return array_key_exists($schemaName, $this->schemas); + } + + public function addSchema(string $schemaName, Schema $schema): Reference + { + $this->schemas[$schemaName] = $schema; + + return new Reference('schemas', $schemaName, $this); + } + + public function removeSchema(string $schemaName): void + { + unset($this->schemas[$schemaName]); + } + + public function removeResponse(string $responseName): void + { + unset($this->responses[$responseName]); + } + + public function toArray() + { + $result = []; + + if (count($this->securitySchemes)) { + $result['securitySchemes'] = collect($this->securitySchemes) + ->map(fn (SecurityScheme $s) => $s->toArray()) + ->toArray(); + } + + if (count($this->schemas)) { + $result['schemas'] = collect($this->schemas) + ->mapWithKeys(function (Schema $s, string $fullName) { + return [ + $this->uniqueSchemaName($fullName) => $s->setTitle($this->uniqueSchemaName($fullName))->toArray(), + ]; + }) + ->sortKeys() + ->toArray(); + } + + if (count($this->responses)) { + $result['responses'] = collect($this->responses) + ->mapWithKeys(function (Response $r, string $fullName) { + return [ + $this->uniqueSchemaName($fullName) => $r->toArray(), + ]; + }) + ->toArray(); + } + + return $result; + } + + /** + * @deprecated Use context instead + */ + public function uniqueSchemaName(string $fullName) + { + $shortestPossibleName = class_basename($fullName); + + if ( + ($this->tempNames[$shortestPossibleName] ?? null) === null + || ($this->tempNames[$shortestPossibleName] ?? null) === $fullName + ) { + $this->tempNames[$shortestPossibleName] = $fullName; + + return static::slug($shortestPossibleName); + } + + return static::slug($fullName); + } + + public function getSchemaReference(string $schemaName) + { + return new Reference('schemas', $schemaName, $this); + } + + public function getSchema(string $schemaName) + { + return $this->schemas[$schemaName]; + } + + /** + * @internal + * + * @deprecated + */ + public static function slug(string $name) + { + return Str::replace('\\', '.', $name); + } + + public function has(Reference $reference): bool + { + $this->ensureValidReference($reference); + + return array_key_exists($reference->fullName, $this->{$reference->referenceType}); + } + + public function add(Reference $reference, $object): Reference + { + $this->ensureValidReference($reference, $object); + + $this->{$reference->referenceType}[$reference->fullName] = $object; + + return $reference; + } + + public function get(Reference $reference) + { + $this->ensureValidReference($reference); + + $references = $this->{$reference->referenceType}; + + if (! array_key_exists($reference->fullName, $references)) { + throw new OpenApiReferenceTargetNotFoundException(sprintf( + '[%s] reference target doesn\'t exist in the [%s] list', + $reference->fullName, + $reference->referenceType, + )); + } + + return $references[$reference->fullName]; + } + + private function ensureValidReference(Reference $reference, $object = null) + { + $references = [ + 'schemas' => Schema::class, + 'responses' => Response::class, + ]; + + if (! in_array($reference->referenceType, $referenceTypes = array_keys($references))) { + $validTypes = implode(', ', $referenceTypes); + + throw new InvalidArgumentException("Only $validTypes references are allowed"); + } + + if ($object === null) { + return; + } + + $expectedType = $references[$reference->referenceType]; + + if (! is_a($object, $expectedType)) { + $actualType = get_class($object); + + throw new InvalidArgumentException("Object must be $expectedType to be added to $reference->referenceType references, $actualType given"); + } + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Encoding.php b/vendor/dedoc/scramble/src/Support/Generator/Encoding.php new file mode 100644 index 00000000..9af39e3b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Encoding.php @@ -0,0 +1,110 @@ + */ + public array $headers = [], + public ?string $style = null, + public ?bool $explode = null, + public ?bool $allowReserved = null, + ) {} + + /** + * @return $this + */ + public function setContentType(?string $contentType): self + { + $this->contentType = $contentType; + + return $this; + } + + /** + * @param array $headers + * @return $this + */ + public function setHeaders(array $headers): self + { + $this->headers = $headers; + + return $this; + } + + /** + * @return $this + */ + public function addHeader(string $key, Header|Reference $header): self + { + $this->headers[$key] = $header; + + return $this; + } + + /** + * @return $this + */ + public function removeHeader(string $key): self + { + unset($this->headers[$key]); + + return $this; + } + + /** + * @return $this + */ + public function setStyle(?string $style): self + { + $this->style = $style; + + return $this; + } + + /** + * @return $this + */ + public function setExplode(?bool $explode): self + { + $this->explode = $explode; + + return $this; + } + + /** + * @return $this + */ + public function setAllowReserved(?bool $allowReserved): self + { + $this->allowReserved = $allowReserved; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $result = array_filter([ + 'contentType' => $this->contentType, + 'style' => $this->style, + 'explode' => $this->explode, + 'allowReserved' => $this->allowReserved, + ], fn ($value) => $value !== null); + + $headers = array_map(fn ($h) => $h->toArray(), $this->headers); + + return array_merge( + $result, + $headers ? ['headers' => $headers] : [], + $this->extensionPropertiesToArray(), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Example.php b/vendor/dedoc/scramble/src/Support/Generator/Example.php new file mode 100644 index 00000000..0659a4c1 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Example.php @@ -0,0 +1,25 @@ + $this->value], + fn ($v) => ! $v instanceof MissingValue, + ) + array_filter([ + 'summary' => $this->summary, + 'description' => $this->description, + 'externalValue' => $this->externalValue, + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Header.php b/vendor/dedoc/scramble/src/Support/Generator/Header.php new file mode 100644 index 00000000..5217accf --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Header.php @@ -0,0 +1,179 @@ + */ + public array $examples = [], + /** @var array */ + public array $content = [], + ) {} + + /** + * @return $this + */ + public function setDescription(?string $description): self + { + $this->description = $description; + + return $this; + } + + /** + * @return $this + */ + public function setRequired(?bool $required): self + { + $this->required = $required; + + return $this; + } + + /** + * @return $this + */ + public function setDeprecated(?bool $deprecated): self + { + $this->deprecated = $deprecated; + + return $this; + } + + /** + * @return $this + */ + public function setExplode(?bool $explode): self + { + $this->explode = $explode; + + return $this; + } + + /** + * @return $this + */ + public function setSchema(Schema|Reference|null $schema): self + { + $this->schema = $schema; + + return $this; + } + + /** + * @return $this + */ + public function setExample(mixed $example): self + { + $this->example = $example; + + return $this; + } + + /** + * @param array $examples + * @return $this + */ + public function setExamples(array $examples): self + { + $this->examples = $examples; + + return $this; + } + + /** + * @param array $content + * @return $this + */ + public function setContent(array $content): self + { + $this->content = $content; + + return $this; + } + + /** + * @return $this + */ + public function addContent(string $key, MediaType $mediaType): self + { + $this->content[$key] = $mediaType; + + return $this; + } + + /** + * @return $this + */ + public function addExample(string $key, Example|Reference $example): self + { + $this->examples[$key] = $example; + + return $this; + } + + /** + * @return $this + */ + public function removeContent(string $key): self + { + unset($this->content[$key]); + + return $this; + } + + /** + * @return $this + */ + public function removeExample(string $key): self + { + unset($this->examples[$key]); + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $result = array_filter([ + 'description' => $this->description, + 'required' => $this->required, + 'deprecated' => $this->deprecated, + 'explode' => $this->explode, + ], fn ($value) => $value !== null); + + if ($this->schema) { + $result['schema'] = $this->schema->toArray(); + } + + $examples = []; + foreach ($this->examples as $key => $example) { + $serializedExample = $example->toArray(); + if ($serializedExample) { + $examples[$key] = $serializedExample; + } + } + + $content = array_map(fn ($mt) => $mt->toArray(), $this->content); + + return array_merge( + $result, + $this->example instanceof MissingValue ? [] : ['example' => $this->example], + $examples ? ['examples' => $examples] : [], + $content ? ['content' => $content] : [], + $this->extensionPropertiesToArray(), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/InfoObject.php b/vendor/dedoc/scramble/src/Support/Generator/InfoObject.php new file mode 100644 index 00000000..52003774 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/InfoObject.php @@ -0,0 +1,46 @@ +title = $title; + $this->version = $version; + } + + public static function make(string $title) + { + return new self($title); + } + + public function setVersion(string $version): self + { + $this->version = $version; + + return $this; + } + + public function setDescription(string $description): self + { + $this->description = $description; + + return $this; + } + + public function toArray() + { + return array_filter([ + 'title' => $this->title, + 'version' => $this->version, + 'description' => $this->description, + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Link.php b/vendor/dedoc/scramble/src/Support/Generator/Link.php new file mode 100644 index 00000000..9bd9e631 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Link.php @@ -0,0 +1,116 @@ + */ + public array $parameters = [], + public mixed $requestBody = new MissingValue, + public ?string $description = null, + public ?Server $server = null, + ) {} + + /** + * @return $this + */ + public function setOperationRef(?string $operationRef): self + { + $this->operationRef = $operationRef; + + return $this; + } + + /** + * @return $this + */ + public function setOperationId(?string $operationId): self + { + $this->operationId = $operationId; + + return $this; + } + + /** + * @param array $parameters + * @return $this + */ + public function setParameters(array $parameters): self + { + $this->parameters = $parameters; + + return $this; + } + + /** + * @return $this + */ + public function addParameter(string $name, mixed $value): self + { + $this->parameters[$name] = $value; + + return $this; + } + + /** + * @return $this + */ + public function setRequestBody(mixed $requestBody): self + { + $this->requestBody = $requestBody; + + return $this; + } + + /** + * @return $this + */ + public function setDescription(?string $description): self + { + $this->description = $description; + + return $this; + } + + /** + * @return $this + */ + public function setServer(?Server $server): self + { + $this->server = $server; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $result = array_filter([ + 'operationRef' => $this->operationRef, + 'operationId' => $this->operationId, + 'description' => $this->description, + ], fn ($value) => $value !== null); + + if ($this->server) { + $result['server'] = $this->server->toArray(); + } + + if ($this->parameters) { + $result['parameters'] = $this->parameters; + } + + return array_merge( + $result, + $this->requestBody instanceof MissingValue ? [] : ['requestBody' => $this->requestBody], + $this->extensionPropertiesToArray(), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/MediaType.php b/vendor/dedoc/scramble/src/Support/Generator/MediaType.php new file mode 100644 index 00000000..13281c9c --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/MediaType.php @@ -0,0 +1,130 @@ + */ + public array $examples = [], + /** @var array */ + public array $encoding = [], + ) {} + + /** + * @return $this + */ + public function setSchema(Schema|Reference|null $schema): self + { + $this->schema = $schema; + + return $this; + } + + /** + * @return $this + */ + public function setExample(mixed $example): self + { + $this->example = $example; + + return $this; + } + + /** + * @param array $examples + * @return $this + */ + public function setExamples(array $examples): self + { + $this->examples = $examples; + + return $this; + } + + /** + * @param array $encoding + * @return $this + */ + public function setEncoding(array $encoding): self + { + $this->encoding = $encoding; + + return $this; + } + + /** + * @return $this + */ + public function addExample(string $key, Example|Reference $example): self + { + $this->examples[$key] = $example; + + return $this; + } + + /** + * @return $this + */ + public function addEncoding(string $key, Encoding $encoding): self + { + $this->encoding[$key] = $encoding; + + return $this; + } + + /** + * @return $this + */ + public function removeExample(string $key): self + { + unset($this->examples[$key]); + + return $this; + } + + /** + * @return $this + */ + public function removeEncoding(string $key): self + { + unset($this->encoding[$key]); + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $result = []; + + if ($this->schema) { + $result['schema'] = $this->schema->toArray(); + } + + $examples = []; + foreach ($this->examples as $key => $example) { + $serializedExample = $example->toArray(); + if ($serializedExample) { + $examples[$key] = $serializedExample; + } + } + + $encoding = array_map(fn ($e) => $e->toArray(), $this->encoding); + + return array_merge( + $result, + $this->example instanceof MissingValue ? [] : ['example' => $this->example], + $examples ? ['examples' => $examples] : [], + $encoding ? ['encoding' => $encoding] : [], + $this->extensionPropertiesToArray(), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/MissingExample.php b/vendor/dedoc/scramble/src/Support/Generator/MissingExample.php new file mode 100644 index 00000000..59454bbf --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/MissingExample.php @@ -0,0 +1,8 @@ +version = $version; + $this->components = new Components; + } + + public static function make(string $version) + { + return new self($version); + } + + public function setComponents(Components $components) + { + $this->components = $components; + + return $this; + } + + public function secure(SecurityScheme $securityScheme) + { + $this->components->addSecurityScheme($securityScheme->schemeName, $securityScheme); + + $this->security ??= []; + $this->security[] = new SecurityRequirement([$securityScheme->schemeName => []]); + + return $this; + } + + public function setInfo(InfoObject $info) + { + $this->info = $info; + + return $this; + } + + /** + * @param Path[] $paths + */ + public function paths(array $paths) + { + $this->paths = $paths; + + return $this; + } + + public function addPath(Path $path) + { + $this->paths[] = $path; + + return $this; + } + + public function addServer(Server $server) + { + $this->servers[] = $server; + + return $this; + } + + public function toArray() + { + $result = [ + 'openapi' => $this->version, + 'info' => $this->info->toArray(), + ]; + + if (count($this->servers)) { + $result['servers'] = array_map( + fn (Server $s) => $s->toArray(), + $this->servers, + ); + } + + if (count($this->tags)) { + $result['tags'] = array_map( + fn (Tag $s) => $s->toArray(), + $this->tags, + ); + } + + if ($this->security) { + $result['security'] = array_map( + fn (SecurityRequirement $sr) => $sr->toArray(), + $this->security, + ); + } + + if (count($this->paths)) { + $paths = []; + + foreach ($this->paths as $pathBuilder) { + $paths['/'.$pathBuilder->path] = array_merge( + $paths['/'.$pathBuilder->path] ?? [], + $pathBuilder->toArray(), + ); + } + + $result['paths'] = $paths; + } + + if (count($serializedComponents = $this->components->toArray())) { + $result['components'] = $serializedComponents; + } + + return $result; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Operation.php b/vendor/dedoc/scramble/src/Support/Generator/Operation.php new file mode 100644 index 00000000..179eccdd --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Operation.php @@ -0,0 +1,222 @@ +|null */ + public ?array $security = null; + + public array $tags = []; + + /** @var (Parameter|Reference)[] */ + public array $parameters = []; + + public ?RequestBodyObject $requestBodyObject = null; + + /** @var (Response|Reference)[]|null */ + public ?array $responses = []; + + /** @var Server[] */ + public array $servers = []; + + public function __construct(string $method) + { + $this->method = $method; + } + + public static function make(string $method) + { + return new self($method); + } + + public function addRequestBodyObject(RequestBodyObject $requestBodyObject) + { + $this->requestBodyObject = $requestBodyObject; + + return $this; + } + + /** + * @param Server[] $servers + */ + public function servers(array $servers) + { + $this->servers = $servers; + + return $this; + } + + /** + * @param Response|Reference $response + */ + public function addResponse($response) + { + $this->responses[] = $response; + + return $this; + } + + public function addSecurity($security) + { + if ($security === []) { + $security = new SecurityRequirement([]); + } + + $this->security ??= []; + $this->security[] = $security; + + return $this; + } + + public function setOperationId(?string $operationId) + { + $this->operationId = $operationId; + + return $this; + } + + public function setMethod(string $method) + { + $this->method = $method; + + return $this; + } + + public function setPath(string $path) + { + $this->path = $path; + + return $this; + } + + public function summary(string $summary) + { + $this->summary = $summary; + + return $this; + } + + public function description(string $description) + { + $this->description = $description; + + return $this; + } + + public function deprecated(bool $deprecated) + { + $this->deprecated = $deprecated; + + return $this; + } + + public function setTags(array $tags) + { + $this->tags = array_map(fn ($t) => (string) $t, $tags); + + return $this; + } + + public function addParameters(array $parameters) + { + $this->parameters = array_merge($this->parameters, $parameters); + + return $this; + } + + /** + * @return array + */ + public function toArray() + { + $result = []; + + if ($this->operationId) { + $result['operationId'] = $this->operationId; + } + + if ($this->description) { + $result['description'] = $this->description; + } + + if ($this->summary) { + $result['summary'] = $this->summary; + } + + if ($this->deprecated) { + $result['deprecated'] = $this->deprecated; + } + + if (count($this->tags)) { + $result['tags'] = $this->tags; + } + + if (count($this->parameters)) { + $result['parameters'] = array_map(fn (Parameter|Reference $p) => $p->toArray(), $this->parameters); + } + + if ($this->requestBodyObject) { + $result['requestBody'] = $this->requestBodyObject->toArray(); + } + + if ($this->responses !== null && count($this->responses)) { + $responses = []; + foreach ($this->responses as $response) { + if ($response instanceof Response) { + $responses[$response->code ?: 'default'] = $response->toArray(); + } elseif ($response instanceof Reference) { + try { + $referencedResponse = $response->resolve(); + } catch (OpenApiReferenceTargetNotFoundException) { + // This catch is needed in case a reference target is removed from the document (when a + // reference is not used in the document for example). But all of this should not really be + // needed when `code` is not stored in the response due to the resolution + // will not be needed at all to resolve the code. + continue; + } + + $responses[$referencedResponse->code ?: 'default'] = $response->toArray(); + } + } + $result['responses'] = $responses; + } + + if ($this->security !== null) { + $result['security'] = array_map( + fn ($s) => $s->toArray(), + $this->security, + ); + } + + if (count($this->servers)) { + $servers = []; + foreach ($this->servers as $server) { + $servers[] = $server->toArray(); + } + $result['servers'] = $servers; + } + + return array_merge( + $result, + $this->extensionPropertiesToArray(), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Parameter.php b/vendor/dedoc/scramble/src/Support/Generator/Parameter.php new file mode 100644 index 00000000..1e67ffcf --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Parameter.php @@ -0,0 +1,149 @@ + */ + public array $examples = []; + + public bool $deprecated = false; + + public bool $allowEmptyValue = false; + + public ?Schema $schema = null; + + public function __construct(string $name, string $in) + { + $this->name = $name; + $this->in = $in; + + $this->example = new MissingValue; + + if ($this->in === 'path') { + $this->required = true; + } + } + + public static function make(string $name, string $in): static + { + return new static($name, $in); + } + + public function toArray(): array + { + $result = array_filter([ + 'name' => $this->name, + 'in' => $this->in, + 'required' => $this->required, + 'description' => $this->description, + 'deprecated' => $this->deprecated, + 'allowEmptyValue' => $this->allowEmptyValue, + 'style' => $this->style, + ]); + + if ($this->schema) { + $result['schema'] = $this->schema->toArray(); + } + + $examples = []; + if ($this->examples) { + foreach ($this->examples as $key => $example) { + $serializedExample = $example->toArray(); + if ($serializedExample) { + $examples[$key] = $serializedExample; + } + } + } + + return array_merge( + $result, + $this->example instanceof MissingValue ? [] : ['example' => $this->example], + ! is_null($this->explode) ? [ + 'explode' => $this->explode, + ] : [], + $examples ? ['examples' => $examples] : [], + $this->extensionPropertiesToArray(), + ); + } + + public function required(bool $required) + { + $this->required = $required; + + return $this; + } + + public function setName(string $name): self + { + $this->name = $name; + + return $this; + } + + public function setSchema(?Schema $schema): self + { + $this->schema = $schema; + + return $this; + } + + public function description(string $description) + { + $this->description = $description; + + return $this; + } + + /** + * @param array|scalar|null|MissingValue $example + */ + public function example($example) + { + $this->example = $example; + + return $this; + } + + public function setExplode(bool $explode): self + { + $this->explode = $explode; + + return $this; + } + + public function setStyle(string $style): self + { + $this->style = $style; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Path.php b/vendor/dedoc/scramble/src/Support/Generator/Path.php new file mode 100644 index 00000000..73b4eb43 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Path.php @@ -0,0 +1,60 @@ + */ + public array $operations = []; + + /** @var Server[] */ + public array $servers = []; + + public function __construct(string $path) + { + $this->path = $path; + } + + public static function make(string $path) + { + return new self($path); + } + + /** + * @param Server[] $servers + */ + public function servers(array $servers) + { + $this->servers = $servers; + + return $this; + } + + public function addOperation(Operation $operationBuilder) + { + $this->operations[$operationBuilder->method] = $operationBuilder; + + return $this; + } + + public function toArray() + { + $result = []; + + foreach ($this->operations as $method => $operation) { + $result[$method] = $operation->toArray(); + } + + if (count($this->servers)) { + $servers = []; + foreach ($this->servers as $server) { + $servers[] = $server->toArray(); + } + $result['servers'] = $servers; + } + + return $result; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Reference.php b/vendor/dedoc/scramble/src/Support/Generator/Reference.php new file mode 100644 index 00000000..bd586e61 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Reference.php @@ -0,0 +1,91 @@ +referenceType = $referenceType; + $this->fullName = $fullName; + $this->components = $components; + $this->shortName = $shortName; + } + + public function resolve() + { + return $this->components->get($this); + } + + public function getUniqueName() + { + return $this->components->uniqueSchemaName($this->shortName ?: $this->fullName); + } + + public function setDescription(string $description): Type + { + $casesDescription = $this->getEnumReferenceCasesDescription(); + + if ($description && $casesDescription) { + $description = Str::replaceLast($casesDescription, '', $description)."\n".$casesDescription; + } + + return parent::setDescription($description); + } + + /** + * This is a workaround for Stoplight Elements. When `enum_cases_description_strategy` is set to `description` and + * enum used as array item value and user adds some description, we want to keep the description in the UI. + */ + private function getEnumReferenceCasesDescription(): ?string + { + $schema = $this->resolve(); + + if (! $schema instanceof Schema) { + return null; + } + + if (! is_string($casesDescription = $schema->type->getAttribute('casesDescription'))) { + return null; + } + + return $casesDescription; + } + + public function toArray() + { + if ($this->nullable) { + return (new AnyOf)->setItems([(clone $this)->nullable(false), new NullType])->toArray(); + } + + $parentArray = parent::toArray(); + unset($parentArray['type']); + + return array_filter([ + ...$parentArray, + '$ref' => "#/components/{$this->referenceType}/{$this->getUniqueName()}", + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/RequestBodyObject.php b/vendor/dedoc/scramble/src/Support/Generator/RequestBodyObject.php new file mode 100644 index 00000000..ae62634d --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/RequestBodyObject.php @@ -0,0 +1,61 @@ + */ + public array $content; + + /** + * Determines if the request body is required in the request. + */ + public bool $required = false; + + public static function make() + { + return new self; + } + + public function setContent(string $type, Schema|Reference $schema) + { + $this->content[$type] = $schema; + + return $this; + } + + public function required(bool $required = true) + { + $this->required = $required; + + return $this; + } + + public function description(string $string) + { + $this->description = $string; + + return $this; + } + + public function toArray() + { + $result = array_filter([ + 'description' => $this->description, + 'required' => $this->required, + ]); + + $content = []; + foreach ($this->content as $mediaType => $schema) { + $content[$mediaType] = [ + 'schema' => $schema->toArray(), + ]; + } + + $result['content'] = $content; + + return $result; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Response.php b/vendor/dedoc/scramble/src/Support/Generator/Response.php new file mode 100644 index 00000000..458b3101 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Response.php @@ -0,0 +1,159 @@ + */ + public array $content = []; + + public string $description = ''; + + /** @var array */ + public array $headers = []; + + /** @var array */ + public array $links = []; + + public function __construct(int|string|null $code) + { + $this->code = $code; + } + + public static function make(int|string|null $code) + { + return new self($code); + } + + /** + * @return $this + */ + public function setCode(int|string|null $code): self + { + $this->code = $code; + + return $this; + } + + /** + * @return $this + */ + public function setDescription(string $string): self + { + $this->description = $string; + + return $this; + } + + /** + * @param Schema|Reference $schema + * @return $this + */ + public function setContent(string $type, $schema): self + { + $this->content[$type] = $schema; + + return $this; + } + + /** + * @return $this + */ + public function addHeader(string $name, Header|Reference $header): self + { + $this->headers[$name] = $header; + + return $this; + } + + /** + * @return $this + */ + public function removeHeader(string $name): self + { + unset($this->headers[$name]); + + return $this; + } + + /** + * @param array $headers + * @return $this + */ + public function setHeaders(array $headers): self + { + $this->headers = $headers; + + return $this; + } + + /** + * @return $this + */ + public function addLink(string $name, Link|Reference $link): self + { + $this->links[$name] = $link; + + return $this; + } + + /** + * @return $this + */ + public function removeLink(string $name): self + { + unset($this->links[$name]); + + return $this; + } + + /** + * @param array $links + * @return $this + */ + public function setLinks(array $links): self + { + $this->links = $links; + + return $this; + } + + public function toArray() + { + $result = [ + 'description' => $this->description, + ]; + + if (count($this->content)) { + $result['content'] = array_map(fn ($c) => ['schema' => $c->toArray()], $this->content); + } + + $headers = array_map(fn ($header) => $header->toArray(), $this->headers); + $links = array_map(fn ($link) => $link->toArray(), $this->links); + + return array_merge( + $result, + $headers ? ['headers' => $headers] : [], + $links ? ['links' => $links] : [], + $this->extensionPropertiesToArray(), + ); + } + + public function getContent(string $mediaType) + { + return $this->content[$mediaType]; + } + + /** + * @deprecated Use `setDescription` instead. + */ + public function description(string $string) + { + return $this->setDescription($string); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Schema.php b/vendor/dedoc/scramble/src/Support/Generator/Schema.php new file mode 100644 index 00000000..2ba3ec6e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Schema.php @@ -0,0 +1,77 @@ +setType($type); + + return $schema; + } + + private function setType(Type $type) + { + $this->type = $type; + + return $this; + } + + public function toArray() + { + $typeArray = $this->type->toArray(); + + if ($typeArray instanceof \stdClass) { // mixed + $typeArray = []; + } + + $result = array_merge($typeArray, array_filter([ + 'title' => $this->title, + ])); + + if (empty($result)) { + return (object) []; + } + + return $result; + } + + public static function createFromParameters(array $parameters) + { + $schema = (new static)->setType($type = new ObjectType); + + collect($parameters) + ->each(function (Parameter $parameter) use ($type) { + $paramType = $parameter->schema ?? new StringType; + $paramType = $paramType instanceof Schema ? $paramType->type : $paramType; + + $paramType->setDescription($parameter->description); + $paramType->example($parameter->example); + + $type->addProperty($parameter->name, $paramType); + }) + ->tap(fn (Collection $params) => $type->setRequired( + $params->where('required', true)->map->name->values()->all() + )); + + return $schema; + } + + public function setTitle(?string $title): Schema + { + $this->title = $title; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/SecurityRequirement.php b/vendor/dedoc/scramble/src/Support/Generator/SecurityRequirement.php new file mode 100644 index 00000000..123ae8d3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/SecurityRequirement.php @@ -0,0 +1,28 @@ + + */ + private array $items = []; + + public function __construct(array|string $items) + { + if (is_string($items)) { // keeping backward compatibility with synthetic Security object + $this->items[$items] = []; + } else { + $this->items = $items; + } + } + + public function toArray() + { + return count($this->items) ? $this->items : (object) []; + } +} + +// To keep backward compatibility +class_alias(SecurityRequirement::class, 'Dedoc\Scramble\Support\Generator\Security'); diff --git a/vendor/dedoc/scramble/src/Support/Generator/SecurityScheme.php b/vendor/dedoc/scramble/src/Support/Generator/SecurityScheme.php new file mode 100644 index 00000000..2c88b044 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/SecurityScheme.php @@ -0,0 +1,78 @@ +type = $type; + } + + public static function apiKey(string $in, string $name) + { + return (new ApiKeySecurityScheme($in, $name))->as('apiKey'); + } + + public static function http(string $scheme, string $bearerFormat = '') + { + return (new HttpSecurityScheme($scheme, $bearerFormat))->as('http'); + } + + public static function oauth2() + { + return (new Oauth2SecurityScheme)->as('oauth2'); + } + + public static function openIdConnect(string $openIdConnectUrl) + { + return (new OpenIdConnectUrlSecurityScheme($openIdConnectUrl))->as('openIdConnect'); + } + + public static function mutualTLS() + { + return (new static('mutualTLS'))->as('mutualTLS'); + } + + public function as(string $schemeName): self + { + $this->schemeName = $schemeName; + + return $this; + } + + public function setDescription(string $description): self + { + $this->description = $description; + + return $this; + } + + public function default(): self + { + $this->default = true; + + return $this; + } + + public function toArray() + { + return array_filter([ + 'type' => $this->type, + 'description' => $this->description, + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/ApiKeySecurityScheme.php b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/ApiKeySecurityScheme.php new file mode 100644 index 00000000..49069c26 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/ApiKeySecurityScheme.php @@ -0,0 +1,28 @@ +in = $in; + $this->name = $name; + } + + public function toArray() + { + return array_merge(parent::toArray(), [ + 'in' => $this->in, + 'name' => $this->name, + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/HttpSecurityScheme.php b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/HttpSecurityScheme.php new file mode 100644 index 00000000..66a7fbbd --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/HttpSecurityScheme.php @@ -0,0 +1,28 @@ +scheme = $scheme; + $this->bearerFormat = $bearerFormat; + } + + public function toArray() + { + return array_merge(parent::toArray(), array_filter([ + 'scheme' => $this->scheme, + 'bearerFormat' => $this->bearerFormat, + ])); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlow.php b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlow.php new file mode 100644 index 00000000..01bbff63 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlow.php @@ -0,0 +1,56 @@ + */ + public array $scopes = []; + + public function authorizationUrl(string $authorizationUrl): OAuthFlow + { + $this->authorizationUrl = $authorizationUrl; + + return $this; + } + + public function tokenUrl(string $tokenUrl): OAuthFlow + { + $this->tokenUrl = $tokenUrl; + + return $this; + } + + public function refreshUrl(string $refreshUrl): OAuthFlow + { + $this->refreshUrl = $refreshUrl; + + return $this; + } + + public function addScope(string $name, string $description = '') + { + $this->scopes[$name] = $description; + + return $this; + } + + public function toArray() + { + return [ + ...array_filter([ + 'authorizationUrl' => $this->authorizationUrl, + 'tokenUrl' => $this->tokenUrl, + 'refreshUrl' => $this->refreshUrl, + ]), + // Never filter 'scopes' as it is allowed to be empty. If empty it must be an object + 'scopes' => empty($this->scopes) ? new \stdClass : $this->scopes, + ]; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlows.php b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlows.php new file mode 100644 index 00000000..b4b50e33 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OAuthFlows.php @@ -0,0 +1,55 @@ +implicit = $flow; + + return $this; + } + + public function password(?OAuthFlow $flow): OAuthFlows + { + $this->password = $flow; + + return $this; + } + + public function clientCredentials(?OAuthFlow $flow): OAuthFlows + { + $this->clientCredentials = $flow; + + return $this; + } + + public function authorizationCode(?OAuthFlow $flow): OAuthFlows + { + $this->authorizationCode = $flow; + + return $this; + } + + public function toArray() + { + return array_map( + fn ($f) => $f->toArray(), + array_filter([ + 'implicit' => $this->implicit, + 'password' => $this->password, + 'clientCredentials' => $this->clientCredentials, + 'authorizationCode' => $this->authorizationCode, + ]) + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/Oauth2SecurityScheme.php b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/Oauth2SecurityScheme.php new file mode 100644 index 00000000..23e50474 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/Oauth2SecurityScheme.php @@ -0,0 +1,41 @@ +oAuthFlows = new OAuthFlows; + } + + public function flows(callable $flows) + { + $flows($this->oAuthFlows); + + return $this; + } + + public function flow(string $name, callable $flow) + { + return $this->flows(function (OAuthFlows $flows) use ($flow, $name) { + if (! $flows->$name) { + $flows->$name(new OAuthFlow); + } + $flow($flows->$name); + }); + } + + public function toArray() + { + return array_merge(parent::toArray(), [ + 'flows' => $this->oAuthFlows->toArray(), + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OpenIdConnectUrlSecurityScheme.php b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OpenIdConnectUrlSecurityScheme.php new file mode 100644 index 00000000..b8c238eb --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/SecuritySchemes/OpenIdConnectUrlSecurityScheme.php @@ -0,0 +1,24 @@ +openIdConnectUrl = $openIdConnectUrl; + } + + public function toArray() + { + return array_merge(parent::toArray(), [ + 'openIdConnectUrl' => $this->openIdConnectUrl, + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Server.php b/vendor/dedoc/scramble/src/Support/Generator/Server.php new file mode 100644 index 00000000..781009e2 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Server.php @@ -0,0 +1,53 @@ + + */ + public array $variables = []; + + public function __construct(string $url) + { + $this->url = $url; + } + + public static function make(string $url) + { + return new self($url); + } + + public function setDescription(string $description): Server + { + $this->description = $description; + + return $this; + } + + public function toArray() + { + return array_filter([ + 'url' => $this->url, + 'description' => $this->description, + 'variables' => count($this->variables) + ? array_map(fn (ServerVariable $v) => $v->toArray(), $this->variables) + : null, + ]); + } + + /** + * @param array $variables + */ + public function variables(array $variables) + { + $this->variables = $variables; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/ServerVariable.php b/vendor/dedoc/scramble/src/Support/Generator/ServerVariable.php new file mode 100644 index 00000000..571d7dd7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/ServerVariable.php @@ -0,0 +1,42 @@ +|null $enum + */ + public function __construct( + public string $default, + public ?array $enum = null, + public ?string $description = null + ) {} + + /** + * @param non-empty-array|null $enum + */ + public static function make( + string $default, + ?array $enum = null, + ?string $description = null + ) { + return new self($default, $enum, $description); + } + + public function toArray() + { + $result = array_filter([ + 'default' => $this->default, + 'enum' => $this->enum && count($this->enum) ? $this->enum : null, + 'description' => $this->description, + ]); + + return array_merge( + $result, + $this->extensionPropertiesToArray() + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Tag.php b/vendor/dedoc/scramble/src/Support/Generator/Tag.php new file mode 100644 index 00000000..3d256432 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Tag.php @@ -0,0 +1,27 @@ + $this->name, + 'description' => $this->description, + ]); + + return array_merge( + $result, + $this->extensionPropertiesToArray() + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/TypeTransformer.php b/vendor/dedoc/scramble/src/Support/Generator/TypeTransformer.php new file mode 100644 index 00000000..b8310318 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/TypeTransformer.php @@ -0,0 +1,559 @@ + */ + private array $cache = []; + + /** @var TypeToSchemaExtension[] */ + private array $typeToSchemaExtensions; + + /** @var ExceptionToResponseExtension[] */ + private array $exceptionToResponseExtensions; + + /** + * @param class-string[] $typeToSchemaExtensionsClasses + * @param class-string[] $exceptionToResponseExtensionsClasses + */ + public function __construct( + private Infer $infer, + public readonly OpenApiContext $context, + array $typeToSchemaExtensionsClasses = [], + array $exceptionToResponseExtensionsClasses = [] + ) { + $this->typeToSchemaExtensions = collect($typeToSchemaExtensionsClasses) + ->map(function ($extensionClass) { + return new $extensionClass($this->infer, $this, $this->getComponents(), $this->context); + }) + ->all(); + + $this->exceptionToResponseExtensions = collect($exceptionToResponseExtensionsClasses) + ->map(function ($extensionClass) { + return new $extensionClass($this->infer, $this, $this->getComponents(), $this->context); + }) + ->all(); + } + + public function getComponents(): Components + { + return $this->context->openApi->components; + } + + public function transform(Type $type): OpenApiType + { + if ($type instanceof TemplateType && $type->is) { + $type = $type->is; + } + + $openApiType = $this->shouldCache($type) + ? $this->transformCached($type) + : $this->transformUncached($type); + + return $this->applyTypeAttributes($openApiType, $type); + } + + private function getCacheKey(Type $type): string + { + if ($type instanceof TemplateType && $type->is) { + $type = $type->is; + } + + return $type::class.'|'.$type->toString(); + } + + private function shouldCache(Type $type): bool + { + $attributesHandledAfterCache = array_flip(['docNode', 'format', 'file', 'line']); + + return ! array_diff_key($type->attributes(), $attributesHandledAfterCache) + && ! $type instanceof ArrayItemType_ + && ! $type instanceof \Dedoc\Scramble\Support\Type\KeyedArrayType + && ! $type instanceof \Dedoc\Scramble\Support\Type\ArrayType + && ! $type instanceof Union + && ! $type instanceof \Dedoc\Scramble\Support\Type\IntersectionType; + } + + private function transformCached(Type $type): OpenApiType + { + if ($type instanceof TemplateType && $type->is) { + $type = $type->is; + } + + $key = $this->getCacheKey($type); + + if (isset($this->cache[$key])) { + $openApiType = $this->cache[$key]->clone(); + $this->registerReferences($openApiType); + + return $openApiType; + } + + $openApiType = $this->transformUncached($type); + $this->cache[$key] = $openApiType->clone(); + + return $openApiType; + } + + private function registerReferences(OpenApiType $type): void + { + if ($type instanceof Reference) { + $this->context->references->schemas->add($type->fullName, $type); + + return; + } + + if ($type instanceof ArrayType) { + $this->registerReferences($type->items); + + foreach ($type->prefixItems as $item) { + $this->registerReferences($item); + } + + return; + } + + if ($type instanceof ObjectType) { + foreach ($type->properties as $property) { + if ($property) { + $this->registerReferences($property); + } + } + + if ($type->additionalProperties) { + $this->registerReferences($type->additionalProperties); + } + + return; + } + + if ($type instanceof AnyOf || $type instanceof AllOf) { + foreach ($type->items as $item) { + $this->registerReferences($item); + } + } + } + + private function transformUncached(Type $type): OpenApiType + { + if ($type instanceof TemplateType && $type->is) { + $type = $type->is; + } + + $openApiType = new UnknownType; + + if ( + $type instanceof \Dedoc\Scramble\Support\Type\KeyedArrayType + && $type->isList + ) { + $visibleItems = collect($type->items)->reject(fn (ArrayItemType_ $item) => $this->isHiddenArrayItem($item))->values()->all(); + /** @see https://stackoverflow.com/questions/57464633/how-to-define-a-json-array-with-concrete-item-definition-for-every-index-i-e-a */ + $openApiType = (new ArrayType) + ->setMin(count($visibleItems)) + ->setMax(count($visibleItems)) + ->setPrefixItems( + array_map( + fn ($item) => $this->transform($item->value), + $visibleItems + ) + ) + ->setAdditionalItems(false); + } elseif ( + $type instanceof \Dedoc\Scramble\Support\Type\KeyedArrayType + && ! $type->isList + ) { + $openApiType = new ObjectType; + $requiredKeys = []; + + $props = collect($type->items) + ->reject(fn (ArrayItemType_ $item) => $this->isHiddenArrayItem($item)) + ->mapWithKeys(function (ArrayItemType_ $item) use (&$requiredKeys) { + if (! $item->isOptional) { + $requiredKeys[] = $item->key; + } + + return [ + (string) $item->key => $this->transform($item), + ]; + }); + + $openApiType->properties = $props->all(); + + $openApiType->setRequired($requiredKeys); + } elseif ( + $type instanceof \Dedoc\Scramble\Support\Type\ArrayType + ) { + $keyType = $this->transform($type->key); + + if ($keyType instanceof IntegerType) { + $openApiType = (new ArrayType)->setItems($this->transform($type->value)); + } else { + $openApiType = (new ObjectType) + ->additionalProperties($this->transform($type->value)); + } + } elseif ($type instanceof ArrayItemType_) { + $typeValue = clone $type->value; + + /** @var PhpDocNode|null $valueDocNode */ + $valueDocNode = $type->value->getAttribute('docNode'); + /** @var PhpDocNode|null $arrayItemDocNode */ + $arrayItemDocNode = $type->getAttribute('docNode'); + + if ($valueDocNode || $arrayItemDocNode) { + $docNode = new PhpDocNode([ + ...($arrayItemDocNode?->children ?: []), + ...($valueDocNode?->children ?: []), + ]); + PhpDoc::addSummaryAttributes($docNode); + + $typeValue->setAttribute('docNode', $docNode); + } + + $openApiType = $this->transform($typeValue); + + // @todo use PhpDocSchemaTransformer + + /** @var PhpDocNode|null $docNode */ + $docNode = $typeValue->getAttribute('docNode'); + + if ($docNode) { + $varNode = array_values($docNode->getVarTagValues())[0] ?? null; + + $openApiType = $varNode + ? $this->transform(PhpDocTypeHelper::toType($varNode->type)) + : $openApiType; + + $commentDescription = trim($docNode->getAttribute('summary').' '.$docNode->getAttribute('description')); // @phpstan-ignore binaryOp.invalid, binaryOp.invalid + $varNodeDescription = $varNode && $varNode->description ? trim($varNode->description) : ''; + if ($commentDescription || $varNodeDescription) { + $openApiType->setDescription(implode('. ', array_filter([$varNodeDescription, $commentDescription]))); + } + + if ($examples = ExamplesExtractor::make($docNode)->extract(preferString: $openApiType instanceof StringType)) { + $openApiType->examples($examples); + } + + if ($default = ExamplesExtractor::make($docNode, '@default')->extract(preferString: $openApiType instanceof StringType)) { + $openApiType->default($default[0]); + } + + $deprecated = array_values($docNode->getTagsByName('@deprecated'))[0]->value ?? null; + if ($deprecated instanceof DeprecatedTagValueNode) { + $openApiType->deprecated(true); + + if ($deprecated->description) { + $openApiType->setDescription(implode(' ', array_filter([ + $openApiType->description, + $deprecated->description, + ]))); + } + } + + if ($format = array_values($docNode->getTagsByName('@format'))[0]->value->value ?? null) { + $openApiType->format($format); + } + } + } elseif ($type instanceof Union) { + if (count($type->types) === 2 && collect($type->types)->contains(fn ($t) => $t instanceof \Dedoc\Scramble\Support\Type\NullType)) { + $notNullType = collect($type->types)->first(fn ($t) => ! ($t instanceof \Dedoc\Scramble\Support\Type\NullType)); + if ($notNullType) { + $openApiType = $this->transform($notNullType)->nullable(true); + } else { + $openApiType = new NullType; + } + } else { + [$literals, $otherTypes] = collect($type->types) + ->partition(fn ($t) => $t instanceof LiteralStringType || $t instanceof LiteralIntegerType) + ->all(); + + [$stringLiterals, $integerLiterals] = collect($literals) + ->partition(fn ($t) => $t instanceof LiteralStringType) + ->all(); + + $items = array_map($this->transform(...), $otherTypes->values()->toArray()); // @phpstan-ignore argument.type + $literalSchemas = []; + + if ($stringLiterals->count()) { + $items[] = $literalSchemas[] = (new StringType)->enum( + $stringLiterals->map->value->unique()->values()->toArray() // @phpstan-ignore property.notFound + ); + } + + if ($integerLiterals->count()) { + $items[] = $literalSchemas[] = (new IntegerType)->enum( + $integerLiterals->map->value->unique()->values()->toArray() // @phpstan-ignore property.notFound + ); + } + + // In case $otherTypes consist just of null and there is string or integer literals, make type nullable + $otherTypesIsNullable = count($otherTypes) === 1 && collect($otherTypes)->contains(fn ($t) => $t instanceof \Dedoc\Scramble\Support\Type\NullType); + if ($otherTypesIsNullable && ($stringLiterals->count() || $integerLiterals->count())) { + $items = array_map(function ($s) { + $s->nullable(true); + // Per JSON Schema type and enum are independent constraints. + // When type allows null, enum must also include null or it will reject valid null values. + if (count($s->enum) > 0) { + $s->enum(array_merge($s->enum, [null])); + } + + return $s; + }, $literalSchemas); + } + + // Removing duplicated schemas before making a resulting AnyOf type. + $uniqueItems = collect($items)->unique(fn ($i) => json_encode($i->toArray()))->values()->all(); + $openApiType = count($uniqueItems) === 1 ? $uniqueItems[0] : (new AnyOf)->setItems($uniqueItems); + } + } elseif ($type instanceof LiteralStringType) { + $openApiType = (new StringType)->const($type->value); + } elseif ($type instanceof LiteralIntegerType) { + $openApiType = (new IntegerType)->const($type->value); + } elseif ($type instanceof LiteralFloatType) { + $openApiType = (new NumberType)->const($type->value); + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\StringType) { + $openApiType = new StringType; + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\FloatType) { + $openApiType = new NumberType; + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\IntegerRangeType) { + $openApiType = new IntegerType; + + if ($type->min !== null) { + $openApiType->setMin($type->min); + } + + if ($type->max !== null) { + $openApiType->setMax($type->max); + } + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\IntegerType) { + $openApiType = new IntegerType; + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\BooleanType) { + $openApiType = new BooleanType; + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\NullType) { + $openApiType = new NullType; + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\MixedType) { + $openApiType = new MixedType; + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\ObjectType) { + $openApiType = new ObjectType; + } elseif ($type instanceof \Dedoc\Scramble\Support\Type\IntersectionType) { + $openApiType = (new AllOf)->setItems(array_map( + fn ($t) => $this->transform($t), + $type->types, + )); + } + + if ($typeHandledByExtension = $this->handleUsingExtensions($type)) { + $openApiType = $typeHandledByExtension; + } + + return $openApiType; + } + + private function applyTypeAttributes(OpenApiType $openApiType, Type $type): OpenApiType + { + if ($type->hasAttribute('format')) { + $openApiType->format($type->getAttribute('format')); + } + + if ($type->hasAttribute('file')) { + $openApiType->setAttribute('file', $type->getAttribute('file')); + } + + if ($type->hasAttribute('line')) { + $openApiType->setAttribute('line', $type->getAttribute('line')); + } + + return $openApiType; + } + + private function handleUsingExtensions(Type $type): OpenApiType|Reference|null + { + $extension = collect($this->typeToSchemaExtensions) + ->filter(fn ($ext) => method_exists($ext, 'shouldHandle') && $ext->shouldHandle($type)) + ->values() + ->last(); + + if (! $extension) { + return null; + } + + /** @var Reference|null $reference */ + $reference = method_exists($extension, 'reference') + ? $extension->reference($type) + : null; + + if ($reference && $this->context->references->schemas->has($reference->fullName)) { + return $this->context->references->schemas->add($reference->fullName, $reference); + } + + if ($reference) { + $reference = $this->context->references->schemas->add($reference->fullName, $reference); + + $this->getComponents()->addSchema($reference->fullName, Schema::fromType(new UnknownType)); + } + + $handledType = $extension->toSchema($type); + + if ($handledType && $reference) { + $this->getComponents()->addSchema($reference->fullName, Schema::fromType($handledType)); + } + + /* + * If we couldn't handle a type, the reference is removed. + */ + if (! $handledType && $reference) { + $this->getComponents()->removeSchema($reference->fullName); + } + + return $reference ?: $handledType; + } + + public function toResponse(Type $type): Response|Reference|null + { + // In case of union type being returned and all of its types resulting in the same response, we want to make + // sure to take only unique types to avoid having the same types in the response. + if ($type instanceof Union) { + $uniqueItems = collect($type->types)->unique(fn ($i) => json_encode($this->transform($i)->toArray()))->values()->all(); + $type = count($uniqueItems) === 1 ? $uniqueItems[0] : Union::wrap($uniqueItems); + } + + if (! $response = $this->handleResponseUsingExtensions($type)) { + if ($type->isInstanceOf(\Throwable::class)) { + return null; + } + + $response = Response::make(200) + ->setContent( + 'application/json', + Schema::fromType($this->transform($type)), + ); + } + + if ($docNode = $type->getAttribute('docNode')) { + /** @var PhpDocNode $docNode */ + $description = (string) Str::of($docNode->getAttribute('summary') ?: '') // @phpstan-ignore argument.type + ->append("\n\n".($docNode->getAttribute('description') ?: '')) // @phpstan-ignore binaryOp.invalid + ->append("\n\n".$response->description) + ->trim(); + $response->description($description); + + $code = (int) (array_values($docNode->getTagsByName('@status'))[0]->value->value ?? $response->code ?? 200); + $response->code = $code; + + if ($varType = $docNode->getVarTagValues()[0]->type ?? null) { + $type = PhpDocTypeHelper::toType($varType); + + $typeResponse = $this->toResponse($type); + + if ($typeResponse instanceof Reference) { + $typeResponse = deep_copy($typeResponse->resolve()); + } + + if ($typeResponse instanceof Response) { + $response->setContent('application/json', $typeResponse->getContent('application/json')); + } + } + } + + return $response; + } + + private function handleResponseUsingExtensions(Type $type): Response|Reference|null + { + if (! $type->isInstanceOf(\Throwable::class)) { + foreach (array_reverse($this->typeToSchemaExtensions) as $extension) { + if (! method_exists($extension, 'shouldHandle')) { + continue; + } + + if (! $extension->shouldHandle($type)) { + continue; + } + + if ($response = $extension->toResponse($type)) { + return $response; + } + } + } + + // We want latter registered extensions to have a higher priority to allow custom extensions to override default ones. + $extension = collect($this->exceptionToResponseExtensions) + ->filter(fn (ExceptionToResponseExtension $ext) => method_exists($ext, 'shouldHandle') && $ext->shouldHandle($type)) + ->reverse() + ->first(); + + if (! $extension) { + return null; + } + + /** @var Reference|null $reference */ + $reference = method_exists($extension, 'reference') + ? $extension->reference($type) + : null; + + if ($reference && $this->getComponents()->has($reference)) { + return $reference; + } + + if ($response = $extension->toResponse($type)) { + if ($reference) { + return $this->getComponents()->add($reference, $response); + } + + return $response; + } + + return null; + } + + private function isHiddenArrayItem(ArrayItemType_ $item): bool + { + /** @var PhpDocNode|null $arrayItemDocNode */ + $arrayItemDocNode = $item->getAttribute('docNode'); + /** @var PhpDocNode|null $valueDocNode */ + $valueDocNode = $item->value->getAttribute('docNode'); + + foreach ([$arrayItemDocNode, $valueDocNode] as $docNode) { + if ($docNode && count($docNode->getTagsByName('@hidden')) > 0) { + return true; + } + } + + return false; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Types/ArrayType.php b/vendor/dedoc/scramble/src/Support/Generator/Types/ArrayType.php new file mode 100644 index 00000000..107f8d95 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Types/ArrayType.php @@ -0,0 +1,96 @@ +setAttribute('missing', true); + + $this->items = $defaultMissingType; + } + + public function clone(): static + { + $clone = parent::clone(); + $clone->items = $clone->items->clone(); + $clone->prefixItems = array_map( + fn (Type $item) => $item->clone(), + $clone->prefixItems, + ); + + return $clone; + } + + public function setMin($min) + { + $this->minItems = $min; + + return $this; + } + + public function setMax($max) + { + $this->maxItems = $max; + + return $this; + } + + public function setItems($items) + { + $this->items = $items; + + return $this; + } + + public function setPrefixItems($prefixItems) + { + $this->prefixItems = $prefixItems; + + return $this; + } + + public function setAdditionalItems($additionalItems) + { + $this->additionalItems = $additionalItems; + + return $this; + } + + public function toArray() + { + $shouldOmitItems = $this->items->getAttribute('missing') + && count($this->prefixItems); + + return array_merge( + parent::toArray(), + $shouldOmitItems ? [] : [ + 'items' => $this->items->toArray(), + ], + $this->prefixItems ? [ + 'prefixItems' => array_map(fn ($item) => $item->toArray(), $this->prefixItems), + ] : [], + array_filter([ + 'minItems' => $this->minItems, + 'maxItems' => $this->maxItems, + 'additionalItems' => $this->additionalItems, + ], fn ($v) => $v !== null) + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Types/BooleanType.php b/vendor/dedoc/scramble/src/Support/Generator/Types/BooleanType.php new file mode 100644 index 00000000..a4ffb6c8 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Types/BooleanType.php @@ -0,0 +1,11 @@ +min = $min; + + return $this; + } + + public function setMax($max) + { + $this->max = $max; + + return $this; + } + + public function toArray() + { + return array_merge(parent::toArray(), array_filter([ + 'minimum' => $this->min, + 'maximum' => $this->max, + ], fn ($v) => $v !== null)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Types/ObjectType.php b/vendor/dedoc/scramble/src/Support/Generator/Types/ObjectType.php new file mode 100644 index 00000000..2ba86cd6 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Types/ObjectType.php @@ -0,0 +1,98 @@ + */ + public array $properties = []; + + /** @var string[] */ + public array $required = []; + + public ?Type $additionalProperties = null; + + public function __construct() + { + parent::__construct('object'); + } + + public function clone(): static + { + $clone = parent::clone(); + + foreach ($clone->properties as $name => $property) { + $clone->properties[$name] = $property?->clone(); + } + + if ($clone->additionalProperties) { + $clone->additionalProperties = $clone->additionalProperties->clone(); + } + + return $clone; + } + + public function addProperty(string $name, $propertyType) + { + $this->properties[$name] = $propertyType; + + return $this; + } + + public function hasProperty(string $name) + { + return array_key_exists($name, $this->properties); + } + + public function getProperty(string $name) + { + return $this->properties[$name]; + } + + public function setRequired(array $keys) + { + $this->required = $keys; + + return $this; + } + + public function addRequired(array $keys) + { + $this->required = array_merge( + $this->required, + array_diff($keys, $this->required), + ); + + return $this; + } + + public function toArray() + { + $result = parent::toArray(); + + if (count($this->properties)) { + $properties = []; + foreach ($this->properties as $name => $property) { + $properties[$name] = $property ? $property->toArray() : ['type' => 'string']; + } + $result['properties'] = $properties; + } + + if (count($this->required)) { + $result['required'] = $this->required; + } + + if ($this->additionalProperties) { + $result['additionalProperties'] = $this->additionalProperties->toArray(); + } + + return $result; + } + + public function additionalProperties(Type $type) + { + $this->additionalProperties = $type; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Types/StringType.php b/vendor/dedoc/scramble/src/Support/Generator/Types/StringType.php new file mode 100644 index 00000000..ee863ca3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Types/StringType.php @@ -0,0 +1,37 @@ +min = $min; + + return $this; + } + + public function setMax($max) + { + $this->max = $max; + + return $this; + } + + public function toArray() + { + return array_merge(parent::toArray(), array_filter([ + 'minLength' => $this->min, + 'maxLength' => $this->max, + ], fn ($v) => $v !== null)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Types/Type.php b/vendor/dedoc/scramble/src/Support/Generator/Types/Type.php new file mode 100644 index 00000000..86fc6581 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Types/Type.php @@ -0,0 +1,223 @@ + */ + public $examples = []; + + public array $enum = []; + + /** @var scalar|null */ + public $const = null; + + public bool $nullable = false; + + public bool $deprecated = false; + + public ?string $pattern = null; + + public function __construct(string $type) + { + $this->type = $type; + $this->example = new MissingValue; + $this->default = new MissingValue; + } + + public function clone(): static + { + return clone $this; + } + + /** + * @return $this + */ + public function nullable(bool $nullable): self + { + $this->nullable = $nullable; + + return $this; + } + + /** + * @return $this + */ + public function format(string $format): self + { + $this->format = $format; + + return $this; + } + + /** + * @return $this + */ + public function contentMediaType(string $mediaType): self + { + $this->contentMediaType = $mediaType; + + return $this; + } + + /** + * @return $this + */ + public function contentEncoding(string $encoding): self + { + $this->contentEncoding = $encoding; + + return $this; + } + + /** + * @return $this + */ + public function addProperties(Type $fromType): self + { + $this->attributes = $fromType->attributes; + $this->format = $fromType->format; + $this->description = $fromType->description; + $this->contentMediaType = $fromType->contentMediaType; + $this->contentEncoding = $fromType->contentEncoding; + $this->example = $fromType->example; + $this->default = $fromType->default; + $this->examples = $fromType->examples; + $this->enum = $fromType->enum; + $this->const = $fromType->const; + $this->nullable = $fromType->nullable; + $this->deprecated = $fromType->deprecated; + $this->pattern = $fromType->pattern; + + return $this; + } + + public function toArray() + { + return array_merge( + array_filter([ + 'type' => $this->nullable ? [$this->type, 'null'] : $this->type, + 'format' => $this->format, + 'contentMediaType' => $this->contentMediaType, + 'contentEncoding' => $this->contentEncoding, + 'description' => $this->description, + 'deprecated' => $this->deprecated, + 'pattern' => $this->pattern, + 'enum' => count($this->enum) ? $this->enum : null, + 'const' => ! is_null($this->const) ? $this->const : null, + ]), + $this->example instanceof MissingValue ? [] : ['example' => $this->example], + $this->default instanceof MissingValue ? [] : ['default' => $this->default], + count( + $examples = collect($this->examples) + ->reject(fn ($example) => $example instanceof MissingValue) + ->values() + ->toArray() + ) ? ['examples' => $examples] : [], + $this->extensionPropertiesToArray(), + ); + } + + /** + * @return $this + */ + public function setDescription(string $description): self + { + $this->description = $description; + + return $this; + } + + /** + * @return $this + */ + public function enum(array $enum): self + { + $this->enum = $enum; + + return $this; + } + + /** + * @param scalar $const + * @return $this + */ + public function const($const): self + { + $this->const = $const; + + return $this; + } + + /** + * @param array|scalar|null|MissingValue $example + * @return $this + */ + public function example($example): self + { + $this->example = $example; + + return $this; + } + + /** + * @param array|scalar|null|MissingValue $default + * @return $this + */ + public function default($default): self + { + $this->default = $default; + + return $this; + } + + /** + * @param array $examples + * @return $this + */ + public function examples(array $examples): self + { + $this->examples = $examples; + + return $this; + } + + /** @return $this */ + public function deprecated(bool $deprecated): self + { + $this->deprecated = $deprecated; + + return $this; + } + + /** @return $this */ + public function pattern(?string $pattern): self + { + $this->pattern = $pattern; + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/Types/UnknownType.php b/vendor/dedoc/scramble/src/Support/Generator/Types/UnknownType.php new file mode 100644 index 00000000..35e5f614 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/Types/UnknownType.php @@ -0,0 +1,5 @@ +unique) + ->take(-$this->fallbackEloquentPartsCount) + ->join($this->separator); + } + + public function getFallback(): string + { + return implode($this->separator, $this->unique); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/UniqueNamesOptionsCollection.php b/vendor/dedoc/scramble/src/Support/Generator/UniqueNamesOptionsCollection.php new file mode 100644 index 00000000..6b189c5f --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/UniqueNamesOptionsCollection.php @@ -0,0 +1,61 @@ + + */ + private array $eloquentNames = []; + + /** + * @var array + */ + private array $fallbackEloquentNames = []; + + /** + * @var array + */ + private array $fallbackNames = []; + + public function __construct( + private Collection $names = new Collection, + ) {} + + public function push(UniqueNameOptions $name) + { + $this->names->push($name); + + $eloquentKey = $name->eloquent ?? ''; + $this->eloquentNames[$eloquentKey] ??= []; + $this->eloquentNames[$eloquentKey][] = $name; + + $this->fallbackEloquentNames[$name->getFallbackEloquent()] ??= []; + $this->fallbackEloquentNames[$name->getFallbackEloquent()][] = $name; + + $this->fallbackNames[$name->getFallback()] ??= []; + $this->fallbackNames[$name->getFallback()][] = $name; + + return $this; + } + + public function getUniqueName(UniqueNameOptions $name, ?callable $onNotUniqueFallback = null): string + { + if ($name->eloquent && count($this->eloquentNames[$name->eloquent]) === 1) { + return $name->eloquent; + } + + if (count($this->fallbackEloquentNames[$name->getFallbackEloquent()]) === 1) { + return $name->getFallbackEloquent(); + } + + if (count($this->fallbackNames[$name->getFallback()]) === 1) { + return $name->getFallback(); + } + + return $onNotUniqueFallback ? $onNotUniqueFallback($name->getFallback()) : throw new \LogicException('Cannot retrieve a unique name'); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/WithAttributes.php b/vendor/dedoc/scramble/src/Support/Generator/WithAttributes.php new file mode 100644 index 00000000..a01e7a40 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/WithAttributes.php @@ -0,0 +1,50 @@ + */ + private $attributes = []; + + /** + * @param mixed $value + */ + public function setAttribute(string $key, $value): void + { + $this->attributes[$key] = $value; + } + + public function hasAttribute(string $key): bool + { + return array_key_exists($key, $this->attributes); + } + + public function getAttribute(string $key, mixed $default = null): mixed + { + if ($this->hasAttribute($key)) { + return $this->attributes[$key]; + } + + return $default; + } + + /** + * @return array + */ + public function attributes(): array + { + return $this->attributes; + } + + /** + * @param array $attributes + * @return $this + */ + public function mergeAttributes($attributes): self + { + $this->attributes = array_merge($this->attributes, $attributes); + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Generator/WithExtensions.php b/vendor/dedoc/scramble/src/Support/Generator/WithExtensions.php new file mode 100644 index 00000000..7b86666d --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Generator/WithExtensions.php @@ -0,0 +1,56 @@ + */ + private $extensions = []; + + public function setExtensionProperty(string $key, mixed $value): void + { + $this->extensions[$key] = $value; + } + + public function hasExtensionProperty(string $key): bool + { + return array_key_exists($key, $this->extensions); + } + + public function getExtensionProperty(string $key): mixed + { + if ($this->hasExtensionProperty($key)) { + return $this->extensions[$key]; + } + + return null; + } + + /** + * @return array + */ + public function extensionProperties(): array + { + return $this->extensions; + } + + /** + * @param array $extensionsProperties + */ + public function mergeExtensionProperties(array $extensionsProperties): self + { + $this->extensions = array_merge($this->extensions, $extensionsProperties); + + return $this; + } + + /** + * @return array + */ + public function extensionPropertiesToArray(): array + { + return collect($this->extensions) + ->mapWithKeys(fn ($v, $k) => ["x-$k" => $v]) + ->all(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Helpers/ExamplesExtractor.php b/vendor/dedoc/scramble/src/Support/Helpers/ExamplesExtractor.php new file mode 100644 index 00000000..57c8ecf3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Helpers/ExamplesExtractor.php @@ -0,0 +1,63 @@ +docNode?->getTagsByName($this->tagName) ?? [])) { + return []; + } + + return array_map( + fn ($example) => $this->getTypedExampleValue($example->value->value ?? null, $preferString), + array_values($examples), + ); + } + + private function getTypedExampleValue($exampleValue, bool $preferString = false) + { + if (! is_string($exampleValue)) { + return new MissingValue; + } + + if (function_exists('json_decode')) { + $json = json_decode($exampleValue, true); + + $exampleValue = $json === null || $json == $exampleValue + ? $exampleValue + : $json; + } + + if ($exampleValue === 'null') { + $exampleValue = null; + } elseif (in_array($exampleValue, ['true', 'false'])) { + $exampleValue = $exampleValue === 'true'; + } elseif (is_numeric($exampleValue) && ! $preferString) { + $exampleValue = floatval($exampleValue); + + if (floor($exampleValue) == $exampleValue) { + $exampleValue = intval($exampleValue); + } + } + + return $exampleValue; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Helpers/JsonResourceHelper.php b/vendor/dedoc/scramble/src/Support/Helpers/JsonResourceHelper.php new file mode 100644 index 00000000..506408ec --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Helpers/JsonResourceHelper.php @@ -0,0 +1,70 @@ +name] ?? null) { + return $cachedModelType; + } + + $modelClass = static::getModelName( + $jsonClass->name, + new \ReflectionClass($jsonClass->name), + new FileNameResolver(ClassReflector::make($jsonClass->name)->getNameContext()), + ); + + $modelType = new UnknownType("Cannot resolve [$modelClass] model type."); + if ($modelClass) { + $modelType = new ObjectType($modelClass); + } + + static::$jsonResourcesModelTypesCache[$jsonClass->name] = $modelType; + + return $modelType; + } + + private static function getModelName(string $jsonResourceClassName, \ReflectionClass $reflectionClass, FileNameResolver $getFqName) + { + $phpDoc = $reflectionClass->getDocComment() ?: ''; + + $mixinOrPropertyLine = Str::of($phpDoc) + ->replace(['/**', '*/'], '') + ->explode("\n") + ->first(fn ($str) => Str::is(['*@property*$resource', '*@mixin*'], $str)); + + if ($mixinOrPropertyLine) { + $modelName = Str::replace(['@property-read', '@property', '$resource', '@mixin', ' ', '*', "\r"], '', $mixinOrPropertyLine); + + $modelClass = $getFqName($modelName); + + if (class_exists($modelClass)) { + return $modelClass; + } + } + + $modelName = (string) Str::of(Str::of($jsonResourceClassName)->explode('\\')->last())->replace('Resource', '')->singular(); + + $modelClass = 'App\\Models\\'.$modelName; + if (! class_exists($modelClass)) { + return null; + } + + return $modelClass; + } +} diff --git a/vendor/dedoc/scramble/src/Support/IndexBuilders/Bag.php b/vendor/dedoc/scramble/src/Support/IndexBuilders/Bag.php new file mode 100644 index 00000000..e9cc1663 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/IndexBuilders/Bag.php @@ -0,0 +1,30 @@ + + */ +class Bag +{ + /** + * @param T $data + */ + public function __construct( + public array $data = [] + ) {} + + /** + * @template TKey of key-of + * + * @param TKey $key + * @param T[TKey] $value + * @return $this + */ + public function set(string $key, $value): self + { + $this->data[$key] = $value; // @phpstan-ignore-line + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/IndexBuilders/IndexBuilder.php b/vendor/dedoc/scramble/src/Support/IndexBuilders/IndexBuilder.php new file mode 100644 index 00000000..bfa6ed20 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/IndexBuilders/IndexBuilder.php @@ -0,0 +1,14 @@ + + */ +interface IndexBuilder +{ + public function afterAnalyzedNode(Scope $scope, Node $node): void; +} diff --git a/vendor/dedoc/scramble/src/Support/IndexBuilders/PaginatorsCandidatesBuilder.php b/vendor/dedoc/scramble/src/Support/IndexBuilders/PaginatorsCandidatesBuilder.php new file mode 100644 index 00000000..90de9dd5 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/IndexBuilders/PaginatorsCandidatesBuilder.php @@ -0,0 +1,44 @@ + + */ +class PaginatorsCandidatesBuilder implements IndexBuilder +{ + /** + * @param Bag $bag + */ + public function __construct(public readonly Bag $bag) {} + + public function afterAnalyzedNode(Scope $scope, Node $node): void + { + if (! $node instanceof Node\Expr\StaticCall && ! $node instanceof Node\Expr\MethodCall) { + return; + } + + if (! $node->name instanceof Node\Identifier) { + return; + } + + if (! in_array($node->name->name, PaginateMethodsReturnTypeExtension::PAGINATE_METHODS)) { + return; + } + + $this->bag->set( + $key = 'paginatorCandidates', + [ + ...($this->bag->data[$key] ?? []), + $node, + ], + ); + $this->bag->set('scope', $scope); + } +} diff --git a/vendor/dedoc/scramble/src/Support/IndexBuilders/RequestParametersBuilder.php b/vendor/dedoc/scramble/src/Support/IndexBuilders/RequestParametersBuilder.php new file mode 100644 index 00000000..ce41dddd --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/IndexBuilders/RequestParametersBuilder.php @@ -0,0 +1,238 @@ +> + */ +class RequestParametersBuilder implements IndexBuilder +{ + /** + * @param Bag> $bag + */ + public function __construct( + public readonly Bag $bag, + ) {} + + public function afterAnalyzedNode(Scope $scope, Node $node): void + { + // @todo: Find more general approach to get a comment related to the node + [$commentHolderNode, $methodCallNode] = match ($node::class) { + Node\Stmt\Expression::class => [ + $node, + $node->expr instanceof Node\Expr\Assign ? $node->expr->expr : $node->expr, + ], + Node\Arg::class => [$node, $node->value], + Node\ArrayItem::class => [$node, $node->value], + default => [null, null], + }; + + if (! $commentHolderNode) { + return; + } + + if (! $methodCallNode instanceof Node\Expr\MethodCall) { + return; + } + + $varType = $scope->getType($methodCallNode->var); + + if (! $varType->isInstanceOf(Request::class)) { + return; + } + + if (! $name = $this->getNameNodeValue($scope, $methodCallNode->name)) { + return; + } + + if (! ($parameterName = TypeHelper::getArgType($scope, $methodCallNode->args, ['key', 0])->value ?? null)) { + return; + } + + if ($this->shouldIgnoreParameter($commentHolderNode)) { + return; + } + + [$parameterType, $parameterDefault] = match ($name) { + 'integer' => $this->makeIntegerParameter($scope, $methodCallNode), + 'float' => $this->makeFloatParameter($scope, $methodCallNode), + 'boolean' => $this->makeBooleanParameter($scope, $methodCallNode), + 'enum' => $this->makeEnumParameter($scope, $methodCallNode), + 'query' => $this->makeQueryParameter($scope, $methodCallNode), + 'string', 'str', 'input' => $this->makeStringParameter($scope, $methodCallNode), + 'get', 'post' => $this->makeFlatParameter($scope, $methodCallNode), + default => [null, null], + }; + + if (! $parameterType) { + return; + } + + if ($parameterDefaultFromDoc = $this->getParameterDefaultFromPhpDoc($commentHolderNode)) { + $parameterDefault = $parameterDefaultFromDoc; + } + + $this->ensureExplicitParameterPlacementInQuery($commentHolderNode, $parameterType); + + $this->bag->set( + $parameterName, + new InferredParameter( + name: $parameterName, + description: $this->makeDescriptionFromComments($commentHolderNode), + type: $parameterType, + default: $parameterDefault ?? new MissingValue, + ) + ); + } + + private function getNameNodeValue(Scope $scope, Node $nameNode) + { + if ($nameNode instanceof Node\Identifier) { + return $nameNode->name; + } + + $type = $scope->getType($nameNode); + if (! $type instanceof LiteralStringType) { + return null; + } + + return $type->value; + } + + private function makeIntegerParameter(Scope $scope, Node $node) + { + return [ + new IntegerType, + TypeHelper::getArgType($scope, $node->args, ['default', 1], new LiteralIntegerType(0))->value ?? null, + ]; + } + + private function makeFloatParameter(Scope $scope, Node $node) + { + return [ + new FloatType, + TypeHelper::getArgType($scope, $node->args, ['default', 1], new LiteralFloatType(0))->value ?? null, + ]; + } + + private function makeBooleanParameter(Scope $scope, Node $node) + { + return [ + new BooleanType, + TypeHelper::getArgType($scope, $node->args, ['default', 1], new LiteralBooleanType(false))->value ?? null, + ]; + } + + private function makeStringParameter(Scope $scope, Node $node) + { + return [ + new StringType, + TypeHelper::getArgType($scope, $node->args, ['default', 1])->value ?? null, + ]; + } + + private function makeFlatParameter(Scope $scope, Node $node) + { + $type = new StringType; + + $type->setAttribute('isFlat', true); + + return [ + $type, + TypeHelper::getArgType($scope, $node->args, ['default', 1])->value ?? null, + ]; + } + + private function makeEnumParameter(Scope $scope, Node $node) + { + $enumClassType = TypeHelper::getArgType($scope, $node->args, ['default', 1]); + + if (! $enumClassType instanceof GenericClassStringType) { + return [null, null]; + } + + return [ + $enumClassType->type, + null, + ]; + } + + private function makeQueryParameter(Scope $scope, Node $node) + { + return [ + tap(new UnknownType, fn (InferType $t) => $t->setAttribute('isInQuery', true)), + TypeHelper::getArgType($scope, $node->args, ['default', 1])->value ?? null, + ]; + } + + private function makeDescriptionFromComments(NodeAbstract $node): string + { + /* + * @todo: consider adding only @param annotation support, + * so when description is taken only if comment is marked with @param + */ + if ($phpDoc = $node->getAttribute('parsedPhpDoc')) { + return trim($phpDoc->getAttribute('summary').' '.$phpDoc->getAttribute('description')); + } + + if ($node->getComments()) { + $docText = collect($node->getComments()) + ->map(fn (Comment $c) => $c->getReformattedText()) + ->join("\n"); + + return (string) Str::of($docText)->replace(['//', ' * ', '/**', '/*', '*/'], '')->trim(); + } + + return ''; + } + + private function shouldIgnoreParameter(NodeAbstract $node): bool + { + /** @var PhpDocNode|null $phpDoc */ + $phpDoc = $node->getAttribute('parsedPhpDoc'); + + return (bool) $phpDoc?->getTagsByName('@ignoreParam') + || (bool) $phpDoc?->getTagsByName('@hidden'); + } + + private function getParameterDefaultFromPhpDoc(NodeAbstract $node): mixed + { + /** @var PhpDocNode|null $phpDoc */ + $phpDoc = $node->getAttribute('parsedPhpDoc'); + + return ExamplesExtractor::make($phpDoc, '@default')->extract()[0] ?? null; + } + + private function ensureExplicitParameterPlacementInQuery(NodeAbstract $node, InferType $parameterType): void + { + /** @var PhpDocNode|null $phpDoc */ + $phpDoc = $node->getAttribute('parsedPhpDoc'); + + if ($phpDoc?->getTagsByName('@query')) { + $parameterType->setAttribute('isInQuery', true); + } + } +} diff --git a/vendor/dedoc/scramble/src/Support/IndexBuilders/ScopeCollector.php b/vendor/dedoc/scramble/src/Support/IndexBuilders/ScopeCollector.php new file mode 100644 index 00000000..ca29b8ea --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/IndexBuilders/ScopeCollector.php @@ -0,0 +1,42 @@ +> + */ +class ScopeCollector implements IndexBuilder +{ + /** @var WeakMap */ + public static WeakMap $scopes; + + public function __construct() + { + if (! isset(static::$scopes)) { + static::$scopes = new WeakMap; + } + } + + public function afterAnalyzedNode(Scope $scope, Node $node): void + { + if ( + $node instanceof FunctionLike + && $scope->context->functionDefinition + ) { + static::$scopes->offsetSet($scope->context->functionDefinition, $scope); + } + } + + public function getScope(FunctionLikeDefinition $definition): ?Scope + { + return static::$scopes->offsetExists($definition) + ? static::$scopes->offsetGet($definition) + : null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/AbortHelpersExceptionInfer.php b/vendor/dedoc/scramble/src/Support/InferExtensions/AbortHelpersExceptionInfer.php new file mode 100644 index 00000000..af832f62 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/AbortHelpersExceptionInfer.php @@ -0,0 +1,57 @@ +name instanceof Name) { + return []; + } + + if (! in_array($node->name->toString(), ['abort', 'abort_if', 'abort_unless'])) { + return []; + } + + /* + * Params for these functions are the same with an exception that `abort` fn doesn't + * take a first argument. Hence, params shift var. + */ + $paramsShift = $node->name->toString() === 'abort' ? 0 : 1; + + $codeType = TypeHelper::getArgType($scope, $node->args, ['code', 0 + $paramsShift]); + + if ($codeType instanceof LiteralIntegerType && $codeType->value === 404) { + return [new ObjectType(ModelNotFoundException::class)]; + } + + $messageType = TypeHelper::getArgType($scope, $node->args, ['message', 1 + $paramsShift], new LiteralStringType('')); + $headersType = TypeHelper::getArgType($scope, $node->args, ['headers', 2 + $paramsShift], new ArrayType); + + return [ + new Generic(HttpException::class, [ + $codeType, + $messageType, + $headersType, + ]), + ]; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/AfterAnonymousResourceCollectionDefinitionCreatedExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterAnonymousResourceCollectionDefinitionCreatedExtension.php new file mode 100644 index 00000000..67cd4933 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterAnonymousResourceCollectionDefinitionCreatedExtension.php @@ -0,0 +1,50 @@ +classDefinition; + + if ($definition->parentFqn) { + $definition->templateTypes = $this->index->getClass($definition->parentFqn)->templateTypes ?? []; + } + + $tResource = collect($definition->templateTypes)->firstOrFail('name', 'TResource'); + $tCollects = collect($definition->templateTypes)->firstOrFail('name', 'TCollects'); + + $definition->methods['__construct'] = new ShallowFunctionDefinition( + type: new FunctionType( + name: '__construct', + arguments: [ + 'resource' => $tResource, + 'collects' => new GenericClassStringType($tCollects), + ], + returnType: new VoidType, + ), + definingClassName: JsonResource::class, + // @todo now required, but should not be required! + selfOutType: new Generic('self', [$tResource, new TemplatePlaceholderType, $tCollects]), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/AfterEloquentCollectionDefinitionCreatedExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterEloquentCollectionDefinitionCreatedExtension.php new file mode 100644 index 00000000..3939544e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterEloquentCollectionDefinitionCreatedExtension.php @@ -0,0 +1,81 @@ +classDefinition; + + $tKey = collect($definition->templateTypes)->firstOrFail('name', 'TKey'); + $tModel = collect($definition->templateTypes)->firstOrFail('name', 'TModel'); + + $definition->methods['map'] = $this->buildMapMethodDefinition($tKey, $tModel); + $definition->methods['mapWithKeys'] = $this->buildMapWithKeysMethodDefinition($tKey, $tModel); + } + + private function buildMapMethodDefinition(TemplateType $tKey, TemplateType $tModel): ShallowFunctionDefinition + { + $templates = [ + $tMapValue = new TemplateType('TMapValue'), + ]; + + return new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'map', + arguments: [ + 'callback' => new FunctionType('{}', [$tModel, $tKey], $tMapValue), + ], + returnType: new Generic(StaticReference::STATIC, [$tKey, $tMapValue]), + ), function (FunctionType $ft) use ($templates) { + $ft->templates = $templates; + }), + definingClassName: EloquentCollection::class, + ); + } + + private function buildMapWithKeysMethodDefinition(TemplateType $tKey, TemplateType $tModel): ShallowFunctionDefinition + { + $templates = [ + $tMapWithKeysKey = new TemplateType('TMapWithKeysKey', is: new Union([new IntegerType, new StringType])), + $tMapWithKeysValue = new TemplateType('TMapWithKeysValue'), + ]; + + return new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'mapWithKeys', + arguments: [ + 'callback' => new FunctionType('{}', [$tModel, $tKey], new Generic('iterable', [ + $tMapWithKeysKey, + $tMapWithKeysValue, + ])), + ], + returnType: new Generic(StaticReference::STATIC, [$tMapWithKeysKey, $tMapWithKeysValue]), + ), function (FunctionType $ft) use ($templates) { + $ft->templates = $templates; + }), + definingClassName: EloquentCollection::class, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/AfterJsonApiResourceDefinitionCreatedExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterJsonApiResourceDefinitionCreatedExtension.php new file mode 100644 index 00000000..5142fdd3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterJsonApiResourceDefinitionCreatedExtension.php @@ -0,0 +1,148 @@ +classDefinition; + + $definition->templateTypes = [ + ...$definition->templateTypes, + new TemplateType('TJsonApiLinks', is: new ArrayType, default: new KeyedArrayType), + new TemplateType('TJsonApiMeta', is: new ArrayType, default: new KeyedArrayType), + new TemplateType('TUsesRequestQueryString', is: new BooleanType, default: new LiteralBooleanType(true)), + new TemplateType('TIncludesPreviouslyLoadedRelationships', is: new BooleanType, default: new LiteralBooleanType(false)), + new TemplateType('TLoadedRelationshipsMap', is: new UnknownType), + new TemplateType('TLoadedRelationshipIdentifiers', is: new ArrayType, default: new KeyedArrayType), + ]; + + $definition->methods['newCollection'] = $this->buildNewCollectionMethodDefinition(); + $definition->methods['respectFieldsAndIncludesInQueryString'] = $this->buildRespectFieldsAndIncludesInQueryStringMethodDefinition(); + $definition->methods['ignoreFieldsAndIncludesInQueryString'] = $this->buildIgnoreFieldsAndIncludesInQueryStringMethodDefinition(); + $definition->methods['includePreviouslyLoadedRelationships'] = $this->buildIncludePreviouslyLoadedRelationshipsMethodDefinition(); + } + + private function buildNewCollectionMethodDefinition(): ShallowFunctionDefinition + { + $templates = [ + $tResource1 = new TemplateType('TResource1'), + ]; + + return new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'newCollection', + arguments: [ + 'resource' => $tResource1, + ], + returnType: new Generic(AnonymousResourceCollection::class, [ + $tResource1, + new ArrayType, + new ObjectType(StaticReference::STATIC), + ]), + ), function (FunctionType $ft) use ($templates) { + $ft->templates = $templates; + }), + definingClassName: JsonApiResource::class, + isStatic: true, + ); + } + + private function buildRespectFieldsAndIncludesInQueryStringMethodDefinition(): ShallowFunctionDefinition + { + $templates = [ + $tValue1 = new TemplateType('TValue1', is: new BooleanType), + ]; + + return new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'respectFieldsAndIncludesInQueryString', + arguments: [ + 'value' => $tValue1, + ], + returnType: new SelfType(JsonApiResource::class), + ), function (FunctionType $ft) use ($templates) { + $ft->templates = $templates; + }), + argumentsDefaults: [ + 'value' => new LiteralBooleanType(true), + ], + definingClassName: JsonApiResource::class, + selfOutType: new Generic('self', [ + new TemplatePlaceholderType, // TResource + new TemplatePlaceholderType, // TAdditional + new TemplatePlaceholderType, // TJsonApiLinks + new TemplatePlaceholderType, // TJsonApiMeta + $tValue1, // TUsesRequestQueryString + new TemplatePlaceholderType, // TIncludesPreviouslyLoadedRelationships + new TemplatePlaceholderType, // TLoadedRelationshipsMap + new TemplatePlaceholderType, // TLoadedRelationshipIdentifiers + ]), + ); + } + + private function buildIgnoreFieldsAndIncludesInQueryStringMethodDefinition(): ShallowFunctionDefinition + { + return new ShallowFunctionDefinition( + type: new FunctionType( + name: 'ignoreFieldsAndIncludesInQueryString', + returnType: new SelfType(JsonApiResource::class), + ), + definingClassName: JsonApiResource::class, + selfOutType: new Generic('self', [ + new TemplatePlaceholderType, // TResource + new TemplatePlaceholderType, // TAdditional + new TemplatePlaceholderType, // TJsonApiLinks + new TemplatePlaceholderType, // TJsonApiMeta + new LiteralBooleanType(false), // TUsesRequestQueryString + new TemplatePlaceholderType, // TIncludesPreviouslyLoadedRelationships + new TemplatePlaceholderType, // TLoadedRelationshipsMap + new TemplatePlaceholderType, // TLoadedRelationshipIdentifiers + ]), + ); + } + + private function buildIncludePreviouslyLoadedRelationshipsMethodDefinition(): ShallowFunctionDefinition + { + return new ShallowFunctionDefinition( + type: new FunctionType( + name: 'includePreviouslyLoadedRelationships', + returnType: new SelfType(JsonApiResource::class), + ), + definingClassName: JsonApiResource::class, + selfOutType: new Generic('self', [ + new TemplatePlaceholderType, // TResource + new TemplatePlaceholderType, // TAdditional + new TemplatePlaceholderType, // TJsonApiLinks + new TemplatePlaceholderType, // TJsonApiMeta + new TemplatePlaceholderType, // TUsesRequestQueryString + new LiteralBooleanType(true), // TIncludesPreviouslyLoadedRelationships + new TemplatePlaceholderType, // TLoadedRelationshipsMap + new TemplatePlaceholderType, // TLoadedRelationshipIdentifiers + ]), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/AfterJsonResourceDefinitionCreatedExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterJsonResourceDefinitionCreatedExtension.php new file mode 100644 index 00000000..67e31467 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterJsonResourceDefinitionCreatedExtension.php @@ -0,0 +1,125 @@ +classDefinition; + + $definition->propagatesTemplates(true); + + $definition->templateTypes = [ + $tResource = new TemplateType('TResource'), + $tAdditional = new TemplateType('TAdditional', default: new ArrayType), + ]; + + $definition->methods['__construct'] = new ShallowFunctionDefinition( + type: new FunctionType( + name: '__construct', + arguments: [ + 'resource' => $tResource, + ], + returnType: new VoidType, + ), + definingClassName: JsonResource::class, + // @todo now required, but should not be required! + selfOutType: new Generic('self', [$tResource, new TemplatePlaceholderType]), + ); + + $additionalTemplates = [ + $tAdditional1 = new TemplateType('TAdditional1'), + ]; + $definition->methods['additional'] = new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'additional', + arguments: [ + 'additional' => $tAdditional1, + ], + returnType: new SelfType(JsonResource::class), + ), function (FunctionType $ft) use ($additionalTemplates) { + $ft->templates = $additionalTemplates; + }), + definingClassName: JsonResource::class, + selfOutType: new Generic('self', [ + new TemplatePlaceholderType, + $tAdditional1, + ]) + ); + + $definition->methods['newCollection'] = $this->buildNewCollectionMethodDefinition(); + + $definition->methods['collection'] = $this->buildCollectionMethodDefinition(); + } + + private function buildNewCollectionMethodDefinition(): ShallowFunctionDefinition + { + $templates = [ + $tResource1 = new TemplateType('TResource1'), + ]; + + return new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'newCollection', + arguments: [ + 'resource' => $tResource1, + ], + returnType: new Generic(AnonymousResourceCollection::class, [ + $tResource1, + new ArrayType, + new ObjectType(StaticReference::STATIC), + ]), + ), function (FunctionType $ft) use ($templates) { + $ft->templates = $templates; + }), + definingClassName: JsonResource::class, + isStatic: true, + ); + } + + private function buildCollectionMethodDefinition(): ShallowFunctionDefinition + { + $templates = [ + $tResource1 = new TemplateType('TResource1'), + ]; + + return new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'collection', + arguments: [ + 'resource' => $tResource1, + ], + returnType: new StaticMethodCallReferenceType( + StaticReference::STATIC, + 'newCollection', + [$tResource1], + ), + ), function (FunctionType $ft) use ($templates) { + $ft->templates = $templates; + }), + definingClassName: JsonResource::class, + isStatic: true, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/AfterResourceCollectionDefinitionCreatedExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterResourceCollectionDefinitionCreatedExtension.php new file mode 100644 index 00000000..7664cb72 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterResourceCollectionDefinitionCreatedExtension.php @@ -0,0 +1,66 @@ +classDefinition; + + if ($definition->parentFqn) { + $definition->templateTypes = $this->index->getClass($definition->parentFqn)->templateTypes ?? []; + } + + $definition->templateTypes[] = $tCollects = new TemplateType( + 'TCollects', // @todo rename to TCollectedResource + default: new UnknownType, + ); + + $definition->properties['collects'] = new ClassPropertyDefinition( + type: new GenericClassStringType($tCollects), + attributes: [], + ); + + $definition->properties['collection'] = new ClassPropertyDefinition( + type: new Generic(Collection::class, [ + new IntegerType, + $tCollects, + ]), + attributes: [], + ); + + $definition->methods['toArray'] = new ShallowFunctionDefinition( + type: new FunctionType( + name: 'toArray', + arguments: [ + 'resource' => new MixedType, + ], + returnType: new ArrayType($tCollects), + ), + definingClassName: ResourceCollection::class, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/AfterResponseDefinitionCreatedExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterResponseDefinitionCreatedExtension.php new file mode 100644 index 00000000..1a46011e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/AfterResponseDefinitionCreatedExtension.php @@ -0,0 +1,84 @@ +classDefinition; + + $definition->templateTypes = [ + new TemplateType('TContent'), + new TemplateType('TStatus'), + new TemplateType('THeaders'), + ]; + + $definition->methods['setContent'] = $this->buildSetContentMethodDefinition(); + $definition->methods['setStatusCode'] = $this->buildSetStatusCodeMethodDefinition(); + } + + private function buildSetContentMethodDefinition(): ShallowFunctionDefinition + { + $templates = [ + $tContent1 = new TemplateType('TContent1'), + ]; + + return new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'setContent', + arguments: [ + 'content' => $tContent1, + ], + returnType: new SelfType(Response::class), + ), function (FunctionType $ft) use ($templates) { + $ft->templates = $templates; + }), + definingClassName: Response::class, + selfOutType: new Generic('self', [ + $tContent1, + new TemplatePlaceholderType, + new TemplatePlaceholderType, + ]), + ); + } + + private function buildSetStatusCodeMethodDefinition(): ShallowFunctionDefinition + { + $templates = [ + $tStatus1 = new TemplateType('TStatus1'), + ]; + + return new ShallowFunctionDefinition( + type: tap(new FunctionType( + name: 'setStatusCode', + arguments: [ + 'code' => $tStatus1, + ], + returnType: new SelfType(Response::class), + ), function (FunctionType $ft) use ($templates) { + $ft->templates = $templates; + }), + definingClassName: Response::class, + selfOutType: new Generic('self', [ + new TemplatePlaceholderType, + $tStatus1, + new TemplatePlaceholderType, + ]), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/ArrayMergeReturnTypeExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/ArrayMergeReturnTypeExtension.php new file mode 100644 index 00000000..63b6895b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/ArrayMergeReturnTypeExtension.php @@ -0,0 +1,36 @@ +arguments->all()); + + if (! $arguments->every(fn ($arg) => $arg instanceof KeyedArrayType)) { + return null; + } + /** @var Collection $arguments */ + $items = $arguments->flatMap->items + // unique them by key like array_merge works + ->reduce(function ($carry, $item) { + $carry[$item->key] = $item; + + return $carry; + }, []); + + return new KeyedArrayType(array_values($items)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/BinaryFileResponseTypeFactory.php b/vendor/dedoc/scramble/src/Support/InferExtensions/BinaryFileResponseTypeFactory.php new file mode 100644 index 00000000..feee721e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/BinaryFileResponseTypeFactory.php @@ -0,0 +1,116 @@ +file, + new LiteralIntegerType(200), + $this->headers, + $this->disposition, + ]); + + $responseType->setAttribute('mimeType', $this->guessMimeType()); + $responseType->setAttribute('contentDisposition', $this->guessContentDisposition()); + + return $responseType; + } + + private function guessMimeType(): string + { + $mimeType = 'application/octet-stream'; + + if ($fileMime = $this->guessMimeTypeFromFile()) { + $mimeType = $fileMime; + } + + return $mimeType; + } + + private function guessMimeTypeFromFile(): ?string + { + $fileName = $this->guessFileNameFromType($this->file); + + if ($fileName && class_exists(ExtensionMimeTypeDetector::class)) { + return (new ExtensionMimeTypeDetector)->detectMimeTypeFromPath($fileName); + } + + return null; + } + + private function guessContentDisposition(): ?string + { + $contentDisposition = $this->disposition instanceof LiteralStringType ? $this->disposition->value : null; + + if ($contentDisposition !== 'attachment') { + return $contentDisposition; + } + + return $this->getContentDispositionAttachmentHeader( + $this->guessFileNameFromType($this->file), + $this->guessFileNameFromType($this->name), + ); + } + + private function guessFileNameFromType(Type $fileArgumentType): ?string + { + $stringLiterals = (new TypeWalker)->findAll( + Union::wrap(...array_filter([$fileArgumentType->getOriginal(), $fileArgumentType])), + fn (Type $t) => $t instanceof LiteralStringType, + ); + + foreach (array_reverse($stringLiterals) as $stringLiteral) { + if (! $stringLiteral instanceof LiteralStringType) { + continue; + } + + if ($this->isFileName($stringLiteral->value)) { + return $stringLiteral->value; + } + } + + return null; + } + + private function isFileName(string $str): bool + { + return (bool) preg_match('/^.*\.[^.]+$/', $str); + } + + private function getContentDispositionAttachmentHeader(?string $fileName, ?string $overridingFileName): string + { + if (! $fileName && ! $overridingFileName) { + return 'attachment'; + } + + $name = $overridingFileName ?: $fileName; + + return 'attachment; filename='.basename($name); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/EloquentBuilderExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/EloquentBuilderExtension.php new file mode 100644 index 00000000..ec63cc23 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/EloquentBuilderExtension.php @@ -0,0 +1,121 @@ +isInstanceOf(Builder::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + if ($event->getDefinition()->hasMethodDefinition($event->getName())) { + return $this->handleExistingMethodReturnType($event); + } + + if ($this->shouldForwardCallToModel($event)) { + return $this->forwardCallToModel($event); + } + + if (! $modelType = $this->getModelType($event->getInstance())) { + return null; + } + if ( + $this->modelIsSoftDeletable($modelType) + && ($softDeleteCallResult = $this->handleSoftDeletes($event, $modelType)) + ) { + return $softDeleteCallResult; + } + + return null; + } + + private function handleExistingMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->getName()) { + 'get' => ($modelType = $this->getModelType($event->getInstance())) + ? ModelCollectionTypeResolver::resolve($modelType) + : null, + default => null, + }; + } + + private function shouldForwardCallToModel(MethodCallEvent $event): bool + { + if (! $modelType = $this->getModelType($event->getInstance())) { + return false; + } + + return $event->scope->index->getClass($modelType->name)?->hasMethodDefinition( + $this->getScopeMethodName($event->name), + ) ?? false; + } + + private function forwardCallToModel(MethodCallEvent $event): ?Type + { + if (! $modelType = $this->getModelType($event->getInstance())) { + return null; + } + + return $event->getInstance(); + } + + /** + * @see SoftDeletes + */ + private function modelIsSoftDeletable(ObjectType $modelType): bool + { + return method_exists($modelType->name, 'bootSoftDeletes'); + } + + private function handleSoftDeletes(MethodCallEvent $event, ObjectType $modelType): ?Type + { + return match ($event->name) { + 'onlyTrashed', 'withTrashed', 'withoutTrashed' => $event->getInstance(), + 'restoreOrCreate', 'createOrRestore' => $modelType, + default => null, + }; + } + + private function getModelType(ObjectType $instance): ?ObjectType + { + $type = $this->normalizeType($instance); + + $modelType = $type->templateTypes[0] ?? null; + + if (! $modelType instanceof ObjectType) { + return null; + } + + if (! $modelType->isInstanceOf(Model::class)) { + return null; + } + + return $modelType; + } + + private function normalizeType(ObjectType $type): Generic + { + if ($type instanceof Generic) { + return $type; + } + + return new Generic($type->name, []); + } + + private function getScopeMethodName(string $scope): string + { + return 'scope'.ucfirst($scope); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/FacadeStaticMethodReturnTypeExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/FacadeStaticMethodReturnTypeExtension.php new file mode 100644 index 00000000..2934c274 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/FacadeStaticMethodReturnTypeExtension.php @@ -0,0 +1,65 @@ + */ + private static array $rootClassCache = []; + + public function shouldHandle(string $name): bool + { + return is_a($name, Facade::class, true); + } + + public function getStaticMethodReturnType(StaticMethodCallEvent $event): ?Type + { + $rootClass = $this->getRootClass($event->getCallee()); + + if (! $rootClass) { + return null; + } + + return ReferenceTypeResolver::getInstance()->resolve( + $event->scope, + new MethodCallReferenceType( + new ObjectType($rootClass), + $event->getName(), + $event->arguments instanceof AutoResolvingArgumentTypeBag + ? $event->arguments->allUnresolved() + : $event->arguments->all(), + ), + ); + } + + private function getRootClass(string $facadeClass): ?string + { + if (array_key_exists($facadeClass, self::$rootClassCache)) { + return self::$rootClassCache[$facadeClass]; + } + + return self::$rootClassCache[$facadeClass] = $this->getFreshRootClass($facadeClass); + } + + private function getFreshRootClass(string $facadeClass): ?string + { + $seeTag = SchemaClassDocReflector::createFromClassName($facadeClass)->getTagValue('@see'); + $rootClass = ltrim(explode("\n", $seeTag?->value ?? '')[0], '\\'); + + if (! $rootClass) { + return null; + } + + return class_exists($rootClass) ? $rootClass : null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceCollectionMethodReturnTypeExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceCollectionMethodReturnTypeExtension.php new file mode 100644 index 00000000..4fb5cae8 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceCollectionMethodReturnTypeExtension.php @@ -0,0 +1,38 @@ +isInstanceOf(JsonApiAnonymousResourceCollection::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->getName()) { + 'response', 'toResponse' => new Generic(JsonResponse::class, [ + ResourceCollectionTypeManager::make($event->getInstance())->getResponseType(), + new UnknownType, + new KeyedArrayType([ + new ArrayItemType_('Content-type', new LiteralStringType('application/vnd.api+json')), + ]), + ]), + default => null, + }; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceMethodReturnTypeExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceMethodReturnTypeExtension.php new file mode 100644 index 00000000..aea59e3b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/JsonApiResourceMethodReturnTypeExtension.php @@ -0,0 +1,38 @@ +isInstanceOf(JsonApiResource::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->getName()) { + 'response', 'toResponse' => new Generic(JsonResponse::class, [ + new Generic(ResourceResponse::class, [$event->getInstance()]), + new UnknownType, + new KeyedArrayType([ + new ArrayItemType_('Content-type', new LiteralStringType('application/vnd.api+json')), + ]), + ]), + default => null, + }; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/JsonResourceExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/JsonResourceExtension.php new file mode 100644 index 00000000..df41f776 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/JsonResourceExtension.php @@ -0,0 +1,263 @@ +isInstanceOf(JsonResource::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->name) { + 'toArray' => $event->methodDefiningClassName === JsonResource::class + ? $this->getModelMethodReturn($event->getInstance()->name, 'toArray', $event->arguments, $event->scope) + : null, + + 'response', 'toResponse' => new Generic(JsonResponse::class, [ + new Generic(ResourceResponse::class, [$event->getInstance()]), + new UnknownType, + new ArrayType, + ]), + + 'whenLoaded' => count($event->arguments) === 1 + ? Union::wrap([ + $this->getModelPropertyType( + $event->getDefinition(), + $event->getArg('attribute', 0)->value ?? '', + $event->scope + ), + new ObjectType(MissingValue::class), + ]) + : Union::wrap([ + $this->value($event->getArg('value', 1)), + $this->value($event->getArg('default', 2, new ObjectType(MissingValue::class))), + ]), + + 'when', 'unless', 'whenPivotLoaded' => Union::wrap([ + $this->value($event->getArg('value', 1)), + $this->value($event->getArg('default', 2, new ObjectType(MissingValue::class))), + ]), + + 'merge' => new Generic(MergeValue::class, [ + new LiteralBooleanType(true), + $this->value($event->getArg('value', 0)), + ]), + + 'mergeWhen', 'mergeUnless' => new Generic(MergeValue::class, [ + new BooleanType, + $this->value($event->getArg('value', 1)), + ]), + + 'whenHas', 'whenAppended' => count($event->arguments) === 1 + ? Union::wrap([$this->getModelPropertyType( + $event->getDefinition(), + $event->getArg('attribute', 0)->value ?? '', + $event->scope + ), new ObjectType(MissingValue::class)]) + : Union::wrap([ + ($valueType = $event->getArg('value', 1, new NullType)) instanceof NullType + ? $this->getModelPropertyType( + $event->getDefinition(), + $event->getArg('attribute', 0)->value ?? '', + $event->scope + ) + : $this->value($valueType), + $this->value($event->getArg('default', 2, new ObjectType(MissingValue::class))), + ]), + + 'whenNotNull' => Union::wrap([ + $this->value($this->removeNullFromUnion($event->getArg('value', 0))), + $this->value($event->getArg('default', 1, new ObjectType(MissingValue::class))), + ]), + + 'whenNull' => Union::wrap([ + new NullType, + $this->value($event->getArg('default', 1, new ObjectType(MissingValue::class))), + ]), + + 'whenAggregated' => count($event->arguments) <= 3 + ? Union::wrap([ + match ($event->getArg('aggregate', 2)?->value ?? '') { // @phpstan-ignore property.notFound + 'count' => new IntegerType, + 'avg', 'sum' => new FloatType, + default => new StringType, + }, + $this->value($event->getArg('default', 4, new ObjectType(MissingValue::class))), + ]) + : Union::wrap([ + $this->value($event->getArg('value', 3)), + $this->value($event->getArg('default', 4, new ObjectType(MissingValue::class))), + ]), + + 'whenExistsLoaded' => count($event->arguments) === 1 + ? Union::wrap([new BooleanType, new ObjectType(MissingValue::class)]) + : Union::wrap([ + $this->value($event->getArg('value', 1)), + $this->value($event->getArg('default', 2, new ObjectType(MissingValue::class))), + ]), + + 'whenPivotLoadedAs' => Union::wrap([ + $this->value($event->getArg('value', 2)), + $this->value($event->getArg('default', 3, new ObjectType(MissingValue::class))), + ]), + + 'hasPivotLoaded', 'hasPivotLoadedAs' => new BooleanType, + + 'whenCounted' => count($event->arguments) === 1 + ? Union::wrap([new IntegerType, new ObjectType(MissingValue::class)]) + : Union::wrap([ + $this->value($event->getArg('value', 1)), + $this->value($event->getArg('default', 2, new ObjectType(MissingValue::class))), + ]), + + 'attributes' => $this->getAttributesMethodReturnType($event), + + default => ! $event->getDefinition() || $event->getDefinition()->hasMethodDefinition($event->name) + ? null + : $this->proxyMethodCallToModel($event), + }; + } + + public function getStaticMethodReturnType(StaticMethodCallEvent $event): ?Type + { + return match ($event->getName()) { + 'make' => ReferenceTypeResolver::getInstance() + ->resolve( + $event->scope, + new NewCallReferenceType($event->getCallee(), $event->arguments instanceof AutoResolvingArgumentTypeBag ? $event->arguments->allUnresolved() : $event->arguments->all()), + ), + default => null, + }; + } + + public function getPropertyType(PropertyFetchEvent $event): ?Type + { + return match ($event->name) { + 'resource' => JsonResourceHelper::modelType($event->getDefinition()), + default => ! $event->getDefinition() || $event->getDefinition()->hasPropertyDefinition($event->name) + ? null + : $this->getModelPropertyType($event->getDefinition(), $event->name, $event->scope), + }; + } + + private function getModelPropertyType(ClassDefinition $jsonResourceDefinition, string $name, Scope $scope): Type + { + return ReferenceTypeResolver::getInstance()->resolve( + $scope, + new PropertyFetchReferenceType( + JsonResourceHelper::modelType($jsonResourceDefinition), + $name, + ), + ); + } + + private function proxyMethodCallToModel(MethodCallEvent $event): Type + { + return $this->getModelMethodReturn($event->getInstance()->name, $event->name, $event->arguments, $event->scope); + } + + private function getModelMethodReturn(string $resourceClassName, string $methodName, ArgumentTypeBag $arguments, Scope $scope): Type + { + $modelType = JsonResourceHelper::modelType($scope->index->getClass($resourceClassName)); + + $argumentsList = $arguments instanceof AutoResolvingArgumentTypeBag + ? $arguments->allUnresolved() + : $arguments->all(); + + return ReferenceTypeResolver::getInstance()->resolve( + $scope, + new MethodCallReferenceType($modelType, $methodName, arguments: $argumentsList), + ); + } + + private function value(Type $type): Type + { + return $type instanceof FunctionType ? $type->getReturnType() : $type; + } + + private function removeNullFromUnion(Type $type): Type + { + $type = Union::wrap( + ReferenceTypeResolver::getInstance()->resolve(new GlobalScope, $type) + ); + + $types = $type instanceof Union ? $type->types : [$type]; + + return Union::wrap( + collect($types)->filter(fn ($t) => ! $t instanceof NullType)->values()->all() + ); + } + + private function getAttributesMethodReturnType(MethodCallEvent $event): Generic + { + $argument = $event->getArg('attributes', 0); + + $value = $argument instanceof KeyedArrayType + ? collect($argument->items)->map(fn (ArrayItemType_ $t) => $t->value instanceof LiteralStringType ? $t->value->value : null)->filter()->values()->all() + : ($argument instanceof LiteralStringType ? $argument->value : []); + + $modelToArrayReturn = $this->getModelMethodReturn($event->getInstance()->name, 'toArray', $event->arguments, $event->scope); + + if (! $modelToArrayReturn instanceof KeyedArrayType) { + return new Generic(MergeValue::class, [ + new LiteralBooleanType(true), + new KeyedArrayType([]), + ]); + } + + return new Generic(MergeValue::class, [ + new LiteralBooleanType(true), + new KeyedArrayType( + collect($modelToArrayReturn->items) + ->filter(fn (ArrayItemType_ $t) => in_array($t->key, $value)) + ->values() + ->all() + ), + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/JsonResponseMethodReturnTypeExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/JsonResponseMethodReturnTypeExtension.php new file mode 100644 index 00000000..8a39e245 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/JsonResponseMethodReturnTypeExtension.php @@ -0,0 +1,28 @@ +isInstanceOf(JsonResponse::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->name) { + 'setData' => tap($event->getInstance(), function (Generic $instance) use ($event) { + $instance->templateTypes[0] = $event->getArg('data', 0); + }), + default => null, + }; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/ModelCollectionTypeResolver.php b/vendor/dedoc/scramble/src/Support/InferExtensions/ModelCollectionTypeResolver.php new file mode 100644 index 00000000..6645bfac --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/ModelCollectionTypeResolver.php @@ -0,0 +1,45 @@ +resolve( + new GlobalScope, + new NewCallReferenceType( + static::resolveClass($modelType->name), + [new ArrayType($modelType)], + ), + ); + } + + public static function resolveClass(string $modelClass): string + { + try { + $reflectionMethod = new \ReflectionMethod($modelClass, 'newCollection'); + + if ($reflectionMethod->getDeclaringClass()->getName() === Model::class) { + return EloquentCollection::class; + } + + /** @var Model $model */ + $model = app($modelClass); + + return get_class($model->newCollection([])); + } catch (Throwable) { + return EloquentCollection::class; + } + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/ModelExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/ModelExtension.php new file mode 100644 index 00000000..1bbb43c5 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/ModelExtension.php @@ -0,0 +1,380 @@ +isInstanceOf(Model::class); + } + + public function hasProperty(ObjectType $type, string $name) + { + $info = $this->getModelInfo($type); + + return $info->get('attributes')->has($name) || $info->get('relations')->has($name); + } + + public function getPropertyType(PropertyFetchEvent $event): ?Type + { + if (! $this->hasProperty($event->getInstance(), $event->getName())) { + return null; + } + + $info = $this->getModelInfo($event->getInstance()); + + if ($attribute = $info->get('attributes')->get($event->getName())) { + $baseType = $this->getAttributeTypeFromEloquentCasts($attribute['cast'] ?? '') + ?? $this->getAttributeTypeFromDbColumnType($attribute['type'], $attribute['driver']) + ?? new UnknownType("Virtual attribute ({$attribute['name']}) type inference not supported."); + + if ($attribute['nullable']) { + return Union::wrap([$baseType, new NullType]); + } + + return $baseType; + } + + if ($relation = $info->get('relations')->get($event->getName())) { + return $this->getRelationType($relation); + } + + throw new \LogicException('Should not happen'); + } + + /** + * MySQL/MariaDB decimal is mapped to a string by PDO. + * Floating point numbers and decimals are all mapped to strings when using the pgsql driver. + */ + private function getAttributeTypeFromDbColumnType(?string $columnType, ?string $dbDriverName): ?AbstractType + { + if ($columnType === null) { + return null; + } + + $typeName = str($columnType) + ->before(' ') // strip modifiers from a type name such as `bigint unsigned` + ->before('(') // strip the length from a type name such as `tinyint(4)` + ->toString(); + + if (in_array($typeName, ['int', 'integer', 'tinyint', 'smallint', 'mediumint', 'bigint'])) { + return new IntegerType; + } + + if ($dbDriverName === 'sqlite' && in_array($typeName, ['float', 'double', 'decimal'])) { + return new FloatType; + } + + if (in_array($dbDriverName, ['mysql', 'mariadb']) && in_array($typeName, ['float', 'double'])) { + return new FloatType; + } + + return new StringType; + } + + /** + * @todo Add support for custom castables. + */ + private function getAttributeTypeFromEloquentCasts(string $cast): ?AbstractType + { + if ($cast && enum_exists($cast)) { + return new ObjectType($cast); + } + + $castAsType = Str::before($cast, ':'); + $castAsParameters = str($cast)->after("{$castAsType}:")->explode(','); + + if (Str::startsWith($castAsType, 'encrypted:')) { + $castAsType = $castAsParameters->first(); // array, collection, json, object + } + + return match ($castAsType) { + 'array', 'json' => new ArrayType, + 'real', 'float', 'double' => new FloatType, + 'int', 'integer', 'timestamp' => new IntegerType, + 'bool', 'boolean' => new BooleanType, + 'string', 'decimal' => new StringType, + 'object' => new ObjectType('\stdClass'), + 'collection' => new ObjectType(Collection::class), + 'Illuminate\Database\Eloquent\Casts\AsEnumCollection' => new Generic(Collection::class, [ + new IntegerType, // @todo array-key + new TemplateType($castAsParameters->first()), + ]), + 'date', 'datetime', 'custom_datetime' => $this->addDateFormatForCast(new ObjectType(Carbon::class), $castAsParameters), + 'immutable_date', 'immutable_datetime', 'immutable_custom_datetime' => $this->addDateFormatForCast(new ObjectType(CarbonImmutable::class), $castAsParameters), + default => null, + }; + } + + /** + * @param Collection $castAsParameters + */ + private function addDateFormatForCast(ObjectType $type, Collection $castAsParameters): ObjectType + { + $format = match ($castAsParameters->first()) { + 'Y-m-d' => 'date', + default => null, + }; + + if ($format) { + $type->setAttribute('format', $format); + } + + return $type; + } + + private function getRelationType(array $relation): Type + { + if ($isManyRelation = Str::contains($relation['type'], 'Many')) { + return ModelCollectionTypeResolver::resolve(new ObjectType($relation['related'])); + } + + return new ObjectType($relation['related']); + } + + protected function getToArrayMethodReturnType(MethodCallEvent $event): ?Type + { + if ($this->getRealToArrayMethodDefinitionClassName($event) !== Model::class) { + return null; + } + + $info = $this->getModelInfo($event->getInstance()); + + /** @var Model|null $instance */ + $instance = $info->get('instance'); + if (! $instance) { + return null; + } + + $arrayableAttributesTypes = $info->get('attributes', collect()) + ->when($instance->getVisible(), fn ($c, $visible) => $c->only($visible)) + ->when($instance->getHidden(), fn ($c, $visible) => $c->except($visible)) + ->filter(fn ($attr) => $attr['appended'] !== false) + ->map(function ($_, $name) use ($event) { + $propertyType = $event->getInstance()->getPropertyType($name); + + (new TypeWalker)->replace($propertyType, function (Type $t) { + return ($t->isInstanceOf(Carbon::class) || $t->isInstanceOf(CarbonImmutable::class)) + ? tap(new StringType, fn ($type) => $type->setAttribute('format', $t->getAttribute('format') ?? 'date-time')) + : null; + }); + + return $propertyType; + }); + + $arrayableRelationsTypes = $info->get('relations', collect()) + ->only($this->getProtectedValue($instance, 'with')) + ->when($instance->getVisible(), fn ($c, $visible) => $c->only($visible)) + ->when($instance->getHidden(), fn ($c, $visible) => $c->except($visible)) + ->map(function ($_, $name) use ($event) { + return $event->getInstance()->getPropertyType($name); + }); + + return new KeyedArrayType([ + ...$arrayableAttributesTypes->map(fn ($type, $name) => new ArrayItemType_($name, $type))->values()->all(), + ...$arrayableRelationsTypes->map(fn ($type, $name) => new ArrayItemType_($name, $type, isOptional: true))->values()->all(), + ]); + } + + protected function getGetOriginalMethodReturnType(MethodCallEvent $event): ?Type + { + $key = $event->getArg('key', 0); + + if (! $key instanceof LiteralStringType) { + return null; + } + + return $this->getPropertyType( + new PropertyFetchEvent($event->getInstance(), $key->value, $event->scope) + ); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->getName()) { + 'toArray' => $this->getToArrayMethodReturnType($event), + 'getOriginal' => $this->getGetOriginalMethodReturnType($event), + 'resolveResourceFromAttribute' => $this->getResolveResourceFromAttributeMethodReturnType($event), + 'guessResource' => $this->getGuessResourceMethodReturnType($event), + 'toResource' => $this->getToResourceMethodReturnType($event), + default => $this->maybeProxyMethodCallToBuilder($event), + }; + } + + protected function getToResourceMethodReturnType(MethodCallEvent $event): ?Type + { + $resourceClassArg = $event->getArg('resourceClass', 0); + + if ($resourceClassArg instanceof LiteralString) { + return $this->makeResource($resourceClassArg->getValue(), $event->getInstance()); + } + + return $this->getGuessResourceMethodReturnType($event); + } + + protected function makeResource(string $resourceClass, ObjectType $model): ?ObjectType + { + $resource = ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new StaticMethodCallReferenceType($resourceClass, 'make', [$model]) + ); + + if (! $resource instanceof ObjectType) { + return null; + } + + return $resource; + } + + protected function getGuessResourceMethodReturnType(MethodCallEvent $event): ?Type + { + $resourceClass = $this->resolveResourceFromAttribute($event->getInstance()->name); + + if (is_string($resourceClass) && class_exists($resourceClass)) { + return $this->makeResource($resourceClass, $event->getInstance()); + } + + try { + /** @var array $candidates */ + $candidates = $event->getInstance()->name::guessResourceName(); + foreach ($candidates as $candidate) { + if (is_string($candidate) && class_exists($candidate)) { // @phpstan-ignore function.alreadyNarrowedType + return $this->makeResource($candidate, $event->getInstance()); + } + } + } catch (\Throwable) { + } + + return null; + } + + protected function getResolveResourceFromAttributeMethodReturnType(MethodCallEvent $event): Type + { + $result = $this->resolveResourceFromAttribute($event->getInstance()->name); + + return $result ? new LiteralStringType($result) : new NullType; + } + + protected function resolveResourceFromAttribute(string $modelClassName): ?string + { + if (! class_exists(UseResource::class)) { + return null; + } + + if (! class_exists($modelClassName)) { + return null; + } + + $attributes = (new ReflectionClass($modelClassName))->getAttributes(UseResource::class); + + return $attributes !== [] + ? $attributes[0]->newInstance()->class + : null; + } + + public function getStaticMethodReturnType(StaticMethodCallEvent $event): ?Type + { + return match ($event->name) { + 'all' => ModelCollectionTypeResolver::resolve(new ObjectType($event->callee)), + default => $this->maybeProxyMethodCallToBuilder($event), + }; + } + + private function maybeProxyMethodCallToBuilder(MethodCallEvent|StaticMethodCallEvent $event): ?Type + { + if (! $definition = $event->getDefinition()) { + return null; + } + + if ($definition->hasMethodDefinition($event->getName())) { + return null; + } + + $referenceCall = new MethodCallReferenceType( + new Generic(Builder::class, [new ObjectType($definition->name)]), + $event->getName(), + $event->arguments instanceof AutoResolvingArgumentTypeBag ? $event->arguments->allUnresolved() : $event->arguments->all(), + ); + + return ReferenceTypeResolver::getInstance()->resolve($event->scope, $referenceCall); + } + + private function getModelInfo(ObjectType $type) + { + return static::$cache[$type->name] ??= (new ModelInfo($type->name))->handle(); + } + + private function getProtectedValue($obj, $name) + { + $array = (array) $obj; + $prefix = chr(0).'*'.chr(0); + + return $array[$prefix.$name]; + } + + /** + * Due to vendor classes being not analyzed for now, we may have a situation when defining class name in event is not + * truly represents the location of the method. But we want to make sure to get it right. + */ + private function getRealToArrayMethodDefinitionClassName(MethodCallEvent $event) + { + $className = $event->methodDefiningClassName ?: $event->getInstance()->name; + + try { + $reflectionMethod = new \ReflectionMethod($className, 'toArray'); + + return $reflectionMethod->getDeclaringClass()->getName(); + } catch (Throwable) { + } + + return $event->methodDefiningClassName; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/PaginateMethodsReturnTypeExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/PaginateMethodsReturnTypeExtension.php new file mode 100644 index 00000000..b0829657 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/PaginateMethodsReturnTypeExtension.php @@ -0,0 +1,110 @@ +isQueryLikeClass($name); + } + + public function getStaticMethodReturnType(StaticMethodCallEvent $event): ?Type + { + if (! in_array($event->name, PaginateMethodsReturnTypeExtension::PAGINATE_METHODS)) { + return null; + } + + return $this->getPaginatorType( + $this->normalizeCalleeQueryBuilder($event->callee), + $event->name, + ); + } + + private function normalizeCalleeQueryBuilder(string $model): Generic + { + return new Generic(\Illuminate\Database\Eloquent\Builder::class, [ + new ObjectType($model), + ]); + } + + public function getMethodReturnType(AnyMethodCallEvent $event): ?Type + { + if (! in_array($event->name, PaginateMethodsReturnTypeExtension::PAGINATE_METHODS)) { + return null; + } + + $shouldBeHandled = $event->getInstance() instanceof UnknownType + || $this->isQueryLike($event->getInstance()) + || $event->getDefinition()?->getMethodDefinition($event->name)?->definingClassName === Builder::class; + + if (! $shouldBeHandled) { + return null; + } + + return $this->getPaginatorType($event->getInstance(), $event->name); + } + + private function getPaginatorType(Type $callee, string $name): Generic + { + $valueType = (new TypeWalker)->first( + $callee, + fn (Type $type) => $type->isInstanceOf(Model::class), + ) ?: new UnknownType; + + return match ($name) { + 'paginate', 'fastPaginate' => new Generic(LengthAwarePaginator::class, [new IntegerType, $valueType]), + 'cursorPaginate' => new Generic(CursorPaginator::class, [new IntegerType, $valueType]), + 'simplePaginate', 'simpleFastPaginate' => new Generic(Paginator::class, [new IntegerType, $valueType]), + default => throw new \Exception('Pagination method '.$name.' is not handled'), + }; + } + + private function isQueryLike(Type $instance): bool + { + if (! $instance instanceof ObjectType) { + return false; + } + + return $this->isQueryLikeClass($instance->name); + } + + private function isQueryLikeClass(string $class): bool + { + return collect([ + EloquentBuilder::class, + BaseBuilder::class, + BelongsToMany::class, + HasManyThrough::class, + Model::class, + ])->some(fn (string $queryClass) => is_a($class, $queryClass, true)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/PossibleExceptionInfer.php b/vendor/dedoc/scramble/src/Support/InferExtensions/PossibleExceptionInfer.php new file mode 100644 index 00000000..2273b3cc --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/PossibleExceptionInfer.php @@ -0,0 +1,67 @@ +validate + // $request->validate + if ($node instanceof Expr\MethodCall) { + $isCallToValidate = $node->name instanceof Identifier && $node->name->name === 'validate'; + if ( + $scope->getType($node->var)->isInstanceOf(Validator::class) // Validator::make() + || $scope->getType($node->var)->isInstanceOf(Request::class) // $request + || ($node->var instanceof Expr\Variable && ($node->var->name ?? null) === 'this') + ) { + if ($isCallToValidate) { + return [ + new ObjectType(ValidationException::class), + ]; + } + } + // Validator::validate()? + + // $this->authorize + if ( + $node->name instanceof Identifier && $node->name->name === 'authorize' + && ($node->var instanceof Expr\Variable && ($node->var->name ?? null) === 'this') // $this + ) { + return [ + new ObjectType(AuthorizationException::class), + ]; + } + } + + // Gate::authorize + if ($node instanceof Expr\StaticCall) { + $isCallToAuthorize = $node->name instanceof Identifier && $node->name->name === 'authorize'; + + if ( + $isCallToAuthorize + && $node->class instanceof FullyQualified + && is_a($node->class->toString(), Gate::class, true) + ) { + return [ + new ObjectType(AuthorizationException::class), + ]; + } + } + + // $this->authorizeResource in __constructor + return []; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/RequestExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/RequestExtension.php new file mode 100644 index 00000000..2a933287 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/RequestExtension.php @@ -0,0 +1,44 @@ +isInstanceOf(Request::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->getName()) { + 'user' => new ObjectType($this->getUserModelClass()), + default => null, + }; + } + + protected function getUserModelClass(): string + { + $model = config('auth.providers.users.model'); + + if ($model && is_string($model) && class_exists($model)) { + return $model; + } + + if (class_exists($model = \App\Models\User::class)) { + return $model; + } + + if (class_exists($model = \App\User::class)) { + return $model; + } + + return \App\Models\User::class; // @phpstan-ignore class.notFound + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/ResourceCollectionTypeInfer.php b/vendor/dedoc/scramble/src/Support/InferExtensions/ResourceCollectionTypeInfer.php new file mode 100644 index 00000000..1a5cd24c --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/ResourceCollectionTypeInfer.php @@ -0,0 +1,66 @@ +isInstanceOf(ResourceCollection::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->name) { + 'toArray' => $this->getToArrayReturnType($event), + default => null, + }; + } + + private function getToArrayReturnType(MethodCallEvent $event): ?Type + { + if ($event->methodDefiningClassName === ResourceCollection::class) { + $isManualAnnotation = $event->getInstance() instanceof Generic + && count($event->getInstance()->templateTypes) === 1; + + if ($isManualAnnotation) { + return $this->getCollectionType($event->getInstance(), $event->scope->index); + } + + return null; // default behavior + } + + $parentType = $this->getCollectionType($event->getInstance(), $event->scope->index); + + $realType = $event->getDefinition()->getMethodDefinition('toArray')?->getReturnType(); + if ($realType instanceof UnknownType) { + /** + * When inferred return type of `toArray` method cannot be inferred, we'd like to fall back to + * the default behavior of ResourceCollection, so there is still SOME information. + */ + return $parentType; + } + + return null; // default behavior + } + + private function getCollectionType(ObjectType $type, Index $index): ArrayType + { + $normalizedType = (! $type instanceof Generic) ? new Generic($type->name) : $type; + + return new ArrayType( + (new ResourceCollectionTypeManager($normalizedType, $index))->getCollectedType(), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/ResourceResponseMethodReturnTypeExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/ResourceResponseMethodReturnTypeExtension.php new file mode 100644 index 00000000..9f9b663e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/ResourceResponseMethodReturnTypeExtension.php @@ -0,0 +1,40 @@ +isInstanceOf(ResourceResponse::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + if ($event->name !== 'toResponse') { + return null; + } + + $resourceType = $event->getInstance()->templateTypes[0] ?? null; + if (! $resourceType) { + return new Generic(JsonResponse::class, [new UnknownType, new UnknownType, new KeyedArrayType]); + } + + return new Generic(JsonResponse::class, [ + new Generic(ResourceResponse::class, [$resourceType]), + new UnknownType, + new ArrayType, + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/ResponseFactoryTypeInfer.php b/vendor/dedoc/scramble/src/Support/InferExtensions/ResponseFactoryTypeInfer.php new file mode 100644 index 00000000..79724649 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/ResponseFactoryTypeInfer.php @@ -0,0 +1,134 @@ +isInstanceOf(ResponseFactory::class); + } + + public function getFunctionReturnType(FunctionCallEvent $event): ?Type + { + if (count($event->arguments)) { + return new Generic(Response::class, [ + $event->getArg('content', 0, new LiteralStringType('')), + $event->getArg('status', 1, new LiteralIntegerType(200)), + $event->getArg('headers', 2, new ArrayType), + ]); + } + + return new ObjectType(ResponseFactory::class); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->name) { + 'noContent' => new Generic(Response::class, [ + new LiteralStringType(''), + $event->getArg('status', 0, new LiteralIntegerType(204)), + $event->getArg('headers', 1, new ArrayType), + ]), + 'json' => new Generic(JsonResponse::class, [ + $event->getArg('data', 0, new ArrayType), + $event->getArg('status', 1, new LiteralIntegerType(200)), + $event->getArg('headers', 2, new ArrayType), + ]), + 'make' => new Generic(Response::class, [ + $event->getArg('content', 0, new LiteralStringType('')), + $event->getArg('status', 1, new LiteralIntegerType(200)), + $event->getArg('headers', 2, new ArrayType), + ]), + 'download' => (new BinaryFileResponseTypeFactory( + file: $event->getArg('file', 0), + name: $event->getArg('name', 1, new NullType), + headers: $event->getArg('headers', 2, new ArrayType), + disposition: $event->getArg('disposition', 3, new LiteralStringType('attachment')), + ))->build(), + 'file' => (new BinaryFileResponseTypeFactory( + file: $event->getArg('file', 0), + headers: $event->getArg('headers', 1, new ArrayType), + ))->build(), + 'stream' => new Generic(StreamedResponse::class, [ + $event->getArg('callbackOrChunks', 0), + $event->getArg('status', 1, new LiteralIntegerType(200)), + $event->getArg('headers', 2, new ArrayType), + ]), + 'streamJson' => new Generic(StreamedJsonResponse::class, [ + $event->getArg('data', 0), + $event->getArg('status', 1, new LiteralIntegerType(200)), + $event->getArg('headers', 2, new ArrayType), + ]), + 'streamDownload' => new Generic(StreamedResponse::class, [ + $event->getArg('callback', 0), + new LiteralIntegerType(200), + $event->getArg('headers', 2, new ArrayType), + ]), + 'eventStream' => (new Generic(StreamedResponse::class, [ + $event->getArg('callback', 0), + new LiteralIntegerType(200), + $event->getArg('headers', 1, new ArrayType), + ]))->mergeAttributes([ + 'mimeType' => 'text/event-stream', + 'endStreamWith' => ($endStreamWithType = $event->getArg('endStreamWith', 2, new LiteralStringType(''))) instanceof LiteralStringType + ? $endStreamWithType->value + : null, + ]), + default => null, + }; + } + + public function getType(Expr $node, Scope $scope): ?Type + { + // call Response and JsonResponse constructors + if ( + $node instanceof Expr\New_ + && ( + $scope->getType($node)->isInstanceOf(JsonResponse::class) + || $scope->getType($node)->isInstanceOf(Response::class) + ) + ) { + /** @var ObjectType $nodeType */ + $nodeType = $scope->getType($node); + + $contentName = $nodeType->isInstanceOf(JsonResponse::class) ? 'data' : 'content'; + $contentDefaultType = $nodeType->isInstanceOf(JsonResponse::class) + ? new ArrayType + : new LiteralStringType(''); + + return new Generic($nodeType->name, [ + TypeHelper::getArgType($scope, $node->args, [$contentName, 0], $contentDefaultType), + TypeHelper::getArgType($scope, $node->args, ['status', 1], new LiteralIntegerType(200)), + TypeHelper::getArgType($scope, $node->args, ['headers', 2], new ArrayType), + ]); + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/ShallowFunctionDefinition.php b/vendor/dedoc/scramble/src/Support/InferExtensions/ShallowFunctionDefinition.php new file mode 100644 index 00000000..a0fd1e80 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/ShallowFunctionDefinition.php @@ -0,0 +1,34 @@ +type, $this->argumentsDefaults, $this->definingClassName, $this->isStatic); + $this->isFullyAnalyzed = true; + } + + public function getSelfOutType(): ?Generic + { + return $this->selfOutType; + } + + public function copyFromParent(): self + { + $copy = clone $this; + $copy->type = $copy->type->clone(); + + return $copy; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/TransformsToResourceCollectionExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/TransformsToResourceCollectionExtension.php new file mode 100644 index 00000000..ff18d7ec --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/TransformsToResourceCollectionExtension.php @@ -0,0 +1,158 @@ +name; + + if (! class_exists($type) || ! method_exists($type, 'toResourceCollection')) { + return false; + } + + return in_array(TransformsToResourceCollection::class, class_uses_recursive($type)); + } + + public function getMethodReturnType(MethodCallEvent $event): ?Type + { + return match ($event->getName()) { + 'guessResourceCollection' => $this->getGuessResourceCollectionMethodReturnType($event), + 'toResourceCollection' => $this->getToResourceCollectionMethodReturnType($event), + default => null, + }; + } + + protected function getToResourceCollectionMethodReturnType(MethodCallEvent $event): ?Type + { + $resourceClassArg = $event->getArg('resourceClass', 0); + + if ($resourceClassArg instanceof LiteralString) { + return $this->makeResourceCollection($resourceClassArg->getValue(), $event->getInstance()); + } + + return $this->getGuessResourceCollectionMethodReturnType($event); + } + + protected function getGuessResourceCollectionMethodReturnType(MethodCallEvent $event): ?Type + { + $modelClass = $this->getModelClassFromCollection($event->getInstance()); + + if (! $modelClass) { + return null; + } + + $collectionClass = $this->resolveResourceCollectionFromAttribute($modelClass); + if ($collectionClass && class_exists($collectionClass)) { + /** @see AfterResourceCollectionDefinitionCreatedExtension */ + return new Generic($collectionClass, [new ObjectType($modelClass)]); + } + + $resourceClass = $this->resolveResourceFromAttribute($modelClass); + if ($resourceClass && class_exists($resourceClass)) { + return $this->makeResourceCollection($resourceClass, $event->getInstance()); + } + + try { + /** @var array $candidates */ + $candidates = $modelClass::guessResourceName(); + + foreach ($candidates as $candidate) { + $collectionCandidate = $candidate.'Collection'; + if (class_exists($collectionCandidate)) { + return new Generic($collectionCandidate, [new ObjectType($modelClass)]); + } + } + + foreach ($candidates as $candidate) { + if (is_string($candidate) && class_exists($candidate)) { // @phpstan-ignore function.alreadyNarrowedType + return $this->makeResourceCollection($candidate, $event->getInstance()); + } + } + } catch (Throwable) { + } + + return null; + } + + private function getModelClassFromCollection(ObjectType $collectionType): ?string + { + if (! $collectionType instanceof Generic) { + return null; + } + + $itemType = $collectionType->templateTypes[1] ?? null; + + if (! $itemType instanceof ObjectType) { + return null; + } + + return $itemType->isInstanceOf(Model::class) ? $itemType->name : null; + } + + private function makeResourceCollection(string $resourceClass, ObjectType $collection): ?ObjectType + { + $result = ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new StaticMethodCallReferenceType($resourceClass, 'collection', [$collection]) + ); + + if (! $result instanceof ObjectType) { + return null; + } + + return $result; + } + + protected function resolveResourceFromAttribute(string $modelClassName): ?string + { + if (! class_exists(UseResource::class)) { + return null; + } + + if (! class_exists($modelClassName)) { + return null; + } + + $attributes = (new ReflectionClass($modelClassName))->getAttributes(UseResource::class); + + return $attributes !== [] + ? $attributes[0]->newInstance()->class + : null; + } + + protected function resolveResourceCollectionFromAttribute(string $modelClassName): ?string + { + if (! class_exists(UseResourceCollection::class)) { + return null; + } + + if (! class_exists($modelClassName)) { + return null; + } + + $attributes = (new ReflectionClass($modelClassName))->getAttributes(UseResourceCollection::class); + + return $attributes !== [] + ? $attributes[0]->newInstance()->class + : null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/TranslationReturnTypeExtension.php b/vendor/dedoc/scramble/src/Support/InferExtensions/TranslationReturnTypeExtension.php new file mode 100644 index 00000000..8a5bece8 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/TranslationReturnTypeExtension.php @@ -0,0 +1,30 @@ +arguments) === 0) { + return null; + } + + if (count($event->arguments) >= 2) { + return new StringType; + } + + return new TranslatedStringType($event->getArg('key', 0)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/TypeTraceInfer.php b/vendor/dedoc/scramble/src/Support/InferExtensions/TypeTraceInfer.php new file mode 100644 index 00000000..259807fc --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/TypeTraceInfer.php @@ -0,0 +1,27 @@ +isInClass()) { + $type = $scope->getType($node); + + $type->setAttribute('file', $scope->context->classDefinition->name); + $type->setAttribute('line', $node->getAttribute('startLine')); + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/InferExtensions/ValidatorTypeInfer.php b/vendor/dedoc/scramble/src/Support/InferExtensions/ValidatorTypeInfer.php new file mode 100644 index 00000000..678ccca7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/InferExtensions/ValidatorTypeInfer.php @@ -0,0 +1,31 @@ +class instanceof Node\Name && is_a($node->class->toString(), ValidatorFacade::class, true)) + ) { + return new Generic(Validator::class, [ + TypeHelper::getArgType($scope, $node->args, ['rules', 1]), + ]); + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationBuilder.php b/vendor/dedoc/scramble/src/Support/OperationBuilder.php new file mode 100644 index 00000000..dbe78bf1 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationBuilder.php @@ -0,0 +1,48 @@ +operationTransformers->all() as $operationTransformerClass) { + $instance = is_callable($operationTransformerClass) + ? $operationTransformerClass + : ContainerUtils::makeContextable($operationTransformerClass, [ + OpenApi::class => $openApi, + OpenApiContext::class => $typeTransformer->context, + GeneratorConfig::class => $config, + TypeTransformer::class => $typeTransformer, + ]); + + if (is_callable($instance)) { + $instance($operation, $routeInfo); + + continue; + } + + if ($instance instanceof OperationTransformer) { + $instance->handle($operation, $routeInfo); + + continue; + } + + // @phpstan-ignore deadCode.unreachable + throw new InvalidArgumentException('(callable(Operation, RouteInfo): void)|OperationTransformer type for operation transformer expected, received '.$instance::class); + } + + return $operation; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/DeprecationExtension.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/DeprecationExtension.php new file mode 100644 index 00000000..63ca76d9 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/DeprecationExtension.php @@ -0,0 +1,80 @@ +reflectionAction() || $this->isExplicitlyMarkedNotDeprecated($routeInfo)) { + return; + } + + $reflectionAction = $routeInfo->reflectionAction(); + + $deprecatedClassTags = $reflectionAction instanceof ReflectionMethod + ? $this->getClassDeprecatedTagValues($reflectionAction->getDeclaringClass()->getName()) + : []; + $deprecatedTags = $routeInfo->phpDoc()->getDeprecatedTagValues(); + + // Skip if no deprecations found + if (! $deprecatedClassTags && ! $deprecatedTags) { + return; + } + + $description = Str::of($this->generateDescription($deprecatedClassTags)); + + if ($description->isNotEmpty()) { + $description = $description->append("\n\n"); + } + + $description = $description->append($this->generateDescription($deprecatedTags)); + + $operation + ->description((string) $description) + ->deprecated(true); + } + + protected function isExplicitlyMarkedNotDeprecated(RouteInfo $routeInfo): bool + { + return $routeInfo->phpDoc()->getTagsByName('@notDeprecated') + || $routeInfo->phpDoc()->getTagsByName('@not-deprecated'); // inconsistent alias. + } + + /** + * @return array + */ + protected function getClassDeprecatedTagValues(string $fqdn): array + { + $reflector = ClassReflector::make($fqdn); + $classPhpDocString = $reflector->getReflection()->getDocComment(); + + if ($classPhpDocString === false) { + return []; + } + + return array_values(PhpDoc::parse($classPhpDocString)->getDeprecatedTagValues()); + } + + /** + * @param array $deprecatedTagValues + */ + private function generateDescription(array $deprecatedTagValues): string + { + return implode("\n", array_map(fn ($tag) => $tag->description, $deprecatedTagValues)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ErrorResponsesExtension.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ErrorResponsesExtension.php new file mode 100644 index 00000000..b702283e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ErrorResponsesExtension.php @@ -0,0 +1,135 @@ +getActionType()) { + return; + } + + $this->attachNotFoundException($operation, $methodType); + $this->attachAuthorizationException($routeInfo, $methodType); + $this->attachAuthenticationException($routeInfo, $methodType); + $this->attachCustomRequestExceptions($methodType); + } + + private function attachNotFoundException(Operation $operation, FunctionType $methodType) + { + $hasModelParams = collect($operation->parameters) + ->contains(function (Parameter $parameter) { + return $parameter->in === 'path' + && $parameter->schema->type->getAttribute('isModelId') === true; + }); + + if (! $hasModelParams) { + return; + } + + $methodType->exceptions = [ + ...$methodType->exceptions, + new ObjectType(ModelNotFoundException::class), + ]; + } + + private function attachAuthorizationException(RouteInfo $routeInfo, FunctionType $methodType) + { + if (! collect($routeInfo->route->gatherMiddleware())->contains(fn ($m) => is_string($m) && Str::startsWith($m, ['can:', Authorize::class.':']))) { + return; + } + + if (collect($methodType->exceptions)->contains(fn (Type $e) => $e->isInstanceOf(AuthorizationException::class))) { + return; + } + + $methodType->exceptions = [ + ...$methodType->exceptions, + new ObjectType(AuthorizationException::class), + ]; + } + + private function attachAuthenticationException(RouteInfo $routeInfo, FunctionType $methodType) + { + if (count($routeInfo->phpDoc()->getTagsByName('@unauthenticated'))) { + return; + } + + $isAuthMiddleware = fn ($m) => is_string($m) && ($m === 'auth' || Str::startsWith($m, 'auth:')); + + if (! collect($routeInfo->route->gatherMiddleware())->contains($isAuthMiddleware)) { + return; + } + + if (collect($methodType->exceptions)->contains(fn (Type $e) => $e->isInstanceOf(AuthenticationException::class))) { + return; + } + + $methodType->exceptions = [ + ...$methodType->exceptions, + new ObjectType(AuthenticationException::class), + ]; + } + + private function attachCustomRequestExceptions(FunctionType $methodType) + { + if (! $formRequest = collect($methodType->arguments)->first(fn (Type $arg) => $arg->isInstanceOf(FormRequest::class))) { + return; + } + + $formRequest = $formRequest instanceof ObjectType + ? $formRequest + : ($formRequest instanceof TemplateType ? $formRequest->is : null); + + if (! $formRequest) { + return; + } + + $formRequest = $this->infer->analyzeClass($formRequest->name); + + if ( + $formRequest->hasMethodDefinition('rules') + || $formRequest->hasMethodDefinition('after') + ) { + $methodType->exceptions = [ + ...$methodType->exceptions, + new ObjectType(ValidationException::class), + ]; + } + + if ($formRequest->hasMethodDefinition('authorize')) { + $authorizeReturnType = $formRequest->getMethodCallType('authorize'); + if ( + (! $authorizeReturnType instanceof LiteralBooleanType) + || $authorizeReturnType->value !== true + ) { + $methodType->exceptions = [ + ...$methodType->exceptions, + new ObjectType(AuthorizationException::class), + ]; + } + } + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/AttributesParametersExtractor.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/AttributesParametersExtractor.php new file mode 100644 index 00000000..c281949c --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/AttributesParametersExtractor.php @@ -0,0 +1,265 @@ +reflectionAction()) { + return $parameterExtractionResults; + } + + $ignoredParameters = collect($reflectionAction->getAttributes(IgnoreParam::class, ReflectionAttribute::IS_INSTANCEOF)) + ->values() + ->map(fn (ReflectionAttribute $ra) => $ra->newInstance()) + ->all(); + + $parameters = collect($reflectionAction->getAttributes(ParameterAttribute::class, ReflectionAttribute::IS_INSTANCEOF)) + ->values() + ->map(fn (ReflectionAttribute $ra) => $this->createParameter($parameterExtractionResults, $ra->newInstance(), $ra->getArguments())) + ->reject(fn (Parameter $p) => $this->shouldIgnoreParameter($p, $ignoredParameters)) + ->all(); + + $extractedAttributes = collect($parameters)->map(fn ($p) => "$p->name.$p->in")->all(); + foreach ($parameterExtractionResults as $automaticallyExtractedParameters) { + $this->addParametersFromClassAttributes($automaticallyExtractedParameters); + + // Named results map to a reusable component schema. Stripping fields from them would corrupt + // that shared schema for every other operation that references the same FormRequest. + // Action-level attributes are appended as a separate schemaless result instead, + // so RequestBodyExtension composes them as an allOf overlay on top of the intact $ref. + if ($automaticallyExtractedParameters->schemaName) { + continue; + } + + $automaticallyExtractedParameters->parameters = collect($automaticallyExtractedParameters->parameters) + ->reject(fn (Parameter $p) => $this->shouldIgnoreParameter($p, $ignoredParameters)) + ->filter(fn (Parameter $p) => ! in_array("$p->name.$p->in", $extractedAttributes)) + ->values() + ->all(); + } + + return [...$parameterExtractionResults, new ParametersExtractionResult($parameters)]; + } + + private function addParametersFromClassAttributes(ParametersExtractionResult $parameterExtractionResult): void + { + if (! $parameterExtractionResult->sourceClass || ! class_exists($parameterExtractionResult->sourceClass)) { + return; + } + + $reflection = new ReflectionClass($parameterExtractionResult->sourceClass); + + $ignoredParameters = collect($reflection->getAttributes(IgnoreParam::class, ReflectionAttribute::IS_INSTANCEOF)) + ->values() + ->map(fn (ReflectionAttribute $ra) => $ra->newInstance()) + ->all(); + + if ($ignoredParameters) { + $parameterExtractionResult->parameters = collect($parameterExtractionResult->parameters) + ->reject(fn (Parameter $p) => $this->shouldIgnoreParameter($p, $ignoredParameters)) + ->values() + ->all(); + } + + $attrs = collect($reflection->getAttributes(ParameterAttribute::class, ReflectionAttribute::IS_INSTANCEOF)) + ->values() + ->map(fn (ReflectionAttribute $ra) => $this->createParameter([$parameterExtractionResult], $ra->newInstance(), $ra->getArguments())) + ->reject(fn (Parameter $p) => $this->shouldIgnoreParameter($p, $ignoredParameters)) + ->keyBy(fn (Parameter $p) => "$p->name.$p->in") + ->all(); + + if (empty($attrs)) { + return; + } + + $parameterExtractionResult->parameters = collect($parameterExtractionResult->parameters) + ->map(fn (Parameter $p) => $attrs["$p->name.$p->in"] ?? $p) + ->values() + ->all(); + } + + /** + * @param ParametersExtractionResult[] $extractedParameters + */ + private function createParameter(array $extractedParameters, ParameterAttribute $attribute, array $attributeArguments): Parameter + { + $attributeParameter = $this->createParameterFromAttribute($attribute); + + if (! $attribute->infer) { + return $attributeParameter; + } + + if (! $inferredParameter = $this->getParameterFromAutomaticallyInferred($extractedParameters, $attribute->in, $attribute->name)) { + return $attributeParameter; + } + + $parameter = deep_copy($inferredParameter); + + $namedAttributes = $this->createNamedAttributes($attribute::class, $attributeArguments); + + foreach ($namedAttributes as $name => $attrValue) { + if ($name === 'in' || $name === 'name') { + continue; + } + + if ($name === 'default') { + $parameter->schema->type->default = $attrValue; + } + + if ($name === 'type') { + $parameter->schema->type = $attributeParameter->schema->type; + } + + if ($name === 'format') { + $parameter->schema->type->format = $attributeParameter->schema->type->format; + } + + if ($name === 'deprecated') { + $parameter->deprecated = $attributeParameter->deprecated; + } + + if ($name === 'description') { + $parameter->description = $attributeParameter->description; + } + + if ($name === 'required') { + $parameter->required = $attributeParameter->required; + } + + if ($name === 'example') { + $parameter->example = $attributeParameter->example; + } + + if ($name === 'examples') { + $parameter->examples = $attributeParameter->examples; + } + + $parameter->setAttribute('nonBody', $attributeParameter->getAttribute('nonBody')); + } + + return $parameter; + } + + private function createParameterFromAttribute(ParameterAttribute $attribute): Parameter + { + $default = $attribute->default instanceof MissingValue ? new OpenApiMissingValue : $attribute->default; + $type = $attribute->type ? $this->openApiTransformer->transform( + PhpDocTypeHelper::toType( + PhpDoc::parse("/** @return $attribute->type */")->getReturnTagValues()[0]->type ?? new IdentifierTypeNode('mixed') + ) + ) : new MixedType; + + $parameter = Parameter::make($attribute->name, $attribute->in) + ->description($attribute->description ?: '') + ->setSchema(Schema::fromType( + $type->default($default) // @phpstan-ignore argument.type + )) + ->required($attribute->required); + + $parameter->setAttribute('nonBody', $attribute->in !== 'body'); + + $parameter->deprecated = $attribute->deprecated; + + if (! $attribute->example instanceof MissingValue) { + $parameter->example = $attribute->example; + } + + if ($attribute->examples) { + $parameter->examples = array_map( + fn (Example $e) => Example::toOpenApiExample($e), + $attribute->examples, + ); + } + + if ($attribute->format) { + $type->format = $attribute->format; + } + + return $parameter; + } + + /** + * @param ParametersExtractionResult[] $extractedParameters + */ + private function getParameterFromAutomaticallyInferred(array $extractedParameters, string $in, string $name): ?Parameter + { + foreach ($extractedParameters as $automaticallyExtractedParameters) { + foreach ($automaticallyExtractedParameters->parameters as $parameter) { + if ( + $parameter->in === $in + && $parameter->name === $name + ) { + return $parameter; + } + } + } + + return null; + } + + private function createNamedAttributes(string $class, array $attributeArguments): array + { + $reflectionClass = new ReflectionClass($class); + + if (! $reflectionConstructor = $reflectionClass->getConstructor()) { + return $attributeArguments; + } + + $constructorParameters = $reflectionConstructor->getParameters(); + + return collect($attributeArguments) + ->mapWithKeys(function ($value, $key) use ($constructorParameters) { + $name = is_string($key) ? $key : $constructorParameters[$key]->getName(); + + return [$name => $value]; + }) + ->all(); + } + + /** + * @param IgnoreParam[] $ignoredParameters + */ + private function shouldIgnoreParameter(Parameter $parameter, array $ignoredParameters): bool + { + foreach ($ignoredParameters as $ignoredParameter) { + if ($ignoredParameter->name !== $parameter->name) { + continue; + } + + if ($ignoredParameter->in) { + return $ignoredParameter->in === $parameter->in; + } + + if (in_array($parameter->in, ['body', 'query'], true)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php new file mode 100644 index 00000000..ff017a68 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php @@ -0,0 +1,141 @@ +[] + */ + private static array $ignoredInstancesOf = []; + + public function __construct( + private PrettyPrinter $printer, + private TypeTransformer $openApiTransformer, + ) {} + + public function handle(RouteInfo $routeInfo, array $parameterExtractionResults): array + { + if (! $requestClassName = $this->getFormRequestClassName($routeInfo)) { + return $parameterExtractionResults; + } + + if ($this->isIgnored($requestClassName)) { + return $parameterExtractionResults; + } + + $parameterExtractionResults[] = $this->extractFormRequestParameters($requestClassName, $routeInfo); + + return $parameterExtractionResults; + } + + /** + * @param class-string|class-string[] $ignoredClasses + */ + public static function ignoreInstanceOf(string|array $ignoredClasses): void + { + $ignoredClasses = Arr::wrap($ignoredClasses); + + foreach ($ignoredClasses as $ignoredClass) { + if (! in_array($ignoredClass, self::$ignoredInstancesOf, true)) { + self::$ignoredInstancesOf[] = $ignoredClass; + } + } + } + + private function getFormRequestClassName(RouteInfo $routeInfo): ?string + { + if (! $reflectionAction = $routeInfo->reflectionAction()) { + return null; + } + + /** @var ReflectionParameter $requestParam */ + if (! $requestParam = collect($reflectionAction->getParameters())->first($this->isCustomRequestParam(...))) { + return null; + } + + $requestClassName = $requestParam->getType()->getName(); + + $reflectionClass = new ReflectionClass($requestClassName); + + // If the classname is actually an interface, it may be bound to the container. + if (! $reflectionClass->isInstantiable() && app()->bound($requestClassName)) { + $classInstance = app()->getBindings()[$requestClassName]['concrete'](app()); + $requestClassName = $classInstance::class; + } + + return $requestClassName; + } + + private function isCustomRequestParam(ReflectionParameter $reflectionParameter): bool + { + if (! $reflectionParameter->getType() instanceof ReflectionNamedType) { + return false; + } + + $className = $reflectionParameter->getType()->getName(); + + return method_exists($className, 'rules'); + } + + private function isIgnored(string $className): bool + { + foreach (self::$ignoredInstancesOf as $ignoredClass) { + if (is_a($className, $ignoredClass, true)) { + return true; + } + } + + return false; + } + + public function extractFormRequestParameters(string $requestClassName, RouteInfo $routeInfo): ParametersExtractionResult + { + $classReflector = Infer\Reflector\ClassReflector::make($requestClassName); + + $reflection = $classReflector->getReflection(); + + $phpDocReflector = SchemaClassDocReflector::createFromDocString($reflection->getDocComment() ?: ''); + + $schemaNameAttr = ($reflection->getAttributes(SchemaName::class)[0] ?? null)?->newInstance(); + $schemaName = ($phpDocReflector->getTagValue('@ignoreSchema')->value ?? null) !== null + ? null + : ($schemaNameAttr ? ($schemaNameAttr->input ?? $schemaNameAttr->name) : $phpDocReflector->getSchemaName($requestClassName)); + + return new ParametersExtractionResult( + parameters: $this->makeParameters( + rules: (new ComposedFormRequestRulesEvaluator($this->printer, $classReflector, $routeInfo->method))->handle(), + typeTransformer: $this->openApiTransformer, + rulesDocsRetriever: new TypeBasedRulesDocumentationRetriever( + $routeInfo->getScope(), + new MethodCallReferenceType(new ObjectType($requestClassName), 'rules', []), + ), + in: in_array(mb_strtolower($routeInfo->method), RequestBodyExtension::HTTP_METHODS_WITHOUT_REQUEST_BODY) + ? 'query' + : 'body', + ), + schemaName: $schemaName, + description: $phpDocReflector->getDescription(), + sourceClass: $requestClassName, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/InferredParameter.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/InferredParameter.php new file mode 100644 index 00000000..d64dc3f3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/InferredParameter.php @@ -0,0 +1,44 @@ +|scalar|null|MissingValue $default + */ + public function __construct( + public string $name, + public ?string $description = null, + public Type $type = new UnknownType, + public mixed $default = new MissingValue, + ) {} + + public function toOpenApiParameter(TypeTransformer $transformer): Parameter + { + $parameter = Parameter::make($this->name, 'query'/* @todo: this is just a temp solution */) + ->description($this->description ?: '') + ->setSchema(Schema::fromType( + $transformer + ->transform($this->type) + ->default($this->default) + )); + + if ($this->type->getAttribute('isFlat')) { + $parameter->setAttribute('isFlat', true); + } + + if ($this->type->getAttribute('isInQuery')) { + $parameter->setAttribute('isInQuery', true); + } + + return $parameter; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/JsonApiResourceParametersExtractor.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/JsonApiResourceParametersExtractor.php new file mode 100644 index 00000000..77dc81a4 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/JsonApiResourceParametersExtractor.php @@ -0,0 +1,187 @@ +getActionType()?->getReturnType()) { + return $parameterExtractionResults; + } + + if (! $resourceType = $this->getResourceType($returnType)) { + return $parameterExtractionResults; + } + + $reflectionJsonApi = ReflectionJsonApiResource::createForClass($resourceType->name); + + $parameters = array_values(array_filter([ + $this->getIncludeParameter($reflectionJsonApi), + ...$this->getAllIncludedSparseFieldsParameters($reflectionJsonApi, $resourceType), + ])); + + if (! $parameters) { + return $parameterExtractionResults; + } + + return [ + ...$parameterExtractionResults, + new ParametersExtractionResult( + $parameters, + sourceClass: $resourceType->name, + ), + ]; + } + + private function getIncludeParameter(ReflectionJsonApiResource $reflectionJsonApi): ?Parameter + { + $includes = $this->getAvailableRelations($reflectionJsonApi); + + if (! $includes) { + return null; + } + + return $this->queryParameterFactory->createEnumArray( + name: 'include', + values: $includes, + ); + } + + /** + * @return list + */ + private function getAllIncludedSparseFieldsParameters(ReflectionJsonApiResource $reflectionJsonApi, Generic $resourceType): array + { + if ($this->shouldIgnoreFieldsAndIncludesInQueryString($resourceType)) { + return []; + } + + return [ + $this->getSparseFieldsParameter($reflectionJsonApi), + ...array_map( + fn ($rn) => $this->getSparseFieldsParameter(ReflectionJsonApiResource::createForClass($rn)), + $this->getAvailableIncludeResourcesNames($reflectionJsonApi) + ), + ]; + } + + private function shouldIgnoreFieldsAndIncludesInQueryString(Generic $resourceType): bool + { + $usesQueryString = $this->jsonApiResourceTypeManager->getPropertyType($resourceType, 'usesRequestQueryString'); + + return $usesQueryString instanceof LiteralBooleanType && $usesQueryString->value === false; + } + + private function getSparseFieldsParameter(ReflectionJsonApiResource $reflectionJsonApi): ?Parameter + { + if (! $fields = $this->getAvailableFields($reflectionJsonApi)) { + return null; + } + + if (! $type = $this->getType($reflectionJsonApi)) { + return null; + } + + return $this->queryParameterFactory->createEnumArray( + name: 'fields['.$type.']', + values: $fields, + ); + } + + /** + * @todo Union + */ + private function getResourceType(Type $type): ?Generic + { + if ($type instanceof TemplateType) { + $type = $type->is; + } + if (! $type) { + return null; + } + + /** @var ObjectType|null $resourceType */ + $resourceType = (new TypeWalker)->first( + $type, + fn ($t) => $t instanceof ObjectType && $t->isInstanceOf(JsonApiResource::class), + ); + + if (! $resourceType) { + return null; + } + + return $this->jsonApiResourceTypeManager->normalizeType($resourceType); + } + + /** + * @return string[] + */ + private function getAvailableRelations(ReflectionJsonApiResource $reflectionJsonApi): array + { + return array_map( + fn ($r) => $r->name, + $reflectionJsonApi->getNestedRelationshipItems($this->config->jsonApi->maxRelationshipDepth()), + ); + } + + /** + * @return list + */ + private function getAvailableIncludeResourcesNames(ReflectionJsonApiResource $reflectionJsonApi): array + { + return array_values(array_unique(array_map( + fn ($r) => $r->resourceType->name, + $reflectionJsonApi->getNestedRelationshipItems($this->config->jsonApi->maxRelationshipDepth()), + ))); + } + + /** + * @return string[] + */ + private function getAvailableFields(ReflectionJsonApiResource $reflectionJsonApi): array + { + if (! $attributesType = $reflectionJsonApi->getAttributesType()) { + return []; + } + + $fields = []; + foreach ($attributesType->items as $item) { + if ($item->isNumericKey()) { + continue; + } + $fields[] = $item->key; + } + + return $fields; + } + + private function getType(ReflectionJsonApiResource $reflectionJsonApi): ?string + { + $type = $reflectionJsonApi->getTypeType(new Generic($reflectionJsonApi->name, [])); + + return $type instanceof LiteralString ? $type->getValue() : null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/MethodCallsParametersExtractor.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/MethodCallsParametersExtractor.php new file mode 100644 index 00000000..2dac3666 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/MethodCallsParametersExtractor.php @@ -0,0 +1,52 @@ +getActionDefinition(); + + $previouslyExtractedParameters = collect($parameterExtractionResults)->flatMap->parameters->keyBy('name'); + + $inferredParameters = array_values(array_filter( + $routeInfo->requestParametersFromCalls->data, + fn (InferredParameter $p) => ! $previouslyExtractedParameters->has($p->name), + )); + + $parameters = array_map(function (InferredParameter $p) use ($routeInfo) { + $parameter = $p->toOpenApiParameter($this->openApiTransformer); + + $parameter->in = in_array(mb_strtolower($routeInfo->method), RequestBodyExtension::HTTP_METHODS_WITHOUT_REQUEST_BODY) + ? 'query' + : 'body'; + + if ($parameter->getAttribute('isInQuery')) { + $parameter->in = 'query'; + } + + return $parameter; + }, $inferredParameters); + + $extractedParameters = new ParametersExtractionResult($parameters); + + /* + * Possible improvements here: using defaults when merging results, etc. + */ + + return [...$parameterExtractionResults, $extractedParameters]; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ParameterExtractor.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ParameterExtractor.php new file mode 100644 index 00000000..078cd25a --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ParameterExtractor.php @@ -0,0 +1,15 @@ +route); + + $aliases = $reflectionRoute->getSignatureParametersMap(); + $routeParams = collect($route->signatureParameters()); + $reflectionParamsByKeys = $routeParams->keyBy->name; + $paramsValuesClasses = $reflectionRoute->getBoundParametersTypes(); + $phpDocTypehintParam = collect($routeInfo->phpDoc()->getParamTagValues())->keyBy(fn (ParamTagValueNode $n) => Str::replace('$', '', $n->parameterName)); + + /* + * Figure out param type based on importance priority: + * 1. Typehint (reflection) + * 2. PhpDoc Typehint + * 3. String (?) + */ + $parameters = array_map(function (string $paramName) use ($routeInfo, $route, $aliases, $reflectionParamsByKeys, $phpDocTypehintParam, $paramsValuesClasses) { + $originalParamName = $paramName; + $paramName = $aliases[$paramName]; + + $description = $phpDocTypehintParam[$paramName]?->description ?? ''; + [$schemaType, $description, $isOptional] = $this->getParameterType( + $paramName, + $description, + $routeInfo, + $route, + $phpDocTypehintParam[$paramName] ?? null, + $reflectionParamsByKeys[$paramName] ?? null, + $paramsValuesClasses[$originalParamName] ?? null, + ); + + $param = Parameter::make($paramName, 'path') + ->description($description) + ->setSchema(Schema::fromType($schemaType)); + + if ($isOptional) { + $param->setExtensionProperty('optional', true); + } + + $param->setAttribute('nonBody', true); + + return $param; + }, array_values(array_diff($route->parameterNames(), $this->getParametersFromString($route->getDomain())))); + + return [...$parameterExtractionResults, new ParametersExtractionResult($parameters)]; + } + + private function getParametersFromString(?string $str) + { + return Str::of($str)->matchAll('/\{(.*?)\}/')->values()->toArray(); + } + + private function getParameterType( + string $paramName, + string $description, + RouteInfo $routeInfo, + Route $route, + ?ParamTagValueNode $phpDocParam, + ?ReflectionParameter $reflectionParam, + ?string $boundClass, + ) { + $type = $boundClass ? new ObjectType($boundClass) : new UnknownType; + if ($routeInfo->reflectionAction()) { + $type->setAttribute('file', $routeInfo->reflectionAction()->getFileName()); + $type->setAttribute('line', $routeInfo->reflectionAction()->getStartLine()); + } + + if ($phpDocParam?->type) { + $type = PhpDocTypeHelper::toType($phpDocParam->type); + } + + if ($reflectionParam?->hasType()) { + $type = TypeHelper::createTypeFromReflectionType($reflectionParam->getType()); + } + + $simplifiedType = Union::wrap(array_map( + fn (InferType $t) => $t instanceof ObjectType + ? (enum_exists($t->name) ? $t : new \Dedoc\Scramble\Support\Type\StringType) + : $t, + $type instanceof Union ? $type->types : [$type], + )); + + $schemaType = $this->openApiTransformer->transform($simplifiedType); + + if ($isModelId = $type instanceof ObjectType) { + [$schemaType, $description] = $this->getModelIdTypeAndDescription($schemaType, $type, $paramName, $description, $route->bindingFields()[$paramName] ?? null); + + $schemaType->setAttribute('isModelId', true); + } + + if ($schemaType instanceof \Dedoc\Scramble\Support\Generator\Types\UnknownType) { + $schemaType = (new StringType)->mergeAttributes($schemaType->attributes()); + } + + if ($reflectionParam?->isDefaultValueAvailable()) { + $schemaType->default($reflectionParam->getDefaultValue()); + } + + $description ??= ''; + + if ($isOptional = Str::contains($route->uri(), ['{'.$paramName.'?}', '{'.Str::snake($paramName).'?}'], ignoreCase: true)) { + $description = implode('. ', array_filter(['**Optional**', $description])); + } + + return [$schemaType, $description, $isOptional]; + } + + private function getModelIdTypeAndDescription( + Type $baseType, + InferType $type, + string $paramName, + string $description, + ?string $bindingField, + ): array { + $defaults = [ + $baseType, + $description ?: 'The '.Str::of($paramName)->kebab()->replace(['-', '_'], ' ').' ID', + ]; + + if (! $type->isInstanceOf(Model::class)) { + return $defaults; + } + + /** @var ObjectType $type */ + $defaults[0] = $this->openApiTransformer->transform(new IntegerType); + + try { + /** @var Model $modelInstance */ + $modelInstance = resolve($type->name); + } catch (BindingResolutionException) { + return $defaults; + } + + $modelKeyName = $modelInstance->getKeyName(); + $routeKeyName = $bindingField ?: $modelInstance->getRouteKeyName(); + + if ($description === '') { + $keyDescriptionName = in_array($routeKeyName, ['id', 'uuid']) + ? Str::upper($routeKeyName) + : (string) Str::of($routeKeyName)->lower()->kebab()->replace(['-', '_'], ' '); + + $description = 'The '.Str::of($paramName)->kebab()->replace(['-', '_'], ' ').' '.$keyDescriptionName; + } + + $modelTraits = class_uses($type->name); + if ($routeKeyName === $modelKeyName && Arr::hasAny($modelTraits, [HasUuids::class, HasVersion4Uuids::class])) { + return [(new StringType)->format('uuid'), $description]; + } + + $propertyType = $type->getPropertyType($routeKeyName); + if ($propertyType instanceof UnknownType) { + $propertyType = new IntegerType; + } + + return [$this->openApiTransformer->transform($propertyType), $description]; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesDocumentationRetriever.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesDocumentationRetriever.php new file mode 100644 index 00000000..b5928acc --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesDocumentationRetriever.php @@ -0,0 +1,16 @@ + + */ + public function getDocNodes(): array; +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesNodes.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesNodes.php new file mode 100644 index 00000000..bf8ddd8c --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/RulesNodes.php @@ -0,0 +1,88 @@ +find( // @phpstan-ignore argument.type + $statements, + fn (Node $node) => $node instanceof Node\Expr\ArrayItem && $node->getAttribute('parsedPhpDoc'), + ), + className: $className, + ); + } + + /** + * @return array + */ + public function getDocNodes(): array + { + return collect($this->nodes) + ->mapWithKeys(function (Node\Expr\ArrayItem $item) { + if (! $item->key) { + return []; + } + + try { + $key = $this->buildEvaluator()->evaluateSilently($item->key); + } catch (ConstExprEvaluationException $e) { + return []; + } + + if (! is_string($key)) { + return []; + } + + $parsedDoc = $item->getAttribute('parsedPhpDoc'); + if (! $parsedDoc instanceof PhpDocNode) { + return []; + } + + return [$key => $parsedDoc]; + }) + ->all(); + } + + private function buildEvaluator(): ConstExprEvaluator + { + return new ConstExprEvaluator(function ($expr) { + $default = new stdClass; + + $evaluatedConstFetch = (new ConstFetchEvaluator([ + 'self' => $this->className, + 'static' => $this->className, + ]))->evaluate($expr, $default); + + if ($evaluatedConstFetch !== $default) { + return $evaluatedConstFetch; + } + + return null; + }); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/TypeBasedRulesDocumentationRetriever.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/TypeBasedRulesDocumentationRetriever.php new file mode 100644 index 00000000..e5152bb1 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/TypeBasedRulesDocumentationRetriever.php @@ -0,0 +1,66 @@ + + */ + public function getDocNodes(): array + { + /** @var ArrayItemType_[] $arrayItemsNodes */ + $arrayItemsNodes = (new TypeWalker)->findAll( + ReferenceTypeResolver::getInstance()->resolve($this->scope, $this->type), + fn (Type $t) => $t instanceof ArrayItemType_ && $t->getAttribute('docNode'), + ); + + return collect($arrayItemsNodes) + ->mapWithKeys(function (ArrayItemType_ $item) { + if (! $key = $this->getArrayItemKey($item)) { + return []; + } + + $parsedDoc = $item->getAttribute('docNode'); + if (! $parsedDoc instanceof PhpDocNode) { + return []; + } + + return [$key => $parsedDoc]; + }) + ->all(); + } + + private function getArrayItemKey(ArrayItemType_ $item): ?string + { + if (is_string($item->key)) { + return $item->key; + } + + if ($item->keyType) { + $resolvedKeyType = ReferenceTypeResolver::getInstance()->resolve($this->scope, $item->keyType); + + return $resolvedKeyType instanceof LiteralStringType + ? $resolvedKeyType->value + : null; + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php new file mode 100644 index 00000000..982f4fe6 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php @@ -0,0 +1,128 @@ +actionNode()) { + return $parameterExtractionResults; + } + + [$callToValidate, $validationRules] = $this->getCallToValidateAndValidationRulesNodes($astNode); + + if (! $validationRules) { + return $parameterExtractionResults; + } + + $validationRulesNode = $validationRules instanceof Node\Arg ? $validationRules->value : $validationRules; + + $phpDocReflector = new SchemaClassDocReflector($callToValidate->getAttribute('parsedPhpDoc', new PhpDocNode([]))); + + $parameterExtractionResults[] = new ParametersExtractionResult( + parameters: $this->makeParameters( + rules: (new NodeRulesEvaluator($this->printer, $astNode, $validationRulesNode, $routeInfo->method, $routeInfo->className(), $routeInfo->getScope()))->handle(), + typeTransformer: $this->openApiTransformer, + rulesDocsRetriever: new TypeBasedRulesDocumentationRetriever( + $routeInfo->getScope(), + $routeInfo->getScope()->getType($validationRulesNode), + ), + in: in_array(mb_strtolower($routeInfo->method), RequestBodyExtension::HTTP_METHODS_WITHOUT_REQUEST_BODY) + ? 'query' + : 'body', + ), + schemaName: $phpDocReflector->getSchemaName(), + description: $phpDocReflector->getDescription(), + ); + + return $parameterExtractionResults; + } + + private function getCallToValidateAndValidationRulesNodes(FunctionLike $methodNode) + { + // $request->validate, when $request is a Request instance + /** @var Node\Expr\MethodCall $callToValidate */ + $callToValidate = (new NodeFinder)->findFirst( + $methodNode, + fn (Node $node) => $node instanceof Node\Expr\MethodCall + && $node->var instanceof Node\Expr\Variable + && is_a($this->getPossibleParamType($methodNode, $node->var), Request::class, true) + && $node->name instanceof Node\Identifier + && $node->name->name === 'validate' + ); + $validationRules = $callToValidate->args[0] ?? null; + + if (! $validationRules) { + // $this->validate($request, $rules), rules are second param. First should be $request, but no way to check type. So relying on convention. + $callToValidate = (new NodeFinder)->findFirst( + $methodNode, + fn (Node $node) => $node instanceof Node\Expr\MethodCall + && count($node->args) >= 2 + && $node->var instanceof Node\Expr\Variable && $node->var->name === 'this' + && $node->name instanceof Node\Identifier && $node->name->name === 'validate' + && $node->args[0]->value instanceof Node\Expr\Variable + && is_a($this->getPossibleParamType($methodNode, $node->args[0]->value), Request::class, true) + && $node->name->name === 'validate' + ); + $validationRules = $callToValidate->args[1] ?? null; + } + + if (! $validationRules) { + // Validator::make($request->...(), $rules), rules are second param. First should be $request, but no way to check type. So relying on convention. + $callToValidate = (new NodeFinder)->findFirst( + $methodNode, + fn (Node $node) => $node instanceof Node\Expr\StaticCall + && count($node->args) >= 2 + && $node->class instanceof Node\Name && is_a($node->class->toString(), \Illuminate\Support\Facades\Validator::class, true) + && $node->name instanceof Node\Identifier && $node->name->name === 'make' + && $node->args[0]->value instanceof Node\Expr\MethodCall && is_a($this->getPossibleParamType($methodNode, $node->args[0]->value->var), Request::class, true) + ); + $validationRules = $callToValidate->args[1] ?? null; + } + + return [$callToValidate, $validationRules]; + } + + private function getPossibleParamType(FunctionLike $methodNode, Node\Expr\Variable $node): ?string + { + $paramsMap = collect($methodNode->getParams()) + ->mapWithKeys(function (Node\Param $param) { + if (! isset($param->type->name)) { + return []; + } + + try { + return [ + $param->var->name => $param->type->name, + ]; + } catch (\Throwable $exception) { + throw $exception; + } + }) + ->toArray(); + + return $paramsMap[$node->name] ?? null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RequestBodyExtension.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RequestBodyExtension.php new file mode 100644 index 00000000..3b9a46d1 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RequestBodyExtension.php @@ -0,0 +1,274 @@ +phpDoc()->getAttribute('description')); // @phpstan-ignore argument.type + + /** @var Collection $rulesResults */ + $rulesResults = collect(); + + try { + $rulesResults = collect($this->extractParameters($operation, $routeInfo)); + } catch (Throwable $exception) { + if (Scramble::shouldThrowOnError()) { + throw $exception; + } + $description = $description->append('⚠️ Cannot generate request documentation: '.$exception->getMessage()); + } + + // Only set summary and description from PHPDoc if they haven't been set by other extensions (e.g., Endpoint attribute) + if (empty($operation->summary)) { + $operation->summary(Str::of($routeInfo->phpDoc()->getAttribute('summary'))->rtrim('.')); // @phpstan-ignore argument.type + } + + if (empty($operation->description)) { + $operation->description($description); + } + + $allParams = $rulesResults + ->flatMap(fn ($p) => $p->parameters) + ->unique(fn ($p) => "$p->name.$p->in") + ->values() + ->all(); + + if (empty($allParams)) { + return; + } + + if (in_array($operation->method, static::HTTP_METHODS_WITHOUT_REQUEST_BODY)) { + $operation->addParameters($this->prepareQueryParams($allParams)); + + return; + } + + [$nonBodyParams, $bodyParams] = array_map( + fn ($c) => $c->all(), + collect($allParams) + ->partition(fn (Parameter $p) => $p->in !== 'body' || $p->getAttribute('isInQuery') || $p->getAttribute('nonBody')) + ->all(), + ); + + $operation->addParameters($this->prepareQueryParams($nonBodyParams)); + + if (! $bodyParams) { + return; + } + + [$schemaResults, $schemalessResults] = $rulesResults->partition('schemaName')->all(); + $schemalessResults = collect([$this->mergeSchemalessRulesResults($schemalessResults->values())]); + + $schemas = $schemaResults->merge($schemalessResults) + ->map(function (ParametersExtractionResult $r) use ($nonBodyParams) { + $qpNames = collect($nonBodyParams)->keyBy(fn ($p) => "$p->name.$p->in"); + + $r->parameters = collect($r->parameters)->filter(fn ($p) => ! $qpNames->has("$p->name.$p->in"))->values()->all(); + + return $r; + }) + ->filter(fn (ParametersExtractionResult $r) => count($r->parameters) || $r->schemaName) + ->map($this->makeSchemaFromResults(...)); + + if ($schemas->isEmpty()) { + return; + } + + $schema = $this->makeComposedRequestBodySchema($schemas); + if (! $schema instanceof Reference) { + $schema = Schema::fromType($schema); + } + + $operation->addRequestBodyObject( + RequestBodyObject::make() + ->setContent($this->getMediaType($operation, $routeInfo, $allParams), $schema) + ->required($this->isSchemaRequired($schema)) + ); + } + + protected function isSchemaRequired(Reference|Schema $schema): bool + { + $schema = $schema instanceof Reference + ? $schema->resolve() + : $schema; + + $type = $schema instanceof Schema ? $schema->type : $schema; + + if ($type instanceof ObjectType) { + return count($type->required) > 0; + } + + return false; + } + + protected function makeSchemaFromResults(ParametersExtractionResult $result): Type + { + $requestBodySchema = Schema::createFromParameters( + $parameters = $this->unescapeEscapedDotNamedParameters($this->convertDotNamedParamsToComplexStructures($result->parameters)) + ); + + if (count($parameters) === 1 && $parameters[0]->name === '*' && $parameters[0]->schema) { + $requestBodySchema->type = $parameters[0]->schema->type; + } + + if (! $result->schemaName) { + return $requestBodySchema->type; + } + + $components = $this->openApiTransformer->getComponents(); + if (! $components->hasSchema($result->schemaName)) { + $requestBodySchema->type->setDescription($result->description ?: ''); + + $components->addSchema($result->schemaName, $requestBodySchema); + } + + return new Reference('schemas', $result->schemaName, $components); + } + + /** + * @param Collection $schemas + */ + protected function makeComposedRequestBodySchema(Collection $schemas): Type + { + if ($schemas->count() === 1) { + return $schemas->first(); // @phpstan-ignore return.type + } + + return (new AllOf)->setItems($schemas->all()); + } + + /** + * @param Collection $schemalessResults + */ + protected function mergeSchemalessRulesResults(Collection $schemalessResults): ParametersExtractionResult + { + return new ParametersExtractionResult( + parameters: $this->convertDotNamedParamsToComplexStructures($schemalessResults->values()->flatMap->parameters->unique(fn ($p) => "$p->name.$p->in")->values()->all()), + ); + } + + /** + * @param Parameter[] $params + * @return Parameter[] + */ + protected function prepareQueryParams(array $params): array + { + return $this->unescapeEscapedDotNamedParameters( + $this->config->get('flatten_deep_query_parameters', true) + ? $this->convertDotNamedParamsToFlatQueryParams($params) + : $this->convertDotNamedParamsToComplexStructures($params) + ); + } + + /** + * @param Parameter[] $params + * @return Parameter[] + */ + protected function convertDotNamedParamsToComplexStructures(array $params): array + { + return (new DeepParametersMerger(collect($params)))->handle(); + } + + /** + * @param Parameter[] $params + * @return Parameter[] + */ + protected function convertDotNamedParamsToFlatQueryParams(array $params): array + { + return (new QueryParametersConverter(collect($params)))->handle(); + } + + /** + * @param Parameter[] $params + * @return Parameter[] + */ + protected function unescapeEscapedDotNamedParameters(array $params): array + { + return array_map(fn ($p) => tap($p, fn (Parameter $p) => $p->setName(Str::replace('\\.', '.', $p->name))), $params); + } + + /** + * @param Parameter[] $bodyParams + */ + protected function getMediaType(Operation $operation, RouteInfo $routeInfo, array $bodyParams): string + { + if ( + ($mediaTags = $routeInfo->phpDoc()->getTagsByName('@requestMediaType')) + && ($mediaType = trim(Arr::first($mediaTags)->value->value ?? null)) + ) { + return $mediaType; + } + + $jsonMediaType = 'application/json'; + + if ($operation->method === 'get') { + return $jsonMediaType; + } + + return $this->hasBinary($bodyParams) ? 'multipart/form-data' : $jsonMediaType; + } + + /** + * @param Parameter[] $bodyParams + */ + protected function hasBinary(array $bodyParams): bool + { + return collect($bodyParams)->contains(function (Parameter $parameter) { + // @todo: Use OpenApi document tree walker when ready + $parameterString = json_encode($parameter->toArray(), JSON_THROW_ON_ERROR); + + return Str::contains($parameterString, '"contentMediaType":"application\/octet-stream"'); + }); + } + + /** + * @return ParametersExtractionResult[] + */ + private function extractParameters(Operation $operation, RouteInfo $routeInfo): array + { + $result = []; + foreach ($this->config->parametersExtractors->all() as $extractorClass) { + /** @var ParameterExtractor $extractor */ + $extractor = ContainerUtils::makeContextable($extractorClass, [ + GeneratorConfig::class => $this->config, + TypeTransformer::class => $this->openApiTransformer, + Operation::class => $operation, + JsonApiQueryParameterFactory::class => new JsonApiQueryParameterFactory( + arraySerialization: $this->config->jsonApi->arraySerialization, + ), + ]); + + $result = $extractor->handle($routeInfo, $result); + } + + return $result; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RequestEssentialsExtension.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RequestEssentialsExtension.php new file mode 100644 index 00000000..87672e62 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RequestEssentialsExtension.php @@ -0,0 +1,272 @@ +className()))->replace('Controller', ''); + + if ($groupAttrsInstances = $this->getTagsAnnotatedByGroups($routeInfo)) { + $attributeInstance = $groupAttrsInstances[0]->newInstance(); + + $operation->setAttribute('groupWeight', $attributeInstance->weight); + + return array_filter([ + $attributeInstance->name ?: $defaultName, + ]); + } + + return array_values(array_unique(array_filter([ + ...$this->extractTagsForMethod($routeInfo), + $defaultName, + ]))); + } + + public function handle(Operation $operation, RouteInfo $routeInfo) + { + $this->attachTagsToOpenApi($routeInfo); + + $pathAliases = ReflectionRoute::createFromRoute($routeInfo->route)->getSignatureParametersMap(); + + $tagResolver = Scramble::$tagResolver ?? fn () => $this->getDefaultTags($operation, $routeInfo); + + $uriWithoutOptionalParams = Str::replace('?}', '}', $routeInfo->route->uri); + + $operation + ->setMethod($this->getOperationMethod($routeInfo)) + ->setPath(Str::replace( + collect($pathAliases)->keys()->map(fn ($k) => '{'.$k.'}')->all(), + collect($pathAliases)->values()->map(fn ($v) => '{'.$v.'}')->all(), + $uriWithoutOptionalParams, + )) + ->setTags($tagResolver($routeInfo, $operation)) + ->servers($this->getAlternativeServers($routeInfo->route)); + + if (count($routeInfo->phpDoc()->getTagsByName('@unauthenticated'))) { + $operation->security = []; + } + + $operation->setAttribute('operationId', $this->getOperationId($routeInfo)); + + $this->setTitleAndDescriptionFromEndpointAttribute($operation, $routeInfo); + } + + private function getOperationMethod(RouteInfo $routeInfo): string + { + $methods = array_map('strtolower', $routeInfo->route->methods()); + + if ($explicitMethod = $this->getEndpointAttributeInstance($routeInfo)?->method) { + $explicitMethod = strtolower($explicitMethod); + + if (in_array($explicitMethod, $methods)) { + return $explicitMethod; + } + } + + return $routeInfo->method; + } + + /** + * Checks if route domain needs to have alternative servers defined. Route needs to have alternative servers defined if + * the route has not matching domain to any servers in the root. + * + * Domain is matching if all the server variables matching. + */ + private function getAlternativeServers(Route $route) + { + if (! $route->getDomain()) { + return []; + } + + [$protocol] = explode('://', url('/')); + $expectedServer = (new ServerFactory($this->config->serverVariables->all())) + ->make($protocol.'://'.$route->getDomain().'/'.$this->config->apiPath()->serverPath()); + + if ($this->isServerMatchesAllGivenServers($expectedServer, $this->openApi->servers)) { + return []; + } + + $matchingServers = collect($this->openApi->servers)->filter(fn (Server $s) => $this->isMatchingServerUrls($expectedServer->url, $s->url)); + if ($matchingServers->count()) { + return $matchingServers->values()->toArray(); + } + + return [$expectedServer]; + } + + private function isServerMatchesAllGivenServers(Server $expectedServer, array $actualServers) + { + return collect($actualServers)->every(fn (Server $s) => $this->isMatchingServerUrls($expectedServer->url, $s->url)); + } + + private function isMatchingServerUrls(string $expectedUrl, string $actualUrl) + { + $mask = function (string $url) { + [, $urlPart] = explode('://', $url); + [$domain, $path] = count($parts = explode('/', $urlPart, 2)) !== 2 ? [$parts[0], ''] : $parts; + + /** @var Collection $params */ + $params = Str::of($domain)->matchAll('/\{(.*?)\}/'); + + return $params->join('.').'/'.$path; + }; + + return $mask($expectedUrl) === $mask($actualUrl); + } + + private function extractTagsForMethod(RouteInfo $routeInfo) + { + $classPhpDoc = $routeInfo->isClassBased() + ? $routeInfo->reflectionMethod()?->getDeclaringClass()->getDocComment() + : false; + + $classPhpDoc = $classPhpDoc ? PhpDoc::parse($classPhpDoc) : new PhpDocNode([]); + + if (! count($tagNodes = $classPhpDoc->getTagsByName('@tags'))) { + return []; + } + + return explode(',', array_values($tagNodes)[0]->value->value); + } + + private function getOperationId(RouteInfo $routeInfo) + { + $routeClassName = $routeInfo->className() ?: ''; + + return new UniqueNameOptions( + eloquent: (function () use ($routeInfo) { + // Manual operation ID setting. + // Check if Endpoint attribute is present with an `operationId` value + $operationId = $this->getEndpointAttributeInstance($routeInfo)?->operationId; + + if ($operationId) { + return $operationId; + } + + // Failing that, lets look for the annotation + if ( + ($operationId = $routeInfo->phpDoc()->getTagsByName('@operationId')) + && ($value = trim(Arr::first($operationId)?->value?->value)) + ) { + return $value; + } + + // Using route name as operation ID if set. We need to avoid using generated route names as this + // will result gibberish operation IDs when routes without names are cached. + if (($name = $routeInfo->route->getName()) && ! Str::contains($name, 'generated::')) { + return Str::startsWith($name, 'api.') ? Str::replaceFirst('api.', '', $name) : $name; + } + + // If no name and no operationId manually set, falling back to controller and method name (unique implementation). + return null; + })(), + unique: collect(explode('\\', Str::endsWith($routeClassName, 'Controller') ? Str::replaceLast('Controller', '', $routeClassName) : $routeClassName)) + ->filter() + ->push($routeInfo->methodName()) + ->map(function ($part) { + if ($part === Str::upper($part)) { + return Str::lower($part); + } + + return Str::camel($part); + }) + ->reject(fn ($p) => in_array(Str::lower($p), ['app', 'http', 'api', 'controllers', 'invoke'])) + ->values() + ->toArray(), + ); + } + + /** + * @return ReflectionAttribute[] + */ + private function getTagsAnnotatedByGroups(RouteInfo $routeInfo): array + { + $reflection = $routeInfo->reflectionAction(); + + $methodClassGroupAttributes = $reflection instanceof ReflectionMethod + ? $reflection->getDeclaringClass()->getAttributes(Group::class) + : []; + + return [ + ...($reflection?->getAttributes(Group::class) ?? []), + ...$methodClassGroupAttributes, + ]; + } + + private function attachTagsToOpenApi(RouteInfo $routeInfo): void + { + if (! $groups = $this->getTagsAnnotatedByGroups($routeInfo)) { + return; + } + + foreach ($groups as $group) { + $groupInstance = $group->newInstance(); + + if (! $groupInstance->name) { + continue; + } + + $this->openApiContext->groups->push($group); + } + } + + private function setTitleAndDescriptionFromEndpointAttribute(Operation $operation, RouteInfo $routeInfo): void + { + if (! $endpointAttribute = $this->getEndpointAttributeInstance($routeInfo)) { + return; + } + + if ($endpointAttribute->title) { + $operation->summary($endpointAttribute->title); + } + + if ($endpointAttribute->description) { + $operation->description($endpointAttribute->description); + } + } + + private function getEndpointAttributeInstance(RouteInfo $routeInfo): ?Endpoint + { + return ($routeInfo->reflectionAction()?->getAttributes(Endpoint::class)[0] ?? null) + ?->newInstance(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ResponseExtension.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ResponseExtension.php new file mode 100644 index 00000000..25b86375 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ResponseExtension.php @@ -0,0 +1,240 @@ +collectInferredResponses($routeInfo); + + $responses = $this->applyResponsesAttributes($inferredResponses, $routeInfo); + + foreach ($responses as $response) { + if (in_array($routeInfo->method, static::HTTP_METHODS_WITHOUT_RESPONSE_BODY)) { + $response = $this->removeContentFromResponse($response); + } + + $operation->addResponse($response); + } + } + + /** + * @return Collection + */ + private function collectInferredResponses(RouteInfo $routeInfo): Collection + { + $returnType = $routeInfo->getReturnType(); + + if (! $returnType) { + return collect(); + } + + $returnTypes = $returnType instanceof Union + ? $returnType->types + : [$returnType]; + + $responses = collect($returnTypes) + ->merge($routeInfo->getActionType()->exceptions ?? []) + ->map(function (Type $type) use ($routeInfo) { + /* + * Any inline comments on the entire response type that are not originating in the controller, + * should not leak to the resulting documentation. + */ + $docSource = $type->getAttribute('docNode')?->getAttribute('sourceClass'); + + if ($docSource && ($docSource !== $routeInfo->className())) { + $type->setAttribute('docNode', null); + } + + return $type; + }) + ->map($this->openApiTransformer->toResponse(...)) + ->filter() + ->unique(fn ($response) => ($response instanceof Response ? $response->code : 'ref').':'.json_encode($response->toArray())) + ->values(); + + [$responses, $references] = $responses->partition(fn ($r) => $r instanceof Response)->all(); + /** @var Collection $responses */ + /** @var Collection $references */ + + return $responses + ->groupBy('code') + ->map($this->mergeResponses(...)) + ->values() + ->concat($references) + ->values(); + } + + /** + * @param Collection $inferredResponses + * @return Collection + */ + private function applyResponsesAttributes(Collection $inferredResponses, RouteInfo $routeInfo): Collection + { + $responseAttributes = $routeInfo->reflectionAction()?->getAttributes(ResponseAttribute::class, ReflectionAttribute::IS_INSTANCEOF) ?: []; + + if (! count($responseAttributes)) { + return $inferredResponses; + } + + foreach ($responseAttributes as $responseAttribute) { + $responseAttributeInstance = $responseAttribute->newInstance(); + + $originalResponse = $inferredResponses + ->map(fn (Response|Reference $r): Response => $r instanceof Reference ? $r->resolve() : $r) + ->first(fn (Response $r) => $r->code === $responseAttributeInstance->status); + + $newResponse = ResponseAttribute::toOpenApiResponse( + $responseAttributeInstance, + $originalResponse, + $this->openApiTransformer, + ($fileName = $routeInfo->reflectionAction()?->getFileName()) + ? FileNameResolver::createForFile($fileName) + : null, + ); + + $responseHasChanged = ! $originalResponse + || json_encode($newResponse->toArray(), JSON_THROW_ON_ERROR) !== json_encode($originalResponse->toArray(), JSON_THROW_ON_ERROR); + + if (! $responseHasChanged) { + continue; + } + + $inferredResponses = $inferredResponses + ->map(function (Response|Reference $r) use ($responseAttributeInstance, $newResponse) { + $response = $r instanceof Reference ? $r->resolve() : $r; + + /** @var Response $response */ + + return $response->code === $responseAttributeInstance->status ? $newResponse : $r; + }); + + if (! $originalResponse) { + $inferredResponses->push($newResponse); + } + } + + return $inferredResponses; + } + + /** + * @param Collection $responses + */ + private function mergeResponses(Collection $responses): Response + { + if (count($responses) === 1) { + /** @var Response $response */ + $response = $responses->first(); + + return $response; + } + + return tap( + Response::make((int) $responses->first()?->code) + ->setDescription(trim($responses->map->description->join("\n\n"))) + ->setLinks($this->mergeLinks($responses)) + ->setHeaders($this->mergeHeaders($responses)), + fn (Response $r) => $this->addContentToResponse($r, $responses), + ); + } + + /** + * @param Collection $responses + * @return array + */ + private function mergeHeaders(Collection $responses): array + { + return array_merge(...$responses->map->headers); + } + + /** + * @param Collection $responses + * @return array + */ + private function mergeLinks(Collection $responses): array + { + return array_merge(...$responses->map->links); + } + + /** + * @param Collection $responses + * @return array + */ + private function mergeContent(Collection $responses): array + { + /** @var Collection> $contentCollections */ + $contentCollections = collect(); + + foreach ($responses as $r) { + foreach ($r->content as $typeName => $content) { + if (! $contentCollections->has($typeName)) { + $contentCollections->offsetSet($typeName, collect()); + } + $contentCollections->get($typeName)?->push($content); + } + } + + return $contentCollections + ->map(function (Collection $schemas) { + $types = $schemas + ->map(function (Schema|Reference $s) { + return $s instanceof Reference ? $s->resolve()->type : $s->type; + }) + /* + * Empty response body can happen, and in case it is going to be grouped + * by status, it should become an empty string. + */ + ->map(fn ($type) => $type ?: new OpenApiTypes\StringType) + ->unique(fn ($type) => json_encode($type->toArray())) + ->values() + ->all(); + + return Schema::fromType(count($types) > 1 ? (new AnyOf)->setItems($types) : $types[0]); + }) + ->all(); + } + + /** + * @param Collection $responses + */ + private function addContentToResponse(Response $r, Collection $responses): void + { + $mergedContentTypes = $this->mergeContent($responses); + + foreach ($mergedContentTypes as $type => $mergedContent) { + $r->setContent($type, $mergedContent); + } + } + + private function removeContentFromResponse(Response|Reference $response): Response + { + if ($response instanceof Reference) { + $response = clone $response->resolve(); + } + + /** @var Response $response */ + $response->content = []; + + return $response; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/ResponseHeadersExtension.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/ResponseHeadersExtension.php new file mode 100644 index 00000000..47950b47 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/ResponseHeadersExtension.php @@ -0,0 +1,78 @@ +reflectionAction()) { + return; + } + + $headerAttributesInstances = array_map( + fn (ReflectionAttribute $attribute) => $attribute->newInstance(), + $reflectionMethod->getAttributes(HeaderAttribute::class, ReflectionAttribute::IS_INSTANCEOF), + ); + + if (! $headerAttributesInstances || ! $operation->responses) { + return; + } + + foreach ($operation->responses as $i => $response) { + if (! $responseStatusCode = $this->getResponseStatusCode($response)) { + continue; + } + + $applicableHeaders = array_filter( + $headerAttributesInstances, + fn (HeaderAttribute $attribute) => $attribute->status === '*' || $attribute->status == $responseStatusCode + ); + + if (! count($applicableHeaders)) { + continue; + } + + if ($response instanceof Reference) { + $response = deep_copy($response->resolve()); + } + + if (! $response instanceof Response) { + continue; + } + + foreach ($applicableHeaders as $header) { + $response->addHeader( + $header->name, + HeaderAttribute::toOpenApiHeader($header, $this->openApiTransformer), + ); + } + + $operation->responses[$i] = $response; + } + } + + private function getResponseStatusCode(Response|Reference $response): ?string + { + if ($response instanceof Response) { + return (string) $response->code; + } + + $unreferencedResponse = $response->resolve(); + if ($unreferencedResponse instanceof Response) { + return (string) $unreferencedResponse->code; + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php new file mode 100644 index 00000000..fc2e8895 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php @@ -0,0 +1,49 @@ +classReflector->getMethod('rules'); + $rulesMethodNode = $rulesMethod->getAstNode(); + + /** @var Return_ $returnNodeStatement */ + $returnNodeStatement = (new NodeFinder)->findFirst( + $rulesMethodNode ? [$rulesMethodNode] : [], + fn ($node) => $node instanceof Return_ && $node->expr instanceof Array_ + ); + $returnNode = $returnNodeStatement?->expr ?? null; + + $evaluators = [ + new FormRequestRulesEvaluator($this->classReflector, $this->method), + new NodeRulesEvaluator($this->printer, $rulesMethodNode, $returnNode, $this->method, $this->classReflector->className, $rulesMethod->getFunctionLikeDefinition()->getScope()), + ]; + + $exceptions = []; + + foreach ($evaluators as $evaluator) { + try { + return $evaluator->handle(); + } catch (\Throwable $e) { + $exceptions[$evaluator::class] = $e; + } + } + + throw RulesEvaluationException::fromExceptions($exceptions); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ConstFetchEvaluator.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ConstFetchEvaluator.php new file mode 100644 index 00000000..d4ab5f1b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/ConstFetchEvaluator.php @@ -0,0 +1,45 @@ + $classMap + */ + public function __construct(public readonly array $classMap) {} + + public function evaluate(Node\Expr $expr, mixed $default = null): mixed + { + if (! $expr instanceof Node\Expr\ClassConstFetch) { + return $default; + } + + $className = $expr->class instanceof Node\Name + ? $expr->class->toString() + : null; + + $constName = $expr->name instanceof Node\Identifier + ? $expr->name->toString() + : null; + + if ($className && array_key_exists($className, $this->classMap)) { + $className = $this->classMap[$className]; + } + + if (! $className || ! $constName) { + return $default; + } + + if (! class_exists($className)) { + return $default; + } + + return match ($constName) { + 'class' => $className, + default => constant("$className::$constName"), + }; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/FormRequestRulesEvaluator.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/FormRequestRulesEvaluator.php new file mode 100644 index 00000000..0f066fa3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/FormRequestRulesEvaluator.php @@ -0,0 +1,43 @@ +rules($this->classReflector->className, $this->method); + } catch (Throwable $e) { + throw RulesEvaluationException::fromExceptions([self::class => $e]); + } + } + + protected function rules(string $requestClassName, string $method) + { + /** @var Request $request */ + $request = (new $requestClassName); + + $rules = []; + + if (method_exists($request, 'setMethod')) { + $request->setMethod(strtoupper($method)); + } + + if (method_exists($request, 'rules')) { + $rules = $request->rules(); + } + + return $rules; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php new file mode 100644 index 00000000..c1842e23 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php @@ -0,0 +1,267 @@ +rulesNode) { + return []; + } + + try { + return $this->rules(); + } catch (Throwable $e) { + throw RulesEvaluationException::fromExceptions([ + self::class => $this->lastEvaluationException ?? $e, + ]); + } + } + + /** + * @return array + */ + private function rules(): array + { + $vars = $this->evaluateDefinedVars(); + + $rules = $this->evaluateExpression($this->rulesNode, $vars) ?? []; + + foreach ($rules as &$item) { + if (is_string($item)) { + $item = trim($item, '|,'); + + continue; + } + + if (is_array($item)) { + $item = array_values(array_filter($item)); + } + } + + return $rules; // @phpstan-ignore return.type + } + + /** + * @return array + */ + private function evaluateDefinedVars(): array + { + $parameters = $this->evaluateParameters(); + + $variables = $this->evaluateVariables($parameters); + + return array_merge($variables, $parameters); + } + + /** + * @return array + */ + private function evaluateParameters(): array + { + return collect($this->functionLikeNode->getParams()) + ->filter(fn (Node\Param $param) => isset($param->type->name)) + ->filter(fn (Node\Param $param) => ! class_exists($className = (string) $param->type) || ! is_a($className, Request::class, true)) + ->filter(fn (Node\Param $param) => isset($param->var->name) && is_string($param->var->name)) + ->mapWithKeys(function (Node\Param $param) { + try { + $type = (string) $param->type; + $primitives = [ + 'int' => 1, + 'bool' => true, + 'string' => '', + 'float' => 1, + ]; + $value = $primitives[$type] ?? app($type); + + return [ + $param->var->name => $value, + ]; + } catch (Throwable $e) { + // @todo communicate warning + return [ + $param->var->name => new Optional(null), + ]; + } + }) + ->all(); + } + + /** + * @param array $predefinedVariables + * @return array + */ + private function evaluateVariables(array $predefinedVariables = []): array + { + $variables = $this->collectVariableNames(); + + if (empty($variables)) { + return []; + } + + return collect($this->functionLikeNode->getStmts()) + ->filter(fn (Stmt $stmt) => $stmt instanceof Stmt\Expression && $stmt->expr instanceof Assign) + ->filter(fn (Stmt $stmt) => isset($stmt->expr->var->name) && in_array($stmt->expr->var->name, $variables)) + ->reduce(fn (array $variables, Stmt $stmt) => [ + ...$variables, + $stmt->expr->var->name => $this->evaluateExpression($stmt->expr, array_merge($variables, $predefinedVariables)), // @phpstan-ignore property.notFound + ], []); + } + + /** + * @return string[] + */ + private function collectVariableNames(): array + { + return array_values(array_filter(array_map( + fn (Variable $var) => is_string($var->name) ? $var->name : null, + (new NodeFinder)->findInstanceOf($this->rulesNode ?: [], Variable::class), + ))); + } + + /** + * @param array $variables + */ + private function evaluateExpression(?Node\Expr $expression, array $variables): mixed + { + $result = $this->doEvaluateExpression($expression, $variables); + + /* + * If evaluation is successful, we reset the exception. + * + * Otherwise, this point won't be reached, and the specific evaluation exception will be used to + * communicate the error. + */ + $this->lastEvaluationException = null; + + return $result; + } + + /** + * @param array $variables + */ + private function doEvaluateExpression(?Node\Expr $expression, array $variables): mixed + { + if (! $expression) { + return null; + } + + return (new ConstExprEvaluator(function ($expr) use ($variables) { + $default = new stdClass; + + $evaluatedConstFetch = (new ConstFetchEvaluator([ + 'self' => $this->className, + 'static' => $this->className, + ]))->evaluate($expr, $default); + + if ($evaluatedConstFetch !== $default) { + return $evaluatedConstFetch; + } + + try { + return $this->evaluateWithScopedVariables($this->printer->prettyPrint([$expr]), [ + ...$variables, + 'request' => tap(request(), fn ($r) => $r->setMethod(strtoupper($this->method))), + 'this' => $this->tryCreatingCurrentClassInstance(), + ]); + } catch (Throwable $e) { + $this->lastEvaluationException = $e; + } + + /* + * In case something happened while evaluating expression, we don't want to return just null. + * It is important to preserve the base value type as much as possible in case the result of expression + * evaluation gets passed to `array_merge`. So in case `$code` should've returned array, we return empty array. + */ + $exprType = $this->getType($expr); + + return $exprType instanceof KeyedArrayType || $exprType instanceof ArrayType + ? [] + : null; + }))->evaluateDirectly($expression); + } + + /** + * @param array $variables + */ + private function evaluateWithScopedVariables(string $code, array $variables): mixed + { + $runner = static function ($__scramble_code, $__scramble_vars) { + $varsMap = []; + + foreach ($__scramble_vars as $varName => $variable) { + $varsMap['$'.$varName] = '$'.($newName = $varName.'__'); + ${$newName} = $variable; + } + + /** @var string $__scramble_code */ + $__scramble_code = Str::replace(array_keys($varsMap), array_values($varsMap), $__scramble_code); // @phpstan-ignore argument.templateType + + return eval("return $__scramble_code;"); + }; + + return $runner($code, $variables); + } + + private function tryCreatingCurrentClassInstance(): mixed + { + if (! $this->className) { + return null; + } + + $instance = null; + + try { + $instance = app($this->className); + } catch (\Throwable) { + try { + $instance = new $this->className; + } catch (\Throwable) { + // @todo communicate warning + } + } + + if (! $instance) { + return null; + } + + return new PublicProxy($instance); + } + + private function getType(Node\Expr $expr): Type + { + return ReferenceTypeResolver::getInstance()->resolve($this->scope, $this->scope->getType($expr)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/PublicProxy.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/PublicProxy.php new file mode 100644 index 00000000..b21207fa --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/PublicProxy.php @@ -0,0 +1,52 @@ +target = $target; + } + + /** + * @param array $arguments + */ + public function __call(string $name, array $arguments): mixed + { + $method = new \ReflectionMethod($this->target, $name); + $method->setAccessible(true); + + return $method->invokeArgs($this->target, $arguments); + } + + public function __get(string $name): mixed + { + $prop = new \ReflectionProperty($this->target, $name); + $prop->setAccessible(true); + + return $prop->getValue($this->target); + } + + /** + * @param mixed $value + */ + public function __set(string $name, $value): void + { + $prop = new \ReflectionProperty($this->target, $name); + $prop->setAccessible(true); + + $prop->setValue($this->target, $value); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/RulesEvaluator.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/RulesEvaluator.php new file mode 100644 index 00000000..a873f3cb --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesEvaluator/RulesEvaluator.php @@ -0,0 +1,15 @@ + + * + * @throws RulesEvaluationException + */ + public function handle(): array; +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/DeepParametersMerger.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/DeepParametersMerger.php new file mode 100644 index 00000000..af2204a9 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/DeepParametersMerger.php @@ -0,0 +1,243 @@ + $parameters + */ + public function __construct(private Collection $parameters) {} + + /** + * @return Parameter[] + */ + public function handle(): array + { + return $this->parameters->groupBy('in') // @phpstan-ignore return.type + ->map(fn ($parameters) => $this->handleNested($parameters->keyBy('name'))->values()) + ->flatten() // `flatten` type inference is not working hence ignore + ->values() + ->all(); + } + + /** + * @param Collection $parameters + * @return Collection + */ + private function handleNested(Collection $parameters): Collection + { + /** + * @var Collection $forcedFlatParameters + * @var Collection $maybeDeepParameters + */ + [$forcedFlatParameters, $maybeDeepParameters] = $parameters->partition(fn (Parameter $p) => $p->getAttribute('isFlat') === true); + + /** + * @var Collection $nested + * @var Collection $parameters + */ + [$nested, $parameters] = $maybeDeepParameters + ->sortBy(fn ($_, $key) => count(self::splitOnUnescapedDots($key))) + ->partition(fn ($_, $key) => (bool) preg_match(self::DOT_REGEX, $key)); + + $nestedParentsKeys = $nested->keys()->map(fn ($key) => self::splitOnUnescapedDots($key)[0]); + + /** + * @var Collection $nestedParents + * @var Collection $parameters + */ + [$nestedParents, $parameters] = $parameters->partition(fn ($_, $key) => $nestedParentsKeys->contains($key)); + + /** @var Collection $nested */ + $nested = $nested->merge($nestedParents); + + $nested = $nested + ->groupBy(fn ($_, $key) => self::splitOnUnescapedDots($key)[0]) + ->map(function (Collection $params, $groupName) { + $params = $params->keyBy('name'); + + $baseParam = $params->get( + $groupName, + Parameter::make($groupName, $params->first()->in) // @phpstan-ignore property.nonObject + ->setSchema(Schema::fromType(new ObjectType)) + ); + + $params->offsetUnset($groupName); + + foreach ($params as $param) { + $this->setDeepType( + $baseParam->schema->type, // @phpstan-ignore-line + $param->name, + $param, + ); + } + + $objectIsRequiredDueToNestedRequiredProperties = $baseParam->schema?->type instanceof ObjectType + && $params->some(fn (Parameter $p) => $p->required); + + return $baseParam->required( + $baseParam->required || $objectIsRequiredDueToNestedRequiredProperties + ); + }); + + return $parameters + ->merge($forcedFlatParameters) + ->merge($nested); + } + + private function setDeepType(Type &$base, string $key, Parameter $parameter): void + { + $typeToSet = $this->extractTypeFromParameter($parameter); + + $parts = self::splitOnUnescapedDots($key); + $containingType = $this->getOrCreateDeepTypeContainer( + $base, + $parts[0] === '*' + ? $parts + : collect($parts) + ->splice(1) + ->values() + ->all(), + ); + + $settingKey = collect($parts)->last(); + + if (! is_string($settingKey)) { + return; + } + + $isSettingArrayItems = $settingKey === '*'; + + if ($containingType === $base && $base instanceof UnknownType) { + $containingType = ($isSettingArrayItems ? new ArrayType : new ObjectType) + ->addProperties($base); + + $base = $containingType; + } + + if (! ($containingType instanceof ArrayType || $containingType instanceof ObjectType)) { + return; + } + + if ($isSettingArrayItems && $containingType instanceof ArrayType) { + $containingType->items = $typeToSet; + + return; + } + + if (! $isSettingArrayItems && $containingType instanceof ObjectType) { + $containingType + ->addProperty($settingKey, $typeToSet) + ->addRequired($parameter->required ? [$settingKey] : []); + } + + if ($isSettingArrayItems) { + $containingType->properties = collect($containingType->properties) + ->map(fn ($prop) => $prop instanceof UnknownType ? $typeToSet : $prop) + ->all(); + } + } + + /** + * @return list + */ + private static function splitOnUnescapedDots(string $key): array + { + $parts = preg_split(self::DOT_REGEX, $key); + + return $parts !== false ? $parts : [$key]; + } + + /** + * @param string[] $path + */ + private function getOrCreateDeepTypeContainer(Type &$base, array $path): Type + { + $key = $path[0]; + + if (count($path) === 1) { + if ($key !== '*' && $base instanceof ArrayType) { + $base = new ObjectType; + } + + return $base; + } + + if ($key === '*') { + if (! $base instanceof ArrayType) { + $base = new ArrayType; + } + + $next = $path[1]; + if ($next === '*') { + if (! $base->items instanceof ArrayType) { + $base->items = new ArrayType; + } + } else { + if (! $base->items instanceof ObjectType) { + $base->items = new ObjectType; + } + } + + return $this->getOrCreateDeepTypeContainer( + $base->items, + collect($path)->splice(1)->values()->all(), + ); + } else { + if (! $base instanceof ObjectType) { + $base = new ObjectType; + } + + $next = $path[1]; + + if (! $base->hasProperty($key)) { + $base = $base->addProperty( + $key, + $next === '*' ? new ArrayType : new ObjectType, + ); + } + if (($existingType = $base->getProperty($key)) instanceof UnknownType) { + $base = $base->addProperty( + $key, + ($next === '*' ? new ArrayType : new ObjectType)->addProperties($existingType), + ); + } + + if ($next === '*' && ! $existingType instanceof ArrayType) { + $base->addProperty($key, (new ArrayType)->addProperties($existingType)); + } + if ($next !== '*' && $existingType instanceof ArrayType) { + $base->addProperty($key, (new ObjectType)->addProperties($existingType)); + } + + return $this->getOrCreateDeepTypeContainer( + $base->properties[$key], + collect($path)->splice(1)->values()->all(), + ); + } + } + + private function extractTypeFromParameter(Parameter $parameter): Type + { + $paramType = $parameter->schema?->type; + if (! $paramType instanceof Type) { + throw new \Exception('Parameter type is required.'); + } + + $paramType->setDescription($parameter->description); + $paramType->example($parameter->example); + + return $paramType; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/GeneratesParametersFromRules.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/GeneratesParametersFromRules.php new file mode 100644 index 00000000..7fef4198 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/GeneratesParametersFromRules.php @@ -0,0 +1,21 @@ + $rules + * @param Node[]|RulesDocumentationRetriever $rulesDocsRetriever + * @return Parameter[] + */ + private function makeParameters($rules, TypeTransformer $typeTransformer, array|RulesDocumentationRetriever $rulesDocsRetriever = [], string $in = 'query'): array + { + return (new RulesToParameters($rules, $rulesDocsRetriever, $typeTransformer, $in))->mergeDotNotatedKeys(false)->handle(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/ParametersExtractionResult.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/ParametersExtractionResult.php new file mode 100644 index 00000000..227a4be0 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/ParametersExtractionResult.php @@ -0,0 +1,21 @@ +getVarTagValues())) { + $varTag = array_values($varTags)[0]; + + $type = $this->openApiTransformer + ->transform(PhpDocTypeHelper::toType($varTag->type)) + ->mergeAttributes($type->attributes()); + } + + $description = (string) Str::of($docNode->getAttribute('summary') ?: '') // @phpstan-ignore argument.type + ->append(' '.($docNode->getAttribute('description') ?: '')) // @phpstan-ignore binaryOp.invalid + ->trim(); + + if ($description) { + $type->setDescription($description); + } + + if ($examples = ExamplesExtractor::make($docNode)->extract(preferString: $type instanceof StringType)) { + $type->example($examples[0]); + } + + if ($default = ExamplesExtractor::make($docNode, '@default')->extract(preferString: $type instanceof StringType)) { + $type->default($default[0]); + } + + $deprecated = array_values($docNode->getTagsByName('@deprecated'))[0]->value ?? null; + if ($deprecated instanceof DeprecatedTagValueNode) { + $type->deprecated(true); + + if ($deprecated->description) { + $type->setDescription(implode(' ', array_filter([ + $type->description, + $deprecated->description, + ]))); + } + } + + if ($format = array_values($docNode->getTagsByName('@format'))[0]->value->value ?? null) { + $type->format($format); + } + + if ($docNode->getTagsByName('@query')) { + $type->setAttribute('isInQuery', true); + } + + return $type; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/QueryParametersConverter.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/QueryParametersConverter.php new file mode 100644 index 00000000..d55b85dd --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/QueryParametersConverter.php @@ -0,0 +1,123 @@ + $parameters + */ + public function __construct(private Collection $parameters) {} + + /** + * @return Parameter[] + */ + public function handle(): array + { + $paramsByName = $this->parameters->keyBy(fn ($p) => $p->name); + + /* + * Rejecting array "container" parameters for cases when there are properties specified. For example: + * ['filter' => 'array', 'filter.accountable' => 'integer'] + * In this ruleset `filter` should not be documented at all as the accountable is enough. + */ + $parameters = $this->parameters->reject(fn (Parameter $p) => $paramsByName->keys()->some(fn (string $key) => Str::startsWith($key, $p->name.'.'))); + + [$parametersToAdapt, $otherParameters] = $parameters->partition($this->shouldAdaptParameterForQuery(...))->all(); + + return $parametersToAdapt + ->map(fn ($p) => $this->convertParameterToQueryAdapted($p, $paramsByName)) + ->merge(array_map( + $this->maybeMarkDeepParameter(...), + $this->convertDotNamedParamsToComplexStructures($otherParameters), + )) + ->filter() + ->values() + ->all(); + } + + private function shouldAdaptParameterForQuery(Parameter $parameter): bool + { + if ($parameter->getAttribute('isFlat')) { + return false; + } + + $isScalar = ! in_array($parameter->schema->type->type ?? null, ['array', 'object', null], strict: true); + + $isArrayOfScalar = ($parameter->schema->type ?? null) instanceof ArrayType + && ! in_array($parameter->schema->type->items->type ?? null, ['array', 'object', null], strict: true); + + if (! Str::contains($parameter->name, '*')) { // no nested arrays + return $isScalar || $isArrayOfScalar; + } + + if (Str::endsWith($parameter->name, '*') && (Str::substrCount($parameter->name, '*') === 1)) { + return $isScalar; + } + + return false; + } + + /** + * @param Collection $paramsByName + */ + private function convertParameterToQueryAdapted(Parameter $originalParameter, Collection $paramsByName): ?Parameter + { + $parameter = clone $originalParameter; + + $parameter->name = Str::of($parameter->name) + ->split(DeepParametersMerger::DOT_REGEX) + ->map(fn ($str, $i) => $i === 0 ? $str : ($str === '*' ? '[]' : "[$str]")) + ->join(''); + + if ($parameter->schema?->type instanceof ArrayType) { + $parameter->name .= '[]'; + } + + if ( + $parameter->name !== $originalParameter->name + && ($sameNameParam = $paramsByName->get($parameter->name)) + && $sameNameParam !== $originalParameter + ) { + return null; + } + + if (Str::endsWith($parameter->name, '[]') && $parameter->schema && ! $parameter->schema->type instanceof ArrayType) { + $parameter->schema->type = (new ArrayType) + ->setItems($parameter->schema->type) + ->addProperties($parameter->schema->type); + } + + return $parameter; + } + + private function maybeMarkDeepParameter(Parameter $parameter): Parameter + { + // if parameter has serialization attributes set (style or explode), we do not mark parameter with extension property + if ($parameter->explode !== null || $parameter->style !== null) { + return $parameter; + } + + $isDeep = in_array($parameter->schema?->type->type, ['array', 'object', null], strict: true); + + if ($isDeep) { + $parameter->setExtensionProperty('deepObject-style', 'qs'); + } + + return $parameter; + } + + /** + * @param Collection $params + * @return Parameter[] + */ + private function convertDotNamedParamsToComplexStructures(Collection $params): array + { + return (new DeepParametersMerger($params))->handle(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php new file mode 100644 index 00000000..2c9b9e9b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php @@ -0,0 +1,246 @@ +addProperties($prevType); + } + + public function bool(Type $prevType) + { + return (new BooleanType)->addProperties($prevType); + } + + public function boolean(Type $_) + { + return $this->bool($_); + } + + public function numeric(Type $prevType) + { + return (new NumberType)->addProperties($prevType); + } + + public function decimal(Type $prevType) + { + return (new NumberType)->addProperties($prevType); + } + + public function int(Type $prevType) + { + return (new IntegerType)->addProperties($prevType); + } + + public function integer(Type $_) + { + return $this->int($_); + } + + public function array(Type $_, $params) + { + if (count($params)) { + $object = (new \Dedoc\Scramble\Support\Generator\Types\ObjectType) + ->setRequired($params); + + foreach ($params as $param) { + $object->addProperty($param, new UnknownType); + } + + return $object; + } + + return new ArrayType; + } + + public function email(Type $type) + { + if ($type instanceof UnknownType) { + $type = $this->string($type); + } + + return $type->format('email'); + } + + public function uuid(Type $type) + { + if ($type instanceof UnknownType) { + $type = $this->string($type); + } + + return $type->format('uuid'); + } + + public function nullable(Type $type) + { + return $type->nullable(true); + } + + public function present(Type $type): Type + { + $type->setAttribute('required', true); + + return $type; + } + + public function required(Type $type) + { + $type->setAttribute('required', true); + + if ($type instanceof ArrayType && $type->minItems === null) { + $type->setMin(1); + } + + return $type; + } + + public function min(Type $type, $params) + { + if ( + $type instanceof NumberType + || $type instanceof ArrayType + || $type instanceof StringType + ) { + $type->setMin((float) $params[0]); + } + + return $type; + } + + public function max(Type $type, $params) + { + if ( + $type instanceof NumberType + || $type instanceof ArrayType + || $type instanceof StringType + ) { + $type->setMax((float) $params[0]); + } + + return $type; + } + + public function size(Type $type, $params) + { + $type = $this->min($type, $params); + + return $this->max($type, $params); + } + + /** + * @param array $params + */ + public function between(Type $type, array $params): Type + { + if (count($params) !== 2) { + return $type; + } + + $type = $this->min($type, [$params[0]]); + + return $this->max($type, [$params[1]]); + } + + public function image(Type $type) + { + return $this->file($type); + } + + public function file(Type $type) + { + if ($type instanceof UnknownType) { + $type = $this->string($type); + } + + return $type->contentMediaType('application/octet-stream')->format('binary'); + } + + public function url(Type $type) + { + if ($type instanceof UnknownType) { + $type = $this->string($type); + } + + return $type->format('uri'); + } + + public function date(Type $type, $params) + { + if ($type instanceof UnknownType) { + $type = $this->string($type); + } + + return match ($params[0] ?? 'Y-m-d H:i:s') { + 'Y-m-d' => $type->format('date'), + 'Y-m-d H:i:s' => $type->format('date-time'), + default => $type, + }; + } + + public function date_format(Type $type, $params) + { + return $this->date($type, $params); + } + + public function conditionalRules(Type $type, ConditionalRules $rule, RuleTransformerContext $context): Type + { + $ifRules = $rule->rules(); + $elseRules = $rule->defaultRules(); + + $rules = [$ifRules, $elseRules]; + + $types = $type instanceof AnyOf + ? array_map(fn (Type $t) => (clone $t)->addProperties($type), $type->items) + : [$type]; + + $newTypes = []; + foreach ($rules as $conditionRules) { + foreach ($types as $type) { + $newTypes[] = $newT = $this->rulesToSchemaTransformer->transform($conditionRules, clone $type, $context); + if (! $conditionRules) { + $newT->setAttribute('isEmptyRules', true); + } + } + } + + $isRequired = collect($newTypes)->every(fn (Type $t) => (bool) $t->getAttribute('required', false)); + + $newTypes = array_values(array_filter($newTypes, fn (Type $t) => ! $t->getAttribute('isEmptyRules'))); + + if (count($newTypes) === 1) { + if ($isRequired !== $newTypes[0]->getAttribute('required')) { + $newTypes[0]->setAttribute('required', $isRequired); + } + + return $newTypes[0]; + } + + $anyOf = (new AnyOf)->setItems($newTypes); + + if ($isRequired) { + $anyOf->setAttribute('required', true); + } + + return $anyOf; + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesToParameters.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesToParameters.php new file mode 100644 index 00000000..54182ff8 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/RulesToParameters.php @@ -0,0 +1,142 @@ + */ + private array $rulesDocs; + + /** + * @param array $rules + * @param Node[]|RulesDocumentationRetriever $validationNodesResults + */ + public function __construct( + private array $rules, + array|RulesDocumentationRetriever $validationNodesResults, + private TypeTransformer $openApiTransformer, + private string $in = 'query', + ) { + // This is for backward compatibility + $this->rulesDocs = is_array($validationNodesResults) + ? RulesNodes::makeFromStatements($validationNodesResults)->getDocNodes() + : $validationNodesResults->getDocNodes(); + } + + public function mergeDotNotatedKeys(bool $mergeDotNotatedKeys = true): self + { + $this->mergeDotNotatedKeys = $mergeDotNotatedKeys; + + return $this; + } + + /** @return Parameter[] */ + public function handle(): array + { + return $this->transformSchemaBagToParameters($this->toSchemaBag()); + } + + public function toSchemaBag(): SchemaBag + { + $schemaBag = $this->createSchemaBag(); + + $this->applySchemaBagTransformingExtensions($schemaBag); + + return $schemaBag; + } + + private function createSchemaBag(): SchemaBag + { + $bag = new SchemaBag; + + foreach ($this->rules as $name => $ruleSet) { + $schema = $this->makeRuleSetToSchemaTransformer()->transform( + $ruleSet, + context: $this->makeRuleTransformerContext($name, $ruleSet), + ); + + $bag->set($name, $schema); + } + + return $bag; + } + + private function applySchemaBagTransformingExtensions(SchemaBag $schemaBag): void + { + $extensions = $this->getGeneratorConfig()->ruleTransformers + ->instances(AllRulesSchemasTransformer::class, [ + TypeTransformer::class => $this->openApiTransformer, + RuleSetToSchemaTransformer::class => $this->makeRuleSetToSchemaTransformer(), + ]); + + foreach ($this->rules as $name => $ruleSet) { + $rules = RuleSetToSchemaTransformer::normalizeAndPrioritizeRules($ruleSet); + + foreach ($rules as $rule) { + $normalizedRule = NormalizedRule::fromValue($rule); + + $extensions + ->filter(fn (AllRulesSchemasTransformer $bagTransformer) => $bagTransformer->shouldHandle($normalizedRule)) + ->each(fn (AllRulesSchemasTransformer $bagTransformer) => $bagTransformer->transformAll( + $schemaBag, + $normalizedRule, + $this->makeRuleTransformerContext($name, $ruleSet), + )); + } + } + } + + private function getGeneratorConfig(): GeneratorConfig + { + return $this->openApiTransformer->context->config; + } + + /** @return array */ + private function transformSchemaBagToParameters(SchemaBag $schemaBag): array + { + return (new SchemaBagToParametersTransformer( + $this->openApiTransformer, + $this->mergeDotNotatedKeys, + $this->rulesDocs, + $this->in, + ))->handle($schemaBag); + } + + /** + * @param RuleSet $rules + */ + private function makeRuleTransformerContext(string $name, mixed $rules): RuleTransformerContext + { + return RuleTransformerContext::makeFromOpenApiContext($this->openApiTransformer->context, [ + 'field' => $name, + 'fieldRules' => collect(Arr::wrap($rules)), + ]); + } + + public function makeRuleSetToSchemaTransformer(): RuleSetToSchemaTransformer + { + return new RuleSetToSchemaTransformer( + $this->openApiTransformer, + $this->openApiTransformer->context->config->ruleTransformers, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/SchemaBagToParametersTransformer.php b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/SchemaBagToParametersTransformer.php new file mode 100644 index 00000000..4ea98a30 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/OperationExtensions/RulesExtractor/SchemaBagToParametersTransformer.php @@ -0,0 +1,71 @@ + */ + private array $rulesDocs = [], + private string $in = 'query', + ) {} + + /** @return Parameter[] */ + public function handle(SchemaBag $schemaBag): array + { + return $this->transformSchemaBagToParameters($schemaBag); + } + + /** @return array */ + private function transformSchemaBagToParameters(SchemaBag $schemaBag): array + { + return collect($schemaBag->all()) + ->reject(fn ($_, $name) => $this->shouldIgnoreParameter($name)) + ->map(function ($schema, $name) { + if (! $rulesDocs = $this->rulesDocs[$name] ?? null) { + return $schema; + } + + return (new PhpDocSchemaTransformer($this->openApiTransformer))->transform($schema, $rulesDocs); + }) + ->map(fn ($schema, $name) => $this->makeParameterFromSchema($schema, $name)) + ->values() + ->pipe(fn ($c) => $this->mergeDotNotatedKeys ? collect((new DeepParametersMerger($c))->handle()) : $c) + ->all(); + } + + protected function shouldIgnoreParameter(string $name): bool + { + $rulesDocs = $this->rulesDocs[$name] ?? null; + + return (bool) ($rulesDocs?->getTagsByName('@ignoreParam') ?? []) + || (bool) ($rulesDocs?->getTagsByName('@hidden') ?? []); + } + + protected function makeParameterFromSchema(OpenApiSchema $schema, string $name): Parameter + { + $description = $schema->description; + $example = $schema->example; + + $schema->setDescription('')->example(new MissingValue); + + return Parameter::make($name, $schema->getAttribute('isInQuery') ? 'query' : $this->in) + ->setSchema(Schema::fromType($schema)) + ->example($example) + ->required((bool) $schema->getAttribute('required', false)) + ->description($description); + } +} diff --git a/vendor/dedoc/scramble/src/Support/PhpDoc.php b/vendor/dedoc/scramble/src/Support/PhpDoc.php new file mode 100644 index 00000000..27b5c20f --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/PhpDoc.php @@ -0,0 +1,88 @@ + true, 'indexes' => true]); + $lexer = new Lexer($config); + $constExprParser = new ConstExprParser($config); + $typeParser = new TypeParser($config, $constExprParser); + $phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser); + + return [$lexer, $phpDocParser, $typeParser]; + } + + $lexer = new Lexer; + $constExprParser = new ConstExprParser; + $typeParser = new TypeParser($constExprParser); + + return [$lexer, new PhpDocParser($typeParser, $constExprParser), $typeParser]; + } + + public static function parse(string $docComment, ?FileNameResolver $nameResolver = null): PhpDocNode + { + $docComment = Str::replace(['@body'], '@var', $docComment); + + [$lexer, $phpDocParser] = static::getTokenizerAndParser(); + + $tokens = new TokenIterator($lexer->tokenize($docComment)); + + /** @var PhpDocNode $node */ + $node = $phpDocParser->parse($tokens); + + static::addSummaryAttributes($node); + + if ($nameResolver) { + $tagValues = [ + ...$node->getParamTagValues(), + ...$node->getMixinTagValues(), + ...$node->getExtendsTagValues(), + ...$node->getUsesTagValues(), + ...$node->getReturnTagValues(), + ...$node->getReturnTagValues('@response'), + ...$node->getReturnTagValues('@scramble-return'), + ...$node->getVarTagValues(), + ...$node->getThrowsTagValues(), + ]; + + foreach ($tagValues as $tagValue) { + PhpDocTypeWalker::traverse($tagValue->type, [ + new ResolveFqnPhpDocTypeVisitor($nameResolver), + ]); + } + } + + return $node; + } + + public static function addSummaryAttributes(PhpDocNode $phpDoc) + { + $text = collect($phpDoc->children) + ->filter(fn ($v) => $v instanceof PhpDocTextNode) + ->map(fn (PhpDocTextNode $n) => $n->text) + ->implode("\n"); + + $text = Str::of($text) + ->trim() + ->explode("\n\n", 2); + + $phpDoc->setAttribute('summary', trim($text[0] ?? '')); + $phpDoc->setAttribute('description', trim($text[1] ?? '')); + } +} diff --git a/vendor/dedoc/scramble/src/Support/ResponseExtractor/ModelInfo.php b/vendor/dedoc/scramble/src/Support/ResponseExtractor/ModelInfo.php new file mode 100644 index 00000000..75e3e628 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/ResponseExtractor/ModelInfo.php @@ -0,0 +1,309 @@ +qualifyModel($this->class); + + $reflectionClass = new ReflectionClass($class); + if (! $reflectionClass->isInstantiable()) { + return collect([ + 'instance' => null, + 'class' => $class, + 'attributes' => collect(), + 'relations' => collect(), + ]); + } + + /** @var Model $model */ + $model = app()->make($class); + + return $this->displayJson( + $model, + $class, + $this->getAttributes($model), + $this->getRelations($model), + ); + } + + /** + * Get the column attributes for the given model. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @return \Illuminate\Support\Collection + */ + protected function getAttributes($model) + { + $connection = $model->getConnection(); + $schema = $connection->getSchemaBuilder(); + $table = $model->getTable(); + $columns = $schema->getColumns($table); + $indexes = $schema->getIndexes($table); + + return collect($columns) + ->values() + ->map(fn ($column) => [ + 'driver' => $connection->getDriverName(), + 'name' => $column['name'], + 'type' => $column['type'], + 'increments' => $column['auto_increment'], + 'nullable' => $column['nullable'], + 'default' => $this->getColumnDefault($column, $model), + 'unique' => $this->columnIsUnique($column['name'], $indexes), + 'fillable' => $model->isFillable($column['name']), + 'appended' => null, + 'hidden' => $this->attributeIsHidden($column['name'], $model), + 'cast' => $this->getCastType($column['name'], $model), + ]) + ->merge($this->getVirtualAttributes($model, $columns)) + ->keyBy('name'); + } + + private function getColumnDefault($column, Model $model) + { + $attributeDefault = $model->getAttributes()[$column['name']] ?? null; + + return match (true) { + $attributeDefault instanceof BackedEnum => $attributeDefault->value, + $attributeDefault instanceof UnitEnum => $attributeDefault->name, + default => $attributeDefault ?? $column['default'], + }; + } + + private function columnIsUnique($column, array $indexes) + { + return collect($indexes)->contains( + fn ($index) => count($index['columns']) === 1 && $index['columns'][0] === $column && $index['unique'] + ); + } + + /** + * Get the virtual (non-column) attributes for the given model. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param array[] $columns + * @return \Illuminate\Support\Collection + */ + protected function getVirtualAttributes($model, $columns) + { + $class = new ReflectionClass($model); + + $keyedColumns = collect($columns)->keyBy('name'); + + return collect($class->getMethods()) + ->reject( + fn (ReflectionMethod $method) => $method->isStatic() + || $method->isAbstract() + || $method->getDeclaringClass()->getName() !== get_class($model) + ) + ->mapWithKeys(function (ReflectionMethod $method) use ($model) { + if (preg_match('/^get(.*)Attribute$/', $method->getName(), $matches) === 1) { + return [Str::snake($matches[1]) => 'accessor']; + } elseif ($model->hasAttributeMutator($method->getName())) { + return [Str::snake($method->getName()) => 'attribute']; + } else { + return []; + } + }) + ->reject(fn ($cast, $name) => $keyedColumns->has($name)) + ->map(fn ($cast, $name) => [ + 'driver' => null, + 'name' => $name, + 'type' => null, + 'increments' => false, + 'nullable' => null, + 'default' => null, + 'unique' => null, + 'fillable' => $model->isFillable($name), + 'hidden' => $this->attributeIsHidden($name, $model), + 'appended' => $model->hasAppended($name), + 'cast' => $cast, + ]) + ->values(); + } + + /** + * Get the relations from the given model. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @return \Illuminate\Support\Collection + */ + protected function getRelations($model) + { + return collect(get_class_methods($model)) + ->map(fn ($method) => new ReflectionMethod($model, $method)) + ->reject( + fn (ReflectionMethod $method) => $method->isStatic() + || $method->isAbstract() + || $method->getDeclaringClass()->getName() === Model::class, + ) + ->filter(function (ReflectionMethod $method) { + $file = new SplFileObject($method->getFileName()); + $file->seek($method->getStartLine() - 1); + $code = ''; + while ($file->key() < $method->getEndLine()) { + $code .= $file->current(); + $file->next(); + } + + return collect($this->relationMethods) + ->contains(fn ($relationMethod) => str_contains($code, '$this->'.$relationMethod.'(')); + }) + ->map(function (ReflectionMethod $method) use ($model) { + try { + $relation = $method->invoke($model); + } catch (\Throwable $e) { + // @todo: add verbosity levels for debugging + return null; + } + + if (! $relation instanceof Relation) { + return null; + } + + return [ + 'name' => $method->getName(), + 'type' => Str::afterLast(get_class($relation), '\\'), + 'related' => get_class($relation->getRelated()), + ]; + }) + ->filter() + ->values() + ->keyBy('name'); + } + + /** + * Render the model information as JSON. + */ + protected function displayJson($model, $class, $attributes, $relations) + { + return collect([ + 'instance' => $model, + 'class' => $class, + 'attributes' => $attributes, + 'relations' => $relations, + ]); + } + + /** + * Get the cast type for the given column. + * + * @param string $column + * @param \Illuminate\Database\Eloquent\Model $model + * @return string|null + */ + protected function getCastType($column, $model) + { + if ($model->hasGetMutator($column) || $model->hasSetMutator($column)) { + return 'accessor'; + } + + if ($model->hasAttributeMutator($column)) { + return 'attribute'; + } + + return $this->getCastsWithDates($model)->get($column) ?? null; + } + + /** + * Get the model casts, including any date casts. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @return \Illuminate\Support\Collection + */ + protected function getCastsWithDates($model) + { + return collect($model->getDates()) + ->filter() + ->flip() + ->map(fn () => 'datetime') + ->merge($model->getCasts()); + } + + /** + * Determine if the given attribute is hidden. + * + * @param string $attribute + * @param \Illuminate\Database\Eloquent\Model $model + * @return bool + */ + protected function attributeIsHidden($attribute, $model) + { + if (count($model->getHidden()) > 0) { + return in_array($attribute, $model->getHidden()); + } + + if (count($model->getVisible()) > 0) { + return ! in_array($attribute, $model->getVisible()); + } + + return false; + } + + /** + * Qualify the given model class base name. + * + * @return string + * + * @see \Illuminate\Console\GeneratorCommand + */ + protected function qualifyModel(string $model) + { + if (class_exists($model)) { + return $model; + } + + if (str_contains($model, '\\') && class_exists($model)) { + return $model; + } + + $model = ltrim($model, '\\/'); + + $model = str_replace('/', '\\', $model); + + $rootNamespace = app()->getNamespace(); + + if (Str::startsWith($model, $rootNamespace)) { + return $model; + } + + return is_dir(app_path('Models')) + ? $rootNamespace.'Models\\'.$model + : $rootNamespace.$model; + } +} diff --git a/vendor/dedoc/scramble/src/Support/RouteInfo.php b/vendor/dedoc/scramble/src/Support/RouteInfo.php new file mode 100644 index 00000000..f016f05c --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/RouteInfo.php @@ -0,0 +1,227 @@ +> */ + public readonly Bag $requestParametersFromCalls; + + public readonly Infer\Extensions\IndexBuildingBroker $indexBuildingBroker; + + public function __construct( + public readonly Route $route, + public readonly string $method, + ) { + /** @var Bag> $bag */ + $bag = new Bag; + $this->requestParametersFromCalls = $bag; + $this->indexBuildingBroker = app(Infer\Extensions\IndexBuildingBroker::class); + } + + public function isClassBased(): bool + { + return is_string($this->route->getAction('uses')); + } + + public function className(): ?string + { + return $this->isClassBased() + ? ltrim(explode('@', $this->route->getAction('uses'))[0], '\\') + : null; + } + + public function methodName(): ?string + { + return $this->isClassBased() + ? explode('@', $this->route->getAction('uses'))[1] + : null; + } + + public function phpDoc(): PhpDocNode + { + if ($this->phpDoc) { + return $this->phpDoc; + } + + if (! $this->actionNode()) { + return new PhpDocNode([]); + } + + return $this->phpDoc = $this->actionNode()->getAttribute('parsedPhpDoc') ?: new PhpDocNode([]); // @phpstan-ignore return.type + } + + public function setPhpDoc(PhpDocNode $node): self + { + $this->phpDoc = $node; + + return $this; + } + + /** + * @deprecated use `actionNode` instead + */ + public function methodNode(): ?ClassMethod + { + if ($this->methodNode || ! $this->isClassBased() || ! $this->reflectionMethod()) { + return $this->methodNode; + } + + $methodNode = $this->getActionReflector()->getAstNode(); + if (! $methodNode instanceof ClassMethod) { + throw new LogicException('ClassMethod node expected from method reflector'); + } + + return $this->methodNode = $methodNode; + } + + protected function closureNode(): ?FunctionLike + { + if ($this->actionNode || $this->isClassBased()) { + return $this->actionNode; + } + + return $this->actionNode = $this->getActionReflector()->getAstNode(); + } + + public function actionNode(): ?FunctionLike + { + return $this->isClassBased() ? $this->methodNode() : $this->closureNode(); // @phpstan-ignore method.deprecated + } + + public function reflectionAction(): ReflectionMethod|ReflectionClosure|null + { + return $this->isClassBased() ? $this->reflectionMethod() : $this->reflectionClosure(); + } + + public function reflectionClosure(): ?ReflectionClosure + { + if ($this->isClassBased()) { + return null; + } + + $uses = $this->route->getAction('uses'); + + if (! $uses instanceof Closure) { + return null; + } + + if (! class_exists(ReflectionClosure::class)) { + return null; + } + + return new ReflectionClosure($uses); + } + + public function reflectionMethod(): ?ReflectionMethod + { + if (! $this->isClassBased()) { + return null; + } + + if (! method_exists($this->className(), $this->methodName())) { + return null; + } + + return (new ReflectionClass($this->className())) + ->getMethod($this->methodName()); + } + + public function getReturnType() + { + return (new RouteResponseTypeRetriever($this))->getResponseType(); + } + + public function getActionReflector(): MethodReflector|ClosureReflector + { + if ($this->isClassBased()) { + return MethodReflector::make(...explode('@', $this->route->getAction('uses'))); + } + + if ($this->route->getAction('uses') instanceof Closure) { + return ClosureReflector::make($this->route->getAction('uses')); + } + + throw new LogicException('Cannot determine the action reflector'); + } + + public function getActionDefinition(): ?Infer\Definition\FunctionLikeDefinition + { + if ($this->actionDefinition) { + return $this->actionDefinition; + } + + $scopeCollector = new ScopeCollector; + + $this->actionDefinition = $this->getActionReflector()->getFunctionLikeDefinition( + indexBuilders: [ + new RequestParametersBuilder($this->requestParametersFromCalls), + $scopeCollector, + ...$this->indexBuildingBroker->indexBuilders, + ], + withSideEffects: true, + ); + + $this->scope = $scopeCollector->getScope($this->actionDefinition); + + return $this->actionDefinition; + } + + public function getActionType(): ?FunctionType + { + return $this->getActionDefinition()?->type; + } + + /** + * @deprecated use `getActionType` + * + * @todo Maybe better name is needed as this method performs method analysis, indexes building, etc. + */ + public function getMethodType(): ?FunctionType + { + return $this->getActionType(); + } + + /** @internal */ + public function getScope(): Infer\Scope\Scope + { + $this->getActionDefinition(); + + if (! $this->scope) { + throw new RuntimeException('Scope is not initialized for route.'); + } + + return $this->scope; + } +} diff --git a/vendor/dedoc/scramble/src/Support/RouteResponseTypeRetriever.php b/vendor/dedoc/scramble/src/Support/RouteResponseTypeRetriever.php new file mode 100644 index 00000000..a34455ef --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/RouteResponseTypeRetriever.php @@ -0,0 +1,201 @@ +routeInfo->getActionType()) { + return null; + } + + if ($manuallyDefinedType = $this->getManuallyDefinedType()) { + return $manuallyDefinedType; + } + + return $this->getInferredType(); + } + + private function getManuallyDefinedType(): ?Type + { + if ($annotatedBodyType = $this->getAnnotatedBodyType()) { + return $annotatedBodyType; + } + + if ($phpDocType = $this->getMethodPhpDocReturnType()) { + return $phpDocType; + } + + return null; + } + + private function getAnnotatedBodyType(): ?Type + { + $definition = $this->routeInfo->getActionDefinition(); + + if (! $definition instanceof FunctionLikeAstDefinition) { + return null; + } + + $inferredTypeAttribute = $definition->getInferredReturnType(); + + $types = $inferredTypeAttribute instanceof Union + ? $inferredTypeAttribute->types + : [$inferredTypeAttribute]; + + $someReturnTypeHasBodyAnnotation = collect($types)->some(function (Type $type) { + /** @var PhpDocNode $docNode */ + if (! $docNode = $type->getAttribute('docNode')) { + return false; + } + + return (bool) ($docNode->getVarTagValues()[0]->type ?? null); + }); + + if ($someReturnTypeHasBodyAnnotation) { + return $inferredTypeAttribute; + } + + return null; + } + + private function getInferredType(): ?Type + { + if ($this->routeInfo->isClassBased()) { + if (! $methodType = $this->routeInfo->getActionType()) { + return null; + } + + return (new ObjectType($this->routeInfo->className())) + ->getMethodReturnType($methodType->name); + } + + if (! $definition = $this->routeInfo->getActionDefinition()) { + return null; + } + + return ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new CallableCallReferenceType($definition->type, []) + ); + } + + private function getMethodPhpDocReturnType(): ?Type + { + if (! $phpDocReturnNode = $this->getDocReturnNode()) { + return null; + } + + $source = $phpDocReturnNode->getAttribute('source'); + + $phpDocReturnType = PhpDocTypeHelper::toType($phpDocReturnNode); + + if ($source === 'response') { + return $phpDocReturnType; + } + + /* + * The code below is used to add a backward compatibility for @return annotation support. + */ + $inferredReturnType = $this->getInferredType(); + + if (! $inferredReturnType || $inferredReturnType instanceof UnknownType) { + return $phpDocReturnType; + } + + if ( + $phpDocReturnType instanceof Generic + && (new TypeWalker)->first($phpDocReturnType, fn (Type $t) => $t->isInstanceOf(JsonResource::class) || $t->isInstanceOf(Model::class)) + ) { + return $phpDocReturnType; + } + + $phpDocReturnTypeWeight = $phpDocReturnType ? $this->countKnownTypes($phpDocReturnType) : 0; + $inferredReturnTypeWeight = $this->countKnownTypes($inferredReturnType); + if ($phpDocReturnTypeWeight > $inferredReturnTypeWeight) { + return $phpDocReturnType; + } + + return null; + } + + private function getDocReturnNode(): ?TypeNode + { + if (! $this->routeInfo->phpDoc()) { + return null; + } + + if (($responseType = $this->routeInfo->phpDoc()->getReturnTagValues('@response')[0] ?? null) && optional($responseType)->type) { + $responseType->type->setAttribute('source', 'response'); + + return $responseType->type; + } + + if (($returnType = $this->routeInfo->phpDoc()->getReturnTagValues()[0] ?? null) && optional($returnType)->type) { + return $returnType->type; + } + + return null; + } + + private function countKnownTypes(Type $type): int + { + $counterVisitor = new class extends AbstractTypeVisitor + { + public int $count = 0; + + public function leave(Type $type): ?Type + { + if ( + $type instanceof ObjectType + || $type instanceof StringType + || $type instanceof IntegerType + || $type instanceof FloatType + || $type instanceof BooleanType + || $type instanceof NullType + /* + * Give some weight for keyed array item so when comparing `array` to `array{foo: unknown}`, + * the keyed array is preferred. + */ + || $type instanceof ArrayItemType_ && is_string($type->key) + ) { + $this->count++; + } + + return null; + } + }; + + (new TypeTraverser([$counterVisitor]))->traverse($type); + + return $counterVisitor->count; + } +} diff --git a/vendor/dedoc/scramble/src/Support/RuleTransforming/NormalizedRule.php b/vendor/dedoc/scramble/src/Support/RuleTransforming/NormalizedRule.php new file mode 100644 index 00000000..3c5f93f9 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/RuleTransforming/NormalizedRule.php @@ -0,0 +1,76 @@ + $parameters + */ + public function __construct( + public readonly string|object $rule, + public readonly array $parameters = [], + ) {} + + /** + * @template TRuleParam of string|object + * + * @param TRuleParam $rule + * @return ($rule is string ? self : self) + */ + public static function fromValue(string|object $rule): self + { + if (is_string($rule)) { + $explodedRule = explode(':', $rule, 2); + + $ruleName = $explodedRule[0]; + + return new NormalizedRule( + $ruleName, + isset($explodedRule[1]) ? static::parseParameters($ruleName, $explodedRule[1]) : [], + ); + } + + return new NormalizedRule($rule); + } + + /** @return list */ + protected static function parseParameters(string $rule, string $parameters): array + { + return static::ruleIsRegex($rule) ? [$parameters] : str_getcsv($parameters, escape: '\\'); + } + + protected static function ruleIsRegex(string $rule): bool + { + return in_array(strtolower($rule), ['regex', 'not_regex', 'notregex'], true); + } + + /** + * @return TRule + */ + public function getRule(): object|string + { + return $this->rule; + } + + /** + * @return list + */ + public function getParameters(): array + { + return $this->parameters; + } + + public function is(object|string $rule): bool + { + if (is_string($this->rule)) { + return $this->rule === $rule; + } + + return is_string($rule) && is_a(get_class($this->rule), $rule, true); + } +} diff --git a/vendor/dedoc/scramble/src/Support/RuleTransforming/RuleSetToSchemaTransformer.php b/vendor/dedoc/scramble/src/Support/RuleTransforming/RuleSetToSchemaTransformer.php new file mode 100644 index 00000000..54cc0ae3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/RuleTransforming/RuleSetToSchemaTransformer.php @@ -0,0 +1,204 @@ +validate(['foo' => ['required', 'boolean']]); + * ``` + * In this example, the rule set is `['required', 'boolean']`. + * Rules are processed in order defined in `static::RULES_PRIORITY` – this is needed so the type defining rules are + * processed first. + */ +class RuleSetToSchemaTransformer +{ + private const RULES_PRIORITY = [ + 'bool', 'boolean', 'numeric', 'int', 'integer', 'file', 'image', 'string', 'array', 'exists', + ]; + + private const IGNORE_STRINGABLE_RULES = [ + RequiredIf::class, + ExcludeIf::class, + ProhibitedIf::class, + Enum::class, + ]; + + public function __construct( + private TypeTransformer $openApiTransformer, + private RuleTransformers $config, + ) {} + + /** + * @param RuleSet $rules + */ + public function transform( + mixed $rules, + OpenApiType $initialType = new UnknownType, + ?RuleTransformerContext $context = null, + ): OpenApiType { + $rules = self::normalizeAndPrioritizeRules($rules); + + $schema = $this->transformToSchema($rules, $initialType, $context); + + $isRequired = $this->checkIfRequired($rules); + if ($isRequired !== null) { + $schema->setAttribute('required', $isRequired); + } + + return $schema; + } + + /** + * @param Collection $rules + */ + protected function transformToSchema(Collection $rules, OpenApiType $initialType, ?RuleTransformerContext $context): OpenApiType + { + $context ??= RuleTransformerContext::makeFromOpenApiContext($this->openApiTransformer->context, [ + 'field' => '', + 'fieldRules' => $rules, + ]); + + return $rules->reduce(function (OpenApiType $type, $rule) use ($context) { + if ($schema = $this->handledUsingExtension($rule, $type, $context)) { + return $schema; + } + + if (is_string($rule)) { + return $this->transformStringRuleToSchema($type, $rule, $context); + } + + // if ($rule instanceof DocumentableRule) { + // return $rule->toSchema($type, RuleMappingContext::class); + // } + + return method_exists($rule, 'docs') + ? $rule->docs($type, $this->openApiTransformer) + : $this->transformRuleValueToSchema($type, $rule, $context); + }, $initialType); + } + + protected function handledUsingExtension(string|object $rule, OpenApiType $type, RuleTransformerContext $context): ?OpenApiType + { + $normalizedRule = NormalizedRule::fromValue($rule); + + $extensions = $this->config + ->instances(RuleTransformer::class, [ + TypeTransformer::class => $this->openApiTransformer, + RulesMapper::class => new RulesMapper($this->openApiTransformer, $this), + ]) + ->filter(fn (RuleTransformer $ruleTransformer) => $ruleTransformer->shouldHandle($normalizedRule)) + ->values(); + + if ($extensions->isEmpty()) { + return null; + } + + return $extensions->reduce(function (OpenApiType $type, RuleTransformer $transformer) use ($normalizedRule, $context) { + return $transformer->toSchema($type, $normalizedRule, $context); + }, $type); + } + + protected function transformStringRuleToSchema(OpenApiType $type, string $rule, RuleTransformerContext $context): OpenApiType + { + $rulesHandler = new RulesMapper($this->openApiTransformer, $this); + + $normalizedRule = NormalizedRule::fromValue($rule); + + $ruleName = $normalizedRule->getRule(); + + return method_exists($rulesHandler, $ruleName) + ? $rulesHandler->$ruleName($type, $normalizedRule->getParameters(), $context) + : $type; + } + + /** + * @param object $rule + */ + protected function transformRuleValueToSchema(OpenApiType $type, $rule, RuleTransformerContext $context): OpenApiType + { + $rulesHandler = new RulesMapper($this->openApiTransformer, $this); + + $methodName = Str::camel(class_basename(get_class($rule))); + + return method_exists($rulesHandler, $methodName) + ? $rulesHandler->$methodName($type, $rule, $context) + : $type; + } + + /** + * @param Collection $rules + */ + protected function checkIfRequired(Collection $rules): ?bool + { + if ($rules->containsStrict('sometimes')) { + return false; + } + + if ($rules->containsStrict('required') || $rules->containsStrict('present')) { + return true; + } + + return null; + } + + /** + * @param RuleSet $rules + * @return Collection + */ + public static function normalizeAndPrioritizeRules(mixed $rules): Collection + { + $normalizedRules = Arr::wrap(is_string($rules) ? explode('|', $rules) : $rules); + + return collect($normalizedRules) + ->map(function ($rule) { + if (is_string($rule)) { + return $rule; + } + + if (in_array($rule::class, self::IGNORE_STRINGABLE_RULES)) { + return $rule; + } + + if (! method_exists($rule, '__toString')) { + return $rule; + } + + try { + return $rule->__toString(); + } catch (\Throwable) { + return $rule; + } + }) + ->sortByDesc(self::rulesSorter(...)); + } + + /** + * @param Rule $rule + */ + protected static function rulesSorter($rule): int + { + if (! is_string($rule)) { + return -2; + } + + $index = array_search($rule, self::RULES_PRIORITY); + + return $index === false ? -1 : count(self::RULES_PRIORITY) - $index; + } +} diff --git a/vendor/dedoc/scramble/src/Support/RuleTransforming/RuleTransformerContext.php b/vendor/dedoc/scramble/src/Support/RuleTransforming/RuleTransformerContext.php new file mode 100644 index 00000000..21559008 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/RuleTransforming/RuleTransformerContext.php @@ -0,0 +1,34 @@ + $fieldRules + */ + public function __construct( + public string $field, + public Collection $fieldRules, + public OpenApi $openApi, + public GeneratorConfig $config, + ) {} + + /** + * @param array{field?: string, fieldRules?: Collection} $bindings + */ + public static function makeFromOpenApiContext(OpenApiContext $openApiContext, array $bindings = []): self + { + return ContainerUtils::makeContextable(RuleTransformerContext::class, [ + OpenApi::class => $openApiContext->openApi, + GeneratorConfig::class => $openApiContext->config, + ...$bindings, + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/RuleTransforming/SchemaBag.php b/vendor/dedoc/scramble/src/Support/RuleTransforming/SchemaBag.php new file mode 100644 index 00000000..045dc837 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/RuleTransforming/SchemaBag.php @@ -0,0 +1,70 @@ + $items + */ + public function __construct( + private array $items = [] + ) {} + + public function has(string $name): bool + { + return array_key_exists($name, $this->items); + } + + public function get(string $name): ?Type + { + return $this->items[$name] ?? null; + } + + public function getOrFail(string $name): Type + { + $item = $this->get($name); + if (! $item) { + throw new OutOfBoundsException("[$name] schema doesn't exist"); + } + + return $item; + } + + public function set(string $name, Type $schema): self + { + $this->items[$name] = $schema; + + return $this; + } + + public function first(): ?Type + { + return array_values($this->all())[0] ?? null; + } + + public function firstOrFail(): Type + { + if (! $item = $this->first()) { + throw new OutOfBoundsException("Schema bag doesn't have any item exist"); + } + + return $item; + } + + public function remove(string $name): self + { + unset($this->items[$name]); + + return $this; + } + + /** @return array */ + public function all(): array + { + return $this->items; + } +} diff --git a/vendor/dedoc/scramble/src/Support/SchemaClassDocReflector.php b/vendor/dedoc/scramble/src/Support/SchemaClassDocReflector.php new file mode 100644 index 00000000..d5362540 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/SchemaClassDocReflector.php @@ -0,0 +1,90 @@ +phpDoc->getTagsByName($tagName))[0]?->value ?? null; + } + + public function getSchemaName(string $default = '', string $tagName = '@schemaName'): ?string + { + return explode("\n", array_values($this->phpDoc->getTagsByName($tagName))[0]->value->value ?? $default)[0]; + } + + public function getDescription(): string + { + return trim(implode("\n", array_map( + static function (PhpDocChildNode $child): string { + $s = (string) $child; + if ($child instanceof PhpDocTagNode) { + $s = explode("\n", $s, 2)[1] ?? ''; + } + + return $s === '' ? '' : ' '.$s; + }, + array_filter($this->phpDoc->children, fn ($n) => $n instanceof PhpDocTextNode || $n instanceof PhpDocTagNode) + ))); + } + + public static function createFromDocString(string $phpDocString, ?string $definingClassName = null) + { + $parsedDoc = PhpDoc::parse($phpDocString ?: '/** */'); + + if ($definingClassName) { + $classReflector = ClassReflector::make($definingClassName); + + $nameContext = $classReflector->getNameContext(); + + $parsedDoc = static::replaceClassNamesInDoc($parsedDoc, $nameContext); + } + + return new self($parsedDoc); + } + + public static function createFromClassName(string $className) + { + $classReflector = ClassReflector::make($className); + + $nameContext = $classReflector->getNameContext(); + $reflection = $classReflector->getReflection(); + + $parsedDoc = static::replaceClassNamesInDoc(PhpDoc::parse($reflection->getDocComment() ?: '/** */'), $nameContext); + + return new self($parsedDoc); + } + + private static function replaceClassNamesInDoc(PhpDocNode $docNode, NameContext $nameContext): PhpDocNode + { + $tagValues = [ + ...$docNode->getReturnTagValues(), + ...$docNode->getVarTagValues(), + ...$docNode->getThrowsTagValues(), + ]; + + foreach ($tagValues as $tagValue) { + if (! $tagValue->type) { + continue; + } + PhpDocTypeWalker::traverse($tagValue->type, [ + new ResolveFqnPhpDocTypeVisitor(new FileNameResolver($nameContext)), + ]); + } + + return $docNode; + } +} diff --git a/vendor/dedoc/scramble/src/Support/ServerFactory.php b/vendor/dedoc/scramble/src/Support/ServerFactory.php new file mode 100644 index 00000000..d73bd44e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/ServerFactory.php @@ -0,0 +1,40 @@ + */ + private array $variables; + + /** + * @param array $variables + */ + public function __construct(array $variables = []) + { + $this->variables = $variables; + } + + public function make(string $url, string $description = '') + { + return (new Server($url)) + ->variables($this->getServerVariables($url)) + ->setDescription($description); + } + + private function getServerVariables(string $url) + { + $params = Str::of($url)->matchAll('/\{(.*?)\}/'); + + return collect($this->variables) + ->only($params) + ->merge($params->reject(fn ($p) => array_key_exists($p, $this->variables))->mapWithKeys(fn ($p) => [ + $p => ServerVariable::make('example'), // @phpstan-ignore array.invalidKey + ])) + ->toArray(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/AbstractType.php b/vendor/dedoc/scramble/src/Support/Type/AbstractType.php new file mode 100644 index 00000000..42522192 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/AbstractType.php @@ -0,0 +1,119 @@ +original = $original; + + return $this; + } + + public function getOriginal(): ?Type + { + return $this->original; + } + + public function nodes(): array + { + return []; + } + + public function isInstanceOf(string $className): bool + { + return false; + } + + public function accepts(Type $otherType): bool + { + return is_a($otherType::class, $this::class, true); + } + + public function acceptedBy(Type $otherType): bool + { + return is_a($this::class, $otherType::class, true); + } + + public function intersect(Type $otherType): Type + { + if ($this->accepts($otherType)) { + return $otherType; + } + + if ($otherType->accepts($this)) { + return $this; + } + + return new NeverType; + } + + public function getPropertyType(string $propertyName, Scope $scope): Type + { + $className = $this::class; + + return new UnknownType("Cannot get a property type [$propertyName] on type [{$className}]"); + } + + public function getOffsetValueType(Type $offset): Type + { + return new UnknownType('Cannot get an offset value type '.$this::class); + } + + public function widen(): Type + { + return $this; + } + + public function getMethodDefinition(string $methodName, Scope $scope = new GlobalScope): ?FunctionLikeDefinition + { + return null; + } + + /** + * Creates a deep clone of the type. + * + * Note that template types are not cloned but rather kept as is due to templates replacement in the generics + * is made via reference comparison, not just via names. For now. + */ + public function clone(): static + { + $cloned = clone $this; + + foreach ($cloned->nodes() as $nodeName) { + /** @var Type|Type[] $nodeValue */ + $nodeValue = $cloned->$nodeName; + + if ($nodeValue instanceof TemplateType) { + continue; + } + + if (! is_array($nodeValue)) { + $cloned->$nodeName = $nodeValue->clone(); + + continue; + } + + /** @var Type $nodeItemValue */ + foreach ($nodeValue as $i => $nodeItemValue) { + if ($nodeItemValue instanceof TemplateType) { + continue; + } + + $cloned->$nodeName[$i] = $nodeItemValue->clone(); + } + } + + return $cloned; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/AbstractTypeVisitor.php b/vendor/dedoc/scramble/src/Support/Type/AbstractTypeVisitor.php new file mode 100644 index 00000000..efa1e65a --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/AbstractTypeVisitor.php @@ -0,0 +1,16 @@ +key + */ + public function isNumericKey() + { + return $this->key === null || is_numeric($this->key); + } + + public function isSame(Type $type) + { + throw new LogicException('ArrayItemType_ should never be checked.'); + } + + public function toString(): string + { + return '{'.$this->key.': '.$this->value->toString().'}'; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/ArrayType.php b/vendor/dedoc/scramble/src/Support/Type/ArrayType.php new file mode 100644 index 00000000..f3910580 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/ArrayType.php @@ -0,0 +1,48 @@ +value; + } + + public function accepts(Type $otherType): bool + { + if (parent::accepts($otherType)) { + return true; + } + + if ($otherType instanceof KeyedArrayType) { + return true; + } + + return false; + } + + public function isSame(Type $type) + { + return false; + } + + public function toString(): string + { + if ($this->key instanceof IntegerType) { + return sprintf('array<%s>', $this->value->toString()); + } + + return sprintf('array<%s, %s>', $this->key->toString(), $this->value->toString()); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/BooleanType.php b/vendor/dedoc/scramble/src/Support/Type/BooleanType.php new file mode 100644 index 00000000..91eb4522 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/BooleanType.php @@ -0,0 +1,16 @@ +name; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/CoalesceType.php b/vendor/dedoc/scramble/src/Support/Type/CoalesceType.php new file mode 100644 index 00000000..f7f36d89 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/CoalesceType.php @@ -0,0 +1,48 @@ +left)) { + return $this->left; + } + + return TypeHelper::mergeTypes( + TypeHelper::withoutNull($this->left), + $this->right, + ); + } + + public function isResolvable(): bool + { + return TypeHelper::isResolvable($this->left) + && TypeHelper::isResolvable($this->right); + } + + public function isSame(Type $type) + { + return $type instanceof static + && $this->left->isSame($type->left) + && $this->right->isSame($type->right); + } + + public function toString(): string + { + return $this->left->toString().'??'.$this->right->toString(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Contracts/Generic.php b/vendor/dedoc/scramble/src/Support/Type/Contracts/Generic.php new file mode 100644 index 00000000..3d039d6b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Contracts/Generic.php @@ -0,0 +1,13 @@ +toString() === $this->toString(); + } + + public function toString(): string + { + return "{$this->name}::{$this->caseName}"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/FloatType.php b/vendor/dedoc/scramble/src/Support/Type/FloatType.php new file mode 100644 index 00000000..a5baf871 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/FloatType.php @@ -0,0 +1,16 @@ + + */ + public array $exceptions; + + public function __construct( + string $name, + $arguments = [], + $returnType = null, + $exceptions = [] + ) { + $this->name = $name; + $this->arguments = $arguments; + $this->returnType = $returnType ?: new VoidType; + $this->exceptions = $exceptions; + } + + public function nodes(): array + { + return ['returnType', 'arguments']; + } + + public function setReturnType(Type $type): self + { + $this->returnType = $type; + + return $this; + } + + public function getReturnType(): Type + { + return $this->returnType; + } + + public function isSame(Type $type) + { + return $type instanceof static + && $this->returnType->isSame($type->returnType) + && collect($this->arguments)->every(fn (Type $t, $i) => $t->isSame($type->arguments[$i])); + } + + public function toString(): string + { + return sprintf( + '%s(%s): %s', + $this->templates ? sprintf('<%s>', implode(', ', array_map(fn ($t) => $t->toDefinitionString(), $this->templates))) : '', + implode(', ', array_map(fn ($t) => $t->toString(), $this->arguments)), + $this->returnType->toString(), + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Generic.php b/vendor/dedoc/scramble/src/Support/Type/Generic.php new file mode 100644 index 00000000..dd038a2f --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Generic.php @@ -0,0 +1,102 @@ + + */ + public array $templateTypes = []; + + public function __construct( + string $name, + array $templateTypes = [] + ) { + parent::__construct($name); + if (! array_is_list($templateTypes)) { + throw new \InvalidArgumentException('[$templateTypes] for Generic must be a list.'); + } + $this->templateTypes = $templateTypes; + } + + public function nodes(): array + { + return ['templateTypes']; + } + + public function accepts(Type $otherType): bool + { + if ($this->name === 'iterable') { + return $otherType instanceof ArrayType + || $otherType instanceof KeyedArrayType + || $otherType->isInstanceOf(Traversable::class); + } + + return parent::accepts($otherType); + } + + public function getPropertyType(string $propertyName, Scope $scope = new GlobalScope): Type + { + $propertyType = parent::getPropertyType($propertyName, $scope); + + $templateNameToIndexMap = ($classDefinition = $scope->index->getClass($this->name)) + ? array_flip(array_map(fn ($t) => $t->name, $classDefinition->templateTypes)) + : []; + + /** @var array $inferredTemplates */ + $inferredTemplates = collect($templateNameToIndexMap) + ->mapWithKeys(fn ($i, $name) => [$name => $this->templateTypes[$i] ?? new UnknownType]) + ->toArray(); + + return (new TypeWalker)->replace($propertyType, function (Type $t) use ($inferredTemplates) { + if (! $t instanceof TemplateType) { + return null; + } + + if (array_key_exists($t->name, $inferredTemplates)) { + return $inferredTemplates[$t->name]; + } + + return null; + }); + } + + public function isSame(Type $type) + { + if (! $type instanceof static) { + return false; + } + + if ($type->name !== $this->name) { + return false; + } + + if (count($type->templateTypes) !== count($this->templateTypes)) { + return false; + } + + foreach ($type->templateTypes as $i => $templateType) { + if (! $templateType->isSame($this->templateTypes[$i])) { + return false; + } + } + + return true; + } + + public function toString(): string + { + return sprintf( + '%s<%s>', + $this->name, + implode(', ', array_map(fn ($t) => $t->toString(), $this->templateTypes)) + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/GenericClassStringType.php b/vendor/dedoc/scramble/src/Support/Type/GenericClassStringType.php new file mode 100644 index 00000000..a3b7d829 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/GenericClassStringType.php @@ -0,0 +1,36 @@ +type->toString().'>'; + } + + public function nodes(): array + { + return ['type']; + } + + public function getTypes(): array + { + return [$this->type]; + } + + public function getValue(): string + { + return $this->type instanceof ObjectType ? $this->type->name : ''; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/IntegerRangeType.php b/vendor/dedoc/scramble/src/Support/Type/IntegerRangeType.php new file mode 100644 index 00000000..4bf2d0c9 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/IntegerRangeType.php @@ -0,0 +1,33 @@ +min = $min; + $this->max = $max; + } + + public function isSame(Type $type) + { + return parent::isSame($type) && $type->toString() === $this->toString(); + } + + public function toString(): string + { + if ($this->min !== null || $this->max !== null) { + $min = $this->min ?? 'min'; + $max = $this->max ?? 'max'; + + return parent::toString()."<{$min}, {$max}>"; + } + + return parent::toString(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/IntegerType.php b/vendor/dedoc/scramble/src/Support/Type/IntegerType.php new file mode 100644 index 00000000..33b59288 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/IntegerType.php @@ -0,0 +1,16 @@ +types = $types; + } + + public function nodes(): array + { + return ['types']; + } + + public function isSame(Type $type) + { + return $type instanceof static + && collect($this->types)->every(fn (Type $t, $i) => $t->isSame($type->types[$i])); + } + + public function toString(): string + { + return implode('&', array_map(fn ($t) => $t->toString(), $this->types)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/KeyedArrayType.php b/vendor/dedoc/scramble/src/Support/Type/KeyedArrayType.php new file mode 100644 index 00000000..f5db693b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/KeyedArrayType.php @@ -0,0 +1,153 @@ +isList = static::checkIsList($items); + } else { + $this->isList = $isList; + } + } + + public static function checkIsList(array $items): bool + { + return collect($items)->every(fn (ArrayItemType_ $item) => $item->key === null) + || collect($items)->every(fn (ArrayItemType_ $item) => is_numeric($item->key)); // @todo add consecutive check to be sure it is really a list + } + + public function nodes(): array + { + return ['items']; + } + + public function isSame(Type $type) + { + return false; + } + + public function getItemValueTypeByKey(string|int $key, Type $default = new UnknownType): Type + { + foreach ($this->items as $item) { + if ($item->key === $key) { + return $item->value; + } + } + + return $default; + } + + public function getKeyType(): Type + { + $items = collect($this->items); + + if ($items->isNotEmpty() && $items->every(fn (ArrayItemType_ $t) => $t->key === null || is_int($t->key))) { + return new IntegerType; + } + + if ($items->isNotEmpty() && $items->every(fn (ArrayItemType_ $t) => is_string($t->key))) { + return new Union( + $items->map(fn (ArrayItemType_ $t) => new LiteralStringType((string) $t->key))->all(), + ); + } + + return new Union([new IntegerType, new StringType]); + } + + public function getOffsetValueType(Type $offset): Type + { + $default = parent::getOffsetValueType($offset); + + if (! $offset instanceof LiteralType) { + return $default; + } + + $offsetValue = $offset->getValue(); + + if (! is_string($offsetValue) && ! is_int($offsetValue)) { + return $default; + } + + if ($this->isList && is_int($offsetValue)) { + $item = $this->getListItemAt($offsetValue); + + return $item + ? $item->value->mergeAttributes($item->attributes()) + : $default; + } + + foreach ($this->items as $item) { + if ($item->key === $offsetValue) { + return $item->value->mergeAttributes($item->attributes()); + } + } + + return $default; + } + + public function toString(): string + { + $name = $this->isList ? 'list' : 'array'; + + return sprintf( + '%s{%s}', + $name, + implode(', ', array_map(function (ArrayItemType_ $item) { + if ($this->isList) { + return $item->value->toString(); + } + + return sprintf( + '%s%s: %s', + $item->isNumericKey() ? $this->indexOfNumericKeyItem($item) : $item->key, + $item->isOptional ? '?' : '', + $item->value->toString() + ); + }, $this->items)) + ); + } + + private function getListItemAt(int $position): ?ArrayItemType_ + { + $current = 0; + + foreach ($this->items as $item) { + if ($item->isNumericKey()) { + if ($current === $position) { + return $item; + } + + $current++; + } + } + + return null; + } + + private function indexOfNumericKeyItem(ArrayItemType_ $search): int + { + for ($position = 0; $item = $this->getListItemAt($position); $position++) { + if ($item === $search) { + return $position; + } + } + + return 0; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralBooleanType.php b/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralBooleanType.php new file mode 100644 index 00000000..30909bda --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralBooleanType.php @@ -0,0 +1,34 @@ +value = $value; + } + + public function getValue(): bool + { + return $this->value; + } + + public function isSame(Type $type) + { + return $type instanceof static && $type->value === $this->value; + } + + public function toString(): string + { + $value = $this->value ? 'true' : 'false'; + + return parent::toString()."($value)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralFloatType.php b/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralFloatType.php new file mode 100644 index 00000000..92224d80 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralFloatType.php @@ -0,0 +1,32 @@ +value = $value; + } + + public function getValue(): float + { + return $this->value; + } + + public function isSame(Type $type) + { + return $type instanceof static && $type->value === $this->value; + } + + public function toString(): string + { + return parent::toString()."($this->value)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralIntegerType.php b/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralIntegerType.php new file mode 100644 index 00000000..375a557b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralIntegerType.php @@ -0,0 +1,32 @@ +value = $value; + } + + public function getValue(): int + { + return $this->value; + } + + public function isSame(Type $type) + { + return $type instanceof static && $type->value === $this->value; + } + + public function toString(): string + { + return parent::toString()."($this->value)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralStringType.php b/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralStringType.php new file mode 100644 index 00000000..96c3aeb7 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Literal/LiteralStringType.php @@ -0,0 +1,32 @@ +value = $value; + } + + public function getValue(): string + { + return $this->value; + } + + public function isSame(Type $type) + { + return $type instanceof static && $type->value === $this->value; + } + + public function toString(): string + { + return parent::toString()."($this->value)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/MissingType.php b/vendor/dedoc/scramble/src/Support/Type/MissingType.php new file mode 100644 index 00000000..193d5020 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/MissingType.php @@ -0,0 +1,18 @@ +name === 'iterable' && $className === 'iterable') { + return true; + } + + return is_a($this->name, $className, true); + } + + public function isSame(Type $type) + { + return $type instanceof static && $type->name === $this->name; + } + + public function getPropertyType(string $propertyName, Scope $scope = new GlobalScope): Type + { + if ($propertyType = app(ExtensionsBroker::class)->getPropertyType(new PropertyFetchEvent( + instance: $this, + name: $propertyName, + scope: $scope, + ))) { + return $propertyType; + } + + $definition = $scope->index->getClass($this->name); + + if (! $propertyDefinition = $definition?->getPropertyDefinition($propertyName)) { + return new UnknownType("Cannot get a property type [$propertyName] on type [{$this->name}]"); + } + + return $propertyDefinition->type ?: $propertyDefinition->defaultType; + } + + public function getMethodDefinition(string $methodName, Scope $scope = new GlobalScope): ?FunctionLikeDefinition + { + $classDefinition = $scope->index->getClass($this->name); + + return $classDefinition?->getMethodDefinition($methodName, $scope); + } + + public function getMethodReturnType(string $methodName, array|ArgumentTypeBag $arguments = [], Scope $scope = new GlobalScope): Type + { + $arguments = $arguments instanceof ArgumentTypeBag ? $arguments : new UnresolvableArgumentTypeBag($arguments); + $classDefinition = $scope->index->getClass($this->name); + + if ($returnType = app(ExtensionsBroker::class)->getMethodReturnType(new MethodCallEvent( + instance: $this, + name: $methodName, + scope: $scope, + arguments: $arguments, + methodDefiningClassName: $definingClassName = $classDefinition ? $classDefinition->getMethodDefiningClassName($methodName, $scope->index) : $this->name, + ))) { + return $returnType; + } + + /* + * For now, when parent class is in `vendor`, we may do not know that certain definition exists. + */ + if (! $methodDefinition = $this->getMethodDefinition($methodName)) { + return new UnknownType("No method {$definingClassName}@{$methodName} definition found, it may be located in `vendor` which is not analyzed."); + } + + $returnType = $methodDefinition->getReturnType(); + + // Here templates should be replaced for generics and arguments should be taken into account. + return $returnType instanceof TemplateType && $returnType->is + ? $returnType->is + : $returnType; + } + + public function accepts(Type $otherType): bool + { + if (! $otherType instanceof ObjectType) { + return false; + } + + return is_a($otherType->name, $this->name, true); + } + + public function toString(): string + { + return $this->name; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/OffsetAccessType.php b/vendor/dedoc/scramble/src/Support/Type/OffsetAccessType.php new file mode 100644 index 00000000..9c3167a2 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/OffsetAccessType.php @@ -0,0 +1,39 @@ +type->getOffsetValueType($this->offset); + } + + public function isResolvable(): bool + { + return TypeHelper::isResolvable($this->type) + && TypeHelper::isResolvable($this->offset); + } + + public function isSame(Type $type) + { + return false; + } + + public function toString(): string + { + return $this->type->toString().'['.$this->offset->toString().']'; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/OffsetSetType.php b/vendor/dedoc/scramble/src/Support/Type/OffsetSetType.php new file mode 100644 index 00000000..3a8b25b9 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/OffsetSetType.php @@ -0,0 +1,165 @@ +offset instanceof KeyedArrayType) { + return new UnknownType; + } + + if (! $this->type instanceof KeyedArrayType && ! $this->type instanceof ArrayType) { + return new UnknownType; + } + + $path = $this->normalizePath($this->offset); + if (! $path) { + return new UnknownType; + } + + $result = $this->type->clone(); + + return $this->applyPath($result, $path, $this->value); + } + + public function acceptedBy(Type $otherType): bool + { + return $otherType instanceof ArrayType + || $otherType instanceof KeyedArrayType + || $otherType instanceof OffsetSetType + || $otherType instanceof OffsetUnsetType; + } + + public function isResolvable(): bool + { + return TypeHelper::isResolvable($this->type) + && TypeHelper::isResolvable($this->offset) + && TypeHelper::isResolvable($this->value); + } + + public function isSame(Type $type) + { + return false; + } + + public function toString(): string + { + return 'OffsetSet<'.$this->type->toString().', '.$this->offset->toString().', '.$this->value->toString().'>'; + } + + /** + * @param list $path + */ + private function applyPath(ArrayType|KeyedArrayType &$target, array $path, Type $value): ArrayType|KeyedArrayType + { + if (count($path) === 0) { + return $target; + } + + $pathItem = array_shift($path); + + if ($pathItem === null && $target instanceof KeyedArrayType && count($target->items) === 0) { + $target = (new ArrayType)->mergeAttributes($target->attributes()); + + if ($path) { + $target->value = new KeyedArrayType; + + $this->applyPath($target->value, $path, $value); + } else { + $target->value = $value; + } + + return $target; + } + + if ($target instanceof ArrayType) { + $target->value = Union::wrap([$target->value, $value]); + + $this->applyPath($target, $path, $value); + + return $target; + } + + $targetItem = Arr::first( + $target->items, + fn (ArrayItemType_ $t) => $t->key === $pathItem, + ); + + if ($targetItem) { + if ($path) { + if (! $targetItem->value instanceof KeyedArrayType && ! $targetItem->value instanceof ArrayType) { + return $target; + } + + $this->applyPath($targetItem->value, $path, $value); + } else { + $targetItem->value = $value; + } + } else { + if ($path) { + $targetItem = new ArrayItemType_( + key: $pathItem, + value: new KeyedArrayType, + ); + $target->items[] = $targetItem; + + $this->applyPath($targetItem->value, $path, $value); // @phpstan-ignore argument.type + } else { + $target->items[] = new ArrayItemType_(key: $pathItem, value: $value); + } + } + + $target->isList = KeyedArrayType::checkIsList($target->items); + + return $target; + } + + /** + * @return null|non-empty-list + */ + private function normalizePath(KeyedArrayType $path): ?array + { + $pathItems = array_map(fn (ArrayItemType_ $t) => $t->value, $path->items); + + $normalizedPath = []; + foreach ($pathItems as $pathItemType) { + if ($pathItemType instanceof TemplatePlaceholderType) { + $normalizedPath[] = null; + + continue; + } + + if ($pathItemType instanceof LiteralString || $pathItemType instanceof LiteralIntegerType) { + $normalizedPath[] = $pathItemType->getValue(); + + continue; + } + + return null; + } + + if (! count($normalizedPath)) { + return null; + } + + return $normalizedPath; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/OffsetUnsetType.php b/vendor/dedoc/scramble/src/Support/Type/OffsetUnsetType.php new file mode 100644 index 00000000..af9d5e0c --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/OffsetUnsetType.php @@ -0,0 +1,151 @@ +offset instanceof KeyedArrayType) { + return new UnknownType; + } + + if (! $this->type instanceof KeyedArrayType && ! $this->type instanceof ArrayType) { + return new UnknownType; + } + + if ($this->type instanceof ArrayType) { + return $this->type; // ?? + } + + $path = $this->normalizePath($this->offset); + if (! $path) { + return new UnknownType; + } + + return $this->unsetPath($this->type->clone(), $path); + } + + public function isResolvable(): bool + { + return TypeHelper::isResolvable($this->type) + && TypeHelper::isResolvable($this->offset); + } + + public function isSame(Type $type) + { + return false; + } + + public function toString(): string + { + return 'OffsetUnset<'.$this->type->toString().', '.$this->offset->toString().'>'; + } + + /** + * @param array $path + */ + private function unsetPath(KeyedArrayType $target, array $path): KeyedArrayType + { + $modifyingType = $target; + + foreach ($path as $i => $pathItem) { + $isLast = $i === array_key_last($path); + + $modifyingType = $isLast + ? $this->applyLeafUnsetting($modifyingType, $pathItem) + : $this->applyIntermediateUnsettingStep($modifyingType, $pathItem); + + if ($modifyingType === null) { + return $target; + } + } + + return $target; + } + + private function applyIntermediateUnsettingStep(KeyedArrayType $modifyingType, string|int|null $pathItem): ?KeyedArrayType + { + $targetItems = $modifyingType->items; + + $targetItem = Arr::first( + $targetItems, + fn (ArrayItemType_ $t) => $t->key === $pathItem, + ); + + if ($targetItem) { + if (! $targetItem->value instanceof KeyedArrayType) { + return null; + } + $newModifyingType = $targetItem->value; + } else { + $targetItem = new ArrayItemType_( + key: $pathItem, + value: $newModifyingType = new KeyedArrayType, + ); + $targetItems[] = $targetItem; + } + + $modifyingType->items = $targetItems; + $modifyingType->isList = KeyedArrayType::checkIsList($targetItems); + + return $newModifyingType; + } + + private function applyLeafUnsetting(KeyedArrayType $modifyingType, string|int|null $pathItem): KeyedArrayType + { + $targetItems = array_values(array_filter( + $modifyingType->items, + fn (ArrayItemType_ $t) => $t->key !== $pathItem + )); + + $modifyingType->items = $targetItems; + $modifyingType->isList = KeyedArrayType::checkIsList($targetItems); + + return $modifyingType; + } + + /** + * @return null|list + */ + private function normalizePath(KeyedArrayType $path): ?array + { + $pathItems = array_map(fn (ArrayItemType_ $t) => $t->value, $path->items); + + $normalizedPath = []; + foreach ($pathItems as $pathItemType) { + if ($pathItemType instanceof LiteralString || $pathItemType instanceof LiteralIntegerType) { + $normalizedPath[] = $pathItemType->getValue(); + + continue; + } + + return null; + } + + return $normalizedPath; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/RecursiveTemplateSolver.php b/vendor/dedoc/scramble/src/Support/Type/RecursiveTemplateSolver.php new file mode 100644 index 00000000..6d18b87d --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/RecursiveTemplateSolver.php @@ -0,0 +1,155 @@ +clone(); + } + + if ($parameter instanceof Union) { + return $this->makeUnion(array_map( + fn (Type $pt) => $this->solve($pt, $argument, $template), + $this->getStructurallyMatchingTypes($parameter, $argument), + )); + } + + if ($argument instanceof Union) { + return $this->makeUnion(array_map( + fn (Type $at) => $this->solve($parameter, $at, $template), + $argument->types, + )); + } + + if ($parameter instanceof Generic && $parameter->name === 'iterable') { + $argument = $this->normalizeIterable($argument); + } + + $solutions = []; + + $this->walk($parameter, function ($t) use (&$solutions, $parameter, $argument, $template): void { + // Current path of type $t in $parameter + $path = TypePath::findFirst( + $parameter, + fn ($pt) => $pt === $t, + ); + + // There will be some path, but just in case + if (! $path) { + return; + } + + // Given our current location in $parameter type, get the corresponding + // type in the same location in $argument + $argumentT = $path->getFrom($argument); + + // In case there is nothing in the same location in $argument + if (! $argumentT instanceof Type) { + return; + } + + // In case we are at the root of both types, we don't need to solve anything + // because it would be an invisible recursive call below. + if ($parameter === $t && $argumentT === $argument) { + return; + } + + $solutions[] = $this->solve($t, $argumentT, $template); + }); + + return $this->makeUnion($solutions); + } + + private function walk(Type $type, Closure $cb): void + { + $typeTraverser = new TypeTraverser([ + new class($cb) extends AbstractTypeVisitor + { + public function __construct(private Closure $cb) {} + + public function enter(Type $type): Type + { + ($this->cb)($type); + + return $type; + } + }, + ]); + + $typeTraverser->traverse($type); + } + + private function normalizeIterable(Type $argument): Type + { + if ($argument instanceof ArrayType) { + return new Generic('iterable', [$argument->key, $argument->value]); + } + + if ($argument instanceof KeyedArrayType) { + return new Generic('iterable', [ + $argument->getKeyType(), + $argument->items + ? new Union(array_map(fn (ArrayItemType_ $t) => $t->value, $argument->items)) + : new UnknownType, + ]); + } + + return $argument; + + } + + /** + * @return Type[] + */ + private function getStructurallyMatchingTypes(Union $parameter, Type $argument): array + { + $matching = []; + + foreach ($parameter->types as $pt) { + if ( + $pt instanceof Generic + && $argument instanceof Generic + && $pt->isInstanceOf($argument->name) + ) { + $matching[] = $pt; + + continue; + } + + if ($pt::class === $argument::class) { + $matching[] = $pt; + + continue; + } + + if ($pt->accepts($argument)) { + $matching[] = $pt; + } + } + + if ($matching) { + return $matching; + } + + return $parameter->types; + } + + /** + * @param (Type|null)[] $types + */ + private function makeUnion(array $types): ?Type + { + $types = array_values(array_filter($types)); + + if (! $types) { + return null; + } + + return Union::wrap($types); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/AbstractReferenceType.php b/vendor/dedoc/scramble/src/Support/Type/Reference/AbstractReferenceType.php new file mode 100644 index 00000000..30d6876f --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/AbstractReferenceType.php @@ -0,0 +1,19 @@ +toString() === $this->toString(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/CallableCallReferenceType.php b/vendor/dedoc/scramble/src/Support/Type/Reference/CallableCallReferenceType.php new file mode 100644 index 00000000..7ff6a9e3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/CallableCallReferenceType.php @@ -0,0 +1,31 @@ + $t->toString(), $this->arguments), + ); + + $calleeString = is_string($this->callee) ? $this->callee : $this->callee->toString(); + + return "(λ{$calleeString})($argsTypes)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/ConstFetchReferenceType.php b/vendor/dedoc/scramble/src/Support/Type/Reference/ConstFetchReferenceType.php new file mode 100644 index 00000000..ccf1a9ce --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/ConstFetchReferenceType.php @@ -0,0 +1,18 @@ +callee) ? $this->callee : $this->callee->toString(); + + return "(#{$callee})::{$this->constName}"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/MethodCallReferenceType.php b/vendor/dedoc/scramble/src/Support/Type/Reference/MethodCallReferenceType.php new file mode 100644 index 00000000..c2882203 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/MethodCallReferenceType.php @@ -0,0 +1,30 @@ + $t->toString(), $this->arguments), + ); + + return "(#{$this->callee->toString()}).{$this->methodName}($argsTypes)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/NewCallReferenceType.php b/vendor/dedoc/scramble/src/Support/Type/Reference/NewCallReferenceType.php new file mode 100644 index 00000000..91c85e29 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/NewCallReferenceType.php @@ -0,0 +1,36 @@ +name) && is_a($this->name, $className, true); + } + + public function toString(): string + { + $argsTypes = implode( + ', ', + array_map(fn ($t) => $t->toString(), $this->arguments), + ); + + $name = is_string($this->name) ? $this->name : $this->name->toString(); + + return "(new {$name})($argsTypes)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/PotentialMethodMutatingCallType.php b/vendor/dedoc/scramble/src/Support/Type/Reference/PotentialMethodMutatingCallType.php new file mode 100644 index 00000000..a9360018 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/PotentialMethodMutatingCallType.php @@ -0,0 +1,30 @@ + $t->toString(), $this->arguments), + ); + + return "self-out-(#{$this->callee->toString()}).{$this->methodName}($argsTypes)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/PropertyFetchReferenceType.php b/vendor/dedoc/scramble/src/Support/Type/Reference/PropertyFetchReferenceType.php new file mode 100644 index 00000000..4b17a1b5 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/PropertyFetchReferenceType.php @@ -0,0 +1,23 @@ +object->toString()}).{$this->propertyName}"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/StaticMethodCallReferenceType.php b/vendor/dedoc/scramble/src/Support/Type/Reference/StaticMethodCallReferenceType.php new file mode 100644 index 00000000..2295d5e0 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/StaticMethodCallReferenceType.php @@ -0,0 +1,32 @@ + $t->toString(), $this->arguments), + ); + + $calleeType = is_string($this->callee) ? $this->callee : $this->callee->toString(); + + return "(#{$calleeType})::{$this->methodName}($argsTypes)"; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Reference/StaticReference.php b/vendor/dedoc/scramble/src/Support/Type/Reference/StaticReference.php new file mode 100644 index 00000000..e1b8b307 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Reference/StaticReference.php @@ -0,0 +1,41 @@ +keyword, self::KEYWORDS)) { + throw new \InvalidArgumentException("[$this->keyword] keyword must be one of possible values."); + } + } + + public function isStatic() + { + return $this->keyword === static::STATIC; + } + + public function isSelf() + { + return $this->keyword === static::SELF; + } + + public function isParent() + { + return $this->keyword === static::PARENT; + } + + public function toString() + { + return $this->keyword; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/SelfType.php b/vendor/dedoc/scramble/src/Support/Type/SelfType.php new file mode 100644 index 00000000..3498f352 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/SelfType.php @@ -0,0 +1,16 @@ +name === $type->name; + } + + public function isInstanceOf(string $className): bool + { + return $this->is?->isInstanceOf($className) ?: false; + } + + public function toString(): string + { + return $this->name; + } + + public function toDefinitionString(): string + { + if (! $this->is) { + return $this->name; + } + + return sprintf('%s is %s', $this->name, $this->is->toString()); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TranslatedStringType.php b/vendor/dedoc/scramble/src/Support/Type/TranslatedStringType.php new file mode 100644 index 00000000..8148b0d3 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TranslatedStringType.php @@ -0,0 +1,45 @@ +key->isSame($this->key); + } + + public function toString(): string + { + return '__('.$this->key->toString().')'; + } + + public function resolve(): Type + { + if ($this->key instanceof LiteralStringType) { + $translator = tap(clone app('translator'), fn (Translator $t) => $t->setLocale(config('app.locale'))); + + return new LiteralStringType($translator->get($this->key->value)); + } + + return $this->key; + } + + public function isResolvable(): bool + { + return TypeHelper::isResolvable($this->key); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Type.php b/vendor/dedoc/scramble/src/Support/Type/Type.php new file mode 100644 index 00000000..edbe2f12 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Type.php @@ -0,0 +1,60 @@ + + */ + public function attributes(): array; + + /** + * @param array $attributes + * @return $this + */ + public function mergeAttributes(array $attributes): static; + + public function isInstanceOf(string $className): bool; + + public function accepts(Type $otherType): bool; + + public function acceptedBy(Type $otherType): bool; + + /** + * @return string[] + */ + public function nodes(): array; + + public function getPropertyType(string $propertyName, Scope $scope): Type; + + public function getOffsetValueType(Type $offset): Type; + + public function getMethodDefinition(string $methodName, Scope $scope = new GlobalScope): ?FunctionLikeDefinition; + + /** @return $this */ + public function setOriginal(?Type $original): self; + + public function getOriginal(): ?Type; + + public function widen(): Type; + + /** + * @return bool + */ + public function isSame(self $type); + + public function toString(): string; + + public function clone(): static; +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypeAttributes.php b/vendor/dedoc/scramble/src/Support/Type/TypeAttributes.php new file mode 100644 index 00000000..34277e26 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypeAttributes.php @@ -0,0 +1,52 @@ + */ + private $attributes = []; + + /** + * @param mixed $value + */ + public function setAttribute(string $key, $value): void + { + $this->attributes[$key] = $value; + } + + public function hasAttribute(string $key): bool + { + return array_key_exists($key, $this->attributes); + } + + /** + * @return mixed + */ + public function getAttribute(string $key) + { + if ($this->hasAttribute($key)) { + return $this->attributes[$key]; + } + + return null; + } + + /** + * @return array + */ + public function attributes(): array + { + return $this->attributes; + } + + /** + * @return $this + */ + public function mergeAttributes($attributes): static + { + $this->attributes = array_merge($this->attributes, $attributes); + + return $this; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypeHelper.php b/vendor/dedoc/scramble/src/Support/Type/TypeHelper.php new file mode 100644 index 00000000..5b18c3ae --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypeHelper.php @@ -0,0 +1,316 @@ +types + : [$type]; + + foreach ($nestedTypes as $nestedType) { + $alreadyAdded = false; + + foreach ($flattenedTypes as $existingType) { + if ($nestedType->isSame($existingType)) { + $alreadyAdded = true; + + break; + } + } + + if (! $alreadyAdded) { + $flattenedTypes[] = $nestedType; + } + } + } + + $hasVoidOrNever = false; + + foreach ($flattenedTypes as $type) { + if ($type instanceof VoidType || $type instanceof NeverType) { + $hasVoidOrNever = true; + + break; + } + } + + if ($hasVoidOrNever && count($flattenedTypes) > 1) { + $filtered = []; + + foreach ($flattenedTypes as $type) { + if ($type instanceof VoidType || $type instanceof NeverType) { + continue; + } + + $filtered[] = $type; + } + + $flattenedTypes = $filtered; + } + + return Union::wrap($flattenedTypes); + } + + public static function withoutNull(Type $type): Type + { + if ($type instanceof NullType) { + return new NeverType; + } + + if (! $type instanceof Union) { + return $type; + } + + $types = collect($type->types) + ->map(fn (Type $t) => static::withoutNull($t)) + ->reject(fn (Type $t) => $t instanceof NeverType) + ->all(); + + return count($types) ? static::mergeTypes(...$types) : new NeverType; + } + + public static function canContainNull(Type $type): bool + { + if ($type instanceof NullType || $type instanceof UnknownType || $type instanceof MixedType) { + return true; + } + + if ($type instanceof Union) { + return collect($type->types)->contains(fn (Type $t) => static::canContainNull($t)); + } + + return false; + } + + public static function createTypeFromTypeNode(Node $typeNode) + { + if ($typeNode instanceof Node\NullableType) { + return Union::wrap([ + new NullType, + static::createTypeFromTypeNode($typeNode->type), + ]); + } + + if ($typeNode instanceof Node\Identifier) { + if ($typeNode->name === 'int') { + return new IntegerType; + } + + if ($typeNode->name === 'string') { + return new StringType; + } + + if ($typeNode->name === 'bool') { + return new BooleanType; + } + + if ($typeNode->name === 'float') { + return new FloatType; + } + + if ($typeNode->name === 'array') { + return new ArrayType( + value: new MixedType, + ); + } + + if ($typeNode->name === 'mixed') { + return new MixedType; + } + + if ($typeNode->name === 'null') { + return new NullType; + } + + if (in_array($typeNode->name, ['true', 'false'])) { + return new LiteralBooleanType($typeNode->name === 'true'); + } + + return new ObjectType($typeNode->toString()); + } + + if ($typeNode instanceof Node\Name) { + return new ObjectType($typeNode->toString()); + } + + if ($typeNode instanceof Node\UnionType) { + return Union::wrap(array_map( + fn ($node) => static::createTypeFromTypeNode($node), + $typeNode->types + )); + } + + return new UnknownType('Cannot get type from AST node '.(new Standard)->prettyPrint([$typeNode])); + } + + /** + * @param (Node\Arg|Node\VariadicPlaceholder)[] $args + * @param array{0: string, 1: int} $parameterNameIndex + * @return ($default is null ? Type|null : Type) + */ + public static function getArgType(Scope $scope, array $args, array $parameterNameIndex, ?Type $default = null): ?Type + { + $default = $default ?: new UnknownType("Cannot get a type of the arg #{$parameterNameIndex[1]}($parameterNameIndex[0])"); + + $matchingArg = static::getArg($args, $parameterNameIndex); + + return $matchingArg instanceof Node\Arg ? $scope->getType($matchingArg->value) : $default; + } + + public static function unpackIfArray($type) + { + if (! $type instanceof KeyedArrayType) { + return $type; + } + + $unpackedItems = collect($type->items) + ->flatMap(function (ArrayItemType_ $type) { + if ($type->shouldUnpack && $type->value instanceof KeyedArrayType) { + return $type->value->items; + } + + return [$type]; + }) + ->reduce(function ($arrayItems, ArrayItemType_ $itemType) { + if (! $itemType->key) { + $arrayItems[] = $itemType; + } else { + $arrayItems[$itemType->key] = $itemType; + } + + return $arrayItems; + }, []); + + return (new KeyedArrayType(array_values($unpackedItems)))->mergeAttributes($type->attributes()); + } + + /** + * @param (Node\Arg|Node\VariadicPlaceholder)[] $args + * @param array{0: string, 1: int} $parameterNameIndex + */ + private static function getArg(array $args, array $parameterNameIndex): Node\Arg|Node\VariadicPlaceholder|null + { + [$name, $index] = $parameterNameIndex; + + return collect($args)->first( + fn ($arg) => ($arg->name->name ?? '') === $name, + fn () => empty($args[$index]->name->name) ? ($args[$index] ?? null) : null, + ); + } + + public static function createTypeFromValue(mixed $value) + { + if (is_string($value)) { + return new LiteralStringType($value); + } + + if (is_int($value)) { + return new LiteralIntegerType($value); + } + + if (is_float($value)) { + return new FloatType; + } + + if (is_bool($value)) { + return new LiteralBooleanType($value); + } + + if (is_array($value)) { + return new KeyedArrayType(array_map( + fn ($key) => new ArrayItemType_($key, static::createTypeFromValue($value[$key])), + array_keys($value), + )); + } + + if ($value === null) { + return new NullType; + } + + if (is_object($value)) { + if (enum_exists($value::class)) { + return new EnumCaseType( + $value::class, + $value->name, + ); + } + + return new ObjectType($value::class); // @todo generics + } + + return new MixedType; + } + + public static function createTypeFromReflectionType(ReflectionType $reflectionType, bool $handleNullable = true) + { + if ($reflectionType->allowsNull() && $handleNullable) { + return Union::wrap([ + new NullType, + static::createTypeFromReflectionType($reflectionType, handleNullable: false), + ]); + } + + if ($reflectionType instanceof ReflectionUnionType) { + return Union::wrap(array_map( + fn ($node) => static::createTypeFromReflectionType($node, $handleNullable), + $reflectionType->getTypes(), + )); + } + + if ($reflectionType instanceof ReflectionNamedType) { + if ($reflectionType->getName() === 'int') { + return new IntegerType; + } + + if ($reflectionType->getName() === 'string') { + return new StringType; + } + + if ($reflectionType->getName() === 'bool') { + return new BooleanType; + } + + if ($reflectionType->getName() === 'float') { + return new FloatType; + } + + if ($reflectionType->getName() === 'array') { + return new ArrayType; + } + + return new ObjectType($reflectionType->getName()); + } + + return new UnknownType('Cannot create type from reflection type '.((string) $reflectionType)); + } + + public static function isResolvable(Type $type): bool + { + if ($type instanceof TemplateType) { + return false; + } + + if ($type instanceof LateResolvingType) { + return false; + } + + return true; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypePath.php b/vendor/dedoc/scramble/src/Support/Type/TypePath.php new file mode 100644 index 00000000..f0dc4673 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypePath.php @@ -0,0 +1,42 @@ +items as $item) { + if (! $typeInCheck = $item->getFrom($typeInCheck)) { + return null; + } + } + + return $typeInCheck; + } + + /** + * @param Closure(Type): bool $cb + */ + public static function findFirst(Type $type, Closure $cb): ?TypePath + { + $traverser = new TypeTraverser([ + $visitor = new TypePathFindingVisitor($cb), + ]); + + $traverser->traverse($type); + + return $visitor->getPath(); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypePathFindingVisitor.php b/vendor/dedoc/scramble/src/Support/Type/TypePathFindingVisitor.php new file mode 100644 index 00000000..c0d0e175 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypePathFindingVisitor.php @@ -0,0 +1,128 @@ + + */ + private WeakMap $pointers; + + /** + * @param Closure(Type): bool $cb + */ + public function __construct( + private Closure $cb, + ) { + $this->pointers = new WeakMap; + } + + public function enter(Type $type): ?Type + { + if ($this->foundPathItems !== null) { + return null; + } + + $this->pushPointers($type); + + $this->pushPathItem($type); + + if (($this->cb)($type)) { + $this->foundPathItems = $this->pathItems ?: []; + } + + return null; + } + + public function leave(Type $type): ?Type + { + $this->popPathItem($type); + + return null; + } + + public function getPath(): ?TypePath + { + if ($this->foundPathItems === null) { + return null; + } + + return new TypePath($this->foundPathItems); + } + + private function pushPointers(Type $type): void + { + $propertiesWithNodes = $type->nodes(); + + foreach ($propertiesWithNodes as $propertyWithNode) { + /** @var Type|Type[] $node */ + $node = $type->$propertyWithNode; + + if (! is_array($node)) { + $this->pointers->offsetSet( + $node, + [ + new TypePathItem( + key: $propertyWithNode, + condition: new TypePathItemCondition( + class: $type::class, + objectName: $type instanceof ObjectType ? $type->name : null, + ) + ), + ] + ); + } else { + foreach ($node as $index => $item) { + $this->pointers->offsetSet( + $item, + [ + new TypePathItem( + key: $propertyWithNode, + condition: new TypePathItemCondition( + class: $type::class, + objectName: $type instanceof ObjectType ? $type->name : null, + ) + ), + new TypePathItem( + key: $item instanceof ArrayItemType_ + ? ($item->key ?? $index) + : $index, + kind: $item instanceof ArrayItemType_ ? TypePathItem::KIND_ARRAY_ITEM : TypePathItem::KIND_DEFAULT, + ), + ] + ); + } + } + } + } + + private function pushPathItem(Type $type): void + { + $pointer = $this->pointers[$type] ?? null; + + if ($pointer) { + $this->pathItems ??= []; + + $this->pathItems = array_merge($this->pathItems, $pointer); + } + } + + private function popPathItem(Type $type): void + { + $pointer = $this->pointers[$type] ?? null; + + if ($pointer && $this->pathItems) { + array_splice($this->pathItems, 0 - count($pointer)); + } + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypePathItem.php b/vendor/dedoc/scramble/src/Support/Type/TypePathItem.php new file mode 100644 index 00000000..e547d56d --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypePathItem.php @@ -0,0 +1,55 @@ +condition && $type instanceof Type && ! $this->condition->matches($type)) { + return null; + } + + if ($this->kind === self::KIND_ARRAY_ITEM) { + if (! is_array($type)) { + // fail! + return null; + } + + foreach ($type as $index => $arrayItem) { + if (! $arrayItem instanceof ArrayItemType_) { + // fail? + continue; + } + + $itemKey = $arrayItem->key ?? $index; + + if ($this->key === $itemKey) { + return $arrayItem; + } + } + + return null; + } + + if (is_array($type)) { + return array_key_exists($this->key, $type) ? $type[$this->key] : null; + } + + return $type->{$this->key} ?? null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypePathItemCondition.php b/vendor/dedoc/scramble/src/Support/Type/TypePathItemCondition.php new file mode 100644 index 00000000..7b2d133e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypePathItemCondition.php @@ -0,0 +1,24 @@ +class && $this->class !== $result::class) { + return false; + } + + if ($this->objectName) { + return $result instanceof ObjectType && $result->isInstanceOf($this->objectName); + } + + return true; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypeTraverser.php b/vendor/dedoc/scramble/src/Support/Type/TypeTraverser.php new file mode 100644 index 00000000..026caf08 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypeTraverser.php @@ -0,0 +1,99 @@ + */ + private WeakMap $traversedTypes; + + /** + * @param TypeVisitor[] $visitors + */ + public function __construct( + private array $visitors = [], + ) { + $this->traversedTypes = new WeakMap; + } + + public function traverse(Type $type): Type + { + if (isset($this->traversedTypes[$type])) { + return $type; + } + + $this->traversedTypes[$type] = true; + + $enterResult = $this->enterType($type); + + if ($enterResult === TypeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + return $type; + } + + if ($enterResult instanceof Type) { + $type = $enterResult; + } + + if ($enterResult !== TypeVisitor::DONT_TRAVERSE_CHILDREN) { + $propertiesWithNodes = $type->nodes(); + + foreach ($propertiesWithNodes as $propertyWithNode) { + $node = $type->$propertyWithNode; + if (! is_array($node)) { + $type->$propertyWithNode = $this->traverse($node); + } else { + foreach ($node as $index => $item) { + $type->$propertyWithNode[$index] = $this->traverse($item); + } + } + } + } + + $leaveResult = $this->leaveType($type); + if ($leaveResult instanceof Type) { + $type = $leaveResult; + } + + return $type; + } + + private function enterType(Type $type): Type|int|null + { + $result = null; + $resultType = $type; + foreach ($this->visitors as $visitor) { + $enterResult = $visitor->enter($resultType); + + if ($enterResult === TypeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + return $enterResult; + } + + if ($enterResult instanceof Type) { + $resultType = $enterResult; + } + + $result = $enterResult; + } + + return $resultType === $type ? $result : $resultType; + } + + private function leaveType(Type $type): Type|int|null + { + $result = null; + $resultType = $type; + foreach ($this->visitors as $visitor) { + $leaveResult = $visitor->leave($resultType); + + if ($leaveResult instanceof Type) { + $resultType = $leaveResult; + } + + $result = $leaveResult; + } + + return $resultType === $type ? $result : $resultType; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypeVisitor.php b/vendor/dedoc/scramble/src/Support/Type/TypeVisitor.php new file mode 100644 index 00000000..507e02ff --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypeVisitor.php @@ -0,0 +1,15 @@ +visitedNodesWeakMap = new WeakMap; + } + + public function first(Type $type, callable $lookup): ?Type + { + return RecursionGuard::run($type, function () use ($type, $lookup) { + if ($lookup($type)) { + return $type; + } + + $publicChildren = collect($type->nodes()) + ->flatMap(fn ($node) => is_array($type->$node) ? array_values($type->$node) : [$type->$node]); + + foreach ($publicChildren as $child) { + if ($child && $foundType = $this->first($child, $lookup)) { + return $foundType; + } + } + + return null; + }, fn () => null); + } + + /** + * @param callable(Type): bool $lookup + * @return Type[] + */ + public function findAll(Type $type, callable $lookup): array + { + return RecursionGuard::run($type, function () use ($type, $lookup) { + $foundTypes = []; + + if ($lookup($type)) { + $foundTypes[] = $type; + } + + $publicChildren = collect($type->nodes()) + ->flatMap(fn ($node) => is_array($type->$node) ? array_values($type->$node) : [$type->$node]); + + foreach ($publicChildren as $child) { + if ($child && $allFoundTypes = $this->findAll($child, $lookup)) { + $foundTypes = [...$foundTypes, ...$allFoundTypes]; + } + } + + return $foundTypes; + }, fn () => []); + } + + public function replace(Type $subject, callable $replacer): Type + { + if ($replaced = $replacer($subject)) { + return $replaced; + } + + if (in_array($subject, $this->visitedNodes, strict: true)) { + return $subject; + } + $this->visitedNodes[] = $subject; + + $propertiesWithNodes = $subject->nodes(); + + foreach ($propertiesWithNodes as $propertyWithNode) { + $node = $subject->$propertyWithNode; + if (! $node) { + continue; + } + if (! is_array($node)) { + $subject->$propertyWithNode = $this->replace($node, $replacer); + } else { + foreach ($node as $index => $item) { + $subject->$propertyWithNode[$index] = $this->replace($item, $replacer); + } + } + } + + return $subject; + } + + /** + * Maps a type to a new type. This method is not mutating passed type, hence + * the callback mush always return a type. + * + * @param callable(Type): Type $cb + * @param (callable(Type): (string[]))|null $nodesNamesGetter + */ + public function map( + Type $subject, + callable $cb, + ?callable $nodesNamesGetter = null, + bool $preserveAttributes = true, + ): Type { + $nodesNamesGetter ??= fn (Type $t) => $t->nodes(); + + if ($this->visitedNodesWeakMap->offsetExists($subject)) { + return $this->visitedNodesWeakMap->offsetGet($subject); + } + + $subNodes = $nodesNamesGetter($subject); + + $subjectToMap = $subNodes ? clone $subject : $subject; + + $mappedSubject = $cb($subjectToMap); + + if ($preserveAttributes) { + $mappedSubject->mergeAttributes($subject->attributes()); + } + + if ($subjectToMap !== $mappedSubject) { // type was changed during the mapping + return $mappedSubject; + } + + $this->visitedNodesWeakMap->offsetSet($subject, $mappedSubject); + + foreach ($nodesNamesGetter($mappedSubject) as $propertyWithNode) { + $node = $mappedSubject->$propertyWithNode; + if (! is_array($node)) { + $mappedSubject->$propertyWithNode = $this->map($node, $cb, $nodesNamesGetter, $preserveAttributes); + } else { + foreach ($node as $index => $item) { + $mappedSubject->$propertyWithNode[$index] = $this->map($item, $cb, $nodesNamesGetter, $preserveAttributes); + } + } + } + + return $mappedSubject; + } + + /** + * @param callable(Type): void $cb + * @param (callable(Type): (string[]))|null $nodesNamesGetter + */ + public function walk( + Type $subject, + callable $cb, + ?callable $nodesNamesGetter = null, + ): void { + $nodesNamesGetter ??= fn (Type $t) => $t->nodes(); + + if ($this->visitedNodesWeakMap->offsetExists($subject)) { + return; + } + + $cb($subject); + + $this->visitedNodesWeakMap->offsetSet($subject, $subject); + + foreach ($nodesNamesGetter($subject) as $propertyWithNode) { + /** @var Type[]|Type $node */ + $node = $subject->$propertyWithNode; + if (! is_array($node)) { + $this->walk($node, $cb, $nodesNamesGetter); + } else { + foreach ($node as $index => $item) { + $this->walk($item, $cb, $nodesNamesGetter); + } + } + } + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/TypeWidener.php b/vendor/dedoc/scramble/src/Support/Type/TypeWidener.php new file mode 100644 index 00000000..646cc858 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/TypeWidener.php @@ -0,0 +1,111 @@ +widenPair($a, $b) ?: $this->widenPair($b, $a); + + if ($merged !== null) { + unset($items[$i], $items[$j]); + $items[] = $merged; + $items = array_values($items); + $changed = true; + + continue 3; + } + } + } + } + + return Union::wrap($items); + } + + private function widenPair(Type $a, Type $b): ?Type + { + // mixed|* -> mixed + if ($a instanceof MixedType) { + return new MixedType; + } + + // true|false -> bool + if ( + ($a instanceof LiteralBooleanType && $a->value === true) + && ($b instanceof LiteralBooleanType && $b->value === false) + ) { + return new BooleanType; + } + + // bool|false or bool|true -> bool + if ( + ($a instanceof BooleanType && ! $a instanceof LiteralBooleanType) + && $b instanceof LiteralBooleanType + ) { + return new BooleanType; + } + + // int|42 -> int + if ( + ($a instanceof IntegerType && ! $a instanceof LiteralIntegerType) + && $b instanceof LiteralIntegerType + ) { + return new IntegerType; + } + + // float|42 -> float (?) + if ( + ($a instanceof FloatType && ! $a instanceof LiteralFloatType) + && ($b instanceof LiteralFloatType || $b instanceof LiteralIntegerType) + ) { + return new FloatType; + } + + // string|'wow' -> string + if ( + ($a instanceof StringType && ! $a instanceof LiteralStringType) + && $b instanceof LiteralStringType + ) { + return new StringType; + } + + // list{}|array -> array + if ($a instanceof KeyedArrayType && count($a->items) === 0 && $b instanceof ArrayType) { + return $b; + } + + if ( + $a instanceof Generic + && $b instanceof Generic + && $a->name === $b->name + && $a->isInstanceOf(Enumerable::class) + ) { + return new Generic($a->name, [ + (new Union([$a->templateTypes[0] ?? new UnknownType, $b->templateTypes[0] ?? new UnknownType]))->widen(), + (new Union([$a->templateTypes[1] ?? new UnknownType, $b->templateTypes[1] ?? new UnknownType]))->widen(), + ]); + } + + return null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/Union.php b/vendor/dedoc/scramble/src/Support/Type/Union.php new file mode 100644 index 00000000..850b7aab --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/Union.php @@ -0,0 +1,110 @@ +types) !== count($type->types)) { + return false; + } + + return collect($this->types)->every(fn (Type $t, $i) => $t->isSame($type->types[$i])); + } + + public function widen(): Type + { + return app(TypeWidener::class)->widen($this->types)->mergeAttributes($this->attributes()); + } + + public function accepts(Type $otherType): bool + { + // If the other type is also a union, all of its subtypes must be accepted + if ($otherType instanceof Union) { + foreach ($otherType->types as $inner) { + if (! $this->accepts($inner)) { + return false; + } + } + + return true; + } + + foreach ($this->types as $type) { + if ($type->accepts($otherType)) { + return true; + } + } + + return false; + } + + public function acceptedBy(Type $otherType): bool + { + foreach ($this->types as $type) { + if (! $otherType->accepts($type)) { + return false; + } + } + + return true; + } + + /** + * @param Type|Type[] $types + */ + public static function wrap(...$types): Type + { + $types = Arr::wrap(...$types); + + $uniqueTypes = []; + + foreach (array_values($types) as $type) { + $alreadyAdded = false; + + foreach ($uniqueTypes as $existingType) { + if ($type->isSame($existingType)) { + $alreadyAdded = true; + + break; + } + } + + if (! $alreadyAdded) { + $uniqueTypes[] = $type; + } + } + + if (! count($uniqueTypes)) { + return new VoidType; + } + + if (count($uniqueTypes) === 1) { + return $uniqueTypes[0]; + } + + return new self($uniqueTypes); + } + + public function toString(): string + { + return implode('|', array_map(fn ($t) => $t->toString(), $this->types)); + } +} diff --git a/vendor/dedoc/scramble/src/Support/Type/UnknownType.php b/vendor/dedoc/scramble/src/Support/Type/UnknownType.php new file mode 100644 index 00000000..8562985e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/Type/UnknownType.php @@ -0,0 +1,25 @@ +setAttribute('docNode', PhpDoc::parse('/** Base path for paginator generated URLs. */')); + }), + tap(new ArrayItemType_('per_page', new IntegerRangeType(min: 0)), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** Number of items shown per page. */')); + }), + tap(new ArrayItemType_('next_cursor', new Union([new StringType, new NullType])), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** The "cursor" that points to the next set of items. */')); + }), + tap(new ArrayItemType_('next_page_url', new Union([new StringType, new NullType])), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** @format uri */')); + }), + tap(new ArrayItemType_('prev_cursor', new Union([new StringType, new NullType])), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** The "cursor" that points to the previous set of items. */')); + }), + tap(new ArrayItemType_('prev_page_url', new Union([new StringType, new NullType])), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** @format uri */')); + }), + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeManagers/JsonApiResourceTypeManager.php b/vendor/dedoc/scramble/src/Support/TypeManagers/JsonApiResourceTypeManager.php new file mode 100644 index 00000000..5291cdca --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeManagers/JsonApiResourceTypeManager.php @@ -0,0 +1,75 @@ + 'TResource', + 'additional' => 'TAdditional', + 'jsonApiLinks' => 'TJsonApiLinks', + 'jsonApiMeta' => 'TJsonApiMeta', + 'usesRequestQueryString' => 'TUsesRequestQueryString', + 'includesPreviouslyLoadedRelationships' => 'TIncludesPreviouslyLoadedRelationships', + 'loadedRelationshipsMap' => 'TLoadedRelationshipsMap', + 'loadedRelationshipIdentifiers' => 'TLoadedRelationshipIdentifiers', + ]; + + public function createType( + string $name, + Type $resource = new MixedType, + Type $additional = new MixedType, + Type $jsonApiLinks = new KeyedArrayType, + Type $jsonApiMeta = new KeyedArrayType, + Type $usesRequestQueryString = new LiteralBooleanType(true), + Type $includesPreviouslyLoadedRelationships = new LiteralBooleanType(false), + Type $loadedRelationshipsMap = new MixedType, + Type $loadedRelationshipIdentifiers = new KeyedArrayType, + ): Generic { + return new Generic($name, [ + /* TResource */ $resource, + /* TAdditional */ $additional, + /* TJsonApiLinks */ $jsonApiLinks, + /* TJsonApiMeta */ $jsonApiMeta, + /* TUsesRequestQueryString */ $usesRequestQueryString, + /* TIncludesPreviouslyLoadedRelationships */ $includesPreviouslyLoadedRelationships, + /* TLoadedRelationshipsMap */ $loadedRelationshipsMap, + /* TLoadedRelationshipIdentifiers */ $loadedRelationshipIdentifiers, + ]); + } + + public function normalizeType(ObjectType $type): Generic + { + if ($type instanceof Generic) { + return $type; + } + + $inferredCreationType = ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new NewCallReferenceType($type->name, []), + ); + + if ($inferredCreationType instanceof Generic) { + return $inferredCreationType; + } + + return $this->createType(name: $type->name); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeManagers/LengthAwarePaginatorTypeManager.php b/vendor/dedoc/scramble/src/Support/TypeManagers/LengthAwarePaginatorTypeManager.php new file mode 100644 index 00000000..d66abc45 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeManagers/LengthAwarePaginatorTypeManager.php @@ -0,0 +1,56 @@ +setAttribute('docNode', PhpDoc::parse('/** Generated paginator links. */')); + $linkObject = new KeyedArrayType([ + new ArrayItemType_('url', new Union([new StringType, new NullType])), + new ArrayItemType_('label', new StringType), + new ArrayItemType_('active', new BooleanType), + ]); + $t->value = new ArrayType($linkObject); + }), + new ArrayItemType_('next_page_url', new Union([new StringType, new NullType])), + tap(new ArrayItemType_('path', new Union([new StringType, new NullType])), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** Base path for paginator generated URLs. */')); + }), + tap(new ArrayItemType_('per_page', new IntegerRangeType(min: 0)), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** Number of items shown per page. */')); + }), + new ArrayItemType_('prev_page_url', new Union([new StringType, new NullType])), + tap(new ArrayItemType_('to', new Union([new IntegerRangeType(min: 1), new NullType])), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** Number of the last item in the slice. */')); + }), + tap(new ArrayItemType_('total', new IntegerRangeType(min: 0)), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** Total number of items being paginated. */')); + }), + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeManagers/ManagesProperties.php b/vendor/dedoc/scramble/src/Support/TypeManagers/ManagesProperties.php new file mode 100644 index 00000000..35a75852 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeManagers/ManagesProperties.php @@ -0,0 +1,38 @@ +templateTypes[$indexOfPropertyTemplateTypeInGeneric] ?? null; + } + + /** + * @param Type[] $propertiesTypes + */ + public function setPropertiesTypes(Generic $type, array $propertiesTypes): Generic + { + foreach ($propertiesTypes as $propertyName => $propertyType) { + $indexOfPropertyTemplateTypeInGeneric = array_search(self::ORDERED_PROPERTY_TO_TEMPLATE_MAP[$propertyName] ?? null, array_values(self::ORDERED_PROPERTY_TO_TEMPLATE_MAP)); + + if ($indexOfPropertyTemplateTypeInGeneric === false) { + continue; + } + + $type->templateTypes[$indexOfPropertyTemplateTypeInGeneric] = $propertyType; + } + + return $type; + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeManagers/PaginatorTypeManager.php b/vendor/dedoc/scramble/src/Support/TypeManagers/PaginatorTypeManager.php new file mode 100644 index 00000000..ec3e15e0 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeManagers/PaginatorTypeManager.php @@ -0,0 +1,40 @@ +setAttribute('docNode', PhpDoc::parse('/** Base path for paginator generated URLs. */')); + }), + tap(new ArrayItemType_('per_page', new IntegerRangeType(min: 0)), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** Number of items shown per page. */')); + }), + new ArrayItemType_('prev_page_url', new Union([new StringType, new NullType])), + tap(new ArrayItemType_('to', new Union([new IntegerRangeType(min: 1), new NullType])), function (ArrayItemType_ $t) { + $t->setAttribute('docNode', PhpDoc::parse('/** Number of the last item in the slice. */')); + }), + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeManagers/ResourceCollectionTypeManager.php b/vendor/dedoc/scramble/src/Support/TypeManagers/ResourceCollectionTypeManager.php new file mode 100644 index 00000000..5323b833 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeManagers/ResourceCollectionTypeManager.php @@ -0,0 +1,126 @@ +getInferredCollectedType()) { + return $inferredCollectedType; + } + + if ($collectedTypeFromProperty = $this->guessCollectedTypeResourceName()) { + return $collectedTypeFromProperty; + } + + return new UnknownType; + } + + private function getInferredCollectedType(): ?Generic + { + $collectsClassNameType = $this->type->templateTypes[/* TCollects */ 2] ?? null; + + if (! $collectsClassNameType instanceof ObjectType) { + return $this->getCollectedTypeFromManualAnnotation(); + } + + return new Generic($collectsClassNameType->name, [new UnknownType]); + } + + private function getCollectedTypeFromManualAnnotation(): ?Generic + { + $type = (new TypeWalker)->first( + new Union([ + $this->type->templateTypes[0] ?? new UnknownType, + $this->type->templateTypes[1] ?? new UnknownType, + ]), + fn (Type $t) => $t->isInstanceOf(JsonResource::class), + ); + + if (! $type instanceof ObjectType) { + return null; + } + + if (! $type instanceof Generic) { + return new Generic($type->name); + } + + return $type; + } + + private function guessCollectedTypeResourceName(): ?Generic + { + if (! $classDefinition = $this->index->getClass($this->type->name)) { + return null; + } + + $collectsPropertyDefaultType = $classDefinition->getPropertyDefinition('collects')?->defaultType; + if ( + $collectsPropertyDefaultType instanceof GenericClassStringType + && $collectsPropertyDefaultType->type instanceof ObjectType + ) { + return new Generic($collectsPropertyDefaultType->type->name, [new UnknownType]); + } + + if ( + str_ends_with($classDefinition->name, 'Collection') && + (class_exists($class = Str::replaceLast('Collection', '', $classDefinition->name)) || + class_exists($class = Str::replaceLast('Collection', 'Resource', $classDefinition->name))) + ) { + return new Generic($class, [new UnknownType]); + } + + return null; + } + + public static function make(ObjectType $type): self + { + return new self( + $type instanceof Generic ? $type : new Generic($type->name), + app(Index::class), + ); + } + + public function getResponseType(): Generic + { + if ($this->isPaginatedResource()) { + return new Generic(PaginatedResourceResponse::class, [$this->type]); + } + + return new Generic(ResourceResponse::class, [$this->type]); + } + + private function isPaginatedResource(): bool + { + $resourceType = $this->type->templateTypes[/* TResource */ 0] ?? null; + + if (! $resourceType instanceof ObjectType) { + return false; + } + + return $resourceType->isInstanceOf(AbstractPaginator::class) + || $resourceType->isInstanceOf(AbstractCursorPaginator::class); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/AnonymousResourceCollectionTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/AnonymousResourceCollectionTypeToSchema.php new file mode 100644 index 00000000..57ed7711 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/AnonymousResourceCollectionTypeToSchema.php @@ -0,0 +1,62 @@ +isInstanceOf(AnonymousResourceCollection::class) + && count($type->templateTypes) > 0; + } + + /** + * @param Generic $type + */ + public function toSchema(Type $type): ?OpenApiType + { + return (new ResourceCollectionTypeToSchema( + $this->infer, + $this->openApiTransformer, + $this->components, + $this->openApiContext, + ))->toSchema($type); + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type): ?Response + { + return (new ResourceCollectionTypeToSchema( + $this->infer, + $this->openApiTransformer, + $this->components, + $this->openApiContext, + ))->toResponse($type); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ArrayableToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ArrayableToSchema.php new file mode 100644 index 00000000..8609ad93 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ArrayableToSchema.php @@ -0,0 +1,68 @@ +isInstanceOf(Arrayable::class) + && ! $type->isInstanceOf(Collection::class) // prevents collections being documented in schemas + && ((new \ReflectionClass($type->name))->isInstantiable()); // @phpstan-ignore argument.type + } + + /** + * @param ObjectType $type + */ + public function toSchema(Type $type): OpenApiSchema + { + $this->infer->analyzeClass($type->name); + + $toArrayReturnType = $type->getMethodReturnType('toArray'); + + return $this->openApiTransformer->transform($toArrayReturnType); + } + + /** + * @param ObjectType $type + */ + public function toResponse(Type $type): ?Response + { + return Response::make(200) + ->setContent( + 'application/json', + Schema::fromType($this->openApiTransformer->transform($type)), + ) + ->setDescription('`'.$this->openApiContext->references->schemas->uniqueName($type->name).'`'); + } + + public function reference(ObjectType $type): Reference + { + return ClassBasedReference::create('schemas', $type->name, $this->components); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/BinaryFileResponseToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/BinaryFileResponseToSchema.php new file mode 100644 index 00000000..c6a61891 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/BinaryFileResponseToSchema.php @@ -0,0 +1,148 @@ +isInstanceOf(BinaryFileResponse::class) + && count($type->templateTypes) === 4; + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type): ?Response + { + if (! $status = $this->getStatus($type)) { + return null; + } + + return Response::make($status) + ->setContent( + $contentType = $this->getContentType($type), + Schema::fromType($this->buildType($contentType)), + ) + ->setHeaders($this->makeHeaders($type)); + } + + /** + * @return array + */ + private function makeHeaders(Generic $type): array + { + $headers = []; + + if ($contentDisposition = $type->getAttribute('contentDisposition')) { + $headers['Content-Disposition'] = new Header( + required: true, + schema: Schema::fromType(new StringType), + example: $contentDisposition, + ); + } + + return $headers; + } + + private function getStatus(Generic $type): ?int + { + $statusType = $type->templateTypes[1 /* TStatus */] ?? null; + + if (! $statusType instanceof LiteralIntegerType) { + return null; + } + + return $statusType->value; + } + + private function getContentType(Generic $type): string + { + $attributeMimeType = is_string($attributeMimeType = $type->getAttribute('mimeType')) + ? $attributeMimeType + : null; + + return $this->guessMimeTypeFromHeaders($type->templateTypes[2 /* THeaders */]) + ?: $attributeMimeType + ?: 'application/octet-stream'; + } + + private function guessMimeTypeFromHeaders(Type $headersType): ?string + { + $stringLiteralContentTypeHeader = $headersType instanceof KeyedArrayType + ? collect($headersType->items) + ->first(function (ArrayItemType_ $t) { + return is_string($t->key) + && Str::lower($t->key) === 'content-type' + && $t->value instanceof LiteralStringType; + }) + ?->value + : null; + + if ($stringLiteralContentTypeHeader instanceof LiteralStringType) { + return $stringLiteralContentTypeHeader->value; + } + + return null; + } + + private function buildType(string $contentType): OpenApiType + { + $type = new StringType; + + if (! in_array($contentType, static::$nonBinaryMimeTypes)) { + $type->format('binary'); + } + + return $type; + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CarbonInterfaceToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CarbonInterfaceToSchema.php new file mode 100644 index 00000000..1ab24078 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CarbonInterfaceToSchema.php @@ -0,0 +1,21 @@ +isInstanceOf(CarbonInterface::class); + } + + public function toSchema(Type $type) + { + return (new StringType)->format('date-time'); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CollectionToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CollectionToSchema.php new file mode 100644 index 00000000..5523bd67 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CollectionToSchema.php @@ -0,0 +1,31 @@ +isInstanceOf(Collection::class); + } + + /** + * @param ObjectType $type + */ + public function toSchema(Type $type) + { + if ($type instanceof Generic && array_key_exists(1, $type->templateTypes)) { + return $this->openApiTransformer->transform(new ArrayType(value: $type->templateTypes[1]/* TValue */)); + } + + return new OpenApiObjectType; + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CursorPaginatorTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CursorPaginatorTypeToSchema.php new file mode 100644 index 00000000..e9151a25 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/CursorPaginatorTypeToSchema.php @@ -0,0 +1,65 @@ +name === CursorPaginator::class + && $this->getCollectedType($type); + } + + /** + * @param Generic $type + */ + public function toSchema(Type $type) + { + if (! $collectedType = $this->getCollectedType($type)) { + return null; + } + + $paginatorArray = (new CursorPaginatorTypeManager)->getToArrayType(new ArrayType($collectedType)); + + return $this->openApiTransformer->transform($paginatorArray); + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type) + { + if (! $collectedType = $this->getCollectedType($type)) { + return null; + } + + return Response::make(200) + ->setContent('application/json', Schema::fromType($this->openApiTransformer->transform($type))) + ->setDescription('Paginated set of `'.$this->openApiContext->references->schemas->uniqueName($collectedType->name).'`'); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/EloquentCollectionToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/EloquentCollectionToSchema.php new file mode 100644 index 00000000..39dfed48 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/EloquentCollectionToSchema.php @@ -0,0 +1,34 @@ +templateTypes) === 1 + && $type->isInstanceOf(Collection::class) + && $type->templateTypes[0]->isInstanceOf(Model::class); + } + + /** + * @param Generic $type + */ + public function toSchema(Type $type) + { + $type = new ArrayType(value: $type->templateTypes[0]); + + return $this->openApiTransformer->transform($type); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/EnumToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/EnumToSchema.php new file mode 100644 index 00000000..6d69d8df --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/EnumToSchema.php @@ -0,0 +1,177 @@ +name); + } + + /** + * @param ObjectType $type + */ + public function toSchema(Type $type): OpenApi\Type + { + $name = $type->name; + + if (! isset($name::cases()[0]->value)) { // only backed enums support + return new UnknownType; + } + + $values = array_map(fn ($s) => $s->value, $name::cases()); + + $schemaType = is_string($values[0]) ? new StringType : new IntegerType; + $schemaType->enum($values); + + $this->addEnumCasesDescriptions($type, $schemaType); + + $this->addEnumDescription($type, $schemaType); + + $this->addEnumNames($type, $schemaType); + + return $schemaType; + } + + public function reference(ObjectType $type): Reference + { + return ClassBasedReference::create('schemas', $type->name, $this->components); + } + + protected function addEnumCasesDescriptions(ObjectType $type, OpenApi\Type $schemaType): void + { + $enumReflection = new ReflectionEnum($type->name); // @phpstan-ignore argument.type + + $cases = collect(array_filter( + $enumReflection->getCases(), + fn ($case) => $case instanceof ReflectionEnumBackedCase, + )) + ->keyBy(fn (ReflectionEnumBackedCase $case): int|string => $case->getBackingValue()) + ->map(function (ReflectionEnumBackedCase $case) { + $doc = PhpDoc::parse($case->getDocComment() ?: '/** */'); + + return trim(Str::replace("\n", ' ', $doc->getAttribute('summary').' '.$doc->getAttribute('description'))); // @phpstan-ignore binaryOp.invalid, binaryOp.invalid + }); + + if (! $cases->some(fn ($description) => (bool) $description)) { + return; + } + + $enumDescriptionStrategy = config('scramble.enum_cases_description_strategy'); + + if ($enumDescriptionStrategy === 'description') { + $this->handleDescriptionEnumStrategy($schemaType, $cases); + + return; + } + + if ($enumDescriptionStrategy === 'extension') { + $this->handleExtensionEnumStrategy($schemaType, $cases); + + return; + } + } + + protected function addEnumDescription(ObjectType $type, OpenApi\Type $schemaType): void + { + $enumReflection = new ReflectionEnum($type->name); // @phpstan-ignore argument.type + + $doc = PhpDoc::parse($enumReflection->getDocComment() ?: '/** */'); + $description = trim(Str::replace("\n", ' ', $doc->getAttribute('summary').' '.$doc->getAttribute('description'))); // @phpstan-ignore binaryOp.invalid, binaryOp.invalid + + if (! $description) { + return; + } + + $schemaType->setDescription($description."\n".$schemaType->description); + + /* + * Cases description are stored in attribute due to if enum is used as a property in some object, + * users may override enum class description and some way is needed + */ + $schemaType->setAttribute('description', $description); + } + + protected function addEnumNames(ObjectType $type, OpenApi\Type $schemaType): void + { + /** @var 'name'|'varnames'|null $enumNameStrategy */ + $enumNameStrategy = config('scramble.enum_cases_names_strategy'); + + if (! $enumNameStrategy) { + return; + } + + $enumReflection = new ReflectionEnum($type->name); // @phpstan-ignore argument.type + + $nameCases = collect($enumReflection->getCases()) + ->filter(fn (ReflectionEnumUnitCase $case) => $case instanceof ReflectionEnumBackedCase) + ->keyBy(fn (ReflectionEnumBackedCase $case): int|string => $case->getBackingValue()) + ->map(fn (ReflectionEnumBackedCase $case) => $case->getName()); + + $this->handleEnumNames($schemaType, $nameCases, $enumNameStrategy); + } + + /** + * @param Collection $cases + */ + protected function handleDescriptionEnumStrategy(OpenApi\Type $schema, Collection $cases): void + { + $description = $cases + ->map(fn ($description, $value) => "| `{$value}`
{$description} |") + ->prepend('|---|') + ->prepend('| |') + ->join("\n"); + + $schema->setDescription($description); + + /* + * Cases description are stored in attribute due to if enum is used as a property in some object, + * users may override enum class description and some way is needed + */ + $schema->setAttribute('casesDescription', $description); + } + + /** + * @param Collection $cases + */ + protected function handleExtensionEnumStrategy(OpenApi\Type $schema, Collection $cases): void + { + $schema->setExtensionProperty( + 'enumDescriptions', + $cases->all() + ); + } + + /** + * @param 'name'|'varnames' $strategy + * @param Collection $cases + */ + protected function handleEnumNames(OpenApi\Type $schema, Collection $cases, string $strategy): void + { + $extensionKey = $strategy === 'varnames' ? 'enum-varnames' : 'enumNames'; + + $schema->setExtensionProperty( + $extensionKey, + $cases->values()->all() + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/FlattensMergeValues.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/FlattensMergeValues.php new file mode 100644 index 00000000..d69ccf25 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/FlattensMergeValues.php @@ -0,0 +1,151 @@ +flatMap(function (ArrayItemType_ $item) { + if ($item->value instanceof KeyedArrayType) { + $item->value->items = $this->flattenMergeValues($item->value->items); + $item->value->isList = KeyedArrayType::checkIsList($item->value->items); + + return [$item]; + } + + if ($item->value->isInstanceOf(JsonResource::class)) { + $resource = $this->getResourceType($item->value); + + if ($resource->isInstanceOf(MissingValue::class)) { + return []; + } + + if ( + $resource instanceof Union + && (new TypeWalker)->first($resource, fn (Type $t) => $t->isInstanceOf(MissingValue::class)) + ) { + $item->isOptional = true; + + return [$item]; + } + } + + if ($this->isUnionWithMissingValue($item->value)) { + $newType = $this->removeMissingValueFromUnion($item->value); + + if ($newType instanceof VoidType) { + return []; + } + + $item->isOptional = true; + + $item->value = $newType; + + return $this->flattenMergeValues([$item]); + } + + $item->value = $this->flattenNestedKeyedArrayValues($item->value); + + if ( + $item->value instanceof Generic + && $item->value->isInstanceOf(MergeValue::class) + ) { + $arrayToMerge = $item->value->templateTypes[1]; + + // Second generic argument of the `MergeValue` class must be a keyed array. + // Otherwise, we ignore it from the resulting array. + if (! $arrayToMerge instanceof KeyedArrayType) { + return []; + } + + $arrayToMergeItems = $this->flattenMergeValues($arrayToMerge->items); + + $mergingArrayValuesShouldBeRequired = $item->value->templateTypes[0] instanceof LiteralBooleanType + && $item->value->templateTypes[0]->value === true; + + if (! $mergingArrayValuesShouldBeRequired || $item->isOptional) { + foreach ($arrayToMergeItems as $mergingItem) { + $mergingItem->isOptional = true; + } + } + + return $arrayToMergeItems; + } + + return [$item]; + }) + ->values() + ->all(); + } + + private function flattenNestedKeyedArrayValues(Type $type): Type + { + return (new TypeWalker)->replace($type, function (Type $t) { + if (! $t instanceof KeyedArrayType) { + return null; + } + + $t->items = $this->flattenMergeValues($t->items); + $t->isList = KeyedArrayType::checkIsList($t->items); + + return $t; + }); + } + + private function removeMissingValueFromUnion(Union $type): Type + { + return Union::wrap(array_values( + array_filter($type->types, fn (Type $t) => ! $t->isInstanceOf(MissingValue::class)) + )); + } + + /** + * @phpstan-assert-if-true Union $type + */ + private function isUnionWithMissingValue(Type $type): bool + { + return $type instanceof Union + && (bool) array_filter($type->types, fn (Type $t) => $t->isInstanceOf(MissingValue::class)); + } + + /** + * @todo Maybe does not belong here as simply provides a knowledge about locating a type in a json resource generics. + * This is something similar to Scramble's PRO wrap handling logic. + */ + private function getResourceType(Type $type): Type + { + if (! $type instanceof Generic) { + return new UnknownType; + } + + if ($type->isInstanceOf(AnonymousResourceCollection::class)) { + return $type->templateTypes[0] ?? new UnknownType; + } + + if ($type->isInstanceOf(JsonResource::class)) { + return $type->templateTypes[0]; + } + + return new UnknownType; + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/HandlesJsonApiResourceResponse.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/HandlesJsonApiResourceResponse.php new file mode 100644 index 00000000..da12a717 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/HandlesJsonApiResourceResponse.php @@ -0,0 +1,90 @@ +isUserDefinedWithMethod($type)) { + return parent::getWithType($type); + } + + if (! $collectedResource = $this->getResourceType($type)) { + return null; + } + + $items = []; + + if ($includedResources = $this->getIncludedResources($collectedResource)) { + $items[] = new InferType\ArrayItemType_('included', $includedResources, isOptional: true); + } + + if ($jsonApiObject = $this->getJsonApiObject()) { + $items[] = new InferType\ArrayItemType_('jsonapi', $jsonApiObject); + } + + return $items ? new InferType\KeyedArrayType($items) : null; + } + + protected function getJsonApiObject(): ?InferType\KeyedArrayType + { + $information = JsonApiResource::$jsonApiInformation; + + if (! $information) { + return null; + } + + $type = TypeHelper::createTypeFromValue($information); + + return $type instanceof InferType\KeyedArrayType ? $type : null; + } + + protected function isUserDefinedWithMethod(ObjectType $resource): bool + { + $resourceDefinition = $this->infer->index->getClass($resource->name); + + $withDefiningClassName = $resourceDefinition?->getMethod('with')?->definingClassName; + + return $withDefiningClassName !== JsonApiResource::class + && $withDefiningClassName !== JsonApiAnonymousResourceCollection::class; + } + + protected function getIncludedResources(ObjectType $resource): ?InferType\ArrayType + { + $relationships = ReflectionJsonApiResource::createForClass($resource->name)->getNestedRelationshipItems( + $this->openApiContext->config->jsonApi->maxRelationshipDepth(), + ); + + if (! $relationships) { + return null; + } + + $included = array_map(fn ($r) => $r->resourceType, $relationships); + + return new InferType\ArrayType(InferType\Union::wrap($included)); + } + + public function getResourceType(InferType\ObjectType $type): ?ObjectType + { + if ($type->isInstanceOf(JsonApiAnonymousResourceCollection::class)) { + $collectedType = ResourceCollectionTypeManager::make($type)->getCollectedType(); + + return $collectedType instanceof Generic ? $collectedType : null; + } + + return $type; + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiAnonymousCollectionTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiAnonymousCollectionTypeToSchema.php new file mode 100644 index 00000000..56f3bf0b --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiAnonymousCollectionTypeToSchema.php @@ -0,0 +1,34 @@ +isInstanceOf(JsonApiAnonymousResourceCollection::class); + } + + /** + * @see JsonApiResourceCollectionMethodReturnTypeExtension::getMethodReturnType() + */ + protected function getResponseType(ObjectType $type): Type + { + return ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new MethodCallReferenceType($type, 'toResponse', []) + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiPaginatedResourceResponseToSchemaExtension.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiPaginatedResourceResponseToSchemaExtension.php new file mode 100644 index 00000000..a53ed7cc --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiPaginatedResourceResponseToSchemaExtension.php @@ -0,0 +1,17 @@ +templateTypes[0]->isInstanceOf(JsonApiAnonymousResourceCollection::class); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceResponseToSchemaExtension.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceResponseToSchemaExtension.php new file mode 100644 index 00000000..368c2d3c --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceResponseToSchemaExtension.php @@ -0,0 +1,21 @@ +templateTypes[0]->isInstanceOf(JsonApiResource::class) + || $type->templateTypes[0]->isInstanceOf(JsonApiAnonymousResourceCollection::class) + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceTypeToSchema.php new file mode 100644 index 00000000..26a3bdf1 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonApiResourceTypeToSchema.php @@ -0,0 +1,151 @@ +isInstanceOf(JsonApiResource::class); + } + + /** + * @param ObjectType $type + */ + public function toSchema(Type $type): OpenApiType\ObjectType + { + $type = app(JsonApiResourceTypeManager::class)->normalizeType($type); + + $reflection = ReflectionJsonApiResource::createForClass($type->name); + + $schema = (new OpenApiType\ObjectType) + ->addProperty( + 'id', + $this->openApiTransformer->transform($reflection->getIdType($type)), + ) + ->addProperty( + 'type', + $this->openApiTransformer->transform($reflection->getTypeType($type)), + ) + ->setRequired(['id', 'type']); + + $this->attachAttributes($schema, $reflection); + $this->attachRelationships($schema, $reflection); + $this->attachLinks($schema, $reflection); + $this->attachMeta($schema, $reflection); + + return $schema; + } + + private function attachAttributes(OpenApiType\ObjectType $schema, ReflectionJsonApiResource $reflection): void + { + if (! $attributes = $reflection->getAttributesType()) { + return; + } + + /* + * Due to sparse fields, all attributes are optional by default. + */ + foreach ($attributes->items as $item) { + if ($item->value instanceof FunctionType) { + $item->value = $item->value->getReturnType(); + } + + $item->isOptional = true; + } + + $schema->addProperty('attributes', $this->openApiTransformer->transform($attributes)); + } + + private function attachRelationships(OpenApiType\ObjectType $schema, ReflectionJsonApiResource $reflection): void + { + $relationshipItems = $reflection->getRelationshipItems(); + + if (! $relationshipItems) { + return; + } + + $items = array_map(function ($relationship) { + $identifierType = $this->buildRelationshipIdentifierType($this->normalizeType($relationship->resourceType)); + + $value = $relationship->isCollection + ? new KeyedArrayType([new ArrayItemType_('data', new ArrayType($identifierType))]) + : new KeyedArrayType([new ArrayItemType_('data', $identifierType)]); + + return tap( + new ArrayItemType_($relationship->name, $value, isOptional: true), + fn (ArrayItemType_ $t) => $t->setAttribute('docNode', $relationship->phpDoc), + ); + }, $relationshipItems); + + $schema->addProperty('relationships', $this->openApiTransformer->transform(new KeyedArrayType($items))); + } + + private function attachLinks(OpenApiType\ObjectType $schema, ReflectionJsonApiResource $reflection): void + { + if (! $links = $reflection->getLinksType()) { + return; + } + + $schema + ->addProperty('links', $this->openApiTransformer->transform($links)) + ->addRequired(['links']); + } + + private function attachMeta(OpenApiType\ObjectType $schema, ReflectionJsonApiResource $reflection): void + { + if (! $meta = $reflection->getMetaType()) { + return; + } + + $schema + ->addProperty('meta', $this->openApiTransformer->transform($meta)) + ->addRequired(['meta']); + } + + private function buildRelationshipIdentifierType(Generic $relationshipType): KeyedArrayType + { + $reflection = ReflectionJsonApiResource::createForClass($relationshipType->name); + + return new KeyedArrayType([ + new ArrayItemType_('id', $reflection->getIdType($relationshipType)), + new ArrayItemType_('type', $reflection->getTypeType($relationshipType)), + ]); + } + + /** + * @see JsonApiResourceMethodReturnTypeExtension::getMethodReturnType() + */ + protected function getResponseType(ObjectType $type): Type + { + return ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new MethodCallReferenceType($type, 'toResponse', []) + ); + } + + public function reference(ObjectType $type): ?Reference + { + return ClassBasedReference::create('schemas', $type->name, $this->components); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonResourceTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonResourceTypeToSchema.php new file mode 100644 index 00000000..1529727f --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/JsonResourceTypeToSchema.php @@ -0,0 +1,95 @@ +isInstanceOf(JsonResource::class) + && ! $type->isInstanceOf(ResourceCollection::class); + } + + /** + * @param ObjectType $type + */ + public function toSchema(Type $type) + { + $type = $this->normalizeType($type); + + $array = ReferenceTypeResolver::getInstance()->resolve( + new GlobalScope, + new MethodCallReferenceType($type, 'toArray', arguments: []), + ); + + // The case when `toArray` is not defined. + if ($array instanceof ArrayType) { + return $this->openApiTransformer->transform($array); + } + + if (! $array instanceof KeyedArrayType) { + return $this->openApiTransformer->transform($array); + } + + $array->items = $this->flattenMergeValues($array->items); + $array->isList = KeyedArrayType::checkIsList($array->items); + + return $this->openApiTransformer->transform($array); + } + + /** + * @param ObjectType $type + */ + public function toResponse(Type $type) + { + return $this->openApiTransformer->toResponse( + $this->getResponseType($type) + ); + } + + protected function normalizeType(ObjectType $type): Generic + { + return $type instanceof Generic ? $type : new Generic($type->name, [new \Dedoc\Scramble\Support\Type\UnknownType]); + } + + protected function getResponseType(ObjectType $type): Type + { + return new Generic(ResourceResponse::class, [$type]); + } + + public function reference(ObjectType $type) + { + return ClassBasedReference::create('schemas', $type->name, $this->components); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/LengthAwarePaginatorTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/LengthAwarePaginatorTypeToSchema.php new file mode 100644 index 00000000..b3c81423 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/LengthAwarePaginatorTypeToSchema.php @@ -0,0 +1,65 @@ +name === LengthAwarePaginator::class + && $this->getCollectedType($type); + } + + /** + * @param Generic $type + */ + public function toSchema(Type $type) + { + if (! $collectedType = $this->getCollectedType($type)) { + return null; + } + + $paginatorArray = (new LengthAwarePaginatorTypeManager)->getToArrayType(new ArrayType($collectedType)); + + return $this->openApiTransformer->transform($paginatorArray); + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type) + { + if (! $collectedType = $this->getCollectedType($type)) { + return null; + } + + return Response::make(200) + ->setContent('application/json', Schema::fromType($this->openApiTransformer->transform($type))) + ->setDescription('Paginated set of `'.$this->openApiContext->references->schemas->uniqueName($collectedType->name).'`'); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/MergesOpenApiObjects.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/MergesOpenApiObjects.php new file mode 100644 index 00000000..ad63ca9e --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/MergesOpenApiObjects.php @@ -0,0 +1,30 @@ +properties as $name => $property) { + if (! array_key_exists($name, $into->properties)) { + $into->addProperty($name, $property); + } else { + if ( + $into->properties[$name] instanceof OpenApiTypes\ObjectType + && $property instanceof OpenApiTypes\ObjectType + ) { + $this->mergeOpenApiObjects($into->properties[$name], $property); + } + } + } + + $into->addRequired($what->required); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ModelToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ModelToSchema.php new file mode 100644 index 00000000..0b0bc9cc --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ModelToSchema.php @@ -0,0 +1,65 @@ +isInstanceOf(Model::class) + && $type->name !== Model::class; + } + + /** + * @param ObjectType $type + */ + public function toSchema(Type $type) + { + return $this->createArrayableExtension()->toSchema($type); + } + + /** + * @param ObjectType $type + */ + public function toResponse(Type $type) + { + return $this->createArrayableExtension()->toResponse($type); + } + + public function reference(ObjectType $type) + { + return $this->createArrayableExtension()->reference($type); + } + + private function createArrayableExtension(): ArrayableToSchema + { + return new ArrayableToSchema( + $this->infer, + $this->openApiTransformer, + $this->components, + $this->openApiContext, + ); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatedResourceResponseTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatedResourceResponseTypeToSchema.php new file mode 100644 index 00000000..c8d73425 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatedResourceResponseTypeToSchema.php @@ -0,0 +1,214 @@ +isInstanceOf(PaginatedResourceResponse::class) + && count($type->templateTypes) >= 1 + && $type->templateTypes[0] instanceof Generic; // Collection + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type): Response + { + $resourceType = $this->getResourceType($type); + + return $this + ->makeResponse($resourceType) + ->setContent('application/json', Schema::fromType($this->wrap( + $this->wrapper($this->getCollectingClassType($type)), + $this->getCollectionSchema($type), + $this->getMergedAdditionalSchema($type), + ))) + ->setDescription($this->getPaginatedDescription($type)); + } + + private function getCollectionSchema(Generic $type): OpenApiType + { + // When transforming response's collection, we can get either a reference, + // or an array of schema (if collection is anonymous). + return $this->openApiTransformer->transform($this->getResourceType($type)); + } + + private function getMergedAdditionalSchema(Generic $type): OpenApiObjectType + { + $resourceType = $this->getResourceType($type); + + $paginationInformation = $this->getPaginatedInformationSchema($type); + $with = $this->getWithSchema($resourceType); + $additional = $this->getAdditionalSchema($resourceType); + + if ($with) { + $this->mergeOpenApiObjects($paginationInformation, $with); + } + + if ($additional) { + $this->mergeOpenApiObjects($paginationInformation, $additional); + } + + return $paginationInformation; + } + + protected function getPaginatedInformationSchema(Generic $type): OpenApiObjectType + { + $resourceType = $this->getResourceType($type); + + $paginationInformation = $this->getDefaultPaginationInformationArray($type); + + if ($this->getPaginationInformationMethod($resourceType)) { + $paginationInformation = ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new MethodCallReferenceType($resourceType, 'paginationInformation', [ + new UnknownType, + $this->getPaginatedArray($type), + $paginationInformation, + ]) + ); + } + + if (! $paginationInformation instanceof KeyedArrayType) { + return new OpenApiObjectType; + } + + $schema = $this->openApiTransformer->transform($paginationInformation); + + return $schema instanceof OpenApiObjectType ? $schema : new OpenApiObjectType; + } + + protected function getPaginationInformationMethod(ObjectType $resourceType): ?FunctionLikeDefinition + { + $resourceTypeDefinition = $this->infer->index->getClass($resourceType->name); + + $method = $resourceTypeDefinition?->getMethod('paginationInformation'); + + if (! $method) { + return null; + } + + return $this->isUserDefinedPaginationInformationMethod($method) ? $method : null; + } + + protected function isUserDefinedPaginationInformationMethod(FunctionLikeDefinition $method): bool + { + return true; + } + + protected function getPaginatedArray(Generic $type): KeyedArrayType + { + $normalizedPaginatorType = $this->getPaginatorType($type); + + $typeManager = match ($normalizedPaginatorType->name) { + Paginator::class => new PaginatorTypeManager, + CursorPaginator::class => new CursorPaginatorTypeManager, + LengthAwarePaginator::class => new LengthAwarePaginatorTypeManager, + default => null, + }; + + if (! $typeManager) { + return new KeyedArrayType; + } + + return $typeManager->getToArrayType(new ArrayType($normalizedPaginatorType->templateTypes[1])); + } + + protected function getDefaultPaginationInformationArray(Generic $type): KeyedArrayType + { + $paginatorArray = $this->getPaginatedArray($type); + + $defaultLinkType = new Union([new StringType, new NullType]); + $excludedMetaKeys = ['data', 'first_page_url', 'last_page_url', 'prev_page_url', 'next_page_url']; + + return new KeyedArrayType([ + new ArrayItemType_('links', new KeyedArrayType([ + new ArrayItemType_('first', $paginatorArray->getItemValueTypeByKey('first_page_url', $defaultLinkType)), + new ArrayItemType_('last', $paginatorArray->getItemValueTypeByKey('last_page_url', $defaultLinkType)), + new ArrayItemType_('prev', $paginatorArray->getItemValueTypeByKey('prev_page_url', $defaultLinkType)), + new ArrayItemType_('next', $paginatorArray->getItemValueTypeByKey('next_page_url', $defaultLinkType)), + ])), + new ArrayItemType_( + 'meta', + new KeyedArrayType(array_values(array_filter( + $paginatorArray->items, + fn (ArrayItemType_ $t) => ! in_array($t->key, $excludedMetaKeys), + ))), + ), + ]); + } + + protected function getPaginatedDescription(Generic $type): string + { + $collectedType = $this->getCollectingClassType($type); + + if ($collectedType instanceof UnknownType) { + return 'Paginated set'; + } + + return 'Paginated set of `'.$this->openApiContext->references->schemas->uniqueName($collectedType->name).'`'; + } + + private function getCollectingClassType(Generic $type): Generic|UnknownType + { + return (new ResourceCollectionTypeManager($this->getResourceType($type), $this->infer->index))->getCollectedType(); + } + + private function getResourceType(Generic $type): Generic + { + $resourceType = $type->templateTypes[/* TData */ 0] ?? null; + + if (! $resourceType instanceof Generic) { + throw new \InvalidArgumentException('Resource type of the response must be Generic, got '.($resourceType ? $resourceType::class : 'null')); + } + + return $resourceType; + } + + private function getPaginatorType(Generic $type): Generic + { + $paginatorType = $this->getResourceType($type)->templateTypes[/* TResource */ 0]; + + if (! $paginatorType instanceof ObjectType) { + throw new LogicException('Paginator type must be an object, got '.$paginatorType->toString()); + } + + return new Generic($paginatorType->name, [ + new IntegerType, + $this->getCollectingClassType($type), + ]); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatorTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatorTypeToSchema.php new file mode 100644 index 00000000..feb55356 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PaginatorTypeToSchema.php @@ -0,0 +1,65 @@ +name === Paginator::class + && $this->getCollectedType($type); + } + + /** + * @param Generic $type + */ + public function toSchema(Type $type) + { + if (! $collectedType = $this->getCollectedType($type)) { + return null; + } + + $paginatorArray = (new PaginatorTypeManager)->getToArrayType(new ArrayType($collectedType)); + + return $this->openApiTransformer->transform($paginatorArray); + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type) + { + if (! $collectedType = $this->getCollectedType($type)) { + return null; + } + + return Response::make(200) + ->setContent('application/json', Schema::fromType($this->openApiTransformer->transform($type))) + ->setDescription('Paginated set of `'.$this->openApiContext->references->schemas->uniqueName($collectedType->name).'`'); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PlainObjectToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PlainObjectToSchema.php new file mode 100644 index 00000000..b65174f5 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/PlainObjectToSchema.php @@ -0,0 +1,154 @@ + */ + private array $cache = []; + + public function shouldHandle(Type $type) + { + $isObject = $type instanceof ObjectType && class_exists($type->name); + + if (! $isObject) { + return false; + } + + if (is_a($type->name, Response::class, true)) { + return false; + } + + if (! (new ReflectionClass($type->name))->isInstantiable()) { + return false; + } + + if (! $this->getSerializedType($type)) { + return false; + } + + return true; + } + + /** + * @param ObjectType $type + */ + public function toSchema(Type $type) + { + if ($serializedType = $this->getSerializedType($type)) { + return $this->openApiTransformer->transform($serializedType); + } + + return null; + } + + private function getSerializedType(ObjectType $type): ?Type + { + $cacheKey = $type->toString(); + + if (isset($this->cache[$cacheKey])) { + return $this->cache[$cacheKey] ?: null; + } + + $serializedType = $this->getFreshSerializedType($type); + + $this->cache[$cacheKey] = $serializedType ?: false; + + return $serializedType; + } + + private function getFreshSerializedType(ObjectType $type): ?Type + { + $definition = $this->infer->index->getClass($type->name); + + if ($jsonSerializableType = $this->getJsonSerializableType($definition, $type)) { + return $jsonSerializableType; + } + + if ($publicPropertiesType = $this->getSerializedPublicPropertiesType($definition, $type)) { + return $publicPropertiesType; + } + + return null; + } + + private function getJsonSerializableType(ClassDefinition $definition, ObjectType $type): ?Type + { + if (! $definition->getMethod('jsonSerialize')) { + return null; + } + + return ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new MethodCallReferenceType($type, 'jsonSerialize', []) + ); + } + + /** @see Infer\Definition\ClassPropertyDefinition */ + private function getSerializedPublicPropertiesType(ClassDefinition $definition, ObjectType $type): ?Type + { + $items = []; + foreach ($definition->getData()->properties as $name => $propertyDefinition) { + if ($propertyDefinition->visibility !== PropertyVisibility::Public) { + continue; + } + + if ($propertyDefinition->getAttributes(Hidden::class, AttributeDefinition::IS_INSTANCEOF)) { + continue; + } + + $item = new ArrayItemType_( + $name, + ReferenceTypeResolver::getInstance()->resolve( + new GlobalScope, + new PropertyFetchReferenceType($type, $name), + ), + ); + + if ($docNode = $propertyDefinition->getDocNode()) { + $item->setAttribute('docNode', $docNode); + } + + $items[] = $item; + } + + if (! count($items)) { + return null; + } + + return new KeyedArrayType($items); + } + + public function reference(ObjectType $type) + { + if (ltrim($type->name, '\\') === stdClass::class) { + return null; + } + + if (! $this->getSerializedType($type)) { + return null; + } + + return ClassBasedReference::create('schemas', $type->name, $this->components); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceCollectionTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceCollectionTypeToSchema.php new file mode 100644 index 00000000..75a62ed0 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceCollectionTypeToSchema.php @@ -0,0 +1,58 @@ +isInstanceOf(ResourceCollection::class); + } + + protected function normalizeType(ObjectType $type): Generic + { + return $type instanceof Generic + ? $type + : new Generic($type->name, [ + new UnknownType, + new UnknownType, + ResourceCollectionTypeManager::make($type)->getCollectedType(), + ]); + } + + protected function getResponseType(ObjectType $type): Type + { + return ResourceCollectionTypeManager::make( + $this->normalizeType($type) + )->getResponseType(); + } + + public function reference(ObjectType $type): ?Reference + { + if (! $this->shouldReferenceResourceCollection($type)) { + return null; + } + + return parent::reference($type); + } + + /** + * @internal + */ + public function shouldReferenceResourceCollection(ObjectType $type): bool + { + return ! Str::contains(class_basename($type->name), 'anonymous', ignoreCase: true) + && ! $type->isInstanceOf(AnonymousResourceCollection::class); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceResponseTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceResponseTypeToSchema.php new file mode 100644 index 00000000..27451f6d --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResourceResponseTypeToSchema.php @@ -0,0 +1,306 @@ +isInstanceOf(ResourceResponse::class) + && count($type->templateTypes) >= 1; + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type): Response + { + $resource = $type->templateTypes[0]; + + if (! $resource instanceof ObjectType) { + throw new LogicException('ResourceResponse data is expected to be an object'); + } + + return $this + ->makeResponse($resource) + ->setContent('application/json', Schema::fromType($this->wrap( + $this->wrapper($resource), + $this->openApiTransformer->transform($resource), + $this->getMergedAdditionalSchema($resource), + ))) + ->setDescription($this->getDescription($resource)); + } + + protected function getDescription(ObjectType $resourceType): string + { + if ($this->isNonReferencedResourceCollection($resourceType)) { + return $this->getNonReferencedResourceCollectionDescription($resourceType); + } + + return '`'.$this->openApiContext->references->schemas->uniqueName($resourceType->name).'`'; + } + + private function getMergedAdditionalSchema(ObjectType $resourceType): ?OpenApiType + { + $with = $this->getWithSchema($resourceType); + $additional = $this->getAdditionalSchema($resourceType); + + if (! $with && ! $additional) { + return null; + } + + $mergedAdditional = new OpenApiObjectType; + + if ($with) { + $this->mergeOpenApiObjects($mergedAdditional, $with); + } + + if ($additional) { + $this->mergeOpenApiObjects($mergedAdditional, $additional); + } + + return $mergedAdditional; + } + + protected function getNonReferencedResourceCollectionDescription(ObjectType $resourceType): string + { + $resourceType = (! $resourceType instanceof Generic) ? new Generic($resourceType->name) : $resourceType; + + $collectedResourceType = (new ResourceCollectionTypeManager($resourceType, $this->infer->index))->getCollectedType(); + + if (! $collectedResourceType instanceof ObjectType) { + return 'Array of items'; + } + + return 'Array of `'.$this->openApiContext->references->schemas->uniqueName($collectedResourceType->name).'`'; + } + + protected function isNonReferencedResourceCollection(ObjectType $resourceType): bool + { + if (! $resourceType->isInstanceOf(ResourceCollection::class)) { + return false; + } + + return ! $this->getResourceCollectionTypeToSchemaInstance()->shouldReferenceResourceCollection($resourceType); + } + + protected function getResourceCollectionTypeToSchemaInstance(): ResourceCollectionTypeToSchema + { + return new ResourceCollectionTypeToSchema( + $this->infer, + $this->openApiTransformer, + $this->components, + $this->openApiContext, + ); + } + + protected function makeResponse(ObjectType $resourceType): Response + { + $baseResponse = $this->openApiTransformer->toResponse( + $this->makeBaseResponseType($resourceType), + ); + + if (! $baseResponse instanceof Response) { + throw new LogicException('Paginated base response is expected to be an instance of Response, got '.($baseResponse ? $baseResponse::class : 'null')); + } + + return $baseResponse; + } + + protected function makeBaseResponseType(ObjectType $resourceType): Generic + { + $definition = $this->infer->analyzeClass($resourceType->name); + + $responseType = new Generic(JsonResponse::class, [new UnknownType, new LiteralIntegerType(200), new KeyedArrayType]); + + $methodDefinition = $definition->getMethod('withResponse'); + if (! $methodDefinition instanceof Infer\Definition\FunctionLikeAstDefinition) { + return $responseType; + } + + $responseParameterName = array_keys($methodDefinition->type->arguments)[1] ?? null; + if (! is_string($responseParameterName)) { + return $responseType; + } + + $flow = $methodDefinition->getFlowContainer(); + + $responseFinalType = Union::wrap(...array_map( + fn (FlowNode $n) => $flow + ->withEntryBindings([$responseParameterName => $responseType]) + ->getTypeAt(new Variable($responseParameterName), $n), + $flow->getReachableNodes(fn (FlowNode $n) => $n instanceof Infer\Flow\TerminateNode), + )); + + $responseFinalType = ReferenceTypeResolver::getInstance()->resolve($methodDefinition->getScope(), $responseFinalType); + + return $responseFinalType instanceof Generic && $responseFinalType->isInstanceOf(JsonResponse::class) + ? $responseFinalType + : $responseType; + } + + protected function wrap(?string $wrapKey, OpenApiType $data, ?OpenApiType $additional): OpenApiType + { + $dataIsWrapped = $this->dataIsWrapped($this->unref($data), $wrapKey); + + if ($wrapKey && ! $dataIsWrapped) { // $this->haveDefaultWrapperAndDataIsUnwrapped($data) + $data = (new OpenApiObjectType) + ->addProperty($wrapKey, $data) + ->setRequired([$wrapKey]); + } elseif (! $dataIsWrapped && $additional) { // $this->haveAdditionalInformationAndDataIsUnwrapped($data, $with, $additional) + $data = (new OpenApiObjectType) + ->addProperty($wrapKey ?? 'data', $data) + ->setRequired([$wrapKey ?? 'data']); + } + + if (! $additional) { + return $data; + } + + if ($data instanceof OpenApiObjectType) { + $this->mergeOpenApiObjects($data, $additional); + + return $data; + } + + return (new AllOf)->setItems([ + $data, + $additional, + ]); + } + + protected function wrapper(Type $resource): ?string + { + if (! $resource instanceof ObjectType) { + return null; + } + + return $resource->name::$wrap ?? null; + } + + protected function dataIsWrapped(OpenApiType $unrefedData, ?string $wrapKey): bool + { + if (! $wrapKey) { + return false; + } + + if (! $unrefedData instanceof OpenApiObjectType) { + return false; + } + + return array_key_exists($wrapKey, $unrefedData->properties); + } + + protected function unref(OpenApiType $type): OpenApiType + { + if ($type instanceof Reference) { + $type = $type->resolve(); + + return $type instanceof Schema ? $type->type : throw new \Exception('Cannot unref not Schema'); + } + + return $type; + } + + protected function getWithSchema(ObjectType $resource): ?OpenApiObjectType + { + if (! $withArray = $this->getWithType($resource)) { + return null; + } + + $withArray = $withArray->clone(); + + $withArray->items = $this->flattenMergeValues($withArray->items); + + $schema = $this->openApiTransformer->transform($withArray); + + if (! $schema instanceof OpenApiObjectType) { + return null; + } + + return $schema; + } + + protected function getWithType(ObjectType $type): ?KeyedArrayType + { + $withArray = ReferenceTypeResolver::getInstance()->resolve( + new Infer\Scope\GlobalScope, + new MethodCallReferenceType($type, 'with', []) + ); + + if (! $withArray instanceof KeyedArrayType) { + return null; + } + + return $withArray; + } + + protected function getAdditionalSchema(ObjectType $resource): ?OpenApiObjectType + { + if (! $resource instanceof Generic) { + return null; + } + + $additional = $resource->templateTypes[/* TAdditional */ 1] ?? null; + + if (! $additional instanceof KeyedArrayType) { + return null; + } + + $additional = $additional->clone(); + + $additional->items = $this->flattenMergeValues($additional->items); + + $schema = $this->openApiTransformer->transform($additional); + + if (! $schema instanceof OpenApiObjectType) { + return null; + } + + return $schema; + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponsableTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponsableTypeToSchema.php new file mode 100644 index 00000000..77f67d7a --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponsableTypeToSchema.php @@ -0,0 +1,34 @@ +isInstanceOf(Responsable::class); + } + + /** + * @param ObjectType $type + */ + public function toResponse(Type $type) + { + $responseType = ReferenceTypeResolver::getInstance() + ->resolve( + new GlobalScope, + new MethodCallReferenceType($type, 'toResponse', []), + ); + + return $this->openApiTransformer->toResponse($responseType); + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponseTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponseTypeToSchema.php new file mode 100644 index 00000000..d6afbd4a --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/ResponseTypeToSchema.php @@ -0,0 +1,160 @@ +isInstanceOf(\Illuminate\Http\Response::class) + || $type->isInstanceOf(JsonResponse::class) + ) + && count($type->templateTypes) >= 2; + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type) + { + if ($this->isResponsable($type) && $responsableResponse = $this->handleResponsableResponse($type)) { + return $responsableResponse; + } + + if (! $type->templateTypes[1] instanceof LiteralIntegerType) { + return null; + } + + $emptyContent = $type->templateTypes[0] instanceof NullType + || ($type->templateTypes[0]->value ?? null) === ''; + + $response = Response::make($code = $type->templateTypes[1]->value) + ->description($code === 204 ? 'No content' : ''); + + if (! $emptyContent) { + $response->setContent( + 'application/json', + Schema::fromType($this->openApiTransformer->transform($type->templateTypes[0])), + ); + } + + $this->addHeaders($response, $type); + + return $response; + } + + private function isResponsable(Generic $type): bool + { + $data = $type->templateTypes[0]; + + return $data instanceof ObjectType && $data->isInstanceOf(Responsable::class); + } + + private function handleResponsableResponse(Generic $jsonResponseType): ?Response + { + $data = $jsonResponseType->templateTypes[0]; + $statusCode = $jsonResponseType->templateTypes[1]; + + $response = $this->openApiTransformer->toResponse($data); + if (! $response instanceof Response) { + throw new LogicException("{$data->toString()} is expected to produce Response instance when casted to response."); + } + + $responseStatusCode = $statusCode instanceof UnknownType + ? $response->code + : ($statusCode->value ?? null); + + if (! $responseStatusCode) { + return null; + } + + $response->code = $responseStatusCode; + if (! $data->isInstanceOf(ResourceResponse::class)) { + $response->setContent( + 'application/json', + Schema::fromType($this->openApiTransformer->transform($data)), + ); + } + + $this->addHeaders($response, $jsonResponseType); + + return $response; + } + + private function addHeaders(Response $response, Generic $type): void + { + $headersType = $type->templateTypes[2] ?? null; + + if (! $headersType instanceof KeyedArrayType) { + return; + } + + foreach ($headersType->items as $item) { + $this->addHeader($response, $item); + } + } + + private function addHeader(Response $response, ArrayItemType_ $item): void + { + if (! $key = $this->getNormalizedHeaderKey($item)) { + return; + } + + if (Str::lower($key) === 'content-type') { + $this->handleContentTypeHeader($response, $item); + + return; + } + + $response->addHeader( + $key, + new Header(schema: Schema::fromType($this->openApiTransformer->transform($item->value))), + ); + } + + private function handleContentTypeHeader(Response $response, ArrayItemType_ $item): void + { + $contentTypeValue = $item->value instanceof LiteralStringType ? $item->value->value : null; + + if (! $contentTypeValue) { + return; + } + + if (! $firstMediaType = array_keys($response->content)[0] ?? null) { + return; + } + + $mediaType = $response->getContent($firstMediaType); + + unset($response->content[$firstMediaType]); + $response->content[$contentTypeValue] = $mediaType; + } + + private function getNormalizedHeaderKey(ArrayItemType_ $item): ?string + { + $key = $item->key ?: ($item->keyType instanceof LiteralStringType ? $item->keyType->value : null); + + return is_string($key) ? $key : null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/StreamedResponseToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/StreamedResponseToSchema.php new file mode 100644 index 00000000..7864addf --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/StreamedResponseToSchema.php @@ -0,0 +1,174 @@ +isInstanceOf(StreamedResponse::class) + && count($type->templateTypes) === 3; + } + + /** + * @param Generic $type + */ + public function toResponse(Type $type): ?Response + { + if (! $status = $this->getStatus($type)) { + return null; + } + + return Response::make($status) + ->setDescription($this->makeDescription($type)) + ->setContent( + $this->getContentType($type), + Schema::fromType($this->makeType($type)), + ) + ->setHeaders($this->makeHeaders()); + } + + private function getStatus(Generic $type): ?int + { + $statusType = $type->templateTypes[1 /* TStatus */] ?? null; + + if (! $statusType instanceof LiteralIntegerType) { + return null; + } + + return $statusType->value; + } + + private function isServerSentEventsResponse(Generic $type): bool + { + return $type->getAttribute('mimeType') === 'text/event-stream'; + } + + private function makeDescription(Generic $type): string + { + if (! $this->isServerSentEventsResponse($type)) { + return ''; + } + + $description = 'A server-sent events (SSE) streamed response.'; + + if ($endStreamWith = $this->getEndStreamWith($type)) { + $description .= " `$endStreamWith` update will be sent to the event stream when the stream is complete."; + } + + return $description; + } + + private function getContentType(Generic $type): string + { + $attributeMimeType = is_string($attributeMimeType = $type->getAttribute('mimeType')) + ? $attributeMimeType + : null; + + return $attributeMimeType + ?: $this->guessMimeTypeFromHeaders($type->templateTypes[2 /* THeaders */]) + ?: ($type->isInstanceOf(StreamedJsonResponse::class) ? 'application/json' : self::$defaultMimeType) + ?: self::$defaultMimeType; + } + + private function guessMimeTypeFromHeaders(Type $headersType): ?string + { + $stringLiteralContentTypeHeader = $headersType instanceof KeyedArrayType + ? collect($headersType->items) + ->first(function (ArrayItemType_ $t) { + return is_string($t->key) + && Str::lower($t->key) === 'content-type' + && $t->value instanceof LiteralStringType; + }) + ?->value + : null; + + if ($stringLiteralContentTypeHeader instanceof LiteralStringType) { + return $stringLiteralContentTypeHeader->value; + } + + return null; + } + + private function makeType(Generic $type): OpenApiType + { + if ($type->isInstanceOf(StreamedJsonResponse::class)) { + return $this->openApiTransformer->transform($type->templateTypes[0 /* TData */]); + } + + if ($this->isServerSentEventsResponse($type)) { + $schema = (new ObjectType) + ->addProperty('event', (new StringType)->example('update')) + ->addProperty('data', new MixedType) + ->setRequired(['event', 'data']); + + if ($example = $this->makeExample($type)) { + $schema->examples([$example]); + } + + return $schema; + } + + return new StringType; + } + + private function makeExample(Generic $type): ?string + { + if (! $this->isServerSentEventsResponse($type)) { + return null; + } + + $example = "event: update\ndata: {data}\n\n"; + + if ($endStreamWith = $this->getEndStreamWith($type)) { + $example .= "event: update\ndata: $endStreamWith\n\n"; + } + + return $example; + } + + /** + * @return array + */ + private function makeHeaders(): array + { + return [ + 'Transfer-Encoding' => new Header( + required: true, + schema: Schema::fromType((new StringType)->enum(['chunked'])), + ), + ]; + } + + private function getEndStreamWith(Generic $type): ?string + { + $endStreamWith = $type->getAttribute('endStreamWith'); + + return is_string($endStreamWith) ? $endStreamWith : null; + } +} diff --git a/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/VoidTypeToSchema.php b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/VoidTypeToSchema.php new file mode 100644 index 00000000..1b2aaf37 --- /dev/null +++ b/vendor/dedoc/scramble/src/Support/TypeToSchemaExtensions/VoidTypeToSchema.php @@ -0,0 +1,25 @@ +templateTypes[1] ?? $type->templateTypes[0] ?? null; + + if (! $collectedType instanceof ObjectType) { + return null; + } + + return $collectedType; + } +} diff --git a/vendor/dedoc/scramble/stubs/DeepCopy.stub b/vendor/dedoc/scramble/stubs/DeepCopy.stub new file mode 100644 index 00000000..82b21be6 --- /dev/null +++ b/vendor/dedoc/scramble/stubs/DeepCopy.stub @@ -0,0 +1,11 @@ +resolve('html-escaped-string'); -echo get_class($type); // Radebatz\TypeInfoExtras\Type\ExplicitType -echo $type->getExplicitType() // "html-escaped-string" - -$type = $resolver->resolve('class-string'); -echo get_class($type); // Radebatz\TypeInfoExtras\Type\ClassLikeType -echo $type->getExplicitType(); // "class-string" -echo get_class($type->getObjectType()); // Symfony\Component\TypeInfo\Type\ObjectType -echo $type->getObjectType(); // Foo - -$type = $resolver->resolve('int<5,20>'); -echo get_class($type); // Radebatz\TypeInfoExtras\Type\IntRangeType - -``` - -If your code is doing `instanceof` checks on the returned `Type`, then you will need to add another case and treat -`Radebatz\TypeInfoExtras\Tests\Type\ExplicitType` same as `BuiltinType`. diff --git a/vendor/radebatz/type-info-extras/composer.json b/vendor/radebatz/type-info-extras/composer.json deleted file mode 100644 index e6722218..00000000 --- a/vendor/radebatz/type-info-extras/composer.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "radebatz/type-info-extras", - "description": "Extras for symfony/type-info", - "keywords": ["component", "types", "symfony", "type-info"], - "license": [ - "MIT" - ], - "homepage": "http://radebatz.net/mano/", - "authors": [ - { - "name": "Martin Rademacher", - "email": "mano@radebatz.org" - } - ], - "config": { - "sort-packages": true, - "preferred-install": { - "symfony/type-info": "source", - "*": "dist" - } - }, - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "require": { - "php": ">=8.2", - "phpstan/phpdoc-parser": "^2.0", - "symfony/type-info": "^7.3.8 || ^7.4.1 || ^8.0 || ^8.1-@dev" - }, - "autoload": { - "psr-4": { "Radebatz\\TypeInfoExtras\\": "src" }, - "exclude-from-classmap": [ - "/tests/" - ] - }, - "autoload-dev": { - "psr-4": { - "Radebatz\\TypeInfoExtras\\Tests\\": "tests/" - } - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.70", - "phpstan/phpstan": "^2.1", - "phpunit/phpunit": "^11.0" - }, - "scripts-descriptions": { - "cs": "Fix all codestyle issues", - "lint": "Test codestyle", - "test": "Run all PHP, codestyle tests", - "analyse": "Run static analysis (phpstan)" - }, - "scripts": { - "cs": "export XDEBUG_MODE=off && php-cs-fixer fix --allow-risky=yes", - "lint": "@cs --dry-run", - "test": [ - "export XDEBUG_MODE=off && phpunit", - "@lint" - ], - "analyse": [ - "export XDEBUG_MODE=off && phpstan analyse --memory-limit=2G" - ] - } -} diff --git a/vendor/radebatz/type-info-extras/src/Type/ClassLikeType.php b/vendor/radebatz/type-info-extras/src/Type/ClassLikeType.php deleted file mode 100644 index e40aa850..00000000 --- a/vendor/radebatz/type-info-extras/src/Type/ClassLikeType.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Radebatz\TypeInfoExtras\Type; - -use Symfony\Component\TypeInfo\Type\ObjectType; -use Symfony\Component\TypeInfo\TypeIdentifier; - -/** - * Class-like string type. - * - * @author Martin Rademacher - * - * @extends ExplicitType - */ -final class ClassLikeType extends ExplicitType -{ - public function __construct(string $explicitType, private ObjectType $objectType) - { - parent::__construct(TypeIdentifier::STRING, $explicitType); - } - - public function getObjectType(): ObjectType - { - return $this->objectType; - } - - public function __toString(): string - { - return \sprintf('%s<%s>', $this->getExplicitType(), $this->objectType); - } -} diff --git a/vendor/radebatz/type-info-extras/src/Type/ExplicitType.php b/vendor/radebatz/type-info-extras/src/Type/ExplicitType.php deleted file mode 100644 index 397282f5..00000000 --- a/vendor/radebatz/type-info-extras/src/Type/ExplicitType.php +++ /dev/null @@ -1,85 +0,0 @@ -typeIdentifier; - } - - /** - * Get the underlying builtin type. - */ - public function getBuiltinType(): BuiltinType - { - return Type::builtin($this->typeIdentifier); - } - - public function getExplicitType(): string - { - return $this->explicitType; - } - - public function isIdentifiedBy(TypeIdentifier|string ...$identifiers): bool - { - foreach ($identifiers as $identifier) { - if (\is_string($identifier)) { - try { - $identifier = TypeIdentifier::from($identifier); - } catch (\ValueError) { - continue; - } - } - - if ($identifier === $this->typeIdentifier) { - return true; - } - } - - return false; - } - - public function isNullable(): bool - { - return false; - } - - public function accepts(mixed $value): bool - { - return match ($this->typeIdentifier) { - TypeIdentifier::ARRAY => \is_array($value), - TypeIdentifier::INT => \is_int($value), - default => false, - }; - } - - public function __toString(): string - { - return $this->explicitType; - } -} diff --git a/vendor/radebatz/type-info-extras/src/Type/IntRangeType.php b/vendor/radebatz/type-info-extras/src/Type/IntRangeType.php deleted file mode 100644 index 41cd11ea..00000000 --- a/vendor/radebatz/type-info-extras/src/Type/IntRangeType.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * @extends ExplicitType - */ -final class IntRangeType extends ExplicitType -{ - public function __construct( - private int $from = \PHP_INT_MIN, - private int $to = \PHP_INT_MAX, - ?string $explicitType = null, - ) { - parent::__construct(TypeIdentifier::INT, $explicitType ?? $this->canonical($this->from, $this->to)); - } - - public function getFrom(): int - { - return $this->from; - } - - public function getTo(): int - { - return $this->to; - } - - public function accepts(mixed $value): bool - { - return parent::accepts($value) && $this->from <= $value && $value <= $this->to; - } - - public function __toString(): string - { - return $this->canonical($this->from, $this->to); - } - - protected function canonical(int $from, int $to): string - { - $min = \PHP_INT_MIN === $from ? 'min' : $from; - $max = \PHP_INT_MAX === $to ? 'max' : $to; - - return \sprintf('int<%s, %s>', $min, $max); - } -} diff --git a/vendor/radebatz/type-info-extras/src/Type/Type.php b/vendor/radebatz/type-info-extras/src/Type/Type.php deleted file mode 100644 index 51a8a08b..00000000 --- a/vendor/radebatz/type-info-extras/src/Type/Type.php +++ /dev/null @@ -1,11 +0,0 @@ -name) { - 'min' => \PHP_INT_MIN, - 'max' => \PHP_INT_MAX, - default => throw new \DomainException(\sprintf('Invalid int range value "%s".', $node->name)), - }; - } - - if ($node instanceof ConstTypeNode && $node->constExpr instanceof ConstExprIntegerNode) { - return (int) $node->constExpr->value; - } - - throw new \DomainException(\sprintf('Invalid int range expression "%s".', \get_class($node))); - }; - - $boundaries = array_map(fn (TypeNode $t): int => $getBoundaryFromNode($t), $node->genericTypes); - - return Type::intRange($boundaries[0], $boundaries[1]); - } - - /** - * @param array $variableTypes - */ - protected function tryAsClassLike(BaseType $type, array $variableTypes): ?ClassLikeType - { - if ($type instanceof ExplicitType - && \in_array($type->getExplicitType(), ['class-string', 'interface-string', 'trait-string'], true) - && 1 === \count($variableTypes) && $variableTypes[0] instanceof ObjectType) { - return Type::classLike($type->getExplicitType(), $variableTypes[0]); - } - - return null; - } - - public function reduceToBuiltinType(?BaseType $type) - { - return $type instanceof ExplicitType - ? $type->getBuiltinType() - : $type; - } -} diff --git a/vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php b/vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php deleted file mode 100644 index 2473daf7..00000000 --- a/vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php +++ /dev/null @@ -1,385 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Radebatz\TypeInfoExtras\TypeResolver; - -use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode; -use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode; -use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode; -use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; -use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNullNode; -use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode; -use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode; -use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; -use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; -use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; -use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; -use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; -use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; -use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; -use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; -use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode; -use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode; -use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode; -use PHPStan\PhpDocParser\Ast\Type\TypeNode; -use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; -use PHPStan\PhpDocParser\Lexer\Lexer; -use PHPStan\PhpDocParser\Parser\ConstExprParser; -use PHPStan\PhpDocParser\Parser\TokenIterator; -use PHPStan\PhpDocParser\Parser\TypeParser; -use PHPStan\PhpDocParser\ParserConfig; -use Radebatz\TypeInfoExtras\Type\Type; -use Symfony\Component\TypeInfo\Exception\InvalidArgumentException; -use Symfony\Component\TypeInfo\Exception\UnsupportedException; -use Symfony\Component\TypeInfo\Type as BaseType; -use Symfony\Component\TypeInfo\Type\BackedEnumType; -use Symfony\Component\TypeInfo\Type\BuiltinType; -use Symfony\Component\TypeInfo\Type\CollectionType; -use Symfony\Component\TypeInfo\Type\GenericType; -use Symfony\Component\TypeInfo\Type\ObjectType; -use Symfony\Component\TypeInfo\TypeContext\TypeContext; -use Symfony\Component\TypeInfo\TypeIdentifier; -use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; - -/** - * Resolves type for a given string. - * - * @author Mathias Arlaud - * @author Baptiste Leduc - * @author Martin Rademacher - */ -final class StringTypeResolver implements TypeResolverInterface -{ - use ResolverExtrasTrait; - - /** - * @var array - */ - private static array $classExistCache = []; - - private readonly Lexer $lexer; - private readonly TypeParser $parser; - - /** - * @param array $extraTypeAliases - */ - public function __construct( - ?Lexer $lexer = null, - ?TypeParser $parser = null, - private readonly array $extraTypeAliases = [], - ) { - $this->lexer = $lexer ?? new Lexer(new ParserConfig([])); - $this->parser = $parser ?? new TypeParser($config = new ParserConfig([]), new ConstExprParser($config)); - } - - public function resolve(mixed $subject, ?TypeContext $typeContext = null): BaseType - { - if ($subject instanceof \Stringable) { - $subject = (string) $subject; - } elseif (!\is_string($subject)) { - throw new UnsupportedException(\sprintf('Expected subject to be a "string", "%s" given.', get_debug_type($subject)), $subject); - } - - try { - $tokens = new TokenIterator($this->lexer->tokenize($subject)); - $node = $this->parser->parse($tokens); - - return $this->getTypeFromNode($node, $typeContext); - } catch (\DomainException $e) { - throw new UnsupportedException(\sprintf('Cannot resolve "%s".', $subject), $subject, previous: $e); - } - } - - private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): BaseType - { - $typeIsCollectionObject = static fn (BaseType $type): bool => $type->isIdentifiedBy(\Traversable::class) || $type->isIdentifiedBy(\ArrayAccess::class); - - if ($node instanceof CallableTypeNode) { - return Type::callable(); - } - - if ($node instanceof ArrayTypeNode) { - return Type::array($this->getTypeFromNode($node->type, $typeContext)); - } - - if ($node instanceof ArrayShapeNode) { - $shape = []; - foreach ($node->items as $item) { - $shape[(string) $item->keyName] = [ - 'type' => $this->getTypeFromNode($item->valueType, $typeContext), - 'optional' => $item->optional, - ]; - } - - return Type::arrayShape( - $shape, - $node->sealed, - $node->unsealedType?->keyType ? $this->getTypeFromNode($node->unsealedType->keyType, $typeContext) : null, - $node->unsealedType?->valueType ? $this->getTypeFromNode($node->unsealedType->valueType, $typeContext) : null, - ); - } - - if ($node instanceof ObjectShapeNode) { - $shape = []; - foreach ($node->items as $item) { - $shape[(string) $item->keyName] = [ - 'type' => $this->getTypeFromNode($item->valueType, $typeContext), - 'optional' => $item->optional, - ]; - } - - return ($shape && method_exists(Type::class, 'objectShape')) - ? Type::objectShape($shape) - : Type::object(); - } - - if ($node instanceof ThisTypeNode) { - if (null === $typeContext) { - throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "$this".', TypeContext::class)); - } - - return Type::object($typeContext->getCalledClass()); - } - - if ($node instanceof ConstTypeNode) { - if ($node->constExpr instanceof ConstFetchNode) { - $className = match (strtolower($node->constExpr->className)) { - 'self' => $typeContext->getDeclaringClass(), - 'static' => $typeContext->getCalledClass(), - 'parent' => $typeContext->getParentClass(), - default => null, - }; - - if (null === $className) { - $classType = $this->resolveCustomIdentifier($node->constExpr->className, $typeContext); - if (!$classType instanceof ObjectType) { - return Type::mixed(); - } - - $className = $classType->getClassName(); - } - - if (!class_exists($className)) { - return Type::mixed(); - } - - $types = []; - - foreach ((new \ReflectionClass($className))->getReflectionConstants() as $const) { - if (preg_match('/^' . str_replace('\*', '.*', preg_quote($node->constExpr->name, '/')) . '$/', $const->getName())) { - $types[] = Type::fromValue($const->getValue()); - } - } - - return CollectionType::mergeCollectionValueTypes($types); - } - - return match ($node->constExpr::class) { - ConstExprArrayNode::class => Type::array(), - ConstExprFalseNode::class => Type::false(), - ConstExprFloatNode::class => Type::float(), - ConstExprIntegerNode::class => Type::int(), - ConstExprNullNode::class => Type::null(), - ConstExprStringNode::class => Type::string(), - ConstExprTrueNode::class => Type::true(), - default => throw new \DomainException(\sprintf('Unhandled "%s" constant expression.', $node->constExpr::class)), - }; - } - - if ($node instanceof IdentifierTypeNode) { - $type = match ($node->name) { - 'bool', 'boolean' => Type::bool(), - 'true' => Type::true(), - 'false' => Type::false(), - 'int', 'integer' => Type::int(), - 'positive-int' => Type::intRange(from: 1, explicitType: $node->name), - 'negative-int' => Type::intRange(to: -1, explicitType: $node->name), - 'non-positive-int' => Type::intRange(to: 0, explicitType: $node->name), - 'non-negative-int' => Type::intRange(from: 0, explicitType: $node->name), - 'non-zero-int' => BaseType::union(Type::intRange(to: -1), Type::intRange(from: 1)), - 'float', 'double' => Type::float(), - 'string' => BaseType::string(), - 'class-string', - 'trait-string', - 'interface-string', - 'callable-string', - 'numeric-string', - 'lowercase-string', - 'non-empty-lowercase-string', - 'non-empty-string', - 'non-falsy-string', - 'truthy-string', - 'literal-string', - 'html-escaped-string' => Type::explicit(TypeIdentifier::STRING, $node->name), - 'resource' => Type::resource(), - 'object' => Type::object(), - 'callable' => Type::callable(), - 'array', 'non-empty-array' => Type::array(), - 'list', 'non-empty-list' => Type::list(), - 'iterable' => Type::iterable(), - 'mixed' => Type::mixed(), - 'null' => Type::null(), - 'array-key' => Type::arrayKey(), - 'scalar' => Type::union(Type::int(), Type::float(), Type::string(), Type::bool()), - 'number' => Type::union(Type::int(), Type::float()), - 'numeric' => Type::union(Type::int(), Type::float(), Type::string()), - 'self' => $typeContext ? Type::object($typeContext->getDeclaringClass()) : throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "self".', TypeContext::class)), - 'static' => $typeContext ? Type::object($typeContext->getCalledClass()) : throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "static".', TypeContext::class)), - 'parent' => $typeContext ? Type::object($typeContext->getParentClass()) : throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "parent".', TypeContext::class)), - 'void' => Type::void(), - 'never', 'never-return', 'never-returns', 'no-return' => Type::never(), - default => $this->resolveCustomIdentifier($node->name, $typeContext), - }; - - if ($typeIsCollectionObject($type)) { - return Type::collection($type); - } - - return $type; - } - - if ($node instanceof NullableTypeNode) { - return Type::nullable($this->getTypeFromNode($node->type, $typeContext)); - } - - if ($node instanceof GenericTypeNode) { - if ($node->type instanceof IdentifierTypeNode && 'value-of' === $node->type->name) { - $type = $this->getTypeFromNode($node->genericTypes[0], $typeContext); - if ($type instanceof BackedEnumType) { - return $type->getBackingType(); - } - - if ($type instanceof CollectionType) { - return $type->getCollectionValueType(); - } - - throw new \DomainException(\sprintf('"%s" is not a valid type for "value-of".', $node->genericTypes[0])); - } - - if ($node->type instanceof IdentifierTypeNode && 'key-of' === $node->type->name) { - $type = $this->getTypeFromNode($node->genericTypes[0], $typeContext); - if ($type instanceof CollectionType) { - return $type->getCollectionKeyType(); - } - - throw new \DomainException(\sprintf('"%s" is not a valid type for "key-of".', $node->genericTypes[0])); - } - - $type = $this->getTypeFromNode($node->type, $typeContext); - - // handle integer ranges - if ($type->isIdentifiedBy(TypeIdentifier::INT)) { - return $this->resolveIntRange($node); - } - - $variableTypes = array_map(fn (TypeNode $t): BaseType => $this->getTypeFromNode($t, $typeContext), $node->genericTypes); - - if ($type instanceof CollectionType) { - $asList = $type->isList(); - $keyType = $type->getCollectionKeyType(); - $type = $type->getWrappedType(); - - if ($type instanceof GenericType) { - $type = $type->getWrappedType(); - } - - if (1 === \count($variableTypes)) { - return new CollectionType(Type::generic($type, $keyType, $variableTypes[0]), $asList); - } elseif (2 === \count($variableTypes)) { - if ($asList) { - throw new \DomainException(\sprintf('"%s" type cannot have a key type defined.', $node->type)); - } - - return Type::collection($type, $variableTypes[1], $this->reduceToBuiltinType($variableTypes[0]), $asList); - } - } - - if ($typeIsCollectionObject($type)) { - return match (\count($variableTypes)) { - 1 => Type::collection($type, $variableTypes[0]), - 2 => Type::collection($type, $variableTypes[1], $variableTypes[0]), - default => Type::collection($type), - }; - } - - if ($classLike = $this->tryAsClassLike($type, $variableTypes)) { - return $classLike; - } - - if ($type instanceof BuiltinType && TypeIdentifier::ARRAY !== $type->getTypeIdentifier() && TypeIdentifier::ITERABLE !== $type->getTypeIdentifier()) { - return $type; - } - - return Type::generic($type, ...$variableTypes); - } - - if ($node instanceof UnionTypeNode) { - $types = []; - - foreach ($node->types as $nodeType) { - $type = $this->getTypeFromNode($nodeType, $typeContext); - - if ($type instanceof BuiltinType && TypeIdentifier::MIXED === $type->getTypeIdentifier()) { - return Type::mixed(); - } - - $types[] = $type; - } - - return Type::union(...$types); - } - - if ($node instanceof IntersectionTypeNode) { - return Type::intersection(...array_map(fn (TypeNode $t): BaseType => $this->getTypeFromNode($t, $typeContext), $node->types)); - } - - throw new \DomainException(\sprintf('Unhandled "%s" node.', $node::class)); - } - - private function resolveCustomIdentifier(string $identifier, ?TypeContext $typeContext): BaseType - { - $className = $typeContext ? $typeContext->normalize($identifier) : $identifier; - - if (!isset(self::$classExistCache[$className])) { - self::$classExistCache[$className] = false; - - if (class_exists($className) || interface_exists($className)) { - self::$classExistCache[$className] = true; - } else { - try { - new \ReflectionClass($className); - self::$classExistCache[$className] = true; - } catch (\Throwable) { - } - } - } - - if (self::$classExistCache[$className]) { - if (is_subclass_of($className, \UnitEnum::class) && !interface_exists($className)) { - return Type::enum($className); - } - - return Type::object($className); - } - - if (isset($typeContext?->templates[$identifier])) { - return Type::template($identifier, $typeContext->templates[$identifier]); - } - - if (isset($typeContext?->typeAliases[$identifier])) { - return $typeContext->typeAliases[$identifier]; - } - - if (isset($this->extraTypeAliases[$identifier])) { - return $this->resolve($this->extraTypeAliases[$identifier]); - } - - throw new \DomainException(\sprintf('Unhandled "%s" identifier.', $identifier)); - } -} diff --git a/vendor/darkaonline/l5-swagger/LICENSE b/vendor/spatie/laravel-package-tools/LICENSE.md similarity index 88% rename from vendor/darkaonline/l5-swagger/LICENSE rename to vendor/spatie/laravel-package-tools/LICENSE.md index 5c30679d..cfe14134 100644 --- a/vendor/darkaonline/l5-swagger/LICENSE +++ b/vendor/spatie/laravel-package-tools/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Darius Matulionis +Copyright (c) spatie Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/spatie/laravel-package-tools/README.md b/vendor/spatie/laravel-package-tools/README.md new file mode 100644 index 00000000..5406b0f7 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/README.md @@ -0,0 +1,678 @@ +
+ + + + Logo for laravel-package-tools + + + +

Tools for creating Laravel packages

+ +[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/laravel-package-tools.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-package-tools) +![Tests](https://github.com/spatie/laravel-package-tools/workflows/Tests/badge.svg) +[![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-package-tools.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-package-tools) + +
+ +This package contains a `PackageServiceProvider` that you can use in your packages to easily register config files, +migrations, and more. + +Here's an example of how it can be used. + +```php +use Spatie\LaravelPackageTools\PackageServiceProvider; +use Spatie\LaravelPackageTools\Package; +use MyPackage\ViewComponents\Alert; +use Spatie\LaravelPackageTools\Commands\Concerns; +use Spatie\LaravelPackageTools\Commands\InstallCommand; + +class YourPackageServiceProvider extends PackageServiceProvider +{ + public function configurePackage(Package $package): void + { + $package + ->name('your-package-name') + ->hasConfigFile() + ->hasViews() + ->hasViewComponent('spatie', Alert::class) + ->hasViewComposer('*', MyViewComposer::class) + ->sharesDataWithAllViews('downloads', 3) + ->hasTranslations() + ->hasAssets() + ->publishesServiceProvider('MyProviderName') + ->hasRoute('web') + ->hasMigration('create_package_tables') + ->hasCommand(YourCoolPackageCommand::class) + ->hasInstallCommand(function(InstallCommand $command) { + $command + ->publishConfigFile() + ->publishAssets() + ->publishMigrations() + ->copyAndRegisterServiceProviderInApp() + ->askToStarRepoOnGitHub(); + }); + } +} +``` + +Under the hood it will do the necessary work to register the necessary things and make all sorts of files publishable. + +## Support us + +[](https://spatie.be/github-ad-click/laravel-package-tools) + +We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can +support us by [buying one of our paid products](https://spatie.be/open-source/support-us). + +We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. +You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards +on [our virtual postcard wall](https://spatie.be/open-source/postcards). + +## Getting started + +This package is opinionated on how you should structure your package. To get started easily, consider +using [our package-skeleton repo](https://github.com/spatie/package-skeleton-laravel) to start your package. The +skeleton is structured perfectly to work perfectly with the `PackageServiceProvider` in this package. + +## Usage + +To avoid needing to scroll through to find the right usage section, here is a Table of Contents: + +* [Directory Structure](#directory-structure) +* [Getting Started](#getting-started) +* [Assets](#assets) +* [Blade Components](#blade-view-components) +* [Commands - Callable and Console](#commands---callable-and-console) +* [Config Files](#config-files) +* [Inertia Components](#inertia-components) +* [Working with migrations](#working-with-migrations) +* [Routes](#routes) +* [Publishable Service Providers](#publishable-service-providers) +* [Translations](#translations) +* [Views](#views) +* [View Composers](#view-composers) +* [Views Global Shared Data](#views-global-shared-data) +* [Creating and Install Command](#creating-an-install-command) +* [Lifecycle Hooks](#lifecycle-hooks) + +### Directory Structure + +This package is opinionated on how you should structure your package, +and by default expects a structure based on +[our package-skeleton repo](https://github.com/spatie/package-skeleton-laravel), +and to get started easily you should consider using this to start your package. + +The structure for a package expected by default looks like this: + +``` + +/src/ Default location for PackageServiceProvider extended class +/src/Commands/ Commands (callable and console-only) +/src/Components/ Blade components +/src/Providers/ Other Service Providers +/config/ Mergeable and publishable config files +/database/factories/ Database factories +/database/migrations/ Publishable stubs and loadable migrations +/resources/dist/ Publishable assets +/resources/js/pages/ Inertia views +/resources/lang/ International translations +/resources/views/ Views +/routes/ Routes +``` + +Note: When using paths in any Package method except `discoversMigrations()`, +the path given is relative to the location of your primary Service Provider +i.e. relative to `/src` +so e.g. `/ConfigFiles` would be specified as `../ConfigFiles`. + +### Getting Started + +In your package you should let your service provider extend `Spatie\LaravelPackageTools\PackageServiceProvider`. + +```php +use Spatie\LaravelPackageTools\PackageServiceProvider; +use Spatie\LaravelPackageTools\Package; + +class YourPackageServiceProvider extends PackageServiceProvider +{ + public function configurePackage(Package $package) : void + { + $package->name('your-package-name'); + } +} +``` + +Defining your package name with a call to `name()` is mandatory. + +**Note:** If your package name starts with `laravel-` then this prefix will be omitted +and the remainder of the name used as a short-name instead when publishing files etc. + +And now let's look at all the different Laravel functions this supports... + +assigning the package name to `name` is mandatory. + +### Assets + +Any assets your package provides, should be placed in the `/resources/dist/` directory. + +You can make these assets publishable the `hasAssets` method. + +```php +$package + ->name('your-package-name') + ->hasAssets(); +``` + +Users of your package will be able to publish the assets with this command: + +```bash +php artisan vendor:publish --tag=your-package-name-assets +``` + +This will copy over the assets to the `public/vendor/` directory in the app where your package is +installed in. + +### Blade view components + +Any Blade view components that your package provides should be placed in the `/src/Components` directory. + +You can register these views with the `hasViewComponents` command. + +```php +$package + ->name('your-package-name') + ->hasViewComponents('spatie', Alert::class); +``` + +This will register your view components with Laravel. In the case of `Alert::class`, it can be referenced in views +as ``, where `spatie` is the prefix you provided during registration. + +Calling `hasViewComponents` will also make view components publishable, and will be published +to `app/Views/Components/vendor/`. + +Users of your package will be able to publish the view components with this command: + +```bash +php artisan vendor:publish --tag=your-package-name-components +``` + +### Commands - Callable and Console + +You can register any command you package provides with the `hasCommand` function. + +```php +$package + ->name('your-package-name') + ->hasCommand(YourCoolPackageCommand::class); +```` + +If your package provides multiple commands, you can either use `hasCommand` multiple times, or pass an array +to `hasCommands` + +```php +$package + ->name('your-package-name') + ->hasCommands([ + YourCoolPackageCommand::class, + YourOtherCoolPackageCommand::class, + ]); +``` + +### Config Files + +To register a config file, you should create a php file with your package name in the `config` directory of your +package. In this example it should be at `/config/your-package-name.php`. + +If your package name starts with `laravel-`, we expect that your config file does not contain that prefix. So if your +package name is `laravel-cool-package`, the config file should be named `cool-package.php`. + +To register that config file, call `hasConfigFile()` on `$package` in the `configurePackage` method. + +```php +$package + ->name('your-package-name') + ->hasConfigFile(); +``` + +The `hasConfigFile` method will also make the config file publishable. Users of your package will be able to publish the +config file with this command. + +```bash +php artisan vendor:publish --tag=your-package-name-config +``` + +Should your package have multiple config files, you can pass their names as an array to `hasConfigFile` + +```php +$package + ->name('your-package-name') + ->hasConfigFile(['my-config-file', 'another-config-file']); +``` + +### Inertia Components + +Any `.vue` or `.jsx` files your package provides, should be placed in the `/resources/js/Pages` directory. + +You can register these components with the `hasInertiaComponents` command. + +```php +$package + ->name('your-package-name') + ->hasInertiaComponents(); +``` + +This will register your components with Laravel. + +The user should publish the inertia components manually or using the [installer-command](#adding-an-installer-command) in order to use them. + +If you have an inertia component `/resources/js/Pages/myComponent.vue`, you can use it like +this: `Inertia::render('YourPackageName/myComponent')`. Of course, you can also use subdirectories to organise your components. + +#### Publishing inertia components + +Calling `hasInertiaComponents` will also make inertia components publishable. Users of your package will be able to publish the views with this +command: + +```bash +php artisan vendor:publish --tag=your-package-name-inertia-components +``` + +Also, the inertia components are available in a convenient way with your package [installer-command](#adding-an-installer-command) + +### Working with migrations + +The `PackageServiceProvider` assumes that any migrations are placed in this +directory: `/database/migrations`. Inside that directory you can put any migrations. + +To register your migration, you should pass its name without the extension to the `hasMigration` table. + +If your migration file is called `create_my_package_tables.php.stub` you can register them like this: + +```php +$package + ->name('your-package-name') + ->hasMigration('create_my_package_tables'); +``` + +Should your package contain multiple migration files, you can just call `hasMigration` multiple times or +use `hasMigrations`. + +```php +$package + ->name('your-package-name') + ->hasMigrations(['my_package_tables', 'some_other_migration']); +``` + +Alternatively, if you wish to publish all migrations in your package by default, you may call `discoversMigrations`. + +```php +$package + ->name('your-package-name') + ->discoversMigrations(); +``` + +Calling this method will look for migrations in the `./database/migrations` directory of your project. However, if you have defined your migrations +in a different folder, you may pass a value to the `$path` variable to instruct the app to discover migrations from that location. + +```php +$package + ->name('your-package-name') + ->discoversMigrations(path: '/path/to/your/migrations/folder'); +``` + +Calling either `hasMigration`, `hasMigration` or `discoversMigrations` will also make migrations publishable. Users of your package will be able to publish the +migrations with this command: + +```bash +php artisan vendor:publish --tag=your-package-name-migrations +``` + +Like you might expect, published migration files will be prefixed with the current datetime. + +You can also enable the migrations to be registered without needing the users of your package to publish them: + +```php +$package + ->name('your-package-name') + ->hasMigrations(['my_package_tables', 'some_other_migration']) + ->runsMigrations(); +``` + +### Routes + +The `PackageServiceProvider` assumes that any route files are placed in this directory: `/routes`. Inside +that directory you can put any route files. + +To register your route, you should pass its name without the extension to the `hasRoute` method. + +If your route file is called `web.php` you can register them like this: + +```php +$package + ->name('your-package-name') + ->hasRoute('web'); +``` + +Should your package contain multiple route files, you can just call `hasRoute` multiple times or use `hasRoutes`. + +```php +$package + ->name('your-package-name') + ->hasRoutes(['web', 'admin']); +``` + +### Publishable Service Providers + +Some packages need an example service provider to be copied into the `app\Providers` directory of the Laravel app. Think +of for instance, the `laravel/horizon` package that copies an `HorizonServiceProvider` into your app with some sensible +defaults. + +```php +$package + ->name('your-package-name') + ->publishesServiceProvider($nameOfYourServiceProvider); +``` + +The file that will be copied to the app should be stored in your package +in `/resources/stubs/{$nameOfYourServiceProvider}.php.stub`. + +When your package is installed into an app, running this command... + +```bash +php artisan vendor:publish --tag=your-package-name-provider +``` + +... will copy `/resources/stubs/{$nameOfYourServiceProvider}.php.stub` in your package +to `app/Providers/{$nameOfYourServiceProvider}.php` in the app of the user. + +### Translations + +Any translations your package provides, should be placed in the `/resources/lang/` +directory. + +You can register these translations with the `hasTranslations` command. + +```php +$package + ->name('your-package-name') + ->hasTranslations(); +``` + +This will register the translations with Laravel. + +Assuming you save this translation file at `/resources/lang/en/translations.php`... + +```php +return [ + 'translatable' => 'translation', +]; +``` + +... your package and users will be able to retrieve the translation with: + +```php +trans('your-package-name::translations.translatable'); // returns 'translation' +``` + +If your package name starts with `laravel-` then you should leave that off in the example above. + +Coding with translation strings as keys, you should create JSON files +in `/resources/lang/.json`. + +For example, creating `/resources/lang/it.json` file like so: + +```json +{ + "Hello!": "Ciao!" +} +``` + +...the output of... + +```php +trans('Hello!'); +``` + +...will be `Ciao!` if the application uses the Italian language. + +Calling `hasTranslations` will also make translations publishable. Users of your package will be able to publish the +translations with this command: + +```bash +php artisan vendor:publish --tag=your-package-name-translations +``` + +### Views + +Any views your package provides, should be placed in the `/resources/views` directory. + +You can register these views with the `hasViews` command. + +```php +$package + ->name('your-package-name') + ->hasViews(); +``` + +This will register your views with Laravel. + +If you have a view `/resources/views/myView.blade.php`, you can use it like +this: `view('your-package-name::myView')`. Of course, you can also use subdirectories to organise your views. A view +located at `/resources/views/subdirectory/myOtherView.blade.php` can be used +with `view('your-package-name::subdirectory.myOtherView')`. + +#### Using a custom view namespace + +You can pass a custom view namespace to the `hasViews` method. + +```php +$package + ->name('your-package-name') + ->hasViews('custom-view-namespace'); +``` + +You can now use the views of the package like this: + +```php +view('custom-view-namespace::myView'); +``` + +#### Publishing the views + +Calling `hasViews` will also make views publishable. Users of your package will be able to publish the views with this +command: + +```bash +php artisan vendor:publish --tag=your-package-name-views +``` + +> **Note:** +> +> If you use custom view namespace then you should change your publish command like this: +```bash +php artisan vendor:publish --tag=custom-view-namespace-views +``` + +### View Composers + +You can register any view composers that your project uses with the `hasViewComposers` method. You may also register a +callback that receives a `$view` argument instead of a classname. + +To register a view composer with all views, use an asterisk as the view name `'*'`. + +```php +$package + ->name('your-package-name') + ->hasViewComposer('viewName', MyViewComposer::class) + ->hasViewComposer('*', function($view) { + $view->with('sharedVariable', 123); + }); +``` + +### Views Global Shared Data + +You can share data with all views using the `sharesDataWithAllViews` method. This will make the shared variable +available to all views. + +```php +$package + ->name('your-package-name') + ->sharesDataWithAllViews('companyName', 'Spatie'); +``` + +### Creating an Install Command + +Instead of letting your users manually publishing config files, migrations, and other files manually, you could opt to +add an install command that does all this work in one go. Packages like Laravel Horizon and Livewire provide such +commands. + +When using Laravel Package Tools, you don't have to write an `InstallCommand` yourself. Instead, you can simply +call, `hasInstallCommand` and configure it using a closure. Here's an example. + +```php +use Spatie\LaravelPackageTools\PackageServiceProvider; +use Spatie\LaravelPackageTools\Package; +use Spatie\LaravelPackageTools\Commands\Concerns; + +class YourPackageServiceProvider extends PackageServiceProvider +{ + public function configurePackage(Package $package): void + { + $package + ->name('your-package-name') + ->hasConfigFile() + ->hasMigration('create_package_tables') + ->publishesServiceProvider('MyServiceProviderName') + ->hasInstallCommand(function(InstallCommand $command) { + $command + ->publishConfigFile() + ->publishAssets() + ->publishMigrations() + ->askToRunMigrations() + ->copyAndRegisterServiceProviderInApp() + ->askToStarRepoOnGitHub('your-vendor/your-repo-name') + }); + } +} +``` + +With this in place, the package user can call this command: + +```bash +php artisan your-package-name:install +``` + +Using the code above, that command will: + +- publish the config file +- publish the assets +- publish the migrations +- copy the `/resources/stubs/MyProviderName.php.stub` from your package to `app/Providers/MyServiceProviderName.php`, and also register that + provider in `config/app.php` +- ask if migrations should be run now +- prompt the user to open up `https://github.com/'your-vendor/your-repo-name'` in the browser in order to star it + +You can also call `startWith` and `endWith` on the `InstallCommand`. They will respectively be executed at the start and +end when running `php artisan your-package-name:install`. You can use this to perform extra work or display extra +output. + +```php +use Spatie\LaravelPackageTools\Commands\Concerns; + +public function configurePackage(Package $package): void +{ + $package + // ... configure package + ->hasInstallCommand(function(InstallCommand $command) { + $command + ->startWith(function(InstallCommand $command) { + $command->info('Hello, and welcome to my great new package!'); + }) + ->publishConfigFile() + ->publishAssets() + ->publishMigrations() + ->askToRunMigrations() + ->copyAndRegisterServiceProviderInApp() + ->askToStarRepoOnGitHub('your-vendor/your-repo-name') + ->endWith(function(InstallCommand $command) { + $command->info('Have a great day!'); + }) + }); +} +``` + +### Lifecycle Hooks + +You can put any custom logic your package needs while starting up in one of these methods: + +- `registeringPackage`: will be called at the start of the `register` method of `PackageServiceProvider` +- `packageRegistered`: will be called at the end of the `register` method of `PackageServiceProvider` +- `bootingPackage`: will be called at the start of the `boot` method of `PackageServiceProvider` +- `packageBooted`: will be called at the end of the `boot` method of `PackageServiceProvider` + +## Testing + +```bash +composer test +``` + +This package now supports test groups as follows: + +```bash +composer test -- --group=blade +``` + +The current groups suported are: +* base +* assets +* blade +* commands +* config +* inertia +* migrations +* provider +* routes +* shareddata +* translations +* viewcomposer +* views +* installer + +Additionally, if you wish to test only backwards compatibility you can use: +* legacy + +**Note:** `InvalidPackage` exceptions thrown during Laravel application bootup are reported by Pest, +but because the occur before the start of a test case +Pest by default does not allow you intentionally to test for them being thrown. +The tests in this package now include checks for intentional `InvalidPackage` exceptions being thrown +by catching and saving such exceptions in the TestServiceProvider, +and then rethrowing the exception at the very start of a Pest test case, +and this is achieved by loading a modified version of the Pest `test()` function +before anything else is loaded. +Whilst this is done for you if you run `composer test`, +if you want to run `vendor/bin/pest` directly you now need to run it like this: + +```bash +php -d auto_prepend_file=tests/Prepend.php vendor/bin/pest +``` + +## Changelog + +Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. + +## Contributing + +Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details. + +## Security Vulnerabilities + +Please review [our security policy](../../security/policy) on how to report security vulnerabilities. + +## Credits + +- [Freek Van der Herten](https://github.com/freekmurze) +- [All Contributors](../../contributors) + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/vendor/spatie/laravel-package-tools/composer.json b/vendor/spatie/laravel-package-tools/composer.json new file mode 100644 index 00000000..40569c39 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/composer.json @@ -0,0 +1,52 @@ +{ + "name": "spatie/laravel-package-tools", + "description": "Tools for creating Laravel packages", + "keywords": [ + "spatie", + "laravel-package-tools" + ], + "homepage": "https://github.com/spatie/laravel-package-tools", + "license": "MIT", + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "require": { + "php": "^8.1", + "illuminate/contracts": "^10.0|^11.0|^12.0|^13.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^8.0|^9.2|^10.0|^11.0", + "pestphp/pest": "^2.1|^3.1|^4.0", + "phpunit/php-code-coverage": "^10.0|^11.0|^12.0", + "phpunit/phpunit": "^10.5|^11.5|^12.5", + "spatie/pest-plugin-test-time": "^2.2|^3.0" + }, + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Spatie\\LaravelPackageTools\\Tests\\": "tests" + } + }, + "scripts": { + "test": "php -d auto_prepend_file=tests/Prepend.php vendor/bin/pest --colors=always", + "test-coverage": "@test --coverage", + "test-dirty": "@test --dirty --compact" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/vendor/spatie/laravel-package-tools/src/Commands/Concerns/AskToRunMigrations.php b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/AskToRunMigrations.php new file mode 100644 index 00000000..e54fa1d1 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/AskToRunMigrations.php @@ -0,0 +1,28 @@ +askToRunMigrations = true; + + return $this; + } + + protected function processAskToRunMigrations(): self + { + if ($this->askToRunMigrations) { + if ($this->confirm('Would you like to run the migrations now?')) { + $this->comment('Running migrations...'); + + $this->call('migrate'); + } + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Commands/Concerns/AskToStarRepoOnGitHub.php b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/AskToStarRepoOnGitHub.php new file mode 100644 index 00000000..67bcbf6d --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/AskToStarRepoOnGitHub.php @@ -0,0 +1,39 @@ +starRepo = $vendorSlashRepoName; + $this->defaultStarAnswer = $defaultAnswer; + + return $this; + } + + protected function processStarRepo(): self + { + if ($this->starRepo) { + if ($this->confirm('Would you like to star our repo on GitHub?', $this->defaultStarAnswer)) { + $repoUrl = "https://github.com/{$this->starRepo}"; + + if (PHP_OS_FAMILY == 'Darwin') { + exec("open {$repoUrl}"); + } + if (PHP_OS_FAMILY == 'Windows') { + exec("start {$repoUrl}"); + } + if (PHP_OS_FAMILY == 'Linux') { + exec("xdg-open {$repoUrl}"); + } + } + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Commands/Concerns/PublishesResources.php b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/PublishesResources.php new file mode 100644 index 00000000..57898d01 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/PublishesResources.php @@ -0,0 +1,49 @@ +publishes = array_merge($this->publishes, $tag); + + return $this; + } + + public function publishAssets(): self + { + return $this->publish('assets'); + } + + public function publishConfigFile(): self + { + return $this->publish('config'); + } + + public function publishInertiaComponents(): self + { + return $this->publish('inertia-components'); + } + + public function publishMigrations(): self + { + return $this->publish('migrations'); + } + + protected function processPublishes(): self + { + foreach ($this->publishes as $tag) { + $name = str_replace('-', ' ', $tag); + $this->comment("Publishing {$name}..."); + + $this->callSilently("vendor:publish", [ + '--tag' => "{$this->package->shortName()}-{$tag}", + ]); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Commands/Concerns/SupportsServiceProviderInApp.php b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/SupportsServiceProviderInApp.php new file mode 100644 index 00000000..f3288d08 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/SupportsServiceProviderInApp.php @@ -0,0 +1,75 @@ +copyServiceProviderInApp = true; + + return $this; + } + + protected function processCopyServiceProviderInApp(): self + { + if ($this->copyServiceProviderInApp) { + $this->comment('Publishing service provider...'); + + $this->copyServiceProviderInApp(); + } + + return $this; + } + + protected function copyServiceProviderInApp(): self + { + $providerName = $this->package->publishableProviderName; + + if (! $providerName) { + return $this; + } + + $this->callSilent('vendor:publish', ['--tag' => $this->package->shortName() . '-provider']); + + $namespace = Str::replaceLast('\\', '', $this->laravel->getNamespace()); + + if (intval(app()->version()) < 11 || ! file_exists(base_path('bootstrap/providers.php'))) { + $appConfig = file_get_contents(config_path('app.php')); + } else { + $appConfig = file_get_contents(base_path('bootstrap/providers.php')); + } + + $class = '\\Providers\\' . Str::replace('/', '\\', $providerName) . '::class'; + + if (Str::contains($appConfig, $namespace . $class)) { + return $this; + } + + if (intval(app()->version()) < 11 || ! file_exists(base_path('bootstrap/providers.php'))) { + file_put_contents(config_path('app.php'), str_replace( + "{$namespace}\\Providers\\BroadcastServiceProvider::class,", + "{$namespace}\\Providers\\BroadcastServiceProvider::class," . PHP_EOL . " {$namespace}{$class},", + $appConfig + )); + } else { + file_put_contents(base_path('bootstrap/providers.php'), str_replace( + "{$namespace}\\Providers\\AppServiceProvider::class,", + "{$namespace}\\Providers\\AppServiceProvider::class," . PHP_EOL . " {$namespace}{$class},", + $appConfig + )); + } + + file_put_contents(app_path('Providers/' . $providerName . '.php'), str_replace( + "namespace App\Providers;", + "namespace {$namespace}\Providers;", + file_get_contents(app_path('Providers/' . $providerName . '.php')) + )); + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Commands/Concerns/SupportsStartWithEndWith.php b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/SupportsStartWithEndWith.php new file mode 100644 index 00000000..37006b67 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Commands/Concerns/SupportsStartWithEndWith.php @@ -0,0 +1,43 @@ +startWith = $callable; + + return $this; + } + + public function endWith(callable $callable): self + { + $this->endWith = $callable; + + return $this; + } + + protected function processStartWith(): self + { + if ($this->startWith) { + ($this->startWith)($this); + } + + return $this; + } + + protected function processEndWith(): self + { + if ($this->endWith) { + ($this->endWith)($this); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Commands/InstallCommand.php b/vendor/spatie/laravel-package-tools/src/Commands/InstallCommand.php new file mode 100644 index 00000000..a358dbda --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Commands/InstallCommand.php @@ -0,0 +1,48 @@ +signature = $package->shortName() . ':install'; + + $this->description = 'Install ' . $package->name; + + $this->package = $package; + + $this->hidden = true; + + parent::__construct(); + } + + public function handle() + { + $this + ->processStartWith() + ->processPublishes() + ->processAskToRunMigrations() + ->processCopyServiceProviderInApp() + ->processStarRepo() + ->processEndWith(); + + $this->info("{$this->package->shortName()} has been installed!"); + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasAssets.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasAssets.php new file mode 100644 index 00000000..91488f44 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasAssets.php @@ -0,0 +1,15 @@ +hasAssets = true; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasBladeComponents.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasBladeComponents.php new file mode 100644 index 00000000..1361aed6 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasBladeComponents.php @@ -0,0 +1,24 @@ +viewComponents[$viewComponentName] = $prefix; + + return $this; + } + + public function hasViewComponents(string $prefix, ...$viewComponentNames): static + { + foreach ($viewComponentNames as $componentName) { + $this->viewComponents[$componentName] = $prefix; + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasCommands.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasCommands.php new file mode 100644 index 00000000..d414d8e6 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasCommands.php @@ -0,0 +1,43 @@ +commands[] = $commandClassName; + + return $this; + } + + public function hasCommands(...$commandClassNames): static + { + $this->commands = array_merge( + $this->commands, + collect($commandClassNames)->flatten()->toArray() + ); + + return $this; + } + + public function hasConsoleCommand(string $commandClassName): static + { + $this->consoleCommands[] = $commandClassName; + + return $this; + } + + public function hasConsoleCommands(...$commandClassNames): static + { + $this->consoleCommands = array_merge( + $this->consoleCommands, + collect($commandClassNames)->flatten()->toArray() + ); + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasConfigs.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasConfigs.php new file mode 100644 index 00000000..329e3691 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasConfigs.php @@ -0,0 +1,21 @@ +shortName(); + + if (! is_array($configFileName)) { + $configFileName = [$configFileName]; + } + + $this->configFileNames = $configFileName; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasInertia.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasInertia.php new file mode 100644 index 00000000..3bacf6b3 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasInertia.php @@ -0,0 +1,17 @@ +hasInertiaComponents = true; + + $this->viewNamespace = $namespace; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasInstallCommand.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasInstallCommand.php new file mode 100644 index 00000000..b615e286 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasInstallCommand.php @@ -0,0 +1,19 @@ +consoleCommands[] = $installCommand; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasMigrations.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasMigrations.php new file mode 100644 index 00000000..d6edbe8c --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasMigrations.php @@ -0,0 +1,46 @@ +runsMigrations = $runsMigrations; + + return $this; + } + + public function hasMigration(string $migrationFileName): static + { + $this->migrationFileNames[] = $migrationFileName; + + return $this; + } + + public function hasMigrations(...$migrationFileNames): static + { + $this->migrationFileNames = array_merge( + $this->migrationFileNames, + collect($migrationFileNames)->flatten()->toArray() + ); + + return $this; + } + + public function discoversMigrations(bool $discoversMigrations = true, string $path = '/database/migrations'): static + { + $this->discoversMigrations = $discoversMigrations; + $this->migrationsPath = $path; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasRoutes.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasRoutes.php new file mode 100644 index 00000000..e785bf6c --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasRoutes.php @@ -0,0 +1,25 @@ +routeFileNames[] = $routeFileName; + + return $this; + } + + public function hasRoutes(...$routeFileNames): static + { + $this->routeFileNames = array_merge( + $this->routeFileNames, + collect($routeFileNames)->flatten()->toArray() + ); + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasServiceProviders.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasServiceProviders.php new file mode 100644 index 00000000..a1b7b1a3 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasServiceProviders.php @@ -0,0 +1,15 @@ +publishableProviderName = $providerName; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasTranslations.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasTranslations.php new file mode 100644 index 00000000..b5d2276c --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasTranslations.php @@ -0,0 +1,15 @@ +hasTranslations = true; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViewComposers.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViewComposers.php new file mode 100644 index 00000000..6f546d61 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViewComposers.php @@ -0,0 +1,21 @@ +viewComposers[$viewName] = $viewComposer; + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViewSharedData.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViewSharedData.php new file mode 100644 index 00000000..5a87b490 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViewSharedData.php @@ -0,0 +1,15 @@ +sharedViewData[$name] = $value; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViews.php b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViews.php new file mode 100644 index 00000000..d802c504 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/Package/HasViews.php @@ -0,0 +1,24 @@ +hasViews = true; + + $this->viewNamespace = $namespace; + + return $this; + } + + public function viewNamespace(): string + { + return $this->viewNamespace ?? $this->shortName(); + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessAssets.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessAssets.php new file mode 100644 index 00000000..641b94af --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessAssets.php @@ -0,0 +1,20 @@ +package->hasAssets || ! $this->app->runningInConsole()) { + return $this; + } + + $vendorAssets = $this->package->basePath('/../resources/dist'); + $appAssets = public_path("vendor/{$this->package->shortName()}"); + + $this->publishes([$vendorAssets => $appAssets], "{$this->package->shortName()}-assets"); + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessBladeComponents.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessBladeComponents.php new file mode 100644 index 00000000..71f18126 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessBladeComponents.php @@ -0,0 +1,26 @@ +package->viewComponents)) { + return $this; + } + + foreach ($this->package->viewComponents as $componentClass => $prefix) { + $this->loadViewComponentsAs($prefix, [$componentClass]); + } + + if ($this->app->runningInConsole()) { + $vendorComponents = $this->package->basePath('/Components'); + $appComponents = base_path("app/View/Components/vendor/{$this->package->shortName()}"); + + $this->publishes([$vendorComponents => $appComponents], "{$this->package->name}-components"); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessCommands.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessCommands.php new file mode 100644 index 00000000..90b759b7 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessCommands.php @@ -0,0 +1,28 @@ +package->commands)) { + return $this; + } + + $this->commands($this->package->commands); + + return $this; + } + + protected function bootPackageConsoleCommands(): self + { + if (empty($this->package->consoleCommands) || ! $this->app->runningInConsole()) { + return $this; + } + + $this->commands($this->package->consoleCommands); + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessConfigs.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessConfigs.php new file mode 100644 index 00000000..e88d6c9d --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessConfigs.php @@ -0,0 +1,65 @@ +package->configFileNames)) { + return $this; + } + + foreach ($this->package->configFileNames as $configFileName) { + $configFilePath = $this->normalizeConfigPath($configFileName); + $vendorConfig = $this->package->basePath("/../config/{$configFilePath}.php"); + + // Only mergeConfigFile if a .php file and not if a stub file + if (! is_file($vendorConfig)) { + continue; + } + + $normalizedConfigKey = $this->normalizeConfigKey($configFileName); + $this->mergeConfigFrom($vendorConfig, $normalizedConfigKey); + } + + return $this; + } + + protected function bootPackageConfigs(): self + { + if (empty($this->package->configFileNames) || ! $this->app->runningInConsole()) { + return $this; + } + + foreach ($this->package->configFileNames as $configFileName) { + $configFilePath = $this->normalizeConfigPath($configFileName); + + $vendorConfig ; + if ( + ! is_file($vendorConfig = $this->package->basePath("/../config/{$configFilePath}.php")) + && + ! is_file($vendorConfig = $this->package->basePath("/../config/{$configFilePath}.php.stub")) + ) { + continue; + } + + $this->publishes( + [$vendorConfig => config_path("{$configFilePath}.php")], + "{$this->package->shortName()}-config" + ); + } + + return $this; + } + + protected function normalizeConfigKey(string $configFileName): string + { + return str_replace(['/', '\\'], '.', $configFileName); + } + + protected function normalizeConfigPath(string $configFileName): string + { + return str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $configFileName); + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessInertia.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessInertia.php new file mode 100644 index 00000000..7516f21d --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessInertia.php @@ -0,0 +1,29 @@ +package->hasInertiaComponents) { + return $this; + } + + $namespace = $this->package->viewNamespace; + $directoryName = Str::of($this->packageView($namespace))->studly()->remove('-')->value(); + $vendorComponents = $this->package->basePath('/../resources/js/Pages'); + $appComponents = base_path("resources/js/Pages/{$directoryName}"); + + if ($this->app->runningInConsole()) { + $this->publishes( + [$vendorComponents => $appComponents], + "{$this->packageView($namespace)}-inertia-components" + ); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessMigrations.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessMigrations.php new file mode 100644 index 00000000..9bd85696 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessMigrations.php @@ -0,0 +1,109 @@ +package->discoversMigrations) { + $this->discoverPackageMigrations(); + + return $this; + } + + $now = Carbon::now(); + + foreach ($this->package->migrationFileNames as $migrationFileName) { + $vendorMigration = $this->package->basePath("/../database/migrations/{$migrationFileName}.php"); + + // Support for the .stub file extension + if (! file_exists($vendorMigration)) { + $vendorMigration .= '.stub'; + } + + if ($this->app->runningInConsole()) { + $appMigration = $this->generateMigrationName($migrationFileName, $now->addSecond()); + + $this->publishes( + [$vendorMigration => $appMigration], + "{$this->package->shortName()}-migrations" + ); + } + + if ($this->package->runsMigrations) { + $this->loadMigrationsFrom($vendorMigration); + } + } + + return $this; + } + + protected function discoverPackageMigrations(): void + { + $now = Carbon::now(); + $migrationsPath = trim($this->package->migrationsPath, '/'); + + $files = (new Filesystem())->files($this->package->basePath("/../{$migrationsPath}")); + + foreach ($files as $file) { + $filePath = $file->getPathname(); + $migrationFileName = Str::replace(['.stub', '.php'], '', $file->getFilename()); + + // Publish but do not add timestamp to non migration files + if (Str::endsWith($filePath, [".php", ".php.stub"])) { + $appMigration = $this->generateMigrationName($migrationFileName, $now->addSecond()); + } else { + $appMigration = database_path("migrations/{$file->getFilename()}"); + } + + if ($this->app->runningInConsole()) { + $this->publishes( + [$filePath => $appMigration], + "{$this->package->shortName()}-migrations" + ); + } + + // Do not load non migration files + if ($this->package->runsMigrations && Str::endsWith($filePath, [".php", ".php.stub"])) { + $this->loadMigrationsFrom($filePath); + } + } + } + + protected function generateMigrationName(string $migrationFileName, Carbon|CarbonImmutable $now): string + { + $migrationsPath = 'migrations/' . dirname($migrationFileName) . '/'; + $migrationFileName = basename($migrationFileName); + + $migrationFileName = self::stripTimestampPrefix($migrationFileName); + + $len = strlen($migrationFileName) + 4; + + if (Str::contains($migrationFileName, '/')) { + $migrationsPath .= Str::of($migrationFileName)->beforeLast('/')->finish('/'); + $migrationFileName = Str::of($migrationFileName)->afterLast('/'); + } + + foreach (glob(database_path("{$migrationsPath}*.php")) as $filename) { + if ((substr($filename, -$len) === $migrationFileName . '.php')) { + return $filename; + } + } + + $timestamp = $now->format('Y_m_d_His'); + $formattedFileName = Str::of($migrationFileName)->snake()->finish('.php'); + + return database_path("{$migrationsPath}{$timestamp}_{$formattedFileName}"); + } + + private static function stripTimestampPrefix(string $filename): string + { + return preg_replace('/^\d{4}_\d{2}_\d{2}_\d{6}_/', '', $filename); + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessRoutes.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessRoutes.php new file mode 100644 index 00000000..832f2137 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessRoutes.php @@ -0,0 +1,19 @@ +package->routeFileNames)) { + return $this; + } + + foreach ($this->package->routeFileNames as $routeFileName) { + $this->loadRoutesFrom("{$this->package->basePath('/../routes/')}{$routeFileName}.php"); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessServiceProviders.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessServiceProviders.php new file mode 100644 index 00000000..8f79bee6 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessServiceProviders.php @@ -0,0 +1,21 @@ +package->publishableProviderName || ! $this->app->runningInConsole()) { + return $this; + } + + $providerName = $this->package->publishableProviderName; + $vendorProvider = $this->package->basePath("/../resources/stubs/{$providerName}.php.stub"); + $appProvider = base_path("app/Providers/{$providerName}.php"); + + $this->publishes([$vendorProvider => $appProvider], "{$this->package->shortName()}-provider"); + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessTranslations.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessTranslations.php new file mode 100644 index 00000000..21da0c85 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessTranslations.php @@ -0,0 +1,32 @@ +package->hasTranslations) { + return $this; + } + + $vendorTranslations = $this->package->basePath('/../resources/lang'); + $appTranslations = (function_exists('lang_path')) + ? lang_path("vendor/{$this->package->shortName()}") + : resource_path("lang/vendor/{$this->package->shortName()}"); + + $this->loadTranslationsFrom($vendorTranslations, $this->package->shortName()); + + $this->loadJsonTranslationsFrom($vendorTranslations); + $this->loadJsonTranslationsFrom($appTranslations); + + if ($this->app->runningInConsole()) { + $this->publishes( + [$vendorTranslations => $appTranslations], + "{$this->package->shortName()}-translations" + ); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewComposers.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewComposers.php new file mode 100644 index 00000000..ca1f252f --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewComposers.php @@ -0,0 +1,21 @@ +package->viewComposers)) { + return $this; + } + + foreach ($this->package->viewComposers as $viewName => $viewComposer) { + View::composer($viewName, $viewComposer); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewSharedData.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewSharedData.php new file mode 100644 index 00000000..71d8662b --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViewSharedData.php @@ -0,0 +1,21 @@ +package->sharedViewData)) { + return $this; + } + + foreach ($this->package->sharedViewData as $name => $value) { + View::share($name, $value); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViews.php b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViews.php new file mode 100644 index 00000000..9f623b6b --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Concerns/PackageServiceProvider/ProcessViews.php @@ -0,0 +1,26 @@ +package->hasViews) { + return $this; + } + + $namespace = $this->package->viewNamespace; + $viewsPath = $this->package->basePath('/../resources/views'); + $vendorViews = realpath($viewsPath) ?: $viewsPath; + $appViews = base_path("resources/views/vendor/{$this->packageView($namespace)}"); + + $this->loadViewsFrom($vendorViews, $this->package->viewNamespace()); + + if ($this->app->runningInConsole()) { + $this->publishes([$vendorViews => $appViews], "{$this->packageView($namespace)}-views"); + } + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Exceptions/InvalidPackage.php b/vendor/spatie/laravel-package-tools/src/Exceptions/InvalidPackage.php new file mode 100644 index 00000000..6e5d79d4 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Exceptions/InvalidPackage.php @@ -0,0 +1,13 @@ +name("yourName")`'); + } +} diff --git a/vendor/spatie/laravel-package-tools/src/Package.php b/vendor/spatie/laravel-package-tools/src/Package.php new file mode 100644 index 00000000..d5e5f039 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/Package.php @@ -0,0 +1,67 @@ +name = $name; + + return $this; + } + + public function shortName(): string + { + return Str::after($this->name, 'laravel-'); + } + + public function basePath(?string $directory = null): string + { + if ($directory === null) { + return $this->basePath; + } + + return $this->basePath . DIRECTORY_SEPARATOR . ltrim($directory, DIRECTORY_SEPARATOR); + } + + public function setBasePath(string $path): static + { + $this->basePath = $path; + + return $this; + } +} diff --git a/vendor/spatie/laravel-package-tools/src/PackageServiceProvider.php b/vendor/spatie/laravel-package-tools/src/PackageServiceProvider.php new file mode 100644 index 00000000..7bf3f406 --- /dev/null +++ b/vendor/spatie/laravel-package-tools/src/PackageServiceProvider.php @@ -0,0 +1,126 @@ +registeringPackage(); + + $this->package = $this->newPackage(); + $this->package->setBasePath($this->getPackageBaseDir()); + + $this->configurePackage($this->package); + if (empty($this->package->name)) { + throw InvalidPackage::nameIsRequired(); + } + + $this->registerPackageConfigs(); + + $this->packageRegistered(); + + return $this; + } + + public function registeringPackage() + { + } + + public function newPackage(): Package + { + return new Package(); + } + + public function packageRegistered() + { + } + + public function boot() + { + $this->bootingPackage(); + + $this + ->bootPackageAssets() + ->bootPackageBladeComponents() + ->bootPackageCommands() + ->bootPackageConsoleCommands() + ->bootPackageConfigs() + ->bootPackageInertia() + ->bootPackageMigrations() + ->bootPackageRoutes() + ->bootPackageServiceProviders() + ->bootPackageTranslations() + ->bootPackageViews() + ->bootPackageViewComposers() + ->bootPackageViewSharedData() + ->packageBooted(); + + return $this; + } + + public function bootingPackage() + { + } + + public function packageBooted() + { + } + + protected function getPackageBaseDir(): string + { + $reflector = new ReflectionClass(get_class($this)); + + $packageBaseDir = dirname($reflector->getFileName()); + + // Some packages like to keep Laravels directory structure and place + // the service providers in a Providers folder. + // move up a level when this is the case. + if (str_ends_with($packageBaseDir, DIRECTORY_SEPARATOR.'Providers')) { + $packageBaseDir = dirname($packageBaseDir); + } + + return $packageBaseDir; + } + + public function packageView(?string $namespace): ?string + { + return is_null($namespace) + ? $this->package->shortName() + : $this->package->viewNamespace; + } +} diff --git a/vendor/swagger-api/swagger-ui/.agignore b/vendor/swagger-api/swagger-ui/.agignore deleted file mode 100644 index 849ddff3..00000000 --- a/vendor/swagger-api/swagger-ui/.agignore +++ /dev/null @@ -1 +0,0 @@ -dist/ diff --git a/vendor/swagger-api/swagger-ui/.browserslistrc b/vendor/swagger-api/swagger-ui/.browserslistrc deleted file mode 100644 index f5d4abc4..00000000 --- a/vendor/swagger-api/swagger-ui/.browserslistrc +++ /dev/null @@ -1,29 +0,0 @@ -[node-production] -maintained node versions - -[node-development] -node 24 - -[browser-production] -> 1% -last 2 versions -Firefox ESR -not dead - -[browser-development] -last 1 chrome version -last 1 firefox version -last 1 safari version - -[isomorphic-production] -> 1% -last 2 versions -Firefox ESR -not dead -maintained node versions - -[isomorphic-development] -last 1 chrome version -last 1 firefox version -last 1 safari version -node 24 diff --git a/vendor/swagger-api/swagger-ui/.claude/SKILL_USAGE_EXAMPLE.md b/vendor/swagger-api/swagger-ui/.claude/SKILL_USAGE_EXAMPLE.md deleted file mode 100644 index af1b863b..00000000 --- a/vendor/swagger-api/swagger-ui/.claude/SKILL_USAGE_EXAMPLE.md +++ /dev/null @@ -1,565 +0,0 @@ -# Skill Usage Example: Adding OAS 4.0 Support - -This document demonstrates how to use the `/add-oas-support` skill to add OpenAPI 4.0 support to Swagger UI. - -## Scenario - -You want to add support for OpenAPI Specification 4.0 (a hypothetical major version) to Swagger UI. OAS 4.0 includes: -- New `workflows` top-level field -- New `asyncAPI` authentication type -- Updated JSON Schema to Draft 2024 -- Enhanced callback syntax -- New `info.license.attribution` field - -## Step-by-Step Usage - -### 1. Start the Skill - -```bash -/add-oas-support --version 4.0 --type major -``` - -### 2. Answer Questions - -Claude will ask you questions about the new version: - -**Q: What is the exact version number?** -A: 4.0 - -**Q: Is it a major or minor version?** -A: Major (breaking changes from OAS 3.x) - -**Q: Does it use a new JSON Schema version?** -A: Yes, JSON Schema Draft 2024 - -**Q: What are the new top-level fields?** -A: `workflows` - defines reusable workflow definitions - -**Q: What features are added/removed/changed?** -A: -- Added: `workflows` object for workflow automation -- Added: `asyncAPI` authentication type -- Added: `info.license.attribution` field -- Changed: Callback syntax enhanced with workflow references -- Removed: Deprecated `x-` extension fields from OAS 3.0 - -**Q: Are there new authentication types?** -A: Yes, `asyncAPI` type for asynchronous authentication flows - -### 3. Claude Creates Plugin Structure - -Claude will execute: - -```bash -mkdir -p src/core/plugins/oas40/{auth-extensions,oas31-extensions,spec-extensions,components/auth,wrap-components} -``` - -### 4. Claude Generates Files - -#### 4a. Version Detection (src/core/plugins/oas40/fn.js) - -```javascript -/** - * @prettier - */ - -export const isOAS40 = (jsSpec) => { - const oasVersion = jsSpec.get("openapi") - return ( - typeof oasVersion === "string" && - /^4\.0\.(?:[1-9]\d*|0)$/.test(oasVersion) - ) -} - -export const createOnlyOAS40Selector = - (selector) => - (state, ...args) => - (system) => { - if (system.getSystem().specSelectors.isOAS40()) { - const selectedValue = selector(state, ...args) - return typeof selectedValue === "function" - ? selectedValue(system) - : selectedValue - } else { - return null - } - } - -// ... other factory functions -``` - -#### 4b. Main Plugin Export (src/core/plugins/oas40/index.js) - -```javascript -/** - * @prettier - */ - -import afterLoad from "./after-load.js" -import { - isOAS40, - createSystemSelector, - createOnlyOAS40Selector, - createOnlyOAS40SelectorWrapper, - createOnlyOAS40ComponentWrapper, -} from "./fn.js" - -import Workflows from "./components/workflows.jsx" -import AsyncAPIAuth from "./components/auth/async-api-auth.jsx" -import OAS40License from "./components/license.jsx" -// ... other imports - -const OpenAPI40Plugin = ({ fn }) => { - return { - fn: { - oas40: { - isOAS40, - createSystemSelector, - createOnlyOAS40Selector, - createOnlyOAS40SelectorWrapper, - createOnlyOAS40ComponentWrapper, - }, - }, - components: { - Workflows, - AsyncAPIAuth, - OAS40License, - }, - wrapComponents: { - // Wrap existing components - }, - statePlugins: { - spec: { - selectors: { - isOAS40: createSystemSelector(selectIsOAS40), - workflows: createOnlyOAS40Selector(selectWorkflows), - }, - wrapSelectors: { - isOAS31: isOAS31SelectorWrapper, - isOAS3: isOAS3SelectorWrapper, - }, - }, - auth: { - wrapSelectors: { - definitionsToAuthorize: definitionsToAuthorizeWrapper, - }, - }, - oas40: { - selectors: {}, - }, - }, - afterLoad, - } -} - -export default OpenAPI40Plugin -``` - -#### 4c. Workflows Component (src/core/plugins/oas40/components/workflows.jsx) - -```javascript -/** - * @prettier - */ - -import React from "react" -import PropTypes from "prop-types" - -const Workflows = ({ specSelectors, getComponent }) => { - const workflows = specSelectors.workflows() - - if (!workflows || !workflows.size) { - return null - } - - const Operations = getComponent("operations", true) - - return ( -
-

Workflows

-
-

- Reusable workflow definitions that can be referenced from operations. -

-
- {workflows.entrySeq().map(([workflowName, workflow]) => ( -
-

{workflowName}

-

{workflow.get("description")}

- {/* Render workflow steps */} -
- ))} -
- ) -} - -Workflows.propTypes = { - specSelectors: PropTypes.object.isRequired, - getComponent: PropTypes.func.isRequired, -} - -export default Workflows -``` - -#### 4d. AsyncAPI Auth Component (src/core/plugins/oas40/components/auth/async-api-auth.jsx) - -```javascript -/** - * @prettier - */ - -import React from "react" -import PropTypes from "prop-types" - -const AsyncAPIAuth = ({ schema, name, getComponent, onChange, authorized }) => { - const authValue = authorized && authorized.getIn([name]) - const Input = getComponent("Input") - const Button = getComponent("Button") - - return ( -
-

AsyncAPI Authentication

-

{schema.get("description")}

- onChange({ key: e.target.value })} - /> - -
- ) -} - -AsyncAPIAuth.propTypes = { - schema: PropTypes.object.isRequired, - name: PropTypes.string.isRequired, - getComponent: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - authorized: PropTypes.object, -} - -export default AsyncAPIAuth -``` - -### 5. Claude Registers Plugin - -Updates `src/core/presets/apis/index.js`: - -```javascript -import OpenAPI40Plugin from "../../plugins/oas40/index.js" - -export default function PresetApis() { - return [ - BasePreset, - OpenAPI30Plugin, - JSONSchema202012Plugin, - JSONSchema202012SamplesPlugin, - OpenAPI31Plugin, - JSONSchema2024Plugin, // New JSON Schema version - JSONSchema2024SamplesPlugin, - OpenAPI40Plugin, // LAST - overrides all previous - ] -} -``` - -### 6. Claude Adds Tests - -#### Unit Test (test/unit/core/plugins/oas40/fn.js) - -```javascript -/** - * @prettier - */ - -import { fromJS } from "immutable" -import { isOAS40 } from "src/core/plugins/oas40/fn.js" - -describe("oas40 plugin - fn - isOAS40", () => { - it("should match OpenAPI 4.0.0", () => { - const spec = fromJS({ openapi: "4.0.0" }) - expect(isOAS40(spec)).toBe(true) - }) - - it("should match OpenAPI 4.0.15", () => { - const spec = fromJS({ openapi: "4.0.15" }) - expect(isOAS40(spec)).toBe(true) - }) - - it("should NOT match OpenAPI 4.0", () => { - const spec = fromJS({ openapi: "4.0" }) - expect(isOAS40(spec)).toBe(false) - }) - - it("should NOT match OpenAPI 3.1.0", () => { - const spec = fromJS({ openapi: "3.1.0" }) - expect(isOAS40(spec)).toBe(false) - }) -}) -``` - -#### E2E Test (test/e2e-cypress/e2e/features/oas40/workflows.cy.js) - -```javascript -/** - * @prettier - */ - -describe("OpenAPI 4.0 - Workflows", () => { - it("should render workflows section", () => { - cy.visit("/e2e-cypress/static/documents/oas40/petstore-workflows.yaml") - cy.get(".opblock-tag").contains("Workflows").should("exist") - }) - - it("should display workflow definitions", () => { - cy.visit("/e2e-cypress/static/documents/oas40/petstore-workflows.yaml") - cy.get(".opblock-workflow").should("have.length.greaterThan", 0) - }) -}) -``` - -### 7. Claude Updates Documentation - -Updates to `README.md`: - -```markdown -## Compatibility - -Swagger UI supports the following OpenAPI Specification versions: - -- OpenAPI 2.0 (Swagger) -- OpenAPI 3.0.x -- OpenAPI 3.1.x -- OpenAPI 4.0.x ⭐ NEW -``` - -Updates to `CLAUDE.md`: - -```markdown -### 26 Core Plugins - -- `oas3` - OpenAPI 3.0.x support -- `oas31` - OpenAPI 3.1.x support -- `oas40` - OpenAPI 4.0.x support (NEW) -``` - -### 8. Claude Runs Tests - -```bash -# Lint -npm run lint-errors - -# Unit tests -npm run test:unit - -# Build -npm run build - -# Artifact tests -npm run test:artifact - -# E2E tests -npm run cy:ci -``` - -### 9. Claude Creates Commit - -```bash -git add src/core/plugins/oas40/ -git add src/core/presets/apis/index.js -git add test/unit/core/plugins/oas40/ -git add test/e2e-cypress/e2e/features/oas40/ -git add README.md CLAUDE.md - -git commit -m "$(cat <<'EOF' -feat(oas40): add support for OpenAPI 4.0 - -Implement OpenAPI 4.0.x specification support with: -- Version detection and plugin architecture -- Workflows rendering for reusable workflow definitions -- AsyncAPI authentication type support -- JSON Schema Draft 2024 integration -- Enhanced callback syntax with workflow references -- License attribution field support - -Breaking changes from OAS 3.1: -- Removed deprecated x- extension fields -- Updated JSON Schema to Draft 2024 - -Includes comprehensive unit and E2E tests. - -Co-Authored-By: Claude Sonnet 4.5 -EOF -)" -``` - -### 10. Result - -You now have complete OpenAPI 4.0 support in Swagger UI: - -``` -src/core/plugins/oas40/ -├── index.js (367 lines) -├── fn.js (124 lines) -├── selectors.js (45 lines) -├── after-load.js (78 lines) -├── auth-extensions/ -│ └── wrap-selectors.js (32 lines) -├── oas31-extensions/ -│ └── fn.js (56 lines) -├── spec-extensions/ -│ ├── selectors.js (89 lines) -│ └── wrap-selectors.js (23 lines) -├── components/ -│ ├── workflows.jsx (156 lines) -│ ├── license.jsx (67 lines) -│ ├── version-pragma-filter.jsx (78 lines) -│ └── auth/ -│ └── async-api-auth.jsx (54 lines) -└── wrap-components/ - ├── info.jsx (12 lines) - └── license.jsx (12 lines) - -test/unit/core/plugins/oas40/ -├── fn.js (67 lines) -└── components/ - └── version-pragma-filter.jsx (45 lines) - -test/e2e-cypress/e2e/features/oas40/ -├── workflows.cy.js (34 lines) -└── async-api-auth.cy.js (28 lines) -``` - -**Total:** ~1,367 lines of code, fully tested and documented. - -## Benefits of Using the Skill - -### Without the Skill (Manual Implementation) - -**Estimated Time:** 40-60 hours - -**Challenges:** -- Understanding plugin architecture (4-6 hours) -- Studying OAS 3.1 patterns (3-4 hours) -- Creating file structure (1 hour) -- Implementing version detection (2 hours) -- Creating selector factories (4-6 hours) -- Implementing components (12-16 hours) -- Writing tests (8-12 hours) -- Debugging integration issues (6-8 hours) -- Documentation (2-3 hours) - -**Error-prone areas:** -- Plugin loading order -- Selector wrapping logic -- Component lifecycle -- Redux state management -- afterLoad hook timing - -### With the Skill (Guided Implementation) - -**Estimated Time:** 8-12 hours - -**Benefits:** -- Automated boilerplate generation -- Follows established patterns -- Guided step-by-step process -- Built-in error checking -- Comprehensive test coverage -- Documentation templates -- Best practices enforced - -**What the skill handles:** -- ✅ File structure creation -- ✅ Boilerplate code generation -- ✅ Pattern adherence -- ✅ Test scaffolding -- ✅ Documentation updates -- ✅ Build verification -- ✅ Commit message formatting - -## Customization Options - -The skill is flexible and can be customized per version: - -### For Minor Versions (e.g., 3.2) - -```bash -/add-oas-support --version 3.2 --type minor -``` - -Claude will: -- Create lighter plugin structure -- Reuse more from previous version -- Focus on incremental additions -- Skip breaking change handling -- Minimize component wrapping - -### For Major Versions (e.g., 5.0) - -```bash -/add-oas-support --version 5.0 --type major -``` - -Claude will: -- Create comprehensive plugin structure -- Implement extensive component wrapping -- Handle breaking changes -- Create new base components if needed -- Add deprecation warnings -- Update preset configurations - -## Troubleshooting - -### Skill Not Found - -``` -Error: Skill 'add-oas-support' not found -``` - -**Solution:** Ensure the skill file exists in `.claude/skills/add-oas-support.md` - -### Version Already Exists - -``` -Error: Plugin for OAS 4.0 already exists -``` - -**Solution:** Remove existing plugin or choose a different version - -### Build Failures - -``` -Error: Module not found -``` - -**Solution:** Check plugin registration in presets and import paths - -### Test Failures - -``` -Error: isOAS40 is not a function -``` - -**Solution:** Verify selector is properly exported and registered - -## Next Steps - -After the skill completes: - -1. **Test with real specs** - Use actual OAS 4.0 examples -2. **Review generated code** - Ensure it matches your requirements -3. **Add custom features** - Extend beyond the boilerplate -4. **Optimize performance** - Profile and optimize hot paths -5. **Submit PR** - Follow the PR template and guidelines - -## Additional Resources - -- **OAS 4.0 Spec:** https://spec.openapis.org/oas/v4.0.0 (hypothetical) -- **Plugin API:** `docs/customization/plugin-api.md` -- **CLAUDE.md:** Complete codebase guide -- **Skill Source:** `.claude/skills/add-oas-support.md` - ---- - -**Note:** This example uses hypothetical OAS 4.0 features for demonstration purposes. Adapt the skill usage to match the actual specification features of the version you're implementing. diff --git a/vendor/swagger-api/swagger-ui/.claude/skills/README.md b/vendor/swagger-api/swagger-ui/.claude/skills/README.md deleted file mode 100644 index 0d554806..00000000 --- a/vendor/swagger-api/swagger-ui/.claude/skills/README.md +++ /dev/null @@ -1,151 +0,0 @@ -# Swagger UI Claude Skills - -This directory contains custom skills for working with the Swagger UI codebase in Claude Code. - -## Available Skills - -### `/add-oas-support` - Add OpenAPI Specification Version Support - -Comprehensive skill for adding support for a new OpenAPI Specification version to Swagger UI. - -**Usage:** -``` -/add-oas-support --version 4.0 --type major -/add-oas-support --version 3.2 --type minor -``` - -**Parameters:** -- `--version` (required): The OpenAPI version to add support for (e.g., "3.2", "4.0") -- `--type` (optional): Version type - "major" or "minor" (default: "minor") - -**Structure:** -The skill includes both a **Quick Reference** section at the top for experienced developers, and a **comprehensive guide** below for detailed implementation instructions. - -**What it does:** -1. **Analyzes the target OAS version specification using WebFetch** - - Fetches official spec from https://spec.openapis.org/ - - Systematically identifies new/modified/removed fields - - Maps specification changes to Swagger UI components - - Creates specification change document -2. Creates the plugin directory structure -3. Implements version detection logic -4. Creates selector factories and component wrappers -5. Implements new feature components based on spec analysis -6. Handles authentication changes -7. Registers plugin in presets -8. Adds unit and E2E tests -9. Updates documentation -10. Runs full test suite - -**Key Features:** -- ✅ **Quick Reference** section for rapid development -- ✅ Comprehensive specification analysis workflow using WebFetch -- ✅ Detailed mapping table from spec changes → components (15+ change types) -- ✅ Real examples from OAS 3.1 implementation (6 detailed examples) -- ✅ Iterative spec-driven development approach -- ✅ Component-by-component verification checklist -- ✅ Best practices for continuous spec reference -- ✅ WebFetch query templates for spec analysis -- ✅ Common pitfalls and solutions -- ✅ Pre-submit checklist - -**Based on:** -This skill follows the patterns established by the OAS 3.1 implementation (commit history analyzed from `src/core/plugins/oas31/`). - -**Key patterns:** -- Plugin-based architecture with Redux state management -- Selector factories for version-specific logic -- Component wrapping for conditional rendering -- afterLoad lifecycle hooks for function overrides -- Plugin loading order (new version loaded LAST) - -**Prerequisites:** -- Understanding of Swagger UI plugin architecture -- Access to the new OAS specification document -- All existing tests passing - -**Example workflow:** - -Adding OAS 4.0 support: -``` -# Start the skill -/add-oas-support --version 4.0 --type major - -# Claude will: -# 1. Ask about new features in OAS 4.0 -# 2. Create plugin directory structure -# 3. Generate boilerplate code -# 4. Guide through implementation -# 5. Add tests -# 6. Update documentation -# 7. Verify build -``` - -## Creating New Skills - -To create a new skill for Swagger UI: - -1. Create a markdown file in `.claude/skills/` -2. Add frontmatter with skill metadata: - ```yaml - --- - name: skill-name - description: Brief description - args: - param1: - description: "Parameter description" - required: true - type: string - --- - ``` -3. Write comprehensive instructions following the patterns in existing skills -4. Document common pitfalls and best practices -5. Include code templates with placeholders -6. Add to this README - -## Skill Development Guidelines - -When creating skills for Swagger UI: - -1. **Follow project conventions:** - - No semicolons - - Double quotes - - @prettier pragma in all new files - - .jsx extension for React components - -2. **Use the plugin architecture:** - - Don't modify core unnecessarily - - Follow established patterns - - Load new plugins at the end of presets - -3. **Include comprehensive tests:** - - Unit tests for logic - - Component tests for UI - - E2E tests for integration - -4. **Document thoroughly:** - - Update CLAUDE.md - - Update relevant docs - - Add inline JSDoc comments - -5. **Security first:** - - Use DOMPurify for HTML - - Validate all input - - Follow OWASP guidelines - -## Contributing - -To contribute new skills: - -1. Fork the repository -2. Create skill in `.claude/skills/` -3. Test thoroughly -4. Update this README -5. Submit pull request - -## Resources - -- **CLAUDE.md** - Comprehensive codebase guide -- **Plugin API** - `docs/customization/plugin-api.md` -- **Development Setup** - `docs/development/setting-up.md` -- **Contributing Guide** - https://github.com/swagger-api/.github/blob/HEAD/CONTRIBUTING.md diff --git a/vendor/swagger-api/swagger-ui/.claude/skills/add-oas-support.md b/vendor/swagger-api/swagger-ui/.claude/skills/add-oas-support.md deleted file mode 100644 index 36062c30..00000000 --- a/vendor/swagger-api/swagger-ui/.claude/skills/add-oas-support.md +++ /dev/null @@ -1,2333 +0,0 @@ ---- -name: add-oas-support -description: Add support for a new OpenAPI Specification version to Swagger UI -args: - version: - description: "OpenAPI version to add support for (e.g., 3.2, 4.0)" - required: true - type: string - type: - description: "Version type: 'major' for new major version, 'minor' for patch/minor" - required: false - type: string - default: "minor" ---- - -# Add OpenAPI Specification Version Support - -This skill guides you through adding support for a new OpenAPI Specification (OAS) version to Swagger UI, following the established architectural patterns from OAS 3.1 implementation. - ---- - -## 📌 Quick Reference - -> **New to this skill?** Skip to [Prerequisites](#prerequisites) for the full guide. -> **Already familiar?** Use this quick reference for rapid development. - -### 🚀 Quick Start - -```bash -# Major version (e.g., OAS 4.0) -/add-oas-support --version 4.0 --type major - -# Minor version (e.g., OAS 3.2) -/add-oas-support --version 3.2 --type minor -``` - -### 📋 Essential Checklist - -#### Phase 1: Specification Analysis ⭐ MOST IMPORTANT -- [ ] Access official spec at **https://spec.openapis.org/oas/v{VERSION}/** -- [ ] Use WebFetch to analyze spec systematically -- [ ] Create specification change document -- [ ] Map changes to components using [mapping table](#step-1d-map-specification-changes-to-swagger-ui-components) - -#### Phase 2: Implementation -- [ ] Create plugin directory structure -- [ ] Implement version detection (`isOAS{VERSION}`) -- [ ] Create selector factories -- [ ] Implement components for each spec change -- [ ] Wrap existing components if modified -- [ ] Register plugin in preset (LAST position) - -#### Phase 3: Testing & Documentation -- [ ] Add unit tests -- [ ] Add E2E tests with spec examples -- [ ] Update documentation -- [ ] Verify build & run full test suite - -### 🔍 WebFetch Queries for Spec Analysis - -```javascript -// 1. Fetch main spec differences -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "List all new top-level fields, modified fields, and removed fields compared to version {PREV_VERSION}" -) - -// 2. Analyze new feature -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "Describe the '{NEW_FIELD}' field: structure, type, purpose, required/optional, and provide examples" -) - -// 3. Check JSON Schema version -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "What JSON Schema version does this use? List changes from {PREV_JSON_SCHEMA_VERSION}" -) - -// 4. Security schemes -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "List all security scheme types, highlighting new or changed types" -) -``` - -### 🗺️ Spec Change → Component Mapping (Quick) - -| Spec Change | Component Action | File Location | -|-------------|------------------|---------------| -| **New top-level field (Object)** | Create new component | `components/{field}.jsx` | -| **New top-level field (String)** | Add to info or create display | `components/{field}.jsx` | -| **Modified Info object** | Wrap Info component | `wrap-components/info.jsx` | -| **New info subfield** | Create component + wrap Info | `components/info-{field}.jsx` | -| **New auth type** | Create auth component | `components/auth/{type}.jsx` | -| **Modified security** | Wrap auth selector | `auth-extensions/wrap-selectors.js` | -| **New operation field** | Extend Operation/selector | `spec-extensions/selectors.js` | -| **JSON Schema keyword** | Create keyword component | `json-schema-{V}-extensions/` | -| **JSON Schema version** | Create entire new plugin | `plugins/json-schema-{V}/` | - -See [full mapping table with examples](#step-1d-map-specification-changes-to-swagger-ui-components) for detailed guidance. - -### 📝 Component Implementation Pattern - -**For each new feature:** - -1. **Check spec first:** - ```javascript - WebFetch(url, "Describe {field} structure, type, examples") - ``` - -2. **Create selector:** - ```javascript - // spec-extensions/selectors.js - export const selectNewField = createSelector( - (state) => state, - (state) => state.getIn(["spec", "json", "newField"]) - ) - ``` - -3. **Create component:** - ```javascript - // components/new-field.jsx - const NewField = ({ specSelectors, getComponent }) => { - const data = specSelectors.selectNewField() - if (!data) return null - return
{/* render based on spec structure */}
- } - ``` - -4. **Register in plugin:** - ```javascript - // index.js - components: { NewField }, - statePlugins: { - spec: { - selectors: { - selectNewField: createOnlyOAS{V}Selector(selectNewField) - } - } - } - ``` - -5. **Add tests** (unit + E2E with spec examples) - -### ⚠️ Common Pitfalls - -1. ❌ **Guessing field structure** → ✅ Always check spec with WebFetch -2. ❌ **Forgetting @prettier pragma** → ✅ Add to all new files -3. ❌ **Wrong plugin load order** → ✅ New plugin LAST in preset -4. ❌ **Using semicolons** → ✅ No semicolons (project convention) -5. ❌ **Single quotes** → ✅ Use double quotes -6. ❌ **Skipping spec examples** → ✅ Use as test fixtures -7. ❌ **Hardcoded assumptions** → ✅ Verify everything in spec -8. ❌ **`props.Ori` in ComponentWrapper** → ✅ Use `props.originalComponent` (see Pattern 3) -9. ❌ **Copying entire Info component for minor version** → ✅ Use OpenAPIVersion wrapper + `getComponent("OAS{PREV}Info")` -10. ❌ **OAS{VERSION} logic in OAS{PREV} plugin** → ✅ Each version's logic lives in its own plugin -11. ❌ **`isOAS{PREV}` wrapper on minor version** → ✅ Only add if regex overlaps; minor bumps don't need it -12. ❌ **New HTTP method in core `validOperationMethods`** → ✅ Add to `OPERATION_METHODS` + wrap with `createOnlyOAS{VERSION}SelectorWrapper` -13. ❌ **Assuming new OAS meta-schema = new JSON Schema dialect** → ✅ Verify — OAS 3.2 still uses JSON Schema 2020-12 -14. ❌ **Inline version guard in wrap-components** → ✅ Always use `createOnlyOAS{VERSION}ComponentWrapper`; never write `(Original, system) => (props) => { if (...isOAS{VERSION}...) }` by hand — even expanding an existing guard (`isOAS31 || isOAS32`) is wrong; add dedicated wrap-components in the new plugin instead - -### ✅ Pre-Submit Checklist - -- [ ] All spec changes mapped to components -- [ ] All components reference spec in comments -- [ ] Tests use examples from spec -- [ ] Version detection regex correct -- [ ] Plugin loaded last in preset -- [ ] All tests passing (lint + unit + E2E) -- [ ] Build completes successfully -- [ ] Documentation updated -- [ ] No semicolons, double quotes everywhere -- [ ] @prettier pragma in all new files - -### 📚 Quick Links - -- **OAS Specs:** https://spec.openapis.org/ -- **OAS 3.1:** https://spec.openapis.org/oas/v3.1.0/ -- **OAS 3.0:** https://spec.openapis.org/oas/v3.0.3/ -- **JSON Schema:** https://json-schema.org/ -- **CLAUDE.md:** Complete codebase guide - -### 🔖 Full Guide Navigation - -Need detailed guidance? Jump to these sections: - -- **[Prerequisites](#prerequisites)** - Setup and requirements -- **[Architecture Overview](#architecture-overview)** - Plugin system explained -- **[Key Design Patterns](#key-design-patterns-from-oas-31)** - Code patterns from OAS 3.1 -- **[Step 1: Analyze Specification](#step-1-analyze-new-oas-version-specification)** - ⭐ Complete spec analysis workflow - - [1D: Mapping Table](#step-1d-map-specification-changes-to-swagger-ui-components) - 15+ spec change types - - [1E: Real Examples](#step-1e-examples-oas-31-specification-changes--components) - 6 detailed examples - - [1H: WebFetch Workflow](#step-1h-using-webfetch-to-analyze-specifications) - Spec analysis with WebFetch -- **[Step 2-17: Implementation](#step-2-create-plugin-directory-structure)** - Complete implementation steps -- **[Practical Workflow](#practical-workflow-from-spec-to-components)** - End-to-end example -- **[Best Practices](#best-practices-iterative-spec-driven-development)** - Spec-driven development -- **[Common Pitfalls](#common-pitfalls)** - What to avoid -- **[Final Checklist](#final-checklist)** - Pre-submission verification - ---- - -**Remember: The specification at https://spec.openapis.org/ is your source of truth!** - ---- - -## Prerequisites - -Before starting, ensure: -- You understand the Swagger UI plugin architecture -- You've read the CLAUDE.md guide -- You have the OAS specification document for the new version -- All tests pass: `npm test` - -## Architecture Overview - -Adding OAS version support involves: - -1. **Plugin Creation** - New plugin following established patterns -2. **Version Detection** - Regex-based version identifier -3. **Selector Factories** - Conditional logic for version-specific features -4. **Component Implementation** - New/wrapped components for rendering -5. **State Management** - Redux selectors/actions for new features -6. **Lifecycle Hooks** - afterLoad modifications -7. **Preset Registration** - Plugin loading order -8. **Testing** - Unit and E2E tests -9. **Documentation** - Update guides and README - -## Key Design Patterns (from OAS 3.1) - -### Pattern 1: Version Detection -```javascript -export const isOAS{VERSION} = (jsSpec) => { - const oasVersion = jsSpec.get("openapi") - return ( - typeof oasVersion === "string" && /^{MAJOR}\.{MINOR}\.(?:[1-9]\d*|0)$/.test(oasVersion) - ) -} -``` - -### Pattern 2: Selector Factories - -**createOnlyOAS{VERSION}Selector** - Features exclusive to this version: -```javascript -export const createOnlyOAS{VERSION}Selector = - (selector) => - (state, ...args) => - (system) => { - if (system.getSystem().specSelectors.isOAS{VERSION}()) { - const selectedValue = selector(state, ...args) - return typeof selectedValue === "function" - ? selectedValue(system) - : selectedValue - } else { - return null - } - } -``` - -**createOnlyOAS{VERSION}SelectorWrapper** - Override previous versions: -```javascript -export const createOnlyOAS{VERSION}SelectorWrapper = - (selector) => - (oriSelector, system) => - (state, ...args) => { - if (system.getSystem().specSelectors.isOAS{VERSION}()) { - const selectedValue = selector(state, ...args) - return typeof selectedValue === "function" - ? selectedValue(oriSelector, system) - : selectedValue - } else { - return oriSelector(...args) - } - } -``` - -### Pattern 3: Component Wrappers - -**⚠️ IMPORTANT: `originalComponent` prop name, not `Ori`** - -`createOnlyOAS{VERSION}ComponentWrapper` passes the original component as `originalComponent` in props (NOT `Ori` — that's the OAS3/OAS30ComponentWrapFactory convention). Use `const { originalComponent: Ori } = props` to access it. - -**Pattern A — Reuse previous version's component via getComponent:** -```javascript -const ComponentWrapper = createOnlyOAS{VERSION}ComponentWrapper(({ getSystem }) => { - const system = getSystem() - const OAS{PREV_VERSION}Component = system.getComponent("OAS{PREV_VERSION}Component", true) - return -}) -``` - -**Pattern B — Render original component with extra props (e.g. version badge):** -```javascript -// Use `originalComponent` (NOT `Ori`) — that's the prop name createOnlyOAS{VERSION}ComponentWrapper passes -const OpenAPIVersionWrapper = createOnlyOAS{VERSION}ComponentWrapper((props) => { - const { originalComponent: Ori } = props - return -}) -``` - -### Pattern 4: Function Wrapping -```javascript -export const wrapOAS{VERSION}Fn = (fn, system) => { - const { fn: systemFn, specSelectors } = system - return Object.fromEntries( - Object.entries(fn).map(([name, newImpl]) => { - const oriImpl = systemFn[name] - const impl = (...args) => - specSelectors.isOAS{VERSION}() - ? newImpl(...args) - : typeof oriImpl === "function" - ? oriImpl(...args) - : undefined - return [name, impl] - }) - ) -} -``` - -## Implementation Steps - -### Step 1: Analyze New OAS Version Specification - -**CRITICAL: Always start by reviewing the official specification at https://spec.openapis.org/** - -#### 1A. Access the Official Specification - -**Primary Resource:** https://spec.openapis.org/ - -**Available Versions:** -- OAS 2.0: https://spec.openapis.org/oas/v2.0/ -- OAS 3.0.x: https://spec.openapis.org/oas/v3.0.3/ -- OAS 3.1.x: https://spec.openapis.org/oas/v3.1.0/ -- Future versions will follow the pattern: https://spec.openapis.org/oas/v{MAJOR}.{MINOR}.{PATCH}/ - -**What to download:** -1. The specification document (usually in HTML or Markdown format) -2. JSON Schema definitions (if available) -3. Example specification files -4. Change log or migration guide (if available) - -#### 1B. Systematic Specification Analysis - -Use the WebFetch tool to retrieve and analyze the specification: - -```bash -# Example for analyzing OAS 4.0 (hypothetical) -WebFetch("https://spec.openapis.org/oas/v4.0.0/", "List all new top-level fields, objects, and keywords introduced in this version compared to OAS 3.1") -``` - -**Read these sections carefully:** - -1. **Version Number Section** - - Confirm the exact version format (e.g., "4.0.0", "3.2.0") - - Note any version detection changes - -2. **OpenAPI Object (Root Level)** - - New top-level fields (e.g., `webhooks` in OAS 3.1) - - Modified existing fields - - Removed/deprecated fields - -3. **Info Object** - - New fields (e.g., `summary` in OAS 3.1) - - Changes to `license`, `contact`, etc. - -4. **Paths Object & Operations** - - New operation-level fields - - Changes to parameters, request bodies, responses - - New callback syntax or features - -5. **Components Object** - - New component types - - Schema changes (especially JSON Schema version) - - New security schemes - -6. **Security Object** - - New authentication types (e.g., `mutualTLS` in OAS 3.1) - - Changes to OAuth2 flows - - New security-related fields - -7. **Schema Object** - - JSON Schema version change (critical!) - - New keywords (e.g., `examples` vs `example`) - - Type system changes - -#### 1C. Create Specification Change Document - -Create a structured document listing all changes: - -**Template:** -```markdown -# OpenAPI {VERSION} Specification Analysis - -## Version Information -- Version: {MAJOR}.{MINOR}.{PATCH} -- Release Date: YYYY-MM-DD -- Previous Version: {PREVIOUS_VERSION} -- JSON Schema Version: Draft {VERSION} or {YEAR} - -## New Top-Level Fields -1. **`newField`** (Type: Object) - - Location: Root level - - Purpose: Description from spec - - Required: Yes/No - - Example from spec: {...} - - Swagger UI Impact: Needs new component in BaseLayout - -## Modified Existing Fields -1. **`info`** - - New subfields: `summary`, `newField` - - Changed behavior: Description - - Swagger UI Impact: Need to wrap Info component - -## New Operation-Level Features -1. **`newOperationField`** - - Purpose: Description - - Swagger UI Impact: Extend Operation component - -## New Authentication Types -1. **`newAuthType`** - - Type name: "newAuthType" - - Fields: {...} - - Swagger UI Impact: New auth component needed - -## JSON Schema Changes -- Version change: Draft 2020-12 → Draft 2024 -- New keywords: `keyword1`, `keyword2` -- Removed keywords: `oldKeyword` -- Swagger UI Impact: New json-schema plugin needed - -## Breaking Changes -1. Removed `x-deprecated-field` -2. Changed behavior of `existingField` -3. Required fields now enforced - -## Deprecated Features -1. `oldField` - Use `newField` instead -2. `oldAuthType` - Use `newAuthType` instead - -## Examples from Specification -- Link to example files -- Notable edge cases to test -``` - -#### 1D. Map Specification Changes to Swagger UI Components - -**CRITICAL MAPPING GUIDE:** Use this table to determine what components to create/modify: - -| Spec Change Type | Swagger UI Component Type | Location | Example from OAS 3.1 | -|-----------------|---------------------------|----------|---------------------| -| **New Top-Level Field (Object)** | New standalone component | `components/{feature-name}.jsx` | `webhooks` → `Webhooks.jsx` | -| **New Top-Level Field (String/Primitive)** | Add to existing layout component or create info component | `components/{field-name}.jsx` | `jsonSchemaDialect` → `JsonSchemaDialect.jsx` | -| **Modified Info Object** | Wrap Info component OR create OAS{VERSION}Info | `wrap-components/info.jsx` | `info.summary` → Wrapped InfoContainer | -| **New Info Subfield** | Create new component, render in Info | `components/info-{field}.jsx` | `info.license.identifier` → OAS31License | -| **Modified License/Contact** | Wrap component | `wrap-components/license.jsx` | `license.identifier` → Wrapped License | -| **New Authentication Type** | New auth component | `components/auth/{type}.jsx` | `mutualTLS` → `MutualTLSAuth.jsx` | -| **Modified Security Scheme** | Wrap auth selector | `auth-extensions/wrap-selectors.js` | mutualTLS → `definitionsToAuthorize` wrapper | -| **New Operation Field** | Extend Operation or create plugin selector | `spec-extensions/selectors.js` | N/A in OAS 3.1 | -| **New Callback Feature** | Extend Callbacks component or wrap | `wrap-components/callbacks.jsx` | Enhanced in OAS 3.1 | -| **New Parameter Type** | Extend Parameters component | `components/parameters/` | N/A in OAS 3.1 | -| **New Response Feature** | Extend Responses component | `components/responses/` | N/A in OAS 3.1 | -| **New Media Type Feature** | Modify media-type component or fn | `oas{PREV}-extensions/fn.js` | File upload detection changes | -| **JSON Schema Keyword** | New keyword component | `json-schema-{VERSION}-extensions/components/keywords/` | `example` → `JSONSchema202012KeywordExample.jsx` | -| **JSON Schema Version Change** | Entire new plugin + samples plugin | `plugins/json-schema-{VERSION}/` | JSON Schema 2020-12 plugin | -| **Modified Schema Rendering** | Wrap schema component | `wrap-components/model.jsx` | Models → OAS31Models wrapper | -| **Version Detection Field** | Update VersionPragmaFilter | `components/version-pragma-filter.jsx` | Updated for isOAS31 | -| **Deprecated Field** | Remove or add deprecation warning | Component showing warning | N/A in OAS 3.1 | - -#### 1E. Examples: OAS 3.1 Specification Changes → Components - -**Example 1: `webhooks` Top-Level Field** - -**From Spec (OAS 3.1):** -```yaml -openapi: 3.1.0 -webhooks: - newPet: - post: - requestBody: - description: Information about a new pet - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - responses: - '200': - description: Return a 200 status -``` - -**Mapping Decision:** -- Type: New top-level field (Object containing operations) -- Location in spec: Root level, same structure as `paths` -- Swagger UI component: New standalone component -- File: `src/core/plugins/oas31/components/webhooks.jsx` -- Rendering: Reuses Operations component to render webhook operations -- Selector: `selectWebhooks` in `spec-extensions/selectors.js` -- Layout integration: Added to BaseLayout or StandaloneLayout - -**Example 2: `info.summary` Field** - -**From Spec (OAS 3.1):** -```yaml -info: - title: My API - summary: A brief summary of the API # NEW in OAS 3.1 - description: Detailed description... - version: 1.0.0 -``` - -**Mapping Decision:** -- Type: New subfield in existing Info object -- Location: `info.summary` -- Swagger UI component: Wrap existing Info component -- File: `src/core/plugins/oas31/wrap-components/info.jsx` + `src/core/plugins/oas31/components/info.jsx` -- Selector: `selectInfoSummary` (if needed) -- Rendering: Display summary above or below title - -**Example 3: `mutualTLS` Authentication Type** - -**From Spec (OAS 3.1):** -```yaml -components: - securitySchemes: - myMutualTLS: - type: mutualTLS # NEW in OAS 3.1 - description: Client certificate authentication -``` - -**Mapping Decision:** -- Type: New authentication scheme type -- Location: `components.securitySchemes[name].type` -- Swagger UI component: New auth component -- File: `src/core/plugins/oas31/components/auth/mutual-tls-auth.jsx` -- Selector wrapper: `auth-extensions/wrap-selectors.js` → `definitionsToAuthorize` -- Rendering: Display in authorize modal with certificate upload UI - -**Example 4: `license.identifier` Field** - -**From Spec (OAS 3.1):** -```yaml -info: - license: - name: Apache 2.0 - identifier: Apache-2.0 # NEW in OAS 3.1 (SPDX ID) - url: https://www.apache.org/licenses/LICENSE-2.0.html -``` - -**Mapping Decision:** -- Type: New optional field in license object (alternative to url) -- Location: `info.license.identifier` -- Swagger UI component: Wrap License component + create selector -- File: `src/core/plugins/oas31/wrap-components/license.jsx` + selector in `spec-extensions/selectors.js` -- Logic: If `identifier` exists, construct URL from SPDX registry -- Rendering: Make license text a hyperlink to SPDX page - -**Example 5: JSON Schema 2020-12 Keywords** - -**From Spec (OAS 3.1):** -```yaml -# OAS 3.1 uses JSON Schema 2020-12 which includes: -components: - schemas: - Pet: - type: object - properties: - name: - type: string - example: Fluffy # Changed from 'examples' array in OAS 3.0 - xml: # Now a JSON Schema keyword, not OAS-specific - name: pet - externalDocs: # Now a JSON Schema keyword - url: https://example.com - discriminator: # Now a JSON Schema keyword - propertyName: petType -``` - -**Mapping Decision:** -- Type: JSON Schema version upgrade (Draft 7 → 2020-12) -- Location: Schema objects throughout spec -- Swagger UI components: Entire new plugin -- Files: - - `src/core/plugins/json-schema-2020-12/` (main plugin) - - `src/core/plugins/json-schema-2020-12-samples/` (sample generation) - - `src/core/plugins/json-schema-2020-12/components/keywords/example.jsx` - - `src/core/plugins/json-schema-2020-12/components/keywords/xml.jsx` - - `src/core/plugins/json-schema-2020-12/components/keywords/discriminator.jsx` - - `src/core/plugins/json-schema-2020-12/components/keywords/external-docs.jsx` -- Integration: OAS31 plugin extends JSON Schema 2020-12 keywords in `json-schema-2020-12-extensions/` - -**Example 6: `jsonSchemaDialect` Top-Level Field** - -**From Spec (OAS 3.1):** -```yaml -openapi: 3.1.0 -jsonSchemaDialect: https://spec.openapis.org/oas/3.1/dialect/base # NEW in OAS 3.1 -``` - -**Mapping Decision:** -- Type: New top-level field (String/URI) -- Location: Root level -- Swagger UI component: Simple display component -- File: `src/core/plugins/oas31/components/json-schema-dialect.jsx` -- Selector: `selectJsonSchemaDialectField` in `spec-extensions/selectors.js` -- Rendering: Show in info section with warning if non-default - -#### 1F. Specification Analysis Checklist - -Use WebFetch and Read tools to systematically check: - -- [ ] **Accessed official spec at https://spec.openapis.org/** -- [ ] **Downloaded specification document** -- [ ] **Read version number section** -- [ ] **Analyzed OpenAPI Object (root level) changes** -- [ ] **Reviewed Info Object modifications** -- [ ] **Checked Paths/Operations changes** -- [ ] **Examined Components Object updates** -- [ ] **Identified Security Scheme changes** -- [ ] **Analyzed Schema Object / JSON Schema version** -- [ ] **Listed all new top-level fields** -- [ ] **Listed all modified existing fields** -- [ ] **Documented breaking changes** -- [ ] **Identified deprecated features** -- [ ] **Found example specifications** -- [ ] **Created specification change document** -- [ ] **Mapped changes to Swagger UI components using table above** -- [ ] **Prioritized implementation order** - -#### 1G. Questions to Answer During Analysis - -**Version Type:** -- Is this a major version (4.0) or minor version (3.2)? -- Are there breaking changes? -- Is backward compatibility maintained? - -**JSON Schema:** -- Does it use a new JSON Schema version? -- What new keywords are available? -- Are there removed/deprecated keywords? - -**Top-Level Changes:** -- What new top-level fields are added? -- Which existing fields have new subfields? -- Any removed top-level fields? - -**Operation Changes:** -- Are there new operation-level features? -- Changes to parameters, request bodies, responses? -- New callback or link syntax? - -**Security Changes:** -- What authentication types are new/changed? -- OAuth2 flow modifications? -- New security requirements? - -**Media Type Changes:** -- Are there new media type features? -- File upload handling changes? -- Encoding changes? - -**Component Mapping:** -- Which components need to be created from scratch? -- Which existing components need to be wrapped? -- What selectors are required? -- Are new plugins needed (e.g., for JSON Schema)? - -**Implementation Priority:** -- What's the critical path for basic rendering? -- Which features can be added incrementally? -- What has the highest user impact? - -### Step 1H: Using WebFetch to Analyze Specifications - -**Always use WebFetch tool to retrieve and analyze the specification systematically.** - -**Step-by-step WebFetch workflow:** - -1. **Fetch the specification landing page:** -```javascript -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "Provide a summary of this OpenAPI Specification version, including links to the full specification document and any change logs or migration guides." -) -``` - -2. **Analyze the specification structure:** -```javascript -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "List all top-level fields in the OpenAPI Object for this version. For each field, indicate if it is new, modified, or unchanged from version {PREVIOUS_VERSION}." -) -``` - -3. **Deep dive into new features:** -```javascript -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "Describe the '{NEW_FIELD}' field in detail: its type, structure, purpose, whether it's required, and provide examples from the specification." -) -``` - -4. **Analyze security changes:** -```javascript -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "List all security scheme types supported in this version. Highlight any new or changed security scheme types compared to {PREVIOUS_VERSION}." -) -``` - -5. **Check JSON Schema compatibility:** -```javascript -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "What version of JSON Schema does this OpenAPI Specification version use? List any specific dialect or modifications to JSON Schema." -) -``` - -6. **Find example specifications:** -```javascript -WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "Provide links to example OpenAPI specification files for this version, particularly examples that demonstrate new features." -) -``` - -**Real example from analyzing OAS 3.1:** - -```javascript -// 1. Initial analysis -WebFetch( - "https://spec.openapis.org/oas/v3.1.0/", - "List all new fields and features introduced in OpenAPI 3.1.0 compared to OpenAPI 3.0.3" -) - -// Response would include: -// - webhooks field (new top-level) -// - info.summary (new) -// - info.license.identifier (new) -// - jsonSchemaDialect (new top-level) -// - mutualTLS security scheme type (new) -// - JSON Schema 2020-12 (changed from Draft 7) - -// 2. Deep dive into webhooks -WebFetch( - "https://spec.openapis.org/oas/v3.1.0/", - "Explain the 'webhooks' field in detail: structure, purpose, and provide a complete example" -) - -// 3. Understand JSON Schema changes -WebFetch( - "https://spec.openapis.org/oas/v3.1.0/", - "What are the differences between JSON Schema Draft 7 (used in OAS 3.0) and JSON Schema 2020-12 (used in OAS 3.1) that affect schema definitions?" -) -``` - -**Pro tip:** Use multiple targeted queries rather than one broad query. This gives more precise information for each component you need to implement. - -### Step 2: Create Plugin Directory Structure - -**Directory:** `src/core/plugins/oas{VERSION_NUMBER}/` - -**Files to Create:** -``` -src/core/plugins/oas{VERSION_NUMBER}/ -├── index.js # Main plugin export -├── fn.js # Selector/function factories -├── selectors.js # Version-specific selectors (if needed) -├── after-load.js # Lifecycle hook -├── auth-extensions/ # Authentication features -│ └── wrap-selectors.js -├── oas{PREVIOUS_VERSION}-extensions/ # Extensions to previous version -│ └── fn.js -├── spec-extensions/ # Spec-level features -│ ├── selectors.js -│ └── wrap-selectors.js -├── components/ # New components -│ ├── version-pragma-filter.jsx -│ ├── {new-feature-1}.jsx -│ └── auth/ -│ └── {new-auth-type}.jsx -├── wrap-components/ # Component wrappers -│ ├── info.jsx -│ ├── license.jsx -│ └── {existing-component}.jsx -└── json-schema-{VERSION}-extensions/ # If new JSON Schema version - ├── fn.js - ├── components/keywords/ - └── wrap-components/keywords/ -``` - -**Create the directories:** -```bash -mkdir -p src/core/plugins/oas{VERSION_NUMBER}/{auth-extensions,oas{PREVIOUS_VERSION}-extensions,spec-extensions,components/auth,wrap-components} -``` - -### Step 3: Implement Version Detection (fn.js) - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/fn.js` - -**Template:** -```javascript -/** - * @prettier - */ - -/** - * Detects if a spec is OpenAPI {MAJOR}.{MINOR}.x - */ -export const isOAS{VERSION} = (jsSpec) => { - const oasVersion = jsSpec.get("openapi") - return ( - typeof oasVersion === "string" && - /^{MAJOR}\.{MINOR}\.(?:[1-9]\d*|0)$/.test(oasVersion) - ) -} - -/** - * Creates a selector that only returns a value for OAS {VERSION} specs - */ -export const createOnlyOAS{VERSION}Selector = - (selector) => - (state, ...args) => - (system) => { - if (system.getSystem().specSelectors.isOAS{VERSION}()) { - const selectedValue = selector(state, ...args) - return typeof selectedValue === "function" - ? selectedValue(system) - : selectedValue - } else { - return null - } - } - -/** - * Creates a selector wrapper that uses new impl for OAS {VERSION}, falls back to original - */ -export const createOnlyOAS{VERSION}SelectorWrapper = - (selector) => - (oriSelector, system) => - (state, ...args) => { - if (system.getSystem().specSelectors.isOAS{VERSION}()) { - const selectedValue = selector(state, ...args) - return typeof selectedValue === "function" - ? selectedValue(oriSelector, system) - : selectedValue - } else { - return oriSelector(...args) - } - } - -/** - * Creates a system-aware selector - */ -export const createSystemSelector = - (selector) => - (state, ...args) => - (system) => { - const selectedValue = selector(state, system, ...args) - return typeof selectedValue === "function" - ? selectedValue(system) - : selectedValue - } - -/** - * Creates a component wrapper that only renders for OAS {VERSION}. - * When active, passes `originalComponent` (the unwrapped original) and - * `getSystem` as extra props. Access the original via: - * const { originalComponent: Ori } = props - * NOT via `props.Ori` — that's the OAS3/OAS30ComponentWrapFactory convention. - */ -export const createOnlyOAS{VERSION}ComponentWrapper = - (Component) => (Original, system) => (props) => { - if (system.specSelectors.isOAS{VERSION}()) { - return ( - - ) - } - - return - } - -/** - * Wraps functions to conditionally use OAS {VERSION} implementations - */ -export const wrapOAS{VERSION}Fn = (fn, system) => { - const { fn: systemFn, specSelectors } = system - return Object.fromEntries( - Object.entries(fn).map(([name, newImpl]) => { - const oriImpl = systemFn[name] - const impl = (...args) => - specSelectors.isOAS{VERSION}() - ? newImpl(...args) - : typeof oriImpl === "function" - ? oriImpl(...args) - : undefined - return [name, impl] - }) - ) -} -``` - -### Step 4: Create Main Plugin Export (index.js) - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/index.js` - -**Template:** -```javascript -/** - * @prettier - */ - -import afterLoad from "./after-load.js" -import { - isOAS{VERSION}, - createSystemSelector, - createOnlyOAS{VERSION}Selector, - createOnlyOAS{VERSION}SelectorWrapper, - createOnlyOAS{VERSION}ComponentWrapper, -} from "./fn.js" - -// Import spec extensions -import * as specExtensionsSelectors from "./spec-extensions/selectors.js" -import * as specExtensionsWrapSelectors from "./spec-extensions/wrap-selectors.js" - -// Import auth extensions -import * as authExtensionsWrapSelectors from "./auth-extensions/wrap-selectors.js" - -// Import OAS{PREVIOUS_VERSION} extensions -import * as oas{PREVIOUS_VERSION}ExtensionsFn from "./oas{PREVIOUS_VERSION}-extensions/fn.js" - -// Import components -import VersionPragmaFilter from "./components/version-pragma-filter.jsx" -// Import new feature components here - -// Import wrap components -import InfoWrapper from "./wrap-components/info.jsx" -// Import other wrappers here - -/** - * OpenAPI {MAJOR}.{MINOR} Plugin - * - * Adds support for OpenAPI Specification {MAJOR}.{MINOR}.x - * - * This plugin should be loaded AFTER: - * - oas{PREVIOUS_VERSION} plugin - * - json-schema-{JSON_SCHEMA_VERSION} plugin (if applicable) - * - * It wraps and overrides components/selectors from previous versions. - */ -const OpenAPI{VERSION}Plugin = ({ fn }) => { - return { - fn: { - oas{VERSION_NUMBER}: { - isOAS{VERSION}, - createSystemSelector, - createOnlyOAS{VERSION}Selector, - createOnlyOAS{VERSION}SelectorWrapper, - createOnlyOAS{VERSION}ComponentWrapper, - }, - }, - components: { - // New components - OAS{VERSION}VersionPragmaFilter: VersionPragmaFilter, - // Add new feature components here - }, - wrapComponents: { - // Wrapped components - VersionPragmaFilter: (Ori, system) => - system.specSelectors.isOAS{VERSION}() - ? system.getComponent("OAS{VERSION}VersionPragmaFilter", true) - : Ori, - InfoContainer: InfoWrapper, - // Add other wrappers here - }, - statePlugins: { - spec: { - selectors: { - // Add version detection selector - isOAS{VERSION}: createSystemSelector(specExtensionsSelectors.selectIsOAS{VERSION}), - // Add new feature selectors here - }, - wrapSelectors: { - // Wrap previous version selectors if needed - ...specExtensionsWrapSelectors, - }, - }, - auth: { - wrapSelectors: { - // Add auth extensions if needed - ...authExtensionsWrapSelectors, - }, - }, - oas{VERSION_NUMBER}: { - selectors: { - // Plugin-specific selectors - }, - }, - }, - afterLoad, - } -} - -export default OpenAPI{VERSION}Plugin -``` - -### Step 5: Implement Spec Extensions - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/spec-extensions/selectors.js` - -**Template:** -```javascript -/** - * @prettier - */ - -import { createSelector } from "reselect" -import { isOAS{VERSION} } from "../fn.js" - -/** - * Detects if the current spec is OAS {VERSION} - */ -export const selectIsOAS{VERSION} = (state, system) => () => { - const spec = system.specSelectors.specJson() - return isOAS{VERSION}(spec) -} - -// Add selectors for new OAS {VERSION} fields here -// Example: -// export const selectNewField = createSelector( -// (state) => state, -// (state) => { -// const spec = state.getIn(["spec", "json"]) -// return spec.get("newField") -// } -// ) -``` - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/spec-extensions/wrap-selectors.js` - -**⚠️ Minor version: DON'T wrap `isOAS{PREV}` unless the regex actually matches both versions.** For example, OAS 3.1's `isOAS31` regex (`/^3\.1\./`) will never match `3.2.x`, so wrapping it to return `false` is dead code. Only add the `isOAS{PREV}` override if the previous version's detection regex would incorrectly match the new version. - -**Template:** -```javascript -/** - * @prettier - */ - -import { createOnlyOAS{VERSION}SelectorWrapper } from "../fn.js" - -// Ensure OAS {VERSION} specs are recognized as OAS 3.x (needed when major version number didn't change) -export const isOAS3 = - (oriSelector, system) => - (state, ...args) => { - const isOAS{VERSION} = system.specSelectors.isOAS{VERSION}() - return isOAS{VERSION} || oriSelector(...args) - } - -// ONLY add isOAS{PREV} wrapper if the previous version's regex could match this new version. -// For a minor version bump (e.g. 3.1 → 3.2), the previous regex won't match, so DON'T add this. -// export const isOAS{PREV} = createOnlyOAS{VERSION}SelectorWrapper((state) => () => false) -``` - -### Step 6: Create Version Pragma Filter Component - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/components/version-pragma-filter.jsx` - -**Template:** -```javascript -/** - * @prettier - */ - -import React from "react" -import PropTypes from "prop-types" - -const OAS{VERSION}VersionPragmaFilter = ({ bypass, isSwagger2, isOAS3, isOAS{PREVIOUS_VERSION}, isOAS{VERSION} }) => { - // Handle version detection logic - const isAmbiguous = - (isSwagger2 && isOAS3) || - (isSwagger2 && isOAS{PREVIOUS_VERSION}) || - (isSwagger2 && isOAS{VERSION}) || - (isOAS3 && isOAS{PREVIOUS_VERSION}) || - (isOAS3 && isOAS{VERSION}) || - (isOAS{PREVIOUS_VERSION} && isOAS{VERSION}) - - const isMissing = !isSwagger2 && !isOAS3 && !isOAS{PREVIOUS_VERSION} && !isOAS{VERSION} - - if (bypass) { - return null - } - - if (isAmbiguous) { - return ( -
-
-
-

Unable to render this document, as it contains multiple OpenAPI version fields.

-
-
-
- ) - } - - if (isMissing) { - return ( -
-
-
-

Unable to render this document, as it doesn't contain an OpenAPI version field.

-
-
-
- ) - } - - return null -} - -OAS{VERSION}VersionPragmaFilter.propTypes = { - bypass: PropTypes.bool, - isSwagger2: PropTypes.bool.isRequired, - isOAS3: PropTypes.bool.isRequired, - isOAS{PREVIOUS_VERSION}: PropTypes.bool.isRequired, - isOAS{VERSION}: PropTypes.bool.isRequired, -} - -OAS{VERSION}VersionPragmaFilter.defaultProps = { - bypass: false, -} - -export default OAS{VERSION}VersionPragmaFilter -``` - -### Step 7: Implement New Feature Components - -For each new feature in the OAS specification: - -**Example: New Feature Component** -```javascript -/** - * @prettier - */ - -import React from "react" -import PropTypes from "prop-types" - -const NewFeature = ({ getComponent, specSelectors }) => { - const newFeature = specSelectors.selectNewFeature() - - if (!newFeature || !newFeature.size) { - return null - } - - // Render the new feature - return ( -
-

New Feature

- {/* Render logic here */} -
- ) -} - -NewFeature.propTypes = { - specSelectors: PropTypes.object.isRequired, - getComponent: PropTypes.func.isRequired, -} - -export default NewFeature -``` - -### Step 8: Create Component Wrappers - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/wrap-components/info.jsx` - -**⚠️ Minor version (e.g. 3.2): don't create a new Info component.** If the Info object is identical to the previous version, reuse `OAS{PREV_VERSION}Info` via `getComponent` instead of copying the whole component. Only create a new Info component for major versions with significant structural changes. - -**Template (minor version — reuse previous Info):** -```javascript -/** - * @prettier - */ - -import React from "react" -import { createOnlyOAS{VERSION}ComponentWrapper } from "../fn.js" - -const InfoWrapper = createOnlyOAS{VERSION}ComponentWrapper(({ getSystem }) => { - const system = getSystem() - const OAS{PREV_VERSION}Info = system.getComponent("OAS{PREV_VERSION}Info", true) - return -}) - -export default InfoWrapper -``` - -**Also — OpenAPIVersion wrapper (minor version — change version badge only):** - -For minor versions where the only Info difference is the version badge, use the OpenAPIVersion wrapper pattern instead of wrapping InfoContainer at all: - -```javascript -// wrap-components/openapi-version.jsx -/** - * @prettier - */ -import React from "react" -import { createOnlyOAS{VERSION}ComponentWrapper } from "../fn.js" - -export default createOnlyOAS{VERSION}ComponentWrapper((props) => { - const { originalComponent: Ori } = props // NOT `Ori` from props directly — use `originalComponent` - return -}) -``` - -Then register it in index.js: -```javascript -wrapComponents: { - OpenAPIVersion: OpenAPIVersionWrapper, // changes the version badge shown in the Info header -} -``` - -### Step 9: Implement afterLoad Hook - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/after-load.js` - -**Template:** -```javascript -/** - * @prettier - */ - -import { wrapOAS{VERSION}Fn } from "./fn.js" - -/** - * afterLoad hook for OAS {VERSION} plugin - * - * This hook runs after all plugins are loaded and allows - * modification of the system's functions and behaviors. - */ -function afterLoad({ fn, getSystem }) { - const system = getSystem() - - // Override functions that differ in OAS {VERSION} - if (typeof fn.sampleFromSchema === "function") { - // Example: Wrap sample generation if JSON Schema version changed - // const wrappedFns = wrapOAS{VERSION}Fn({ - // sampleFromSchema: fn.jsonSchema{VERSION}.sampleFromSchema, - // }, system) - // Object.assign(this.fn, wrappedFns) - } - - // Override other functions as needed - // Example: File upload detection - // if (typeof fn.isFileUploadIntended === "function") { - // const wrappedFns = wrapOAS{VERSION}Fn({ - // isFileUploadIntended: makeIsFileUploadIntended(system), - // }, system) - // Object.assign(this.fn, wrappedFns) - // } -} - -export default afterLoad -``` - -### Step 10: Handle Authentication Changes - -If new authentication types are introduced: - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/components/auth/{auth-type}.jsx` - -**Template:** -```javascript -/** - * @prettier - */ - -import React from "react" -import PropTypes from "prop-types" - -const NewAuthType = ({ schema, name, getComponent, onChange, authorized }) => { - const authValue = authorized && authorized.getIn([name]) - - const Input = getComponent("Input") - const Button = getComponent("Button") - - // Render auth UI - return ( -
-

{schema.get("description") || `${name} (${schema.get("type")})`}

- {/* Auth UI here */} -
- ) -} - -NewAuthType.propTypes = { - schema: PropTypes.object.isRequired, - name: PropTypes.string.isRequired, - getComponent: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - authorized: PropTypes.object, -} - -export default NewAuthType -``` - -**File:** `src/core/plugins/oas{VERSION_NUMBER}/auth-extensions/wrap-selectors.js` - -**Template:** -```javascript -/** - * @prettier - */ - -import { createOnlyOAS{VERSION}SelectorWrapper } from "../fn.js" -import { Map } from "immutable" - -/** - * Wraps definitionsToAuthorize to include new auth types - */ -export const definitionsToAuthorize = createOnlyOAS{VERSION}SelectorWrapper( - (state, system) => - (oriSelector) => { - const definitions = oriSelector() - const securityDefinitions = system.specSelectors.securityDefinitions() || Map() - - // Add new auth types to definitions - return definitions.map((definition) => { - const schema = securityDefinitions.get(definition.get("name")) - if (schema && schema.get("type") === "newAuthType") { - // Add new auth type logic - return definition.set("newAuthType", true) - } - return definition - }) - } -) -``` - -### Step 11: Register Plugin in Preset - -**File:** `src/core/presets/apis/index.js` - -**Add import:** -```javascript -import OpenAPI{VERSION}Plugin from "../../plugins/oas{VERSION_NUMBER}/index.js" -``` - -**Add to preset array (IMPORTANT: add at the end):** -```javascript -export default function PresetApis() { - return [ - BasePreset, - OpenAPI30Plugin, - // ... other plugins - OpenAPI{PREVIOUS_VERSION}Plugin, - OpenAPI{VERSION}Plugin, // Load LAST to override previous versions - ] -} -``` - -**Why load last?** -- Wrap-components override previous versions -- Wrap-selectors intercept earlier selectors -- afterLoad modifications apply to dependencies - -### Step 12: Add Unit Tests - -**Directory:** `test/unit/core/plugins/oas{VERSION_NUMBER}/` - -**File:** `test/unit/core/plugins/oas{VERSION_NUMBER}/fn.js` - -**Template:** -```javascript -/** - * @prettier - */ - -import { fromJS } from "immutable" -import { isOAS{VERSION} } from "src/core/plugins/oas{VERSION_NUMBER}/fn.js" - -describe("oas{VERSION_NUMBER} plugin - fn - isOAS{VERSION}", () => { - it("should match OpenAPI {MAJOR}.{MINOR}.0", () => { - const spec = fromJS({ openapi: "{MAJOR}.{MINOR}.0" }) - expect(isOAS{VERSION}(spec)).toBe(true) - }) - - it("should match OpenAPI {MAJOR}.{MINOR}.1", () => { - const spec = fromJS({ openapi: "{MAJOR}.{MINOR}.1" }) - expect(isOAS{VERSION}(spec)).toBe(true) - }) - - it("should match OpenAPI {MAJOR}.{MINOR}.25", () => { - const spec = fromJS({ openapi: "{MAJOR}.{MINOR}.25" }) - expect(isOAS{VERSION}(spec)).toBe(true) - }) - - it("should NOT match OpenAPI {MAJOR}.{MINOR}", () => { - const spec = fromJS({ openapi: "{MAJOR}.{MINOR}" }) - expect(isOAS{VERSION}(spec)).toBe(false) - }) - - it("should NOT match OpenAPI {MAJOR}.{MINOR}.01 (leading zero)", () => { - const spec = fromJS({ openapi: "{MAJOR}.{MINOR}.01" }) - expect(isOAS{VERSION}(spec)).toBe(false) - }) - - it("should NOT match OpenAPI {DIFFERENT_MAJOR}.{DIFFERENT_MINOR}.0", () => { - const spec = fromJS({ openapi: "{DIFFERENT_MAJOR}.{DIFFERENT_MINOR}.0" }) - expect(isOAS{VERSION}(spec)).toBe(false) - }) - - it("should NOT match swagger: 2.0", () => { - const spec = fromJS({ swagger: "2.0" }) - expect(isOAS{VERSION}(spec)).toBe(false) - }) - - it("should handle null spec", () => { - const spec = fromJS({}) - expect(isOAS{VERSION}(spec)).toBe(false) - }) -}) -``` - -**File:** `test/unit/core/plugins/oas{VERSION_NUMBER}/components/version-pragma-filter.jsx` - -**Template:** -```javascript -/** - * @prettier - */ - -import React from "react" -import { shallow } from "enzyme" -import OAS{VERSION}VersionPragmaFilter from "src/core/plugins/oas{VERSION_NUMBER}/components/version-pragma-filter.jsx" - -describe("OAS{VERSION}VersionPragmaFilter", () => { - it("should render nothing when bypass is true", () => { - const wrapper = shallow( - - ) - expect(wrapper.type()).toBe(null) - }) - - it("should render nothing when version is valid", () => { - const wrapper = shallow( - - ) - expect(wrapper.type()).toBe(null) - }) - - it("should render error when version is ambiguous", () => { - const wrapper = shallow( - - ) - expect(wrapper.find(".version-pragma__message--ambiguous")).toHaveLength(1) - }) - - it("should render error when version is missing", () => { - const wrapper = shallow( - - ) - expect(wrapper.find(".version-pragma__message--missing")).toHaveLength(1) - }) -}) -``` - -### Step 13: Add E2E Tests - -**Directory:** `test/e2e-cypress/e2e/features/oas{VERSION_NUMBER}/` - -**Create test spec file:** -```javascript -/** - * @prettier - */ - -describe("OpenAPI {MAJOR}.{MINOR} features", () => { - it("should detect OAS {VERSION} version", () => { - cy.visit("/oas{VERSION_NUMBER}-spec.html") - cy.get(".information-container .version").should("contain", "{MAJOR}.{MINOR}.0") - }) - - // Add tests for new features - it("should render new feature X", () => { - cy.visit("/oas{VERSION_NUMBER}-spec.html") - cy.get(".opblock-tag-section").should("contain", "New Feature") - }) -}) -``` - -**Add test fixtures:** -- Create sample OAS {VERSION} spec in `test/e2e-cypress/static/documents/oas{VERSION_NUMBER}/` -- Create HTML fixture in `test/e2e-cypress/static/` referencing the spec - -### Step 14: Update Documentation - -**Files to update:** - -1. **README.md** - Add OAS {VERSION} to supported versions -2. **CLAUDE.md** - Add plugin to list and update version compatibility -3. **docs/usage/version-detection.md** - Document new version detection -4. **package.json** - Update description if needed - -**Example README.md update:** -```markdown -### OpenAPI Specification Compatibility - -- **OpenAPI 2.0** (Swagger) -- **OpenAPI 3.0.x** -- **OpenAPI 3.1.x** -- **OpenAPI {MAJOR}.{MINOR}.x** ⭐ NEW -``` - -### Step 15: Verify Build - -**Run full build:** -```bash -npm run clean -npm run build -``` - -**Check dist/ output:** -- `dist/swagger-ui.js` - Should include new plugin -- `dist/swagger-ui.css` - Should include new styles (if any) -- Bundle size should be reasonable - -**Run artifact tests:** -```bash -npm run test:artifact -``` - -### Step 16: Run Full Test Suite - -```bash -npm run lint-errors # ESLint errors only -npm run test:unit # Jest unit tests -npm run cy:ci # Cypress E2E tests -``` - -**All tests must pass before submitting PR.** - -### Step 17: Create Pull Request - -**Commit message format:** -``` -feat(oas{VERSION_NUMBER}): add support for OpenAPI {MAJOR}.{MINOR} - -Implement OpenAPI {MAJOR}.{MINOR}.x specification support with: -- Version detection and plugin architecture -- New feature X rendering -- New authentication type Y support -- JSON Schema {VERSION} integration (if applicable) - -Closes #{ISSUE_NUMBER} -``` - -**PR checklist:** -- [ ] All tests passing -- [ ] New feature components implemented -- [ ] Unit tests added -- [ ] E2E tests added -- [ ] Documentation updated -- [ ] Build completes successfully -- [ ] No ESLint errors -- [ ] Follows existing code patterns - -## Common Pitfalls - -1. **Don't forget @prettier pragma** - Add to all new files -2. **Plugin loading order matters** - Load new plugin LAST in preset -3. **Use double quotes** - Project convention -4. **No semicolons** - Project convention -5. **Always use DOMPurify** - For any user-provided HTML -6. **Test with real specs** - Use actual OAS {VERSION} examples -7. **Don't modify core unnecessarily** - Use plugin architecture -8. **Version regex must be exact** - Follow pattern from OAS 3.1 -9. **Component wrappers render ``** - When not active version (handled by `createOnlyOAS{VERSION}ComponentWrapper` automatically) -10. **afterLoad runs after all plugins** - Safe to modify system -11. **`originalComponent` not `Ori`** - `createOnlyOAS{VERSION}ComponentWrapper` passes the original as `originalComponent` prop. Use `const { originalComponent: Ori } = props`. The `Ori` name comes from `OAS30ComponentWrapFactory` which uses a different signature. -12. **Don't copy the entire Info component for minor versions** - Use the OpenAPIVersion wrapper to change the version badge, and reuse the previous version's Info via `getComponent("OAS{PREV}Info", true)` instead of copying. -13. **Don't add version-specific logic to previous version's plugin** - OAS32 logic belongs in the OAS32 plugin, not in OAS31 plugin files (e.g. don't add `isOAS32` checks to `oas31/wrap-components/license.jsx`). -20. **Always use `createOnlyOAS{VERSION}ComponentWrapper` in wrap-components — never inline the version guard** - Each wrap-component in a plugin should use the factory (`createOnlyOAS{VERSION}ComponentWrapper`), not a hand-written `(Original, system) => (props) => { if (system.specSelectors.isOAS{VERSION}()) ... }`. When a later version (OAS32) also needs to reuse the same OAS31 component, it gets its own dedicated wrap-component in the OAS32 plugin — don't expand the guard to `isOAS31 || isOAS32` in the OAS31 file. The OAS31 wrappers for contact/license should use `createOnlyOAS31ComponentWrapper` and nothing else; OAS32 contact/license wrappers live in `oas32/wrap-components/` and handle the OAS32 case. -14. **Don't add `isOAS{PREV}` wrap-selector unless the regex actually overlaps** - For minor versions, the previous version's regex already won't match (e.g. OAS31's `/^3\.1\./` won't match `3.2.x`). Adding the wrapper is dead code. -15. **New HTTP methods: use `OPERATION_METHODS` in `spec/selectors.js`, not `operationsWithRootInherited` wrapper** - Adding the method to `OPERATION_METHODS` makes the core `operations` selector collect those ops. The `validOperationMethods` wrapper (guarded by `createOnlyOAS{VERSION}SelectorWrapper`) then controls whether the UI renders them. No need for an `operationsWithRootInherited` wrapper. -16. **Don't add new HTTP methods to the core `validOperationMethods` constant** - Adding `"query"` to the base constant in `spec/selectors.js` affects ALL OAS versions. Instead, add it only via `createOnlyOAS{VERSION}SelectorWrapper` in your plugin's `spec-extensions/wrap-selectors.js`. -17. **Don't create selectors only used in tests** - Selectors like `selectHasQueryOperations` that are never called from production code should not exist. Remove them. -18. **OAS meta-schema URL ≠ new JSON Schema dialect** - The URL `https://spec.openapis.org/oas/3.2/schema/...` is the OAS 3.2 document structure schema, NOT a new JSON Schema version. OAS 3.2 uses JSON Schema 2020-12, the same as OAS 3.1. Don't list "new JSON Schema version" as a feature unless it actually changes. -19. **Verify "not yet implemented" feature list against previous version** - Features like `pathItems in Components` were already in OAS 3.1, not new in 3.2. Check what's actually new before listing it. - -## JSON Schema Version Changes - -**⚠️ First, verify whether the JSON Schema version actually changed.** The OAS meta-schema URL (e.g. `https://spec.openapis.org/oas/3.2/schema/...`) describes the OAS document structure — it is NOT a new JSON Schema dialect. OAS 3.2 uses JSON Schema 2020-12, the same as OAS 3.1. Only create a new `json-schema-{VERSION}` plugin if the actual JSON Schema dialect changed (as it did from OAS 3.0 Draft-07 → OAS 3.1 JSON Schema 2020-12). - -If the new OAS version uses a different JSON Schema version: - -1. **Create json-schema-{VERSION} plugin** (separate from OAS plugin) -2. **Implement keyword components** for new schema features -3. **Update sample generation** logic -4. **Add schema validation** support -5. **Load json-schema-{VERSION} plugin BEFORE oas{VERSION_NUMBER}** in preset - -Example from OAS 3.1 using JSON Schema 2020-12: -- `src/core/plugins/json-schema-2020-12/` - Main plugin -- `src/core/plugins/json-schema-2020-12-samples/` - Sample generation -- Loaded before `oas31` plugin in preset - -## Major vs Minor Version Considerations - -### Major Version (e.g., 4.0) - -**Likely includes:** -- Breaking changes from previous major version -- New top-level fields -- Removed/deprecated features -- Possibly new JSON Schema version -- New authentication paradigms -- Restructured spec format - -**Implementation approach:** -- Create completely separate plugin -- May need to create new base components -- Extensive wrapping of previous version components -- Significant selector overrides -- New preset configuration options - -### Minor Version (e.g., 3.2) - -**Likely includes:** -- Backward-compatible additions -- New optional fields -- Enhanced existing features -- Same JSON Schema version (verify before assuming it changed) -- Incremental improvements - -**Implementation approach:** -- Lighter plugin with focused additions -- Fewer component wrappers needed -- Selective selector additions -- Reuse most of previous version logic via `getComponent("OAS{PREV}...", true)` -- Simpler afterLoad modifications - -**Key decisions for minor versions (lessons from OAS 3.2):** - -1. **Version badge only changed?** Use `OpenAPIVersion` wrapper + reuse `OAS{PREV}Info` — don't create a new Info component. -2. **New HTTP method (e.g. QUERY)?** Add it to `OPERATION_METHODS` in `src/core/plugins/spec/selectors.js` AND add it to `validOperationMethods` via `createOnlyOAS{VERSION}SelectorWrapper`. Don't add it to the core `validOperationMethods` constant (affects all versions). -3. **`isOAS{PREV}` wrapper needed?** Only if the previous regex also matches the new version string. Minor version bumps (3.1 → 3.2) don't need it. -4. **JSON Schema version comment?** Verify it actually changed. OAS 3.2 uses JSON Schema 2020-12, the same as OAS 3.1. The new OAS meta-schema URL (`https://spec.openapis.org/oas/3.2/schema/...`) describes OAS document structure, not a new JSON Schema dialect. -5. **"Not yet implemented" list?** Double-check each item against the *previous* version's changelog — some may already be implemented. - -## Example: Adding OAS 4.0 (Major) - -**Version detection:** -```javascript -export const isOAS40 = (jsSpec) => { - const oasVersion = jsSpec.get("openapi") - return ( - typeof oasVersion === "string" && /^4\.0\.(?:[1-9]\d*|0)$/.test(oasVersion) - ) -} -``` - -**Directory:** `src/core/plugins/oas40/` - -**Wrap previous version:** -```javascript -// spec-extensions/wrap-selectors.js -export const isOAS31 = createOnlyOAS40SelectorWrapper((state) => () => false) -export const isOAS3 = createOnlyOAS40SelectorWrapper((state) => () => false) -``` - -**Register in preset:** -```javascript -// src/core/presets/apis/index.js -export default function PresetApis() { - return [ - BasePreset, - OpenAPI30Plugin, - OpenAPI31Plugin, - OpenAPI40Plugin, // LAST - ] -} -``` - -## Example: Adding OAS 3.2 (Minor) - -**Version detection:** -```javascript -export const isOAS32 = (jsSpec) => { - const oasVersion = jsSpec.get("openapi") - return ( - typeof oasVersion === "string" && /^3\.2\.(?:[1-9]\d*|0)$/.test(oasVersion) - ) -} -``` - -**Directory:** `src/core/plugins/oas32/` - -**Lighter component wrapping — OAS 3.2 specific patterns:** - -```javascript -// wrap-components/openapi-version.jsx — change the version badge only -// Use `originalComponent` (NOT `Ori`) — that's what createOnlyOAS32ComponentWrapper passes -export default createOnlyOAS32ComponentWrapper((props) => { - const { originalComponent: Ori } = props - return -}) - -// wrap-components/info.jsx — reuse OAS31Info, don't create a new one -export default createOnlyOAS32ComponentWrapper(({ getSystem }) => { - const system = getSystem() - const OAS31Info = system.getComponent("OAS31Info", true) - return -}) - -// wrap-components/contact.jsx and license.jsx — same pattern (OAS32 logic belongs HERE, not in OAS31) -export default createOnlyOAS32ComponentWrapper((props) => { - const { getSystem } = props - const system = getSystem() - const OAS31Contact = system.getComponent("OAS31Contact", true) - return -}) -``` - -**New HTTP method (QUERY) — two-part approach:** - -Part 1: Add to `OPERATION_METHODS` in `src/core/plugins/spec/selectors.js` so the `operations` selector collects QUERY ops: -```javascript -// spec/selectors.js -export const OPERATION_METHODS = [ - "get", "put", "post", "delete", "options", "head", "patch", "trace", "query", -] -``` - -Part 2: Add to `validOperationMethods` via `createOnlyOAS32SelectorWrapper` so the UI only renders them for OAS 3.2: -```javascript -// oas32/spec-extensions/wrap-selectors.js -export const validOperationMethods = createOnlyOAS32SelectorWrapper( - () => (oriSelector, system) => system.oas32Selectors.validOperationMethods() -) - -// oas32/selectors.js -export const validOperationMethods = () => [ - "get", "put", "post", "delete", "options", "head", "patch", "trace", "query", -] -``` - -Do NOT add `"query"` to the core `validOperationMethods` constant — that would affect all versions. - -## Final Checklist - -Before marking the task as complete: - -- [ ] Plugin created in `src/core/plugins/oas{VERSION_NUMBER}/` -- [ ] Version detection function implemented and tested -- [ ] Selector factories created -- [ ] New feature components implemented -- [ ] Component wrappers created for modified components -- [ ] Spec extension selectors implemented -- [ ] Auth extensions implemented (if new auth types) -- [ ] afterLoad hook implemented -- [ ] Plugin registered in preset (LAST position) -- [ ] Unit tests added and passing -- [ ] E2E tests added and passing -- [ ] Documentation updated (README, CLAUDE.md, docs/) -- [ ] Build completes successfully -- [ ] Artifact tests pass -- [ ] No ESLint errors -- [ ] Code follows project conventions (no semicolons, double quotes, @prettier pragma) -- [ ] All security considerations addressed (DOMPurify, input validation) -- [ ] PR created with proper commit message format - -## Practical Workflow: From Spec to Components - -This section demonstrates the complete workflow from specification analysis to component implementation. - -### Workflow Example: Adding a Hypothetical `workflows` Field - -**Scenario:** OAS 4.0 introduces a new `workflows` top-level field for defining reusable workflow definitions. - -#### Phase 1: Specification Research - -**Step 1: Fetch the spec section** -```javascript -WebFetch( - "https://spec.openapis.org/oas/v4.0.0/", - "Describe the 'workflows' field: its structure, purpose, whether it's required, and provide a complete example from the specification." -) -``` - -**Step 2: Analyze the structure** -```javascript -// Response analysis: -// - Type: Object (Map[string, Workflow Object]) -// - Location: Root level (sibling to 'paths') -// - Required: No (optional) -// - Structure: Similar to paths, but defines reusable workflows -// - Example: -workflows: - onNewUser: - steps: - - operationRef: '#/paths/~1users/post' - - operationRef: '#/paths/~1emails~1welcome/post' -``` - -**Step 3: Map to component type** -Using the mapping table from Step 1D: -- Change type: New top-level field (Object) -- Component type: New standalone component -- Similar to: `webhooks` in OAS 3.1 - -#### Phase 2: Component Design - -**File structure decision:** -``` -src/core/plugins/oas40/ -├── components/ -│ └── workflows.jsx # Main component -└── spec-extensions/ - └── selectors.js # Add selectWorkflows -``` - -**Component responsibilities:** -1. Select workflows from spec -2. Render workflow list -3. Display workflow steps -4. Link to referenced operations - -#### Phase 3: Implementation - -**Step 1: Create selector** -```javascript -// src/core/plugins/oas40/spec-extensions/selectors.js - -export const selectWorkflows = createSelector( - (state) => state, - (state) => { - const spec = state.getIn(["spec", "json"]) - return spec.get("workflows") || Map() - } -) -``` - -**Step 2: Create component** -```javascript -// src/core/plugins/oas40/components/workflows.jsx - -/** - * @prettier - */ - -import React from "react" -import PropTypes from "prop-types" -import ImPropTypes from "react-immutable-proptypes" - -const Workflows = ({ specSelectors, getComponent }) => { - const workflows = specSelectors.workflows() - - if (!workflows || !workflows.size) { - return null - } - - const Collapse = getComponent("Collapse") - - return ( -
-

Workflows

-
-

- Reusable workflow definitions that can be referenced from operations. -

-
- {workflows.entrySeq().map(([workflowName, workflow]) => ( -
- -
-

{workflowName}

- {workflow.get("description") && ( -
- {workflow.get("description")} -
- )} -
-
- {workflow.get("steps") && ( -
    - {workflow.get("steps").map((step, idx) => ( -
  • - {step.get("operationRef") || step.get("workflowRef")} -
  • - ))} -
- )} -
-
-
- ))} -
- ) -} - -Workflows.propTypes = { - specSelectors: PropTypes.shape({ - workflows: PropTypes.func.isRequired, - }).isRequired, - getComponent: PropTypes.func.isRequired, -} - -export default Workflows -``` - -**Step 3: Register in plugin** -```javascript -// src/core/plugins/oas40/index.js - -import Workflows from "./components/workflows.jsx" -import { selectWorkflows } from "./spec-extensions/selectors.js" - -const OpenAPI40Plugin = ({ fn }) => { - return { - components: { - Workflows, - }, - statePlugins: { - spec: { - selectors: { - workflows: createOnlyOAS40Selector(selectWorkflows), - }, - }, - }, - } -} -``` - -**Step 4: Add to layout** -```javascript -// Modify layout to include Workflows component -// This would be done in BaseLayout or a wrapper -``` - -#### Phase 4: Testing - -**Step 1: Create test spec** -```yaml -# test/e2e-cypress/static/documents/oas40/workflows-example.yaml -openapi: 4.0.0 -info: - title: Workflows API - version: 1.0.0 -workflows: - onNewUser: - description: Workflow triggered when a new user is created - steps: - - operationRef: '#/paths/~1users/post' - - operationRef: '#/paths/~1emails~1welcome/post' -paths: - /users: - post: - summary: Create user - # ... -``` - -**Step 2: Create E2E test** -```javascript -// test/e2e-cypress/e2e/features/oas40/workflows.cy.js - -describe("OAS 4.0 - Workflows", () => { - beforeEach(() => { - cy.visit("/e2e-cypress/static/documents/oas40/workflows-example.yaml") - }) - - it("should render workflows section", () => { - cy.get(".opblock-tag").contains("Workflows").should("exist") - }) - - it("should display workflow definitions", () => { - cy.get(".opblock-workflow").should("have.length.greaterThan", 0) - }) - - it("should show workflow steps", () => { - cy.get(".opblock-workflow-steps ul li").should("exist") - }) -}) -``` - -**Step 3: Create unit test** -```javascript -// test/unit/core/plugins/oas40/components/workflows.jsx - -import React from "react" -import { shallow } from "enzyme" -import { fromJS } from "immutable" -import Workflows from "src/core/plugins/oas40/components/workflows.jsx" - -describe("Workflows component", () => { - it("should render workflows from spec", () => { - const mockWorkflows = fromJS({ - onNewUser: { - description: "New user workflow", - steps: [ - { operationRef: "#/paths/~1users/post" } - ] - } - }) - - const props = { - specSelectors: { - workflows: () => mockWorkflows - }, - getComponent: (name) => { - if (name === "Collapse") { - return ({ children }) =>
{children}
- } - } - } - - const wrapper = shallow() - expect(wrapper.find(".opblock-workflow")).toHaveLength(1) - }) - - it("should render nothing when no workflows", () => { - const props = { - specSelectors: { - workflows: () => fromJS({}) - }, - getComponent: () => null - } - - const wrapper = shallow() - expect(wrapper.type()).toBe(null) - }) -}) -``` - -### Summary: Spec → Component Mapping Checklist - -For each new feature in the specification: - -- [ ] **Analyze spec using WebFetch** - - [ ] Understand structure and purpose - - [ ] Get examples from spec - - [ ] Identify data type and location - -- [ ] **Map to component type** (using Step 1D table) - - [ ] Determine if new component or wrapper needed - - [ ] Identify similar existing components - - [ ] Plan component hierarchy - -- [ ] **Create selector** - - [ ] Extract data from spec - - [ ] Use appropriate selector factory - - [ ] Add to spec-extensions/selectors.js - -- [ ] **Implement component** - - [ ] Follow existing patterns - - [ ] Use getComponent for sub-components - - [ ] Add PropTypes validation - - [ ] Include @prettier pragma - -- [ ] **Register in plugin** - - [ ] Add to components export - - [ ] Register selector in statePlugins - - [ ] Add to layout if top-level feature - -- [ ] **Create tests** - - [ ] Unit tests for component - - [ ] Unit tests for selector - - [ ] E2E tests with example spec - - [ ] Test edge cases (missing data, etc.) - -- [ ] **Verify with real spec** - - [ ] Test with example from official spec - - [ ] Test edge cases mentioned in spec - - [ ] Verify rendering matches expected behavior - -## Resources - -- **OAS Specification:** https://spec.openapis.org/ -- **OAS 2.0:** https://spec.openapis.org/oas/v2.0/ -- **OAS 3.0:** https://spec.openapis.org/oas/v3.0.3/ -- **OAS 3.1:** https://spec.openapis.org/oas/v3.1.0/ -- **JSON Schema:** https://json-schema.org/ -- **Plugin API Docs:** `docs/customization/plugin-api.md` -- **OAS 3.1 Reference:** `src/core/plugins/oas31/` -- **Version Detection:** `src/core/plugins/versions/` -- **CLAUDE.md:** Complete codebase guide - -## Best Practices: Iterative Spec-Driven Development - -### Continuous Specification Reference - -**NEVER implement a feature without checking the spec first.** The specification is your source of truth. - -#### During Initial Planning -1. Read the entire specification document -2. Create your component mapping document -3. Prioritize features by importance and dependency - -#### During Implementation -**For each component:** - -1. **Before writing code:** - ```javascript - WebFetch( - "https://spec.openapis.org/oas/v{VERSION}/", - "Provide the complete specification for the '{FIELD_NAME}' field, including all properties, data types, constraints, and examples." - ) - ``` - -2. **While writing the selector:** - - Verify the exact field path in the spec - - Check if field is required or optional - - Confirm data type (string, object, array, etc.) - - Note any default values - -3. **While writing the component:** - - Reference spec examples for rendering approach - - Implement all specified subfields - - Handle edge cases mentioned in spec - - Follow spec's field naming exactly - -4. **After writing tests:** - - Use spec examples as test fixtures - - Test all scenarios mentioned in spec - - Verify behavior matches spec descriptions - -#### Common Mistakes to Avoid - -❌ **DON'T:** -- Guess field structure without checking spec -- Assume field behavior from field name -- Skip optional fields thinking they're unimportant -- Implement based on previous version without checking changes -- Use hardcoded strings without verifying in spec - -✅ **DO:** -- Read spec section before each component -- Verify field paths with WebFetch -- Check spec examples for edge cases -- Confirm data types and constraints -- Reference spec in code comments - -#### Example: Spec-Driven Implementation - -**Bad approach (guessing):** -```javascript -// ❌ Guessing the structure -const workflows = spec.get("workflows") -return workflows.map(w => w.name) // Assumes 'name' exists -``` - -**Good approach (spec-driven):** -```javascript -// ✅ First check the spec: -// WebFetch result shows workflows is a Map[string, Workflow Object] -// Each workflow has: description, steps (array), parameters (optional) - -// Then implement correctly: -const workflows = spec.get("workflows") // Map[string, Workflow Object] -if (!workflows || !workflows.size) return null - -return workflows.entrySeq().map(([workflowName, workflowObject]) => { - // workflowName is the key, workflowObject contains: description, steps, etc. - const description = workflowObject.get("description") - const steps = workflowObject.get("steps") || List() - // ... -}) -``` - -### Verification Checklist Per Component - -Before marking a component complete, verify against spec: - -- [ ] **Field location matches spec exactly** (path in spec tree) -- [ ] **Data type implemented correctly** (string, object, array, etc.) -- [ ] **Required fields are handled** (error if missing vs optional) -- [ ] **Optional fields have defaults** (as specified in spec) -- [ ] **All subfields are rendered** (no fields missed) -- [ ] **Constraints are enforced** (min/max, enums, formats) -- [ ] **Examples from spec render correctly** (visual verification) -- [ ] **Edge cases are handled** (empty, null, malformed) -- [ ] **Field descriptions match spec** (for documentation) -- [ ] **Cross-references work** ($ref, operationRef, etc.) - -### Documentation Trail - -Leave a trail connecting your code to the spec: - -```javascript -/** - * @prettier - */ - -/** - * Workflows Component - * - * Renders the 'workflows' field from OpenAPI 4.0 specification. - * - * Spec reference: https://spec.openapis.org/oas/v4.0.0/#workflows-object - * - * Structure (from spec): - * workflows: - * {workflowName}: - * description: string (optional) - * steps: array of Step Objects (required) - * parameters: Map[string, Parameter] (optional) - * - * This component renders a list of workflow definitions, showing - * workflow steps and their operation references. - */ -const Workflows = ({ specSelectors, getComponent }) => { - // Implementation referencing spec structure -} -``` - -### Progressive Implementation Strategy - -Implement features in order of dependency and spec structure: - -**Phase 1: Core Fields (from spec root)** -1. Version detection (always first) -2. Top-level required fields -3. Top-level optional fields with high usage - -**Phase 2: Info Object Enhancements** -1. New info fields -2. Modified license/contact fields -3. Extended metadata fields - -**Phase 3: Component Object Extensions** -1. New component types -2. Schema keyword changes (if JSON Schema version changed) -3. Security scheme additions - -**Phase 4: Operation-Level Features** -1. New operation fields -2. Parameter enhancements -3. Request/response modifications -4. Callback/link changes - -**Phase 5: Advanced Features** -1. Complex interactions (workflows, callbacks, links) -2. Conditional rendering -3. Advanced security features - -### Spec Change Impact Analysis - -Before implementing, analyze the impact: - -| Spec Change | Swagger UI Impact | Implementation Effort | User Impact | -|-------------|-------------------|----------------------|-------------| -| New top-level field | New component + layout change | High | High | -| New info subfield | Wrap or extend Info | Medium | Medium | -| New auth type | New auth component | Medium | High | -| New schema keyword | Keyword component | Low-Medium | Low | -| Field description change | Documentation only | Low | Low | -| Deprecated field | Warning message | Low | Medium | -| Breaking change | Major refactor | High | High | - -Prioritize high user impact changes first. - -## Questions to Answer During Implementation - -### Before Starting -1. What is the exact version number? (e.g., 3.2, 4.0) -2. Have I read the complete specification at https://spec.openapis.org/? -3. Is it a major or minor version change? -4. What is the migration guide URL? - -### Specification Analysis -5. Does it use a new JSON Schema version? -6. What are ALL the new top-level fields? -7. What are ALL the modified existing fields? -8. What features are deprecated or removed? - -### Component Planning -9. Which components need to be created from scratch? -10. Which existing components need to be wrapped? -11. What selectors need to be added/wrapped? -12. What functions need to be overridden in afterLoad? - -### Authentication & Security -13. Are there new authentication types? -14. Do security schemes have new fields? -15. Are there new security requirements or flows? - -### Testing Strategy -16. Where are the official example specs? -17. What edge cases are mentioned in the spec? -18. What are the validation rules? - -### During Each Component -19. What is the exact field path in the spec? -20. What is the exact data type? -21. Is this field required or optional? -22. What are the constraints (min/max, enum, format)? -23. What are the examples in the spec? -24. How should this render visually? - ---- - -**Remember:** -1. **Always check https://spec.openapis.org/ first** -2. **Use WebFetch to analyze spec sections before coding** -3. **Follow the patterns established by OAS 3.1 implementation** -4. **Verify each component against the specification** -5. **Test with examples from the official spec** -6. **Document the spec reference in code comments** -7. **When in doubt, check the spec again!** diff --git a/vendor/swagger-api/swagger-ui/.commitlintrc.json b/vendor/swagger-api/swagger-ui/.commitlintrc.json deleted file mode 100644 index a55865b3..00000000 --- a/vendor/swagger-api/swagger-ui/.commitlintrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": [ - "@commitlint/config-conventional" - ], - "rules": { - "header-max-length": [ - 2, - "always", - 69 - ], - "scope-case": [ - 2, - "always", - [ - "camel-case", - "kebab-case", - "upper-case" - ] - ], - "subject-case": [ - 0, - "always" - ] - } -} diff --git a/vendor/swagger-api/swagger-ui/.dockerignore b/vendor/swagger-api/swagger-ui/.dockerignore deleted file mode 100644 index 47c624e5..00000000 --- a/vendor/swagger-api/swagger-ui/.dockerignore +++ /dev/null @@ -1,8 +0,0 @@ -/.git -/.github -/dev-helpers -/docs -/src -/swagger-ui-dist-package -/test -/node_modules \ No newline at end of file diff --git a/vendor/swagger-api/swagger-ui/.editorconfig b/vendor/swagger-api/swagger-ui/.editorconfig deleted file mode 100644 index 2d959463..00000000 --- a/vendor/swagger-api/swagger-ui/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -root = true - -[*] -end_of_line = lf -indent_style = space -indent_size = 2 -trim_trailing_whitespace = true -insert_final_newline = true -[*.md] -trim_trailing_whitespace = false diff --git a/vendor/swagger-api/swagger-ui/.eslintignore b/vendor/swagger-api/swagger-ui/.eslintignore deleted file mode 100644 index 5e20fb04..00000000 --- a/vendor/swagger-api/swagger-ui/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -dist/ -node_modules/ -test/e2e-selenium/ diff --git a/vendor/swagger-api/swagger-ui/.eslintrc.js b/vendor/swagger-api/swagger-ui/.eslintrc.js deleted file mode 100644 index 87d561e1..00000000 --- a/vendor/swagger-api/swagger-ui/.eslintrc.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @prettier - */ -const path = require("node:path") - -module.exports = { - parser: "@babel/eslint-parser", - env: { - browser: true, - node: true, - es6: true, - jest: true, - "jest/globals": true, - }, - parserOptions: { - ecmaFeatures: { jsx: true }, - babelOptions: { configFile: path.join(__dirname, "babel.config.js") }, - }, - extends: [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:prettier/recommended", - ], - plugins: ["react", "import", "jest", "prettier"], - settings: { - react: { - pragma: "React", - version: "15.0", - }, - }, - rules: { - semi: [2, "never"], - strict: 0, - quotes: [ - 2, - "double", - { - avoidEscape: true, - allowTemplateLiterals: true, - }, - ], - "no-unused-vars": 2, - "no-multi-spaces": 1, - camelcase: [ - "error", - { - allow: [ - "^UNSAFE_", - "^requestSnippetGenerator_", - "^JsonSchema_", - "^curl_", - "^dom_", - "^api_", - "^client_", - "^grant_", - "^code_", - "^redirect_", - "^spec", - ], - }, - ], - "no-use-before-define": [2, "nofunc"], - "no-underscore-dangle": 0, - "no-unused-expressions": 1, - "comma-dangle": 0, - "no-console": [ - 2, - { - allow: ["warn", "error"], - }, - ], - "react/jsx-no-bind": 1, - "react/jsx-no-target-blank": 2, - "react/display-name": 0, - "import/no-extraneous-dependencies": 2, - "react/jsx-filename-extension": 2, - }, -} diff --git a/vendor/swagger-api/swagger-ui/.gitattributes b/vendor/swagger-api/swagger-ui/.gitattributes deleted file mode 100644 index 5e49d7fd..00000000 --- a/vendor/swagger-api/swagger-ui/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -docker-run.sh text eol=lf -/dist/*.map export-ignore -/test export-ignore -/docs export-ignore \ No newline at end of file diff --git a/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Bug_report.md b/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Bug_report.md deleted file mode 100644 index 47e0233c..00000000 --- a/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Bug_report.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -name: Bug report -about: Report an issue you're experiencing - ---- - - - -### Q&A (please complete the following information) - - OS: [e.g. macOS] - - Browser: [e.g. chrome, safari] - - Version: [e.g. 22] - - Method of installation: [e.g. npm, dist assets] - - Swagger-UI version: [e.g. 3.10.0] - - Swagger/OpenAPI version: [e.g. Swagger 2.0, OpenAPI 3.0] - -### Content & configuration - - -Example Swagger/OpenAPI definition: -```yaml -# your YAML here -``` - -Swagger-UI configuration options: -```js -SwaggerUI({ - // your config options here -}) -``` - -``` -?yourQueryStringConfig -``` - -### Describe the bug you're encountering - - -### To reproduce... - -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -### Expected behavior - - -### Screenshots - - -### Additional context or thoughts - diff --git a/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Feature_request.md b/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Feature_request.md deleted file mode 100644 index 2aaa760e..00000000 --- a/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Feature_request.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: Feature request -about: Suggest a new feature or enhancement for this project - ---- - - ### Content & configuration - -Swagger/OpenAPI definition: - ```yaml - # your YAML here - ``` - - Swagger-UI configuration options: - ```js - SwaggerUI({ - // your config options here - }) - ``` - - ``` - ?yourQueryStringConfig - ``` - - -### Is your feature request related to a problem? - - -### Describe the solution you'd like - - -### Describe alternatives you've considered - - -### Additional context - diff --git a/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Support.md b/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Support.md deleted file mode 100644 index 81d8b1e5..00000000 --- a/vendor/swagger-api/swagger-ui/.github/ISSUE_TEMPLATE/Support.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Support -about: Ask a question or request help with your implementation. - ---- - - - -### Q&A (please complete the following information) - - OS: [e.g. macOS] - - Browser: [e.g. chrome, safari] - - Version: [e.g. 22] - - Method of installation: [e.g. npm, dist assets] - - Swagger-UI version: [e.g. 3.10.0] - - Swagger/OpenAPI version: [e.g. Swagger 2.0, OpenAPI 3.0] - -### Content & configuration - - -Swagger/OpenAPI definition: -```yaml -# your YAML here -``` - -Swagger-UI configuration options: -```js -SwaggerUI({ - // your config options here -}) -``` - -``` -?yourQueryStringConfig -``` - -### Screenshots - - -### How can we help? - diff --git a/vendor/swagger-api/swagger-ui/.github/dependabot.yaml b/vendor/swagger-api/swagger-ui/.github/dependabot.yaml deleted file mode 100644 index 6475663f..00000000 --- a/vendor/swagger-api/swagger-ui/.github/dependabot.yaml +++ /dev/null @@ -1,35 +0,0 @@ -version: 2 -updates: - - package-ecosystem: npm - directory: "/" - schedule: - interval: daily - time: "23:00" - commit-message: - prefix: "chore" - include: "scope" - open-pull-requests-limit: 3 - ignore: - # node-fetch must be synced manually - - dependency-name: "node-fetch" - - - package-ecosystem: "docker" - # Look for a `Dockerfile` in the `root` directory - directory: "/" - # Check for updates once a week - schedule: - interval: "weekly" - time: "23:00" - - - package-ecosystem: "github-actions" - target-branch: "master" - directory: "/" - schedule: - interval: "daily" - time: "23:00" - commit-message: - prefix: "chore" - include: "scope" - open-pull-requests-limit: 3 - - diff --git a/vendor/swagger-api/swagger-ui/.github/lock.yml b/vendor/swagger-api/swagger-ui/.github/lock.yml deleted file mode 100644 index e0b8c11a..00000000 --- a/vendor/swagger-api/swagger-ui/.github/lock.yml +++ /dev/null @@ -1,15 +0,0 @@ -daysUntilLock: 365 -skipCreatedBefore: 2017-03-29 # initial release of Swagger UI 3.0.0 -exemptLabels: [] -lockLabel: "locked-by: lock-bot" -setLockReason: false -only: issues -lockComment: false -# lockComment: | -# Locking due to inactivity. - -# This is done to avoid resurrecting old issues and bumping long threads with new, possibly unrelated content. - -# If you think you're experiencing something similar to what you've found here: please [open a new issue](https://github.com/swagger-api/swagger-ui/issues/new/choose), follow the template, and reference this issue in your report. - -# Thanks! diff --git a/vendor/swagger-api/swagger-ui/.github/pull_request_template.md b/vendor/swagger-api/swagger-ui/.github/pull_request_template.md deleted file mode 100644 index 64e9cf6f..00000000 --- a/vendor/swagger-api/swagger-ui/.github/pull_request_template.md +++ /dev/null @@ -1,55 +0,0 @@ - - -### Description - - - - -### Motivation and Context - - - - - - - -### How Has This Been Tested? - - - - - - -### Screenshots (if appropriate): - - - -## Checklist - - - -### My PR contains... - -- [ ] No code changes (`src/` is unmodified: changes to documentation, CI, metadata, etc.) -- [ ] Dependency changes (any modification to dependencies in `package.json`) -- [ ] Bug fixes (non-breaking change which fixes an issue) -- [ ] Improvements (misc. changes to existing features) -- [ ] Features (non-breaking change which adds functionality) - -### My changes... -- [ ] are breaking changes to a public API (config options, System API, major UI change, etc). -- [ ] are breaking changes to a private API (Redux, component props, utility functions, etc.). -- [ ] are breaking changes to a developer API (npm script behavior changes, new dev system dependencies, etc). -- [ ] are not breaking changes. - -### Documentation -- [ ] My changes do not require a change to the project documentation. -- [ ] My changes require a change to the project documentation. -- [ ] If yes to above: I have updated the documentation accordingly. - -### Automated tests -- [ ] My changes can not or do not need to be tested. -- [ ] My changes can and should be tested by unit and/or integration tests. -- [ ] If yes to above: I have added tests to cover my changes. -- [ ] If yes to above: I have taken care to cover edge cases in my tests. -- [ ] All new and existing tests passed. diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/codeql.yml b/vendor/swagger-api/swagger-ui/.github/workflows/codeql.yml deleted file mode 100644 index f8ba9eff..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/codeql.yml +++ /dev/null @@ -1,63 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "master" ] - schedule: - - cron: '16 04 * * 2' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'javascript' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Use only 'java' to analyze code written in Java, Kotlin or both - # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v4 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - config: | - paths-ignore: - - 'dist/' - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 - with: - category: "/language:${{matrix.language}}" diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/dependabot-merge.yml b/vendor/swagger-api/swagger-ui/.github/workflows/dependabot-merge.yml deleted file mode 100644 index 9495d428..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/dependabot-merge.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Merge me! - -on: - pull_request_target: - branches: [ master, next ] - -permissions: - contents: read - -jobs: - merge-me: - name: Merge me! - if: github.actor == 'dependabot[bot]' - runs-on: ubuntu-latest - steps: - # This first step will fail if there's no metadata and so the approval - # will not occur. - - name: Dependabot metadata - id: dependabot-metadata - uses: dependabot/fetch-metadata@v3.0.0 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - # Here the PR gets approved. - - name: Approve a PR - if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ secrets.SWAGGER_BOT_GITHUB_TOKEN }} - # Finally, tell dependabot to merge the PR if all checks are successful - - name: Instruct dependabot to squash & merge - if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} - uses: mshick/add-pr-comment@v3 - with: - repo-token: ${{ secrets.SWAGGER_BOT_GITHUB_TOKEN }} - allow-repeats: true - message: | - @dependabot squash and merge - env: - GITHUB_TOKEN: ${{ secrets.SWAGGER_BOT_GITHUB_TOKEN }} diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/docker-build-push-unstable.yml b/vendor/swagger-api/swagger-ui/.github/workflows/docker-build-push-unstable.yml deleted file mode 100644 index 2acfe76a..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/docker-build-push-unstable.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build & Push SwaggerUI unstable Docker image - -on: - workflow_run: - workflows: ["Node.js CI"] - types: - - completed - branches: [master] - -jobs: - - build-push-unstable: - if: github.event.workflow_run.conclusion == 'success' - name: Build & Push SwaggerUI unstable Docker image - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v6 - - - name: Use Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - cache: npm - cache-dependency-path: package-lock.json - - - name: Install dependencies - run: npm ci - - - name: Build SwaggerUI - run: npm run build - - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Log in to DockerHub - uses: docker/login-action@v4 - with: - username: ${{ secrets.DOCKERHUB_SB_USERNAME }} - password: ${{ secrets.DOCKERHUB_SB_PASSWORD }} - - - name: Build docker image and push - uses: docker/build-push-action@v7 - with: - context: . - push: true - platforms: linux/amd64,linux/arm/v6,linux/arm64,linux/386,linux/ppc64le - provenance: false - tags: swaggerapi/swagger-ui:unstable diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/docker-build-push.yml b/vendor/swagger-api/swagger-ui/.github/workflows/docker-build-push.yml deleted file mode 100644 index c6460b53..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/docker-build-push.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: Build & Push SwaggerUI Docker image - -on: - workflow_run: - workflows: ["Release SwaggerUI"] - types: - - completed - branches: [master] - -jobs: - - build-push: - if: github.event.workflow_run.conclusion == 'success' - name: Build & Push SwaggerUI Docker image - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v6 - - - name: Use Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - cache: npm - cache-dependency-path: package-lock.json - - - name: Install dependencies - run: npm ci - - - name: Build SwaggerUI - run: npm run build - - - name: Determine released version - uses: actions/github-script@v9 - with: - script: | - const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id, - }); - const matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { - return artifact.name == "released-version" - })[0]; - const download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - const fs = require('fs'); - fs.writeFileSync('${{github.workspace}}/released-version.zip', Buffer.from(download.data)); - - run: | - unzip released-version.zip - RELEASED_VERSION=$(cat released-version.txt) - echo "RELEASED_VERSION=$RELEASED_VERSION" >> $GITHUB_ENV - - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Log in to DockerHub - uses: docker/login-action@v4 - with: - username: ${{ secrets.DOCKERHUB_SB_USERNAME }} - password: ${{ secrets.DOCKERHUB_SB_PASSWORD }} - - - name: Build docker image and push - uses: docker/build-push-action@v7 - with: - context: . - push: true - platforms: linux/amd64,linux/arm/v6,linux/arm64,linux/386,linux/ppc64le - provenance: false - tags: swaggerapi/swagger-ui:latest,swaggerapi/swagger-ui:v${{ env.RELEASED_VERSION }} diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/docker-image-check.yml b/vendor/swagger-api/swagger-ui/.github/workflows/docker-image-check.yml deleted file mode 100644 index dd119606..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/docker-image-check.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Security scan for docker image - -on: - workflow_dispatch: - schedule: - - cron: '30 4 * * *' - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@master - with: - image-ref: 'docker.swagger.io/swaggerapi/swagger-ui:unstable' - format: 'table' - exit-code: '1' - ignore-unfixed: true - vuln-type: 'os,library' - severity: 'CRITICAL,HIGH' diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/nodejs.yml b/vendor/swagger-api/swagger-ui/.github/workflows/nodejs.yml deleted file mode 100644 index 0fba4b0c..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/nodejs.yml +++ /dev/null @@ -1,86 +0,0 @@ -# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Node.js CI - -on: - push: - branches: [ master, next ] - pull_request: - branches: [ master, next ] - -env: - CYPRESS_CACHE_FOLDER: cypress/cache - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v6 - - - name: Use Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - - - name: Cache Node Modules and Cypress binary - uses: actions/cache@v5 - id: cache-primes - with: - path: | - node_modules - ${{ env.CYPRESS_CACHE_FOLDER }} - key: ${{ runner.os }}-node-and-cypress-${{ hashFiles('package-lock.json') }} - - - name: Install dependencies - if: steps.cache-primes.outputs.cache-hit != 'true' - run: npm ci - - - name: Lint code for errors only - run: npm run lint-errors - - - name: Run all tests - run: npm run test:unit - env: - CI: true - - - name: Build SwaggerUI - run: npm run build - - - name: Test build artifacts - run: npm run test:artifact - - e2e-tests: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - containers: ['+(a11y|security|bugs)/**/*cy.js', 'features/**/+(o|d)*.cy.js', 'features/**/m*.cy.js', 'features/**/!(o|d|m)*.cy.js'] - - steps: - - uses: actions/checkout@v6 - - - name: Use Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - - - name: Cache Node Modules and Cypress binary - uses: actions/cache@v5 - id: cache-primes - with: - path: | - node_modules - ${{ env.CYPRESS_CACHE_FOLDER }} - key: ${{ runner.os }}-node-and-cypress-${{ hashFiles('package-lock.json') }} - - - name: Install dependencies - if: steps.cache-primes.outputs.cache-hit != 'true' - run: npm ci - - - name: Cypress Test - run: npx start-server-and-test cy:start http://localhost:3204 'npm run cy:run -- --spec "test/e2e-cypress/e2e/${{ matrix.containers }}"' diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-dist.yml b/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-dist.yml deleted file mode 100644 index f738b84e..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-dist.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Release SwaggerUI Dist - -on: - workflow_run: - workflows: ["Release SwaggerUI"] - types: - - completed - branches: [ master ] - -jobs: - release-swagger-ui-dist: - if: github.event.workflow_run.conclusion == 'success' - name: Release swagger-ui-dist npm package - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - persist-credentials: false - ref: master - - - name: Use Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - cache: npm - cache-dependency-path: package-lock.json - - - name: Install dependencies - run: npm ci - - - name: Download build artifact - uses: actions/github-script@v9 - with: - script: | - const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id, - }); - const matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { - return artifact.name == "dist" - })[0]; - const download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - const fs = require('fs'); - fs.writeFileSync('${{github.workspace}}/dist.zip', Buffer.from(download.data)); - - run: | - unzip -o dist.zip -d dist - - - name: Publish to npmjs.com - run: ./deploy.sh - working-directory: ./swagger-ui-dist-package - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - PUBLISH_DIST: true diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-packagist.yml b/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-packagist.yml deleted file mode 100644 index 05d51750..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-packagist.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Release SwaggerUI to Packagist - -on: - workflow_run: - workflows: ["Release SwaggerUI"] - types: - - completed - branches: [ master ] - -jobs: - release-swagger-ui-packagist: - if: github.event.workflow_run.conclusion == 'success' - name: Release swagger-ui to packagist - runs-on: ubuntu-latest - steps: - - name: Update SwaggerUI packagist package - run: | - curl --fail -X POST \ - -H "Content-Type: application/json" \ - "https://packagist.org/api/update-package?username=${{ secrets.PACKAGIST_USER }}&apiToken=${{ secrets.PACKAGIST_API_TOKEN }}" \ - -d "{\"repository\":{\"url\":\"https://packagist.org/packages/swagger-api/swagger-ui\"}}" diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-react.yml b/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-react.yml deleted file mode 100644 index f37cf3ba..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui-react.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Release SwaggerUI React - -on: - workflow_run: - workflows: ["Release SwaggerUI"] - types: - - completed - branches: [ master ] - -jobs: - release-swagger-ui-react: - if: github.event.workflow_run.conclusion == 'success' - name: Release swagger-ui-react npm package - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - persist-credentials: false - ref: master - - - name: Use Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - cache: npm - cache-dependency-path: package-lock.json - - - name: Install dependencies - run: npm ci - - - name: Download build artifact - uses: actions/github-script@v9 - with: - script: | - const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id, - }); - const matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { - return artifact.name == "dist" - })[0]; - const download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - const fs = require('fs'); - fs.writeFileSync('${{github.workspace}}/dist.zip', Buffer.from(download.data)); - - run: | - unzip -o dist.zip -d dist - - - name: Publish to npmjs.com - run: ./run.sh - working-directory: ./flavors/swagger-ui-react/release - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - PUBLISH_FLAVOR_REACT: true diff --git a/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui.yml b/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui.yml deleted file mode 100644 index 2c53028d..00000000 --- a/vendor/swagger-api/swagger-ui/.github/workflows/release-swagger-ui.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Release SwaggerUI -on: - workflow_dispatch: - branches: - - master - -jobs: - release: - name: Release swagger-ui npm package - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - persist-credentials: false - ref: master - - - name: Use Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - cache: npm - cache-dependency-path: package-lock.json - - - name: Determine the next release version - uses: cycjimmy/semantic-release-action@v6 - with: - dry_run: true - extra_plugins: | - @semantic-release/git - @semantic-release/exec - env: - GITHUB_TOKEN: ${{ secrets.SWAGGER_BOT_GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Nothing to release - if: ${{ env.NEXT_RELEASE_VERSION == '' }} - uses: actions/github-script@v9 - with: - script: | - core.setFailed('Nothing to release') - - - name: Install dependencies - run: npm ci - - - name: Prepare for the Release - env: - REACT_APP_VERSION: ${{ env.NEXT_RELEASE_VERSION }} - run: | - npm run test - npm run build - - - name: Semantic Release - id: semantic - uses: cycjimmy/semantic-release-action@v6 - with: - dry_run: false - extra_plugins: | - @semantic-release/git - env: - GITHUB_TOKEN: ${{ secrets.SWAGGER_BOT_GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Release failed - if: steps.semantic.outputs.new_release_published == 'false' - uses: actions/github-script@v9 - with: - script: | - core.setFailed('Release failed') - - - name: Release published - run: | - echo ${{ steps.semantic.outputs.new_release_version }} - echo ${{ steps.semantic.outputs.new_release_major_version }} - echo ${{ steps.semantic.outputs.new_release_minor_version }} - echo ${{ steps.semantic.outputs.new_release_patch_version }} - - - name: Upload build artifacts - uses: actions/upload-artifact@v7 - with: - name: dist - path: ./dist - - - - name: Prepare released version for uploading - shell: bash - run: | - echo ${{ steps.semantic.outputs.new_release_version }} > released-version.txt - - name: Upload released version - uses: actions/upload-artifact@v7 - with: - name: released-version - path: ./released-version.txt - retention-days: 1 diff --git a/vendor/swagger-api/swagger-ui/.gitignore b/vendor/swagger-api/swagger-ui/.gitignore deleted file mode 100644 index e9578575..00000000 --- a/vendor/swagger-api/swagger-ui/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -node_modules -.idea -.vscode -.deps_check -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local -.nyc_output -npm-debug.log* -.eslintcache -*.iml -selenium-debug.log -chromedriver.log -test/e2e/db.json -docs/_book -dev-helpers/examples - -# dist -flavors/**/dist/* -/lib -/es -dist/log* -/swagger-ui-*.tgz - -# Cypress -test/e2e-cypress/screenshots -test/e2e-cypress/videos diff --git a/vendor/swagger-api/swagger-ui/.husky/commit-msg b/vendor/swagger-api/swagger-ui/.husky/commit-msg deleted file mode 100644 index 951570f7..00000000 --- a/vendor/swagger-api/swagger-ui/.husky/commit-msg +++ /dev/null @@ -1 +0,0 @@ -npx commitlint -e diff --git a/vendor/swagger-api/swagger-ui/.husky/pre-commit b/vendor/swagger-api/swagger-ui/.husky/pre-commit deleted file mode 100644 index 2312dc58..00000000 --- a/vendor/swagger-api/swagger-ui/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -npx lint-staged diff --git a/vendor/swagger-api/swagger-ui/.lintstagedrc b/vendor/swagger-api/swagger-ui/.lintstagedrc deleted file mode 100644 index 0b1dd617..00000000 --- a/vendor/swagger-api/swagger-ui/.lintstagedrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*.{js,jsx}": ["eslint --max-warnings 0"], - "*.scss": ["stylelint '**/*.scss'"] -} diff --git a/vendor/swagger-api/swagger-ui/.npmignore b/vendor/swagger-api/swagger-ui/.npmignore deleted file mode 100644 index 600fc159..00000000 --- a/vendor/swagger-api/swagger-ui/.npmignore +++ /dev/null @@ -1,16 +0,0 @@ -* -*/ -!README.md -!NOTICE -!package.json -!dist/swagger-ui.js -!dist/swagger-ui.js.map -!dist/swagger-ui-bundle.js -!dist/swagger-ui-standalone-preset.js -!dist/swagger-ui-es-bundle.js -!dist/swagger-ui-es-bundle-core.js -!dist/swagger-ui-es-bundle-core.js.map -!dist/swagger-ui.css -!dist/swagger-ui.css.map -!dist/oauth2-redirect.html -!dist/oauth2-redirect.js \ No newline at end of file diff --git a/vendor/swagger-api/swagger-ui/.npmrc b/vendor/swagger-api/swagger-ui/.npmrc deleted file mode 100644 index d508edc4..00000000 --- a/vendor/swagger-api/swagger-ui/.npmrc +++ /dev/null @@ -1 +0,0 @@ -save-prefix="=" diff --git a/vendor/swagger-api/swagger-ui/.nvmrc b/vendor/swagger-api/swagger-ui/.nvmrc deleted file mode 100644 index d845d9d8..00000000 --- a/vendor/swagger-api/swagger-ui/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -24.14.0 diff --git a/vendor/swagger-api/swagger-ui/.prettierrc.yaml b/vendor/swagger-api/swagger-ui/.prettierrc.yaml deleted file mode 100644 index 2e34d757..00000000 --- a/vendor/swagger-api/swagger-ui/.prettierrc.yaml +++ /dev/null @@ -1,5 +0,0 @@ -semi: false -trailingComma: es5 -endOfLine: lf -requirePragma: true -insertPragma: true diff --git a/vendor/swagger-api/swagger-ui/.releaserc b/vendor/swagger-api/swagger-ui/.releaserc deleted file mode 100644 index d90e9626..00000000 --- a/vendor/swagger-api/swagger-ui/.releaserc +++ /dev/null @@ -1,26 +0,0 @@ -{ - "branches": [ - {"name": "master"} - ], - "tagFormat": "v${version}", - "plugins": [ - "@semantic-release/commit-analyzer", - [ - "@semantic-release/exec", - { - "verifyReleaseCmd": "echo \"NEXT_RELEASE_VERSION=${nextRelease.version}\" >> $GITHUB_ENV" - } - ], - "@semantic-release/release-notes-generator", - "@semantic-release/npm", - "@semantic-release/github", - ["@semantic-release/git", { - "assets": [ - "dist/*.{js,html,css}", - "package.json", - "package-lock.json" - ], - "message": "chore(release): cut the ${nextRelease.version} release\n\n${nextRelease.notes}" - }] - ] -} diff --git a/vendor/swagger-api/swagger-ui/CLAUDE.md b/vendor/swagger-api/swagger-ui/CLAUDE.md deleted file mode 100644 index 5148b423..00000000 --- a/vendor/swagger-api/swagger-ui/CLAUDE.md +++ /dev/null @@ -1,1072 +0,0 @@ -# CLAUDE.md - Swagger UI Codebase Guide - -> **Last Updated:** 2026-02-24 -> **Version:** 5.32.0 (in development) -> **Purpose:** Comprehensive guide for AI assistants working with the Swagger UI codebase - ---- - -## Table of Contents - -1. [Repository Overview](#repository-overview) -2. [Project Architecture](#project-architecture) -3. [Development Setup](#development-setup) -4. [Build System](#build-system) -5. [Testing Infrastructure](#testing-infrastructure) -6. [Code Style & Conventions](#code-style--conventions) -7. [Git Workflow](#git-workflow) -8. [Plugin Architecture](#plugin-architecture) -9. [Key Files & Directories](#key-files--directories) -10. [Common Workflows](#common-workflows) -11. [Important Guidelines](#important-guidelines) - ---- - -## Repository Overview - -### What is Swagger UI? - -Swagger UI is a tool that allows developers to visualize and interact with API resources without having implementation logic in place. It's automatically generated from OpenAPI (formerly Swagger) Specification documents. - -### Multi-Package Monorepo Structure - -This repository publishes **three different npm packages**: - -1. **swagger-ui** (main package) - - Traditional npm module for single-page applications - - Entry: `dist/swagger-ui.js` - - ES Module: `dist/swagger-ui-es-bundle-core.js` - - Includes dependency resolution via Webpack/Browserify - -2. **swagger-ui-dist** (distribution package) - - Dependency-free module for server-side projects - - Published separately via GitHub workflow - - Template location: `swagger-ui-dist-package/` - -3. **swagger-ui-react** (React component) - - React wrapper component - - Location: `flavors/swagger-ui-react/` - - Uses React hooks - - Released separately via GitHub workflow - -### OpenAPI Specification Compatibility - -- **Current Support:** OpenAPI 2.0, 3.0.x, 3.1.x -- **Latest Version:** v5.31.0 (supports up to OpenAPI 3.1.2) - -### License - -Apache 2.0 - See LICENSE and NOTICE files for details. - ---- - -## Project Architecture - -### Technology Stack - -**Core Framework:** -- React 18 (>=16.8.0 <20) - UI components -- Redux 5.0.1 - State management -- Redux Immutable 4.0.0 - Immutable state -- Immutable.js 3.x - Immutable data structures -- React Redux 9.2.0 - React-Redux bindings - -**API & Schema Processing:** -- swagger-client 3.36.0 - OpenAPI client -- js-yaml 4.1.1 - YAML parsing -- remarkable 2.0.1 - Markdown rendering - -**Security:** -- DOMPurify 3.2.6 - HTML sanitization (CRITICAL for XSS prevention) -- serialize-error 8.1.0 - Error serialization - -**Build Tools:** -- Webpack 5.97.1 - Module bundling -- Babel 7.26.x - JavaScript transpilation -- sass-embedded 1.86.0 - SCSS compilation -- PostCSS - CSS processing - -**Testing:** -- Jest 29.7.0 - Unit testing -- Cypress 14.2.0 - E2E testing -- Enzyme 3.11.0 - React component testing - -**Development:** -- ESLint 8.57.0 - JavaScript linting -- Prettier 3.5.3 - Code formatting -- Stylelint 16.19.1 - CSS linting -- Husky 9.1.7 - Git hooks -- lint-staged 15.5.0 - Pre-commit linting - -### Plugin-Based Architecture - -Swagger UI uses a **sophisticated plugin system** powered by Redux. The core system (`src/core/system.js`) manages: - -- Plugin registration and lifecycle -- Redux store creation and middleware -- State plugin combination -- Action/selector binding -- Configuration management - -**26 Core Plugins** (in `src/core/plugins/`): -- `auth` - Authentication handling -- `configs` - Configuration management -- `deep-linking` - URL-based navigation -- `download-url` - Spec downloading -- `err` - Error handling and transformation -- `filter` - API filtering -- `icons` - Icon components -- `json-schema-2020-12` - JSON Schema 2020-12 support -- `json-schema-2020-12-samples` - Sample generation -- `json-schema-5` - JSON Schema Draft 5 support -- `json-schema-5-samples` - Sample generation for Draft 5 -- `layout` - Layout system -- `logs` - Logging -- `oas3` - OpenAPI 3.0.x support -- `oas31` - OpenAPI 3.1.x support -- `oas32` - OpenAPI 3.2.x support -- `on-complete` - Completion callbacks -- `request-snippets` - Code snippet generation -- `safe-render` - Safe component rendering -- `spec` - Specification handling -- `swagger-client` - API client integration -- `syntax-highlighting` - Code highlighting -- `util` - Utilities -- `versions` - Version detection -- `view` - View rendering -- `view-legacy` - Legacy view support - ---- - -## Development Setup - -### Prerequisites - -- **Node.js:** >=24.14.0 (Node 24.x recommended, as defined in `.nvmrc`) -- **npm:** >=11.9.0 -- **Git:** Any version -- **JDK 7+:** Required for Nightwatch.js integration tests - -### Installation Steps - -```bash -# Clone the repository -git clone https://github.com/swagger-api/swagger-ui.git -cd swagger-ui - -# Install dependencies -npm install - -# Initialize Husky (optional, for git hooks) -npx husky init - -# Start development server -npm run dev - -# Open http://localhost:3200/ -``` - -### Development Server - -The `npm run dev` command starts a hot-reloading Webpack dev server on **port 3200**. - -### Using Local API Definitions - -Edit `dev-helpers/dev-helper-initializer.js` to change the spec URL: - -```javascript -// Replace -url: "https://petstore.swagger.io/v2/swagger.json", - -// With -url: "./examples/your-local-api-definition.yaml", -``` - -**Important:** Local files must be in the `dev-helpers/` directory or subdirectory. Use `dev-helpers/examples/` (already in `.gitignore`). - ---- - -## Build System - -### Babel Environments - -Three Babel environments configured in `babel.config.js`: - -1. **development/production** - Browser builds with `modules: "auto"` -2. **commonjs** - CommonJS modules with `modules: "commonjs"` for Node.js -3. **esm** - ES modules with `modules: false` for modern bundlers - -### Babel Aliases - -```javascript -{ - root: ".", - core: "./src/core" -} -``` - -### Browserslist Environments - -Defined in `.browserslistrc`: - -- `[browser-production]` - Production browser targets -- `[browser-development]` - Latest Chrome, Firefox, Safari -- `[isomorphic-production]` - Browser + Node targets -- `[node-production]` - Maintained Node versions -- `[node-development]` - Node 24 - -### Build Commands - -```bash -# Full build (stylesheets + all bundles) -npm run build - -# Individual builds -npm run build:core # Core bundle (browser) -npm run build:bundle # Isomorphic bundle -npm run build:standalone # Standalone preset -npm run build:es:bundle # ES module bundle -npm run build:es:bundle:core # ES module core -npm run build-stylesheets # CSS only - -# Clean build artifacts -npm run clean -``` - -### Build Output (dist/) - -- `swagger-ui.js` - Core bundle (CommonJS) -- `swagger-ui.css` - Compiled styles -- `swagger-ui-bundle.js` - Isomorphic bundle -- `swagger-ui-standalone-preset.js` - Standalone preset -- `swagger-ui-es-bundle.js` - ES module bundle -- `swagger-ui-es-bundle-core.js` - ES module core -- `oauth2-redirect.html` - OAuth2 redirect page - -### Webpack Configurations - -Located in `webpack/` directory: - -- `_config-builder.js` - Base configuration -- `core.js` - Core build -- `bundle.js` - Bundle build -- `standalone.js` - Standalone build -- `es-bundle.js` - ES bundle -- `es-bundle-core.js` - ES core bundle -- `stylesheets.js` - CSS build -- `dev.js` - Development server -- `dev-e2e.js` - E2E testing server - ---- - -## Testing Infrastructure - -### Unit Tests (Jest) - -**Configuration:** `config/jest/jest.unit.config.js` - -**Environment:** jsdom (simulates browser environment) - -**Location:** `test/unit/` - -**Command:** -```bash -npm run test:unit -``` - -**Key Features:** -- 37 unit test files -- Tests for core plugins, components, system -- XSS security tests -- Silent mode enabled by default (set to `false` for console output) -- Module name mapper for SVG and standalone imports -- Transform ignore patterns for node_modules exceptions - -**Setup Files:** -- `test/unit/jest-shim.js` - Polyfills and shims -- `test/unit/setup.js` - Test environment setup - -### E2E Tests (Cypress) - -**Configuration:** `cypress.config.js` - -**Location:** `test/e2e-cypress/` - -**Base URL:** http://localhost:3230/ - -**Commands:** -```bash -# Run all E2E tests -npm run cy:ci - -# Interactive Cypress runner -npm run cy:dev - -# Headless run -npm run cy:run - -# Start servers and run tests -npm run cy:start # Starts webpack + mock API -``` - -**Structure:** -- `test/e2e-cypress/e2e/` - Test specs (99 test files) -- `test/e2e-cypress/static/` - Test fixtures and documents -- `test/e2e-cypress/support/` - Test helpers and commands - -**Test Categories:** -- `a11y/**/*cy.js` - Accessibility tests -- `security/**/*cy.js` - Security tests -- `bugs/**/*cy.js` - Bug regression tests -- `features/**/*cy.js` - Feature tests - -**Mock API Server:** -```bash -npm run cy:mock-api # JSON Server on port 3204 -``` - -### Artifact Tests - -**Configuration:** `config/jest/jest.artifact.config.js` - -**Purpose:** Verify build artifacts export correctly - -**Command:** -```bash -npm run test:artifact -``` - -### Complete Test Suite - -```bash -npm test # Runs: lint-errors + test:unit + cy:ci -``` - -### CI/CD Testing - -**GitHub Actions Workflow:** `.github/workflows/nodejs.yml` - -**Two Jobs:** -1. **build** - Lint, unit tests, build, artifact tests -2. **e2e-tests** - Cypress tests (matrix strategy with 3 containers) - -**Branches:** `master`, `next` - ---- - -## Code Style & Conventions - -### ESLint Configuration - -**File:** `.eslintrc.js` - -**Parser:** `@babel/eslint-parser` - -**Key Rules:** -- `semi: [2, "never"]` - **No semicolons** -- `quotes: [2, "double"]` - **Double quotes** (allow template literals) -- `no-unused-vars: 2` - Error on unused variables -- `camelcase: ["error"]` - Enforce camelCase (with exceptions for UNSAFE_, request generators, etc.) -- `no-console: [2, {allow: ["warn", "error"]}]` - Only `console.warn` and `console.error` allowed -- `react/jsx-no-bind: 1` - Warning for JSX bind -- `react/jsx-filename-extension: 2` - JSX only in `.jsx` files -- `import/no-extraneous-dependencies: 2` - Error on extraneous dependencies - -**Extends:** -- `eslint:recommended` -- `plugin:react/recommended` -- `plugin:prettier/recommended` - -### Prettier Configuration - -**File:** `.prettierrc.yaml` - -**Settings:** -```yaml -semi: false # No semicolons -trailingComma: es5 # ES5 trailing commas -endOfLine: lf # Unix line endings -requirePragma: true # Require @prettier pragma -insertPragma: true # Insert @prettier pragma -``` - -**IMPORTANT:** Prettier requires `@prettier` pragma comment at the top of files: -```javascript -/** - * @prettier - */ -``` - -### Stylelint Configuration - -**File:** `stylelint.config.js` - -**Custom Syntax:** `postcss-scss` - -**Rules:** -- Uses `stylelint-prettier` plugin -- Prettier integration without pragma requirement - -### Pre-commit Hooks - -**Husky:** `.husky/pre-commit` runs `npx lint-staged` - -**Lint-staged Configuration:** `.lintstagedrc` -```json -{ - "*.{js,jsx}": ["eslint --max-warnings 0"], - "*.scss": ["stylelint '**/*.scss'"] -} -``` - -**Critical:** All staged JS/JSX/SCSS files are linted with **zero warnings tolerance**. - -### File Structure Conventions - -**Components:** -- Location: `src/core/components/` -- Extension: `.jsx` (React components) -- Format: PascalCase for component names - -**Styles:** -- Location: `src/style/` -- Extension: `.scss` -- Format: SCSS with PostCSS processing -- Dark mode: `_dark-mode.scss` - -**Tests:** -- Unit: `test/unit/` (mirrors source structure) -- E2E: `test/e2e-cypress/e2e/` -- Naming: `*.test.js`, `*.spec.js`, `*.cy.js` (Cypress) - ---- - -## Git Workflow - -### Branch Strategy - -**Main Branches:** -- `master` - Production releases -- `next` - Next version development - -**Feature Branches:** -- Should branch from `master` or `next` -- Use descriptive names - -### Commit Conventions - -**Format:** Conventional Commits (enforced by commitlint) - -**Structure:** -``` -(): - - - -