@workos/oagen-emitters 0.6.4 → 0.6.6
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 +16 -0
- package/dist/index.mjs +1 -1
- package/dist/{plugin-CZoeqixh.mjs → plugin-BgVrq-hM.mjs} +23 -17
- package/dist/plugin-BgVrq-hM.mjs.map +1 -0
- package/dist/plugin.mjs +1 -1
- package/package.json +1 -1
- package/src/dotnet/index.ts +13 -7
- package/src/dotnet/models.ts +3 -1
- package/src/go/fixtures.ts +6 -7
- package/src/go/tests.ts +7 -2
- package/src/kotlin/models.ts +5 -3
- package/src/python/client.ts +7 -5
- package/src/python/models.ts +2 -2
- package/dist/plugin-CZoeqixh.mjs.map +0 -1
package/dist/plugin.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as workosEmittersPlugin } from "./plugin-
|
|
1
|
+
import { t as workosEmittersPlugin } from "./plugin-BgVrq-hM.mjs";
|
|
2
2
|
export { workosEmittersPlugin };
|
package/package.json
CHANGED
package/src/dotnet/index.ts
CHANGED
|
@@ -127,7 +127,7 @@ export const dotnetEmitter: Emitter = {
|
|
|
127
127
|
lines.push(' public override bool CanConvert(Type objectType) => objectType == typeof(object);');
|
|
128
128
|
lines.push('');
|
|
129
129
|
lines.push(
|
|
130
|
-
' public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)',
|
|
130
|
+
' public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object? existingValue, Newtonsoft.Json.JsonSerializer serializer)',
|
|
131
131
|
);
|
|
132
132
|
lines.push(' {');
|
|
133
133
|
lines.push(' var jObject = JObject.Load(reader);');
|
|
@@ -143,7 +143,7 @@ export const dotnetEmitter: Emitter = {
|
|
|
143
143
|
lines.push(' }');
|
|
144
144
|
lines.push('');
|
|
145
145
|
lines.push(
|
|
146
|
-
' public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)',
|
|
146
|
+
' public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object? value, Newtonsoft.Json.JsonSerializer serializer)',
|
|
147
147
|
);
|
|
148
148
|
lines.push(' {');
|
|
149
149
|
lines.push(' serializer.Serialize(writer, value);');
|
|
@@ -161,8 +161,10 @@ export const dotnetEmitter: Emitter = {
|
|
|
161
161
|
|
|
162
162
|
// Generate converters for discriminated base models (model-level
|
|
163
163
|
// discriminators detected by enrichModelsFromSpec, e.g. EventSchema).
|
|
164
|
-
//
|
|
165
|
-
// attribute applied to the base class.
|
|
164
|
+
// ReadJson uses Populate (not Deserialize) to avoid infinite recursion
|
|
165
|
+
// with the [JsonConverter] attribute applied to the base class.
|
|
166
|
+
// CanWrite is false so serialization uses the default path and never
|
|
167
|
+
// re-enters WriteJson.
|
|
166
168
|
for (const [baseName, disc] of modelDiscriminators) {
|
|
167
169
|
const baseClass = modelClassName(baseName);
|
|
168
170
|
const converterName = `${baseClass}DiscriminatorConverter`;
|
|
@@ -184,7 +186,7 @@ export const dotnetEmitter: Emitter = {
|
|
|
184
186
|
);
|
|
185
187
|
lines.push('');
|
|
186
188
|
lines.push(
|
|
187
|
-
' public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)',
|
|
189
|
+
' public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object? existingValue, Newtonsoft.Json.JsonSerializer serializer)',
|
|
188
190
|
);
|
|
189
191
|
lines.push(' {');
|
|
190
192
|
lines.push(' var jObject = JObject.Load(reader);');
|
|
@@ -204,11 +206,15 @@ export const dotnetEmitter: Emitter = {
|
|
|
204
206
|
lines.push(' return target;');
|
|
205
207
|
lines.push(' }');
|
|
206
208
|
lines.push('');
|
|
209
|
+
lines.push(' public override bool CanWrite => false;');
|
|
210
|
+
lines.push('');
|
|
207
211
|
lines.push(
|
|
208
|
-
' public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)',
|
|
212
|
+
' public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object? value, Newtonsoft.Json.JsonSerializer serializer)',
|
|
209
213
|
);
|
|
210
214
|
lines.push(' {');
|
|
211
|
-
lines.push(
|
|
215
|
+
lines.push(
|
|
216
|
+
' throw new NotImplementedException("Serialization is handled by the default serializer.");',
|
|
217
|
+
);
|
|
212
218
|
lines.push(' }');
|
|
213
219
|
lines.push(' }');
|
|
214
220
|
lines.push('}');
|
package/src/dotnet/models.ts
CHANGED
|
@@ -161,10 +161,12 @@ export function generateModels(models: Model[], ctx: EmitterContext, discCtx?: D
|
|
|
161
161
|
let initializer = '';
|
|
162
162
|
let setterModifier = '';
|
|
163
163
|
|
|
164
|
-
if (constInit !== null) {
|
|
164
|
+
if (constInit !== null && !isOptional) {
|
|
165
165
|
// Discriminator-style single-value enum/literal: emit with a const
|
|
166
166
|
// initializer and a non-public setter so callers can't drift the
|
|
167
167
|
// wire value. The converter still reads whatever the server sends.
|
|
168
|
+
// Only for required fields — optional literal fields must be nullable
|
|
169
|
+
// so absent keys round-trip correctly.
|
|
168
170
|
csType = baseType;
|
|
169
171
|
initializer = ` = ${constInit};`;
|
|
170
172
|
setterModifier = 'internal ';
|
package/src/go/fixtures.ts
CHANGED
|
@@ -24,12 +24,11 @@ export const ID_PREFIXES: Record<string, string> = {
|
|
|
24
24
|
/**
|
|
25
25
|
* Generate JSON fixture files for test data.
|
|
26
26
|
*/
|
|
27
|
-
export function generateFixtures(spec: {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (spec.models.length === 0) return [];
|
|
27
|
+
export function generateFixtures(spec: { models: Model[]; enums: Enum[]; services: any[] }): {
|
|
28
|
+
files: { path: string; content: string }[];
|
|
29
|
+
pathRewrites: Map<string, string>;
|
|
30
|
+
} {
|
|
31
|
+
if (spec.models.length === 0) return { files: [], pathRewrites: new Map() };
|
|
33
32
|
|
|
34
33
|
const modelMap = new Map(spec.models.map((m) => [m.name, m]));
|
|
35
34
|
const enumMap = new Map(spec.enums.map((e) => [e.name, e]));
|
|
@@ -96,7 +95,7 @@ export function generateFixtures(spec: {
|
|
|
96
95
|
// Remove duplicate files (they'll reference the canonical)
|
|
97
96
|
const deduped = files.filter((f) => !pathRewrites.has(f.path));
|
|
98
97
|
|
|
99
|
-
return deduped;
|
|
98
|
+
return { files: deduped, pathRewrites };
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
function unwrapListModel(model: Model, modelMap: Map<string, Model>): Model | null {
|
package/src/go/tests.ts
CHANGED
|
@@ -72,7 +72,7 @@ export function generateTests(spec: ApiSpec, ctx: EmitterContext): GeneratedFile
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
// Generate fixture JSON files
|
|
75
|
-
const fixtures = generateFixtures(spec);
|
|
75
|
+
const { files: fixtures, pathRewrites: fixtureRewrites } = generateFixtures(spec);
|
|
76
76
|
for (const fixture of fixtures) {
|
|
77
77
|
files.push({
|
|
78
78
|
path: fixture.path,
|
|
@@ -97,7 +97,7 @@ export function generateTests(spec: ApiSpec, ctx: EmitterContext): GeneratedFile
|
|
|
97
97
|
for (const { name: mountName, operations } of testEntries) {
|
|
98
98
|
if (operations.length === 0) continue;
|
|
99
99
|
const mergedService: Service = { name: mountName, operations };
|
|
100
|
-
const testFile = generateServiceTest(mergedService, spec, ctx, accessPaths);
|
|
100
|
+
const testFile = generateServiceTest(mergedService, spec, ctx, accessPaths, fixtureRewrites);
|
|
101
101
|
if (testFile) files.push(testFile);
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -109,6 +109,7 @@ function generateServiceTest(
|
|
|
109
109
|
spec: ApiSpec,
|
|
110
110
|
ctx: EmitterContext,
|
|
111
111
|
_accessPaths: Map<string, string>,
|
|
112
|
+
fixtureRewrites: Map<string, string>,
|
|
112
113
|
): GeneratedFile | null {
|
|
113
114
|
if (service.operations.length === 0) return null;
|
|
114
115
|
|
|
@@ -213,6 +214,10 @@ function generateServiceTest(
|
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
fixturePath = `testdata/list_${fileName(resolved.name)}.json`;
|
|
217
|
+
// If this fixture was deduplicated, use the canonical path
|
|
218
|
+
if (fixtureRewrites.has(fixturePath)) {
|
|
219
|
+
fixturePath = fixtureRewrites.get(fixturePath)!;
|
|
220
|
+
}
|
|
216
221
|
}
|
|
217
222
|
}
|
|
218
223
|
|
package/src/kotlin/models.ts
CHANGED
|
@@ -304,11 +304,13 @@ function renderFields(fields: Field[], overrideFields: Set<string> = new Set()):
|
|
|
304
304
|
let kotlinType: string;
|
|
305
305
|
let defaultExpr: string | null = null;
|
|
306
306
|
|
|
307
|
-
// Const literal fields:
|
|
308
|
-
//
|
|
307
|
+
// Const literal fields: emit a hardcoded default matching the literal
|
|
308
|
+
// value so callers don't have to pass it — but only when the field is
|
|
309
|
+
// required. Optional literal fields must default to null so that absent
|
|
310
|
+
// keys round-trip correctly.
|
|
309
311
|
const literalDefault = literalDefaultExpr(field.type);
|
|
310
312
|
|
|
311
|
-
if (literalDefault !== null) {
|
|
313
|
+
if (literalDefault !== null && field.required) {
|
|
312
314
|
kotlinType = baseType;
|
|
313
315
|
defaultExpr = literalDefault;
|
|
314
316
|
} else if (!field.required) {
|
package/src/python/client.ts
CHANGED
|
@@ -163,15 +163,17 @@ function generateWorkOSClient(spec: ApiSpec, ctx: EmitterContext): GeneratedFile
|
|
|
163
163
|
lines.push(importLine);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
-
// Non-spec service imports —
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
// Non-spec service imports — emitted as plain imports (not wrapped in
|
|
167
|
+
// ignore markers). The overwriteWithPreservedRegions() machinery in oagen
|
|
168
|
+
// relocates top-level ignore blocks to EOF because they have no containing
|
|
169
|
+
// class, stripping the markers from the imports while spliceExtraImports()
|
|
170
|
+
// preserves the bare import lines. Emitting them directly avoids the
|
|
171
|
+
// displacement entirely; spliceExtraImports() will preserve any additional
|
|
172
|
+
// hand-added imports from the existing file on subsequent regenerations.
|
|
170
173
|
for (const s of NON_SPEC_SERVICES) {
|
|
171
174
|
const w = PYTHON_NON_SPEC_WIRING[s.id];
|
|
172
175
|
if (w) lines.push(w.importLine);
|
|
173
176
|
}
|
|
174
|
-
lines.push('# @oagen-ignore-end');
|
|
175
177
|
lines.push('');
|
|
176
178
|
lines.push('');
|
|
177
179
|
|
package/src/python/models.ts
CHANGED
|
@@ -357,8 +357,8 @@ export function generateModels(models: Model[], ctx: EmitterContext): GeneratedF
|
|
|
357
357
|
const wireKey = field.name; // Wire keys are snake_case from the spec
|
|
358
358
|
const isRequired = !isOptionalField(model.name, field, ctx);
|
|
359
359
|
let accessor: string;
|
|
360
|
-
if (field.type.kind === 'literal') {
|
|
361
|
-
//
|
|
360
|
+
if (field.type.kind === 'literal' && isRequired) {
|
|
361
|
+
// Required literal fields have a statically known value; use .get() with a default
|
|
362
362
|
// so deserialization is resilient when the API omits the key.
|
|
363
363
|
accessor = `data.get("${wireKey}", ${pythonLiteralDefault(field.type.value)})`;
|
|
364
364
|
} else {
|