@apollo-deploy/tesseract 1.9.0 → 2.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 (194) hide show
  1. package/README.md +218 -110
  2. package/dist/adapters/csharp/index.d.ts +5 -8
  3. package/dist/adapters/csharp/index.d.ts.map +1 -1
  4. package/dist/adapters/csharp/index.js +314 -74
  5. package/dist/adapters/csharp/index.js.map +1 -1
  6. package/dist/adapters/documentation.d.ts +17 -0
  7. package/dist/adapters/documentation.d.ts.map +1 -0
  8. package/dist/adapters/documentation.js +268 -0
  9. package/dist/adapters/documentation.js.map +1 -0
  10. package/dist/adapters/go/index.d.ts +5 -8
  11. package/dist/adapters/go/index.d.ts.map +1 -1
  12. package/dist/adapters/go/index.js +197 -101
  13. package/dist/adapters/go/index.js.map +1 -1
  14. package/dist/adapters/kotlin/index.d.ts +5 -9
  15. package/dist/adapters/kotlin/index.d.ts.map +1 -1
  16. package/dist/adapters/kotlin/index.js +280 -212
  17. package/dist/adapters/kotlin/index.js.map +1 -1
  18. package/dist/adapters/php/index.d.ts +5 -15
  19. package/dist/adapters/php/index.d.ts.map +1 -1
  20. package/dist/adapters/php/index.js +362 -90
  21. package/dist/adapters/php/index.js.map +1 -1
  22. package/dist/adapters/python/index.d.ts +5 -8
  23. package/dist/adapters/python/index.d.ts.map +1 -1
  24. package/dist/adapters/python/index.js +240 -109
  25. package/dist/adapters/python/index.js.map +1 -1
  26. package/dist/adapters/ruby/index.d.ts +3 -3
  27. package/dist/adapters/ruby/index.d.ts.map +1 -1
  28. package/dist/adapters/ruby/index.js +235 -46
  29. package/dist/adapters/ruby/index.js.map +1 -1
  30. package/dist/adapters/rust/index.d.ts +5 -9
  31. package/dist/adapters/rust/index.d.ts.map +1 -1
  32. package/dist/adapters/rust/index.js +237 -169
  33. package/dist/adapters/rust/index.js.map +1 -1
  34. package/dist/adapters/shared.d.ts +7 -0
  35. package/dist/adapters/shared.d.ts.map +1 -0
  36. package/dist/adapters/shared.js +68 -0
  37. package/dist/adapters/shared.js.map +1 -0
  38. package/dist/adapters/type-utils.d.ts +13 -0
  39. package/dist/adapters/type-utils.d.ts.map +1 -0
  40. package/dist/adapters/type-utils.js +105 -0
  41. package/dist/adapters/type-utils.js.map +1 -0
  42. package/dist/adapters/types.d.ts.map +1 -1
  43. package/dist/adapters/typescript/emitter/emitSchema.d.ts +2 -2
  44. package/dist/adapters/typescript/emitter/emitSchema.js +39 -27
  45. package/dist/adapters/typescript/emitter/emitSchema.js.map +1 -1
  46. package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts +2 -2
  47. package/dist/adapters/typescript/emitter/emitTypeScriptModels.js +15 -15
  48. package/dist/adapters/typescript/emitter/importCollector.d.ts +2 -4
  49. package/dist/adapters/typescript/emitter/importCollector.d.ts.map +1 -1
  50. package/dist/adapters/typescript/emitter/importCollector.js +40 -50
  51. package/dist/adapters/typescript/emitter/importCollector.js.map +1 -1
  52. package/dist/adapters/typescript/emitter/parsers.d.ts +1 -1
  53. package/dist/adapters/typescript/emitter/parsers.js +13 -13
  54. package/dist/adapters/typescript/emitter/typeHoister.d.ts +1 -1
  55. package/dist/adapters/typescript/emitter/typeHoister.d.ts.map +1 -1
  56. package/dist/adapters/typescript/emitter/typeHoister.js +12 -20
  57. package/dist/adapters/typescript/emitter/typeHoister.js.map +1 -1
  58. package/dist/adapters/typescript/index.d.ts +3 -3
  59. package/dist/adapters/typescript/index.d.ts.map +1 -1
  60. package/dist/adapters/typescript/index.js +148 -166
  61. package/dist/adapters/typescript/index.js.map +1 -1
  62. package/dist/adapters/typescript/shared.d.ts +2 -2
  63. package/dist/adapters/typescript/shared.js +12 -12
  64. package/dist/cli.js +50 -37
  65. package/dist/cli.js.map +1 -1
  66. package/dist/collector.d.ts +13 -3
  67. package/dist/collector.d.ts.map +1 -1
  68. package/dist/collector.js +69 -32
  69. package/dist/collector.js.map +1 -1
  70. package/dist/elysia.js +2 -2
  71. package/dist/elysia.js.map +1 -1
  72. package/dist/express.d.ts.map +1 -1
  73. package/dist/express.js.map +1 -1
  74. package/dist/fastify.d.ts +7 -0
  75. package/dist/fastify.d.ts.map +1 -1
  76. package/dist/fastify.js +39 -20
  77. package/dist/fastify.js.map +1 -1
  78. package/dist/helpers/handlebars.d.ts +7 -16
  79. package/dist/helpers/handlebars.d.ts.map +1 -1
  80. package/dist/helpers/handlebars.js +127 -177
  81. package/dist/helpers/handlebars.js.map +1 -1
  82. package/dist/hono.d.ts.map +1 -1
  83. package/dist/hono.js.map +1 -1
  84. package/dist/index.d.ts +14 -12
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +32 -30
  87. package/dist/index.js.map +1 -1
  88. package/dist/koa.d.ts.map +1 -1
  89. package/dist/koa.js.map +1 -1
  90. package/dist/nestjs.d.ts.map +1 -1
  91. package/dist/nestjs.js +41 -14
  92. package/dist/nestjs.js.map +1 -1
  93. package/dist/pipeline/enrich.d.ts +2 -13
  94. package/dist/pipeline/enrich.d.ts.map +1 -1
  95. package/dist/pipeline/enrich.js +157 -212
  96. package/dist/pipeline/enrich.js.map +1 -1
  97. package/dist/pipeline/intake.d.ts +3 -3
  98. package/dist/pipeline/intake.d.ts.map +1 -1
  99. package/dist/pipeline/intake.js +342 -238
  100. package/dist/pipeline/intake.js.map +1 -1
  101. package/dist/pipeline/write.d.ts +1 -14
  102. package/dist/pipeline/write.d.ts.map +1 -1
  103. package/dist/pipeline/write.js +73 -113
  104. package/dist/pipeline/write.js.map +1 -1
  105. package/dist/types/config.d.ts +14 -26
  106. package/dist/types/config.d.ts.map +1 -1
  107. package/dist/types/config.js +16 -12
  108. package/dist/types/config.js.map +1 -1
  109. package/dist/types/ir.d.ts +12 -8
  110. package/dist/types/ir.d.ts.map +1 -1
  111. package/dist/types/manifest.d.ts +10 -11
  112. package/dist/types/manifest.d.ts.map +1 -1
  113. package/dist/types/manifest.js +54 -14
  114. package/dist/types/manifest.js.map +1 -1
  115. package/dist/types/sdk-module.d.ts +2 -2
  116. package/dist/types/sdk-module.js +1 -1
  117. package/dist/utils/format.d.ts +1 -3
  118. package/dist/utils/format.d.ts.map +1 -1
  119. package/dist/utils/format.js +3 -10
  120. package/dist/utils/format.js.map +1 -1
  121. package/dist/utils/naming.d.ts.map +1 -1
  122. package/dist/utils/naming.js +174 -184
  123. package/dist/utils/naming.js.map +1 -1
  124. package/dist/utils/npm-token.d.ts +1 -17
  125. package/dist/utils/npm-token.d.ts.map +1 -1
  126. package/dist/utils/npm-token.js +27 -76
  127. package/dist/utils/npm-token.js.map +1 -1
  128. package/dist/utils/version.d.ts +14 -0
  129. package/dist/utils/version.d.ts.map +1 -0
  130. package/dist/utils/version.js +60 -0
  131. package/dist/utils/version.js.map +1 -0
  132. package/package.json +4 -3
  133. package/templates/csharp/client.hbs +26 -49
  134. package/templates/csharp/csproj.hbs +7 -0
  135. package/templates/csharp/domain.hbs +48 -130
  136. package/templates/csharp/errors.hbs +17 -25
  137. package/templates/csharp/readme.hbs +31 -44
  138. package/templates/csharp/transport.hbs +238 -136
  139. package/templates/csharp/types.hbs +30 -76
  140. package/templates/go/client.hbs +44 -109
  141. package/templates/go/domain.hbs +83 -25
  142. package/templates/go/go-mod.hbs +1 -4
  143. package/templates/go/readme.hbs +32 -29
  144. package/templates/go/transport.hbs +174 -173
  145. package/templates/go/types.hbs +17 -53
  146. package/templates/go/utils.hbs +3 -2
  147. package/templates/kotlin/build-gradle.hbs +1 -1
  148. package/templates/kotlin/client.hbs +18 -15
  149. package/templates/kotlin/domain.hbs +54 -51
  150. package/templates/kotlin/errors.hbs +1 -1
  151. package/templates/kotlin/readme.hbs +35 -137
  152. package/templates/kotlin/transport.hbs +140 -123
  153. package/templates/kotlin/types.hbs +30 -81
  154. package/templates/php/client.hbs +8 -46
  155. package/templates/php/composer.hbs +2 -1
  156. package/templates/php/domain.hbs +86 -117
  157. package/templates/php/errors.hbs +1 -1
  158. package/templates/php/readme.hbs +42 -49
  159. package/templates/php/transport.hbs +192 -145
  160. package/templates/php/types.hbs +63 -60
  161. package/templates/python/client.hbs +15 -2
  162. package/templates/python/domain.hbs +50 -108
  163. package/templates/python/pyproject.hbs +5 -0
  164. package/templates/python/readme.hbs +47 -30
  165. package/templates/python/resources-init.hbs +1 -1
  166. package/templates/python/transport.hbs +167 -53
  167. package/templates/python/types.hbs +22 -58
  168. package/templates/ruby/client.hbs +10 -6
  169. package/templates/ruby/domain.hbs +48 -67
  170. package/templates/ruby/errors.hbs +3 -3
  171. package/templates/ruby/gemspec.hbs +3 -10
  172. package/templates/ruby/index.hbs +3 -3
  173. package/templates/ruby/readme.hbs +33 -70
  174. package/templates/ruby/transport.hbs +129 -103
  175. package/templates/ruby/types.hbs +11 -16
  176. package/templates/ruby/version.hbs +1 -1
  177. package/templates/rust/cargo.hbs +2 -0
  178. package/templates/rust/client.hbs +10 -1
  179. package/templates/rust/domain.hbs +69 -52
  180. package/templates/rust/error.hbs +3 -1
  181. package/templates/rust/lib.hbs +4 -5
  182. package/templates/rust/readme.hbs +33 -46
  183. package/templates/rust/transport.hbs +170 -159
  184. package/templates/rust/types.hbs +34 -111
  185. package/templates/typescript/domain-class.hbs +14 -6
  186. package/templates/typescript/domain.hbs +13 -5
  187. package/templates/typescript/package-json.hbs +3 -1
  188. package/templates/typescript/readme.hbs +42 -42
  189. package/templates/typescript/transport.hbs +46 -8
  190. package/templates/typescript/types-errors.hbs +1 -1
  191. package/dist/adapters/typescript/model-emitter.d.ts +0 -13
  192. package/dist/adapters/typescript/model-emitter.d.ts.map +0 -1
  193. package/dist/adapters/typescript/model-emitter.js +0 -415
  194. package/dist/adapters/typescript/model-emitter.js.map +0 -1
