Socket
Socket
Sign inDemoInstall

@json-layout/core

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@json-layout/core - npm Package Compare versions

Comparing version 0.21.1 to 0.22.0

2

package.json
{
"name": "@json-layout/core",
"version": "0.21.1",
"version": "0.22.0",
"description": "Compilation and state management utilities for JSON Layout.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -12,7 +12,7 @@ // compileStatic is meant to produce a serializable result

import { makeSkeletonTree } from './skeleton-tree.js'
import { resolveRefs } from './utils/resolve-refs.js'
import { resolveLocaleRefs } from './utils/resolve-refs.js'
import clone from '../utils/clone.js'
import { standardComponents } from '@json-layout/vocabulary'
export { resolveRefs } from './utils/resolve-refs.js'
export { resolveLocaleRefs } from './utils/resolve-refs.js'

@@ -107,5 +107,4 @@ /**

const schema = /** @type {import('ajv').SchemaObject} */(clone(_schema))
schema.$id = schema.$id ?? '_jl'
resolveRefs(schema, options.ajv, options.locale)
const getJSONRef = resolveLocaleRefs(schema, options.ajv, options.locale)

@@ -120,6 +119,15 @@ /** @type {string[]} */

const validationErrors = {}
/** @type {Record<string, import('./types.js').SkeletonTree>} */
const skeletonTrees = {}
const skeletonTree = makeSkeletonTree(
// makeSkeletonTree also mutates the schema (adding some error messages)
const mainTreePointer = `${schema.$id}#`
// @ts-ignore
skeletonTrees[mainTreePointer] = 'recursing'
skeletonTrees[mainTreePointer] = makeSkeletonTree(
schema,
schema.$id,
options,
getJSONRef,
skeletonTrees,
validatePointers,

@@ -129,6 +137,5 @@ validationErrors,

expressionsDefinitions,
`${schema.$id}#`,
mainTreePointer,
'main'
)
options.ajv.addSchema(schema)

@@ -175,3 +182,4 @@

schema,
skeletonTree,
mainTree: mainTreePointer,
skeletonTrees,
validates,

@@ -178,0 +186,0 @@ validationErrors,

@@ -82,3 +82,4 @@ // import Debug from 'debug'

ast.exports.compiledLayout = {
skeletonTree: compiledLayout.skeletonTree,
mainTree: compiledLayout.mainTree,
skeletonTrees: clone(compiledLayout.skeletonTrees),
normalizedLayouts: clone(compiledLayout.normalizedLayouts),

@@ -85,0 +86,0 @@ validates: {},

// import Debug from 'debug'
import { normalizeLayoutFragment, isSwitchStruct, isGetItemsExpression, isGetItemsFetch, isItemsLayout, getSchemaFragmentType } from '@json-layout/vocabulary'
import { makeSkeletonTree } from './skeleton-tree.js'
import { partialResolveRefs } from './utils/resolve-refs.js'
/**
* @param {any} schema
* @param {any} rawSchema
* @param {string} sourceSchemaId
* @param {import('./index.js').CompileOptions} options
* @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
* @param {Record<string, import('./types.js').SkeletonTree>} skeletonTrees
* @param {string[]} validates

@@ -13,3 +17,3 @@ * @param {Record<string, string[]>} validationErrors

* @param {string | number} key
* @param {string} pointer
* @param {string} currentPointer
* @param {string | null} parentPointer

@@ -23,4 +27,7 @@ * @param {boolean} required

export function makeSkeletonNode (
schema,
rawSchema,
sourceSchemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -31,3 +38,3 @@ validationErrors,

key,
pointer,
currentPointer,
parentPointer,

@@ -39,6 +46,16 @@ required,

) {
let schemaId = sourceSchemaId
let schema = rawSchema
let pointer = currentPointer
let refFragment
if (schema.$ref) {
[refFragment, schemaId, pointer] = getJSONRef(sourceSchemaId, schema.$ref)
schema = {...rawSchema, ...refFragment}
delete schema.$ref
}
schema = partialResolveRefs(schema, schemaId, getJSONRef)
const { type, nullable } = knownType ? { type: knownType, nullable: false } : getSchemaFragmentType(schema)
// improve on ajv error messages based on ajv-errors (https://ajv.js.org/packages/ajv-errors.html)
schema.errorMessage = schema.errorMessage ?? {}
rawSchema.errorMessage = rawSchema.errorMessage ?? {}
if (!normalizedLayouts[pointer]) {

@@ -145,3 +162,6 @@ const normalizationResult = normalizeLayoutFragment(

schema.properties[propertyKey],
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -164,3 +184,6 @@ validationErrors,

dependentSchema,
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -186,3 +209,6 @@ validationErrors,

schema.allOf[i],
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -223,3 +249,3 @@ validationErrors,

}
/** @type {import('./types.js').SkeletonTree[]} */
/** @type {string[]} */
const childrenTrees = []

@@ -231,12 +257,21 @@ /** @type {string[]} */

delete schema.oneOf[i].title
childrenTrees.push(makeSkeletonTree(
schema.oneOf[i],
options,
validates,
validationErrors,
normalizedLayouts,
expressions,
`${oneOfPointer}/${i}`,
title
))
const childTreePointer = `${oneOfPointer}/${i}`
if (!skeletonTrees[childTreePointer]) {
// @ts-ignore
skeletonTrees[childTreePointer] = 'recursing'
skeletonTrees[childTreePointer] = makeSkeletonTree(
schema.oneOf[i],
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,
validationErrors,
normalizedLayouts,
expressions,
childTreePointer,
title
)
}
childrenTrees.push(childTreePointer)
}

@@ -249,3 +284,3 @@ node.children = node.children ?? []

childrenTrees,
pure: childrenTrees[0].root.pure,
pure: skeletonTrees[childrenTrees[0]]?.root.pure,
propertyKeys: [],

@@ -263,3 +298,6 @@ roPropertyKeys: []

schema.then,
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -282,3 +320,6 @@ validationErrors,

schema.else,
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -315,3 +356,6 @@ validationErrors,

itemSchema,
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -328,6 +372,12 @@ validationErrors,

} else {
node.childrenTrees = [
makeSkeletonTree(
const childTreePointer = `${pointer}/items`
if (!skeletonTrees[childTreePointer]) {
// @ts-ignore
skeletonTrees[childTreePointer] = 'recursing'
skeletonTrees[childTreePointer] = makeSkeletonTree(
schema.items,
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -337,13 +387,18 @@ validationErrors,

expressions,
`${pointer}/items`,
childTreePointer,
schema.items.title
)
]
}
node.childrenTrees = [childTreePointer]
}
}
for (const child of node.children || []) if (!child.pure) node.pure = false
for (const childTree of node.childrenTrees || []) if (!childTree.root.pure) node.pure = false
for (const child of node.children || []) {
if (!child.pure) node.pure = false
}
for (const childTree of node.childrenTrees || []) {
if (!skeletonTrees[childTree]?.root?.pure) node.pure = false
}
return node
}

