@workos/oagen-emitters 0.0.1

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 (73) hide show
  1. package/.github/workflows/ci.yml +20 -0
  2. package/.github/workflows/lint-pr-title.yml +16 -0
  3. package/.github/workflows/lint.yml +21 -0
  4. package/.github/workflows/release-please.yml +28 -0
  5. package/.github/workflows/release.yml +32 -0
  6. package/.husky/commit-msg +1 -0
  7. package/.husky/pre-commit +1 -0
  8. package/.husky/pre-push +1 -0
  9. package/.node-version +1 -0
  10. package/.oxfmtrc.json +10 -0
  11. package/.oxlintrc.json +29 -0
  12. package/.vscode/settings.json +11 -0
  13. package/LICENSE.txt +21 -0
  14. package/README.md +123 -0
  15. package/commitlint.config.ts +1 -0
  16. package/dist/index.d.ts +5 -0
  17. package/dist/index.js +2158 -0
  18. package/docs/endpoint-coverage.md +275 -0
  19. package/docs/sdk-architecture/node.md +355 -0
  20. package/oagen.config.ts +51 -0
  21. package/package.json +83 -0
  22. package/renovate.json +26 -0
  23. package/smoke/sdk-dotnet.ts +903 -0
  24. package/smoke/sdk-elixir.ts +771 -0
  25. package/smoke/sdk-go.ts +948 -0
  26. package/smoke/sdk-kotlin.ts +799 -0
  27. package/smoke/sdk-node.ts +516 -0
  28. package/smoke/sdk-php.ts +699 -0
  29. package/smoke/sdk-python.ts +738 -0
  30. package/smoke/sdk-ruby.ts +723 -0
  31. package/smoke/sdk-rust.ts +774 -0
  32. package/src/compat/extractors/dotnet.ts +8 -0
  33. package/src/compat/extractors/elixir.ts +8 -0
  34. package/src/compat/extractors/go.ts +8 -0
  35. package/src/compat/extractors/kotlin.ts +8 -0
  36. package/src/compat/extractors/node.ts +8 -0
  37. package/src/compat/extractors/php.ts +8 -0
  38. package/src/compat/extractors/python.ts +8 -0
  39. package/src/compat/extractors/ruby.ts +8 -0
  40. package/src/compat/extractors/rust.ts +8 -0
  41. package/src/index.ts +1 -0
  42. package/src/node/client.ts +356 -0
  43. package/src/node/common.ts +203 -0
  44. package/src/node/config.ts +70 -0
  45. package/src/node/enums.ts +87 -0
  46. package/src/node/errors.ts +205 -0
  47. package/src/node/fixtures.ts +139 -0
  48. package/src/node/index.ts +57 -0
  49. package/src/node/manifest.ts +23 -0
  50. package/src/node/models.ts +323 -0
  51. package/src/node/naming.ts +96 -0
  52. package/src/node/resources.ts +380 -0
  53. package/src/node/serializers.ts +286 -0
  54. package/src/node/tests.ts +336 -0
  55. package/src/node/type-map.ts +56 -0
  56. package/src/node/utils.ts +164 -0
  57. package/test/compat/extractors/node.test.ts +145 -0
  58. package/test/fixtures/sample-sdk-node/package.json +7 -0
  59. package/test/fixtures/sample-sdk-node/src/client.ts +24 -0
  60. package/test/fixtures/sample-sdk-node/src/index.ts +4 -0
  61. package/test/fixtures/sample-sdk-node/src/models.ts +28 -0
  62. package/test/fixtures/sample-sdk-node/tsconfig.json +13 -0
  63. package/test/node/client.test.ts +165 -0
  64. package/test/node/enums.test.ts +128 -0
  65. package/test/node/errors.test.ts +65 -0
  66. package/test/node/models.test.ts +301 -0
  67. package/test/node/naming.test.ts +212 -0
  68. package/test/node/resources.test.ts +260 -0
  69. package/test/node/serializers.test.ts +206 -0
  70. package/test/node/type-map.test.ts +127 -0
  71. package/tsconfig.json +20 -0
  72. package/tsup.config.ts +8 -0
  73. package/vitest.config.ts +4 -0
