Laravel AT Protocol Client (alpha & unstable)

Add ScopedEndpoint and PublicEndpoint attributes

Changed files
+110
src
+43
src/Attributes/PublicEndpoint.php
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Attributes; 4 + 5 + use Attribute; 6 + 7 + /** 8 + * Documents that a method is a public endpoint that does not require authentication. 9 + * 10 + * This attribute currently serves as documentation to indicate which AT Protocol 11 + * endpoints can be called without an authenticated session. It helps developers 12 + * understand which endpoints work with `Atp::public()` against public API endpoints 13 + * like `https://public.api.bsky.app`. 14 + * 15 + * While this attribute does not currently perform runtime enforcement, scope 16 + * validation will be implemented in a future release. Correctly attributing 17 + * endpoints now ensures forward compatibility when enforcement is enabled. 18 + * 19 + * Public endpoints typically include operations like: 20 + * - Reading public profiles and posts 21 + * - Searching actors and content 22 + * - Resolving handles to DIDs 23 + * - Accessing repository data (sync endpoints) 24 + * - Describing servers and feed generators 25 + * 26 + * @example Basic usage 27 + * ```php 28 + * #[PublicEndpoint] 29 + * public function getProfile(string $actor): ProfileViewDetailed 30 + * ``` 31 + * 32 + * @see \SocialDept\AtpClient\Attributes\ScopedEndpoint For endpoints that require authentication 33 + */ 34 + #[Attribute(Attribute::TARGET_METHOD)] 35 + class PublicEndpoint 36 + { 37 + /** 38 + * @param string $description Human-readable description of the endpoint 39 + */ 40 + public function __construct( 41 + public readonly string $description = '', 42 + ) {} 43 + }
+67
src/Attributes/ScopedEndpoint.php
··· 1 + <?php 2 + 3 + namespace SocialDept\AtpClient\Attributes; 4 + 5 + use Attribute; 6 + use SocialDept\AtpClient\Enums\Scope; 7 + 8 + /** 9 + * Documents that a method requires authentication with specific OAuth scopes. 10 + * 11 + * This attribute currently serves as documentation to indicate which AT Protocol 12 + * endpoints require authentication and what scopes they need. It helps developers 13 + * understand scope requirements when building applications. 14 + * 15 + * While this attribute does not currently perform runtime enforcement, scope 16 + * validation will be implemented in a future release. Correctly attributing 17 + * endpoints now ensures forward compatibility when enforcement is enabled. 18 + * 19 + * The AT Protocol currently uses "transition scopes" (like `transition:generic`) while 20 + * moving toward more granular scopes. The `granular` parameter allows documenting the 21 + * future granular scope that will replace the transition scope. 22 + * 23 + * @example Basic usage with a transition scope 24 + * ```php 25 + * #[ScopedEndpoint(Scope::TransitionGeneric)] 26 + * public function getTimeline(): GetTimelineResponse 27 + * ``` 28 + * 29 + * @example With future granular scope documented 30 + * ```php 31 + * #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getTimeline')] 32 + * public function getTimeline(): GetTimelineResponse 33 + * ``` 34 + * 35 + * @see \SocialDept\AtpClient\Attributes\PublicEndpoint For endpoints that don't require authentication 36 + * @see \SocialDept\AtpClient\Enums\Scope For available scope values 37 + */ 38 + #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] 39 + class ScopedEndpoint 40 + { 41 + public array $scopes; 42 + 43 + /** 44 + * @param string|Scope|array<string|Scope> $scopes Required scope(s) for this method 45 + * @param string|null $granular Future granular scope equivalent 46 + * @param string $description Human-readable description of scope requirement 47 + */ 48 + public function __construct( 49 + string|Scope|array $scopes, 50 + public readonly ?string $granular = null, 51 + public readonly string $description = '', 52 + ) { 53 + $this->scopes = $this->normalizeScopes($scopes); 54 + } 55 + 56 + protected function normalizeScopes(string|Scope|array $scopes): array 57 + { 58 + if (! is_array($scopes)) { 59 + $scopes = [$scopes]; 60 + } 61 + 62 + return array_map( 63 + fn ($scope) => $scope instanceof Scope ? $scope->value : $scope, 64 + $scopes 65 + ); 66 + } 67 + }