effect-app 4.0.0-beta.23 → 4.0.0-beta.231
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 +1010 -0
- package/dist/Array.d.ts +3 -2
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +4 -4
- package/dist/Chunk.d.ts +1 -1
- package/dist/Chunk.d.ts.map +1 -1
- package/dist/Config/SecretURL.d.ts +4 -2
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +3 -6
- package/dist/Config/internal/configSecretURL.d.ts +1 -1
- package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
- package/dist/Config/internal/configSecretURL.js +2 -2
- package/dist/Config.d.ts +7 -0
- package/dist/Config.d.ts.map +1 -0
- package/dist/Config.js +6 -0
- package/dist/ConfigProvider.d.ts +39 -0
- package/dist/ConfigProvider.d.ts.map +1 -0
- package/dist/ConfigProvider.js +42 -0
- package/dist/Context.d.ts +42 -0
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +67 -0
- package/dist/Effect.d.ts +11 -10
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +5 -7
- package/dist/Function.d.ts +1 -1
- package/dist/Function.d.ts.map +1 -1
- package/dist/Inputify.type.d.ts +1 -1
- package/dist/Layer.d.ts +11 -7
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +3 -2
- package/dist/NonEmptySet.d.ts +4 -2
- package/dist/NonEmptySet.d.ts.map +1 -1
- package/dist/NonEmptySet.js +2 -2
- package/dist/Option.d.ts +2 -1
- package/dist/Option.d.ts.map +1 -1
- package/dist/Option.js +3 -1
- package/dist/Pure.d.ts +8 -6
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +17 -14
- package/dist/Schema/Class.d.ts +66 -20
- package/dist/Schema/Class.d.ts.map +1 -1
- package/dist/Schema/Class.js +192 -23
- package/dist/Schema/FastCheck.d.ts +1 -1
- package/dist/Schema/FastCheck.d.ts.map +1 -1
- package/dist/Schema/Methods.d.ts +1 -1
- package/dist/Schema/SchemaParser.d.ts +5 -0
- package/dist/Schema/SchemaParser.d.ts.map +1 -0
- package/dist/Schema/SchemaParser.js +6 -0
- package/dist/Schema/SpecialJsonSchema.d.ts +34 -0
- package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
- package/dist/Schema/SpecialJsonSchema.js +118 -0
- package/dist/Schema/SpecialOpenApi.d.ts +32 -0
- package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
- package/dist/Schema/SpecialOpenApi.js +123 -0
- package/dist/Schema/brand.d.ts +5 -3
- package/dist/Schema/brand.d.ts.map +1 -1
- package/dist/Schema/brand.js +3 -1
- package/dist/Schema/email.d.ts +1 -1
- package/dist/Schema/email.d.ts.map +1 -1
- package/dist/Schema/email.js +7 -4
- package/dist/Schema/ext.d.ts +339 -56
- package/dist/Schema/ext.d.ts.map +1 -1
- package/dist/Schema/ext.js +350 -53
- package/dist/Schema/moreStrings.d.ts +108 -26
- package/dist/Schema/moreStrings.d.ts.map +1 -1
- package/dist/Schema/moreStrings.js +45 -16
- package/dist/Schema/numbers.d.ts +55 -15
- package/dist/Schema/numbers.d.ts.map +1 -1
- package/dist/Schema/numbers.js +60 -12
- package/dist/Schema/phoneNumber.d.ts +1 -1
- package/dist/Schema/phoneNumber.d.ts.map +1 -1
- package/dist/Schema/phoneNumber.js +6 -3
- package/dist/Schema/schema.d.ts +1 -1
- package/dist/Schema/strings.d.ts +5 -5
- package/dist/Schema/strings.d.ts.map +1 -1
- package/dist/Schema/strings.js +1 -5
- package/dist/Schema.d.ts +214 -8
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +219 -21
- package/dist/Set.d.ts +5 -2
- package/dist/Set.d.ts.map +1 -1
- package/dist/Set.js +3 -2
- package/dist/TypeTest.d.ts +1 -1
- package/dist/Types.d.ts +1 -1
- package/dist/Widen.type.d.ts +1 -1
- package/dist/_ext/Array.d.ts +2 -2
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/Array.js +4 -2
- package/dist/_ext/date.d.ts +1 -1
- package/dist/_ext/misc.d.ts +5 -2
- package/dist/_ext/misc.d.ts.map +1 -1
- package/dist/_ext/misc.js +4 -2
- package/dist/_ext/ord.ext.d.ts +3 -2
- package/dist/_ext/ord.ext.d.ts.map +1 -1
- package/dist/_ext/ord.ext.js +2 -2
- package/dist/builtin.d.ts +1 -1
- package/dist/builtin.d.ts.map +1 -1
- package/dist/client/InvalidationKeys.d.ts +29 -0
- package/dist/client/InvalidationKeys.d.ts.map +1 -0
- package/dist/client/InvalidationKeys.js +33 -0
- package/dist/client/apiClientFactory.d.ts +20 -32
- package/dist/client/apiClientFactory.d.ts.map +1 -1
- package/dist/client/apiClientFactory.js +97 -34
- package/dist/client/clientFor.d.ts +51 -17
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +9 -1
- package/dist/client/errors.d.ts +49 -25
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +43 -17
- package/dist/client/makeClient.d.ts +481 -33
- package/dist/client/makeClient.d.ts.map +1 -1
- package/dist/client/makeClient.js +66 -24
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/faker.d.ts +1 -1
- package/dist/faker.d.ts.map +1 -1
- package/dist/http/Request.d.ts +2 -2
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +2 -2
- package/dist/http/internal/lib.d.ts +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/ids.d.ts +40 -12
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +25 -3
- package/dist/index.d.ts +5 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -8
- package/dist/logger.d.ts +1 -1
- package/dist/middleware.d.ts +14 -8
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +14 -8
- package/dist/rpc/Invalidation.d.ts +402 -0
- package/dist/rpc/Invalidation.d.ts.map +1 -0
- package/dist/rpc/Invalidation.js +150 -0
- package/dist/rpc/MiddlewareMaker.d.ts +11 -7
- package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
- package/dist/rpc/MiddlewareMaker.js +59 -38
- package/dist/rpc/RpcContextMap.d.ts +4 -4
- package/dist/rpc/RpcContextMap.d.ts.map +1 -1
- package/dist/rpc/RpcContextMap.js +4 -4
- package/dist/rpc/RpcMiddleware.d.ts +14 -10
- package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/rpc/RpcMiddleware.js +1 -1
- package/dist/rpc.d.ts +2 -2
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -2
- package/dist/transform.d.ts +2 -2
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +4 -5
- package/dist/utils/effectify.d.ts +2 -2
- package/dist/utils/effectify.d.ts.map +1 -1
- package/dist/utils/effectify.js +2 -2
- package/dist/utils/extend.d.ts +1 -1
- package/dist/utils/extend.d.ts.map +1 -1
- package/dist/utils/gen.d.ts +5 -5
- package/dist/utils/gen.d.ts.map +1 -1
- package/dist/utils/logLevel.d.ts +3 -3
- package/dist/utils/logLevel.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +5 -4
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +4 -4
- package/dist/utils.d.ts +35 -40
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +19 -27
- package/dist/validation/validators.d.ts +1 -1
- package/dist/validation/validators.d.ts.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/package.json +46 -24
- package/src/Array.ts +3 -3
- package/src/Config/SecretURL.ts +5 -2
- package/src/Config/internal/configSecretURL.ts +1 -1
- package/src/Config.ts +14 -0
- package/src/ConfigProvider.ts +48 -0
- package/src/{ServiceMap.ts → Context.ts} +56 -63
- package/src/Effect.ts +13 -14
- package/src/Layer.ts +10 -6
- package/src/NonEmptySet.ts +3 -1
- package/src/Option.ts +2 -0
- package/src/Pure.ts +21 -19
- package/src/Schema/Class.ts +274 -64
- package/src/Schema/SchemaParser.ts +12 -0
- package/src/Schema/SpecialJsonSchema.ts +139 -0
- package/src/Schema/SpecialOpenApi.ts +130 -0
- package/src/Schema/brand.ts +22 -2
- package/src/Schema/email.ts +7 -2
- package/src/Schema/ext.ts +431 -88
- package/src/Schema/moreStrings.ts +93 -37
- package/src/Schema/numbers.ts +64 -16
- package/src/Schema/phoneNumber.ts +5 -1
- package/src/Schema/strings.ts +4 -8
- package/src/Schema.ts +404 -18
- package/src/Set.ts +5 -1
- package/src/_ext/Array.ts +3 -1
- package/src/_ext/misc.ts +4 -1
- package/src/_ext/ord.ext.ts +2 -1
- package/src/client/InvalidationKeys.ts +50 -0
- package/src/client/apiClientFactory.ts +225 -131
- package/src/client/clientFor.ts +95 -29
- package/src/client/errors.ts +52 -26
- package/src/client/makeClient.ts +572 -71
- package/src/client.ts +1 -0
- package/src/http/Request.ts +1 -1
- package/src/ids.ts +25 -3
- package/src/index.ts +5 -10
- package/src/middleware.ts +13 -9
- package/src/rpc/Invalidation.ts +226 -0
- package/src/rpc/MiddlewareMaker.ts +82 -74
- package/src/rpc/README.md +2 -2
- package/src/rpc/RpcContextMap.ts +6 -5
- package/src/rpc/RpcMiddleware.ts +14 -11
- package/src/rpc.ts +1 -1
- package/src/transform.ts +3 -3
- package/src/utils/effectify.ts +1 -1
- package/src/utils/gen.ts +8 -8
- package/src/utils/logLevel.ts +1 -1
- package/src/utils/logger.ts +4 -3
- package/src/utils.ts +57 -134
- package/test/dist/rpc.test.d.ts.map +1 -1
- package/test/dist/secretURL.test.d.ts.map +1 -0
- package/test/dist/special.test.d.ts.map +1 -0
- package/test/dist/stream-error.types.d.ts +2 -0
- package/test/dist/stream-error.types.d.ts.map +1 -0
- package/test/dist/stream-error.types.js +27 -0
- package/test/moreStrings.test.ts +1 -1
- package/test/rpc.test.ts +46 -6
- package/test/schema.test.ts +585 -10
- package/test/secretURL.test.ts +160 -0
- package/test/special.test.ts +1026 -0
- package/test/utils.test.ts +7 -7
- package/tsconfig.base.json +3 -4
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +2 -2
- package/tsconfig.src.json +29 -29
- package/tsconfig.test.json +2 -2
- package/dist/Operations.d.ts +0 -123
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -29
- package/dist/ServiceMap.d.ts +0 -44
- package/dist/ServiceMap.d.ts.map +0 -1
- package/dist/ServiceMap.js +0 -91
- package/eslint.config.mjs +0 -26
- package/src/Operations.ts +0 -55
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpecialJsonSchema — A variant of Schema.toJsonSchemaDocument that
|
|
3
|
+
* post-processes the output (e.g. flattens simple allOf).
|
|
4
|
+
*/
|
|
5
|
+
import type * as JsonSchema from "effect/JsonSchema"
|
|
6
|
+
import type * as Schema from "effect/Schema"
|
|
7
|
+
import * as SchemaRepresentation from "effect/SchemaRepresentation"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Converts a schema to a JSON Schema Document (draft-2020-12), with
|
|
11
|
+
* post-processing that flattens simple allOf entries.
|
|
12
|
+
*/
|
|
13
|
+
export function specialJsonSchemaDocument(
|
|
14
|
+
schema: Schema.Top,
|
|
15
|
+
options?: Schema.ToJsonSchemaOptions
|
|
16
|
+
): JsonSchema.Document<"draft-2020-12"> {
|
|
17
|
+
const doc = SchemaRepresentation.fromAST(schema.ast)
|
|
18
|
+
const jd = SchemaRepresentation.toJsonSchemaDocument(doc, options)
|
|
19
|
+
const processedDefs: JsonSchema.Definitions = {}
|
|
20
|
+
for (const [key, def] of Object.entries(jd.definitions)) {
|
|
21
|
+
processedDefs[key] = postProcessJsonSchema(def)
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
dialect: "draft-2020-12",
|
|
25
|
+
schema: postProcessJsonSchema(jd.schema),
|
|
26
|
+
definitions: processedDefs
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Flattens `allOf` entries into the parent when the parent already has a
|
|
32
|
+
* `type` and every `allOf` entry is a plain constraint object (no `$ref`,
|
|
33
|
+
* no `type`). Merged properties from `allOf` entries win on conflict.
|
|
34
|
+
*/
|
|
35
|
+
export function flattenSimpleAllOf(obj: unknown): unknown {
|
|
36
|
+
if (obj === null || typeof obj !== "object") return obj
|
|
37
|
+
|
|
38
|
+
if (globalThis.Array.isArray(obj)) {
|
|
39
|
+
return obj.map(flattenSimpleAllOf)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const record = obj as Record<string, unknown>
|
|
43
|
+
const result: Record<string, unknown> = {}
|
|
44
|
+
for (const [key, value] of Object.entries(record)) {
|
|
45
|
+
result[key] = flattenSimpleAllOf(value)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (result["type"] && globalThis.Array.isArray(result["allOf"])) {
|
|
49
|
+
const allOf = result["allOf"] as Array<Record<string, unknown>>
|
|
50
|
+
const canFlatten = allOf.every((entry) =>
|
|
51
|
+
typeof entry === "object" && entry !== null && !("$ref" in entry) && !("type" in entry)
|
|
52
|
+
)
|
|
53
|
+
if (canFlatten) {
|
|
54
|
+
const { allOf: _, ...rest } = result
|
|
55
|
+
const merged: Record<string, unknown> = { ...rest }
|
|
56
|
+
for (const entry of allOf) {
|
|
57
|
+
Object.assign(merged, entry)
|
|
58
|
+
}
|
|
59
|
+
return merged
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return result
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Recursively removes `additionalProperties: false` from JSON Schema objects.
|
|
68
|
+
* Only removes when the value is exactly `false` -- other values are left intact.
|
|
69
|
+
*/
|
|
70
|
+
export function removeAdditionalPropertiesFalse(obj: unknown): unknown {
|
|
71
|
+
if (obj === null || typeof obj !== "object") return obj
|
|
72
|
+
|
|
73
|
+
if (globalThis.Array.isArray(obj)) {
|
|
74
|
+
return obj.map(removeAdditionalPropertiesFalse)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const record = obj as Record<string, unknown>
|
|
78
|
+
const result: Record<string, unknown> = {}
|
|
79
|
+
for (const [key, value] of Object.entries(record)) {
|
|
80
|
+
if (key === "additionalProperties" && value === false) continue
|
|
81
|
+
result[key] = removeAdditionalPropertiesFalse(value)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return result
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Flattens nested `anyOf` entries: if an anyOf entry is itself just `{ anyOf: [...] }`
|
|
89
|
+
* with no other keys, its children are inlined. If only one item remains, the anyOf
|
|
90
|
+
* wrapper is removed entirely.
|
|
91
|
+
*/
|
|
92
|
+
export function flattenNestedAnyOf(obj: unknown): unknown {
|
|
93
|
+
if (obj === null || typeof obj !== "object") return obj
|
|
94
|
+
if (globalThis.Array.isArray(obj)) return obj.map(flattenNestedAnyOf)
|
|
95
|
+
|
|
96
|
+
const record = obj as Record<string, unknown>
|
|
97
|
+
const result: Record<string, unknown> = {}
|
|
98
|
+
for (const [key, value] of Object.entries(record)) {
|
|
99
|
+
result[key] = flattenNestedAnyOf(value)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (globalThis.Array.isArray(result["anyOf"])) {
|
|
103
|
+
const anyOf = result["anyOf"] as Array<unknown>
|
|
104
|
+
const flattened: Array<unknown> = []
|
|
105
|
+
for (const entry of anyOf) {
|
|
106
|
+
if (
|
|
107
|
+
typeof entry === "object"
|
|
108
|
+
&& entry !== null
|
|
109
|
+
&& !globalThis.Array.isArray(entry)
|
|
110
|
+
&& "anyOf" in entry
|
|
111
|
+
&& Object.keys(entry).length === 1
|
|
112
|
+
&& globalThis.Array.isArray((entry as Record<string, unknown>)["anyOf"])
|
|
113
|
+
) {
|
|
114
|
+
flattened.push(...(entry as Record<string, unknown>)["anyOf"] as Array<unknown>)
|
|
115
|
+
} else {
|
|
116
|
+
flattened.push(entry)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (flattened.length === 1) {
|
|
120
|
+
const { anyOf: _, ...rest } = result
|
|
121
|
+
const single = flattened[0]
|
|
122
|
+
if (typeof single === "object" && single !== null && !globalThis.Array.isArray(single)) {
|
|
123
|
+
return { ...rest, ...single }
|
|
124
|
+
}
|
|
125
|
+
return single
|
|
126
|
+
}
|
|
127
|
+
result["anyOf"] = flattened
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Applies JSON Schema post-processing: flattens simple allOf,
|
|
135
|
+
* flattens nested anyOf, then strips additionalProperties: false.
|
|
136
|
+
*/
|
|
137
|
+
export function postProcessJsonSchema(obj: JsonSchema.JsonSchema): JsonSchema.JsonSchema {
|
|
138
|
+
return removeAdditionalPropertiesFalse(flattenNestedAnyOf(flattenSimpleAllOf(obj))) as JsonSchema.JsonSchema
|
|
139
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpecialOpenApi — Deduplicates `components/schemas` entries in an OpenAPI spec
|
|
3
|
+
* and applies JSON Schema post-processing (null removal, allOf flattening).
|
|
4
|
+
*
|
|
5
|
+
* When `OpenApi.fromApi` generates the spec, different AST nodes sharing the
|
|
6
|
+
* same identifier can produce duplicate entries (e.g. "X" and "X1") in
|
|
7
|
+
* `components.schemas`. This module provides a transform function that
|
|
8
|
+
* collapses those duplicates, rewrites all `$ref` pointers accordingly,
|
|
9
|
+
* and post-processes schemas for better codegen compatibility.
|
|
10
|
+
*
|
|
11
|
+
* Usage with the OpenApi `Transform` annotation:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { OpenApi } from "effect/unstable"
|
|
15
|
+
* import { deduplicateOpenApiSchemas } from "./SpecialOpenApi.js"
|
|
16
|
+
*
|
|
17
|
+
* const api = HttpApi.make("myApi")
|
|
18
|
+
* .pipe(HttpApi.annotateContext(OpenApi.annotations({ transform: deduplicateOpenApiSchemas })))
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { postProcessJsonSchema } from "./SpecialJsonSchema.js"
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Deduplicates `components.schemas` entries in an OpenAPI spec.
|
|
26
|
+
*
|
|
27
|
+
* Entries sharing the same base identifier (e.g. "X" and "X1") whose JSON
|
|
28
|
+
* representations are identical are collapsed into a single canonical entry,
|
|
29
|
+
* and all `$ref` pointers throughout the spec are rewritten to point to
|
|
30
|
+
* the canonical key.
|
|
31
|
+
*
|
|
32
|
+
* Designed to be used as the `transform` option in `OpenApi.annotations`.
|
|
33
|
+
*/
|
|
34
|
+
export function deduplicateOpenApiSchemas(
|
|
35
|
+
spec: Record<string, any>
|
|
36
|
+
): Record<string, any> {
|
|
37
|
+
const components = spec["components"] as Record<string, any> | undefined
|
|
38
|
+
if (!components) return spec
|
|
39
|
+
const schemas = components["schemas"] as Record<string, any> | undefined
|
|
40
|
+
if (!schemas) return spec
|
|
41
|
+
|
|
42
|
+
const keys = Object.keys(schemas)
|
|
43
|
+
if (keys.length === 0) return spec
|
|
44
|
+
|
|
45
|
+
// Group keys by base identifier (strip trailing digits)
|
|
46
|
+
const groups = new Map<string, Array<{ key: string; fingerprint: string }>>()
|
|
47
|
+
for (const key of keys) {
|
|
48
|
+
const base = getBaseIdentifier(key)
|
|
49
|
+
const fingerprint = JSON.stringify(schemas[key])
|
|
50
|
+
const group = groups.get(base)
|
|
51
|
+
if (group === undefined) {
|
|
52
|
+
groups.set(base, [{ key, fingerprint }])
|
|
53
|
+
} else {
|
|
54
|
+
group.push({ key, fingerprint })
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Build remapping from duplicate keys to canonical keys
|
|
59
|
+
const remapping = new Map<string, string>()
|
|
60
|
+
for (const [, group] of groups) {
|
|
61
|
+
if (group.length <= 1) continue
|
|
62
|
+
const seen = new Map<string, string>() // fingerprint -> canonical key
|
|
63
|
+
for (const entry of group) {
|
|
64
|
+
const canonical = seen.get(entry.fingerprint)
|
|
65
|
+
if (canonical !== undefined) {
|
|
66
|
+
remapping.set(entry.key, canonical)
|
|
67
|
+
} else {
|
|
68
|
+
seen.set(entry.fingerprint, entry.key)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (remapping.size === 0) return postProcessJsonSchema(spec)
|
|
74
|
+
|
|
75
|
+
// Build new schemas object without duplicates
|
|
76
|
+
const newSchemas: Record<string, any> = {}
|
|
77
|
+
for (const key of keys) {
|
|
78
|
+
if (!remapping.has(key)) {
|
|
79
|
+
newSchemas[key] = schemas[key]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Deep clone the spec, replace schemas, and rewrite all $ref pointers
|
|
84
|
+
const newSpec = structuredClone(spec)
|
|
85
|
+
newSpec["components"]["schemas"] = newSchemas
|
|
86
|
+
rewriteRefs(newSpec, remapping)
|
|
87
|
+
|
|
88
|
+
return postProcessJsonSchema(newSpec)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Extracts the base identifier from a schema key by stripping trailing
|
|
93
|
+
* digits appended by the gen() function.
|
|
94
|
+
* E.g. "X1" -> "X", "X" -> "X", "MyType2" -> "MyType"
|
|
95
|
+
*/
|
|
96
|
+
function getBaseIdentifier(key: string): string {
|
|
97
|
+
const match = key.match(/^(.+?)(\d+)$/)
|
|
98
|
+
return match ? match[1]! : key
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Recursively rewrites `$ref` values in a JSON object tree.
|
|
103
|
+
* Mutates the object in-place (caller should pass a deep clone).
|
|
104
|
+
*/
|
|
105
|
+
function rewriteRefs(obj: any, remapping: Map<string, string>): void {
|
|
106
|
+
if (obj === null || typeof obj !== "object") return
|
|
107
|
+
|
|
108
|
+
if (Array.isArray(obj)) {
|
|
109
|
+
for (const item of obj) {
|
|
110
|
+
rewriteRefs(item, remapping)
|
|
111
|
+
}
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (typeof obj.$ref === "string") {
|
|
116
|
+
// OpenAPI refs look like "#/components/schemas/X1"
|
|
117
|
+
const prefix = "#/components/schemas/"
|
|
118
|
+
if (obj.$ref.startsWith(prefix)) {
|
|
119
|
+
const refKey = obj.$ref.slice(prefix.length)
|
|
120
|
+
const canonical = remapping.get(refKey)
|
|
121
|
+
if (canonical !== undefined) {
|
|
122
|
+
obj.$ref = prefix + canonical
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const value of Object.values(obj)) {
|
|
128
|
+
rewriteRefs(value, remapping)
|
|
129
|
+
}
|
|
130
|
+
}
|
package/src/Schema/brand.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
|
-
import type { Option } from "effect"
|
|
4
3
|
import * as B from "effect/Brand"
|
|
4
|
+
import type * as Option from "effect/Option"
|
|
5
5
|
import type * as Result from "effect/Result"
|
|
6
6
|
import * as S from "effect/Schema"
|
|
7
7
|
|
|
@@ -35,11 +35,31 @@ type BrandAnnotations<C extends B.Brand<any>> =
|
|
|
35
35
|
: {}
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
+
export interface BrandedSchema<S extends S.Top, C extends B.Brand<any>> extends
|
|
39
|
+
S.Bottom<
|
|
40
|
+
C,
|
|
41
|
+
S["Encoded"],
|
|
42
|
+
S["DecodingServices"],
|
|
43
|
+
S["EncodingServices"],
|
|
44
|
+
S["ast"],
|
|
45
|
+
BrandedSchema<S, C>,
|
|
46
|
+
S["~type.make.in"],
|
|
47
|
+
S["Iso"],
|
|
48
|
+
S["~type.parameters"],
|
|
49
|
+
C,
|
|
50
|
+
S["~type.mutability"],
|
|
51
|
+
S["~type.optionality"],
|
|
52
|
+
S["~type.constructor.default"],
|
|
53
|
+
S["~encoded.mutability"],
|
|
54
|
+
S["~encoded.optionality"]
|
|
55
|
+
>
|
|
56
|
+
{}
|
|
57
|
+
|
|
38
58
|
export const fromBrand = <C extends B.Brand<any>>(
|
|
39
59
|
constructor: Constructor<C>,
|
|
40
60
|
options?: BrandAnnotations<C>
|
|
41
61
|
) =>
|
|
42
|
-
<Self extends S.Top>(self: Self):
|
|
62
|
+
<Self extends S.Top>(self: Self): BrandedSchema<Self, C> => {
|
|
43
63
|
const branded = S.fromBrand(options?.identifier ?? "Brand", constructor as any)(self as any)
|
|
44
64
|
return options ? (branded as any).pipe(S.annotate(options)) : branded as any
|
|
45
65
|
}
|
package/src/Schema/email.ts
CHANGED
|
@@ -12,11 +12,16 @@ export type Email = string & EmailBrand
|
|
|
12
12
|
export const Email = S
|
|
13
13
|
.String
|
|
14
14
|
.pipe(
|
|
15
|
+
S.annotate({
|
|
16
|
+
title: "Email",
|
|
17
|
+
description: "an email according to RFC 5322",
|
|
18
|
+
format: "email"
|
|
19
|
+
}),
|
|
20
|
+
S.check(S.isMinLength(3), /* a@b */ S.isMaxLength(998)),
|
|
15
21
|
S.refine(isValidEmail as Refinement<string, Email>, {
|
|
16
22
|
identifier: "Email",
|
|
17
|
-
title: "Email",
|
|
18
23
|
description: "an email according to RFC 5322",
|
|
19
|
-
jsonSchema: { format: "email", minLength: 3,
|
|
24
|
+
jsonSchema: { format: "email", minLength: 3, maxLength: 998 }
|
|
20
25
|
}),
|
|
21
26
|
S.annotate({
|
|
22
27
|
toArbitrary: () => (fc) => fc.emailAddress().map((_) => _ as Email)
|