@@ -0,0 +1,275 @@
1
+ # Endpoint Coverage: OpenAPI Spec vs Generated SDK vs workos-node
2
+
3
+ Comparison of HTTP verb + path pairs across three sources, with path parameters normalized to `{}`.
4
+
5
+ - **Spec**: OpenAPI spec baseline (from `smoke-results-spec-baseline.json`)
6
+ - **Gen SDK**: Generated SDK in `./sdk` (from `smoke-results-sdk-node.json`)
7
+ - **workos-node**: Live SDK at `/workos-node` (extracted via static analysis of `this.workos.<verb>()` calls)
8
+
9
+ ## Summary
10
+
11
+ | Source | Endpoints |
12
+ | ------------- | --------- |
13
+ | OpenAPI Spec | 154 |
14
+ | Generated SDK | 154 |
15
+ | workos-node | 134 |
16
+ | **In all 3** | **114** |
17
+
18
+ ## Full Comparison
19
+
20
+ | Endpoint | Spec | Gen SDK | workos-node |
21
+ | ------------------------------------------------------------------------------ | :--: | :-----: | :---------: |
22
+ | `DELETE /api_keys/{}` | Y | Y | Y |
23
+ | `DELETE /auth/factors/{}` | Y | Y | Y |
24
+ | `DELETE /authorization/organization_memberships/{}/role_assignments` | Y | Y | Y |
25
+ | `DELETE /authorization/organization_memberships/{}/role_assignments/{}` | Y | Y | Y |
26
+ | `DELETE /authorization/organizations/{}/resources/{}/{}` | Y | Y | Y |
27
+ | `DELETE /authorization/organizations/{}/roles/{}` | Y | Y | Y |
28
+ | `DELETE /authorization/organizations/{}/roles/{}/permissions/{}` | Y | Y | Y |
29
+ | `DELETE /authorization/permissions/{}` | Y | Y | Y |
30
+ | `DELETE /authorization/resources/{}` | Y | Y | Y |
31
+ | `DELETE /connect/applications/{}` | Y | Y | Y |
32
+ | `DELETE /connect/client_secrets/{}` | Y | Y | Y |
33
+ | `DELETE /connections/{}` | Y | Y | Y |
34
+ | `DELETE /directories/{}` | Y | Y | Y |
35
+ | `DELETE /feature-flags/{}/targets/{}` | Y | Y | Y |
36
+ | `DELETE /fga/v1/resources/{}/{}` | | | Y |
37
+ | `DELETE /organization_domains/{}` | Y | Y | Y |
38
+ | `DELETE /organizations/{}` | Y | Y | Y |
39
+ | `DELETE /radar/lists/{}/{}` | Y | Y | Y |
40
+ | `DELETE /user_management/organization_memberships/{}` | Y | Y | Y |
41
+ | `DELETE /user_management/users/{}` | Y | Y | Y |
42
+ | `DELETE /user_management/users/{}/authorized_applications/{}` | Y | Y | |
43
+ | `DELETE /user_management/users/{}/connected_accounts/{}` | Y | Y | |
44
+ | `DELETE /vault/v1/kv/{}` | | | Y |
45
+ | `DELETE /webhook_endpoints/{}` | Y | Y | Y |
46
+ | `GET /audit_logs/actions` | Y | Y | |
47
+ | `GET /audit_logs/actions/{}/schemas` | Y | Y | |
48
+ | `GET /audit_logs/exports/{}` | Y | Y | Y |
49
+ | `GET /auth/factors/{}` | Y | Y | Y |
50
+ | `GET /authorization/organization_memberships/{}/resources` | Y | Y | Y |
51
+ | `GET /authorization/organization_memberships/{}/role_assignments` | Y | Y | Y |
52
+ | `GET /authorization/organizations/{}/resources/{}/{}` | Y | Y | Y |
53
+ | `GET /authorization/organizations/{}/resources/{}/{}/organization_memberships` | Y | Y | Y |
54
+ | `GET /authorization/organizations/{}/roles` | Y | Y | Y |
55
+ | `GET /authorization/organizations/{}/roles/{}` | Y | Y | Y |
56
+ | `GET /authorization/permissions` | Y | Y | Y |
57
+ | `GET /authorization/permissions/{}` | Y | Y | Y |
58
+ | `GET /authorization/resources` | Y | Y | Y |
59
+ | `GET /authorization/resources/{}` | Y | Y | Y |
60
+ | `GET /authorization/resources/{}/organization_memberships` | Y | Y | Y |
61
+ | `GET /authorization/roles` | Y | Y | Y |
62
+ | `GET /authorization/roles/{}` | Y | Y | Y |
63
+ | `GET /connect/applications` | Y | Y | |
64
+ | `GET /connect/applications/{}` | Y | Y | Y |
65
+ | `GET /connect/applications/{}/client_secrets` | Y | Y | Y |
66
+ | `GET /connections` | Y | Y | |
67
+ | `GET /connections/{}` | Y | Y | Y |
68
+ | `GET /directories` | Y | Y | |
69
+ | `GET /directories/{}` | Y | Y | Y |
70
+ | `GET /directory_groups` | Y | Y | |
71
+ | `GET /directory_groups/{}` | Y | Y | Y |
72
+ | `GET /directory_users` | Y | Y | |
73
+ | `GET /directory_users/{}` | Y | Y | Y |
74
+ | `GET /events` | Y | Y | |
75
+ | `GET /feature-flags` | Y | Y | |
76
+ | `GET /feature-flags/{}` | Y | Y | Y |
77
+ | `GET /fga/v1/resources/{}/{}` | | | Y |
78
+ | `GET /organization_domains/{}` | Y | Y | Y |
79
+ | `GET /organizations` | Y | Y | |
80
+ | `GET /organizations/external_id/{}` | Y | Y | Y |
81
+ | `GET /organizations/{}` | Y | Y | Y |
82
+ | `GET /organizations/{}/api_keys` | Y | Y | |
83
+ | `GET /organizations/{}/audit_log_configuration` | Y | Y | |
84
+ | `GET /organizations/{}/audit_logs_retention` | Y | Y | |
85
+ | `GET /organizations/{}/feature-flags` | Y | Y | |
86
+ | `GET /organizations/{}/roles` | | | Y |
87
+ | `GET /sso/authorize` | Y | Y | |
88
+ | `GET /sso/jwks/{}` | Y | Y | |
89
+ | `GET /sso/logout` | Y | Y | |
90
+ | `GET /sso/profile` | Y | Y | |
91
+ | `GET /user_management/authorize` | Y | Y | |
92
+ | `GET /user_management/email_verification/{}` | Y | Y | Y |
93
+ | `GET /user_management/invitations` | Y | Y | |
94
+ | `GET /user_management/invitations/by_token/{}` | Y | Y | Y |
95
+ | `GET /user_management/invitations/{}` | Y | Y | Y |
96
+ | `GET /user_management/magic_auth/{}` | Y | Y | Y |
97
+ | `GET /user_management/organization_memberships` | Y | Y | |
98
+ | `GET /user_management/organization_memberships/{}` | Y | Y | Y |
99
+ | `GET /user_management/password_reset/{}` | Y | Y | Y |
100
+ | `GET /user_management/sessions/logout` | Y | Y | |
101
+ | `GET /user_management/users` | Y | Y | |
102
+ | `GET /user_management/users/external_id/{}` | Y | Y | Y |
103
+ | `GET /user_management/users/{}` | Y | Y | Y |
104
+ | `GET /user_management/users/{}/auth_factors` | Y | Y | |
105
+ | `GET /user_management/users/{}/authorized_applications` | Y | Y | |
106
+ | `GET /user_management/users/{}/connected_accounts/{}` | Y | Y | |
107
+ | `GET /user_management/users/{}/data_providers` | Y | Y | |
108
+ | `GET /user_management/users/{}/feature-flags` | Y | Y | |
109
+ | `GET /user_management/users/{}/identities` | Y | Y | Y |
110
+ | `GET /user_management/users/{}/sessions` | Y | Y | |
111
+ | `GET /vault/v1/kv/name/{}` | | | Y |
112
+ | `GET /vault/v1/kv/{}` | | | Y |
113
+ | `GET /vault/v1/kv/{}/metadata` | | | Y |
114
+ | `GET /vault/v1/kv/{}/versions` | | | Y |
115
+ | `GET /webhook_endpoints` | Y | Y | |
116
+ | `PATCH /authorization/organizations/{}/resources/{}/{}` | Y | Y | Y |
117
+ | `PATCH /authorization/organizations/{}/roles/{}` | Y | Y | Y |
118
+ | `PATCH /authorization/permissions/{}` | Y | Y | Y |
119
+ | `PATCH /authorization/resources/{}` | Y | Y | Y |
120
+ | `PATCH /authorization/roles/{}` | Y | Y | Y |
121
+ | `POST /api_keys/validations` | Y | Y | Y |
122
+ | `POST /audit_logs/actions/{}/schemas` | Y | Y | Y |
123
+ | `POST /audit_logs/events` | Y | Y | Y |
124
+ | `POST /audit_logs/exports` | Y | Y | Y |
125
+ | `POST /auth/challenges/{}/verify` | Y | Y | Y |
126
+ | `POST /auth/factors/enroll` | Y | Y | Y |
127
+ | `POST /auth/factors/{}/challenge` | Y | Y | Y |
128
+ | `POST /authkit/oauth2/complete` | Y | Y | Y |
129
+ | `POST /authorization/organization_memberships/{}/check` | Y | Y | Y |
130
+ | `POST /authorization/organization_memberships/{}/role_assignments` | Y | Y | Y |
131
+ | `POST /authorization/organizations/{}/roles` | Y | Y | Y |
132
+ | `POST /authorization/organizations/{}/roles/{}/permissions` | Y | Y | Y |
133
+ | `POST /authorization/permissions` | Y | Y | Y |
134
+ | `POST /authorization/resources` | Y | Y | Y |
135
+ | `POST /authorization/roles` | Y | Y | Y |
136
+ | `POST /authorization/roles/{}/permissions` | Y | Y | Y |
137
+ | `POST /connect/applications` | Y | Y | Y |
138
+ | `POST /connect/applications/{}/client_secrets` | Y | Y | Y |
139
+ | `POST /data-integrations/{}/authorize` | Y | Y | Y |
140
+ | `POST /data-integrations/{}/token` | Y | Y | Y |
141
+ | `POST /feature-flags/{}/targets/{}` | Y | Y | Y |
142
+ | `POST /fga/v1/check` | | | Y |
143
+ | `POST /fga/v1/resources` | | | Y |
144
+ | `POST /fga/v1/resources/batch` | | | Y |
145
+ | `POST /fga/v1/warrants` | | | Y |
146
+ | `POST /organization_domains` | Y | Y | Y |
147
+ | `POST /organization_domains/{}/verify` | Y | Y | Y |
148
+ | `POST /organizations` | Y | Y | Y |
149
+ | `POST /organizations/{}/api_keys` | Y | Y | Y |
150
+ | `POST /passwordless/sessions` | | | Y |
151
+ | `POST /passwordless/sessions/{}/send` | | | Y |
152
+ | `POST /portal/generate_link` | Y | Y | Y |
153
+ | `POST /radar/attempts` | Y | Y | Y |
154
+ | `POST /radar/lists/{}/{}` | Y | Y | Y |
155
+ | `POST /sso/logout/authorize` | Y | Y | |
156
+ | `POST /sso/token` | Y | Y | |
157
+ | `POST /user_management/authenticate` | Y | Y | Y |
158
+ | `POST /user_management/authorize/device` | Y | Y | |
159
+ | `POST /user_management/cors_origins` | Y | Y | |
160
+ | `POST /user_management/invitations` | Y | Y | Y |
161
+ | `POST /user_management/invitations/{}/accept` | Y | Y | Y |
162
+ | `POST /user_management/invitations/{}/resend` | Y | Y | Y |
163
+ | `POST /user_management/invitations/{}/revoke` | Y | Y | Y |
164
+ | `POST /user_management/magic_auth` | Y | Y | Y |
165
+ | `POST /user_management/organization_memberships` | Y | Y | Y |
166
+ | `POST /user_management/password_reset` | Y | Y | Y |
167
+ | `POST /user_management/password_reset/confirm` | Y | Y | Y |
168
+ | `POST /user_management/redirect_uris` | Y | Y | |
169
+ | `POST /user_management/sessions/revoke` | Y | Y | Y |
170
+ | `POST /user_management/users` | Y | Y | Y |
171
+ | `POST /user_management/users/{}/auth_factors` | Y | Y | Y |
172
+ | `POST /user_management/users/{}/email_verification/confirm` | Y | Y | Y |
173
+ | `POST /user_management/users/{}/email_verification/send` | Y | Y | Y |
174
+ | `POST /vault/v1/keys/data-key` | | | Y |
175
+ | `POST /vault/v1/keys/decrypt` | | | Y |
176
+ | `POST /vault/v1/kv` | | | Y |
177
+ | `POST /webhook_endpoints` | Y | Y | Y |
178
+ | `POST /widgets/token` | Y | Y | Y |
179
+ | `POST data-integrations/{}/token` | | | Y |
180
+ | `PUT /authorization/organizations/{}/roles/priority` | Y | Y | |
181
+ | `PUT /authorization/organizations/{}/roles/{}/permissions` | Y | Y | Y |
182
+ | `PUT /authorization/roles/{}/permissions` | Y | Y | Y |
183
+ | `PUT /connect/applications/{}` | Y | Y | Y |
184
+ | `PUT /feature-flags/{}/disable` | Y | Y | Y |
185
+ | `PUT /feature-flags/{}/enable` | Y | Y | Y |
186
+ | `PUT /fga/v1/resources/{}/{}` | | | Y |
187
+ | `PUT /organizations/{}` | Y | Y | Y |
188
+ | `PUT /organizations/{}/audit_logs_retention` | Y | Y | |
189
+ | `PUT /radar/attempts/{}` | Y | Y | Y |
190
+ | `PUT /user_management/jwt_template` | Y | Y | |
191
+ | `PUT /user_management/organization_memberships/{}` | Y | Y | Y |
192
+ | `PUT /user_management/organization_memberships/{}/deactivate` | Y | Y | Y |
193
+ | `PUT /user_management/organization_memberships/{}/reactivate` | Y | Y | Y |
194
+ | `PUT /user_management/users/{}` | Y | Y | Y |
195
+ | `PUT /vault/v1/kv/{}` | | | Y |
196
+
197
+ ## In Spec + Generated SDK but NOT in workos-node (40)
198
+
199
+ Many of these are list endpoints that workos-node handles via a shared paginated fetch helper which constructs URLs dynamically. Static analysis of the source does not capture these, so the actual runtime coverage is likely higher.
200
+
201
+ | Endpoint | Notes |
202
+ | ------------------------------------------------------------- | ------------------------------- |
203
+ | `DELETE /user_management/users/{}/authorized_applications/{}` | |
204
+ | `DELETE /user_management/users/{}/connected_accounts/{}` | |
205
+ | `GET /audit_logs/actions` | List endpoint |
206
+ | `GET /audit_logs/actions/{}/schemas` | |
207
+ | `GET /connect/applications` | List endpoint |
208
+ | `GET /connections` | List endpoint |
209
+ | `GET /directories` | List endpoint |
210
+ | `GET /directory_groups` | List endpoint |
211
+ | `GET /directory_users` | List endpoint |
212
+ | `GET /events` | List endpoint |
213
+ | `GET /feature-flags` | List endpoint |
214
+ | `GET /organizations` | List endpoint |
215
+ | `GET /organizations/{}/api_keys` | |
216
+ | `GET /organizations/{}/audit_log_configuration` | |
217
+ | `GET /organizations/{}/audit_logs_retention` | |
218
+ | `GET /organizations/{}/feature-flags` | |
219
+ | `GET /sso/authorize` | URL construction, not HTTP call |
220
+ | `GET /sso/jwks/{}` | |
221
+ | `GET /sso/logout` | URL construction, not HTTP call |
222
+ | `GET /sso/profile` | |
223
+ | `GET /user_management/authorize` | URL construction, not HTTP call |
224
+ | `GET /user_management/invitations` | List endpoint |
225
+ | `GET /user_management/organization_memberships` | List endpoint |
226
+ | `GET /user_management/sessions/logout` | URL construction, not HTTP call |
227
+ | `GET /user_management/users` | List endpoint |
228
+ | `GET /user_management/users/{}/auth_factors` | |
229
+ | `GET /user_management/users/{}/authorized_applications` | |
230
+ | `GET /user_management/users/{}/connected_accounts/{}` | |
231
+ | `GET /user_management/users/{}/data_providers` | |
232
+ | `GET /user_management/users/{}/feature-flags` | |
233
+ | `GET /user_management/users/{}/sessions` | |
234
+ | `GET /webhook_endpoints` | List endpoint |
235
+ | `POST /sso/logout/authorize` | |
236
+ | `POST /sso/token` | |
237
+ | `POST /user_management/authorize/device` | |
238
+ | `POST /user_management/cors_origins` | |
239
+ | `POST /user_management/redirect_uris` | |
240
+ | `PUT /authorization/organizations/{}/roles/priority` | |
241
+ | `PUT /organizations/{}/audit_logs_retention` | |
242
+ | `PUT /user_management/jwt_template` | |
243
+
244
+ ## In workos-node but NOT in Spec (20)
245
+
246
+ These are legacy, deprecated, or separate-product endpoints not present in the current OpenAPI spec.
247
+
248
+ | Endpoint | Notes |
249
+ | ------------------------------------- | -------------------------------------------------------------------------- |
250
+ | `DELETE /fga/v1/resources/{}/{}` | FGA v1 (separate product) |
251
+ | `DELETE /vault/v1/kv/{}` | Vault v1 (separate product) |
252
+ | `GET /fga/v1/resources/{}/{}` | FGA v1 (separate product) |
253
+ | `GET /organizations/{}/roles` | Different path pattern from spec's `/authorization/organizations/{}/roles` |
254
+ | `GET /vault/v1/kv/name/{}` | Vault v1 (separate product) |
255
+ | `GET /vault/v1/kv/{}` | Vault v1 (separate product) |
256
+ | `GET /vault/v1/kv/{}/metadata` | Vault v1 (separate product) |
257
+ | `GET /vault/v1/kv/{}/versions` | Vault v1 (separate product) |
258
+ | `POST /fga/v1/check` | FGA v1 (separate product) |
259
+ | `POST /fga/v1/resources` | FGA v1 (separate product) |
260
+ | `POST /fga/v1/resources/batch` | FGA v1 (separate product) |
261
+ | `POST /fga/v1/warrants` | FGA v1 (separate product) |
262
+ | `POST /passwordless/sessions` | Deprecated passwordless API |
263
+ | `POST /passwordless/sessions/{}/send` | Deprecated passwordless API |
264
+ | `POST /vault/v1/keys/data-key` | Vault v1 (separate product) |
265
+ | `POST /vault/v1/keys/decrypt` | Vault v1 (separate product) |
266
+ | `POST /vault/v1/kv` | Vault v1 (separate product) |
267
+ | `POST data-integrations/{}/token` | Missing leading `/` — likely a source bug |
268
+ | `PUT /fga/v1/resources/{}/{}` | FGA v1 (separate product) |
269
+ | `PUT /vault/v1/kv/{}` | Vault v1 (separate product) |
270
+
271
+ ## Caveats
272
+
273
+ - **workos-node list endpoints**: Many list/paginated GET endpoints in workos-node are handled by a shared `fetchAndDeserialize` or `AutoPaginatable` helper that constructs the URL dynamically. Static grep analysis does not capture these, so the 134 count is a **lower bound**. The actual runtime coverage is likely closer to the spec's 154.
274
+ - **URL-construction-only endpoints**: Some spec endpoints (e.g., `GET /sso/authorize`, `GET /user_management/authorize`) are authorization URLs that the SDK constructs and returns as strings rather than making HTTP calls. These appear as "missing" from workos-node but are functionally present.
275
+ - **Path parameter normalization**: All path parameters are normalized to `{}` for comparison. The spec uses `<ID>`, the generated SDK uses `{paramName}`, and workos-node uses template literals like `${id}`.
@@ -0,0 +1,355 @@
1
+ # Node SDK Architecture
2
+
3
+ Derived from the existing WorkOS Node SDK at `workos-node`.
4
+
5
+ ## Architecture Overview
6
+
7
+ - **Main client**: `WorkOS` class with HTTP methods (`get`, `post`, `put`, `patch`, `delete`) and readonly resource accessors (e.g., `workos.organizations`).
8
+ - **Resource classes**: One per service, constructor receives `WorkOS` client, async methods return deserialized domain types.
9
+ - **Dual interface system**: Domain interfaces (camelCase) and wire/response interfaces (snake_case with `Response` suffix).
10
+ - **Explicit serialization**: `deserialize{Model}` and `serialize{Options}` functions per model/operation.
11
+ - **Pagination**: `AutoPaginatable<T>` with cursor-based `after` param, `autoPagination()` async generator, 350ms rate-limit delay.
12
+ - **Error hierarchy**: Status-code-specific exception classes extending `Error`.
13
+ - **Constructor**: Accepts `string | WorkOSOptions`, env var fallback for `WORKOS_API_KEY`.
14
+ - **Factory**: `createWorkOS()` with overloads for `PublicWorkOS` (no API key) and full `WorkOS`.
15
+
16
+ ## Naming Conventions
17
+
18
+ | Concept | Convention | Example |
19
+ | ---------------- | ---------- | ----------------------------------------------- |
20
+ | Class/Interface | PascalCase | `Organization`, `UserManagement` |
21
+ | Method | camelCase | `listOrganizations`, `createOrganization` |
22
+ | Domain field | camelCase | `allowProfilesOutsideOrganization` |
23
+ | Wire field | snake_case | `allow_profiles_outside_organization` |
24
+ | File | kebab-case | `organization.interface.ts` |
25
+ | Directory | kebab-case | `organizations/`, `user-management/` |
26
+ | Service property | camelCase | `workos.organizations`, `workos.userManagement` |
27
+
28
+ ### Overlay Resolution
29
+
30
+ All service-derived names (class, directory, file, property) are resolved through the overlay before falling back to the default PascalCase convention. This allows the generated SDK to match existing class names in the live SDK. For example, the IR service `MultiFactorAuth` (derived from the `multi-factor-auth` OpenAPI tag) is resolved to `Mfa` via the overlay, producing:
31
+
32
+ - Class: `Mfa`
33
+ - Directory: `src/mfa/`
34
+ - File: `mfa.ts`
35
+ - Property: `workos.mfa`
36
+
37
+ Method names are also overlay-resolved per operation (`resolveMethodName`), and interface names per model (`resolveInterfaceName`).
38
+
39
+ ## Type Mapping
40
+
41
+ | IR TypeRef | TypeScript (Domain) | TypeScript (Wire/Response) |
42
+ | ------------------------- | ------------------- | -------------------------- |
43
+ | `string` | `string` | `string` |
44
+ | `string` (date/date-time) | `string` | `string` |
45
+ | `integer` | `number` | `number` |
46
+ | `number` | `number` | `number` |
47
+ | `boolean` | `boolean` | `boolean` |
48
+ | `unknown` | `any` | `any` |
49
+ | `array(T)` | `T[]` | `T[]` |
50
+ | `model(Name)` | `Name` | `NameResponse` |
51
+ | `enum(Name)` | `Name` | `Name` |
52
+ | `nullable(T)` | `T \| null` | `T \| null` |
53
+ | `union(V1,V2)` | `V1 \| V2` | `V1 \| V2` |
54
+ | `map(V)` | `Record<string, V>` | `Record<string, V>` |
55
+ | `literal(v)` | `'v'` | `'v'` |
56
+
57
+ ## Model Pattern
58
+
59
+ From `src/organizations/interfaces/organization.interface.ts`:
60
+
61
+ ```typescript
62
+ // Domain interface (camelCase)
63
+ export interface Organization {
64
+ object: "organization";
65
+ id: string;
66
+ name: string;
67
+ allowProfilesOutsideOrganization: boolean;
68
+ domains: OrganizationDomain[];
69
+ stripeCustomerId?: string;
70
+ createdAt: string;
71
+ updatedAt: string;
72
+ externalId: string | null;
73
+ metadata: Record<string, string>;
74
+ }
75
+
76
+ // Wire interface (snake_case, Response suffix)
77
+ export interface OrganizationResponse {
78
+ object: "organization";
79
+ id: string;
80
+ name: string;
81
+ allow_profiles_outside_organization: boolean;
82
+ domains: OrganizationDomainResponse[];
83
+ stripe_customer_id?: string;
84
+ created_at: string;
85
+ updated_at: string;
86
+ external_id?: string | null;
87
+ metadata?: Record<string, string>;
88
+ }
89
+ ```
90
+
91
+ Key patterns:
92
+
93
+ - Required domain fields may be optional in the response interface
94
+ - Model refs in response use `Response` suffix
95
+ - Nullable fields use `| null`, optional fields use `?`
96
+
97
+ ## Enum Pattern
98
+
99
+ String literal union types:
100
+
101
+ ```typescript
102
+ export type OrganizationDomainVerificationStrategy = "dns" | "manual";
103
+ ```
104
+
105
+ ## Serialization Pattern
106
+
107
+ From `src/organizations/serializers/organization.serializer.ts`:
108
+
109
+ ```typescript
110
+ export const deserializeOrganization = (
111
+ organization: OrganizationResponse,
112
+ ): Organization => ({
113
+ object: organization.object,
114
+ id: organization.id,
115
+ name: organization.name,
116
+ allowProfilesOutsideOrganization:
117
+ organization.allow_profiles_outside_organization,
118
+ domains: organization.domains.map(deserializeOrganizationDomain),
119
+ ...(typeof organization.stripe_customer_id === "undefined"
120
+ ? undefined
121
+ : { stripeCustomerId: organization.stripe_customer_id }),
122
+ createdAt: organization.created_at,
123
+ updatedAt: organization.updated_at,
124
+ externalId: organization.external_id ?? null,
125
+ metadata: organization.metadata ?? {},
126
+ });
127
+
128
+ export const serializeCreateOrganizationOptions = (
129
+ options: CreateOrganizationOptions,
130
+ ): SerializedCreateOrganizationOptions => ({
131
+ name: options.name,
132
+ domain_data: options.domainData,
133
+ external_id: options.externalId,
134
+ metadata: options.metadata,
135
+ });
136
+ ```
137
+
138
+ Key patterns:
139
+
140
+ - Deserialize: snake_case → camelCase, map nested models recursively
141
+ - Optional wire fields: spread conditional (`typeof x === 'undefined' ? undefined : { ... }`)
142
+ - Nullable domain fields: `?? null` fallback
143
+ - Default values for optional collections: `?? {}`
144
+
145
+ ## Resource Pattern
146
+
147
+ From `src/organizations/organizations.ts`:
148
+
149
+ ```typescript
150
+ export class Organizations {
151
+ constructor(private readonly workos: WorkOS) {}
152
+
153
+ async listOrganizations(
154
+ options?: ListOrganizationsOptions,
155
+ ): Promise<AutoPaginatable<Organization, ListOrganizationsOptions>> {
156
+ return new AutoPaginatable(
157
+ await fetchAndDeserialize<OrganizationResponse, Organization>(
158
+ this.workos,
159
+ "/organizations",
160
+ deserializeOrganization,
161
+ options,
162
+ ),
163
+ (params) =>
164
+ fetchAndDeserialize<OrganizationResponse, Organization>(
165
+ this.workos,
166
+ "/organizations",
167
+ deserializeOrganization,
168
+ params,
169
+ ),
170
+ options,
171
+ );
172
+ }
173
+
174
+ async createOrganization(
175
+ payload: CreateOrganizationOptions,
176
+ requestOptions: CreateOrganizationRequestOptions = {},
177
+ ): Promise<Organization> {
178
+ const { data } = await this.workos.post<OrganizationResponse>(
179
+ "/organizations",
180
+ serializeCreateOrganizationOptions(payload),
181
+ requestOptions,
182
+ );
183
+ return deserializeOrganization(data);
184
+ }
185
+
186
+ async getOrganization(id: string): Promise<Organization> {
187
+ const { data } = await this.workos.get<OrganizationResponse>(
188
+ `/organizations/${id}`,
189
+ );
190
+ return deserializeOrganization(data);
191
+ }
192
+
193
+ async deleteOrganization(id: string): Promise<void> {
194
+ await this.workos.delete(`/organizations/${id}`);
195
+ }
196
+ }
197
+ ```
198
+
199
+ Key patterns:
200
+
201
+ - Constructor takes `private readonly workos: WorkOS`
202
+ - List methods return `AutoPaginatable<T>` via `fetchAndDeserialize`
203
+ - Create/update methods: serialize body → POST → deserialize response
204
+ - Get methods: GET with path param → deserialize response
205
+ - Delete methods: return `Promise<void>`
206
+ - Idempotent POSTs: accept `requestOptions` with `idempotencyKey`
207
+
208
+ ## Pagination Pattern
209
+
210
+ From `src/common/utils/pagination.ts`:
211
+
212
+ ```typescript
213
+ export class AutoPaginatable<
214
+ ResourceType,
215
+ ParametersType extends PaginationOptions = PaginationOptions,
216
+ > {
217
+ readonly object = 'list' as const;
218
+ constructor(
219
+ protected list: List<ResourceType>,
220
+ private apiCall: (params: PaginationOptions) => Promise<List<ResourceType>>,
221
+ options?: ParametersType,
222
+ ) { ... }
223
+
224
+ get data(): ResourceType[] { return this.list.data; }
225
+ get listMetadata() { return this.list.listMetadata; }
226
+ async autoPagination(): Promise<ResourceType[]> { ... }
227
+ }
228
+ ```
229
+
230
+ ## Error Handling
231
+
232
+ From `src/common/exceptions/`:
233
+
234
+ | Exception Class | Status Code |
235
+ | -------------------------------- | ----------- |
236
+ | `BadRequestException` | 400 |
237
+ | `UnauthorizedException` | 401 |
238
+ | `ApiKeyRequiredException` | 403 |
239
+ | `NotFoundException` | 404 |
240
+ | `ConflictException` | 409 |
241
+ | `UnprocessableEntityException` | 422 |
242
+ | `RateLimitExceededException` | 429 |
243
+ | `GenericServerException` | 500+ |
244
+ | `OAuthException` | varies |
245
+ | `NoApiKeyProvidedException` | runtime |
246
+ | `SignatureVerificationException` | runtime |
247
+
248
+ Each exception: extends `Error`, has `readonly status`, `readonly name`, `requestID`, optional `code`.
249
+
250
+ ## Client Architecture
251
+
252
+ From `src/workos.ts`:
253
+
254
+ ```typescript
255
+ export class WorkOS {
256
+ readonly baseURL: string;
257
+ readonly client: HttpClient;
258
+ readonly organizations = new Organizations(this);
259
+ // ... other resource accessors
260
+
261
+ constructor(keyOrOptions?: string | WorkOSOptions, maybeOptions?: WorkOSOptions) { ... }
262
+
263
+ async post<Result, Entity>(path, entity, options?: PostOptions): Promise<{ data: Result }> { ... }
264
+ async get<Result>(path, options?: GetOptions): Promise<{ data: Result }> { ... }
265
+ async put<Result, Entity>(path, entity, options?): Promise<{ data: Result }> { ... }
266
+ async delete(path, options?): Promise<void> { ... }
267
+ }
268
+ ```
269
+
270
+ ## HTTP Client (Retry Logic)
271
+
272
+ From `src/common/net/http-client.ts`:
273
+
274
+ - `MAX_RETRY_ATTEMPTS = 3`
275
+ - `BACKOFF_MULTIPLIER = 1.5`
276
+ - `MINIMUM_SLEEP_TIME_IN_MILLISECONDS = 500`
277
+ - `RETRY_STATUS_CODES = [408, 500, 502, 504]`
278
+ - Path-specific retry: only for `/fga/`, `/vault/`, `/audit_logs/events`
279
+ - Jitter: `sleepTime * (Math.random() + 0.5)`
280
+
281
+ ## Testing Pattern
282
+
283
+ Framework: Jest + `jest-fetch-mock`
284
+
285
+ From `src/organizations/organizations.spec.ts`:
286
+
287
+ ```typescript
288
+ import fetch from "jest-fetch-mock";
289
+ import {
290
+ fetchOnce,
291
+ fetchURL,
292
+ fetchSearchParams,
293
+ fetchBody,
294
+ } from "../common/utils/test-utils";
295
+
296
+ const workos = new WorkOS("sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU");
297
+
298
+ describe("Organizations", () => {
299
+ beforeEach(() => fetch.resetMocks());
300
+
301
+ it("returns organizations and metadata", async () => {
302
+ fetchOnce(listOrganizationsFixture);
303
+ const { data, listMetadata } =
304
+ await workos.organizations.listOrganizations();
305
+ expect(fetchSearchParams()).toEqual({ order: "desc" });
306
+ expect(data).toHaveLength(7);
307
+ });
308
+ });
309
+ ```
310
+
311
+ Test utilities: `fetchOnce`, `fetchURL`, `fetchSearchParams`, `fetchHeaders`, `fetchBody`.
312
+
313
+ ## Directory Structure
314
+
315
+ ```
316
+ src/
317
+ ├── workos.ts # Main client class
318
+ ├── index.ts # Barrel export
319
+ ├── factory.ts # createWorkOS factory
320
+ ├── common/
321
+ │ ├── exceptions/ # Error hierarchy
322
+ │ ├── interfaces/ # WorkOSOptions, PostOptions, GetOptions, PaginationOptions
323
+ │ ├── net/ # HttpClient abstract base
324
+ │ ├── serializers/ # list, event, pagination serializers
325
+ │ └── utils/ # AutoPaginatable, fetchAndDeserialize, test-utils
326
+ ├── {service}/
327
+ │ ├── {service}.ts # Resource class
328
+ │ ├── {service}.spec.ts # Tests
329
+ │ ├── interfaces/ # Model, Response, Options interfaces
330
+ │ │ └── index.ts # Re-exports
331
+ │ ├── serializers/ # Serialize/deserialize functions
332
+ │ │ └── index.ts # Re-exports
333
+ │ └── fixtures/ # JSON test data
334
+ ```
335
+
336
+ ## Structural Guidelines
337
+
338
+ | Category | Choice |
339
+ | ----------------- | ---------------------------------------- |
340
+ | Testing Framework | Jest |
341
+ | HTTP Mocking | jest-fetch-mock |
342
+ | Type Signatures | TypeScript interfaces (inline) |
343
+ | HTTP Client | Abstract HttpClient with FetchHttpClient |
344
+ | JSON Parsing | Built-in JSON.parse/stringify |
345
+ | Package Manager | npm |
346
+ | Build Tool | TypeScript compiler + bundler |
347
+ | Module Format | CJS + ESM dual |
348
+
349
+ ## Additional Generator Files
350
+
351
+ Beyond the standard scaffold, this emitter requires:
352
+
353
+ - `serializers.ts` — explicit serialize/deserialize function generation
354
+ - `common.ts` — AutoPaginatable, fetchAndDeserialize, List types, shared utilities
355
+ - `config.ts` — WorkOSOptions, PostOptions, GetOptions, PaginationOptions