@apollo-deploy/tesseract 0.9.1 → 1.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 (114) 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 +188 -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 +5 -3
  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/adapters/typescript/index.d.ts.map +1 -1
  40. package/dist/adapters/typescript/index.js +43 -2
  41. package/dist/adapters/typescript/index.js.map +1 -1
  42. package/dist/cli.js +2 -1
  43. package/dist/cli.js.map +1 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +14 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/pipeline/intake.d.ts.map +1 -1
  48. package/dist/pipeline/intake.js +32 -21
  49. package/dist/pipeline/intake.js.map +1 -1
  50. package/dist/types/ir.d.ts +1 -1
  51. package/dist/types/ir.d.ts.map +1 -1
  52. package/dist/types/sdk-module.d.ts +22 -2
  53. package/dist/types/sdk-module.d.ts.map +1 -1
  54. package/dist/types/sdk-module.js +1 -1
  55. package/package.json +1 -1
  56. package/templates/csharp/client.hbs +95 -0
  57. package/templates/csharp/csproj.hbs +18 -0
  58. package/templates/csharp/domain.hbs +183 -0
  59. package/templates/csharp/errors.hbs +45 -0
  60. package/templates/csharp/readme.hbs +76 -0
  61. package/templates/csharp/transport.hbs +291 -0
  62. package/templates/csharp/types.hbs +104 -0
  63. package/templates/go/client.hbs +158 -0
  64. package/templates/go/domain.hbs +89 -0
  65. package/templates/go/errors.hbs +38 -0
  66. package/templates/go/go-mod.hbs +6 -0
  67. package/templates/go/readme.hbs +73 -0
  68. package/templates/go/transport.hbs +379 -0
  69. package/templates/go/types.hbs +80 -0
  70. package/templates/go/utils.hbs +45 -0
  71. package/templates/kotlin/build-gradle.hbs +151 -0
  72. package/templates/kotlin/client.hbs +57 -0
  73. package/templates/kotlin/domain.hbs +73 -0
  74. package/templates/kotlin/errors.hbs +42 -0
  75. package/templates/kotlin/gradle-properties.hbs +2 -0
  76. package/templates/kotlin/readme.hbs +133 -0
  77. package/templates/kotlin/settings-gradle.hbs +1 -0
  78. package/templates/kotlin/transport.hbs +246 -0
  79. package/templates/kotlin/types.hbs +100 -0
  80. package/templates/php/client.hbs +74 -0
  81. package/templates/php/composer.hbs +19 -0
  82. package/templates/php/domain.hbs +173 -0
  83. package/templates/php/errors.hbs +131 -0
  84. package/templates/php/readme.hbs +89 -0
  85. package/templates/php/transport.hbs +285 -0
  86. package/templates/php/types.hbs +95 -0
  87. package/templates/python/client.hbs +102 -0
  88. package/templates/python/domain.hbs +139 -0
  89. package/templates/python/index.hbs +20 -0
  90. package/templates/python/pyproject.hbs +26 -0
  91. package/templates/python/readme.hbs +64 -0
  92. package/templates/python/resources-init.hbs +11 -0
  93. package/templates/python/transport.hbs +191 -0
  94. package/templates/python/types-errors.hbs +75 -0
  95. package/templates/python/types.hbs +79 -0
  96. package/templates/ruby/Gemfile.hbs +5 -0
  97. package/templates/ruby/client.hbs +86 -0
  98. package/templates/ruby/domain.hbs +110 -0
  99. package/templates/ruby/errors.hbs +150 -0
  100. package/templates/ruby/gemspec.hbs +30 -0
  101. package/templates/ruby/index.hbs +54 -0
  102. package/templates/ruby/readme.hbs +113 -0
  103. package/templates/ruby/transport.hbs +177 -0
  104. package/templates/ruby/types.hbs +126 -0
  105. package/templates/ruby/version.hbs +5 -0
  106. package/templates/rust/api-mod.hbs +3 -0
  107. package/templates/rust/cargo.hbs +22 -0
  108. package/templates/rust/client.hbs +114 -0
  109. package/templates/rust/domain.hbs +94 -0
  110. package/templates/rust/error.hbs +74 -0
  111. package/templates/rust/lib.hbs +85 -0
  112. package/templates/rust/readme.hbs +77 -0
  113. package/templates/rust/transport.hbs +287 -0
  114. package/templates/rust/types.hbs +142 -0
