@apollo-deploy/tesseract 0.9.1 → 1.0.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 (110) hide show
  1. package/dist/adapters/csharp/index.d.ts +12 -0
  2. package/dist/adapters/csharp/index.d.ts.map +1 -0
  3. package/dist/adapters/csharp/index.js +99 -0
  4. package/dist/adapters/csharp/index.js.map +1 -0
  5. package/dist/adapters/go/index.d.ts +12 -0
  6. package/dist/adapters/go/index.d.ts.map +1 -0
  7. package/dist/adapters/go/index.js +135 -0
  8. package/dist/adapters/go/index.js.map +1 -0
  9. package/dist/adapters/kotlin/index.d.ts +13 -0
  10. package/dist/adapters/kotlin/index.d.ts.map +1 -0
  11. package/dist/adapters/kotlin/index.js +186 -0
  12. package/dist/adapters/kotlin/index.js.map +1 -0
  13. package/dist/adapters/php/index.d.ts +20 -0
  14. package/dist/adapters/php/index.d.ts.map +1 -0
  15. package/dist/adapters/php/index.js +123 -0
  16. package/dist/adapters/php/index.js.map +1 -0
  17. package/dist/adapters/python/index.d.ts +12 -0
  18. package/dist/adapters/python/index.d.ts.map +1 -0
  19. package/dist/adapters/python/index.js +145 -0
  20. package/dist/adapters/python/index.js.map +1 -0
  21. package/dist/adapters/ruby/index.d.ts +12 -0
  22. package/dist/adapters/ruby/index.d.ts.map +1 -0
  23. package/dist/adapters/ruby/index.js +86 -0
  24. package/dist/adapters/ruby/index.js.map +1 -0
  25. package/dist/adapters/rust/index.d.ts +13 -0
  26. package/dist/adapters/rust/index.d.ts.map +1 -0
  27. package/dist/adapters/rust/index.js +213 -0
  28. package/dist/adapters/rust/index.js.map +1 -0
  29. package/dist/adapters/typescript/emitter/emitSchema.d.ts +1 -1
  30. package/dist/adapters/typescript/emitter/emitSchema.d.ts.map +1 -1
  31. package/dist/adapters/typescript/emitter/emitSchema.js +1 -1
  32. package/dist/adapters/typescript/emitter/emitSchema.js.map +1 -1
  33. package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts +2 -2
  34. package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts.map +1 -1
  35. package/dist/adapters/typescript/emitter/emitTypeScriptModels.js +2 -2
  36. package/dist/adapters/typescript/emitter/emitTypeScriptModels.js.map +1 -1
  37. package/dist/adapters/typescript/emitter/importCollector.d.ts +1 -1
  38. package/dist/adapters/typescript/emitter/importCollector.d.ts.map +1 -1
  39. package/dist/cli.js +2 -1
  40. package/dist/cli.js.map +1 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +14 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/pipeline/intake.js +23 -16
  45. package/dist/pipeline/intake.js.map +1 -1
  46. package/dist/types/ir.d.ts +1 -1
  47. package/dist/types/ir.d.ts.map +1 -1
  48. package/dist/types/sdk-module.d.ts +22 -2
  49. package/dist/types/sdk-module.d.ts.map +1 -1
  50. package/dist/types/sdk-module.js +1 -1
  51. package/package.json +1 -1
  52. package/templates/csharp/client.hbs +95 -0
  53. package/templates/csharp/csproj.hbs +18 -0
  54. package/templates/csharp/domain.hbs +183 -0
  55. package/templates/csharp/errors.hbs +45 -0
  56. package/templates/csharp/readme.hbs +76 -0
  57. package/templates/csharp/transport.hbs +291 -0
  58. package/templates/csharp/types.hbs +104 -0
  59. package/templates/go/client.hbs +158 -0
  60. package/templates/go/domain.hbs +89 -0
  61. package/templates/go/errors.hbs +38 -0
  62. package/templates/go/go-mod.hbs +6 -0
  63. package/templates/go/readme.hbs +73 -0
  64. package/templates/go/transport.hbs +379 -0
  65. package/templates/go/types.hbs +80 -0
  66. package/templates/go/utils.hbs +45 -0
  67. package/templates/kotlin/build-gradle.hbs +41 -0
  68. package/templates/kotlin/client.hbs +57 -0
  69. package/templates/kotlin/domain.hbs +73 -0
  70. package/templates/kotlin/errors.hbs +42 -0
  71. package/templates/kotlin/gradle-properties.hbs +2 -0
  72. package/templates/kotlin/readme.hbs +105 -0
  73. package/templates/kotlin/settings-gradle.hbs +1 -0
  74. package/templates/kotlin/transport.hbs +246 -0
  75. package/templates/kotlin/types.hbs +100 -0
  76. package/templates/php/client.hbs +74 -0
  77. package/templates/php/composer.hbs +19 -0
  78. package/templates/php/domain.hbs +173 -0
  79. package/templates/php/errors.hbs +131 -0
  80. package/templates/php/readme.hbs +89 -0
  81. package/templates/php/transport.hbs +285 -0
  82. package/templates/php/types.hbs +95 -0
  83. package/templates/python/client.hbs +102 -0
  84. package/templates/python/domain.hbs +139 -0
  85. package/templates/python/index.hbs +20 -0
  86. package/templates/python/pyproject.hbs +26 -0
  87. package/templates/python/readme.hbs +64 -0
  88. package/templates/python/resources-init.hbs +11 -0
  89. package/templates/python/transport.hbs +191 -0
  90. package/templates/python/types-errors.hbs +75 -0
  91. package/templates/python/types.hbs +79 -0
  92. package/templates/ruby/Gemfile.hbs +5 -0
  93. package/templates/ruby/client.hbs +86 -0
  94. package/templates/ruby/domain.hbs +110 -0
  95. package/templates/ruby/errors.hbs +150 -0
  96. package/templates/ruby/gemspec.hbs +30 -0
  97. package/templates/ruby/index.hbs +54 -0
  98. package/templates/ruby/readme.hbs +113 -0
  99. package/templates/ruby/transport.hbs +177 -0
  100. package/templates/ruby/types.hbs +126 -0
  101. package/templates/ruby/version.hbs +5 -0
  102. package/templates/rust/api-mod.hbs +3 -0
  103. package/templates/rust/cargo.hbs +22 -0
  104. package/templates/rust/client.hbs +114 -0
  105. package/templates/rust/domain.hbs +94 -0
  106. package/templates/rust/error.hbs +74 -0
  107. package/templates/rust/lib.hbs +85 -0
  108. package/templates/rust/readme.hbs +77 -0
  109. package/templates/rust/transport.hbs +287 -0
  110. package/templates/rust/types.hbs +142 -0
