@use-the-fork/spindle 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/build/Assets/Blueprints/Catalog.js +19 -9
  2. package/build/Assets/Blueprints/agents/sdd-implementer.agent.md +6 -6
  3. package/build/Assets/Blueprints/agents/sdd-orchestrator.agent.md +4 -7
  4. package/build/Assets/Blueprints/skills/author-agent/SKILL.md +9 -8
  5. package/build/Assets/Blueprints/skills/author-instruction/SKILL.md +9 -8
  6. package/build/Assets/Blueprints/skills/author-skill/SKILL.md +9 -8
  7. package/build/Assets/Blueprints/skills/grill/SKILL.md +3 -3
  8. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/SKILL.md +16 -12
  9. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/implementer-prompt.md +2 -2
  10. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/corrective-rounds.md +1 -1
  11. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/example-workflow.md +14 -12
  12. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/full-spec.md +43 -23
  13. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/review-inputs.md +7 -7
  14. package/build/Assets/Blueprints/skills/int-testing-agent-guidance/SKILL.md +2 -2
  15. package/build/Assets/Blueprints/skills/int-writing-skills/SKILL.md +2 -2
  16. package/build/Assets/Blueprints/skills/int-writing-skills/references/testing-skills.md +2 -2
  17. package/build/Assets/Blueprints/skills/{asd-ste100 → ref-asd-ste100}/SKILL.md +1 -1
  18. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/SKILL.md +38 -0
  19. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/references/concurrency-and-pools.md +73 -0
  20. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/references/connectors.md +99 -0
  21. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/references/data-transfer-objects.md +104 -0
  22. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/references/handling-failures.md +95 -0
  23. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/references/requests.md +111 -0
  24. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/references/responses.md +56 -0
  25. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/references/sending-requests.md +42 -0
  26. package/build/Assets/Blueprints/skills/ref-php-laravel-saloon-api-integrations/references/testing.md +119 -0
  27. package/build/Assets/Blueprints/skills/{author-design → sdd-author-design}/SKILL.md +12 -4
  28. package/build/Assets/Blueprints/skills/{grill-design → sdd-grill-design}/SKILL.md +1 -1
  29. package/build/Assets/Blueprints/skills/{implement-spec → sdd-implement-spec}/SKILL.md +6 -8
  30. package/build/Assets/Blueprints/skills/{to-spec → sdd-to-spec}/SKILL.md +3 -3
  31. package/build/Assets/Blueprints/skills/{to-tickets → sdd-to-tickets}/SKILL.md +3 -3
  32. package/build/Assets/Blueprints/skills/{setup-project → spindle-project-setup}/SKILL.md +4 -4
  33. package/build/Assets/Blueprints/skills/{update-tooling → spindle-tooling-update}/SKILL.md +5 -5
  34. package/build/Assets/Templates/project/copilot-instructions.md +3 -9
  35. package/build/Assets/Templates/project/ticket-tracker.md +14 -6
  36. package/build/BlueprintManagement/Commands/Prompts.js +1 -1
  37. package/build/SubagentDrivenDevelopment/Commands/ChecksRecordCommand.js +27 -5
  38. package/build/SubagentDrivenDevelopment/Commands/ChecksRequireCommand.js +31 -7
  39. package/build/SubagentDrivenDevelopment/Commands/CorrectCommand.js +0 -1
  40. package/build/SubagentDrivenDevelopment/Commands/FinishCommand.js +2 -1
  41. package/build/SubagentDrivenDevelopment/Commands/ImplementCommand.js +2 -0
  42. package/build/SubagentDrivenDevelopment/Commands/{ClaimCommand.js → IntegrateCommand.js} +8 -7
  43. package/build/SubagentDrivenDevelopment/Commands/ReviewCommand.js +9 -9
  44. package/build/SubagentDrivenDevelopment/Commands/StartCommand.js +23 -0
  45. package/build/SubagentDrivenDevelopment/Commands/StatusCommand.js +5 -0
  46. package/build/SubagentDrivenDevelopment/Commands/groups.js +3 -2
  47. package/build/SubagentDrivenDevelopment/Evidence/GitEvidence.js +188 -37
  48. package/build/SubagentDrivenDevelopment/Models/Workflow.js +0 -2
  49. package/build/SubagentDrivenDevelopment/Persistence/AssignmentSchema.js +4 -0
  50. package/build/SubagentDrivenDevelopment/Persistence/CheckSchema.js +4 -13
  51. package/build/SubagentDrivenDevelopment/Persistence/LedgerRepository.js +66 -5
  52. package/build/SubagentDrivenDevelopment/Persistence/ProgressSchema.js +54 -4
  53. package/build/SubagentDrivenDevelopment/Persistence/ReviewSchema.js +4 -0
  54. package/build/SubagentDrivenDevelopment/Policies/AssignmentPolicy.js +0 -3
  55. package/build/SubagentDrivenDevelopment/Policies/CheckPolicy.js +6 -3
  56. package/build/SubagentDrivenDevelopment/Policies/ReadinessPolicy.js +16 -6
  57. package/build/SubagentDrivenDevelopment/Policies/ReviewPolicy.js +16 -2
  58. package/build/SubagentDrivenDevelopment/Policies/WorkflowPolicy.js +8 -8
  59. package/build/SubagentDrivenDevelopment/Services/AssignmentService.js +73 -51
  60. package/build/SubagentDrivenDevelopment/Services/ChecksService.js +376 -21
  61. package/build/SubagentDrivenDevelopment/Services/CorrectionService.js +83 -14
  62. package/build/SubagentDrivenDevelopment/Services/DeliveryCorrectionService.js +3 -7
  63. package/build/SubagentDrivenDevelopment/Services/DeliveryService.js +43 -38
  64. package/build/SubagentDrivenDevelopment/Services/FeatureCreationService.js +1 -1
  65. package/build/SubagentDrivenDevelopment/Services/FeatureFinalizationService.js +1202 -0
  66. package/build/SubagentDrivenDevelopment/Services/FeatureInspectionService.js +41 -49
  67. package/build/SubagentDrivenDevelopment/Services/FeatureWorktreeService.js +544 -0
  68. package/build/SubagentDrivenDevelopment/Services/ReadinessService.js +2 -2
  69. package/build/SubagentDrivenDevelopment/Services/ReviewService.js +39 -3
  70. package/build/SubagentDrivenDevelopment/Services/SubmissionService.js +4 -1
  71. package/build/SubagentDrivenDevelopment/Services/TicketIntegrationService.js +621 -0
  72. package/build/SubagentDrivenDevelopment/Services/TicketService.js +3 -0
  73. package/build/SubagentDrivenDevelopment/Services/TicketWorktreeService.js +317 -0
  74. package/build/SubagentDrivenDevelopment/Services/WorkflowRecordsCommitter.js +336 -0
  75. package/build/SubagentDrivenDevelopment/SubagentDrivenDevelopmentServiceProvider.js +18 -8
  76. package/build/TemplateRendering/Computed/ComputedVariableProviders.js +2 -0
  77. package/build/TemplateRendering/Computed/Providers/AuthoringFlowchartProvider.js +26 -0
  78. package/package.json +2 -2
  79. package/build/SubagentDrivenDevelopment/Services/IntegrationService.js +0 -258
  80. /package/build/Assets/Blueprints/skills/{asd-ste100 → ref-asd-ste100}/CREDITS.MD +0 -0
  81. /package/build/Assets/Blueprints/skills/{asd-ste100 → ref-asd-ste100}/examples/before-after.md +0 -0
  82. /package/build/Assets/Blueprints/skills/{asd-ste100 → ref-asd-ste100}/references/writing-rules.md +0 -0
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: asd-ste100
2
+ name: ref-asd-ste100
3
3
  description: "Rewrites ambiguous English into ASD-STE100 style — one meaning per word, active voice, simple tense, short sentences. Use when agent output is hard to parse; triggers: simplify this, STE100 rewrite."
