@workos/oagen-emitters 0.2.1 → 0.4.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 (136) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/.release-please-manifest.json +1 -1
  3. package/CHANGELOG.md +15 -0
  4. package/README.md +129 -0
  5. package/dist/index.d.mts +13 -1
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +14549 -3385
  8. package/dist/index.mjs.map +1 -1
  9. package/docs/sdk-architecture/dotnet.md +336 -0
  10. package/docs/sdk-architecture/go.md +338 -0
  11. package/docs/sdk-architecture/php.md +315 -0
  12. package/docs/sdk-architecture/python.md +511 -0
  13. package/oagen.config.ts +328 -2
  14. package/package.json +9 -5
  15. package/scripts/generate-php.js +13 -0
  16. package/scripts/git-push-with-published-oagen.sh +21 -0
  17. package/smoke/sdk-dotnet.ts +45 -12
  18. package/smoke/sdk-go.ts +116 -42
  19. package/smoke/sdk-php.ts +28 -26
  20. package/smoke/sdk-python.ts +5 -2
  21. package/src/dotnet/client.ts +89 -0
  22. package/src/dotnet/enums.ts +323 -0
  23. package/src/dotnet/fixtures.ts +236 -0
  24. package/src/dotnet/index.ts +246 -0
  25. package/src/dotnet/manifest.ts +36 -0
  26. package/src/dotnet/models.ts +344 -0
  27. package/src/dotnet/naming.ts +330 -0
  28. package/src/dotnet/resources.ts +622 -0
  29. package/src/dotnet/tests.ts +693 -0
  30. package/src/dotnet/type-map.ts +201 -0
  31. package/src/dotnet/wrappers.ts +186 -0
  32. package/src/go/client.ts +141 -0
  33. package/src/go/enums.ts +196 -0
  34. package/src/go/fixtures.ts +212 -0
  35. package/src/go/index.ts +84 -0
  36. package/src/go/manifest.ts +36 -0
  37. package/src/go/models.ts +254 -0
  38. package/src/go/naming.ts +179 -0
  39. package/src/go/resources.ts +827 -0
  40. package/src/go/tests.ts +751 -0
  41. package/src/go/type-map.ts +82 -0
  42. package/src/go/wrappers.ts +261 -0
  43. package/src/index.ts +4 -0
  44. package/src/kotlin/client.ts +53 -0
  45. package/src/kotlin/enums.ts +162 -0
  46. package/src/kotlin/index.ts +92 -0
  47. package/src/kotlin/manifest.ts +55 -0
  48. package/src/kotlin/models.ts +395 -0
  49. package/src/kotlin/naming.ts +223 -0
  50. package/src/kotlin/overrides.ts +25 -0
  51. package/src/kotlin/resources.ts +667 -0
  52. package/src/kotlin/tests.ts +1019 -0
  53. package/src/kotlin/type-map.ts +123 -0
  54. package/src/kotlin/wrappers.ts +168 -0
  55. package/src/node/client.ts +128 -115
  56. package/src/node/enums.ts +9 -0
  57. package/src/node/errors.ts +37 -232
  58. package/src/node/field-plan.ts +726 -0
  59. package/src/node/fixtures.ts +9 -1
  60. package/src/node/index.ts +3 -9
  61. package/src/node/models.ts +178 -21
  62. package/src/node/naming.ts +49 -111
  63. package/src/node/resources.ts +527 -397
  64. package/src/node/sdk-errors.ts +41 -0
  65. package/src/node/tests.ts +69 -19
  66. package/src/node/type-map.ts +4 -2
  67. package/src/node/utils.ts +13 -71
  68. package/src/node/wrappers.ts +151 -0
  69. package/src/php/client.ts +179 -0
  70. package/src/php/enums.ts +67 -0
  71. package/src/php/errors.ts +9 -0
  72. package/src/php/fixtures.ts +181 -0
  73. package/src/php/index.ts +96 -0
  74. package/src/php/manifest.ts +36 -0
  75. package/src/php/models.ts +310 -0
  76. package/src/php/naming.ts +279 -0
  77. package/src/php/resources.ts +636 -0
  78. package/src/php/tests.ts +609 -0
  79. package/src/php/type-map.ts +90 -0
  80. package/src/php/utils.ts +18 -0
  81. package/src/php/wrappers.ts +152 -0
  82. package/src/python/client.ts +345 -0
  83. package/src/python/enums.ts +313 -0
  84. package/src/python/fixtures.ts +196 -0
  85. package/src/python/index.ts +95 -0
  86. package/src/python/manifest.ts +38 -0
  87. package/src/python/models.ts +688 -0
  88. package/src/python/naming.ts +189 -0
  89. package/src/python/resources.ts +1322 -0
  90. package/src/python/tests.ts +1335 -0
  91. package/src/python/type-map.ts +93 -0
  92. package/src/python/wrappers.ts +191 -0
  93. package/src/shared/model-utils.ts +472 -0
  94. package/src/shared/naming-utils.ts +154 -0
  95. package/src/shared/non-spec-services.ts +54 -0
  96. package/src/shared/resolved-ops.ts +109 -0
  97. package/src/shared/wrapper-utils.ts +70 -0
  98. package/test/dotnet/client.test.ts +121 -0
  99. package/test/dotnet/enums.test.ts +193 -0
  100. package/test/dotnet/errors.test.ts +9 -0
  101. package/test/dotnet/manifest.test.ts +82 -0
  102. package/test/dotnet/models.test.ts +260 -0
  103. package/test/dotnet/resources.test.ts +255 -0
  104. package/test/dotnet/tests.test.ts +202 -0
  105. package/test/go/client.test.ts +92 -0
  106. package/test/go/enums.test.ts +132 -0
  107. package/test/go/errors.test.ts +9 -0
  108. package/test/go/models.test.ts +265 -0
  109. package/test/go/resources.test.ts +408 -0
  110. package/test/go/tests.test.ts +143 -0
  111. package/test/kotlin/models.test.ts +135 -0
  112. package/test/kotlin/tests.test.ts +176 -0
  113. package/test/node/client.test.ts +92 -12
  114. package/test/node/enums.test.ts +2 -0
  115. package/test/node/errors.test.ts +2 -41
  116. package/test/node/models.test.ts +2 -0
  117. package/test/node/naming.test.ts +23 -0
  118. package/test/node/resources.test.ts +315 -84
  119. package/test/node/serializers.test.ts +3 -1
  120. package/test/node/type-map.test.ts +11 -0
  121. package/test/php/client.test.ts +95 -0
  122. package/test/php/enums.test.ts +173 -0
  123. package/test/php/errors.test.ts +9 -0
  124. package/test/php/models.test.ts +497 -0
  125. package/test/php/resources.test.ts +682 -0
  126. package/test/php/tests.test.ts +185 -0
  127. package/test/python/client.test.ts +200 -0
  128. package/test/python/enums.test.ts +228 -0
  129. package/test/python/errors.test.ts +16 -0
  130. package/test/python/manifest.test.ts +74 -0
  131. package/test/python/models.test.ts +716 -0
  132. package/test/python/resources.test.ts +617 -0
  133. package/test/python/tests.test.ts +202 -0
  134. package/src/node/common.ts +0 -273
  135. package/src/node/config.ts +0 -71
  136. package/src/node/serializers.ts +0 -746
