@workos/oagen-emitters 0.9.1 → 0.10.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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/index.mjs +1 -1
- package/dist/{plugin-Dh9JSScr.mjs → plugin-H0KhxbN7.mjs} +22 -10
- package/dist/{plugin-Dh9JSScr.mjs.map → plugin-H0KhxbN7.mjs.map} +1 -1
- package/dist/plugin.mjs +1 -1
- package/package.json +2 -2
- package/src/dotnet/enums.ts +37 -11
- package/test/dotnet/enums.test.ts +146 -0
package/dist/plugin.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as workosEmittersPlugin } from "./plugin-
|
|
1
|
+
import { t as workosEmittersPlugin } from "./plugin-H0KhxbN7.mjs";
|
|
2
2
|
export { workosEmittersPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workos/oagen-emitters",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "WorkOS' oagen emitters",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "WorkOS",
|
|
@@ -54,6 +54,6 @@
|
|
|
54
54
|
"node": ">=24.10.0"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@workos/oagen": "^0.
|
|
57
|
+
"@workos/oagen": "^0.18.0"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/dotnet/enums.ts
CHANGED
|
@@ -70,18 +70,37 @@ export function generateEnums(enums: Enum[], ctx: EmitterContext): GeneratedFile
|
|
|
70
70
|
lines.push(' [STJS.JsonConverter(typeof(WorkOSStringEnumConverterFactory))]');
|
|
71
71
|
lines.push(` public enum ${typeName}`);
|
|
72
72
|
lines.push(' {');
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
|
|
74
|
+
// If the spec defines a default that matches a member, promote it to
|
|
75
|
+
// ordinal 0 so `default(EnumType)` returns that variant. Other members
|
|
76
|
+
// get explicit ascending ordinals, and the Unknown sentinel is pushed
|
|
77
|
+
// to a high explicit ordinal (99) so it never sits at 0.
|
|
78
|
+
const defaultMatch =
|
|
79
|
+
enumDef.default !== undefined ? uniqueValues.find((v) => v.value === enumDef.default) : undefined;
|
|
80
|
+
|
|
81
|
+
const orderedValues = defaultMatch
|
|
82
|
+
? [defaultMatch, ...uniqueValues.filter((v) => v !== defaultMatch)]
|
|
83
|
+
: uniqueValues;
|
|
77
84
|
|
|
78
85
|
const usedNames = new Set<string>();
|
|
79
|
-
usedNames.add('Unknown');
|
|
80
|
-
// Track used EnumMember wire values to avoid duplicates (sentinel uses "unknown")
|
|
81
86
|
const usedWireValues = new Set<string>();
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
|
|
88
|
+
if (defaultMatch) {
|
|
89
|
+
// Reserve the sentinel name and wire value so spec values that collide
|
|
90
|
+
// with "unknown"/Unknown still get the existing skip/dedup behavior.
|
|
91
|
+
usedNames.add('Unknown');
|
|
92
|
+
usedWireValues.add('unknown');
|
|
93
|
+
} else {
|
|
94
|
+
// Existing behavior: Unknown sentinel at ordinal 0.
|
|
95
|
+
lines.push(` [EnumMember(Value = "unknown")]`);
|
|
96
|
+
lines.push(` Unknown,`);
|
|
97
|
+
lines.push('');
|
|
98
|
+
usedNames.add('Unknown');
|
|
99
|
+
usedWireValues.add('unknown');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < orderedValues.length; i++) {
|
|
103
|
+
const v = orderedValues[i];
|
|
85
104
|
// Skip values whose wire representation collides with the sentinel
|
|
86
105
|
if (usedWireValues.has(String(v.value))) continue;
|
|
87
106
|
usedWireValues.add(String(v.value));
|
|
@@ -102,8 +121,15 @@ export function generateEnums(enums: Enum[], ctx: EmitterContext): GeneratedFile
|
|
|
102
121
|
lines.push(` [System.Obsolete("${msg}")]`);
|
|
103
122
|
}
|
|
104
123
|
lines.push(` [EnumMember(Value = "${v.value}")]`);
|
|
105
|
-
|
|
106
|
-
|
|
124
|
+
// Explicit ordinals only when we promoted a default to position 0.
|
|
125
|
+
const ordinal = defaultMatch ? ` = ${i}` : '';
|
|
126
|
+
lines.push(` ${memberName}${ordinal},`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (defaultMatch) {
|
|
130
|
+
lines.push('');
|
|
131
|
+
lines.push(` [EnumMember(Value = "unknown")]`);
|
|
132
|
+
lines.push(` Unknown = 99,`);
|
|
107
133
|
}
|
|
108
134
|
|
|
109
135
|
lines.push(' }');
|
|
@@ -152,6 +152,152 @@ describe('dotnet/enums', () => {
|
|
|
152
152
|
expect(files).toHaveLength(0);
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
+
it('promotes spec-default member to ordinal 0 and pushes Unknown to 99', () => {
|
|
156
|
+
const enums: Enum[] = [
|
|
157
|
+
{
|
|
158
|
+
name: 'PaginationOrder',
|
|
159
|
+
values: [
|
|
160
|
+
{ name: 'NORMAL', value: 'normal' },
|
|
161
|
+
{ name: 'DESC', value: 'desc' },
|
|
162
|
+
{ name: 'ASC', value: 'asc' },
|
|
163
|
+
],
|
|
164
|
+
default: 'desc',
|
|
165
|
+
},
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
const service: Service = {
|
|
169
|
+
name: 'Test',
|
|
170
|
+
operations: [
|
|
171
|
+
{
|
|
172
|
+
name: 'test',
|
|
173
|
+
httpMethod: 'get',
|
|
174
|
+
path: '/test',
|
|
175
|
+
pathParams: [],
|
|
176
|
+
queryParams: [{ name: 'order', type: { kind: 'enum', name: 'PaginationOrder' }, required: false }],
|
|
177
|
+
headerParams: [],
|
|
178
|
+
response: { kind: 'primitive', type: 'unknown' },
|
|
179
|
+
errors: [],
|
|
180
|
+
injectIdempotencyKey: false,
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const files = generateEnums(enums, {
|
|
186
|
+
...ctx,
|
|
187
|
+
spec: { ...emptySpec, services: [service], enums },
|
|
188
|
+
});
|
|
189
|
+
expect(files).toHaveLength(1);
|
|
190
|
+
expect(files[0].content).toMatchInlineSnapshot(`
|
|
191
|
+
"namespace WorkOS
|
|
192
|
+
{
|
|
193
|
+
using System.Runtime.Serialization;
|
|
194
|
+
using Newtonsoft.Json;
|
|
195
|
+
using STJS = System.Text.Json.Serialization;
|
|
196
|
+
|
|
197
|
+
/// <summary>Represents pagination order values.</summary>
|
|
198
|
+
[JsonConverter(typeof(WorkOSNewtonsoftStringEnumConverter))]
|
|
199
|
+
[STJS.JsonConverter(typeof(WorkOSStringEnumConverterFactory))]
|
|
200
|
+
public enum PaginationOrder
|
|
201
|
+
{
|
|
202
|
+
[EnumMember(Value = "desc")]
|
|
203
|
+
Desc = 0,
|
|
204
|
+
[EnumMember(Value = "normal")]
|
|
205
|
+
Normal = 1,
|
|
206
|
+
[EnumMember(Value = "asc")]
|
|
207
|
+
Asc = 2,
|
|
208
|
+
|
|
209
|
+
[EnumMember(Value = "unknown")]
|
|
210
|
+
Unknown = 99,
|
|
211
|
+
}
|
|
212
|
+
}"
|
|
213
|
+
`);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('falls back to Unknown=0 layout when no default is set', () => {
|
|
217
|
+
const enums: Enum[] = [
|
|
218
|
+
{
|
|
219
|
+
name: 'Status',
|
|
220
|
+
values: [
|
|
221
|
+
{ name: 'ACTIVE', value: 'active' },
|
|
222
|
+
{ name: 'INACTIVE', value: 'inactive' },
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
const service: Service = {
|
|
228
|
+
name: 'Test',
|
|
229
|
+
operations: [
|
|
230
|
+
{
|
|
231
|
+
name: 'test',
|
|
232
|
+
httpMethod: 'get',
|
|
233
|
+
path: '/test',
|
|
234
|
+
pathParams: [],
|
|
235
|
+
queryParams: [{ name: 'status', type: { kind: 'enum', name: 'Status' }, required: false }],
|
|
236
|
+
headerParams: [],
|
|
237
|
+
response: { kind: 'primitive', type: 'unknown' },
|
|
238
|
+
errors: [],
|
|
239
|
+
injectIdempotencyKey: false,
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const files = generateEnums(enums, {
|
|
245
|
+
...ctx,
|
|
246
|
+
spec: { ...emptySpec, services: [service], enums },
|
|
247
|
+
});
|
|
248
|
+
expect(files).toHaveLength(1);
|
|
249
|
+
const content = files[0].content;
|
|
250
|
+
// No explicit ordinals when no default
|
|
251
|
+
expect(content).not.toMatch(/= \d+,/);
|
|
252
|
+
// Unknown sentinel emitted first (no = N)
|
|
253
|
+
const unknownIdx = content.indexOf('Unknown,');
|
|
254
|
+
const activeIdx = content.indexOf('Active,');
|
|
255
|
+
expect(unknownIdx).toBeGreaterThan(0);
|
|
256
|
+
expect(unknownIdx).toBeLessThan(activeIdx);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('drops a spec value that collides with the unknown sentinel even when default is set', () => {
|
|
260
|
+
const enums: Enum[] = [
|
|
261
|
+
{
|
|
262
|
+
name: 'WithUnknown',
|
|
263
|
+
values: [
|
|
264
|
+
{ name: 'NORMAL', value: 'normal' },
|
|
265
|
+
{ name: 'UNKNOWN', value: 'unknown' },
|
|
266
|
+
],
|
|
267
|
+
default: 'normal',
|
|
268
|
+
},
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
const service: Service = {
|
|
272
|
+
name: 'Test',
|
|
273
|
+
operations: [
|
|
274
|
+
{
|
|
275
|
+
name: 'test',
|
|
276
|
+
httpMethod: 'get',
|
|
277
|
+
path: '/test',
|
|
278
|
+
pathParams: [],
|
|
279
|
+
queryParams: [{ name: 'x', type: { kind: 'enum', name: 'WithUnknown' }, required: false }],
|
|
280
|
+
headerParams: [],
|
|
281
|
+
response: { kind: 'primitive', type: 'unknown' },
|
|
282
|
+
errors: [],
|
|
283
|
+
injectIdempotencyKey: false,
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const files = generateEnums(enums, {
|
|
289
|
+
...ctx,
|
|
290
|
+
spec: { ...emptySpec, services: [service], enums },
|
|
291
|
+
});
|
|
292
|
+
expect(files).toHaveLength(1);
|
|
293
|
+
const content = files[0].content;
|
|
294
|
+
expect(content).toContain('Normal = 0,');
|
|
295
|
+
expect(content).toContain('Unknown = 99,');
|
|
296
|
+
// The spec's "unknown" wire value collides with the sentinel and is dropped,
|
|
297
|
+
// so the body should not contain a non-99 ordinal for Unknown.
|
|
298
|
+
expect(content).not.toMatch(/Unknown = [0-8],/);
|
|
299
|
+
});
|
|
300
|
+
|
|
155
301
|
it('generates deprecated enum values with Obsolete attribute', () => {
|
|
156
302
|
const enums: Enum[] = [
|
|
157
303
|
{
|