Upstream dedoc/scramble state of the touched files before c327325c7c

This commit is contained in:
upstream committed 2026-10-02 16:05:25 +02:00
1 parent e6f2982a99
commit 185af03608
1 file changed
+421
+421
View File
@@ -0,0 +1,421 @@
<?php
namespace Dedoc\Scramble;
use Dedoc\Scramble\Configuration\GeneratorConfigCollection;
use Dedoc\Scramble\Configuration\OperationTransformers;
use Dedoc\Scramble\Configuration\ParametersExtractors;
use Dedoc\Scramble\Console\Commands\AnalyzeDocumentation;
use Dedoc\Scramble\Console\Commands\CacheDocumentation;
use Dedoc\Scramble\Console\Commands\ClearDocumentationCache;
use Dedoc\Scramble\Console\Commands\ExportDocumentation;
use Dedoc\Scramble\Contracts\RouteProvider as RouteProviderContract;
use Dedoc\Scramble\DocumentTransformers\AddDocumentTags;
use Dedoc\Scramble\DocumentTransformers\CleanupUnusedResponseReferencesTransformer;
use Dedoc\Scramble\Extensions\ExceptionToResponseExtension;
use Dedoc\Scramble\Extensions\OperationExtension;
use Dedoc\Scramble\Extensions\TypeToSchemaExtension;
use Dedoc\Scramble\Http\Middleware\RestrictedDocsAccess;
use Dedoc\Scramble\Infer\Definition\FunctionLikeDefinition;
use Dedoc\Scramble\Infer\Extensions\ExtensionsBroker;
use Dedoc\Scramble\Infer\Extensions\IndexBuildingBroker;
use Dedoc\Scramble\Infer\Extensions\InferExtension;
use Dedoc\Scramble\Infer\Scope\Index;
use Dedoc\Scramble\Infer\Scope\LazyShallowReflectionIndex;
use Dedoc\Scramble\Infer\Services\FileParser;
use Dedoc\Scramble\Support\DevTools;
use Dedoc\Scramble\Support\ExceptionToResponseExtensions\AuthenticationExceptionToResponseExtension;
use Dedoc\Scramble\Support\ExceptionToResponseExtensions\AuthorizationExceptionToResponseExtension;
use Dedoc\Scramble\Support\ExceptionToResponseExtensions\HttpExceptionToResponseExtension;
use Dedoc\Scramble\Support\ExceptionToResponseExtensions\NotFoundExceptionToResponseExtension;
use Dedoc\Scramble\Support\ExceptionToResponseExtensions\ValidationExceptionToResponseExtension;
use Dedoc\Scramble\Support\Generator\TypeTransformer;
use Dedoc\Scramble\Support\IndexBuilders\IndexBuilder;
use Dedoc\Scramble\Support\IndexBuilders\PaginatorsCandidatesBuilder;
use Dedoc\Scramble\Support\InferExtensions\AbortHelpersExceptionInfer;
use Dedoc\Scramble\Support\InferExtensions\AfterAnonymousResourceCollectionDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterCursorPaginatorDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterEloquentBuilderDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterEloquentCollectionDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterEloquentRelationDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterJsonApiResourceDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterJsonResourceDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterLengthAwarePaginatorDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterModelDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterPaginatorDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterResourceCollectionDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\AfterResponseDefinitionCreatedExtension;
use Dedoc\Scramble\Support\InferExtensions\ArrayMergeReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\ArrStaticMethodReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\EloquentBuilderExtension;
use Dedoc\Scramble\Support\InferExtensions\FacadeStaticMethodReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\JsonApiResourceCollectionMethodReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\JsonApiResourceMethodReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\JsonResourceExtension;
use Dedoc\Scramble\Support\InferExtensions\JsonResponseMethodReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\ModelExtension;
use Dedoc\Scramble\Support\InferExtensions\PaginateMethodsReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\PossibleExceptionInfer;
use Dedoc\Scramble\Support\InferExtensions\RequestExtension;
use Dedoc\Scramble\Support\InferExtensions\ResourceCollectionTypeInfer;
use Dedoc\Scramble\Support\InferExtensions\ResourceResponseMethodReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\ResponseFactoryTypeInfer;
use Dedoc\Scramble\Support\InferExtensions\ShallowFunctionDefinition;
use Dedoc\Scramble\Support\InferExtensions\StorageResponseTypeInfer;
use Dedoc\Scramble\Support\InferExtensions\TransformsToResourceCollectionExtension;
use Dedoc\Scramble\Support\InferExtensions\TranslationReturnTypeExtension;
use Dedoc\Scramble\Support\InferExtensions\TypeTraceInfer;
use Dedoc\Scramble\Support\InferExtensions\ValidatorTypeInfer;
use Dedoc\Scramble\Support\ProNudge\Extensions\LaravelDataRequestBodyNudgeExtractor;
use Dedoc\Scramble\Support\ProNudge\Extensions\LaravelDataReturnTypeNudgeExtension;
use Dedoc\Scramble\Support\ProNudge\Extensions\QueryBuilderUsageNudgeExtension;
use Dedoc\Scramble\Support\RouteProviders\RouteProvider;
use Dedoc\Scramble\Support\Type\FunctionType;
use Dedoc\Scramble\Support\Type\TemplateType;
use Dedoc\Scramble\Support\Type\VoidType;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\ArrayableToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\BinaryFileResponseToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\CarbonInterfaceToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\CollectionToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\CursorPaginatorTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\EloquentCollectionToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\EnumToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\JsonApiAnonymousCollectionTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\JsonApiPaginatedResourceResponseToSchemaExtension;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\JsonApiResourceResponseToSchemaExtension;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\JsonApiResourceTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\JsonResourceTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\LengthAwarePaginatorTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\PaginatedResourceResponseTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\PaginatorTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\PlainObjectToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\ResourceCollectionTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\ResourceResponseTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\ResponsableTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\ResponseTypeToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\StreamedResponseToSchema;
use Dedoc\Scramble\Support\TypeToSchemaExtensions\VoidTypeToSchema;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Routing\Router;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
class ScrambleServiceProvider extends PackageServiceProvider
{
/** @var array<class-string, class-string> */
public $bindings = [
RouteProviderContract::class => RouteProvider::class,
];
public $singletons = [
PrettyPrinter::class => 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,
AfterEloquentBuilderDefinitionCreatedExtension::class,
AfterEloquentRelationDefinitionCreatedExtension::class,
AfterModelDefinitionCreatedExtension::class,
AfterEloquentCollectionDefinitionCreatedExtension::class,
JsonResourceExtension::class,
ResourceResponseMethodReturnTypeExtension::class,
JsonResponseMethodReturnTypeExtension::class,
ModelExtension::class,
TransformsToResourceCollectionExtension::class,
EloquentBuilderExtension::class,
RequestExtension::class,
AfterJsonResourceDefinitionCreatedExtension::class,
AfterLengthAwarePaginatorDefinitionCreatedExtension::class,
AfterPaginatorDefinitionCreatedExtension::class,
AfterCursorPaginatorDefinitionCreatedExtension::class,
AfterResourceCollectionDefinitionCreatedExtension::class,
AfterAnonymousResourceCollectionDefinitionCreatedExtension::class,
AfterResponseDefinitionCreatedExtension::class,
]);
return array_merge(
[
new PossibleExceptionInfer,
new AbortHelpersExceptionInfer,
new PaginateMethodsReturnTypeExtension,
new FacadeStaticMethodReturnTypeExtension,
new ArrStaticMethodReturnTypeExtension,
new ValidatorTypeInfer,
new ResourceCollectionTypeInfer,
new ResponseFactoryTypeInfer,
new StorageResponseTypeInfer,
new ArrayMergeReturnTypeExtension,
new TranslationReturnTypeExtension,
/* 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,
]);
Scramble::infer()
->configure()
->buildDefinitionsUsingAstFor([
Attribute::class,
]);
if (Scramble::$defaultRoutesIgnored) {
Scramble::configure()->expose(false);
}
$this->registerProNudge();
$this->app->booted(function () {
$this->registerRoutes();
});
}
private function registerProNudge(): void
{
if (class_exists('Dedoc\ScramblePro\ScrambleProServiceProvider')) {
return;
}
Scramble::configure()
->withOperationTransformers([
LaravelDataReturnTypeNudgeExtension::class,
QueryBuilderUsageNudgeExtension::class,
])
->withParametersExtractors(function (ParametersExtractors $extractors) {
$extractors->append(LaravelDataRequestBodyNudgeExtractor::class);
});
}
private function registerRoutes(): void
{
$this->registerDevToolsAssetsRoute();
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);
$result = $generator->generate($config);
return view($config->renderer()->view, [
/*
* `spec` here is for backward compatibility in case there is a
* stale published view that expects it to exist, will be removed in 1.0
*/
'spec' => $result->spec($config->openApiVersion()),
'config' => $config,
'result' => $result,
]);
})->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]));
}
}
}
private function registerDevToolsAssetsRoute(): void
{
if (! DevTools::enabled()) {
return;
}
/** @var Router $router */
$router = $this->app->get(Router::class);
$middleware = array_values(array_filter(
(array) config('scramble.middleware', [RestrictedDocsAccess::class]),
fn ($middleware) => is_string($middleware),
));
$router->get('_scramble/dev-tools/devtools.js', function () {
$path = DevTools::assetPath();
abort_unless(is_file($path), 404);
return response()->file($path, [
'Cache-Control' => 'no-store',
'Content-Type' => 'text/javascript; charset=UTF-8',
]);
})
->name('scramble.dev-tools.asset')
->middleware($middleware);
}
}