@@ -0,0 +1,336 @@
1
+ # C# / .NET SDK Architecture
2
+
3
+ Target: .NET 8.0 | Serialization: Newtonsoft.Json | Test: xUnit + Moq
4
+
5
+ ## Architecture Overview
6
+
7
+ The SDK follows a service-per-domain pattern. A static `WorkOS` entry point holds a singleton `WorkOSClient`. Each service (e.g., `OrganizationsService`) inherits from a shared `Service` base class that lazily resolves the client. All IO is async with `CancellationToken` support.
8
+
9
+ **Runtime files (hand-maintained, `@oagen-ignore-file`):**
10
+
11
+ - `WorkOS.cs` — static entry point
12
+ - `Client/WorkOSClient.cs` — HTTP execution, retry, error translation
13
+ - `Client/_interfaces/WorkOSOptions.cs` — client config
14
+ - `Client/_interfaces/WorkOSRequest.cs` — request DTO
15
+ - `Client/Utilities/RequestUtilities.cs` — JSON/query serialization
16
+ - `Services/Webhooks/WebhookService.cs` — webhook signature verification helper
17
+ - `Services/Webhooks/Entities/Webhook.cs` — webhook event envelope used by the helper
18
+ - `Services/Webhooks/Exceptions/WorkOSWebhookException.cs` — webhook verification exception
19
+ - `Services/_common/Service.cs` — base service class
20
+ - `Services/_common/_interfaces/BaseOptions.cs` — marker base for options
21
+ - `Services/_common/_interfaces/ListOptions.cs` — pagination base options
22
+ - `Services/_common/Entities/WorkOSList.cs` — pagination wrapper
23
+ - `Services/_common/Entities/ListMetadata.cs` — cursor metadata
24
+ - `Services/_common/Enums/PaginationOrder.cs` — asc/desc enum
25
+
26
+ **Generated files (emitter output):**
27
+
28
+ - `Services/{Mount}/Entities/*.cs` — model classes
29
+ - `Services/{Mount}/Enums/*.cs` — enum types
30
+ - `Services/{Mount}/{Mount}Service.cs` — service class with methods
31
+ - `Services/{Mount}/_interfaces/*Options.cs` — request option classes
32
+ - `test/WorkOSTests/Tests/*Test.cs` — generated service tests
33
+ - `test/WorkOSTests/testdata/*.json` — generated fixtures
34
+ - `test/WorkOSTests/xunit.runner.json` — generated xUnit runner config
35
+
36
+ ## Naming Conventions
37
+
38
+ | IR Concept | C# Convention | Example |
39
+ | -------------- | ---------------------------- | --------------------------- |
40
+ | Model name | PascalCase | `Organization` |
41
+ | Enum name | PascalCase | `ConnectionState` |
42
+ | Field/property | PascalCase | `EmailVerified` |
43
+ | Method | PascalCase (no Async suffix) | `GetOrganization` |
44
+ | File | PascalCase.cs | `Organization.cs` |
45
+ | Service class | `{Mount}Service` | `OrganizationsService` |
46
+ | Options class | `{Action}{Entity}Options` | `CreateOrganizationOptions` |
47
+ | Namespace | `WorkOS` | — |
48
+
49
+ ## Type Mapping
50
+
51
+ | IR TypeRef | C# Type |
52
+ | ------------------------------ | ----------------------- |
53
+ | `primitive:string` | `string` |
54
+ | `primitive:string` (date-time) | `string` |
55
+ | `primitive:string` (uuid) | `string` |
56
+ | `primitive:string` (binary) | `byte[]` |
57
+ | `primitive:integer` | `int` |
58
+ | `primitive:integer` (int64) | `long` |
59
+ | `primitive:number` | `double` |
60
+ | `primitive:boolean` | `bool` |
61
+ | `primitive:unknown` | `object` |
62
+ | `model:Foo` | `Foo` (reference type) |
63
+ | `enum:Foo` | `Foo` (value type) |
64
+ | `array` | `List<T>` |
65
+ | `map` | `Dictionary<string, T>` |
66
+ | `nullable` (value type) | `T?` |
67
+ | `nullable` (reference type) | `T` |
68
+ | `union` (single) | that type |
69
+ | `union` (multiple) | `object` |
70
+
71
+ ## Model Pattern
72
+
73
+ ```csharp
74
+ namespace WorkOS
75
+ {
76
+ using Newtonsoft.Json;
77
+
78
+ /// <summary>Represents an organization.</summary>
79
+ public class Organization
80
+ {
81
+ [JsonProperty("id")]
82
+ public string Id { get; set; }
83
+
84
+ [JsonProperty("name")]
85
+ public string Name { get; set; }
86
+
87
+ [JsonProperty("allow_profiles_outside_organization")]
88
+ public bool AllowProfilesOutsideOrganization { get; set; }
89
+
90
+ [JsonProperty("created_at")]
91
+ public string CreatedAt { get; set; }
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## Enum Pattern
97
+
98
+ ```csharp
99
+ namespace WorkOS
100
+ {
101
+ using System.Runtime.Serialization;
102
+ using Newtonsoft.Json;
103
+ using Newtonsoft.Json.Converters;
104
+
105
+ [JsonConverter(typeof(StringEnumConverter))]
106
+ public enum OrganizationDomainState
107
+ {
108
+ [EnumMember(Value = "failed")]
109
+ Failed,
110
+
111
+ [EnumMember(Value = "pending")]
112
+ Pending,
113
+
114
+ [EnumMember(Value = "verified")]
115
+ Verified,
116
+ }
117
+ }
118
+ ```
119
+
120
+ ## Resource/Service Pattern
121
+
122
+ ```csharp
123
+ namespace WorkOS
124
+ {
125
+ using System.Net.Http;
126
+ using System.Threading;
127
+ using System.Threading.Tasks;
128
+
129
+ public class OrganizationsService : Service
130
+ {
131
+ public OrganizationsService() { }
132
+ public OrganizationsService(WorkOSClient client) : base(client) { }
133
+
134
+ public async Task<Organization> GetOrganization(
135
+ string id,
136
+ CancellationToken cancellationToken = default)
137
+ {
138
+ var request = new WorkOSRequest
139
+ {
140
+ Method = HttpMethod.Get,
141
+ Path = $"/organizations/{id}",
142
+ };
143
+ return await this.Client.MakeAPIRequest<Organization>(request, cancellationToken);
144
+ }
145
+
146
+ public async Task<WorkOSList<Organization>> ListOrganizations(
147
+ ListOrganizationsOptions options = null,
148
+ CancellationToken cancellationToken = default)
149
+ {
150
+ var request = new WorkOSRequest
151
+ {
152
+ Method = HttpMethod.Get,
153
+ Path = "/organizations",
154
+ Options = options,
155
+ };
156
+ return await this.Client.MakeAPIRequest<WorkOSList<Organization>>(request, cancellationToken);
157
+ }
158
+
159
+ public async Task<Organization> CreateOrganization(
160
+ CreateOrganizationOptions options,
161
+ CancellationToken cancellationToken = default)
162
+ {
163
+ var request = new WorkOSRequest
164
+ {
165
+ Method = HttpMethod.Post,
166
+ Path = "/organizations",
167
+ Options = options,
168
+ };
169
+ return await this.Client.MakeAPIRequest<Organization>(request, cancellationToken);
170
+ }
171
+
172
+ public async Task DeleteOrganization(
173
+ string id,
174
+ CancellationToken cancellationToken = default)
175
+ {
176
+ var request = new WorkOSRequest
177
+ {
178
+ Method = HttpMethod.Delete,
179
+ Path = $"/organizations/{id}",
180
+ };
181
+ await this.Client.MakeRawAPIRequest(request, cancellationToken);
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ ## Options Pattern
188
+
189
+ ```csharp
190
+ namespace WorkOS
191
+ {
192
+ using Newtonsoft.Json;
193
+
194
+ public class CreateOrganizationOptions : BaseOptions
195
+ {
196
+ [JsonProperty("name")]
197
+ public string Name { get; set; }
198
+
199
+ [JsonProperty("domain_data")]
200
+ public List<OrganizationDomainDataOptions> DomainData { get; set; }
201
+ }
202
+
203
+ public class ListOrganizationsOptions : ListOptions
204
+ {
205
+ [JsonProperty("domains")]
206
+ public string[] Domains { get; set; }
207
+ }
208
+ }
209
+ ```
210
+
211
+ ## Pagination Pattern
212
+
213
+ ```csharp
214
+ // Returned by list methods — NOT an iterator
215
+ public class WorkOSList<T>
216
+ {
217
+ [JsonProperty("data")]
218
+ public List<T> Data { get; set; }
219
+
220
+ [JsonProperty("list_metadata")]
221
+ public ListMetadata ListMetadata { get; set; }
222
+ }
223
+ ```
224
+
225
+ ## Error Handling
226
+
227
+ The runtime translates HTTP status codes to SDK-native exceptions:
228
+
229
+ - `AuthenticationError` (401)
230
+ - `NotFoundError` (404)
231
+ - `UnprocessableEntityError` (422)
232
+ - `RateLimitExceededError` (429)
233
+ - `ServerError` (500+)
234
+
235
+ These are hand-maintained in the runtime. The emitter generates error-handling _tests_, not the error classes themselves.
236
+
237
+ ## Hidden Parameter Injection
238
+
239
+ For operations with defaults/inferFromClient, the service method sets properties on the options before making the request:
240
+
241
+ ```csharp
242
+ public async Task<AuthenticationResponse> AuthenticateWithPassword(
243
+ AuthenticateWithPasswordOptions options,
244
+ CancellationToken cancellationToken = default)
245
+ {
246
+ options.GrantType = "password";
247
+ options.ClientId = this.Client.ClientId;
248
+ options.ClientSecret = this.Client.ApiKey;
249
+ var request = new WorkOSRequest
250
+ {
251
+ Method = HttpMethod.Post,
252
+ Path = "/user_management/authenticate",
253
+ Options = options,
254
+ };
255
+ return await this.Client.MakeAPIRequest<AuthenticationResponse>(request, cancellationToken);
256
+ }
257
+ ```
258
+
259
+ ## Testing Pattern
260
+
261
+ xUnit + Moq with HttpMock utility:
262
+
263
+ ```csharp
264
+ public class OrganizationsServiceTest
265
+ {
266
+ private readonly HttpMock httpMock;
267
+ private readonly OrganizationsService service;
268
+
269
+ public OrganizationsServiceTest()
270
+ {
271
+ this.httpMock = new HttpMock();
272
+ var client = new WorkOSClient(new WorkOSOptions
273
+ {
274
+ ApiKey = "sk_test",
275
+ HttpClient = this.httpMock.HttpClient,
276
+ });
277
+ this.service = new OrganizationsService(client);
278
+ }
279
+
280
+ [Fact]
281
+ public async Task TestGetOrganization()
282
+ {
283
+ var fixture = File.ReadAllText("testdata/organization.json");
284
+ this.httpMock.MockResponse(HttpMethod.Get, "/organizations/org_01234", HttpStatusCode.OK, fixture);
285
+ var result = await this.service.GetOrganization("org_01234");
286
+ Assert.NotNull(result);
287
+ Assert.Equal("org_01234", result.Id);
288
+ this.httpMock.AssertRequestWasMade(HttpMethod.Get, "/organizations/org_01234");
289
+ }
290
+ }
291
+ ```
292
+
293
+ ## Structural Guidelines
294
+
295
+ | Category | Choice |
296
+ | ------------------ | --------------------------- |
297
+ | Target Framework | .NET 8.0 |
298
+ | HTTP Client | System.Net.Http.HttpClient |
299
+ | JSON Parsing | Newtonsoft.Json 13.x |
300
+ | Testing Framework | xUnit 2.x |
301
+ | HTTP Mocking | Moq 4.x (HttpClientHandler) |
302
+ | Linting/Formatting | StyleCop.Analyzers |
303
+ | Package Manager | NuGet |
304
+ | Build Tool | dotnet CLI / MSBuild |
305
+
306
+ ## Directory Structure
307
+
308
+ ```
309
+ src/WorkOS.net/
310
+ ├── WorkOS.cs # @oagen-ignore-file
311
+ ├── Client/ # @oagen-ignore-file (all)
312
+ │ ├── WorkOSClient.cs
313
+ │ ├── _interfaces/
314
+ │ └── Utilities/
315
+ ├── Services/
316
+ │ ├── _common/ # @oagen-ignore-file (all)
317
+ │ ├── Webhooks/ # Mixed hand-written + generated
318
+ │ │ ├── Entities/Webhook.cs # @oagen-ignore-file
319
+ │ │ ├── Exceptions/WorkOSWebhookException.cs
320
+ │ │ ├── WebhookService.cs # @oagen-ignore-file
321
+ │ │ └── WebhooksService.cs # Generated
322
+ │ └── {ServiceName}/ # Generated
323
+ │ ├── {ServiceName}Service.cs
324
+ │ ├── _interfaces/
325
+ │ │ └── {Action}{Entity}Options.cs
326
+ │ ├── Entities/
327
+ │ │ └── {Entity}.cs
328
+ │ └── Enums/
329
+ │ └── {EnumName}.cs
330
+ test/WorkOSTests/
331
+ ├── Utilities/HttpMock.cs # @oagen-ignore-file
332
+ ├── Client/ # @oagen-ignore-file
333
+ ├── Services/Webhooks/WebhookTests.cs # @oagen-ignore-file
334
+ ├── Tests/{ServiceName}Test.cs # Generated
335
+ └── xunit.runner.json # Generated
336
+ ```
@@ -0,0 +1,338 @@
1
+ # Go SDK Architecture
2
+
3
+ Scenario B (fresh) -- no backwards-compatibility constraints.
4
+ Reference: stripe/stripe-go for idiomatic Go patterns.
5
+
6
+ ## Architecture Overview
7
+
8
+ Single flat `workos` package. All types, services, and the client live in one package,
9
+ accessed as `workos.Organization`, `workos.NewClient(...)`, etc.
10
+
11
+ - **Client**: Instance-scoped via `NewClient(apiKey, ...Option)`
12
+ - **Services**: Unexported structs with exported methods, accessed as fields on Client
13
+ - **Models**: Exported structs with `json:"snake_case"` tags
14
+ - **Enums**: Typed `string` constants (no iota)
15
+ - **Params**: `*Params` structs with embedded `RequestOptions` for per-request overrides
16
+ - **Errors**: SDK-native error types implementing `error` interface
17
+ - **Pagination**: Iterator with `Next()` / `Current()` / `Err()`
18
+
19
+ ## Naming Conventions
20
+
21
+ | IR Name | Go Name | Context |
22
+ | -------------------------- | --------------------- | ---------------------------------------- |
23
+ | `Organization` (model) | `Organization` | Struct type |
24
+ | `organization` (file) | `organization.go` | File name |
25
+ | `listUsers` (method) | `ListUsers` | Exported method |
26
+ | `user_id` (field) | `UserID` | Struct field (PascalCase, acronym-aware) |
27
+ | `Status` (enum) | `Status` | Type declaration |
28
+ | `active` (enum value) | `StatusActive` | Const: `{TypeName}{PascalValue}` |
29
+ | `Organizations` (service) | `organizationService` | Unexported struct |
30
+ | `organizations` (accessor) | `Organizations()` | Client method returning service |
31
+
32
+ ### Acronym handling
33
+
34
+ Go convention preserves full-caps for common acronyms: `ID`, `URL`, `SSO`, `API`, `HTTP`,
35
+ `JWT`, `MFA`, `CORS`, `SAML`, `SCIM`, `RBAC`, `OAuth`, `OIDC`, `UUID`, `JSON`, `HTML`.
36
+
37
+ ## Type Mapping
38
+
39
+ | IR TypeRef | Go Type |
40
+ | ---------------------------- | --------------------------------- |
41
+ | `primitive:string` | `string` |
42
+ | `primitive:string:date` | `string` |
43
+ | `primitive:string:date-time` | `string` |
44
+ | `primitive:string:uuid` | `string` |
45
+ | `primitive:string:binary` | `[]byte` |
46
+ | `primitive:integer` | `int` |
47
+ | `primitive:number` | `float64` |
48
+ | `primitive:boolean` | `bool` |
49
+ | `primitive:unknown` | `interface{}` |
50
+ | `array` | `[]T` |
51
+ | `model` | `*ModelName` (pointer for nested) |
52
+ | `enum` | `EnumType` |
53
+ | `nullable` | `*T` (pointer) |
54
+ | `union` | `interface{}` |
55
+ | `map` | `map[string]T` |
56
+ | `literal:string` | `string` |
57
+ | `literal:null` | `interface{}` |
58
+
59
+ ## Model Pattern
60
+
61
+ ```go
62
+ // Organization represents an organization.
63
+ type Organization struct {
64
+ ID string `json:"id"`
65
+ Name string `json:"name"`
66
+ Domains []string `json:"domains"`
67
+ Metadata map[string]string `json:"metadata,omitempty"`
68
+ CreatedAt string `json:"created_at"`
69
+ UpdatedAt string `json:"updated_at"`
70
+ }
71
+ ```
72
+
73
+ - Required fields: value types (no pointer)
74
+ - Optional fields: pointer types with `omitempty`
75
+ - Nested models: always pointers
76
+ - JSON tags: snake_case matching the API wire format
77
+
78
+ ## Enum Pattern
79
+
80
+ ```go
81
+ // OrganizationStatus represents the status of an organization.
82
+ type OrganizationStatus string
83
+
84
+ const (
85
+ OrganizationStatusActive OrganizationStatus = "active"
86
+ OrganizationStatusInactive OrganizationStatus = "inactive"
87
+ )
88
+ ```
89
+
90
+ ## Resource/Service Pattern
91
+
92
+ ```go
93
+ type organizationService struct {
94
+ client *Client
95
+ }
96
+
97
+ // ListOrganizations lists all organizations.
98
+ func (s *organizationService) ListOrganizations(
99
+ ctx context.Context,
100
+ params *ListOrganizationsParams,
101
+ opts ...RequestOption,
102
+ ) *Iterator[Organization] {
103
+ // ...
104
+ }
105
+
106
+ // GetOrganization retrieves an organization by ID.
107
+ func (s *organizationService) GetOrganization(
108
+ ctx context.Context,
109
+ id string,
110
+ opts ...RequestOption,
111
+ ) (*Organization, error) {
112
+ // ...
113
+ }
114
+
115
+ // CreateOrganization creates a new organization.
116
+ func (s *organizationService) CreateOrganization(
117
+ ctx context.Context,
118
+ params *CreateOrganizationParams,
119
+ opts ...RequestOption,
120
+ ) (*Organization, error) {
121
+ // ...
122
+ }
123
+
124
+ // DeleteOrganization deletes an organization by ID.
125
+ func (s *organizationService) DeleteOrganization(
126
+ ctx context.Context,
127
+ id string,
128
+ opts ...RequestOption,
129
+ ) error {
130
+ // ...
131
+ }
132
+ ```
133
+
134
+ ### Parameter Structs
135
+
136
+ ```go
137
+ type CreateOrganizationParams struct {
138
+ Name string `json:"name"`
139
+ Domains []string `json:"domains,omitempty"`
140
+ Metadata map[string]string `json:"metadata,omitempty"`
141
+ }
142
+
143
+ type ListOrganizationsParams struct {
144
+ After *string `url:"after,omitempty"`
145
+ Before *string `url:"before,omitempty"`
146
+ Limit *int `url:"limit,omitempty"`
147
+ }
148
+ ```
149
+
150
+ ### Path parameters
151
+
152
+ Path parameters (IDs etc.) are positional function arguments, not part of params structs.
153
+
154
+ ## Client Pattern
155
+
156
+ ```go
157
+ // Client is the WorkOS API client.
158
+ type Client struct {
159
+ apiKey string
160
+ baseURL string
161
+ httpClient *http.Client
162
+ maxRetries int
163
+ // Service accessors
164
+ organizations *organizationService
165
+ users *userService
166
+ // ...
167
+ }
168
+
169
+ // NewClient creates a new WorkOS client.
170
+ func NewClient(apiKey string, opts ...ClientOption) *Client {
171
+ c := &Client{
172
+ apiKey: apiKey,
173
+ baseURL: "https://api.workos.com",
174
+ httpClient: &http.Client{Timeout: 60 * time.Second},
175
+ maxRetries: 3,
176
+ }
177
+ for _, opt := range opts {
178
+ opt(c)
179
+ }
180
+ c.organizations = &organizationService{client: c}
181
+ c.users = &userService{client: c}
182
+ return c
183
+ }
184
+
185
+ // Organizations returns the organizations service.
186
+ func (c *Client) Organizations() *organizationService {
187
+ return c.organizations
188
+ }
189
+ ```
190
+
191
+ ### Functional Options
192
+
193
+ ```go
194
+ type ClientOption func(*Client)
195
+
196
+ func WithBaseURL(url string) ClientOption {
197
+ return func(c *Client) { c.baseURL = url }
198
+ }
199
+
200
+ func WithHTTPClient(client *http.Client) ClientOption {
201
+ return func(c *Client) { c.httpClient = client }
202
+ }
203
+
204
+ func WithMaxRetries(n int) ClientOption {
205
+ return func(c *Client) { c.maxRetries = n }
206
+ }
207
+ ```
208
+
209
+ ### Per-Request Options
210
+
211
+ ```go
212
+ type RequestOption func(*requestConfig)
213
+
214
+ type requestConfig struct {
215
+ extraHeaders http.Header
216
+ timeout time.Duration
217
+ maxRetries *int
218
+ baseURL string
219
+ idempotencyKey string
220
+ }
221
+
222
+ func WithExtraHeaders(h http.Header) RequestOption {
223
+ return func(r *requestConfig) { r.extraHeaders = h }
224
+ }
225
+
226
+ func WithTimeout(d time.Duration) RequestOption {
227
+ return func(r *requestConfig) { r.timeout = d }
228
+ }
229
+
230
+ func WithIdempotencyKey(key string) RequestOption {
231
+ return func(r *requestConfig) { r.idempotencyKey = key }
232
+ }
233
+ ```
234
+
235
+ ## Pagination Pattern
236
+
237
+ ```go
238
+ // Iterator provides auto-pagination over list endpoints.
239
+ type Iterator[T any] struct {
240
+ cur *T
241
+ items []T
242
+ err error
243
+ params listParams
244
+ fetcher func(context.Context, listParams) (*listResponse[T], error)
245
+ ctx context.Context
246
+ done bool
247
+ }
248
+
249
+ // Next advances the iterator. Returns false when done or on error.
250
+ func (it *Iterator[T]) Next() bool { ... }
251
+
252
+ // Current returns the current item.
253
+ func (it *Iterator[T]) Current() *T { return it.cur }
254
+
255
+ // Err returns any error from the last page fetch.
256
+ func (it *Iterator[T]) Err() error { return it.err }
257
+ ```
258
+
259
+ ## Error Handling
260
+
261
+ ```go
262
+ type APIError struct {
263
+ StatusCode int `json:"-"`
264
+ RequestID string `json:"-"`
265
+ Code string `json:"code"`
266
+ Message string `json:"message"`
267
+ }
268
+
269
+ func (e *APIError) Error() string {
270
+ return fmt.Sprintf("workos: %d %s: %s", e.StatusCode, e.Code, e.Message)
271
+ }
272
+
273
+ // Sentinel types for errors.Is() / type assertions
274
+ type AuthenticationError struct{ *APIError }
275
+ type NotFoundError struct{ *APIError }
276
+ type RateLimitExceededError struct{ *APIError }
277
+ type UnprocessableEntityError struct{ *APIError }
278
+ type ServerError struct{ *APIError }
279
+ ```
280
+
281
+ ## Retry Logic
282
+
283
+ - Exponential backoff with jitter: `min(base * 2^attempt + jitter, maxDelay)`
284
+ - Retryable statuses: 429, 500, 502, 503, 504
285
+ - Respect `Retry-After` header
286
+ - Auto-generate UUID idempotency key for POST requests, reused across retries
287
+ - Default max retries: 3
288
+
289
+ ## Test Pattern
290
+
291
+ ```go
292
+ func TestOrganizations_GetOrganization(t *testing.T) {
293
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
294
+ require.Equal(t, http.MethodGet, r.Method)
295
+ require.Equal(t, "/organizations/org_123", r.URL.Path)
296
+ w.Header().Set("Content-Type", "application/json")
297
+ w.WriteHeader(http.StatusOK)
298
+ fixture, _ := os.ReadFile("testdata/organization.json")
299
+ w.Write(fixture)
300
+ }))
301
+ defer server.Close()
302
+
303
+ client := workos.NewClient("sk_test", workos.WithBaseURL(server.URL))
304
+ org, err := client.Organizations().GetOrganization(context.Background(), "org_123")
305
+ require.NoError(t, err)
306
+ require.Equal(t, "org_123", org.ID)
307
+ }
308
+ ```
309
+
310
+ ## Structural Guidelines
311
+
312
+ | Category | Choice |
313
+ | ------------------ | ------------------------------------------------- |
314
+ | Testing Framework | `testing` + `github.com/stretchr/testify/require` |
315
+ | HTTP Mocking | `net/http/httptest` |
316
+ | Documentation | GoDoc comments |
317
+ | Type Signatures | Native Go types (inline) |
318
+ | Linting/Formatting | `gofmt` / `go vet` |
319
+ | HTTP Client | `net/http` (stdlib) |
320
+ | JSON Parsing | `encoding/json` (stdlib) |
321
+ | Package Manager | Go modules (`go.mod`) |
322
+ | Build Tool | `go build` / `go test` |
323
+
324
+ ## Directory Structure (generated SDK)
325
+
326
+ ```
327
+ workos-go/
328
+ +-- go.mod
329
+ +-- go.sum
330
+ +-- workos.go // Package doc, NewClient, ClientOption, RequestOption
331
+ +-- client.go // Client struct, HTTP request execution, retry logic
332
+ +-- errors.go // Error types
333
+ +-- pagination.go // Iterator[T]
334
+ +-- {service}.go // Models, enums, params, service client for each service
335
+ +-- {service}_test.go // Tests for each service
336
+ +-- testdata/
337
+ | +-- {model}.json // JSON fixtures
338
+ ```