| import * as Arr from "../../Array.js"; | |
| import * as Equal from "../../Equal.js"; | |
| import { format } from "../../Formatter.js"; | |
| import { escapeToken } from "../../JsonPointer.js"; | |
| import * as Predicate from "../../Predicate.js"; | |
| import * as Rec from "../../Record.js"; | |
| import * as RegEx from "../../RegExp.js"; | |
| import * as SchemaAST from "../../SchemaAST.js"; | |
| import * as InternalAnnotations from "./annotations.js"; | |
| import * as InternalSchema from "./schema.js"; | |
| /** @internal */ | |
| export function fromAST(ast) { | |
| const { | |
| references, | |
| representations: schemas | |
| } = fromASTs([ast]); | |
| return { | |
| representation: schemas[0], | |
| references | |
| }; | |
| } | |
| /** @internal */ | |
| export function fromASTs(asts) { | |
| const references = {}; | |
| const referenceMap = new Map(); | |
| const uniqueReferences = new Set(); | |
| const visiting = new Set(); | |
| const schemas = Arr.map(asts, ast => recur(ast)); | |
| return { | |
| representations: schemas, | |
| references | |
| }; | |
| function gen(prefix) { | |
| let candidate = prefix; | |
| let suffix = 0; | |
| while (uniqueReferences.has(candidate)) { | |
| candidate = `${prefix}${++suffix}`; | |
| } | |
| uniqueReferences.add(candidate); | |
| return candidate; | |
| } | |
| function recur(ast, prefix) { | |
| const found = referenceMap.get(ast); | |
| if (found !== undefined) { | |
| return { | |
| _tag: "Reference", | |
| $ref: found | |
| }; | |
| } | |
| const last = SchemaAST.getLastEncoding(ast); | |
| const identifier = InternalAnnotations.resolveIdentifier(ast) ?? prefix; | |
| if (ast !== last) { | |
| return recur(last, identifier); | |
| } | |
| // Has identifier → always create reference | |
| if (identifier !== undefined) { | |
| const reference = gen(identifier); | |
| referenceMap.set(ast, reference); | |
| const out = on(ast); | |
| const found = references[identifier]; | |
| // Reuse existing references when duplicate identifiers have the same representation | |
| if (found !== undefined && Equal.equals(out, found)) { | |
| referenceMap.set(ast, identifier); | |
| return { | |
| _tag: "Reference", | |
| $ref: identifier | |
| }; | |
| } | |
| references[reference] = out; | |
| return { | |
| _tag: "Reference", | |
| $ref: reference | |
| }; | |
| } | |
| // Recursion detected → create reference | |
| if (visiting.has(ast)) { | |
| const reference = gen(`${ast._tag}_`); | |
| referenceMap.set(ast, reference); | |
| return { | |
| _tag: "Reference", | |
| $ref: reference | |
| }; | |
| } | |
| // Normal case → inline | |
| visiting.add(ast); | |
| const out = on(ast); | |
| visiting.delete(ast); | |
| // A descendant triggered reference creation (recursion) | |
| const ref = referenceMap.get(ast); | |
| if (ref !== undefined) { | |
| references[ref] = out; | |
| return { | |
| _tag: "Reference", | |
| $ref: ref | |
| }; | |
| } | |
| return out; | |
| } | |
| function getEncodedSchema(last) { | |
| const getLink = last.annotations?.toCodecJson ?? last.annotations?.toCodec; | |
| if (Predicate.isFunction(getLink)) { | |
| const tps = last.typeParameters.map(tp => InternalSchema.make(SchemaAST.toEncoded(tp))); | |
| const link = getLink(tps); | |
| return SchemaAST.replaceEncoding(last, [link]); | |
| } | |
| return SchemaAST.null; | |
| } | |
| function on(last) { | |
| const annotations = fromASTAnnotations(last.annotations); | |
| switch (last._tag) { | |
| case "Declaration": | |
| { | |
| // this must be executed before transforming the type parameters | |
| const encodedSchema = recur(getEncodedSchema(last)); | |
| return { | |
| _tag: "Declaration", | |
| typeParameters: last.typeParameters.map(ast => recur(ast)), | |
| encodedSchema, | |
| checks: fromASTChecks(last.checks), | |
| ...annotations | |
| }; | |
| } | |
| case "Null": | |
| case "Undefined": | |
| case "Void": | |
| case "Never": | |
| case "Unknown": | |
| case "Any": | |
| case "Boolean": | |
| case "Symbol": | |
| return { | |
| _tag: last._tag, | |
| ...annotations | |
| }; | |
| case "String": | |
| { | |
| const contentMediaType = last.annotations?.contentMediaType; | |
| const contentSchema = last.annotations?.contentSchema; | |
| return { | |
| _tag: last._tag, | |
| checks: fromASTChecks(last.checks), | |
| ...annotations, | |
| ...(typeof contentMediaType === "string" && SchemaAST.isAST(contentSchema) ? { | |
| contentSchema: recur(contentSchema) | |
| } : undefined) | |
| }; | |
| } | |
| case "Number": | |
| case "BigInt": | |
| return { | |
| _tag: last._tag, | |
| checks: fromASTChecks(last.checks), | |
| ...annotations | |
| }; | |
| case "Literal": | |
| return { | |
| _tag: last._tag, | |
| literal: last.literal, | |
| ...annotations | |
| }; | |
| case "UniqueSymbol": | |
| return { | |
| _tag: last._tag, | |
| symbol: last.symbol, | |
| ...annotations | |
| }; | |
| case "ObjectKeyword": | |
| return { | |
| _tag: last._tag, | |
| ...annotations | |
| }; | |
| case "Enum": | |
| return { | |
| _tag: last._tag, | |
| enums: last.enums, | |
| ...annotations | |
| }; | |
| case "TemplateLiteral": | |
| return { | |
| _tag: last._tag, | |
| parts: last.parts.map(ast => recur(ast)), | |
| ...annotations | |
| }; | |
| case "Arrays": | |
| return { | |
| _tag: last._tag, | |
| elements: last.elements.map(e => { | |
| const last = SchemaAST.getLastEncoding(e); | |
| return { | |
| isOptional: SchemaAST.isOptional(last), | |
| type: recur(e), | |
| ...fromASTAnnotations(last.context?.annotations) | |
| }; | |
| }), | |
| rest: last.rest.map(ast => recur(ast)), | |
| checks: fromASTChecks(last.checks), | |
| ...annotations | |
| }; | |
| case "Objects": | |
| return { | |
| _tag: last._tag, | |
| propertySignatures: last.propertySignatures.map(ps => { | |
| const last = SchemaAST.getLastEncoding(ps.type); | |
| return { | |
| name: ps.name, | |
| type: recur(ps.type), | |
| isOptional: SchemaAST.isOptional(last), | |
| isMutable: SchemaAST.isMutable(last), | |
| ...fromASTAnnotations(last.context?.annotations) | |
| }; | |
| }), | |
| indexSignatures: last.indexSignatures.map(is => ({ | |
| parameter: recur(is.parameter), | |
| type: recur(is.type) | |
| })), | |
| checks: fromASTChecks(last.checks), | |
| ...annotations | |
| }; | |
| case "Union": | |
| { | |
| const types = InternalSchema.jsonReorder(last.types); | |
| return { | |
| _tag: last._tag, | |
| types: types.map(ast => recur(ast)), | |
| mode: last.mode, | |
| ...annotations | |
| }; | |
| } | |
| case "Suspend": | |
| { | |
| return { | |
| _tag: "Suspend", | |
| checks: [], | |
| thunk: recur(last.thunk()), | |
| ...annotations | |
| }; | |
| } | |
| } | |
| } | |
| function fromASTChecks(checks) { | |
| if (!checks) return []; | |
| return checks.map(getCheck).filter(c => c !== undefined); | |
| function getCheck(c) { | |
| switch (c._tag) { | |
| case "Filter": | |
| { | |
| const meta = c.annotations?.meta; | |
| if (meta) { | |
| return { | |
| _tag: "Filter", | |
| meta: meta._tag === "isPropertyNames" ? { | |
| _tag: "isPropertyNames", | |
| propertyNames: recur(meta.propertyNames) | |
| } : meta, | |
| ...fromASTAnnotations(c.annotations) | |
| }; | |
| } | |
| return undefined; | |
| } | |
| case "FilterGroup": | |
| { | |
| const checks = fromASTChecks(c.checks); | |
| if (Arr.isArrayNonEmpty(checks)) { | |
| return { | |
| _tag: "FilterGroup", | |
| checks, | |
| ...fromASTAnnotations(c.annotations) | |
| }; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** @internal */ | |
| export const fromASTBlacklist = /*#__PURE__*/new Set([ | |
| // `expected` is preserved because is useful to generate descriptions in JSON Schemas | |
| "~structural", "~sentinels", "meta", "arbitrary", "toArbitrary", "toEquivalence", "toFormatter", "toCodec", "toCodecJson", "toCodecIso", SchemaAST.ClassTypeId]); | |
| const standardJsonSchemaAnnotationKeys = /*#__PURE__*/new Set(["title", "description", "default", "examples", "readOnly", "writeOnly", "format", "contentEncoding", "contentMediaType", "contentSchema"]); | |
| function fromASTAnnotations(annotations) { | |
| if (annotations !== undefined) { | |
| const filtered = Rec.filter(annotations, (_, k) => !fromASTBlacklist.has(k)); | |
| if (!Rec.isEmptyRecord(filtered)) { | |
| return { | |
| annotations: filtered | |
| }; | |
| } | |
| } | |
| return undefined; | |
| } | |
| /** @internal */ | |
| export function toJsonSchemaDocument(document, options) { | |
| const { | |
| definitions, | |
| dialect: source, | |
| schemas | |
| } = toJsonSchemaMultiDocument({ | |
| representations: [document.representation], | |
| references: document.references | |
| }, options); | |
| const schema = schemas[0]; | |
| return { | |
| dialect: source, | |
| schema, | |
| definitions | |
| }; | |
| } | |
| /** @internal */ | |
| export function toJsonSchemaMultiDocument(multiDocument, options) { | |
| const generateDescriptions = options?.generateDescriptions ?? false; | |
| const additionalProperties = options?.additionalProperties ?? false; | |
| const includeAnnotationKey = options?.includeAnnotationKey; | |
| const definitions = Rec.map(multiDocument.references, d => recur(d)); | |
| return { | |
| dialect: "draft-2020-12", | |
| schemas: Arr.map(multiDocument.representations, s => recur(s)), | |
| definitions | |
| }; | |
| function recur(s) { | |
| let js = on(s); | |
| if ("annotations" in s) { | |
| const a = collectJsonSchemaAnnotations(s.annotations); | |
| if (a) { | |
| js = { | |
| ...js, | |
| ...a | |
| }; | |
| } | |
| } | |
| if ("checks" in s) { | |
| const checks = collectJsonSchemaChecks(s.checks, js.type); | |
| for (const check of checks) { | |
| js = appendJsonSchema(js, check); | |
| } | |
| } | |
| return js; | |
| } | |
| function on(schema) { | |
| switch (schema._tag) { | |
| case "Any": | |
| case "Unknown": | |
| return {}; | |
| case "ObjectKeyword": | |
| return { | |
| anyOf: [{ | |
| type: "object" | |
| }, { | |
| type: "array" | |
| }] | |
| }; | |
| case "Void": | |
| case "Undefined": | |
| return { | |
| type: "null" | |
| }; | |
| case "BigInt": | |
| return { | |
| "type": "string", | |
| "allOf": [{ | |
| "pattern": "^-?\\d+$" | |
| }] | |
| }; | |
| case "Symbol": | |
| case "UniqueSymbol": | |
| return { | |
| "type": "string", | |
| "allOf": [{ | |
| "pattern": "^Symbol\\((.*)\\)$" | |
| }] | |
| }; | |
| case "Declaration": | |
| return recur(schema.encodedSchema); | |
| case "Suspend": | |
| return recur(schema.thunk); | |
| case "Reference": | |
| return { | |
| $ref: `#/$defs/${escapeToken(schema.$ref)}` | |
| }; | |
| case "Null": | |
| return { | |
| type: "null" | |
| }; | |
| case "Never": | |
| return { | |
| not: {} | |
| }; | |
| case "String": | |
| { | |
| const out = { | |
| type: "string" | |
| }; | |
| if (schema.contentMediaType !== undefined) { | |
| out.contentMediaType = schema.contentMediaType; | |
| } | |
| if (schema.contentSchema !== undefined) { | |
| out.contentSchema = recur(schema.contentSchema); | |
| } | |
| return out; | |
| } | |
| case "Number": | |
| return hasCheck(schema.checks, "isInt") ? { | |
| type: "integer" | |
| } : hasCheck(schema.checks, "isFinite") ? { | |
| type: "number" | |
| } : { | |
| "anyOf": [{ | |
| type: "number" | |
| }, { | |
| type: "string", | |
| enum: ["NaN"] | |
| }, { | |
| type: "string", | |
| enum: ["Infinity"] | |
| }, { | |
| type: "string", | |
| enum: ["-Infinity"] | |
| }] | |
| }; | |
| case "Boolean": | |
| return { | |
| type: "boolean" | |
| }; | |
| case "Literal": | |
| { | |
| const literal = schema.literal; | |
| if (typeof literal === "string") { | |
| return { | |
| type: "string", | |
| enum: [literal] | |
| }; | |
| } | |
| if (typeof literal === "number") { | |
| return { | |
| type: "number", | |
| enum: [literal] | |
| }; | |
| } | |
| if (typeof literal === "boolean") { | |
| return { | |
| type: "boolean", | |
| enum: [literal] | |
| }; | |
| } | |
| // bigint literals are not supported | |
| return { | |
| type: "string", | |
| enum: [String(literal)] | |
| }; | |
| } | |
| case "Enum": | |
| { | |
| return recur({ | |
| _tag: "Union", | |
| types: schema.enums.map(([title, value]) => ({ | |
| _tag: "Literal", | |
| literal: value, | |
| annotations: { | |
| title | |
| } | |
| })), | |
| mode: "anyOf", | |
| annotations: schema.annotations | |
| }); | |
| } | |
| case "TemplateLiteral": | |
| { | |
| const pattern = schema.parts.map(getPartPattern).join(""); | |
| return { | |
| type: "string", | |
| pattern: `^${pattern}$` | |
| }; | |
| } | |
| case "Arrays": | |
| { | |
| // --------------------------------------------- | |
| // handle post rest elements | |
| // --------------------------------------------- | |
| if (schema.rest.length > 1) { | |
| throw new globalThis.Error("Generating a JSON Schema for post-rest elements is not supported"); | |
| } | |
| const out = { | |
| type: "array" | |
| }; | |
| let minItems = schema.elements.length; | |
| const prefixItems = schema.elements.map(e => { | |
| if (e.isOptional) { | |
| minItems--; | |
| } | |
| const v = recur(e.type); | |
| const a = collectJsonSchemaAnnotations(e.annotations); | |
| return a ? appendJsonSchema(v, a) : v; | |
| }); | |
| if (prefixItems.length > 0) { | |
| out.prefixItems = prefixItems; | |
| out.maxItems = schema.elements.length; | |
| if (minItems > 0) { | |
| out.minItems = minItems; | |
| } | |
| } else { | |
| out.items = false; | |
| } | |
| if (schema.rest.length > 0) { | |
| delete out.maxItems; | |
| const rest = recur(schema.rest[0]); | |
| if (Object.keys(rest).length > 0) { | |
| out.items = rest; | |
| } else { | |
| delete out.items; | |
| } | |
| } | |
| return out; | |
| } | |
| case "Objects": | |
| { | |
| if (schema.propertySignatures.length === 0 && schema.indexSignatures.length === 0) { | |
| return { | |
| anyOf: [{ | |
| type: "object" | |
| }, { | |
| type: "array" | |
| }] | |
| }; | |
| } | |
| const out = { | |
| type: "object" | |
| }; | |
| const properties = {}; | |
| const required = []; | |
| for (const ps of schema.propertySignatures) { | |
| const name = ps.name; | |
| if (typeof name !== "string") { | |
| throw new globalThis.Error(`Unsupported property signature name: ${format(name)}`); | |
| } | |
| const v = recur(ps.type); | |
| const a = collectJsonSchemaAnnotations(ps.annotations); | |
| properties[name] = a ? appendJsonSchema(v, a) : v; | |
| // Property is required only if it's not explicitly optional AND doesn't contain Undefined | |
| if (!ps.isOptional) { | |
| required.push(name); | |
| } | |
| } | |
| if (Object.keys(properties).length > 0) { | |
| out.properties = properties; | |
| } | |
| if (required.length > 0) { | |
| out.required = required; | |
| } | |
| out.additionalProperties = additionalProperties; | |
| const patternProperties = {}; | |
| // Handle index signatures | |
| for (const is of schema.indexSignatures) { | |
| let type = recur(is.type); | |
| // Collapse unannotated Never ({ not: {} }) to false, but keep annotated schemas as objects. | |
| if (Object.keys(type).length === 1 && "not" in type) { | |
| type = false; | |
| } | |
| const patterns = getParameterPatterns(is.parameter); | |
| if (patterns.length > 0) { | |
| for (const pattern of patterns) { | |
| patternProperties[pattern] = type; | |
| } | |
| } else { | |
| out.additionalProperties = type; | |
| } | |
| } | |
| if (Object.keys(patternProperties).length > 0) { | |
| out.patternProperties = patternProperties; | |
| delete out.additionalProperties; | |
| } | |
| if (Predicate.isObject(out.additionalProperties) && Rec.isEmptyRecord(out.additionalProperties)) { | |
| delete out.additionalProperties; | |
| } | |
| return out; | |
| } | |
| case "Union": | |
| { | |
| const types = schema.types.map(recur); | |
| if (types.length === 0) { | |
| // anyOf MUST be a non-empty array | |
| return { | |
| not: {} | |
| }; | |
| } | |
| if (types.length > 1) { | |
| const compacted = compactEnums(types); | |
| if (compacted) return compacted; | |
| } | |
| return schema.mode === "anyOf" ? { | |
| anyOf: types | |
| } : { | |
| oneOf: types | |
| }; | |
| } | |
| } | |
| } | |
| // Collapses [{type:"string",enum:["a"]},{type:"string",enum:["b"]}] into {type:"string",enum:["a","b"]}. | |
| // Returns undefined if members have different types, extra keys (e.g. title), or empty enums. | |
| function compactEnums(types) { | |
| let sharedType; | |
| const values = []; | |
| for (const t of types) { | |
| const keys = Object.keys(t); | |
| if (keys.length !== 2 || t.type === undefined || !Array.isArray(t.enum) || t.enum.length === 0) { | |
| return undefined; | |
| } | |
| if (sharedType === undefined) { | |
| sharedType = t.type; | |
| } else if (t.type !== sharedType) { | |
| return undefined; | |
| } | |
| for (const v of t.enum) { | |
| values.push(v); | |
| } | |
| } | |
| return { | |
| type: sharedType, | |
| enum: values | |
| }; | |
| } | |
| function collectJsonSchemaAnnotations(annotations) { | |
| if (annotations === undefined) return undefined; | |
| const out = {}; | |
| if (typeof annotations.title === "string") out.title = annotations.title; | |
| if (typeof annotations.description === "string") out.description = annotations.description;else if (generateDescriptions && typeof annotations.expected === "string") out.description = annotations.expected; | |
| if (annotations.default !== undefined) out.default = annotations.default; | |
| if (Array.isArray(annotations.examples)) out.examples = annotations.examples; | |
| if (typeof annotations.readOnly === "boolean") out.readOnly = annotations.readOnly; | |
| if (typeof annotations.writeOnly === "boolean") out.writeOnly = annotations.writeOnly; | |
| if (typeof annotations.format === "string") out.format = annotations.format; | |
| if (typeof annotations.contentEncoding === "string") out.contentEncoding = annotations.contentEncoding; | |
| if (typeof annotations.contentMediaType === "string") out.contentMediaType = annotations.contentMediaType; | |
| if (includeAnnotationKey) { | |
| for (const [key, value] of Object.entries(annotations)) { | |
| if (value === undefined) continue; | |
| if (standardJsonSchemaAnnotationKeys.has(key)) continue; | |
| if (!includeAnnotationKey(key)) continue; | |
| out[key] = value; | |
| } | |
| } | |
| if (Object.keys(out).length > 0) return out; | |
| } | |
| function collectJsonSchemaChecks(checks, type) { | |
| return checks.map(collectJsonSchemaCheck).filter(c => c !== undefined); | |
| function collectJsonSchemaCheck(check) { | |
| switch (check._tag) { | |
| case "Filter": | |
| return filterToJsonSchema(check, type); | |
| case "FilterGroup": | |
| { | |
| const checks = check.checks.map(collectJsonSchemaCheck).filter(c => c !== undefined); | |
| if (checks.length === 0) return undefined; | |
| let out = { | |
| allOf: checks | |
| }; | |
| const a = collectJsonSchemaAnnotations(check.annotations); | |
| if (a) { | |
| out = { | |
| ...out, | |
| ...a | |
| }; | |
| } | |
| return out; | |
| } | |
| } | |
| } | |
| } | |
| function filterToJsonSchema(filter, type) { | |
| const meta = filter.meta; | |
| if (!meta) return undefined; | |
| let out = on(meta); | |
| const a = collectJsonSchemaAnnotations(filter.annotations); | |
| if (a) { | |
| out = { | |
| ...out, | |
| ...a | |
| }; | |
| } | |
| return out; | |
| function on(meta) { | |
| switch (meta._tag) { | |
| case "isMinLength": | |
| return type === "array" ? { | |
| minItems: meta.minLength | |
| } : { | |
| minLength: meta.minLength | |
| }; | |
| case "isMaxLength": | |
| return type === "array" ? { | |
| maxItems: meta.maxLength | |
| } : { | |
| maxLength: meta.maxLength | |
| }; | |
| case "isLengthBetween": | |
| return type === "array" ? { | |
| allOf: [{ | |
| minItems: meta.minimum | |
| }, { | |
| maxItems: meta.maximum | |
| }] | |
| } : { | |
| allOf: [{ | |
| minLength: meta.minimum | |
| }, { | |
| maxLength: meta.maximum | |
| }] | |
| }; | |
| case "isPattern": | |
| case "isGUID": | |
| case "isULID": | |
| case "isBase64": | |
| case "isBase64Url": | |
| case "isStartsWith": | |
| case "isEndsWith": | |
| case "isIncludes": | |
| case "isUppercased": | |
| case "isLowercased": | |
| case "isCapitalized": | |
| case "isUncapitalized": | |
| case "isTrimmed": | |
| case "isStringFinite": | |
| case "isStringBigInt": | |
| case "isStringSymbol": | |
| return { | |
| pattern: meta.regExp.source | |
| }; | |
| case "isUUID": | |
| return { | |
| pattern: meta.regExp.source, | |
| format: "uuid" | |
| }; | |
| case "isFinite": | |
| case "isInt": | |
| return undefined; | |
| case "isMultipleOf": | |
| return { | |
| multipleOf: meta.divisor | |
| }; | |
| case "isGreaterThanOrEqualTo": | |
| return { | |
| minimum: meta.minimum | |
| }; | |
| case "isLessThanOrEqualTo": | |
| return { | |
| maximum: meta.maximum | |
| }; | |
| case "isGreaterThan": | |
| return { | |
| exclusiveMinimum: meta.exclusiveMinimum | |
| }; | |
| case "isLessThan": | |
| return { | |
| exclusiveMaximum: meta.exclusiveMaximum | |
| }; | |
| case "isBetween": | |
| { | |
| return { | |
| [meta.exclusiveMinimum ? "exclusiveMinimum" : "minimum"]: meta.minimum, | |
| [meta.exclusiveMaximum ? "exclusiveMaximum" : "maximum"]: meta.maximum | |
| }; | |
| } | |
| case "isUnique": | |
| return { | |
| uniqueItems: true | |
| }; | |
| case "isMinProperties": | |
| return { | |
| minProperties: meta.minProperties | |
| }; | |
| case "isMaxProperties": | |
| return { | |
| maxProperties: meta.maxProperties | |
| }; | |
| case "isPropertiesLengthBetween": | |
| return { | |
| minProperties: meta.minimum, | |
| maxProperties: meta.maximum | |
| }; | |
| case "isPropertyNames": | |
| return { | |
| propertyNames: recur(meta.propertyNames) | |
| }; | |
| case "isDateValid": | |
| return { | |
| format: "date-time" | |
| }; | |
| } | |
| } | |
| } | |
| function getParameterPatterns(parameter) { | |
| switch (parameter._tag) { | |
| default: | |
| throw new globalThis.Error(`Unsupported index signature parameter: ${parameter._tag}`); | |
| case "Reference": | |
| return getParameterPatterns(multiDocument.references[parameter.$ref]); | |
| case "String": | |
| return getPatterns(parameter); | |
| case "TemplateLiteral": | |
| return [`^${parameter.parts.map(getPartPattern).join("")}$`]; | |
| case "Union": | |
| return parameter.types.flatMap(getParameterPatterns); | |
| } | |
| } | |
| } | |
| function getPatterns(s) { | |
| return recur(s.checks); | |
| function recur(checks) { | |
| return checks.flatMap(c => { | |
| switch (c._tag) { | |
| case "Filter": | |
| { | |
| if ("regExp" in c.meta) { | |
| return [c.meta.regExp.source]; | |
| } | |
| return []; | |
| } | |
| case "FilterGroup": | |
| return recur(c.checks); | |
| } | |
| }); | |
| } | |
| } | |
| function hasCheck(checks, tag) { | |
| return checks.some(c => { | |
| switch (c._tag) { | |
| case "Filter": | |
| return c.meta._tag === tag; | |
| case "FilterGroup": | |
| return hasCheck(c.checks, tag); | |
| } | |
| }); | |
| } | |
| function appendJsonSchema(a, b) { | |
| if (Object.keys(a).length === 0) return b; | |
| const len = Object.keys(b).length; | |
| if (len === 0) return a; | |
| const members = Array.isArray(b.allOf) && len === 1 ? b.allOf : [b]; | |
| if (Array.isArray(a.allOf)) { | |
| return { | |
| ...a, | |
| allOf: [...a.allOf, ...members] | |
| }; | |
| } | |
| if (typeof a.$ref === "string") { | |
| return { | |
| allOf: [a, ...members] | |
| }; | |
| } | |
| return { | |
| ...a, | |
| allOf: members | |
| }; | |
| } | |
| function getPartPattern(part) { | |
| switch (part._tag) { | |
| case "Literal": | |
| return RegEx.escape(globalThis.String(part.literal)); | |
| case "String": | |
| return SchemaAST.STRING_PATTERN; | |
| case "Number": | |
| return SchemaAST.FINITE_PATTERN; | |
| case "TemplateLiteral": | |
| return part.parts.map(getPartPattern).join(""); | |
| case "Union": | |
| return part.types.map(getPartPattern).join("|"); | |
| default: | |
| throw new globalThis.Error("Unsupported part", { | |
| cause: part | |
| }); | |
| } | |
| } | |
| //# sourceMappingURL=representation.js.map |
Xet Storage Details
- Size:
- 25.8 kB
- Xet hash:
- 4443cf02aa24d803ae21a901052e8c40570be288cd6d21d052bcd6ff5bda9089
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.