@@ -8,3 +8,6 @@ // import Debug from 'debug'

* @param {any} schema
* @param {string} schemaId
* @param {import('./index.js').CompileOptions} options
* @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
* @param {Record<string, import('./types.js').SkeletonTree>} skeletonTrees
* @param {string[]} validates

@@ -20,3 +23,6 @@ * @param {Record<string, string[]>} validationErrors

schema,
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,

@@ -29,5 +35,19 @@ validationErrors,

) {
const root = makeSkeletonNode(schema, options, validates, validationErrors, normalizedLayouts, expressions, '', pointer, null, true)
validates.push(pointer)
const root = makeSkeletonNode(
schema,
schemaId,
options,
getJSONRef,
skeletonTrees,
validates,
validationErrors,
normalizedLayouts,
expressions,
'',
pointer,
null,
true
)
validates.push(root.pointer)
return { title, root }
}

@@ -44,3 +44,4 @@ import type ajvModule from 'ajv/dist/2019.js'

schema?: SchemaObject
skeletonTree: SkeletonTree
mainTree: string,
skeletonTrees: Record<string, SkeletonTree>
validates: Record<string, ValidateFunction>

@@ -74,5 +75,5 @@ validationErrors: Record<string, string[]>

children?: SkeletonNode[] // optional children in the case of arrays and object nodes
childrenTrees?: SkeletonTree[] // other trees that can be instantiated with separate validation (for example in the case of new array items of oneOfs, etc)
childrenTrees?: string[] // other trees that can be instantiated with separate validation (for example in the case of new array items of oneOfs, etc)
required?: boolean
nullable?: boolean
}
import ajvModule from 'ajv/dist/2019.js'
import clone from '../../utils/clone.js'
const Ajv = ajvModule.default
/**
* @param {Record<string, import('ajv').SchemaObject>} schemas
* @param {string} ref
* @param {ajvModule.default} ajv
* @returns {[any, string]}
* @returns {(schemaId: string, ref: string) => [any, string, string]}
*/
const getJSONRef = (schemas, ref, ajv) => {
const [schemaId, pointer] = ref.split('#')
schemas[schemaId] = schemas[schemaId] ?? (ajv.getSchema(schemaId)?.schema)
if (!schemas[schemaId]) throw new Error(`reference not found ${schemaId}`)
const pointerParts = pointer.split('/').filter(p => !!p)
const { value: fragment } = pointerParts.reduce((a, pointerPart) => {
a.path.push(pointerPart)
if (!(pointerPart in a.value)) throw new Error(`reference not found ${schemaId}#${a.path.join('/')}`)
a.value = a.value[pointerPart]
return a
}, { path: ['/'], value: schemas[schemaId] })
return [fragment, schemaId]
const prepareGetJSONRef = (schemas, ajv) =>
{
return (sourceSchemaId, ref) => {
const fullRef = ajv.opts.uriResolver.resolve(sourceSchemaId, ref)
const [schemaId, pointer] = fullRef.split('#')
schemas[schemaId] = schemas[schemaId] ?? (ajv.getSchema(schemaId)?.schema)
if (!schemas[schemaId]) throw new Error(`reference not found ${schemaId}`)
const pointerParts = pointer.split('/').filter(p => !!p)
const { value: fragment } = pointerParts.reduce((a, pointerPart) => {
a.path.push(pointerPart)
if (!(pointerPart in a.value)) throw new Error(`reference not found ${schemaId}#${a.path.join('/')}`)
a.value = a.value[pointerPart]
return a
}, { path: /** @type {string[]} */([]), value: schemas[schemaId] })
return [fragment, schemaId, fullRef]
}
}
/**
* @param {Record<string, import('ajv').SchemaObject>} schemas
* mutates a schema by replacing ~$locale~ in all refs
* @param {import('ajv').SchemaObject} schema
* @param {ajvModule.default} ajv
* @param {string} locale
* @returns {(schemaId: string, ref: string) => [any, string, string]}
*/
export function resolveLocaleRefs (schema, ajv, locale = 'en') {
if (!schema.$id) throw new Error('missing schema id')
const getJSONRef = prepareGetJSONRef({ [schema.$id]: schema }, ajv)
/** @type {any[]} */
const recursed = []
recurseResolveLocale(schema, schema.$id, getJSONRef, locale, recursed)
return getJSONRef
}
/**
* @param {import('ajv').SchemaObject} schemaFragment
* @param {string} schemaId
* @param {ajvModule.default} ajv
* @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
* @param {string} locale
* @returns {import('ajv').SchemaObject}
* @param {any[]} recursed
*/
const recurse = (schemas, schemaFragment, schemaId, ajv, locale = 'en') => {
const recurseResolveLocale = (schemaFragment, schemaId, getJSONRef, locale, recursed) => {
if (recursed.includes(schemaFragment)) return
recursed.push(schemaFragment)
for (const key of Object.keys(schemaFragment)) {
if (schemaFragment[key] && typeof schemaFragment[key] === 'object') {
if ('$ref' in schemaFragment[key]) {
const fullRef = ajv.opts.uriResolver.resolve(schemaId, schemaFragment[key].$ref).replace('~$locale~', locale)
const fullRefDefaultLocale = ajv.opts.uriResolver.resolve(schemaId, schemaFragment[key].$ref).replace('~$locale~', 'en')
const ref = schemaFragment[key].$ref.replace('~$locale~', locale)
const refDefaultLocale = schemaFragment[key].$ref.replace('~$locale~', 'en')
let refFragment, refSchemaId
try {
[refFragment, refSchemaId] = getJSONRef(schemas, fullRef, ajv)
[refFragment, refSchemaId] = getJSONRef(schemaId, ref)
schemaFragment[key].$ref = ref
} catch (err) {
[refFragment, refSchemaId] = getJSONRef(schemas, fullRefDefaultLocale, ajv)
[refFragment, refSchemaId] = getJSONRef(schemaId, refDefaultLocale)
schemaFragment[key].$ref = refDefaultLocale
}
if (typeof refFragment === 'object' && !Array.isArray(refFragment)) {
schemaFragment[key] = { ...refFragment, ...schemaFragment[key] }
delete schemaFragment[key].$ref
if (typeof refFragment === 'string') {
schemaFragment[key] = refFragment
} else {
schemaFragment[key] = refFragment
recurseResolveLocale(refFragment, refSchemaId, getJSONRef, locale, recursed)
}
recurse(schemas, schemaFragment[key], refSchemaId, ajv, locale)
} else {
recurse(schemas, schemaFragment[key], schemaId, ajv, locale)
recurseResolveLocale(schemaFragment[key], schemaId, getJSONRef, locale, recursed)
}
}
}
return schemaFragment
}
/**
* partially resolve a schema but not recursively, used for layout normalization
* @param {import('ajv').SchemaObject} schema
* @param {ajvModule.default} ajv
* @param {string} locale
* @returns {import('ajv').SchemaObject}
* @param {string} schemaId
* @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
*/
export function resolveRefs (schema, ajv, locale = 'en') {
if (!schema.$id) throw new Error('missing schema id')
return recurse({ [schema.$id]: schema }, schema, schema.$id, ajv ?? new Ajv(), locale)
}
export function partialResolveRefs (schema, schemaId, getJSONRef) {
let clonedSchema = null
if (schema.items && schema.items.$ref) {
const [refFragment] = getJSONRef(schemaId, schema.items.$ref)
clonedSchema = clonedSchema ?? clone(schema)
clonedSchema.items = { ...refFragment, ... schema.items }
}
if (schema.properties) {
for (const key in schema.properties) {
if (schema.properties[key].$ref) {
const [refFragment] = getJSONRef(schemaId, schema.properties[key].$ref)
clonedSchema = clonedSchema ?? clone(schema)
clonedSchema.properties[key] = { ...refFragment, ... schema.properties[key] }
}
}
}
if (schema.oneOf) {
for (let i = 0; i < schema.oneOf.length; i++) {
if (schema.oneOf[i].$ref) {
const [refFragment] = getJSONRef(schemaId, schema.oneOf[i].$ref)
clonedSchema = clonedSchema ?? clone(schema)
clonedSchema.oneOf[i] = { ...refFragment, ... schema.oneOf[i] }
}
}
}
if (schema.anyOf) {
for (let i = 0; i < schema.anyOf.length; i++) {
if (schema.anyOf[i].$ref) {
const [refFragment] = getJSONRef(schemaId, schema.anyOf[i].$ref)
clonedSchema = clonedSchema ?? clone(schema)
clonedSchema.anyOf[i] = { ...refFragment, ... schema.anyOf[i] }
}
}
}
if (schema.allOf) {
for (let i = 0; i < schema.allOf.length; i++) {
if (schema.allOf[i].$ref) {
const [refFragment] = getJSONRef(schemaId, schema.allOf[i].$ref)
clonedSchema = clonedSchema ?? clone(schema)
clonedSchema.allOf[i] = { ...refFragment, ... schema.allOf[i] }
}
}
}
return clonedSchema ?? schema
}

@@ -230,3 +230,3 @@ // eslint-disable-next-line import/no-named-default

this.initValidationState()
this.activeItems = {}
this.activatedItems = {}
this.updateState()

@@ -299,3 +299,3 @@ this.handleAutofocus()

const createStateTreeContext = {
activeItems: this.activeItems,
activatedItems: this.activatedItems,
autofocusTarget: this._autofocusTarget,

@@ -327,3 +327,2 @@ initial: !this._lastCreateStateTreeContext,

}
this.activeItems = createStateTreeContext.activeItems
this.files = shallowProduceArray(this.files, createStateTreeContext.files)

@@ -422,3 +421,3 @@ }

if (activateKey !== undefined) {
this.activeItems = produce(this.activeItems, draft => { draft[node.fullKey] = activateKey })
this.activatedItems = produce(this.activatedItems, draft => { draft[node.fullKey] = activateKey })
this._autofocusTarget = node.fullKey + '/' + activateKey

@@ -632,3 +631,3 @@ }

*/
activeItems
activatedItems

@@ -640,3 +639,3 @@ /**

activateItem (node, key) {
this.activeItems = produce(this.activeItems, draft => { draft[node.fullKey] = key })
this.activatedItems = produce(this.activatedItems, draft => { draft[node.fullKey] = key })
this._autofocusTarget = node.fullKey + '/' + key

@@ -655,3 +654,8 @@ if (node.key === '$oneOf') {

deactivateItem (node) {
this.activeItems = produce(this.activeItems, draft => { delete draft[node.fullKey] })
// also deactivate children oneOf for example
this.activatedItems = produce(this.activatedItems, draft => {
for (const key in draft) {
if (key.startsWith(node.fullKey)) delete draft[key]
}
})
this.updateState()

@@ -658,0 +662,0 @@ }

@@ -70,3 +70,3 @@ import { isSwitchStruct, childIsCompObject, isCompositeLayout, isFocusableLayout, isItemsLayout, isGetItemsExpression, isGetItemsFetch } from '@json-layout/vocabulary'

const produceStateNodeData = produce((draft, parentDataPath, children, additionalPropertiesErrors, propertyKeys, removePropertyKeys) => {
if (propertyKeys) {
if (propertyKeys && (propertyKeys.length || children?.length)) {
for (const key of Object.keys(draft)) {

@@ -163,3 +163,6 @@ if (!propertyKeys.includes(key)) delete draft[key]

const matchChildError = (error, skeleton, dataPath, parentDataPath) => {
if (!error.schemaPath.startsWith(skeleton.pointer)) return false
if (!(
error.schemaPath === skeleton.pointer ||
error.schemaPath.startsWith(skeleton.pointer + '/')
)) return false
if (error.instancePath.startsWith(dataPath)) return true

@@ -267,3 +270,3 @@ return false

if (skeleton.pure && reusedNode && !reusedNode.error && !reusedNode.childError) {
cacheKey = [parentOptions, compiledLayout, fullKey, skeleton, childDefinition, parentDisplay.width, validationState, context.activeItems, context.initial, data]
cacheKey = [parentOptions, compiledLayout, fullKey, skeleton, childDefinition, parentDisplay.width, validationState, context.activatedItems, context.initial, data]
if (context.cacheKeys[fullKey] && shallowEqualArray(context.cacheKeys[fullKey], cacheKey)) {

@@ -340,3 +343,3 @@ return reusedNode

/** @type {number} */
const activeChildTreeIndex = fullKey in context.activeItems ? context.activeItems[fullKey] : skeleton.childrenTrees?.findIndex((childTree) => compiledLayout.validates[childTree.root.pointer](data))
const activeChildTreeIndex = fullKey in context.activatedItems ? context.activatedItems[fullKey] : skeleton.childrenTrees?.findIndex((childTree) => compiledLayout.validates[compiledLayout.skeletonTrees[childTree].root.pointer](data))
if (activeChildTreeIndex !== -1) {

@@ -351,6 +354,5 @@ context.errors = context.errors?.filter(error => {

})
context.activeItems = produce(context.activeItems, draft => { draft[fullKey] = activeChildTreeIndex })
const activeChildKey = `${fullKey}/${activeChildTreeIndex}`
if (context.autofocusTarget === fullKey) context.autofocusTarget = activeChildKey
const activeChildTree = skeleton.childrenTrees[activeChildTreeIndex]
const activeChildTree = compiledLayout.skeletonTrees[skeleton.childrenTrees[activeChildTreeIndex]]
children = [

@@ -380,3 +382,3 @@ createStateNode(

const arrayData = /** @type {unknown[]} */(data ?? [])
const childSkeleton = /** @type {import('../index.js').SkeletonNode} */(skeleton?.childrenTrees?.[0]?.root)
const childSkeleton = /** @type {import('../index.js').SkeletonNode} */(skeleton?.childrenTrees?.[0] && compiledLayout.skeletonTrees[skeleton?.childrenTrees?.[0]]?.root)
const listItemOptions = layout.listEditMode === 'inline' ? options : produceReadonlyArrayItemOptions(options)

@@ -391,3 +393,3 @@ children = []

context,
(layout.listEditMode === 'inline-single' && context.activeItems[fullKey] === i) ? options : listItemOptions,
(layout.listEditMode === 'inline-single' && context.activatedItems[fullKey] === i) ? options : listItemOptions,
compiledLayout,

@@ -430,3 +432,4 @@ i,

let nodeData = children
/** @type {unknown} */
let nodeData = typeof data === 'object' && !(data instanceof File)
? produceStateNodeData(

@@ -433,0 +436,0 @@ /** @type {Record<string, unknown>} */(data ?? {}),

@@ -65,3 +65,3 @@ import { type ErrorObject } from 'ajv/dist/2019.js'

files: FileRef[]
activeItems: Record<string, number>
activatedItems: Record<string, number>
autofocusTarget: string | null

@@ -80,3 +80,3 @@ initial: boolean

// [parentOptions, compiledLayout, fullKey, skeleton, childDefinition, parentWidth, validationState, activeItems, initial, data]
// [parentOptions, compiledLayout, fullKey, skeleton, childDefinition, parentWidth, validationState, activatedItems, initial, data]
export type StateNodeCacheKey = [

@@ -83,0 +83,0 @@ StateNodeOptions,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc