effect-app 4.0.0-beta.61 → 4.0.0-beta.62
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/CHANGELOG.md +6 -0
- package/dist/Schema/SpecialJsonSchema.d.ts +10 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -1
- package/dist/Schema/SpecialJsonSchema.js +43 -3
- package/dist/Schema/SpecialOpenApi.d.ts +4 -2
- package/dist/Schema/SpecialOpenApi.d.ts.map +1 -1
- package/dist/Schema/SpecialOpenApi.js +8 -5
- package/package.json +1 -1
- package/src/Schema/SpecialJsonSchema.ts +49 -2
- package/src/Schema/SpecialOpenApi.ts +8 -4
- package/test/special.test.ts +210 -3
package/CHANGELOG.md
CHANGED
|
@@ -37,4 +37,14 @@ import { type JsonSchema, type Schema } from "effect";
|
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
39
|
export declare function specialJsonSchemaDocument(schema: Schema.Top, options?: Schema.ToJsonSchemaOptions): JsonSchema.Document<"draft-2020-12">;
|
|
40
|
+
/**
|
|
41
|
+
* Flattens `allOf` entries into the parent when the parent already has a
|
|
42
|
+
* `type` and every `allOf` entry is a plain constraint object (no `$ref`,
|
|
43
|
+
* no `type`). Merged properties from `allOf` entries win on conflict.
|
|
44
|
+
*/
|
|
45
|
+
export declare function flattenSimpleAllOf(obj: unknown): unknown;
|
|
46
|
+
/**
|
|
47
|
+
* Applies JSON Schema post-processing: flattens simple allOf.
|
|
48
|
+
*/
|
|
49
|
+
export declare function postProcessJsonSchema(obj: JsonSchema.JsonSchema): JsonSchema.JsonSchema;
|
|
40
50
|
//# sourceMappingURL=SpecialJsonSchema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpecialJsonSchema.d.ts","sourceRoot":"","sources":["../../src/Schema/SpecialJsonSchema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAa,KAAK,UAAU,EAAE,KAAK,MAAM,EAAwB,MAAM,QAAQ,CAAA;AAEtF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,GAAG,EAClB,OAAO,CAAC,EAAE,MAAM,CAAC,mBAAmB,GACnC,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"SpecialJsonSchema.d.ts","sourceRoot":"","sources":["../../src/Schema/SpecialJsonSchema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAa,KAAK,UAAU,EAAE,KAAK,MAAM,EAAwB,MAAM,QAAQ,CAAA;AAEtF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,GAAG,EAClB,OAAO,CAAC,EAAE,MAAM,CAAC,mBAAmB,GACnC,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAC,CAatC;AA6ID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CA6BxD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,UAAU,CAEvF"}
|
|
@@ -40,10 +40,14 @@ export function specialJsonSchemaDocument(schema, options) {
|
|
|
40
40
|
const doc = SchemaRepresentation.fromAST(schema.ast);
|
|
41
41
|
const deduped = deduplicateReferences(doc);
|
|
42
42
|
const jd = SchemaRepresentation.toJsonSchemaDocument(deduped, options);
|
|
43
|
+
const processedDefs = {};
|
|
44
|
+
for (const [key, def] of Object.entries(jd.definitions)) {
|
|
45
|
+
processedDefs[key] = postProcessJsonSchema(def);
|
|
46
|
+
}
|
|
43
47
|
return {
|
|
44
48
|
dialect: "draft-2020-12",
|
|
45
|
-
schema: jd.schema,
|
|
46
|
-
definitions:
|
|
49
|
+
schema: postProcessJsonSchema(jd.schema),
|
|
50
|
+
definitions: processedDefs
|
|
47
51
|
};
|
|
48
52
|
}
|
|
49
53
|
/**
|
|
@@ -175,6 +179,42 @@ function rewriteRefs(rep, remapping) {
|
|
|
175
179
|
return rep;
|
|
176
180
|
}
|
|
177
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Flattens `allOf` entries into the parent when the parent already has a
|
|
184
|
+
* `type` and every `allOf` entry is a plain constraint object (no `$ref`,
|
|
185
|
+
* no `type`). Merged properties from `allOf` entries win on conflict.
|
|
186
|
+
*/
|
|
187
|
+
export function flattenSimpleAllOf(obj) {
|
|
188
|
+
if (obj === null || typeof obj !== "object")
|
|
189
|
+
return obj;
|
|
190
|
+
if (globalThis.Array.isArray(obj)) {
|
|
191
|
+
return obj.map(flattenSimpleAllOf);
|
|
192
|
+
}
|
|
193
|
+
const record = obj;
|
|
194
|
+
const result = {};
|
|
195
|
+
for (const [key, value] of Object.entries(record)) {
|
|
196
|
+
result[key] = flattenSimpleAllOf(value);
|
|
197
|
+
}
|
|
198
|
+
if (result["type"] && globalThis.Array.isArray(result["allOf"])) {
|
|
199
|
+
const allOf = result["allOf"];
|
|
200
|
+
const canFlatten = allOf.every((entry) => typeof entry === "object" && entry !== null && !("$ref" in entry) && !("type" in entry));
|
|
201
|
+
if (canFlatten) {
|
|
202
|
+
const { allOf: _, ...rest } = result;
|
|
203
|
+
let merged = { ...rest };
|
|
204
|
+
for (const entry of allOf) {
|
|
205
|
+
merged = { ...merged, ...entry };
|
|
206
|
+
}
|
|
207
|
+
return merged;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Applies JSON Schema post-processing: flattens simple allOf.
|
|
214
|
+
*/
|
|
215
|
+
export function postProcessJsonSchema(obj) {
|
|
216
|
+
return flattenSimpleAllOf(obj);
|
|
217
|
+
}
|
|
178
218
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
179
219
|
function rewriteChecks(checks, remapping) {
|
|
180
220
|
return checks.map((c) => {
|
|
@@ -196,4 +236,4 @@ function rewriteChecks(checks, remapping) {
|
|
|
196
236
|
}
|
|
197
237
|
});
|
|
198
238
|
}
|
|
199
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
239
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3BlY2lhbEpzb25TY2hlbWEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvU2NoZW1hL1NwZWNpYWxKc29uU2NoZW1hLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7OztHQVFHO0FBQ0gsT0FBTyxFQUFFLFNBQVMsRUFBZ0Msb0JBQW9CLEVBQUUsTUFBTSxRQUFRLENBQUE7QUFFdEY7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJCRztBQUNILE1BQU0sVUFBVSx5QkFBeUIsQ0FDdkMsTUFBa0IsRUFDbEIsT0FBb0M7SUFFcEMsTUFBTSxHQUFHLEdBQUcsb0JBQW9CLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUNwRCxNQUFNLE9BQU8sR0FBRyxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUMxQyxNQUFNLEVBQUUsR0FBRyxvQkFBb0IsQ0FBQyxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUE7SUFDdEUsTUFBTSxhQUFhLEdBQTJCLEVBQUUsQ0FBQTtJQUNoRCxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztRQUN4RCxhQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcscUJBQXFCLENBQUMsR0FBRyxDQUFDLENBQUE7SUFDakQsQ0FBQztJQUNELE9BQU87UUFDTCxPQUFPLEVBQUUsZUFBZTtRQUN4QixNQUFNLEVBQUUscUJBQXFCLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQztRQUN4QyxXQUFXLEVBQUUsYUFBYTtLQUMzQixDQUFBO0FBQ0gsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxxQkFBcUIsQ0FDNUIsR0FBa0M7SUFFbEMsTUFBTSxJQUFJLEdBQUcsR0FBRyxDQUFDLFVBQVUsQ0FBQTtJQUMzQixNQUFNLE9BQU8sR0FBRyxNQUFN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
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec
|
|
2
|
+
* SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec
|
|
3
|
+
* and applies JSON Schema post-processing (null removal, allOf flattening).
|
|
3
4
|
*
|
|
4
5
|
* When `OpenApi.fromApi` generates the spec, different AST nodes sharing the
|
|
5
6
|
* same identifier can produce duplicate entries (e.g. "X" and "X1") in
|
|
6
7
|
* `components.schemas`. This module provides a transform function that
|
|
7
|
-
* collapses those duplicates
|
|
8
|
+
* collapses those duplicates, rewrites all `$ref` pointers accordingly,
|
|
9
|
+
* and post-processes schemas for better codegen compatibility.
|
|
8
10
|
*
|
|
9
11
|
* Usage with the OpenApi `Transform` annotation:
|
|
10
12
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpecialOpenApi.d.ts","sourceRoot":"","sources":["../../src/Schema/SpecialOpenApi.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"SpecialOpenApi.d.ts","sourceRoot":"","sources":["../../src/Schema/SpecialOpenApi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAqDrB"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec
|
|
2
|
+
* SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec
|
|
3
|
+
* and applies JSON Schema post-processing (null removal, allOf flattening).
|
|
3
4
|
*
|
|
4
5
|
* When `OpenApi.fromApi` generates the spec, different AST nodes sharing the
|
|
5
6
|
* same identifier can produce duplicate entries (e.g. "X" and "X1") in
|
|
6
7
|
* `components.schemas`. This module provides a transform function that
|
|
7
|
-
* collapses those duplicates
|
|
8
|
+
* collapses those duplicates, rewrites all `$ref` pointers accordingly,
|
|
9
|
+
* and post-processes schemas for better codegen compatibility.
|
|
8
10
|
*
|
|
9
11
|
* Usage with the OpenApi `Transform` annotation:
|
|
10
12
|
*
|
|
@@ -16,6 +18,7 @@
|
|
|
16
18
|
* .pipe(HttpApi.annotateContext(OpenApi.annotations({ transform: deduplicateOpenApiSchemas })))
|
|
17
19
|
* ```
|
|
18
20
|
*/
|
|
21
|
+
import { postProcessJsonSchema } from "./SpecialJsonSchema.js";
|
|
19
22
|
/**
|
|
20
23
|
* Deduplicates `components.schemas` entries in an OpenAPI spec.
|
|
21
24
|
*
|
|
@@ -66,7 +69,7 @@ export function deduplicateOpenApiSchemas(spec) {
|
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
if (remapping.size === 0)
|
|
69
|
-
return spec;
|
|
72
|
+
return postProcessJsonSchema(spec);
|
|
70
73
|
// Build new schemas object without duplicates
|
|
71
74
|
const newSchemas = {};
|
|
72
75
|
for (const key of keys) {
|
|
@@ -78,7 +81,7 @@ export function deduplicateOpenApiSchemas(spec) {
|
|
|
78
81
|
const newSpec = structuredClone(spec);
|
|
79
82
|
newSpec["components"]["schemas"] = newSchemas;
|
|
80
83
|
rewriteRefs(newSpec, remapping);
|
|
81
|
-
return newSpec;
|
|
84
|
+
return postProcessJsonSchema(newSpec);
|
|
82
85
|
}
|
|
83
86
|
/**
|
|
84
87
|
* Extracts the base identifier from a schema key by stripping trailing
|
|
@@ -117,4 +120,4 @@ function rewriteRefs(obj, remapping) {
|
|
|
117
120
|
rewriteRefs(value, remapping);
|
|
118
121
|
}
|
|
119
122
|
}
|
|
120
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
123
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3BlY2lhbE9wZW5BcGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvU2NoZW1hL1NwZWNpYWxPcGVuQXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBRUgsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sd0JBQXdCLENBQUE7QUFFOUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxVQUFVLHlCQUF5QixDQUN2QyxJQUF5QjtJQUV6QixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFvQyxDQUFBO0lBQ3hFLElBQUksQ0FBQyxVQUFVO1FBQUUsT0FBTyxJQUFJLENBQUE7SUFDNUIsTUFBTSxPQUFPLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBb0MsQ0FBQTtJQUN4RSxJQUFJLENBQUMsT0FBTztRQUFFLE9BQU8sSUFBSSxDQUFBO0lBRXpCLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDakMsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQTtJQUVsQyx3REFBd0Q7SUFDeEQsTUFBTSxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQXVELENBQUE7SUFDN0UsS0FBSyxNQUFNLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN2QixNQUFNLElBQUksR0FBRyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQTtRQUNuQyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBQ2hELE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDOUIsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDeEIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUE7UUFDMUMsQ0FBQzthQUFNLENBQUM7WUFDTixLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsR0FBRyxFQUFFLFdBQVcsRUFBRSxDQUFDLENBQUE7UUFDbEMsQ0FBQztJQUNILENBQUM7SUFFRCx3REFBd0Q7SUFDeEQsTUFBTSxTQUFTLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUE7SUFDM0MsS0FBSyxNQUFNLENBQUMsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLEVBQUUsQ0FBQztRQUMvQixJQUFJLEtBQUssQ0FBQyxNQUFNLElBQUksQ0FBQztZQUFFLFNBQVE7UUFDL0IsTUFBTSxJQUFJLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUEsQ0FBQywrQkFBK0I7UUFDdEUsS0FBSyxNQUFNLEtBQUssSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUMxQixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQTtZQUM3QyxJQUFJLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDNUIsU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxDQUFBO1lBQ3JDLENBQUM7aUJBQU0sQ0FBQztnQkFDTixJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1lBQ3hDLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVELElBQUksU0FBUyxDQUFDLElBQUksS0FBSyxDQUFDO1FBQUUsT0FBTyxxQkFBcUIsQ0FBQyxJQUFJLENBQXdCLENBQUE7SUFFbkYsOENBQThDO0lBQzlDLE1BQU0sVUFBVSxHQUF3QixFQUFFLENBQUE7SUFDMUMsS0FBSyxNQUFNLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3hCLFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDaEMsQ0FBQztJQUNILENBQUM7SUFFRCxzRUFBc0U7SUFDdEUsTUFBTSxPQUFPLEdBQUcsZUFBZSxDQUFDLElBQUksQ0FBQyxDQUFBO0lBQ3JDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQyxTQUFTLENBQUMsR0FBRyxVQUFVLENBQUE7SUFDN0MsV0FBVyxDQUFDLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQTtJQUUvQixPQUFPLHFCQUFxQixDQUFDLE9BQU8sQ0FBd0IsQ0FBQTtBQUM5RCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQVMsaUJBQWlCLENBQUMsR0FBVztJQUNwQyxNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxDQUFBO0lBQ3ZDLE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQTtBQUNoQyxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBUyxXQUFXLENBQUMsR0FBUSxFQUFFLFNBQThCO0lBQzNELElBQUksR0FBRyxLQUFLLElBQUksSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRO1FBQUUsT0FBTTtJQUVuRCxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN2QixLQUFLLE1BQU0sSUFBSSxJQUFJLEdBQUcsRUFBRSxDQUFDO1lBQ3ZCLFdBQVcsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUE7UUFDOUIsQ0FBQztRQUNELE9BQU07SUFDUixDQUFDO0lBRUQsSUFBSSxPQUFPLEdBQUcsQ0FBQyxJQUFJLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDakMsbURBQW1EO1FBQ25ELE1BQU0sTUFBTSxHQUFHLHVCQUF1QixDQUFBO1FBQ3RDLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUNoQyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUE7WUFDNUMsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUN2QyxJQUFJLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDNUIsR0FBRyxDQUFDLElBQUksR0FBRyxNQUFNLEdBQUcsU0FBUyxDQUFBO1lBQy9CLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVELEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3ZDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsU0FBUyxDQUFDLENBQUE7SUFDL0IsQ0FBQztBQUNILENBQUMifQ==
|
package/package.json
CHANGED
|
@@ -44,10 +44,14 @@ export function specialJsonSchemaDocument(
|
|
|
44
44
|
const doc = SchemaRepresentation.fromAST(schema.ast)
|
|
45
45
|
const deduped = deduplicateReferences(doc)
|
|
46
46
|
const jd = SchemaRepresentation.toJsonSchemaDocument(deduped, options)
|
|
47
|
+
const processedDefs: JsonSchema.Definitions = {}
|
|
48
|
+
for (const [key, def] of Object.entries(jd.definitions)) {
|
|
49
|
+
processedDefs[key] = postProcessJsonSchema(def)
|
|
50
|
+
}
|
|
47
51
|
return {
|
|
48
52
|
dialect: "draft-2020-12",
|
|
49
|
-
schema: jd.schema,
|
|
50
|
-
definitions:
|
|
53
|
+
schema: postProcessJsonSchema(jd.schema),
|
|
54
|
+
definitions: processedDefs
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -190,6 +194,49 @@ function rewriteRefs(
|
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Flattens `allOf` entries into the parent when the parent already has a
|
|
199
|
+
* `type` and every `allOf` entry is a plain constraint object (no `$ref`,
|
|
200
|
+
* no `type`). Merged properties from `allOf` entries win on conflict.
|
|
201
|
+
*/
|
|
202
|
+
export function flattenSimpleAllOf(obj: unknown): unknown {
|
|
203
|
+
if (obj === null || typeof obj !== "object") return obj
|
|
204
|
+
|
|
205
|
+
if (globalThis.Array.isArray(obj)) {
|
|
206
|
+
return obj.map(flattenSimpleAllOf)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const record = obj as Record<string, unknown>
|
|
210
|
+
const result: Record<string, unknown> = {}
|
|
211
|
+
for (const [key, value] of Object.entries(record)) {
|
|
212
|
+
result[key] = flattenSimpleAllOf(value)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (result["type"] && globalThis.Array.isArray(result["allOf"])) {
|
|
216
|
+
const allOf = result["allOf"] as Array<Record<string, unknown>>
|
|
217
|
+
const canFlatten = allOf.every((entry) =>
|
|
218
|
+
typeof entry === "object" && entry !== null && !("$ref" in entry) && !("type" in entry)
|
|
219
|
+
)
|
|
220
|
+
if (canFlatten) {
|
|
221
|
+
const { allOf: _, ...rest } = result
|
|
222
|
+
let merged: Record<string, unknown> = { ...rest }
|
|
223
|
+
for (const entry of allOf) {
|
|
224
|
+
merged = { ...merged, ...entry }
|
|
225
|
+
}
|
|
226
|
+
return merged
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return result
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Applies JSON Schema post-processing: flattens simple allOf.
|
|
235
|
+
*/
|
|
236
|
+
export function postProcessJsonSchema(obj: JsonSchema.JsonSchema): JsonSchema.JsonSchema {
|
|
237
|
+
return flattenSimpleAllOf(obj) as JsonSchema.JsonSchema
|
|
238
|
+
}
|
|
239
|
+
|
|
193
240
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
194
241
|
function rewriteChecks<M>(
|
|
195
242
|
checks: ReadonlyArray<SchemaRepresentation.Check<M>>,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec
|
|
2
|
+
* SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec
|
|
3
|
+
* and applies JSON Schema post-processing (null removal, allOf flattening).
|
|
3
4
|
*
|
|
4
5
|
* When `OpenApi.fromApi` generates the spec, different AST nodes sharing the
|
|
5
6
|
* same identifier can produce duplicate entries (e.g. "X" and "X1") in
|
|
6
7
|
* `components.schemas`. This module provides a transform function that
|
|
7
|
-
* collapses those duplicates
|
|
8
|
+
* collapses those duplicates, rewrites all `$ref` pointers accordingly,
|
|
9
|
+
* and post-processes schemas for better codegen compatibility.
|
|
8
10
|
*
|
|
9
11
|
* Usage with the OpenApi `Transform` annotation:
|
|
10
12
|
*
|
|
@@ -17,6 +19,8 @@
|
|
|
17
19
|
* ```
|
|
18
20
|
*/
|
|
19
21
|
|
|
22
|
+
import { postProcessJsonSchema } from "./SpecialJsonSchema.js"
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* Deduplicates `components.schemas` entries in an OpenAPI spec.
|
|
22
26
|
*
|
|
@@ -66,7 +70,7 @@ export function deduplicateOpenApiSchemas(
|
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
if (remapping.size === 0) return spec
|
|
73
|
+
if (remapping.size === 0) return postProcessJsonSchema(spec) as Record<string, any>
|
|
70
74
|
|
|
71
75
|
// Build new schemas object without duplicates
|
|
72
76
|
const newSchemas: Record<string, any> = {}
|
|
@@ -81,7 +85,7 @@ export function deduplicateOpenApiSchemas(
|
|
|
81
85
|
newSpec["components"]["schemas"] = newSchemas
|
|
82
86
|
rewriteRefs(newSpec, remapping)
|
|
83
87
|
|
|
84
|
-
return newSpec
|
|
88
|
+
return postProcessJsonSchema(newSpec) as Record<string, any>
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
/**
|
package/test/special.test.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Option, Predicate, Schema, SchemaGetter } from "effect"
|
|
2
2
|
import { InvalidStateError, LoginError, NotFoundError, NotLoggedInError, OptimisticConcurrencyException, ServiceUnavailableError, UnauthorizedError, ValidationError } from "effect-app/client/errors"
|
|
3
|
+
import * as AppSchema from "effect-app/Schema"
|
|
3
4
|
import { Class, TaggedClass } from "effect-app/Schema/Class"
|
|
4
|
-
import { specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema"
|
|
5
|
+
import { flattenSimpleAllOf, specialJsonSchemaDocument } from "effect-app/Schema/SpecialJsonSchema"
|
|
5
6
|
import { deduplicateOpenApiSchemas } from "effect-app/Schema/SpecialOpenApi"
|
|
6
7
|
import * as S from "effect/Schema"
|
|
7
8
|
import { describe, expect, it } from "vitest"
|
|
@@ -447,7 +448,7 @@ describe("SpecialOpenApi", () => {
|
|
|
447
448
|
}
|
|
448
449
|
|
|
449
450
|
const result = deduplicateOpenApiSchemas(spec)
|
|
450
|
-
expect(result).
|
|
451
|
+
expect(result).toStrictEqual(spec)
|
|
451
452
|
})
|
|
452
453
|
|
|
453
454
|
it("rewrites nested $ref pointers in allOf/anyOf/oneOf", () => {
|
|
@@ -520,6 +521,212 @@ describe("SpecialOpenApi", () => {
|
|
|
520
521
|
|
|
521
522
|
it("handles spec without components gracefully", () => {
|
|
522
523
|
const spec = { openapi: "3.1.0", info: { title: "Test", version: "1.0" }, paths: {} }
|
|
523
|
-
|
|
524
|
+
const result = deduplicateOpenApiSchemas(spec)
|
|
525
|
+
expect(result).toStrictEqual(spec)
|
|
526
|
+
})
|
|
527
|
+
|
|
528
|
+
it("flattens allOf in components.schemas", () => {
|
|
529
|
+
const spec = {
|
|
530
|
+
openapi: "3.1.0",
|
|
531
|
+
info: { title: "Test", version: "1.0" },
|
|
532
|
+
paths: {},
|
|
533
|
+
components: {
|
|
534
|
+
schemas: {
|
|
535
|
+
PositiveInt: {
|
|
536
|
+
type: "integer",
|
|
537
|
+
allOf: [{ exclusiveMinimum: 0, title: "PositiveInt" }]
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const result = deduplicateOpenApiSchemas(spec) as any
|
|
544
|
+
|
|
545
|
+
expect(result.components.schemas.PositiveInt).toStrictEqual({
|
|
546
|
+
type: "integer",
|
|
547
|
+
exclusiveMinimum: 0,
|
|
548
|
+
title: "PositiveInt"
|
|
549
|
+
})
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
it("does not flatten allOf containing $ref entries", () => {
|
|
553
|
+
const spec = {
|
|
554
|
+
openapi: "3.1.0",
|
|
555
|
+
info: { title: "Test", version: "1.0" },
|
|
556
|
+
paths: {},
|
|
557
|
+
components: {
|
|
558
|
+
schemas: {
|
|
559
|
+
Composed: {
|
|
560
|
+
type: "object",
|
|
561
|
+
allOf: [{ $ref: "#/components/schemas/Base" }]
|
|
562
|
+
},
|
|
563
|
+
Base: { type: "object", properties: { id: { type: "string" } } }
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const result = deduplicateOpenApiSchemas(spec) as any
|
|
569
|
+
|
|
570
|
+
expect(result.components.schemas.Composed).toStrictEqual({
|
|
571
|
+
type: "object",
|
|
572
|
+
allOf: [{ $ref: "#/components/schemas/Base" }]
|
|
573
|
+
})
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
it("does not flatten allOf entries that define their own type", () => {
|
|
577
|
+
const spec = {
|
|
578
|
+
openapi: "3.1.0",
|
|
579
|
+
info: { title: "Test", version: "1.0" },
|
|
580
|
+
paths: {},
|
|
581
|
+
components: {
|
|
582
|
+
schemas: {
|
|
583
|
+
Mixed: {
|
|
584
|
+
type: "object",
|
|
585
|
+
allOf: [{ type: "string", minLength: 1 }]
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const result = deduplicateOpenApiSchemas(spec) as any
|
|
592
|
+
|
|
593
|
+
expect(result.components.schemas.Mixed).toStrictEqual({
|
|
594
|
+
type: "object",
|
|
595
|
+
allOf: [{ type: "string", minLength: 1 }]
|
|
596
|
+
})
|
|
597
|
+
})
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
describe("flattenSimpleAllOf", () => {
|
|
601
|
+
it("flattens constraint-only allOf into parent with type", () => {
|
|
602
|
+
const input = {
|
|
603
|
+
type: "integer",
|
|
604
|
+
allOf: [{ exclusiveMinimum: 0, title: "PositiveInt" }]
|
|
605
|
+
}
|
|
606
|
+
expect(flattenSimpleAllOf(input)).toStrictEqual({
|
|
607
|
+
type: "integer",
|
|
608
|
+
exclusiveMinimum: 0,
|
|
609
|
+
title: "PositiveInt"
|
|
610
|
+
})
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
it("flattens string type with multiple constraints", () => {
|
|
614
|
+
const input = {
|
|
615
|
+
type: "string",
|
|
616
|
+
allOf: [
|
|
617
|
+
{ minLength: 1, maxLength: 255 },
|
|
618
|
+
{ title: "NonEmptyString255" }
|
|
619
|
+
]
|
|
620
|
+
}
|
|
621
|
+
expect(flattenSimpleAllOf(input)).toStrictEqual({
|
|
622
|
+
type: "string",
|
|
623
|
+
minLength: 1,
|
|
624
|
+
maxLength: 255,
|
|
625
|
+
title: "NonEmptyString255"
|
|
626
|
+
})
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
it("does not flatten allOf with $ref", () => {
|
|
630
|
+
const input = {
|
|
631
|
+
type: "object",
|
|
632
|
+
allOf: [{ $ref: "#/components/schemas/Base" }]
|
|
633
|
+
}
|
|
634
|
+
expect(flattenSimpleAllOf(input)).toStrictEqual(input)
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
it("does not flatten allOf entries with their own type", () => {
|
|
638
|
+
const input = {
|
|
639
|
+
type: "object",
|
|
640
|
+
allOf: [{ type: "string", minLength: 1 }]
|
|
641
|
+
}
|
|
642
|
+
expect(flattenSimpleAllOf(input)).toStrictEqual(input)
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
it("allOf entry wins on property conflict", () => {
|
|
646
|
+
const input = {
|
|
647
|
+
type: "integer",
|
|
648
|
+
title: "OldTitle",
|
|
649
|
+
allOf: [{ title: "NewTitle", minimum: 0 }]
|
|
650
|
+
}
|
|
651
|
+
expect(flattenSimpleAllOf(input)).toStrictEqual({
|
|
652
|
+
type: "integer",
|
|
653
|
+
title: "NewTitle",
|
|
654
|
+
minimum: 0
|
|
655
|
+
})
|
|
656
|
+
})
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
describe("Post-processing integration — real Effect Schema types", () => {
|
|
660
|
+
it("PositiveInt — allOf flattened, no wrapping", () => {
|
|
661
|
+
const doc = specialJsonSchemaDocument(AppSchema.PositiveInt)
|
|
662
|
+
expect(doc.definitions["PositiveInt"]).toStrictEqual({
|
|
663
|
+
type: "integer",
|
|
664
|
+
exclusiveMinimum: 0,
|
|
665
|
+
title: "PositiveInt"
|
|
666
|
+
})
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
it("NonEmptyString255 — multiple allOf constraints merged", () => {
|
|
670
|
+
const doc = specialJsonSchemaDocument(AppSchema.NonEmptyString255)
|
|
671
|
+
expect(doc.definitions["NonEmptyString255"]).toStrictEqual({
|
|
672
|
+
type: "string",
|
|
673
|
+
minLength: 1,
|
|
674
|
+
maxLength: 255,
|
|
675
|
+
title: "NonEmptyString255"
|
|
676
|
+
})
|
|
677
|
+
})
|
|
678
|
+
|
|
679
|
+
it("NullOr(NonEmptyString64k) — null preserved in anyOf, allOf flattened in definition", () => {
|
|
680
|
+
const schema = S.Struct({ note: S.NullOr(AppSchema.NonEmptyString64k) })
|
|
681
|
+
const doc = specialJsonSchemaDocument(schema)
|
|
682
|
+
|
|
683
|
+
// null variant preserved (correct JSON Schema for NullOr)
|
|
684
|
+
expect(doc.schema).toStrictEqual({
|
|
685
|
+
type: "object",
|
|
686
|
+
properties: {
|
|
687
|
+
note: {
|
|
688
|
+
anyOf: [
|
|
689
|
+
{ $ref: "#/$defs/NonEmptyString64k" },
|
|
690
|
+
{ type: "null" }
|
|
691
|
+
]
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
required: ["note"],
|
|
695
|
+
additionalProperties: false
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
// allOf flattened in the referenced definition
|
|
699
|
+
expect(doc.definitions["NonEmptyString64k"]).toStrictEqual({
|
|
700
|
+
type: "string",
|
|
701
|
+
minLength: 1,
|
|
702
|
+
maxLength: 65536,
|
|
703
|
+
title: "NonEmptyString64k"
|
|
704
|
+
})
|
|
705
|
+
})
|
|
706
|
+
|
|
707
|
+
it("NonNegativeInt — allOf flattened", () => {
|
|
708
|
+
const doc = specialJsonSchemaDocument(AppSchema.NonNegativeInt)
|
|
709
|
+
expect(doc.definitions["NonNegativeInt"]).toStrictEqual({
|
|
710
|
+
type: "integer",
|
|
711
|
+
minimum: 0,
|
|
712
|
+
title: "NonNegativeInt"
|
|
713
|
+
})
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
it("NullOr union preserves null and non-null members in anyOf", () => {
|
|
717
|
+
const A = S.String.annotate({ identifier: "A", title: "A" })
|
|
718
|
+
const B = S.Boolean.annotate({ identifier: "B", title: "B" })
|
|
719
|
+
const schema = S.Struct({
|
|
720
|
+
value: S.NullOr(S.Union([A, B]))
|
|
721
|
+
})
|
|
722
|
+
const doc = specialJsonSchemaDocument(schema)
|
|
723
|
+
const valueProp = (doc.schema as Record<string, any>)["properties"]["value"]
|
|
724
|
+
|
|
725
|
+
expect(valueProp).toStrictEqual({
|
|
726
|
+
anyOf: [
|
|
727
|
+
{ anyOf: [{ $ref: "#/$defs/A" }, { $ref: "#/$defs/B" }] },
|
|
728
|
+
{ type: "null" }
|
|
729
|
+
]
|
|
730
|
+
})
|
|
524
731
|
})
|
|
525
732
|
})
|