@@ -6,43 +6,61 @@ import (
6
6
  "encoding/json"
7
7
  "fmt"
8
8
  "io"
9
- "math"
10
9
  "math/rand"
10
+ "net"
11
11
  "net/http"
12
12
  "net/url"
13
+ "strconv"
13
14
  "strings"
14
15
  "time"
15
16
  )
16
17
 
17
- // TransportConfig holds configuration for the HTTP transport layer.
18
+ var retryableMethods = map[string]struct{}{
19
+ http.MethodGet: {}, http.MethodHead: {}, http.MethodOptions: {},
20
+ http.MethodPut: {}, http.MethodDelete: {},
21
+ }
22
+
23
+ var retryableStatuses = map[int]struct{}{
24
+ http.StatusRequestTimeout: {}, 425: {}, http.StatusTooManyRequests: {},
25
+ http.StatusInternalServerError: {}, http.StatusBadGateway: {},
26
+ http.StatusServiceUnavailable: {}, http.StatusGatewayTimeout: {},
27
+ }
28
+
29
+ // TransportConfig configures the HTTP transport.
18
30
  type TransportConfig struct {
19
- BaseURL string
31
+ BaseURL string
20
32
  {{#each securitySchemes}}
21
33
  {{pascalCase configKey}} string
22
34
  {{/each}}
23
- Timeout time.Duration
24
- MaxRetries int
25
- Headers map[string]string
35
+ Timeout time.Duration
36
+ MaxRetries int
37
+ RetryUnsafeRequests bool
38
+ Headers map[string]string
39
+ UserAgent string
26
40
  }
27
41
 
28
- // Transport is the low-level HTTP client that handles authentication,
29
- // serialization, retries, and error normalization.
42
+ // Transport handles authentication, serialization, retries, and errors.
30
43
  type Transport struct {
31
44
  config TransportConfig
32
45
  httpClient *http.Client
33
46
  }
34
47
 
35
- // NewTransport creates a new Transport with the given configuration.
36
48
  func NewTransport(cfg TransportConfig) *Transport {
49
+ if cfg.Timeout <= 0 {
50
+ cfg.Timeout = 15 * time.Second
51
+ }
52
+ if cfg.MaxRetries < 0 {
53
+ cfg.MaxRetries = 0
54
+ }
55
+ if cfg.UserAgent == "" {
56
+ cfg.UserAgent = "{{pkgName}}-go-sdk/{{packageVersion}}"
57
+ }
37
58
  return &Transport{
38
59
  config: cfg,
39
- httpClient: &http.Client{
40
- Timeout: cfg.Timeout,
41
- },
60
+ httpClient: &http.Client{Timeout: cfg.Timeout},
42
61
  }
43
62
  }
44
63
 
45
- // request holds all the parameters for a single HTTP call.
46
64
  type request struct {
47
65
  Method string
48
66
  Path string
@@ -52,125 +70,107 @@ type request struct {
52
70
  Headers map[string]string
53
71
  }
54
72
 
55
- // Execute performs an HTTP request and unmarshals the response body into result.
56
- // result must be a non-nil pointer.
57
73
  func (t *Transport) Execute(ctx context.Context, req request, result interface{}) error {
58
- return t.executeWithRetries(ctx, req, result, t.config.MaxRetries)
74
+ body, err := t.executeWithRetries(ctx, req)
75
+ if err != nil {
76
+ return err
77
+ }
78
+ if result == nil || len(body) == 0 {
79
+ return nil
80
+ }
81
+ if err := json.Unmarshal(body, result); err != nil {
82
+ return fmt.Errorf("failed to unmarshal response: %w", err)
83
+ }
84
+ return nil
59
85
  }
60
86
 
61
- // ExecuteRaw performs an HTTP request and returns the raw response body bytes.
62
87
  func (t *Transport) ExecuteRaw(ctx context.Context, req request) ([]byte, error) {
63
- return t.executeRawWithRetries(ctx, req, t.config.MaxRetries)
88
+ return t.executeWithRetries(ctx, req)
64
89
  }
65
90
 
66
- // executeWithRetries performs the request with exponential backoff retry logic.
67
- func (t *Transport) executeWithRetries(ctx context.Context, req request, result interface{}, maxRetries int) error {
91
+ func (t *Transport) executeWithRetries(ctx context.Context, req request) ([]byte, error) {
92
+ canRetry := t.canRetry(req)
68
93
  var lastErr error
69
94
 
70
- for attempt := 0; attempt <= maxRetries; attempt++ {
71
- if attempt > 0 {
72
- backoff := time.Duration(math.Pow(2, float64(attempt-1))) * 200 * time.Millisecond
73
- jitter := time.Duration(rand.Int63n(int64(backoff / 2)))
74
- select {
75
- case <-ctx.Done():
76
- return ctx.Err()
77
- case <-time.After(backoff + jitter):
78
- }
95
+ for attempt := 0; attempt <= t.config.MaxRetries; attempt++ {
96
+ resp, body, err := t.doRequest(ctx, req)
97
+ if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
98
+ return body, nil
79
99
  }
80
100
 
81
- resp, body, err := t.doRequest(ctx, req)
101
+ var retryAfter time.Duration
82
102
  if err != nil {
83
103
  lastErr = err
84
- if !isRetryable(err) {
85
- return err
104
+ if !canRetry || !isRetryableNetworkError(err) || attempt >= t.config.MaxRetries {
105
+ return nil, normalizeNetworkError(err)
106
+ }
107
+ } else {
108
+ lastErr = parseErrorResponse(resp, body)
109
+ if !canRetry || !isRetryableStatus(resp.StatusCode) || attempt >= t.config.MaxRetries {
110
+ return nil, lastErr
86
111
  }
87
- continue
112
+ retryAfter = parseRetryAfter(resp.Header.Get("Retry-After"))
88
113
  }
89
114
 
90
- if resp.StatusCode >= 200 && resp.StatusCode < 300 {
91
- if result != nil && len(body) > 0 {
92
- if err := json.Unmarshal(body, result); err != nil {
93
- return fmt.Errorf("failed to unmarshal response: %w", err)
94
- }
115
+ delay := retryAfter
116
+ if delay <= 0 {
117
+ capDelay := 400 * time.Millisecond * time.Duration(1<<attempt)
118
+ if capDelay > 30*time.Second {
119
+ capDelay = 30 * time.Second
120
+ }
121
+ if capDelay > 0 {
122
+ delay = time.Duration(rand.Int63n(int64(capDelay) + 1))
95
123
  }
96
- return nil
97
124
  }
98
-
99
- if resp.StatusCode >= 500 && attempt < maxRetries {
100
- lastErr = parseErrorResponse(resp, body)
101
- continue
125
+ select {
126
+ case <-ctx.Done():
127
+ return nil, ctx.Err()
128
+ case <-time.After(delay):
102
129
  }
103
-
104
- return parseErrorResponse(resp, body)
105
130
  }
106
131
 
107
- return lastErr
132
+ return nil, lastErr
108
133
  }
109
134
 
110
- // executeRawWithRetries performs a raw request with retries.
111
- func (t *Transport) executeRawWithRetries(ctx context.Context, req request, maxRetries int) ([]byte, error) {
112
- var lastErr error
113
-
114
- for attempt := 0; attempt <= maxRetries; attempt++ {
115
- if attempt > 0 {
116
- backoff := time.Duration(math.Pow(2, float64(attempt-1))) * 200 * time.Millisecond
117
- jitter := time.Duration(rand.Int63n(int64(backoff / 2)))
118
- select {
119
- case <-ctx.Done():
120
- return nil, ctx.Err()
121
- case <-time.After(backoff + jitter):
122
- }
123
- }
124
-
125
- resp, body, err := t.doRequest(ctx, req)
126
- if err != nil {
127
- lastErr = err
128
- if !isRetryable(err) {
129
- return nil, err
130
- }
131
- continue
132
- }
133
-
134
- if resp.StatusCode >= 200 && resp.StatusCode < 300 {
135
- return body, nil
135
+ func (t *Transport) canRetry(req request) bool {
136
+ if t.config.RetryUnsafeRequests {
137
+ return true
138
+ }
139
+ if _, ok := retryableMethods[strings.ToUpper(req.Method)]; ok {
140
+ return true
141
+ }
142
+ for name, value := range t.config.Headers {
143
+ if strings.EqualFold(name, "X-Idempotency-Key") && value != "" {
144
+ return true
136
145
  }
137
-
138
- if resp.StatusCode >= 500 && attempt < maxRetries {
139
- lastErr = parseErrorResponse(resp, body)
140
- continue
146
+ }
147
+ for name, value := range req.Headers {
148
+ if strings.EqualFold(name, "X-Idempotency-Key") && value != "" {
149
+ return true
141
150
  }
142
-
143
- return nil, parseErrorResponse(resp, body)
144
151
  }
145
-
146
- return nil, lastErr
152
+ return false
147
153
  }
148
154
 
149
- // doRequest builds and executes a single HTTP request.
150
155
  func (t *Transport) doRequest(ctx context.Context, req request) (*http.Response, []byte, error) {
151
- // Build URL
152
156
  base, err := url.Parse(t.config.BaseURL)
153
157
  if err != nil {
154
158
  return nil, nil, fmt.Errorf("invalid base URL: %w", err)
155
159
  }
156
-
157
160
  rel, err := url.Parse(req.Path)
158
161
  if err != nil {
159
162
  return nil, nil, fmt.Errorf("invalid request path: %w", err)
160
163
  }
161
-
162
164
  fullURL := base.ResolveReference(rel)
163
-
164
165
  if len(req.Query) > 0 {
165
166
  fullURL.RawQuery = req.Query.Encode()
166
167
  }
167
168
 
168
- // Build body
169
169
  var bodyReader io.Reader
170
170
  if req.Body != nil {
171
- bodyBytes, err := json.Marshal(req.Body)
172
- if err != nil {
173
- return nil, nil, fmt.Errorf("failed to marshal request body: %w", err)
171
+ bodyBytes, marshalErr := json.Marshal(req.Body)
172
+ if marshalErr != nil {
173
+ return nil, nil, fmt.Errorf("failed to marshal request body: %w", marshalErr)
174
174
  }
175
175
  bodyReader = bytes.NewReader(bodyBytes)
176
176
  }
@@ -180,12 +180,13 @@ func (t *Transport) doRequest(ctx context.Context, req request) (*http.Response,
180
180
  return nil, nil, fmt.Errorf("failed to create request: %w", err)
181
181
  }
182
182
 
183
- // Set default headers
184
- for k, v := range t.config.Headers {
185
- httpReq.Header.Set(k, v)
183
+ for key, value := range t.config.Headers {
184
+ httpReq.Header.Set(key, value)
185
+ }
186
+ httpReq.Header.Set("Accept", "application/json")
187
+ if httpReq.Header.Get("User-Agent") == "" {
188
+ httpReq.Header.Set("User-Agent", t.config.UserAgent)
186
189
  }
187
-
188
- // Set auth headers
189
190
  {{#each securitySchemes}}
190
191
  {{#if (eq type "apiKey")}}
191
192
  {{#if (eq in "header")}}
@@ -195,9 +196,9 @@ func (t *Transport) doRequest(ctx context.Context, req request) (*http.Response,
195
196
  {{/if}}
196
197
  {{#if (eq in "query")}}
197
198
  if t.config.{{pascalCase configKey}} != "" {
198
- q := httpReq.URL.Query()
199
- q.Set("{{paramName}}", t.config.{{pascalCase configKey}})
200
- httpReq.URL.RawQuery = q.Encode()
199
+ query := httpReq.URL.Query()
200
+ query.Set("{{paramName}}", t.config.{{pascalCase configKey}})
201
+ httpReq.URL.RawQuery = query.Encode()
201
202
  }
202
203
  {{/if}}
203
204
  {{/if}}
@@ -218,16 +219,13 @@ func (t *Transport) doRequest(ctx context.Context, req request) (*http.Response,
218
219
  {{/if}}
219
220
  {{/each}}
220
221
 
221
- // Set content type
222
222
  if req.ContentType != "" {
223
223
  httpReq.Header.Set("Content-Type", req.ContentType)
224
224
  } else if req.Body != nil {
225
225
  httpReq.Header.Set("Content-Type", "application/json")
226
226
  }
227
-
228
- // Set custom headers
229
- for k, v := range req.Headers {
230
- httpReq.Header.Set(k, v)
227
+ for key, value := range req.Headers {
228
+ httpReq.Header.Set(key, value)
231
229
  }
232
230
 
233
231
  resp, err := t.httpClient.Do(httpReq)
@@ -235,38 +233,34 @@ func (t *Transport) doRequest(ctx context.Context, req request) (*http.Response,
235
233
  return nil, nil, err
236
234
  }
237
235
  defer resp.Body.Close()
238
-
239
236
  body, err := io.ReadAll(resp.Body)
240
237
  if err != nil {
241
238
  return resp, nil, fmt.Errorf("failed to read response body: %w", err)
242
239
  }
243
-
244
240
  return resp, body, nil
245
241
  }
246
242
 
247
- // Stream executes an HTTP request and returns the response body for streaming.
243
+ // Stream opens a streaming response. Reconnection is left to the caller because
244
+ // replaying a partially consumed stream can duplicate events.
248
245
  func (t *Transport) Stream(ctx context.Context, req request) (*http.Response, error) {
249
246
  base, err := url.Parse(t.config.BaseURL)
250
247
  if err != nil {
251
248
  return nil, fmt.Errorf("invalid base URL: %w", err)
252
249
  }
253
-
254
250
  rel, err := url.Parse(req.Path)
255
251
  if err != nil {
256
252
  return nil, fmt.Errorf("invalid request path: %w", err)
257
253
  }
258
-
259
254
  fullURL := base.ResolveReference(rel)
260
-
261
255
  if len(req.Query) > 0 {
262
256
  fullURL.RawQuery = req.Query.Encode()
263
257
  }
264
258
 
265
259
  var bodyReader io.Reader
266
260
  if req.Body != nil {
267
- bodyBytes, err := json.Marshal(req.Body)
268
- if err != nil {
269
- return nil, fmt.Errorf("failed to marshal request body: %w", err)
261
+ bodyBytes, marshalErr := json.Marshal(req.Body)
262
+ if marshalErr != nil {
263
+ return nil, fmt.Errorf("failed to marshal request body: %w", marshalErr)
270
264
  }
271
265
  bodyReader = bytes.NewReader(bodyBytes)
272
266
  }
@@ -275,105 +269,112 @@ func (t *Transport) Stream(ctx context.Context, req request) (*http.Response, er
275
269
  if err != nil {
276
270
  return nil, fmt.Errorf("failed to create request: %w", err)
277
271
  }
278
-
279
- for k, v := range t.config.Headers {
280
- httpReq.Header.Set(k, v)
272
+ for key, value := range t.config.Headers {
273
+ httpReq.Header.Set(key, value)
281
274
  }
282
-
275
+ httpReq.Header.Set("Accept", "text/event-stream")
276
+ httpReq.Header.Set("User-Agent", t.config.UserAgent)
283
277
  {{#each securitySchemes}}
284
278
  {{#if (eq type "apiKey")}}
285
279
  {{#if (eq in "header")}}
286
- if t.config.{{pascalCase configKey}} != "" {
287
- httpReq.Header.Set("{{paramName}}", t.config.{{pascalCase configKey}})
288
- }
280
+ if t.config.{{pascalCase configKey}} != "" { httpReq.Header.Set("{{paramName}}", t.config.{{pascalCase configKey}}) }
289
281
  {{/if}}
290
282
  {{/if}}
291
- {{#if (and (eq type "http") (eq scheme "bearer"))}}
292
- if t.config.{{pascalCase configKey}} != "" {
293
- httpReq.Header.Set("Authorization", "Bearer "+t.config.{{pascalCase configKey}})
294
- }
295
- {{/if}}
296
- {{#if (eq type "oauth2")}}
297
- if t.config.{{pascalCase configKey}} != "" {
298
- httpReq.Header.Set("Authorization", "Bearer "+t.config.{{pascalCase configKey}})
299
- }
283
+ {{#if (or (and (eq type "http") (eq scheme "bearer")) (eq type "oauth2") (eq type "openIdConnect"))}}
284
+ if t.config.{{pascalCase configKey}} != "" { httpReq.Header.Set("Authorization", "Bearer "+t.config.{{pascalCase configKey}}) }
300
285
  {{/if}}
301
286
  {{/each}}
302
-
303
- if req.ContentType != "" {
304
- httpReq.Header.Set("Content-Type", req.ContentType)
305
- } else if req.Body != nil {
306
- httpReq.Header.Set("Content-Type", "application/json")
307
- }
308
-
309
- for k, v := range req.Headers {
310
- httpReq.Header.Set(k, v)
287
+ for key, value := range req.Headers {
288
+ httpReq.Header.Set(key, value)
311
289
  }
312
290
 
313
291
  resp, err := t.httpClient.Do(httpReq)
314
292
  if err != nil {
315
- return nil, err
293
+ return nil, normalizeNetworkError(err)
316
294
  }
317
-
318
295
  if resp.StatusCode >= 400 {
319
296
  body, _ := io.ReadAll(resp.Body)
320
297
  resp.Body.Close()
321
298
  return nil, parseErrorResponse(resp, body)
322
299
  }
323
-
324
300
  return resp, nil
325
301
  }
326
302
 
327
- // ── Helpers ────────────────────────────────────────────────────────────────
303
+ func (t *Transport) CloseIdleConnections() {
304
+ t.httpClient.CloseIdleConnections()
305
+ }
328
306
 
329
- // isRetryable returns true if the error indicates a transient failure.
330
- func isRetryable(err error) bool {
307
+ func isRetryableStatus(status int) bool {
308
+ _, ok := retryableStatuses[status]
309
+ return ok
310
+ }
311
+
312
+ func isRetryableNetworkError(err error) bool {
331
313
  if err == nil {
332
314
  return false
333
315
  }
334
- msg := err.Error()
335
- return strings.Contains(msg, "connection refused") ||
336
- strings.Contains(msg, "connection reset") ||
337
- strings.Contains(msg, "timeout") ||
338
- strings.Contains(msg, "temporary failure") ||
339
- strings.Contains(msg, "EOF")
316
+ if netErr, ok := err.(net.Error); ok {
317
+ return netErr.Timeout() || netErr.Temporary()
318
+ }
319
+ message := strings.ToLower(err.Error())
320
+ return strings.Contains(message, "connection reset") ||
321
+ strings.Contains(message, "connection refused") ||
322
+ strings.Contains(message, "unexpected eof") ||
323
+ strings.Contains(message, "temporary failure")
340
324
  }
341
325
 
342
- // parseErrorResponse builds an SDKError from an HTTP response.
343
- func parseErrorResponse(resp *http.Response, body []byte) error {
344
- var requestID string
345
- if rid := resp.Header.Get("X-Request-ID"); rid != "" {
346
- requestID = rid
347
- } else if rid := resp.Header.Get("x-request-id"); rid != "" {
348
- requestID = rid
326
+ func normalizeNetworkError(err error) error {
327
+ if err == nil {
328
+ return nil
349
329
  }
330
+ return &SDKError{Status: 0, Code: "network_error", Message: err.Error()}
331
+ }
350
332
 
351
- sdkErr := &SDKError{
352
- Status: resp.StatusCode,
353
- RequestID: requestID,
354
- Message: strings.TrimSpace(string(body)),
333
+ func parseRetryAfter(value string) time.Duration {
334
+ if value == "" {
335
+ return 0
355
336
  }
356
-
357
- // Try to extract a structured error code from the response body
358
- var envelope struct {
359
- Code string `json:"code"`
360
- Message string `json:"message"`
361
- Error string `json:"error"`
337
+ if seconds, err := strconv.ParseFloat(value, 64); err == nil && seconds >= 0 {
338
+ delay := time.Duration(seconds * float64(time.Second))
339
+ if delay > 30*time.Second {
340
+ return 30 * time.Second
341
+ }
342
+ return delay
362
343
  }
363
- if err := json.Unmarshal(body, &envelope); err == nil {
364
- if envelope.Code != "" {
365
- sdkErr.Code = envelope.Code
344
+ if when, err := http.ParseTime(value); err == nil {
345
+ delay := time.Until(when)
346
+ if delay > 30*time.Second {
347
+ return 30 * time.Second
366
348
  }
367
- if envelope.Message != "" {
368
- sdkErr.Message = envelope.Message
369
- } else if envelope.Error != "" {
370
- sdkErr.Message = envelope.Error
349
+ if delay > 0 {
350
+ return delay
371
351
  }
372
352
  }
353
+ return 0
354
+ }
373
355
 
356
+ func parseErrorResponse(resp *http.Response, body []byte) error {
357
+ requestID := resp.Header.Get("X-Request-ID")
358
+ if requestID == "" {
359
+ requestID = resp.Header.Get("Request-ID")
360
+ }
361
+ sdkErr := &SDKError{
362
+ Status: resp.StatusCode, RequestID: requestID,
363
+ Code: "http_error", Message: strings.TrimSpace(string(body)),
364
+ }
365
+ var envelope struct {
366
+ Code string `json:"code"`
367
+ Message string `json:"message"`
368
+ Error string `json:"error"`
369
+ RequestID string `json:"request_id"`
370
+ }
371
+ if err := json.Unmarshal(body, &envelope); err == nil {
372
+ if envelope.Code != "" { sdkErr.Code = envelope.Code } else if envelope.Error != "" { sdkErr.Code = envelope.Error }
373
+ if envelope.Message != "" { sdkErr.Message = envelope.Message }
374
+ if envelope.RequestID != "" { sdkErr.RequestID = envelope.RequestID }
375
+ }
374
376
  if sdkErr.Message == "" {
375
377
  sdkErr.Message = fmt.Sprintf("HTTP %d: %s", resp.StatusCode, resp.Status)
376
378
  }
377
-
378
379
  return sdkErr
379
380
  }
@@ -1,69 +1,35 @@
1
1
  package {{pkgName}}
2
2
 
3
- {{#if schemas}}
4
- import (
5
- "time"
6
- )
3
+ {{#if needsTime}}
4
+ import "time"
7
5
  {{/if}}
8
6
 
9
7
  {{#each schemas}}
10
- {{#if (neq ownership.kind "external")}}
11
- {{#if isEnum}}
12
- // {{name}} {{#if description}}{{description}}{{/if}}
13
- type {{name}} string
8
+ {{#if external}}
9
+ // {{name}} is supplied by an external schema package in the source manifest.
10
+ type {{name}} = interface{}
11
+
12
+ {{else if isEnum}}
13
+ // {{name}}{{#if description}} {{description}}{{/if}}
14
+ type {{name}} {{enumType}}
14
15
 
15
16
  const (
16
17
  {{#each enumValues}}
17
- {{pascalCase ../name}}{{pascalCase this}} {{../name}} = "{{this}}"
18
+ {{pascalCase ../name}}{{pascalCase name}} {{../name}} = {{{value}}}
18
19
  {{/each}}
19
20
  )
20
21
 
21
22
  {{else if isUnionType}}
22
- // {{name}} {{#if description}}{{description}}{{/if}}
23
- type {{name}} struct {
24
- value interface{}
25
- }
26
-
27
- {{#each unionMembers}}
28
- // New{{../name}}From{{this}} creates a {{../name}} from a {{this}} value.
29
- func New{{../name}}From{{this}}(v {{this}}) {{../name}} {
30
- return {{../name}}{value: v}
31
- }
32
- {{/each}}
33
-
34
- // Value returns the underlying value.
35
- func (u {{name}}) Value() interface{} {
36
- return u.value
37
- }
38
-
39
- // UnmarshalJSON implements json.Unmarshaler for {{name}}.
40
- func (u *{{name}}) UnmarshalJSON(data []byte) error {
41
- {{#each unionMembers}}
42
- var v{{@index}} {{this}}
43
- if err := json.Unmarshal(data, &v{{@index}}); err == nil {
44
- u.value = v{{@index}}
45
- return nil
46
- }
47
- {{/each}}
48
- return fmt.Errorf("unable to unmarshal {{name}} from %s", string(data))
49
- }
50
-
51
- // MarshalJSON implements json.Marshaler for {{name}}.
52
- func (u {{name}}) MarshalJSON() ([]byte, error) {
53
- return json.Marshal(u.value)
54
- }
23
+ // {{name}}{{#if description}} {{description}}{{/if}}
24
+ type {{name}} interface{}
55
25
 
56
26
  {{else if isIntersectionType}}
57
- // {{name}} {{#if description}}{{description}}{{/if}}
58
- type {{name}} struct {
59
- {{#each intersectionMembers}}
60
- {{this}}
61
- {{/each}}
62
- }
27
+ // {{name}}{{#if description}} {{description}}{{/if}}
28
+ type {{name}} map[string]interface{}
63
29
 
64
30
  {{else if isTypeAlias}}
65
- // {{name}} {{#if description}}{{description}}{{/if}}
66
- type {{name}} = {{additionalPropertiesType}}
31
+ // {{name}}{{#if description}} {{description}}{{/if}}
32
+ type {{name}} = {{aliasType}}
67
33
 
68
34
  {{else}}
69
35
  {{#if description}}
@@ -71,10 +37,8 @@ type {{name}} = {{additionalPropertiesType}}
71
37
  {{/if}}
72
38
  type {{name}} struct {
73
39
  {{#each properties}}
74
- {{pascalCase name}} {{#if (or nullable (not required))}}*{{/if}}{{#if (eq type "string")}}{{#if (eq format "date-time")}}time.Time{{else}}string{{/if}}{{else if (eq type "integer")}}int{{else if (eq type "number")}}float64{{else if (eq type "boolean")}}bool{{else}}{{goPropertyType type}}{{/if}} `json:"{{name}}{{#if (or nullable (not required))}},omitempty{{/if}}"`{{#if description}} // {{description}}{{/if}}
40
+ {{pascalCase name}} {{#if optional}}*{{/if}}{{targetType}} `json:"{{name}}{{#if optional}},omitempty{{/if}}"`{{#if description}} // {{description}}{{/if}}
75
41
  {{/each}}
76
42
  }
77
- {{/if}}
78
-
79
43
  {{/if}}
80
44
  {{/each}}
@@ -11,9 +11,10 @@ import (
11
11
  // replacements is pairs of (placeholder, value).
12
12
  func buildPath(template string, replacements ...string) string {
13
13
  for i := 0; i < len(replacements); i += 2 {
14
- placeholder := ":" + replacements[i]
14
+ name := replacements[i]
15
15
  value := url.PathEscape(replacements[i+1])
16
- template = strings.Replace(template, placeholder, value, 1)
16
+ template = strings.Replace(template, ":"+name, value, 1)
17
+ template = strings.Replace(template, "${"+name+"}", value, 1)
17
18
  }
18
19
  return template
19
20
  }
@@ -37,7 +37,7 @@ dependencies {
37
37
  }
38
38
 
39
39
  kotlin {
40
- jvmToolchain(25)
40
+ jvmToolchain(17)
41
41
  }
42
42
 
43
43
  tasks.test {