Skip to content

Commit

Permalink
chore(jex): move around a bunch of files and rename functions (#429)
Browse files Browse the repository at this point in the history
  • Loading branch information
franklevasseur authored Oct 16, 2024
1 parent 74c3214 commit a502f8f
Show file tree
Hide file tree
Showing 19 changed files with 69 additions and 71 deletions.
28 changes: 16 additions & 12 deletions jex/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
import { JexError } from './errors'
import * as jex from './jex-representation'
import * as jexir from './jexir'
import { JSONSchema7 } from 'json-schema'

import { jexEquals } from './jex-equals'
import { jexExtends, JexExtensionResult } from './jex-extends'
import { jexMerge } from './jex-merge'

export * as errors from './errors'

export type JSONSchema = JSONSchema7

export const jsonSchemaEquals = async (a: JSONSchema, b: JSONSchema): Promise<boolean> => {
const jexA = await jex.toJex(a)
const jexB = await jex.toJex(b)
return jex.jexEquals(jexA, jexB)
const jexA = await jexir.fromJsonSchema(a)
const jexB = await jexir.fromJsonSchema(b)
return jexEquals(jexA, jexB)
}

export const jsonSchemaExtends = async (a: JSONSchema, b: JSONSchema): Promise<jex.JexExtensionResult> => {
const jexA = await jex.toJex(a)
const jexB = await jex.toJex(b)
return jex.jexExtends(jexA, jexB)
export const jsonSchemaExtends = async (a: JSONSchema, b: JSONSchema): Promise<JexExtensionResult> => {
const jexA = await jexir.fromJsonSchema(a)
const jexB = await jexir.fromJsonSchema(b)
return jexExtends(jexA, jexB)
}

export const jsonSchemaMerge = async (a: JSONSchema, b: JSONSchema): Promise<JSONSchema> => {
const jexA = await jex.toJex(a)
const jexB = await jex.toJex(b)
const jexA = await jexir.fromJsonSchema(a)
const jexB = await jexir.fromJsonSchema(b)

if (jexA.type !== 'object' || jexB.type !== 'object') {
throw new JexError('Both schemas must be objects to be merged')
}

const mergedJex = jex.jexMerge(jexA, jexB)
return jex.fromJex(mergedJex)
const mergedJex = jexMerge(jexA, jexB)
return jexir.toJsonSchema(mergedJex)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as types from './typings'
import * as jexir from './jexir'

type TypeOf<T extends string | number | boolean> = T extends string
? 'string'
Expand All @@ -8,9 +8,9 @@ type TypeOf<T extends string | number | boolean> = T extends string
? 'boolean'
: never

function _literal<T extends string>(value: T): types.JexStringLiteral<T>
function _literal<T extends number>(value: T): types.JexNumberLiteral<T>
function _literal<T extends boolean>(value: T): types.JexBooleanLiteral<T>
function _literal<T extends string>(value: T): jexir.JexStringLiteral<T>
function _literal<T extends number>(value: T): jexir.JexNumberLiteral<T>
function _literal<T extends boolean>(value: T): jexir.JexBooleanLiteral<T>
function _literal(value: string | number | boolean) {
return { type: typeof value as TypeOf<typeof value>, value }
}
Expand All @@ -24,9 +24,9 @@ export const $ = {
null: () => ({ type: 'null' }),
undefined: () => ({ type: 'undefined' }),
literal: _literal,
object: <Args extends Record<string, types.JexType>>(properties: Args) => ({ type: 'object', properties }),
array: <const Args extends types.JexType>(items: Args) => ({ type: 'array', items: items as Args }),
map: <const Args extends types.JexType>(items: Args) => ({ type: 'map', items: items as Args }),
tuple: <const Args extends types.JexType[]>(items: Args) => ({ type: 'tuple', items }),
union: <const Args extends types.JexType[]>(anyOf: Args) => ({ type: 'union', anyOf })
} satisfies Record<string, (...args: any[]) => types.JexType>
object: <Args extends Record<string, jexir.JexType>>(properties: Args) => ({ type: 'object', properties }),
array: <const Args extends jexir.JexType>(items: Args) => ({ type: 'array', items: items as Args }),
map: <const Args extends jexir.JexType>(items: Args) => ({ type: 'map', items: items as Args }),
tuple: <const Args extends jexir.JexType[]>(items: Args) => ({ type: 'tuple', items }),
union: <const Args extends jexir.JexType[]>(anyOf: Args) => ({ type: 'union', anyOf })
} satisfies Record<string, (...args: any[]) => jexir.JexType>
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as types from './typings'
import * as utils from '../utils'
import * as jexir from './jexir'
import * as utils from './utils'
import _ from 'lodash'

export class JexSet extends utils.collection.CustomSet<types.JexType> {
public constructor(items: types.JexType[] = []) {
export class JexSet extends utils.collection.CustomSet<jexir.JexType> {
public constructor(items: jexir.JexType[] = []) {
super(items, { compare: jexEquals })
}
}

export const jexEquals = (a: types.JexType, b: types.JexType): boolean => {
export const jexEquals = (a: jexir.JexType, b: jexir.JexType): boolean => {
if (a.type === 'array' && b.type === 'array') {
return jexEquals(a.items, b.items)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import * as types from './typings'
import * as jexir from './jexir'
import { JexExtensionResult, jexExtends } from './jex-extends'
import { expect, test } from 'vitest'
import { $ } from './jex-builder'
import { toString } from './to-string'

const failureMessage = (res: JexExtensionResult): string => {
if (res.extends) return ''
return '\n' + res.reasons.map((r) => ` - ${r}\n`).join('')
}

const successMessage = (typeA: types.JexType, typeB: types.JexType): string => {
return `${toString(typeA)}${toString(typeB)}`
const successMessage = (typeA: jexir.JexType, typeB: jexir.JexType): string => {
return `${jexir.toString(typeA)}${jexir.toString(typeB)}`
}

const expectJex = (typeA: types.JexType) => ({
const expectJex = (typeA: jexir.JexType) => ({
not: {
toExtend: (typeB: types.JexType) => {
toExtend: (typeB: jexir.JexType) => {
const actual = jexExtends(typeA, typeB)
expect(actual.extends).to.eq(false, successMessage(typeA, typeB))
}
},
toExtend: (typeB: types.JexType) => {
toExtend: (typeB: jexir.JexType) => {
const actual = jexExtends(typeA, typeB)
expect(actual.extends).to.eq(true, failureMessage(actual))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as types from './typings'
import * as jexir from './jexir'
import { PropertyPath, pathToString } from './property-path'
import { toString } from './to-string'

type _JexFailureReason = {
path: PropertyPath
typeA: types.JexType
typeB: types.JexType
typeA: jexir.JexType
typeB: jexir.JexType
}
type _JexExtensionSuccess = { result: true }
type _JexExtensionFailure = { result: false; reasons: _JexFailureReason[] }
Expand All @@ -21,13 +20,13 @@ const _splitSuccessFailure = (results: _JexExtensionResult[]): [_JexExtensionSuc
return [success, failure]
}

type _LiteralOf<T extends types.JexPrimitive> = Extract<types.JexLiteral, { type: T['type'] }>
const _primitiveExtends = <T extends types.JexPrimitive>(
type _LiteralOf<T extends jexir.JexPrimitive> = Extract<jexir.JexLiteral, { type: T['type'] }>
const _primitiveExtends = <T extends jexir.JexPrimitive>(
path: PropertyPath,
typeA: T | _LiteralOf<T>,
typeB: types.JexType
typeB: jexir.JexType
): _JexExtensionResult => {
const isT = (x: types.JexType): x is T | _LiteralOf<T> => x.type === typeA.type
const isT = (x: jexir.JexType): x is T | _LiteralOf<T> => x.type === typeA.type
if (!isT(typeB)) {
return {
result: false,
Expand Down Expand Up @@ -57,7 +56,7 @@ const _primitiveExtends = <T extends types.JexPrimitive>(
return { result: true }
}

const _jexExtends = (path: PropertyPath, typeA: types.JexType, typeB: types.JexType): _JexExtensionResult => {
const _jexExtends = (path: PropertyPath, typeA: jexir.JexType, typeB: jexir.JexType): _JexExtensionResult => {
if (typeB.type === 'any' || typeA.type === 'any') {
return { result: true }
}
Expand Down Expand Up @@ -104,7 +103,7 @@ const _jexExtends = (path: PropertyPath, typeA: types.JexType, typeB: types.JexT

if (typeA.type === 'map') {
if (typeB.type === 'object') {
const valueA: types.JexUnion = { type: 'union', anyOf: [typeA.items, { type: 'undefined' }] } // could be undefined
const valueA: jexir.JexUnion = { type: 'union', anyOf: [typeA.items, { type: 'undefined' }] } // could be undefined
const extensions = Object.entries(typeB.properties).map(([key, valueB]) => {
const newPath: PropertyPath = [...path, { type: 'key', value: key }]
return _jexExtends(newPath, valueA, valueB)
Expand Down Expand Up @@ -133,7 +132,7 @@ const _jexExtends = (path: PropertyPath, typeA: types.JexType, typeB: types.JexT
return { result: false, reasons: failures.flatMap((f) => f.reasons) }
}
if (typeB.type === 'tuple') {
const zipped = typeA.items.map((c, i) => [c, typeB.items[i]] satisfies [types.JexType, types.JexType | undefined])
const zipped = typeA.items.map((c, i) => [c, typeB.items[i]] satisfies [jexir.JexType, jexir.JexType | undefined])
const extensions = zipped.map(([c, p], i): _JexExtensionResult => {
if (p === undefined) {
return { result: true } // A tuple is longer than B
Expand Down Expand Up @@ -169,10 +168,10 @@ const _jexExtends = (path: PropertyPath, typeA: types.JexType, typeB: types.JexT
}

const _reasonToString = (reason: _JexFailureReason): string =>
`${pathToString(reason.path)}: ${toString(reason.typeA)}${toString(reason.typeB)}`
`${pathToString(reason.path)}: ${jexir.toString(reason.typeA)}${jexir.toString(reason.typeB)}`

export type JexExtensionResult = { extends: true } | { extends: false; reasons: string[] }
export const jexExtends = (typeA: types.JexType, typeB: types.JexType): JexExtensionResult => {
export const jexExtends = (typeA: jexir.JexType, typeB: jexir.JexType): JexExtensionResult => {
const extension = _jexExtends([], typeA, typeB)
if (extension.result) return { extends: true }
return { extends: false, reasons: extension.reasons.map(_reasonToString) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as types from './typings'
import * as jexir from './jexir'

export const jexMerge = (a: types.JexObject, b: types.JexObject): types.JexObject => {
const merged: types.JexObject = { type: 'object', properties: {} }
export const jexMerge = (a: jexir.JexObject, b: jexir.JexObject): jexir.JexObject => {
const merged: jexir.JexObject = { type: 'object', properties: {} }

const aKeys = Object.keys(a.properties)
const bKeys = Object.keys(b.properties)
Expand Down
6 changes: 0 additions & 6 deletions jex/src/jex-representation/index.ts

This file was deleted.

File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import z from 'zod'
import { JexType } from './typings'

import zodToJsonSchema from 'zod-to-json-schema'
import { toJex as j2x } from './to-jex'
import { fromJsonSchema as j2x } from './from-json-schema'
import { jexBotCreateSchema, zodBotCreateSchema } from './create-bot.utils.test'
import { jexEquals as jexEquals } from './jex-equals'
import { jexEquals as jexEquals } from '../jex-equals'

const z2j = (s: z.ZodType): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ const _toInternalRep = (schema: JSONSchema7): types.JexType => {
return { type: 'any' }
}

export const toJex = async (schema: JSONSchema7): Promise<types.JexType> => {
export const fromJsonSchema = async (schema: JSONSchema7): Promise<types.JexType> => {
const unref = await _dereference(schema)
const jex = _toInternalRep(unref)
return flattenUnions(jex)
Expand Down
4 changes: 4 additions & 0 deletions jex/src/jexir/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './typings'
export * from './from-json-schema'
export * from './to-json-schema'
export * from './to-string'
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { fromJex } from './from-jex'
import { toJsonSchema } from './to-json-schema'
import { expect, test } from 'vitest'
import { $ } from './jex-builder'
import { $ } from '../jex-builder'
import { JexType } from './typings'
import { JSONSchema7 } from 'json-schema'

const expectJex = (jex: JexType) => ({
toEqualJsonSchema: (expectedJsonSchema: JSONSchema7) => {
expect(fromJex(jex)).toEqual(expectedJsonSchema)
expect(toJsonSchema(jex)).toEqual(expectedJsonSchema)
}
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as types from './typings'
const isOptional = (jexir: types.JexType): boolean =>
jexir.type === 'union' && jexir.anyOf.some((jex) => jex.type === 'undefined')

export const fromJex = (jex: types.JexType): JSONSchema7 => {
export const toJsonSchema = (jex: types.JexType): JSONSchema7 => {
if (jex.type === 'null') {
return { type: 'null' }
}
Expand All @@ -21,29 +21,29 @@ export const fromJex = (jex: types.JexType): JSONSchema7 => {
}

if (jex.type === 'union') {
return { anyOf: jex.anyOf.map(fromJex) }
return { anyOf: jex.anyOf.map(toJsonSchema) }
}

if (jex.type === 'array') {
return { type: 'array', items: fromJex(jex.items) }
return { type: 'array', items: toJsonSchema(jex.items) }
}

if (jex.type === 'map') {
return { type: 'object', additionalProperties: fromJex(jex.items) }
return { type: 'object', additionalProperties: toJsonSchema(jex.items) }
}

if (jex.type === 'object') {
const entries = Object.entries(jex.properties)
const requiredEntries = entries.filter(([_, jex]) => !isOptional(jex)).map(([k]) => k)
return {
type: 'object',
properties: Object.fromEntries(entries.map(([k, v]) => [k, fromJex(v)])),
properties: Object.fromEntries(entries.map(([k, v]) => [k, toJsonSchema(v)])),
required: requiredEntries
}
}

if (jex.type === 'tuple') {
return { type: 'array', items: jex.items.map(fromJex) }
return { type: 'array', items: jex.items.map(toJsonSchema) }
}

return {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, expect } from 'vitest'
import { toString } from './to-string'
import { $ } from './jex-builder'
import { $ } from '../jex-builder'
import { JexType } from './typings'

const expectJex = (jex: JexType) => ({
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as utils from '../utils'

export type JexStringContent = string
export type JexString = {
type: 'string'
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit a502f8f

Please sign in to comment.