@@ -0,0 +1,287 @@
1
+ use crate::error::SdkError;
2
+ use reqwest::{Client as ReqwestClient, Method, RequestBuilder, Response, StatusCode};
3
+ use serde::de::DeserializeOwned;
4
+ use serde::Serialize;
5
+ use std::sync::Arc;
6
+ use std::time::Duration;
7
+ use url::Url;
8
+
9
+ /// Internal transport configuration.
10
+ #[derive(Debug, Clone)]
11
+ pub struct TransportConfig {
12
+ pub base_url: String,
13
+ {{#each securitySchemes}}
14
+ pub {{snakeCase configKey}}: Option<String>,
15
+ {{/each}}
16
+ pub timeout: Duration,
17
+ pub max_retries: u32,
18
+ }
19
+
20
+ /// HTTP transport layer for the SDK.
21
+ ///
22
+ /// Handles request building, authentication, retries, and error mapping.
23
+ /// Cloning a `Transport` produces a shallow clone that shares the underlying
24
+ /// reqwest client.
25
+ #[derive(Clone)]
26
+ pub struct Transport {
27
+ client: ReqwestClient,
28
+ config: Arc<TransportConfig>,
29
+ }
30
+
31
+ impl Transport {
32
+ /// Create a new transport with the given configuration.
33
+ pub fn new(config: TransportConfig) -> Self {
34
+ let client = ReqwestClient::builder()
35
+ .timeout(config.timeout)
36
+ .build()
37
+ .expect("failed to build reqwest client");
38
+
39
+ Self {
40
+ client,
41
+ config: Arc::new(config),
42
+ }
43
+ }
44
+
45
+ /// Execute a request, deserializing the response body as JSON.
46
+ pub async fn execute<T: DeserializeOwned>(
47
+ &self,
48
+ method: Method,
49
+ path: &str,
50
+ query: Option<&[(String, String)]>,
51
+ body: Option<&(impl Serialize + ?Sized)>,
52
+ headers: Option<&[(String, String)]>,
53
+ ) -> Result<T, SdkError> {
54
+ let url = self.build_url(path, query)?;
55
+
56
+ let mut req = self.client.request(method, url);
57
+
58
+ req = self.apply_auth(req);
59
+ req = self.apply_headers(req, headers);
60
+
61
+ if let Some(b) = body {
62
+ req = req.json(b);
63
+ }
64
+
65
+ let response = self.execute_with_retry(req).await?;
66
+
67
+ self.handle_response(response).await
68
+ }
69
+
70
+ /// Execute a request that returns the raw response text.
71
+ pub async fn execute_raw(
72
+ &self,
73
+ method: Method,
74
+ path: &str,
75
+ query: Option<&[(String, String)]>,
76
+ body: Option<&(impl Serialize + ?Sized)>,
77
+ headers: Option<&[(String, String)]>,
78
+ ) -> Result<String, SdkError> {
79
+ let url = self.build_url(path, query)?;
80
+
81
+ let mut req = self.client.request(method, url);
82
+
83
+ req = self.apply_auth(req);
84
+ req = self.apply_headers(req, headers);
85
+
86
+ if let Some(b) = body {
87
+ req = req.json(b);
88
+ }
89
+
90
+ let response = self.execute_with_retry(req).await?;
91
+
92
+ let status = response.status();
93
+ let text = response.text().await.map_err(|e| {
94
+ SdkError::DeserializationError(format!("failed to read response body: {}", e))
95
+ })?;
96
+
97
+ if status.is_success() {
98
+ Ok(text)
99
+ } else {
100
+ Err(self.build_api_error(status, &text))
101
+ }
102
+ }
103
+
104
+ // ── private helpers ─────────────────────────────────────────────────────
105
+
106
+ fn build_url(
107
+ &self,
108
+ path: &str,
109
+ query: Option<&[(String, String)]>,
110
+ ) -> Result<Url, SdkError> {
111
+ let base = Url::parse(&self.config.base_url).map_err(|e| {
112
+ SdkError::ValidationError(format!("invalid base URL: {}", e))
113
+ })?;
114
+
115
+ let joined = base.join(path).map_err(|e| {
116
+ SdkError::ValidationError(format!("invalid path '{}': {}", path, e))
117
+ })?;
118
+
119
+ if let Some(params) = query {
120
+ let mut url = joined;
121
+ {
122
+ let mut qp = url.query_pairs_mut();
123
+ for (k, v) in params {
124
+ qp.append_pair(k, v);
125
+ }
126
+ }
127
+ Ok(url)
128
+ } else {
129
+ Ok(joined)
130
+ }
131
+ }
132
+
133
+ fn apply_auth(&self, mut req: RequestBuilder) -> RequestBuilder {
134
+ {{#each securitySchemes}}
135
+ {{#if (eq type "apiKey")}}
136
+ {{#if (eq in "header")}}
137
+ if let Some(ref key) = self.config.{{snakeCase configKey}} {
138
+ req = req.header("{{paramName}}", key);
139
+ }
140
+ {{/if}}
141
+ {{#if (eq in "query")}}
142
+ // API key via query parameter handled during URL construction
143
+ {{/if}}
144
+ {{/if}}
145
+ {{#if (and (eq type "http") (eq scheme "bearer"))}}
146
+ if let Some(ref token) = self.config.{{snakeCase configKey}} {
147
+ req = req.header("Authorization", format!("Bearer {}", token));
148
+ }
149
+ {{/if}}
150
+ {{#if (eq type "oauth2")}}
151
+ if let Some(ref token) = self.config.{{snakeCase configKey}} {
152
+ req = req.header("Authorization", format!("Bearer {}", token));
153
+ }
154
+ {{/if}}
155
+ {{#if (eq type "openIdConnect")}}
156
+ if let Some(ref token) = self.config.{{snakeCase configKey}} {
157
+ req = req.header("Authorization", format!("Bearer {}", token));
158
+ }
159
+ {{/if}}
160
+ {{/each}}
161
+ req
162
+ }
163
+
164
+ fn apply_headers(
165
+ &self,
166
+ mut req: RequestBuilder,
167
+ headers: Option<&[(String, String)]>,
168
+ ) -> RequestBuilder {
169
+ if let Some(hdrs) = headers {
170
+ for (key, value) in hdrs {
171
+ req = req.header(key.as_str(), value.as_str());
172
+ }
173
+ }
174
+ req
175
+ }
176
+
177
+ async fn execute_with_retry(
178
+ &self,
179
+ req: RequestBuilder,
180
+ ) -> Result<Response, SdkError> {
181
+ let max_retries = self.config.max_retries;
182
+ let mut attempt = 0u32;
183
+
184
+ loop {
185
+ let request = req
186
+ .try_clone()
187
+ .ok_or_else(|| SdkError::NetworkError("failed to clone request for retry".to_string()))?;
188
+
189
+ match request.send().await {
190
+ Ok(response) => {
191
+ let status = response.status();
192
+
193
+ // Retry on server errors (5xx) and 429 Too Many Requests
194
+ if status.is_server_error() || status == StatusCode::TOO_MANY_REQUESTS {
195
+ if attempt < max_retries {
196
+ attempt += 1;
197
+ let backoff = Duration::from_millis(
198
+ 200u64 * 2u64.pow(attempt),
199
+ );
200
+ tokio::time::sleep(backoff).await;
201
+ continue;
202
+ }
203
+ }
204
+ return Ok(response);
205
+ }
206
+ Err(e) => {
207
+ if attempt < max_retries && e.is_connect() || e.is_timeout() {
208
+ attempt += 1;
209
+ let backoff = Duration::from_millis(
210
+ 200u64 * 2u64.pow(attempt),
211
+ );
212
+ tokio::time::sleep(backoff).await;
213
+ continue;
214
+ }
215
+ return Err(SdkError::NetworkError(e.to_string()));
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ async fn handle_response<T: DeserializeOwned>(
222
+ &self,
223
+ response: Response,
224
+ ) -> Result<T, SdkError> {
225
+ let status = response.status();
226
+ let text = response.text().await.map_err(|e| {
227
+ SdkError::DeserializationError(format!("failed to read response body: {}", e))
228
+ })?;
229
+
230
+ if status.is_success() {
231
+ serde_json::from_str(&text).map_err(|e| {
232
+ SdkError::DeserializationError(format!(
233
+ "failed to deserialize response (status {}): {}",
234
+ status, e
235
+ ))
236
+ })
237
+ } else {
238
+ Err(self.build_api_error(status, &text))
239
+ }
240
+ }
241
+
242
+ fn build_api_error(&self, status: StatusCode, body: &str) -> SdkError {
243
+ // Try to extract structured error info from the JSON body
244
+ let code = serde_json::from_str::<serde_json::Value>(body)
245
+ .ok()
246
+ .and_then(|v| {
247
+ v.get("code")
248
+ .or_else(|| v.get("error"))
249
+ .and_then(|c| c.as_str().map(String::from))
250
+ .or_else(|| {
251
+ v.get("error")
252
+ .and_then(|e| e.get("code"))
253
+ .and_then(|c| c.as_str().map(String::from))
254
+ })
255
+ });
256
+
257
+ let message = serde_json::from_str::<serde_json::Value>(body)
258
+ .ok()
259
+ .and_then(|v| {
260
+ v.get("message")
261
+ .or_else(|| v.get("error"))
262
+ .and_then(|m| {
263
+ if m.is_string() {
264
+ m.as_str().map(String::from)
265
+ } else {
266
+ m.get("message").and_then(|s| s.as_str().map(String::from))
267
+ }
268
+ })
269
+ })
270
+ .unwrap_or_else(|| body.to_string());
271
+
272
+ SdkError::ApiError {
273
+ status: status.as_u16(),
274
+ code,
275
+ message,
276
+ body: body.to_string(),
277
+ }
278
+ }
279
+ }
280
+
281
+ impl std::fmt::Debug for Transport {
282
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283
+ f.debug_struct("Transport")
284
+ .field("config", &self.config)
285
+ .finish()
286
+ }
287
+ }
@@ -0,0 +1,142 @@
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ {{#each schemas}}
4
+ {{#if (and (not ownership) (not isEnum) (not isTypeAlias) (not isUnionType) (not isIntersectionType))}}
5
+ {{#if (or externalImport (eq ownership.kind "external"))}}
6
+ // {{name}} is imported from an external schema package.
7
+ {{else}}
8
+ {{#if description}}
9
+ /// {{description}}
10
+ {{/if}}
11
+ #[derive(Debug, Clone, Serialize, Deserialize)]
12
+ {{#if additionalPropertiesType}}
13
+ {{! Additional properties means we need to use a flattened map }}
14
+ #[serde(rename_all = "camelCase")]
15
+ pub struct {{name}} {
16
+ {{#each properties}}
17
+ {{#if description}}
18
+ /// {{description}}
19
+ {{/if}}
20
+ {{#if deprecated}}
21
+ #[deprecated]
22
+ {{/if}}
23
+ {{#if (and nullable (not required))}}
24
+ #[serde(skip_serializing_if = "Option::is_none")]
25
+ pub {{snakeCase name}}: Option<{{{rustType type}}}>,
26
+ {{else if nullable}}
27
+ #[serde(skip_serializing_if = "Option::is_none")]
28
+ pub {{snakeCase name}}: Option<{{{rustType type}}}>,
29
+ {{else if required}}
30
+ pub {{snakeCase name}}: {{{rustType type}}},
31
+ {{else}}
32
+ #[serde(skip_serializing_if = "Option::is_none")]
33
+ pub {{snakeCase name}}: Option<{{{rustType type}}}>,
34
+ {{/if}}
35
+ {{/each}}
36
+ }
37
+ {{else}}
38
+ #[serde(rename_all = "camelCase")]
39
+ pub struct {{name}} {
40
+ {{#each properties}}
41
+ {{#if description}}
42
+ /// {{description}}
43
+ {{/if}}
44
+ {{#if deprecated}}
45
+ #[deprecated]
46
+ {{/if}}
47
+ {{#if (and nullable (not required))}}
48
+ #[serde(skip_serializing_if = "Option::is_none")]
49
+ pub {{snakeCase name}}: Option<{{{rustType type}}}>,
50
+ {{else if nullable}}
51
+ #[serde(skip_serializing_if = "Option::is_none")]
52
+ pub {{snakeCase name}}: Option<{{{rustType type}}}>,
53
+ {{else if required}}
54
+ pub {{snakeCase name}}: {{{rustType type}}},
55
+ {{else}}
56
+ #[serde(skip_serializing_if = "Option::is_none")]
57
+ pub {{snakeCase name}}: Option<{{{rustType type}}}>,
58
+ {{/if}}
59
+ {{/each}}
60
+ }
61
+ {{/if}}
62
+ {{/if}}
63
+ {{/if}}
64
+
65
+ {{#if isEnum}}
66
+ {{#if description}}
67
+ /// {{description}}
68
+ {{/if}}
69
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
70
+ {{#if enumValues}}
71
+ #[serde(rename_all = "camelCase")]
72
+ {{/if}}
73
+ pub enum {{name}} {
74
+ {{#each enumValues}}
75
+ #[serde(rename = "{{this}}")]
76
+ {{pascalCase this}},
77
+ {{/each}}
78
+ }
79
+ {{/if}}
80
+
81
+ {{#if isTypeAlias}}
82
+ {{#if description}}
83
+ /// {{description}}
84
+ {{/if}}
85
+ {{#if properties.length}}
86
+ {{! Type alias with shape — emit as struct }}
87
+ #[derive(Debug, Clone, Serialize, Deserialize)]
88
+ #[serde(rename_all = "camelCase")]
89
+ pub struct {{name}} {
90
+ {{#each properties}}
91
+ {{#if description}}
92
+ /// {{description}}
93
+ {{/if}}
94
+ {{#if required}}
95
+ pub {{snakeCase name}}: {{{rustType type}}},
96
+ {{else}}
97
+ #[serde(skip_serializing_if = "Option::is_none")]
98
+ pub {{snakeCase name}}: Option<{{{rustType type}}}>,
99
+ {{/if}}
100
+ {{/each}}
101
+ }
102
+ {{else}}
103
+ {{! Simple type alias — skip, referenced by name }}
104
+ // {{name}} is a type alias — referenced inline.
105
+ {{/if}}
106
+ {{/if}}
107
+
108
+ {{#if isUnionType}}
109
+ {{#if description}}
110
+ /// {{description}}
111
+ {{/if}}
112
+ {{#if unionMembers.length}}
113
+ {{! Union type with named members — emit as enum }}
114
+ #[derive(Debug, Clone, Serialize, Deserialize)]
115
+ #[serde(untagged)]
116
+ pub enum {{name}} {
117
+ {{#each unionMembers}}
118
+ {{pascalCase this}}({{{rustType this}}}),
119
+ {{/each}}
120
+ }
121
+ {{/if}}
122
+ {{/if}}
123
+
124
+ {{#if isIntersectionType}}
125
+ {{#if description}}
126
+ /// {{description}}
127
+ {{/if}}
128
+ {{#if intersectionMembers.length}}
129
+ {{! Intersection type — flatten into a struct combining all members }}
130
+ #[derive(Debug, Clone, Serialize, Deserialize)]
131
+ #[serde(rename_all = "camelCase")]
132
+ pub struct {{name}} {
133
+ {{#each intersectionMembers}}
134
+ // Fields from {{this}} are expected to be inlined by the code generator.
135
+ {{/each}}
136
+ // Intersection types require manual field specification.
137
+ // TODO: inline fields from {{join intersectionMembers ", "}}
138
+ }
139
+ {{/if}}
140
+ {{/if}}
141
+
142
+ {{/each}}