@@ -0,0 +1,42 @@
1
+ package {{packageName}}
2
+
3
+ /**
4
+ * Primary exception type for the {{packageName}} SDK.
5
+ *
6
+ * All API methods throw [SdkException] on failure.
7
+ */
8
+ class SdkException(
9
+ /** HTTP status code, if this is an API error. */
10
+ val status: Int?,
11
+ /** Machine-readable error code (if available). */
12
+ val code: String?,
13
+ /** Human-readable error message. */
14
+ override val message: String,
15
+ /** Server-assigned request ID for support. */
16
+ val requestId: String?,
17
+ /** The underlying cause, if any. */
18
+ cause: Throwable? = null,
19
+ ) : Exception(message, cause) {
20
+
21
+ /** Returns `true` if this error was caused by the server (5xx). */
22
+ fun isServerError(): Boolean = status != null && status in 500..599
23
+
24
+ /** Returns `true` if this error was caused by the client (4xx). */
25
+ fun isClientError(): Boolean = status != null && status in 400..499
26
+
27
+ /** Returns `true` if this is a network error (should be retried safely). */
28
+ fun isNetworkError(): Boolean = status == null
29
+
30
+ /** Returns `true` if this error is retryable. */
31
+ fun isRetryable(): Boolean = isNetworkError() || status in setOf(429, 502, 503, 504)
32
+
33
+ override fun toString(): String {
34
+ val sb = StringBuilder("SdkException(")
35
+ if (status != null) sb.append("status=$status, ")
36
+ if (code != null) sb.append("code='$code', ")
37
+ sb.append("message='$message'")
38
+ if (requestId != null) sb.append(", requestId='$requestId'")
39
+ sb.append(")")
40
+ return sb.toString()
41
+ }
42
+ }
@@ -0,0 +1,2 @@
1
+ kotlin.code.style=official
2
+ org.gradle.jvmargs=-Xmx1g
@@ -0,0 +1,105 @@
1
+ # {{title}}
2
+
3
+ {{#if description}}{{description}}{{/if}}
4
+
5
+ {{#unless description}}An auto-generated Kotlin SDK for the {{title}} API.{{/unless}}
6
+
7
+ ## Installation
8
+
9
+ ### Gradle
10
+
11
+ Add the dependency to your `build.gradle.kts`:
12
+
13
+ ```kotlin
14
+ repositories {
15
+ mavenCentral()
16
+ // If publishing to a private repository, add it here.
17
+ }
18
+
19
+ dependencies {
20
+ implementation("{{groupId}}:{{artifactId}}:{{#if packageVersion}}{{packageVersion}}{{else}}0.1.0{{/if}}")
21
+ }
22
+ ```
23
+
24
+ ### Maven
25
+
26
+ ```xml
27
+ <dependency>
28
+ <groupId>{{groupId}}</groupId>
29
+ <artifactId>{{artifactId}}</artifactId>
30
+ <version>{{#if packageVersion}}{{packageVersion}}{{else}}0.1.0{{/if}}</version>
31
+ </dependency>
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```kotlin
37
+ import {{packageName}}.Client
38
+ import {{packageName}}.ClientConfig
39
+ import kotlinx.coroutines.runBlocking
40
+
41
+ fun main() = runBlocking {
42
+ val config = ClientConfig(
43
+ {{#each securitySchemes}}
44
+ {{#if (eq type "apiKey")}}
45
+ {{configKey}} = "your-api-key",
46
+ {{/if}}
47
+ {{#if (and (eq type "http") (eq scheme "bearer"))}}
48
+ {{configKey}} = "your-bearer-token",
49
+ {{/if}}
50
+ {{/each}}
51
+ )
52
+
53
+ val client = Client(config)
54
+
55
+ {{#each groups}}
56
+ {{#if @first}}
57
+ // val result = client.{{name}}.someOperation()
58
+ {{/if}}
59
+ {{/each}}
60
+ }
61
+ ```
62
+
63
+ ## API Reference
64
+
65
+ {{#each groups}}
66
+ ### {{interfaceName}}
67
+
68
+ {{#if description}}
69
+ {{description}}
70
+ {{/if}}
71
+
72
+ | Method | HTTP | Description |
73
+ |--------|------|-------------|
74
+ {{#each operations}}
75
+ | `{{name}}()` | `{{httpMethod}}` | {{#if summary}}{{summary}}{{else}}—{{/if}} |
76
+ {{/each}}
77
+
78
+ {{/each}}
79
+
80
+ ## Error Handling
81
+
82
+ All API methods throw `SdkException` on failure. The exception contains:
83
+
84
+ - `status`: HTTP status code (for API errors)
85
+ - `code`: Machine-readable error code (if available)
86
+ - `message`: Human-readable error message
87
+ - `requestId`: Server-assigned request ID for support
88
+
89
+ ```kotlin
90
+ try {
91
+ val result = client.users.getUser("user-123")
92
+ println("Success: $result")
93
+ } catch (e: SdkException) {
94
+ when {
95
+ e.isClientError() -> println("Bad request: ${e.message}")
96
+ e.isServerError() -> println("Server error (${e.status}): ${e.message}")
97
+ e.isNetworkError() -> println("Network error: ${e.message}")
98
+ else -> println("Error: ${e.message}")
99
+ }
100
+ }
101
+ ```
102
+
103
+ ## License
104
+
105
+ MIT
@@ -0,0 +1 @@
1
+ rootProject.name = "{{artifactId}}"
@@ -0,0 +1,246 @@
1
+ package {{packageName}}
2
+
3
+ import io.ktor.client.*
4
+ import io.ktor.client.engine.cio.*
5
+ import io.ktor.client.plugins.*
6
+ import io.ktor.client.plugins.contentnegotiation.*
7
+ import io.ktor.client.request.*
8
+ import io.ktor.client.statement.*
9
+ import io.ktor.http.*
10
+ import io.ktor.serialization.kotlinx.json.*
11
+ import kotlinx.serialization.json.Json
12
+ import kotlinx.serialization.json.JsonObject
13
+ import kotlinx.serialization.json.jsonPrimitive
14
+ import kotlinx.coroutines.*
15
+
16
+ /**
17
+ * Internal transport configuration.
18
+ */
19
+ data class TransportConfig(
20
+ val baseUrl: String,
21
+ {{#each securitySchemes}}
22
+ val {{configKey}}: String? = null,
23
+ {{/each}}
24
+ val timeoutMs: Long = 15000,
25
+ val maxRetries: Int = 3,
26
+ )
27
+
28
+ /**
29
+ * HTTP transport layer for the SDK.
30
+ *
31
+ * Handles request building, authentication, retries, and error mapping
32
+ * using Ktor's HttpClient.
33
+ */
34
+ class Transport(
35
+ private val config: TransportConfig
36
+ ) {
37
+ private val json = Json {
38
+ ignoreUnknownKeys = true
39
+ isLenient = true
40
+ encodeDefaults = true
41
+ coerceInputValues = true
42
+ }
43
+
44
+ private val client = HttpClient(CIO) {
45
+ install(ContentNegotiation) {
46
+ json(json)
47
+ }
48
+
49
+ install(HttpTimeout) {
50
+ requestTimeoutMillis = config.timeoutMs
51
+ connectTimeoutMillis = config.timeoutMs
52
+ socketTimeoutMillis = config.timeoutMs
53
+ }
54
+
55
+ defaultRequest {
56
+ url(config.baseUrl)
57
+ contentType(ContentType.Application.Json)
58
+ accept(ContentType.Application.Json)
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Execute an HTTP request, deserializing the response body as JSON.
64
+ */
65
+ suspend inline fun <reified T> execute(
66
+ method: HttpMethod,
67
+ path: String,
68
+ query: Map<String, String?>? = null,
69
+ body: Any? = null,
70
+ headers: Map<String, String>? = null,
71
+ ): T {
72
+ val response = executeRaw(method, path, query, body, headers)
73
+ return json.decodeFromString<T>(response)
74
+ }
75
+
76
+ /**
77
+ * Execute an HTTP request, returning the raw response body as a string.
78
+ */
79
+ suspend fun executeRaw(
80
+ method: HttpMethod,
81
+ path: String,
82
+ query: Map<String, String?>? = null,
83
+ body: Any? = null,
84
+ headers: Map<String, String>? = null,
85
+ ): String {
86
+ return executeWithRetry(method, path, query, body, headers)
87
+ }
88
+
89
+ /**
90
+ * Close the underlying HTTP client. Call this when the SDK is no longer needed.
91
+ */
92
+ fun close() {
93
+ client.close()
94
+ }
95
+
96
+ // ── private helpers ─────────────────────────────────────────────────────
97
+
98
+ @OptIn(ExperimentalCoroutinesApi::class)
99
+ private suspend fun executeWithRetry(
100
+ method: HttpMethod,
101
+ path: String,
102
+ query: Map<String, String?>? = null,
103
+ body: Any? = null,
104
+ headers: Map<String, String>? = null,
105
+ attempt: Int = 0,
106
+ ): String {
107
+ return try {
108
+ val response = client.request {
109
+ this.method = method
110
+ url {
111
+ // Base URL is set as default; append the path
112
+ val fullUrl = "${config.baseUrl.trimEnd('/')}${path}"
113
+ takeFrom(fullUrl)
114
+
115
+ // Add query parameters
116
+ query?.forEach { (key, value) ->
117
+ if (value != null) {
118
+ parameters.append(key, value)
119
+ }
120
+ }
121
+ }
122
+
123
+ // Set request body
124
+ if (body != null) {
125
+ setBody(body)
126
+ }
127
+
128
+ // Set custom headers
129
+ headers?.forEach { (key, value) ->
130
+ this.headers.append(key, value)
131
+ }
132
+
133
+ // Apply authentication
134
+ applyAuth()
135
+ }
136
+
137
+ val status = response.status.value
138
+
139
+ if (status in 200..299) {
140
+ response.bodyAsText()
141
+ } else if ((status >= 500 || status == 429) && attempt < config.maxRetries) {
142
+ // Retry on server errors and rate limiting
143
+ val backoffMs = 200L * (1L shl attempt)
144
+ delay(backoffMs)
145
+ executeWithRetry(method, path, query, body, headers, attempt + 1)
146
+ } else {
147
+ throw buildSdkException(response)
148
+ }
149
+ } catch (e: SdkException) {
150
+ throw e
151
+ } catch (e: CancellationException) {
152
+ throw e
153
+ } catch (e: Exception) {
154
+ if (attempt < config.maxRetries) {
155
+ val backoffMs = 200L * (1L shl attempt)
156
+ delay(backoffMs)
157
+ executeWithRetry(method, path, query, body, headers, attempt + 1)
158
+ } else {
159
+ throw SdkException(
160
+ status = null,
161
+ code = null,
162
+ message = "Network error: ${e.message}",
163
+ requestId = null,
164
+ cause = e,
165
+ )
166
+ }
167
+ }
168
+ }
169
+
170
+ private suspend fun buildSdkException(response: HttpResponse): SdkException {
171
+ val status = response.status.value
172
+ val body = try {
173
+ response.bodyAsText()
174
+ } catch (e: Exception) {
175
+ null
176
+ }
177
+
178
+ val requestId = response.headers["x-request-id"]
179
+ ?: response.headers["request-id"]
180
+
181
+ // Try to extract structured error info from the JSON body
182
+ var code: String? = null
183
+ var message: String = body ?: "API error (status $status)"
184
+
185
+ if (body != null) {
186
+ try {
187
+ val jsonBody = json.decodeFromString<JsonObject>(body)
188
+ code = jsonBody["code"]?.jsonPrimitive?.content
189
+ ?: jsonBody["error"]?.let { err ->
190
+ when (err) {
191
+ is JsonObject -> err["code"]?.jsonPrimitive?.content
192
+ else -> err.jsonPrimitive?.content
193
+ }
194
+ }
195
+ message = jsonBody["message"]?.jsonPrimitive?.content
196
+ ?: jsonBody["error"]?.let { err ->
197
+ when (err) {
198
+ is JsonObject -> err["message"]?.jsonPrimitive?.content
199
+ else -> err.jsonPrimitive?.content
200
+ }
201
+ }
202
+ ?: message
203
+ } catch (_: Exception) {
204
+ // Use raw body as the message
205
+ message = body
206
+ }
207
+ }
208
+
209
+ return SdkException(
210
+ status = status,
211
+ code = code,
212
+ message = message,
213
+ requestId = requestId,
214
+ )
215
+ }
216
+
217
+ private fun HttpRequestBuilder.applyAuth() {
218
+ {{#each securitySchemes}}
219
+ {{#if (eq type "apiKey")}}
220
+ {{#if (eq in "header")}}
221
+ config.{{configKey}}?.let { key ->
222
+ headers.append("{{paramName}}", key)
223
+ }
224
+ {{/if}}
225
+ {{#if (eq in "query")}}
226
+ // API key via query parameter handled during URL construction
227
+ {{/if}}
228
+ {{/if}}
229
+ {{#if (and (eq type "http") (eq scheme "bearer"))}}
230
+ config.{{configKey}}?.let { token ->
231
+ headers.append("Authorization", "Bearer $token")
232
+ }
233
+ {{/if}}
234
+ {{#if (eq type "oauth2")}}
235
+ config.{{configKey}}?.let { token ->
236
+ headers.append("Authorization", "Bearer $token")
237
+ }
238
+ {{/if}}
239
+ {{#if (eq type "openIdConnect")}}
240
+ config.{{configKey}}?.let { token ->
241
+ headers.append("Authorization", "Bearer $token")
242
+ }
243
+ {{/if}}
244
+ {{/each}}
245
+ }
246
+ }
@@ -0,0 +1,100 @@
1
+ package {{packageName}}
2
+
3
+ import kotlinx.serialization.*
4
+ import kotlinx.serialization.descriptors.*
5
+ import kotlinx.serialization.encoding.*
6
+ import kotlinx.serialization.json.*
7
+
8
+ {{#each schemas}}
9
+ {{#if (not ownership)}}
10
+ {{#unless (or externalImport (eq ownership.kind "external"))}}
11
+ {{#if (not isEnum)}}
12
+ {{#if (not isUnionType)}}
13
+ {{#if (not isIntersectionType)}}
14
+ {{#if (not isTypeAlias)}}
15
+ {{#if description}}
16
+ /**
17
+ * {{description}}
18
+ */
19
+ {{/if}}
20
+ @Serializable
21
+ data class {{name}}(
22
+ {{#each properties}}
23
+ {{#if description}}
24
+ /** {{description}} */
25
+ {{/if}}
26
+ {{#if deprecated}}
27
+ @Deprecated("{{#if deprecationMessage}}{{deprecationMessage}}{{else}}Use a different property.{{/if}}")
28
+ {{/if}}
29
+ {{#if (and nullable (not required))}}
30
+ @SerialName("{{name}}")
31
+ val {{name}}: {{{kotlinType type}}}? = null,
32
+ {{else if nullable}}
33
+ @SerialName("{{name}}")
34
+ val {{name}}: {{{kotlinType type}}}? = null,
35
+ {{else if required}}
36
+ @SerialName("{{name}}")
37
+ val {{name}}: {{{kotlinType type}}},
38
+ {{else}}
39
+ @SerialName("{{name}}")
40
+ val {{name}}: {{{kotlinType type}}}? = null,
41
+ {{/if}}
42
+ {{/each}}
43
+ )
44
+ {{/if}}
45
+ {{/if}}
46
+ {{/if}}
47
+ {{/if}}
48
+ {{/unless}}
49
+ {{/if}}
50
+
51
+ {{#if isEnum}}
52
+ {{#if description}}
53
+ /**
54
+ * {{description}}
55
+ */
56
+ {{/if}}
57
+ @Serializable
58
+ enum class {{name}} {
59
+ {{#each enumValues}}
60
+ @SerialName("{{this}}")
61
+ {{pascalCase this}},
62
+ {{/each}}
63
+ }
64
+ {{/if}}
65
+
66
+ {{#if isUnionType}}
67
+ {{#if unionMembers.length}}
68
+ {{#if description}}
69
+ /**
70
+ * {{description}}
71
+ */
72
+ {{/if}}
73
+ @Serializable
74
+ sealed class {{name}} {
75
+ {{#each unionMembers}}
76
+ @Serializable
77
+ data class {{pascalCase this}}(val value: {{{kotlinType this}}}) : {{../name}}()
78
+ {{/each}}
79
+ }
80
+ {{/if}}
81
+ {{/if}}
82
+
83
+ {{#if isIntersectionType}}
84
+ {{#if intersectionMembers.length}}
85
+ {{#if description}}
86
+ /**
87
+ * {{description}}
88
+ */
89
+ {{/if}}
90
+ {{! Intersection type — emit as interface implemented by concrete types }}
91
+ @Serializable
92
+ data class {{name}}(
93
+ // Intersection of: {{join intersectionMembers ", "}}
94
+ // Fields from all intersected types should be inlined here.
95
+ // TODO: generate combined fields
96
+ )
97
+ {{/if}}
98
+ {{/if}}
99
+
100
+ {{/each}}
@@ -0,0 +1,74 @@
1
+ <?php
2
+
3
+ /**
4
+ * @generated
5
+ * This file was automatically generated by Tesseract.
6
+ * DO NOT MODIFY THIS FILE DIRECTLY.
7
+ */
8
+
9
+ declare(strict_types=1);
10
+
11
+ namespace {{namespace}};
12
+
13
+ {{#each groups}}
14
+ use {{namespace}}\Api\{{className}};
15
+ {{/each}}
16
+
17
+ /**
18
+ * {{title}} SDK Client.
19
+ *
20
+ * Entry point for all API operations. Provides accessor methods
21
+ * for each domain group and manages shared transport configuration.
22
+ *
23
+ * Usage:
24
+ * <code>
25
+ * $client = new Client(['baseUrl' => '{{baseUrl}}']);
26
+ * $result = $client->{{groups.[0].name}}()->{{groups.[0].operations.[0].name}}(...);
27
+ * </code>
28
+ */
29
+ class Client
30
+ {
31
+ private Transport $transport;
32
+
33
+ /**
34
+ * @param array{
35
+ * baseUrl?: string,
36
+ * timeoutMs?: int,
37
+ * retries?: array{attempts?: int, backoffMs?: int, jitter?: bool, maxBackoffMs?: int},
38
+ * defaultHeaders?: array<string, string>,
39
+ {{#each securitySchemes}}
40
+ * {{configKey}}?: string,
41
+ {{/each}}
42
+ * } $config
43
+ */
44
+ public function __construct(array $config = [])
45
+ {
46
+ $this->transport = new Transport($config);
47
+ }
48
+
49
+ /**
50
+ * Get the underlying transport instance for advanced use.
51
+ */
52
+ public function getTransport(): Transport
53
+ {
54
+ return $this->transport;
55
+ }
56
+
57
+ {{#each groups}}
58
+ /** @var {{className}}|null */
59
+ private ?{{className}} $_{{{name}}} = null;
60
+
61
+ /**
62
+ {{#if description}}
63
+ * {{description}}
64
+ *
65
+ {{/if}}
66
+ * @return {{className}}
67
+ */
68
+ public function {{name}}(): {{className}}
69
+ {
70
+ return $this->_{{{name}}} ??= new {{className}}($this->transport);
71
+ }
72
+
73
+ {{/each}}
74
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "{{composerName}}",
3
+ "description": "{{#if description}}{{description}}{{else}}PHP SDK for {{title}}{{/if}}",
4
+ "type": "library",
5
+ "license": "MIT",
6
+ "autoload": {
7
+ "psr-4": {
8
+ "{{namespace}}\\": "src/"
9
+ }
10
+ },
11
+ "require": {
12
+ "php": ">=8.1",
13
+ "guzzlehttp/guzzle": "^7.0",
14
+ "ext-json": "*"
15
+ },
16
+ "require-dev": {
17
+ "phpunit/phpunit": "^10.0"
18
+ }
19
+ }