@effect/platform 0.65.5 → 0.66.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.
@@ -0,0 +1,696 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import * as AST from "@effect/schema/AST"
5
+ import type * as ParseResult from "@effect/schema/ParseResult"
6
+ import type * as Schema from "@effect/schema/Schema"
7
+ import * as Arr from "effect/Array"
8
+ import * as Option from "effect/Option"
9
+ import * as Predicate from "effect/Predicate"
10
+ import * as Record from "effect/Record"
11
+
12
+ /**
13
+ * @category model
14
+ * @since 1.0.0
15
+ */
16
+ export interface Annotations {
17
+ title?: string
18
+ description?: string
19
+ default?: unknown
20
+ examples?: globalThis.Array<unknown>
21
+ }
22
+
23
+ /**
24
+ * @category model
25
+ * @since 1.0.0
26
+ */
27
+ export interface Any extends Annotations {
28
+ $id: "/schemas/any"
29
+ }
30
+
31
+ /**
32
+ * @category model
33
+ * @since 1.0.0
34
+ */
35
+ export interface Unknown extends Annotations {
36
+ $id: "/schemas/unknown"
37
+ }
38
+
39
+ /**
40
+ * @category model
41
+ * @since 0.69.0
42
+ */
43
+ export interface Void extends Annotations {
44
+ $id: "/schemas/void"
45
+ }
46
+
47
+ /**
48
+ * @category model
49
+ * @since 0.71.0
50
+ */
51
+ export interface AnyObject extends Annotations {
52
+ $id: "/schemas/object"
53
+ anyOf: [
54
+ { type: "object" },
55
+ { type: "array" }
56
+ ]
57
+ }
58
+
59
+ /**
60
+ * @category model
61
+ * @since 0.71.0
62
+ */
63
+ export interface Empty extends Annotations {
64
+ $id: "/schemas/{}"
65
+ anyOf: [
66
+ { type: "object" },
67
+ { type: "array" }
68
+ ]
69
+ }
70
+
71
+ /**
72
+ * @category model
73
+ * @since 1.0.0
74
+ */
75
+ export interface Ref extends Annotations {
76
+ $ref: string
77
+ }
78
+
79
+ /**
80
+ * @category model
81
+ * @since 1.0.0
82
+ */
83
+ export interface String extends Annotations {
84
+ type: "string"
85
+ minLength?: number
86
+ maxLength?: number
87
+ pattern?: string
88
+ contentEncoding?: string
89
+ contentMediaType?: string
90
+ contentSchema?: JsonSchema
91
+ }
92
+
93
+ /**
94
+ * @category model
95
+ * @since 1.0.0
96
+ */
97
+ export interface Numeric extends Annotations {
98
+ minimum?: number
99
+ exclusiveMinimum?: number
100
+ maximum?: number
101
+ exclusiveMaximum?: number
102
+ }
103
+
104
+ /**
105
+ * @category model
106
+ * @since 1.0.0
107
+ */
108
+ export interface Number extends Numeric {
109
+ type: "number"
110
+ }
111
+
112
+ /**
113
+ * @category model
114
+ * @since 1.0.0
115
+ */
116
+ export interface Integer extends Numeric {
117
+ type: "integer"
118
+ }
119
+
120
+ /**
121
+ * @category model
122
+ * @since 1.0.0
123
+ */
124
+ export interface Boolean extends Annotations {
125
+ type: "boolean"
126
+ }
127
+
128
+ /**
129
+ * @category model
130
+ * @since 1.0.0
131
+ */
132
+ export interface Array extends Annotations {
133
+ type: "array"
134
+ items?: JsonSchema | globalThis.Array<JsonSchema>
135
+ minItems?: number
136
+ maxItems?: number
137
+ additionalItems?: JsonSchema | boolean
138
+ }
139
+
140
+ /**
141
+ * @category model
142
+ * @since 1.0.0
143
+ */
144
+ export interface Enum extends Annotations {
145
+ enum: globalThis.Array<AST.LiteralValue>
146
+ }
147
+
148
+ /**
149
+ * @category model
150
+ * @since 0.71.0
151
+ */
152
+ export interface Enums extends Annotations {
153
+ $comment: "/schemas/enums"
154
+ anyOf: globalThis.Array<{
155
+ title: string
156
+ enum: [string | number]
157
+ }>
158
+ }
159
+
160
+ /**
161
+ * @category model
162
+ * @since 1.0.0
163
+ */
164
+ export interface AnyOf extends Annotations {
165
+ anyOf: globalThis.Array<JsonSchema>
166
+ }
167
+
168
+ /**
169
+ * @category model
170
+ * @since 1.0.0
171
+ */
172
+ export interface Object extends Annotations {
173
+ type: "object"
174
+ required: globalThis.Array<string>
175
+ properties: Record<string, JsonSchema>
176
+ additionalProperties?: boolean | JsonSchema
177
+ patternProperties?: Record<string, JsonSchema>
178
+ propertyNames?: JsonSchema
179
+ }
180
+
181
+ /**
182
+ * @category model
183
+ * @since 0.71.0
184
+ */
185
+ export type JsonSchema =
186
+ | Any
187
+ | Unknown
188
+ | Void
189
+ | AnyObject
190
+ | Empty
191
+ | Ref
192
+ | String
193
+ | Number
194
+ | Integer
195
+ | Boolean
196
+ | Array
197
+ | Enum
198
+ | Enums
199
+ | AnyOf
200
+ | Object
201
+
202
+ /**
203
+ * @category model
204
+ * @since 1.0.0
205
+ */
206
+ export type Root = JsonSchema & {
207
+ $defs?: Record<string, JsonSchema>
208
+ }
209
+
210
+ /**
211
+ * @category encoding
212
+ * @since 1.0.0
213
+ */
214
+ export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): Root => {
215
+ const $defs: Record<string, any> = {}
216
+ const out = go(schema.ast, $defs, true, []) as Root
217
+ // clean up self-referencing entries
218
+ for (const id in $defs) {
219
+ if ($defs[id]["$ref"] === get$ref(id)) {
220
+ delete $defs[id]
221
+ }
222
+ }
223
+ if (!Record.isEmptyRecord($defs)) {
224
+ out.$defs = $defs
225
+ }
226
+ return out
227
+ }
228
+
229
+ const constAny: JsonSchema = { $id: "/schemas/any" }
230
+
231
+ const constUnknown: JsonSchema = { $id: "/schemas/unknown" }
232
+
233
+ const constVoid: JsonSchema = { $id: "/schemas/void" }
234
+
235
+ const constAnyObject: JsonSchema = {
236
+ "$id": "/schemas/object",
237
+ "anyOf": [
238
+ { "type": "object" },
239
+ { "type": "array" }
240
+ ]
241
+ }
242
+
243
+ const constEmpty: JsonSchema = {
244
+ "$id": "/schemas/{}",
245
+ "anyOf": [
246
+ { "type": "object" },
247
+ { "type": "array" }
248
+ ]
249
+ }
250
+
251
+ const getJsonSchemaAnnotations = (annotated: AST.Annotated): Annotations =>
252
+ Record.getSomes({
253
+ description: AST.getDescriptionAnnotation(annotated),
254
+ title: AST.getTitleAnnotation(annotated),
255
+ examples: AST.getExamplesAnnotation(annotated),
256
+ default: AST.getDefaultAnnotation(annotated)
257
+ })
258
+
259
+ const pruneUndefinedKeyword = (ps: AST.PropertySignature): AST.AST | undefined => {
260
+ const type = ps.type
261
+ if (AST.isUnion(type) && Option.isNone(AST.getJSONSchemaAnnotation(type))) {
262
+ const types = type.types.filter((type) => !AST.isUndefinedKeyword(type))
263
+ if (types.length < type.types.length) {
264
+ return AST.Union.make(types, type.annotations)
265
+ }
266
+ }
267
+ }
268
+
269
+ const DEFINITION_PREFIX = "#/$defs/"
270
+
271
+ const get$ref = (id: string): string => `${DEFINITION_PREFIX}${id}`
272
+
273
+ const getRefinementInnerTransformation = (ast: AST.Refinement): AST.AST | undefined => {
274
+ switch (ast.from._tag) {
275
+ case "Transformation":
276
+ return ast.from
277
+ case "Refinement":
278
+ return getRefinementInnerTransformation(ast.from)
279
+ case "Suspend": {
280
+ const from = ast.from.f()
281
+ if (AST.isRefinement(from)) {
282
+ return getRefinementInnerTransformation(from)
283
+ }
284
+ }
285
+ }
286
+ }
287
+
288
+ const isParseJsonTransformation = (ast: AST.AST): boolean => ast.annotations[AST.TypeAnnotationId] === ParseJsonTypeId
289
+
290
+ const isOverrideAnnotation = (jsonSchema: JsonSchema): boolean => {
291
+ return ("type" in jsonSchema) || ("oneOf" in jsonSchema) || ("anyOf" in jsonSchema) || ("const" in jsonSchema) ||
292
+ ("enum" in jsonSchema) || ("$ref" in jsonSchema)
293
+ }
294
+
295
+ const go = (
296
+ ast: AST.AST,
297
+ $defs: Record<string, JsonSchema>,
298
+ handleIdentifier: boolean,
299
+ path: ReadonlyArray<PropertyKey>
300
+ ): JsonSchema => {
301
+ const hook = AST.getJSONSchemaAnnotation(ast)
302
+ if (Option.isSome(hook)) {
303
+ const handler = hook.value as JsonSchema
304
+ if (AST.isRefinement(ast)) {
305
+ const t = getRefinementInnerTransformation(ast)
306
+ if (t === undefined) {
307
+ try {
308
+ return {
309
+ ...go(ast.from, $defs, true, path),
310
+ ...getJsonSchemaAnnotations(ast),
311
+ ...handler
312
+ }
313
+ } catch (e) {
314
+ return {
315
+ ...getJsonSchemaAnnotations(ast),
316
+ ...handler
317
+ }
318
+ }
319
+ } else if (!isOverrideAnnotation(handler)) {
320
+ return {
321
+ ...go(t, $defs, true, path),
322
+ ...getJsonSchemaAnnotations(ast)
323
+ }
324
+ }
325
+ }
326
+ return handler
327
+ }
328
+ const surrogate = getSurrogateAnnotation(ast)
329
+ if (Option.isSome(surrogate)) {
330
+ return {
331
+ ...(ast._tag === "Transformation" ? getJsonSchemaAnnotations(ast.to) : {}),
332
+ ...go(surrogate.value, $defs, handleIdentifier, path),
333
+ ...getJsonSchemaAnnotations(ast)
334
+ }
335
+ }
336
+ if (handleIdentifier && !AST.isTransformation(ast)) {
337
+ const identifier = getJSONIdentifier(ast)
338
+ if (Option.isSome(identifier)) {
339
+ const id = identifier.value
340
+ const out = { $ref: get$ref(id) }
341
+ if (!Record.has($defs, id)) {
342
+ $defs[id] = out
343
+ $defs[id] = go(ast, $defs, false, path)
344
+ }
345
+ return out
346
+ }
347
+ }
348
+ switch (ast._tag) {
349
+ case "Declaration":
350
+ throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
351
+ case "Literal": {
352
+ const literal = ast.literal
353
+ if (literal === null) {
354
+ return {
355
+ enum: [null],
356
+ ...getJsonSchemaAnnotations(ast)
357
+ }
358
+ } else if (Predicate.isString(literal) || Predicate.isNumber(literal) || Predicate.isBoolean(literal)) {
359
+ return {
360
+ enum: [literal],
361
+ ...getJsonSchemaAnnotations(ast)
362
+ }
363
+ }
364
+ throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
365
+ }
366
+ case "UniqueSymbol":
367
+ throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
368
+ case "UndefinedKeyword":
369
+ throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
370
+ case "VoidKeyword":
371
+ return {
372
+ ...constVoid,
373
+ ...getJsonSchemaAnnotations(ast)
374
+ }
375
+ case "NeverKeyword":
376
+ throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
377
+ case "UnknownKeyword":
378
+ return {
379
+ ...constUnknown,
380
+ ...getJsonSchemaAnnotations(ast)
381
+ }
382
+
383
+ case "AnyKeyword":
384
+ return {
385
+ ...constAny,
386
+ ...getJsonSchemaAnnotations(ast)
387
+ }
388
+ case "ObjectKeyword":
389
+ return {
390
+ ...constAnyObject,
391
+ ...getJsonSchemaAnnotations(ast)
392
+ }
393
+ case "StringKeyword": {
394
+ return ast === AST.stringKeyword ? { type: "string" } : {
395
+ type: "string",
396
+ ...getJsonSchemaAnnotations(ast)
397
+ }
398
+ }
399
+ case "NumberKeyword": {
400
+ return ast === AST.numberKeyword ? { type: "number" } : {
401
+ type: "number",
402
+ ...getJsonSchemaAnnotations(ast)
403
+ }
404
+ }
405
+ case "BooleanKeyword": {
406
+ return ast === AST.booleanKeyword ? { type: "boolean" } : {
407
+ type: "boolean",
408
+ ...getJsonSchemaAnnotations(ast)
409
+ }
410
+ }
411
+ case "BigIntKeyword":
412
+ throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
413
+ case "SymbolKeyword":
414
+ throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
415
+ case "TupleType": {
416
+ const elements = ast.elements.map((e, i) => ({
417
+ ...go(e.type, $defs, true, path.concat(i)),
418
+ ...getJsonSchemaAnnotations(e)
419
+ }))
420
+ const rest = ast.rest.map((annotatedAST) => ({
421
+ ...go(annotatedAST.type, $defs, true, path),
422
+ ...getJsonSchemaAnnotations(annotatedAST)
423
+ }))
424
+ const output: Array = { type: "array" }
425
+ // ---------------------------------------------
426
+ // handle elements
427
+ // ---------------------------------------------
428
+ const len = ast.elements.length
429
+ if (len > 0) {
430
+ output.minItems = len - ast.elements.filter((element) => element.isOptional).length
431
+ output.items = elements
432
+ }
433
+ // ---------------------------------------------
434
+ // handle rest element
435
+ // ---------------------------------------------
436
+ const restLength = rest.length
437
+ if (restLength > 0) {
438
+ const head = rest[0]
439
+ const isHomogeneous = restLength === 1 && ast.elements.every((e) => e.type === ast.rest[0].type)
440
+ if (isHomogeneous) {
441
+ output.items = head
442
+ } else {
443
+ output.additionalItems = head
444
+ }
445
+
446
+ // ---------------------------------------------
447
+ // handle post rest elements
448
+ // ---------------------------------------------
449
+ if (restLength > 1) {
450
+ throw new Error(getJSONSchemaUnsupportedPostRestElementsErrorMessage(path))
451
+ }
452
+ } else {
453
+ if (len > 0) {
454
+ output.additionalItems = false
455
+ } else {
456
+ output.maxItems = 0
457
+ }
458
+ }
459
+
460
+ return {
461
+ ...output,
462
+ ...getJsonSchemaAnnotations(ast)
463
+ }
464
+ }
465
+ case "TypeLiteral": {
466
+ if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) {
467
+ return {
468
+ ...constEmpty,
469
+ ...getJsonSchemaAnnotations(ast)
470
+ }
471
+ }
472
+ let patternProperties: JsonSchema | undefined = undefined
473
+ let propertyNames: JsonSchema | undefined = undefined
474
+ for (const is of ast.indexSignatures) {
475
+ const parameter = is.parameter
476
+ switch (parameter._tag) {
477
+ case "StringKeyword": {
478
+ patternProperties = go(is.type, $defs, true, path)
479
+ break
480
+ }
481
+ case "TemplateLiteral": {
482
+ patternProperties = go(is.type, $defs, true, path)
483
+ propertyNames = {
484
+ type: "string",
485
+ pattern: AST.getTemplateLiteralRegExp(parameter).source
486
+ }
487
+ break
488
+ }
489
+ case "Refinement": {
490
+ patternProperties = go(is.type, $defs, true, path)
491
+ propertyNames = go(parameter, $defs, true, path)
492
+ break
493
+ }
494
+ case "SymbolKeyword":
495
+ throw new Error(getJSONSchemaUnsupportedParameterErrorMessage(path, parameter))
496
+ }
497
+ }
498
+ const output: Object = {
499
+ type: "object",
500
+ required: [],
501
+ properties: {},
502
+ additionalProperties: false
503
+ }
504
+ // ---------------------------------------------
505
+ // handle property signatures
506
+ // ---------------------------------------------
507
+ for (let i = 0; i < ast.propertySignatures.length; i++) {
508
+ const ps = ast.propertySignatures[i]
509
+ const name = ps.name
510
+ if (Predicate.isString(name)) {
511
+ const pruned = pruneUndefinedKeyword(ps)
512
+ output.properties[name] = {
513
+ ...go(pruned ? pruned : ps.type, $defs, true, path.concat(ps.name)),
514
+ ...getJsonSchemaAnnotations(ps)
515
+ }
516
+ // ---------------------------------------------
517
+ // handle optional property signatures
518
+ // ---------------------------------------------
519
+ if (!ps.isOptional && pruned === undefined) {
520
+ output.required.push(name)
521
+ }
522
+ } else {
523
+ throw new Error(getJSONSchemaUnsupportedKeyErrorMessage(name, path))
524
+ }
525
+ }
526
+ // ---------------------------------------------
527
+ // handle index signatures
528
+ // ---------------------------------------------
529
+ if (patternProperties !== undefined) {
530
+ delete output.additionalProperties
531
+ output.patternProperties = { "": patternProperties }
532
+ }
533
+ if (propertyNames !== undefined) {
534
+ output.propertyNames = propertyNames
535
+ }
536
+
537
+ return {
538
+ ...output,
539
+ ...getJsonSchemaAnnotations(ast)
540
+ }
541
+ }
542
+ case "Union": {
543
+ const enums: globalThis.Array<AST.LiteralValue> = []
544
+ const anyOf: globalThis.Array<JsonSchema> = []
545
+ for (const type of ast.types) {
546
+ const schema = go(type, $defs, true, path)
547
+ if ("enum" in schema) {
548
+ if (Object.keys(schema).length > 1) {
549
+ anyOf.push(schema)
550
+ } else {
551
+ for (const e of schema.enum) {
552
+ enums.push(e)
553
+ }
554
+ }
555
+ } else {
556
+ anyOf.push(schema)
557
+ }
558
+ }
559
+ if (anyOf.length === 0) {
560
+ return { enum: enums, ...getJsonSchemaAnnotations(ast) }
561
+ } else {
562
+ if (enums.length >= 1) {
563
+ anyOf.push({ enum: enums })
564
+ }
565
+ return { anyOf, ...getJsonSchemaAnnotations(ast) }
566
+ }
567
+ }
568
+ case "Enums": {
569
+ return {
570
+ $comment: "/schemas/enums",
571
+ anyOf: ast.enums.map((e) => ({ title: e[0], enum: [e[1]] })),
572
+ ...getJsonSchemaAnnotations(ast)
573
+ }
574
+ }
575
+ case "Refinement": {
576
+ throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
577
+ }
578
+ case "TemplateLiteral": {
579
+ const regex = AST.getTemplateLiteralRegExp(ast)
580
+ return {
581
+ type: "string",
582
+ description: "a template literal",
583
+ pattern: regex.source,
584
+ ...getJsonSchemaAnnotations(ast)
585
+ }
586
+ }
587
+ case "Suspend": {
588
+ const identifier = Option.orElse(getJSONIdentifier(ast), () => getJSONIdentifier(ast.f()))
589
+ if (Option.isNone(identifier)) {
590
+ throw new Error(getJSONSchemaMissingIdentifierAnnotationErrorMessage(path, ast))
591
+ }
592
+ return {
593
+ ...go(ast.f(), $defs, true, path),
594
+ ...getJsonSchemaAnnotations(ast)
595
+ }
596
+ }
597
+ case "Transformation": {
598
+ // Properly handle S.parseJson transformations by focusing on
599
+ // the 'to' side of the AST. This approach prevents the generation of useless schemas
600
+ // derived from the 'from' side (type: string), ensuring the output matches the intended
601
+ // complex schema type.
602
+ if (isParseJsonTransformation(ast.from)) {
603
+ return {
604
+ type: "string",
605
+ contentMediaType: "application/json",
606
+ contentSchema: go(ast.to, $defs, true, path),
607
+ ...getJsonSchemaAnnotations(ast)
608
+ }
609
+ }
610
+ return {
611
+ ...getJsonSchemaAnnotations(ast.to),
612
+ ...go(ast.from, $defs, true, path),
613
+ ...getJsonSchemaAnnotations(ast)
614
+ }
615
+ }
616
+ }
617
+ }
618
+
619
+ const getJSONSchemaMissingAnnotationErrorMessage = (
620
+ path: ReadonlyArray<PropertyKey>,
621
+ ast: AST.AST
622
+ ) =>
623
+ getMissingAnnotationErrorMessage(
624
+ `Generating a JSON Schema for this schema requires a "jsonSchema" annotation`,
625
+ path,
626
+ ast
627
+ )
628
+
629
+ const getJSONSchemaMissingIdentifierAnnotationErrorMessage = (
630
+ path: ReadonlyArray<PropertyKey>,
631
+ ast: AST.AST
632
+ ) =>
633
+ getMissingAnnotationErrorMessage(
634
+ `Generating a JSON Schema for this schema requires an "identifier" annotation`,
635
+ path,
636
+ ast
637
+ )
638
+
639
+ const getJSONSchemaUnsupportedParameterErrorMessage = (
640
+ path: ReadonlyArray<PropertyKey>,
641
+ parameter: AST.AST
642
+ ): string => getErrorMessage("Unsupported index signature parameter", undefined, path, parameter)
643
+
644
+ const getJSONSchemaUnsupportedPostRestElementsErrorMessage = (path: ReadonlyArray<PropertyKey>): string =>
645
+ getErrorMessage(
646
+ "Generating a JSON Schema for post-rest elements is not currently supported. You're welcome to contribute by submitting a Pull Request",
647
+ undefined,
648
+ path
649
+ )
650
+
651
+ const getJSONSchemaUnsupportedKeyErrorMessage = (key: PropertyKey, path: ReadonlyArray<PropertyKey>): string =>
652
+ getErrorMessage("Unsupported key", `Cannot encode ${formatPropertyKey(key)} key to JSON Schema`, path)
653
+
654
+ const getMissingAnnotationErrorMessage = (details?: string, path?: ReadonlyArray<PropertyKey>, ast?: AST.AST): string =>
655
+ getErrorMessage("Missing annotation", details, path, ast)
656
+
657
+ const getErrorMessage = (
658
+ reason: string,
659
+ details?: string,
660
+ path?: ReadonlyArray<PropertyKey>,
661
+ ast?: AST.AST
662
+ ): string => {
663
+ let out = reason
664
+
665
+ if (path && Arr.isNonEmptyReadonlyArray(path)) {
666
+ out += `\nat path: ${formatPath(path)}`
667
+ }
668
+
669
+ if (details !== undefined) {
670
+ out += `\ndetails: ${details}`
671
+ }
672
+
673
+ if (ast) {
674
+ out += `\nschema (${ast._tag}): ${ast}`
675
+ }
676
+
677
+ return out
678
+ }
679
+
680
+ const formatPathKey = (key: PropertyKey): string => `[${formatPropertyKey(key)}]`
681
+
682
+ const formatPath = (path: ParseResult.Path): string =>
683
+ isNonEmpty(path) ? path.map(formatPathKey).join("") : formatPathKey(path)
684
+
685
+ const isNonEmpty = <A>(x: ParseResult.SingleOrNonEmpty<A>): x is Arr.NonEmptyReadonlyArray<A> => Array.isArray(x)
686
+
687
+ const formatPropertyKey = (name: PropertyKey): string => typeof name === "string" ? JSON.stringify(name) : String(name)
688
+
689
+ const ParseJsonTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/ParseJson")
690
+ const SurrogateAnnotationId = Symbol.for("@effect/schema/annotation/Surrogate")
691
+ const JSONIdentifierAnnotationId = Symbol.for("@effect/schema/annotation/JSONIdentifier")
692
+
693
+ const getSurrogateAnnotation = AST.getAnnotation<AST.AST>(SurrogateAnnotationId)
694
+ const getJSONIdentifierAnnotation = AST.getAnnotation<string>(JSONIdentifierAnnotationId)
695
+ const getJSONIdentifier = (annotated: AST.Annotated) =>
696
+ Option.orElse(getJSONIdentifierAnnotation(annotated), () => AST.getIdentifierAnnotation(annotated))
package/src/index.ts CHANGED
@@ -194,6 +194,11 @@ export * as Multipart from "./Multipart.js"
194
194
  */
195
195
  export * as OpenApi from "./OpenApi.js"
196
196
 
197
+ /**
198
+ * @since 1.0.0
199
+ */
200
+ export * as OpenApiJsonSchema from "./OpenApiJsonSchema.js"
201
+
197
202
  /**
198
203
  * @since 1.0.0
199
204
  */