4
4
  user-invocable: false
5
5
  metadata:
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: ref-php-laravel-saloon-api-integrations
3
+ description: "Use when building or testing Laravel API integrations with Saloon: connectors, requests, responses, failures, mocks, pools, or DTOs."
4
+ metadata:
5
+ spindle:
6
+ version: 1.0.0
7
+ ---
8
+
9
+ # Working with Saloon
10
+
11
+ This skill covers the latest Saloon release and its Laravel plugin.
12
+
13
+ ## Integration boundaries
14
+
15
+ A connector owns the base URL, shared authentication, default headers, and client configuration.
16
+ A request owns its method, endpoint, and request-specific query, headers, and body.
17
+ The connector sends the request.
18
+
19
+ ## Topic lookup
20
+
21
+ Before writing code, read each reference whose condition matches the task.
22
+ For combined tasks, read all matching references.
23
+
24
+ | When working on | Read |
25
+ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------ |
26
+ | Base URL, shared authentication, default headers, timeouts, or client configuration | [Connectors](references/connectors.md) |
27
+ | HTTP method, endpoint, inputs, query parameters, headers, or JSON body | [Requests](references/requests.md) |
28
+ | `send()` or `sendAsync()`, including promise handling | [Sending requests](references/sending-requests.md) |
29
+ | Status, headers, body parsing, response helpers, or custom response classes | [Responses](references/responses.md) |
30
+ | HTTP or connection failures, throwing, or custom failure detection and exceptions | [Handling failures](references/handling-failures.md) |
31
+ | Tests, request assertions, mocks, or recorded fixtures | [Testing](references/testing.md) |
32
+ | Concurrent batches, rate limits, keyed results, or pool handlers | [Concurrency and pools](references/concurrency-and-pools.md) |
33
+ | Typed response mapping or DTO-driven request bodies | [Data transfer objects](references/data-transfer-objects.md) |
34
+
35
+ ## Sources
36
+
37
+ Use the [official documentation](https://docs.saloon.dev/) for API details beyond these references.
38
+ Use the [latest release](https://github.com/saloonphp/saloon/releases/latest) for current changes.
@@ -0,0 +1,73 @@
1
+ # Concurrency & Pools
2
+
3
+ A pool sends independent requests concurrently through one connector.
4
+ Use pools for batches such as fetching records by ID.
5
+ Concurrency requires `GuzzleSender` (the default) or `HttpSender`.
6
+
7
+ ## Basic pool
8
+
9
+ ```php
10
+ use Saloon\Exceptions\Request\FatalRequestException;
11
+ use Saloon\Exceptions\Request\RequestException;
12
+ use Saloon\Http\Response;
13
+
14
+ $products = [];
15
+ $failures = [];
16
+
17
+ $pool = $connector->pool(
18
+ requests: function () use ($ids): Generator {
19
+ foreach ($ids as $id) {
20
+ yield $id => new GetProductRequest($id);
21
+ }
22
+ },
23
+ concurrency: 10,
24
+ responseHandler: function (Response $response, int|string $key) use (&$products) {
25
+ $products[$key] = $response->dtoOrFail();
26
+ },
27
+ exceptionHandler: function (FatalRequestException|RequestException $exception, int|string $key) use (&$failures) {
28
+ $failures[$key] = $exception instanceof RequestException
29
+ ? $exception->getResponse()->status()
30
+ : $exception->getMessage();
31
+ },
32
+ );
33
+
34
+ $pool->send()->wait();
35
+ ```
36
+
37
+ `send()` returns a promise; call `wait()` before reading the results.
38
+
39
+ ## Requests
40
+
41
+ Provide any of the following as the first argument or with `setRequests()`:
42
+
43
+ - An array of `Request` or `PendingRequest` objects.
44
+ - A generator, to keep memory low for large batches.
45
+ - A closure or invokable class returning an array or generator, to run setup just before sending.
46
+
47
+ Keys from arrays or generators reach both handlers, so key by ID to match results. Responses complete in any order; never rely on input order.
48
+
49
+ ## Concurrency
50
+
51
+ The default is 5 requests in flight.
52
+ Use the `concurrency` argument or `$pool->setConcurrency(10)` to change it. Both also accept a callable.
53
+ Choose a value that respects the API's rate limits. A pool does not throttle requests over time.
54
+
55
+ ## Handlers
56
+
57
+ - `responseHandler` / `withResponseHandler()`: receives each **successful** `Response`.
58
+ - `exceptionHandler` / `withExceptionHandler()`: receives `RequestException` for 4xx/5xx responses and `FatalRequestException` for connection failures. HTTP failures always arrive here, even without `throw()` or `AlwaysThrowOnErrors`.
59
+
60
+ Each pool holds one handler of each kind.
61
+ For failure classification and custom exceptions, read [Handling failures](handling-failures.md).
62
+
63
+ ## Testing pools
64
+
65
+ Use `MockClient` with pools. Mocked HTTP failures reach the exception handler.
66
+ For offline setup and request assertions, read [Testing](testing.md).
67
+
68
+ ```php
69
+ $connector->withMockClient(new MockClient([
70
+ MockResponse::make(['id' => 1, 'name' => 'Widget', 'price_cents' => 999], 200),
71
+ MockResponse::make(['message' => 'Not found'], 404),
72
+ ]));
73
+ ```
@@ -0,0 +1,99 @@
1
+ # Connectors
2
+
3
+ A connector represents one external API. Create one connector per integration under `app/Http/Integrations/<Api>/`.
4
+
5
+ ## Minimal connector
6
+
7
+ Extend `Saloon\Http\Connector` and implement `resolveBaseUrl()`.
8
+
9
+ ```php
10
+ use Saloon\Http\Connector;
11
+
12
+ class InventoryConnector extends Connector
13
+ {
14
+ public function resolveBaseUrl(): string
15
+ {
16
+ return 'https://inventory.example.com/api/v1';
17
+ }
18
+ }
19
+ ```
20
+
21
+ With the Laravel plugin installed, `php artisan saloon:connector` scaffolds one.
22
+
23
+ ## Constructor arguments and authentication
24
+
25
+ Pass instance-specific values through the constructor. Put shared bearer authentication in `defaultAuth()`.
26
+
27
+ ```php
28
+ use Saloon\Contracts\Authenticator;
29
+ use Saloon\Http\Auth\TokenAuthenticator;
30
+ use Saloon\Http\Connector;
31
+
32
+ class InventoryConnector extends Connector
33
+ {
34
+ public function __construct(
35
+ protected readonly string $baseUrl,
36
+ protected readonly string $token,
37
+ ) {}
38
+
39
+ public function resolveBaseUrl(): string
40
+ {
41
+ return $this->baseUrl;
42
+ }
43
+
44
+ protected function defaultAuth(): ?Authenticator
45
+ {
46
+ return new TokenAuthenticator($this->token);
47
+ }
48
+ }
49
+
50
+ $connector = new InventoryConnector(config('services.inventory.url'), config('services.inventory.token'));
51
+ ```
52
+
53
+ `TokenAuthenticator` sends `Authorization: Bearer <token>`.
54
+ Load credentials from configuration or secrets. Never hard-code production credentials.
55
+
56
+ ## Default headers
57
+
58
+ Override `defaultHeaders()` for headers every request needs. Request headers are merged on top.
59
+
60
+ ```php
61
+ protected function defaultHeaders(): array
62
+ {
63
+ return [
64
+ 'Accept' => 'application/json',
65
+ 'Content-Type' => 'application/json',
66
+ ];
67
+ }
68
+ ```
69
+
70
+ Change headers on an instance with `$connector->headers()->add('X-Tenant', 'acme');`.
71
+
72
+ ## Timeouts
73
+
74
+ Defaults are a 10-second connection timeout and a 30-second request timeout.
75
+ Use `HasTimeout` on a connector for shared timeouts, or on a request for endpoint-specific timeouts.
76
+
77
+ ```php
78
+ use Saloon\Traits\Plugins\HasTimeout;
79
+
80
+ class InventoryConnector extends Connector
81
+ {
82
+ use HasTimeout;
83
+
84
+ protected int $connectTimeout = 60;
85
+
86
+ protected int $requestTimeout = 120;
87
+ }
88
+ ```
89
+
90
+ ## HTTP client config
91
+
92
+ The default sender is Guzzle. Pass Guzzle request options through `defaultConfig()` or `$connector->config()->add(...)`.
93
+
94
+ ```php
95
+ public function defaultConfig(): array
96
+ {
97
+ return ['verify' => true];
98
+ }
99
+ ```
@@ -0,0 +1,104 @@
1
+ # Data Transfer Objects
2
+
3
+ A data transfer object (DTO) gives typed fields to API data.
4
+ Define response mapping on the request, or on the connector when all requests share that mapping.
5
+ Retrieve the DTO from the response.
6
+
7
+ ## Define the DTO and mapping
8
+
9
+ ```php
10
+ final class Product
11
+ {
12
+ public function __construct(
13
+ public readonly int $id,
14
+ public readonly string $name,
15
+ public readonly int $priceCents,
16
+ ) {}
17
+ }
18
+ ```
19
+
20
+ ```php
21
+ use Saloon\Http\Request;
22
+ use Saloon\Http\Response;
23
+
24
+ class GetProductRequest extends Request
25
+ {
26
+ // method, constructor, and endpoint ...
27
+
28
+ public function createDtoFromResponse(Response $response): Product
29
+ {
30
+ $data = $response->json();
31
+
32
+ return new Product(
33
+ id: $data['id'],
34
+ name: $data['name'],
35
+ priceCents: $data['price_cents'],
36
+ );
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## Retrieve the DTO
42
+
43
+ ```php
44
+ $response = $connector->send(new GetProductRequest(42));
45
+
46
+ $product = $response->dtoOrFail(); // throws LogicException if the response failed
47
+ $product = $response->dto(); // always maps, even for failed responses
48
+ ```
49
+
50
+ Use `dtoOrFail()` for success-only DTOs. It prevents mapping an error body into `Product`.
51
+ Use `dto()` only when the mapping handles error payloads.
52
+ "Failed" follows `hasRequestFailed()`; see [Handling failures](handling-failures.md).
53
+
54
+ `dto()` returns `mixed`. For static analysis, annotate it with `/** @var Product $product */`, or call the typed method directly: `$request->createDtoFromResponse($response)`.
55
+
56
+ ## DTOs in requests
57
+
58
+ Accept a DTO in a request constructor to build the request body.
59
+
60
+ ```php
61
+ use Saloon\Contracts\Body\HasBody;
62
+ use Saloon\Enums\Method;
63
+ use Saloon\Traits\Body\HasJsonBody;
64
+
65
+ class UpdateProductRequest extends Request implements HasBody
66
+ {
67
+ use HasJsonBody;
68
+
69
+ protected Method $method = Method::PUT;
70
+
71
+ public function __construct(protected readonly Product $product) {}
72
+
73
+ public function resolveEndpoint(): string
74
+ {
75
+ return '/products/' . $this->product->id;
76
+ }
77
+
78
+ protected function defaultBody(): array
79
+ {
80
+ return [
81
+ 'name' => $this->product->name,
82
+ 'price_cents' => $this->product->priceCents,
83
+ ];
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## Keep the source response
89
+
90
+ Implement `WithResponse` and use `HasResponse` to attach the original response to the DTO.
91
+
92
+ ```php
93
+ use Saloon\Contracts\DataObjects\WithResponse;
94
+ use Saloon\Traits\Responses\HasResponse;
95
+
96
+ final class Product implements WithResponse
97
+ {
98
+ use HasResponse;
99
+
100
+ // constructor ...
101
+ }
102
+
103
+ $product->getResponse()->status();
104
+ ```
@@ -0,0 +1,95 @@
1
+ # Handling Failures
2
+
3
+ Saloon separates two kinds of failure:
4
+
5
+ - **HTTP failure**: the API returned 4xx/5xx. Synchronous `send()` returns a `Response` with `failed()` set to `true`, unless throwing is enabled.
6
+ - **Connection failure**: the API returned no response, such as a DNS error or connection timeout. Saloon throws `FatalRequestException`.
7
+
8
+ ```php
9
+ $response = $connector->send(new CreateProductRequest('Widget', 999));
10
+
11
+ if ($response->failed()) {
12
+ $errors = $response->json('errors'); // e.g. a 422 validation payload
13
+ }
14
+ ```
15
+
16
+ ## Throwing on HTTP failures
17
+
18
+ Call `throw()` to throw on a failed response. It returns the response on success.
19
+
20
+ ```php
21
+ $product = $connector->send(new GetProductRequest(42))->throw()->json();
22
+ ```
23
+
24
+ For every request on a connector, add `AlwaysThrowOnErrors`.
25
+
26
+ ```php
27
+ use Saloon\Traits\Plugins\AlwaysThrowOnErrors;
28
+
29
+ class InventoryConnector extends Connector
30
+ {
31
+ use AlwaysThrowOnErrors;
32
+ }
33
+ ```
34
+
35
+ ## Exception hierarchy
36
+
37
+ Catch the most specific type the application needs. `RequestException` exposes the response through `getResponse()`.
38
+
39
+ ```
40
+ SaloonException
41
+ ├── FatalRequestException connection errors, no response
42
+ └── RequestException HTTP 4xx/5xx
43
+ ├── ClientException 4xx
44
+ │ ├── UnauthorizedException (401), PaymentRequiredException (402)
45
+ │ ├── ForbiddenException (403), NotFoundException (404)
46
+ │ ├── MethodNotAllowedException (405), RequestTimeOutException (408)
47
+ │ └── UnprocessableEntityException (422), TooManyRequestsException (429)
48
+ └── ServerException 5xx
49
+ └── InternalServerErrorException (500), ServiceUnavailableException (503), GatewayTimeoutException (504)
50
+ ```
51
+
52
+ ```php
53
+ use Saloon\Exceptions\Request\Statuses\UnprocessableEntityException;
54
+
55
+ try {
56
+ $connector->send(new CreateProductRequest('', 0))->throw();
57
+ } catch (UnprocessableEntityException $e) {
58
+ $errors = $e->getResponse()->json('errors');
59
+ }
60
+ ```
61
+
62
+ ## Async and pools
63
+
64
+ Promises from `sendAsync()` and pools reject on HTTP and connection failures.
65
+ Handle both in `otherwise` or the pool's exception handler. Calling `throw()` is unnecessary.
66
+ For callback examples, read [Sending requests](sending-requests.md) or [Concurrency and pools](concurrency-and-pools.md).
67
+
68
+ ## Custom failure detection
69
+
70
+ Some APIs return 200 with an error in the body. Override `hasRequestFailed()` on the connector or request; returning `null` falls back to the status-code check.
71
+
72
+ ```php
73
+ use Saloon\Http\Response;
74
+
75
+ public function hasRequestFailed(Response $response): ?bool
76
+ {
77
+ return $response->json('status') === 'error';
78
+ }
79
+ ```
80
+
81
+ This changes `failed()`, `throw()`, `dtoOrFail()`, and `AlwaysThrowOnErrors`.
82
+
83
+ ## Custom exception
84
+
85
+ Override `getRequestException()` on the connector or request to throw an application exception. A request's override wins over the connector's.
86
+
87
+ ```php
88
+ use Saloon\Http\Response;
89
+ use Throwable;
90
+
91
+ public function getRequestException(Response $response, ?Throwable $senderException): ?Throwable
92
+ {
93
+ return new InventoryApiException($response->json('message', 'Inventory API error'), $response->status(), $senderException);
94
+ }
95
+ ```
@@ -0,0 +1,111 @@
1
+ # Requests
2
+
3
+ Create a request class for each endpoint operation.
4
+ Saloon appends the endpoint to the connector's base URL.
5
+ Keep request classes beside their connector.
6
+
7
+ ## Minimal request
8
+
9
+ Extend `Saloon\Http\Request`, set `$method`, and implement `resolveEndpoint()`.
10
+
11
+ ```php
12
+ use Saloon\Enums\Method;
13
+ use Saloon\Http\Request;
14
+
15
+ class ListProductsRequest extends Request
16
+ {
17
+ protected Method $method = Method::GET;
18
+
19
+ public function resolveEndpoint(): string
20
+ {
21
+ return '/products';
22
+ }
23
+ }
24
+ ```
25
+
26
+ With the Laravel plugin installed, `php artisan saloon:request` scaffolds one.
27
+
28
+ ## Constructor arguments
29
+
30
+ Pass identifiers and inputs through the constructor.
31
+
32
+ ```php
33
+ class GetProductRequest extends Request
34
+ {
35
+ protected Method $method = Method::GET;
36
+
37
+ public function __construct(protected readonly int $id) {}
38
+
39
+ public function resolveEndpoint(): string
40
+ {
41
+ return '/products/' . $this->id;
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Query parameters
47
+
48
+ Override `defaultQuery()`, or add to an instance with `$request->query()->add('page', 2)`.
49
+
50
+ ```php
51
+ protected function defaultQuery(): array
52
+ {
53
+ return [
54
+ 'sort' => 'name',
55
+ 'filter[active]' => 'true',
56
+ ];
57
+ }
58
+ ```
59
+
60
+ Saloon builds query strings with `http_build_query`.
61
+
62
+ ## Headers
63
+
64
+ Override `defaultHeaders()`; these merge with the connector's headers. Instance changes use `$request->headers()->add(...)`.
65
+
66
+ ```php
67
+ protected function defaultHeaders(): array
68
+ {
69
+ return ['Accept' => 'application/json'];
70
+ }
71
+ ```
72
+
73
+ ## JSON body
74
+
75
+ Implement `HasBody`, use `HasJsonBody`, and override `defaultBody()`.
76
+
77
+ ```php
78
+ use Saloon\Contracts\Body\HasBody;
79
+ use Saloon\Enums\Method;
80
+ use Saloon\Http\Request;
81
+ use Saloon\Traits\Body\HasJsonBody;
82
+
83
+ class CreateProductRequest extends Request implements HasBody
84
+ {
85
+ use HasJsonBody;
86
+
87
+ protected Method $method = Method::POST;
88
+
89
+ public function __construct(
90
+ protected readonly string $name,
91
+ protected readonly int $priceCents,
92
+ ) {}
93
+
94
+ public function resolveEndpoint(): string
95
+ {
96
+ return '/products';
97
+ }
98
+
99
+ protected function defaultBody(): array
100
+ {
101
+ return [
102
+ 'name' => $this->name,
103
+ 'price_cents' => $this->priceCents,
104
+ ];
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Timeouts
110
+
111
+ For endpoint-specific timeouts, use the trait and properties shown in [Timeouts](connectors.md#timeouts) on the request.
@@ -0,0 +1,56 @@
1
+ # Responses
2
+
3
+ Synchronous `send()` returns a `Saloon\Http\Response`.
4
+ For HTTP and connection failure behavior, read [Handling failures](handling-failures.md).
5
+
6
+ ## Reading a response
7
+
8
+ ```php
9
+ $response = $connector->send(new GetProductRequest(42));
10
+
11
+ $response->status(); // 200
12
+ $response->header('Content-Type');
13
+ $response->body(); // raw string
14
+ $response->json(); // decoded array
15
+ $response->json('data.name'); // nested key (dot notation)
16
+ $response->json('missing', 'x'); // default when absent
17
+ $response->object(); // stdClass
18
+ $response->collect('data'); // Laravel Collection (requires illuminate/collections)
19
+ ```
20
+
21
+ ## Useful methods
22
+
23
+ | Method | Purpose |
24
+ | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
25
+ | `status()`, `headers()`, `header($name)` | Status code and headers |
26
+ | `body()`, `json()` / `array()`, `object()`, `collect()` | Body as string, array, object, or Collection |
27
+ | `stream()`, `saveBodyToFile($path)` | Large or binary bodies |
28
+ | `xmlReader()`, `dom()` | XML (needs `saloonphp/xml-wrangler`) or HTML (needs `symfony/dom-crawler`) |
29
+ | `ok()`, `successful()`, `redirect()`, `failed()`, `clientError()`, `serverError()` | Status checks; `failed()` is customizable |
30
+ | `throw()` | Throw if the response is considered failed |
31
+ | `dto()`, `dtoOrFail()` | Convert to a DTO; see [Data Transfer Objects](data-transfer-objects.md) |
32
+ | `getPendingRequest()`, `getPsrRequest()`, `getPsrResponse()` | The built request and the raw PSR-7 messages |
33
+
34
+ ## Custom response class
35
+
36
+ Extend `Response` to add API-specific helpers.
37
+ Register the class on the connector for all requests, or on a request for that request only.
38
+
39
+ ```php
40
+ use Saloon\Http\Response;
41
+
42
+ class InventoryResponse extends Response
43
+ {
44
+ public function nextPage(): ?int
45
+ {
46
+ return $this->json('meta.next_page');
47
+ }
48
+ }
49
+
50
+ class InventoryConnector extends Connector
51
+ {
52
+ protected ?string $response = InventoryResponse::class;
53
+ }
54
+ ```
55
+
56
+ Override `resolveResponseClass(): string` instead of the property when the class must be chosen dynamically.
@@ -0,0 +1,42 @@
1
+ # Sending Requests
2
+
3
+ Pass a request to its connector to send it.
4
+
5
+ ## Synchronous
6
+
7
+ ```php
8
+ $connector = new InventoryConnector($baseUrl, $token);
9
+
10
+ $response = $connector->send(new ListProductsRequest);
11
+
12
+ $products = $response->json('data');
13
+ ```
14
+
15
+ `send()` returns a `Saloon\Http\Response`. A 4xx/5xx status does **not** throw by default; see [Handling Failures](handling-failures.md).
16
+
17
+ ## Asynchronous
18
+
19
+ `sendAsync()` returns a Guzzle `PromiseInterface`.
20
+ Successful responses reach `then`. HTTP and connection failures reject the promise and reach `otherwise`.
21
+
22
+ ```php
23
+ use Saloon\Exceptions\Request\FatalRequestException;
24
+ use Saloon\Exceptions\Request\RequestException;
25
+ use Saloon\Http\Response;
26
+
27
+ $promise = $connector->sendAsync(new ListProductsRequest);
28
+
29
+ $promise
30
+ ->then(function (Response $response) {
31
+ // handle success
32
+ })
33
+ ->otherwise(function (FatalRequestException|RequestException $exception) {
34
+ // RequestException: the API returned 4xx/5xx; use $exception->getResponse()
35
+ // FatalRequestException: no response, e.g. connection failure
36
+ });
37
+
38
+ $promise->wait(); // resolve the promise
39
+ ```
40
+
41
+ For concurrent batches, read [Concurrency and pools](concurrency-and-pools.md).
42
+ For failure classification and custom exceptions, read [Handling failures](handling-failures.md).