@workos/oagen-emitters 0.12.0 → 0.12.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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{plugin-C408Wh-o.mjs → plugin-CmfzawTp.mjs} +825 -66
- package/dist/plugin-CmfzawTp.mjs.map +1 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +1 -1
- package/package.json +9 -9
- package/src/rust/fixtures.ts +87 -1
- package/src/rust/models.ts +17 -2
- package/src/rust/resources.ts +697 -62
- package/src/rust/tests.ts +540 -20
- package/test/rust/fixtures.test.ts +227 -0
- package/test/rust/models.test.ts +38 -0
- package/test/rust/resources.test.ts +505 -2
- package/test/rust/tests.test.ts +504 -0
- package/dist/plugin-C408Wh-o.mjs.map +0 -1
package/dist/plugin.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;cA0Ba,oBAAA,EAAsB,
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;cA0Ba,oBAAA,EAAsB,IAAI,CAAC,WAAA"}
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as workosEmittersPlugin } from "./plugin-
|
|
1
|
+
import { t as workosEmittersPlugin } from "./plugin-CmfzawTp.mjs";
|
|
2
2
|
export { workosEmittersPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workos/oagen-emitters",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "WorkOS' oagen emitters",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "WorkOS",
|
|
@@ -38,22 +38,22 @@
|
|
|
38
38
|
"prepare": "husky"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@commitlint/cli": "^
|
|
42
|
-
"@commitlint/config-conventional": "^
|
|
43
|
-
"@types/node": "^25.
|
|
41
|
+
"@commitlint/cli": "^21.0.1",
|
|
42
|
+
"@commitlint/config-conventional": "^21.0.1",
|
|
43
|
+
"@types/node": "^25.7.0",
|
|
44
44
|
"husky": "^9.1.7",
|
|
45
|
-
"oxfmt": "^0.
|
|
46
|
-
"oxlint": "^1.
|
|
45
|
+
"oxfmt": "^0.49.0",
|
|
46
|
+
"oxlint": "^1.64.0",
|
|
47
47
|
"prettier": "^3.8.3",
|
|
48
|
-
"tsdown": "^0.
|
|
48
|
+
"tsdown": "^0.22.0",
|
|
49
49
|
"tsx": "^4.21.0",
|
|
50
50
|
"typescript": "^6.0.3",
|
|
51
|
-
"vitest": "^4.1.
|
|
51
|
+
"vitest": "^4.1.6"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=24.10.0"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@workos/oagen": "^0.18.
|
|
57
|
+
"@workos/oagen": "^0.18.2"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/rust/fixtures.ts
CHANGED
|
@@ -41,7 +41,12 @@ export function generateModelFixture(
|
|
|
41
41
|
|
|
42
42
|
for (const field of model.fields) {
|
|
43
43
|
if (!field.required) continue;
|
|
44
|
-
|
|
44
|
+
// Prefer the spec `example` value when it is shape-compatible with the
|
|
45
|
+
// declared type. Falls back to the placeholder generator when no example
|
|
46
|
+
// is provided or when the example would not deserialize cleanly.
|
|
47
|
+
const fromExample = exampleFromSpec(field.example, field.type, enumMap);
|
|
48
|
+
result[field.name] =
|
|
49
|
+
fromExample !== undefined ? fromExample : exampleFor(field.type, modelMap, enumMap, visiting, field.name);
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
visiting.delete(model.name);
|
|
@@ -108,3 +113,84 @@ export function exampleFor(
|
|
|
108
113
|
}
|
|
109
114
|
}
|
|
110
115
|
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Resolve a spec-provided `example` against a TypeRef and return the value to
|
|
119
|
+
* embed in the fixture, or `undefined` when the example cannot be used safely.
|
|
120
|
+
*
|
|
121
|
+
* "Safely" means the value would round-trip through serde to the generated
|
|
122
|
+
* Rust type. We deliberately only accept primitives, enum string/number
|
|
123
|
+
* values, and homogenous arrays of those; nested object examples (which the
|
|
124
|
+
* spec sometimes supplies as illustrative metadata blobs) are skipped because
|
|
125
|
+
* they rarely match the strict struct shape Rust expects.
|
|
126
|
+
*/
|
|
127
|
+
export function exampleFromSpec(example: unknown, type: TypeRef, enumMap: Map<string, Enum>): unknown {
|
|
128
|
+
if (example === undefined) return undefined;
|
|
129
|
+
// Spec authors sometimes use `null` as a sentinel; let placeholder gen
|
|
130
|
+
// handle nullable types so we don't emit `null` for required fields.
|
|
131
|
+
if (example === null) return undefined;
|
|
132
|
+
return matchExampleToType(example, type, enumMap);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function matchExampleToType(value: unknown, type: TypeRef, enumMap: Map<string, Enum>): unknown {
|
|
136
|
+
switch (type.kind) {
|
|
137
|
+
case 'primitive':
|
|
138
|
+
return matchPrimitive(value, type.type);
|
|
139
|
+
case 'literal':
|
|
140
|
+
return value === type.value ? value : undefined;
|
|
141
|
+
case 'enum': {
|
|
142
|
+
const e = enumMap.get(type.name);
|
|
143
|
+
if (!e) return undefined;
|
|
144
|
+
const ok = e.values.some((v) => v.value === value);
|
|
145
|
+
return ok ? value : undefined;
|
|
146
|
+
}
|
|
147
|
+
case 'array': {
|
|
148
|
+
if (!Array.isArray(value)) return undefined;
|
|
149
|
+
const out: unknown[] = [];
|
|
150
|
+
for (const item of value) {
|
|
151
|
+
const matched = matchExampleToType(item, type.items, enumMap);
|
|
152
|
+
if (matched === undefined) return undefined;
|
|
153
|
+
out.push(matched);
|
|
154
|
+
}
|
|
155
|
+
// Empty arrays are valid but unhelpful in fixtures — fall back so the
|
|
156
|
+
// placeholder generator can produce a one-element example.
|
|
157
|
+
if (out.length === 0) return undefined;
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
case 'nullable':
|
|
161
|
+
return matchExampleToType(value, type.inner, enumMap);
|
|
162
|
+
case 'map':
|
|
163
|
+
// Map examples are usually free-form metadata blobs that match
|
|
164
|
+
// `HashMap<String, _>`; only accept plain objects with string-keyed values.
|
|
165
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;
|
|
166
|
+
return value;
|
|
167
|
+
case 'union': {
|
|
168
|
+
for (const variant of type.variants) {
|
|
169
|
+
const matched = matchExampleToType(value, variant, enumMap);
|
|
170
|
+
if (matched !== undefined) return matched;
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
case 'model':
|
|
175
|
+
// Model-shaped examples are too risky to copy verbatim: they rarely
|
|
176
|
+
// supply every required field and may use wire names that don't align
|
|
177
|
+
// with the generated struct. Let the recursive generator handle them.
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function matchPrimitive(value: unknown, primitive: 'string' | 'integer' | 'number' | 'boolean' | 'unknown'): unknown {
|
|
183
|
+
switch (primitive) {
|
|
184
|
+
case 'string':
|
|
185
|
+
return typeof value === 'string' ? value : undefined;
|
|
186
|
+
case 'integer':
|
|
187
|
+
return typeof value === 'number' && Number.isInteger(value) ? value : undefined;
|
|
188
|
+
case 'number':
|
|
189
|
+
return typeof value === 'number' ? value : undefined;
|
|
190
|
+
case 'boolean':
|
|
191
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
192
|
+
case 'unknown':
|
|
193
|
+
// `unknown` deserialises to `serde_json::Value`, so any JSON value works.
|
|
194
|
+
return value;
|
|
195
|
+
}
|
|
196
|
+
}
|
package/src/rust/models.ts
CHANGED
|
@@ -105,8 +105,13 @@ function resolveFieldNames(fields: Field[]): string[] {
|
|
|
105
105
|
|
|
106
106
|
function renderField(field: Field, rustField: string, modelName: string, registry: UnionRegistry): string {
|
|
107
107
|
const lines: string[] = [];
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
const hasDescription = !!field.description;
|
|
109
|
+
if (hasDescription) {
|
|
110
|
+
for (const c of docComment(field.description!)) lines.push(` ${c}`);
|
|
111
|
+
}
|
|
112
|
+
if (field.default != null) {
|
|
113
|
+
if (hasDescription) lines.push(' ///');
|
|
114
|
+
lines.push(` /// Defaults to \`${formatDefault(field.default)}\`.`);
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
const rename = rustField !== field.name ? field.name : null;
|
|
@@ -148,3 +153,13 @@ function docComment(text: string): string[] {
|
|
|
148
153
|
.filter((l) => l.length > 0)
|
|
149
154
|
.map((l) => `/// ${l}`);
|
|
150
155
|
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Render a spec-level default value for inclusion in a doc comment. Strings
|
|
159
|
+
* render bare (e.g. `desc`) so they nest naturally inside the surrounding
|
|
160
|
+
* backticks; numbers/booleans use JSON encoding.
|
|
161
|
+
*/
|
|
162
|
+
function formatDefault(value: unknown): string {
|
|
163
|
+
if (typeof value === 'string') return value;
|
|
164
|
+
return JSON.stringify(value);
|
|
165
|
+
}
|