@json-layout/core 0.21.1 → 0.22.0

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.
Files changed (50) hide show
  1. package/package.json +1 -1
  2. package/src/compile/index.js +17 -9
  3. package/src/compile/serialize.js +2 -1
  4. package/src/compile/skeleton-node.js +78 -23
  5. package/src/compile/skeleton-tree.js +22 -2
  6. package/src/compile/types.ts +3 -2
  7. package/src/compile/utils/resolve-refs.js +99 -38
  8. package/src/state/index.js +11 -7
  9. package/src/state/state-node.js +12 -9
  10. package/src/state/types.ts +2 -2
  11. package/types/compile/index.d.ts +0 -16
  12. package/types/compile/index.d.ts.map +0 -1
  13. package/types/compile/serialize.d.ts +0 -6
  14. package/types/compile/serialize.d.ts.map +0 -1
  15. package/types/compile/skeleton-node.d.ts +0 -18
  16. package/types/compile/skeleton-node.d.ts.map +0 -1
  17. package/types/compile/skeleton-tree.d.ts +0 -13
  18. package/types/compile/skeleton-tree.d.ts.map +0 -1
  19. package/types/compile/types.d.ts +0 -57
  20. package/types/compile/types.d.ts.map +0 -1
  21. package/types/compile/utils/resolve-refs.d.ts +0 -9
  22. package/types/compile/utils/resolve-refs.d.ts.map +0 -1
  23. package/types/i18n/en.d.ts +0 -3
  24. package/types/i18n/en.d.ts.map +0 -1
  25. package/types/i18n/fr.d.ts +0 -3
  26. package/types/i18n/fr.d.ts.map +0 -1
  27. package/types/i18n/index.d.ts +0 -3
  28. package/types/i18n/index.d.ts.map +0 -1
  29. package/types/i18n/types.d.ts +0 -36
  30. package/types/i18n/types.d.ts.map +0 -1
  31. package/types/index.d.ts +0 -5
  32. package/types/index.d.ts.map +0 -1
  33. package/types/state/index.d.ts +0 -285
  34. package/types/state/index.d.ts.map +0 -1
  35. package/types/state/state-node.d.ts +0 -36
  36. package/types/state/state-node.d.ts.map +0 -1
  37. package/types/state/state-tree.d.ts +0 -13
  38. package/types/state/state-tree.d.ts.map +0 -1
  39. package/types/state/types.d.ts +0 -175
  40. package/types/state/types.d.ts.map +0 -1
  41. package/types/state/utils/display.d.ts +0 -51
  42. package/types/state/utils/display.d.ts.map +0 -1
  43. package/types/state/utils/immutable.d.ts +0 -21
  44. package/types/state/utils/immutable.d.ts.map +0 -1
  45. package/types/utils/build.d.ts +0 -5
  46. package/types/utils/build.d.ts.map +0 -1
  47. package/types/utils/clone.d.ts +0 -3
  48. package/types/utils/clone.d.ts.map +0 -1
  49. package/types/utils/doc-options.d.ts +0 -14
  50. package/types/utils/doc-options.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@json-layout/core",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "description": "Compilation and state management utilities for JSON Layout.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -9,11 +9,11 @@ import MarkdownIt from 'markdown-it'
9
9
  import { produce } from 'immer'
10
10
  import i18n from '../i18n/index.js'
11
11
  import { makeSkeletonTree } from './skeleton-tree.js'
12
- import { resolveRefs } from './utils/resolve-refs.js'
12
+ import { resolveLocaleRefs } from './utils/resolve-refs.js'
13
13
  import clone from '../utils/clone.js'
14
14
  import { standardComponents } from '@json-layout/vocabulary'
15
15
 
16
- export { resolveRefs } from './utils/resolve-refs.js'
16
+ export { resolveLocaleRefs } from './utils/resolve-refs.js'
17
17
 
18
18
  /**
19
19
  * @typedef {import('./types.js').SkeletonTree} SkeletonTree
@@ -103,9 +103,8 @@ export function compile (_schema, partialOptions = {}) {
103
103
  const options = fillOptions(partialOptions)
104
104
 
105
105
  const schema = /** @type {import('ajv').SchemaObject} */(clone(_schema))
106
-
107
106
  schema.$id = schema.$id ?? '_jl'
108
- resolveRefs(schema, options.ajv, options.locale)
107
+ const getJSONRef = resolveLocaleRefs(schema, options.ajv, options.locale)
109
108
 
110
109
  /** @type {string[]} */
111
110
  const validatePointers = []
@@ -115,18 +114,26 @@ export function compile (_schema, partialOptions = {}) {
115
114
  const expressionsDefinitions = []
116
115
  /** @type {Record<string, string[]>} */
117
116
  const validationErrors = {}
118
-
119
- const skeletonTree = makeSkeletonTree(
117
+ /** @type {Record<string, import('./types.js').SkeletonTree>} */
118
+ const skeletonTrees = {}
119
+
120
+ // makeSkeletonTree also mutates the schema (adding some error messages)
121
+ const mainTreePointer = `${schema.$id}#`
122
+ // @ts-ignore
123
+ skeletonTrees[mainTreePointer] = 'recursing'
124
+ skeletonTrees[mainTreePointer] = makeSkeletonTree(
120
125
  schema,
126
+ schema.$id,
121
127
  options,
128
+ getJSONRef,
129
+ skeletonTrees,
122
130
  validatePointers,
123
131
  validationErrors,
124
132
  normalizedLayouts,
125
133
  expressionsDefinitions,
126
- `${schema.$id}#`,
134
+ mainTreePointer,
127
135
  'main'
128
136
  )
129
-
130
137
  options.ajv.addSchema(schema)
131
138
 
132
139
  const uriResolver = options.ajv.opts.uriResolver
@@ -168,7 +175,8 @@ export function compile (_schema, partialOptions = {}) {
168
175
  return {
169
176
  options,
170
177
  schema,
171
- skeletonTree,
178
+ mainTree: mainTreePointer,
179
+ skeletonTrees,
172
180
  validates,
173
181
  validationErrors,
174
182
  normalizedLayouts,
@@ -79,7 +79,8 @@ export const exportLocalizeErrors = localizeErrors;\n` + code
79
79
 
80
80
  const ast = parseModule(code)
81
81
  ast.exports.compiledLayout = {
82
- skeletonTree: compiledLayout.skeletonTree,
82
+ mainTree: compiledLayout.mainTree,
83
+ skeletonTrees: clone(compiledLayout.skeletonTrees),
83
84
  normalizedLayouts: clone(compiledLayout.normalizedLayouts),
84
85
  validates: {},
85
86
  validationErrors: compiledLayout.validationErrors,
@@ -1,16 +1,20 @@
1
1
  // import Debug from 'debug'
2
2
  import { normalizeLayoutFragment, isSwitchStruct, isGetItemsExpression, isGetItemsFetch, isItemsLayout, getSchemaFragmentType } from '@json-layout/vocabulary'
3
3
  import { makeSkeletonTree } from './skeleton-tree.js'
4
+ import { partialResolveRefs } from './utils/resolve-refs.js'
4
5
 
5
6
  /**
6
- * @param {any} schema
7
+ * @param {any} rawSchema
8
+ * @param {string} sourceSchemaId
7
9
  * @param {import('./index.js').CompileOptions} options
10
+ * @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
11
+ * @param {Record<string, import('./types.js').SkeletonTree>} skeletonTrees
8
12
  * @param {string[]} validates
9
13
  * @param {Record<string, string[]>} validationErrors
10
14
  * @param {Record<string, import('@json-layout/vocabulary').NormalizedLayout>} normalizedLayouts
11
15
  * @param {import('@json-layout/vocabulary').Expression[]} expressions
12
16
  * @param {string | number} key
13
- * @param {string} pointer
17
+ * @param {string} currentPointer
14
18
  * @param {string | null} parentPointer
15
19
  * @param {boolean} required
16
20
  * @param {string} [condition]
@@ -19,24 +23,37 @@ import { makeSkeletonTree } from './skeleton-tree.js'
19
23
  * @returns {import('./types.js').SkeletonNode}
20
24
  */
21
25
  export function makeSkeletonNode (
22
- schema,
26
+ rawSchema,
27
+ sourceSchemaId,
23
28
  options,
29
+ getJSONRef,
30
+ skeletonTrees,
24
31
  validates,
25
32
  validationErrors,
26
33
  normalizedLayouts,
27
34
  expressions,
28
35
  key,
29
- pointer,
36
+ currentPointer,
30
37
  parentPointer,
31
38
  required,
32
39
  condition,
33
40
  dependent,
34
41
  knownType
35
42
  ) {
43
+ let schemaId = sourceSchemaId
44
+ let schema = rawSchema
45
+ let pointer = currentPointer
46
+ let refFragment
47
+ if (schema.$ref) {
48
+ [refFragment, schemaId, pointer] = getJSONRef(sourceSchemaId, schema.$ref)
49
+ schema = {...rawSchema, ...refFragment}
50
+ delete schema.$ref
51
+ }
52
+ schema = partialResolveRefs(schema, schemaId, getJSONRef)
36
53
  const { type, nullable } = knownType ? { type: knownType, nullable: false } : getSchemaFragmentType(schema)
37
54
 
38
55
  // improve on ajv error messages based on ajv-errors (https://ajv.js.org/packages/ajv-errors.html)
39
- schema.errorMessage = schema.errorMessage ?? {}
56
+ rawSchema.errorMessage = rawSchema.errorMessage ?? {}
40
57
  if (!normalizedLayouts[pointer]) {
41
58
  const normalizationResult = normalizeLayoutFragment(
42
59
  /** @type {import('@json-layout/vocabulary').SchemaFragment} */(schema),
@@ -138,7 +155,10 @@ export function makeSkeletonNode (
138
155
  const dependent = schema.dependentRequired && Object.values(schema.dependentRequired).some(dependentProperties => dependentProperties.includes(propertyKey))
139
156
  node.children.push(makeSkeletonNode(
140
157
  schema.properties[propertyKey],
158
+ schemaId,
141
159
  options,
160
+ getJSONRef,
161
+ skeletonTrees,
142
162
  validates,
143
163
  validationErrors,
144
164
  normalizedLayouts,
@@ -156,7 +176,10 @@ export function makeSkeletonNode (
156
176
  const dependentPointer = schema.dependentSchemas?.[propertyKey] ? `${pointer}/dependentSchemas/${propertyKey}` : `${pointer}/dependencies/${propertyKey}`
157
177
  node.children.push(makeSkeletonNode(
158
178
  dependentSchema,
179
+ schemaId,
159
180
  options,
181
+ getJSONRef,
182
+ skeletonTrees,
160
183
  validates,
161
184
  validationErrors,
162
185
  normalizedLayouts,
@@ -177,7 +200,10 @@ export function makeSkeletonNode (
177
200
  for (let i = 0; i < schema.allOf.length; i++) {
178
201
  const allOfNode = makeSkeletonNode(
179
202
  schema.allOf[i],
203
+ schemaId,
180
204
  options,
205
+ getJSONRef,
206
+ skeletonTrees,
181
207
  validates,
182
208
  validationErrors,
183
209
  normalizedLayouts,
@@ -213,23 +239,32 @@ export function makeSkeletonNode (
213
239
  validationErrors[oneOfPointer.replace('_jl#', '/')] = normalizationResult.errors
214
240
  }
215
241
  }
216
- /** @type {import('./types.js').SkeletonTree[]} */
242
+ /** @type {string[]} */
217
243
  const childrenTrees = []
218
244
  /** @type {string[]} */
219
245
  for (let i = 0; i < schema.oneOf.length; i++) {
220
246
  if (!schema.oneOf[i].type) schema.oneOf[i].type = type
221
247
  const title = schema.oneOf[i].title ?? `option ${i}`
222
248
  delete schema.oneOf[i].title
223
- childrenTrees.push(makeSkeletonTree(
224
- schema.oneOf[i],
225
- options,
226
- validates,
227
- validationErrors,
228
- normalizedLayouts,
229
- expressions,
230
- `${oneOfPointer}/${i}`,
231
- title
232
- ))
249
+ const childTreePointer = `${oneOfPointer}/${i}`
250
+ if (!skeletonTrees[childTreePointer]) {
251
+ // @ts-ignore
252
+ skeletonTrees[childTreePointer] = 'recursing'
253
+ skeletonTrees[childTreePointer] = makeSkeletonTree(
254
+ schema.oneOf[i],
255
+ schemaId,
256
+ options,
257
+ getJSONRef,
258
+ skeletonTrees,
259
+ validates,
260
+ validationErrors,
261
+ normalizedLayouts,
262
+ expressions,
263
+ childTreePointer,
264
+ title
265
+ )
266
+ }
267
+ childrenTrees.push(childTreePointer)
233
268
  }
234
269
  node.children = node.children ?? []
235
270
  node.children.push({
@@ -237,7 +272,7 @@ export function makeSkeletonNode (
237
272
  pointer: `${pointer}/oneOf`,
238
273
  parentPointer: pointer,
239
274
  childrenTrees,
240
- pure: childrenTrees[0].root.pure,
275
+ pure: skeletonTrees[childrenTrees[0]]?.root.pure,
241
276
  propertyKeys: [],
242
277
  roPropertyKeys: []
243
278
  })
@@ -250,7 +285,10 @@ export function makeSkeletonNode (
250
285
  node.children = node.children ?? []
251
286
  node.children.push(makeSkeletonNode(
252
287
  schema.then,
288
+ schemaId,
253
289
  options,
290
+ getJSONRef,
291
+ skeletonTrees,
254
292
  validates,
255
293
  validationErrors,
256
294
  normalizedLayouts,
@@ -268,7 +306,10 @@ export function makeSkeletonNode (
268
306
  node.children = node.children ?? []
269
307
  node.children.push(makeSkeletonNode(
270
308
  schema.else,
309
+ schemaId,
271
310
  options,
311
+ getJSONRef,
312
+ skeletonTrees,
272
313
  validates,
273
314
  validationErrors,
274
315
  normalizedLayouts,
@@ -300,7 +341,10 @@ export function makeSkeletonNode (
300
341
  node.children = schema.items.map((/** @type {any} */ itemSchema, /** @type {number} */ i) => {
301
342
  return makeSkeletonNode(
302
343
  itemSchema,
344
+ schemaId,
303
345
  options,
346
+ getJSONRef,
347
+ skeletonTrees,
304
348
  validates,
305
349
  validationErrors,
306
350
  normalizedLayouts,
@@ -312,23 +356,34 @@ export function makeSkeletonNode (
312
356
  )
313
357
  })
314
358
  } else {
315
- node.childrenTrees = [
316
- makeSkeletonTree(
359
+ const childTreePointer = `${pointer}/items`
360
+ if (!skeletonTrees[childTreePointer]) {
361
+ // @ts-ignore
362
+ skeletonTrees[childTreePointer] = 'recursing'
363
+ skeletonTrees[childTreePointer] = makeSkeletonTree(
317
364
  schema.items,
365
+ schemaId,
318
366
  options,
367
+ getJSONRef,
368
+ skeletonTrees,
319
369
  validates,
320
370
  validationErrors,
321
371
  normalizedLayouts,
322
372
  expressions,
323
- `${pointer}/items`,
373
+ childTreePointer,
324
374
  schema.items.title
325
375
  )
326
- ]
376
+ }
377
+ node.childrenTrees = [childTreePointer]
327
378
  }
328
379
  }
329
380
 
330
- for (const child of node.children || []) if (!child.pure) node.pure = false
331
- for (const childTree of node.childrenTrees || []) if (!childTree.root.pure) node.pure = false
381
+ for (const child of node.children || []) {
382
+ if (!child.pure) node.pure = false
383
+ }
384
+ for (const childTree of node.childrenTrees || []) {
385
+ if (!skeletonTrees[childTree]?.root?.pure) node.pure = false
386
+ }
332
387
 
333
388
  return node
334
389
  }
@@ -5,7 +5,10 @@ import { makeSkeletonNode } from './skeleton-node.js'
5
5
 
6
6
  /**
7
7
  * @param {any} schema
8
+ * @param {string} schemaId
8
9
  * @param {import('./index.js').CompileOptions} options
10
+ * @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
11
+ * @param {Record<string, import('./types.js').SkeletonTree>} skeletonTrees
9
12
  * @param {string[]} validates
10
13
  * @param {Record<string, string[]>} validationErrors
11
14
  * @param {Record<string, import('@json-layout/vocabulary').NormalizedLayout>} normalizedLayouts
@@ -16,7 +19,10 @@ import { makeSkeletonNode } from './skeleton-node.js'
16
19
  */
17
20
  export function makeSkeletonTree (
18
21
  schema,
22
+ schemaId,
19
23
  options,
24
+ getJSONRef,
25
+ skeletonTrees,
20
26
  validates,
21
27
  validationErrors,
22
28
  normalizedLayouts,
@@ -24,7 +30,21 @@ export function makeSkeletonTree (
24
30
  pointer,
25
31
  title
26
32
  ) {
27
- const root = makeSkeletonNode(schema, options, validates, validationErrors, normalizedLayouts, expressions, '', pointer, null, true)
28
- validates.push(pointer)
33
+ const root = makeSkeletonNode(
34
+ schema,
35
+ schemaId,
36
+ options,
37
+ getJSONRef,
38
+ skeletonTrees,
39
+ validates,
40
+ validationErrors,
41
+ normalizedLayouts,
42
+ expressions,
43
+ '',
44
+ pointer,
45
+ null,
46
+ true
47
+ )
48
+ validates.push(root.pointer)
29
49
  return { title, root }
30
50
  }
@@ -41,7 +41,8 @@ export type PartialCompileOptions = Partial<Omit<CompileOptions, 'messages'>> &
41
41
  export interface CompiledLayout {
42
42
  options?: CompileOptions
43
43
  schema?: SchemaObject
44
- skeletonTree: SkeletonTree
44
+ mainTree: string,
45
+ skeletonTrees: Record<string, SkeletonTree>
45
46
  validates: Record<string, ValidateFunction>
46
47
  validationErrors: Record<string, string[]>
47
48
  normalizedLayouts: Record<string, NormalizedLayout>
@@ -70,7 +71,7 @@ export interface SkeletonNode {
70
71
  roPropertyKeys: string[]
71
72
  condition?: Expression
72
73
  children?: SkeletonNode[] // optional children in the case of arrays and object nodes
73
- childrenTrees?: SkeletonTree[] // other trees that can be instantiated with separate validation (for example in the case of new array items of oneOfs, etc)
74
+ childrenTrees?: string[] // other trees that can be instantiated with separate validation (for example in the case of new array items of oneOfs, etc)
74
75
  required?: boolean
75
76
  nullable?: boolean
76
77
  }
@@ -1,69 +1,130 @@
1
1
  import ajvModule from 'ajv/dist/2019.js'
2
-
3
- const Ajv = ajvModule.default
2
+ import clone from '../../utils/clone.js'
4
3
 
5
4
  /**
6
5
  * @param {Record<string, import('ajv').SchemaObject>} schemas
7
- * @param {string} ref
8
6
  * @param {ajvModule.default} ajv
9
- * @returns {[any, string]}
7
+ * @returns {(schemaId: string, ref: string) => [any, string, string]}
10
8
  */
11
- const getJSONRef = (schemas, ref, ajv) => {
12
- const [schemaId, pointer] = ref.split('#')
13
- schemas[schemaId] = schemas[schemaId] ?? (ajv.getSchema(schemaId)?.schema)
14
- if (!schemas[schemaId]) throw new Error(`reference not found ${schemaId}`)
15
- const pointerParts = pointer.split('/').filter(p => !!p)
16
- const { value: fragment } = pointerParts.reduce((a, pointerPart) => {
17
- a.path.push(pointerPart)
18
- if (!(pointerPart in a.value)) throw new Error(`reference not found ${schemaId}#${a.path.join('/')}`)
19
- a.value = a.value[pointerPart]
20
- return a
21
- }, { path: ['/'], value: schemas[schemaId] })
22
- return [fragment, schemaId]
9
+ const prepareGetJSONRef = (schemas, ajv) =>
10
+ {
11
+ return (sourceSchemaId, ref) => {
12
+ const fullRef = ajv.opts.uriResolver.resolve(sourceSchemaId, ref)
13
+ const [schemaId, pointer] = fullRef.split('#')
14
+ schemas[schemaId] = schemas[schemaId] ?? (ajv.getSchema(schemaId)?.schema)
15
+ if (!schemas[schemaId]) throw new Error(`reference not found ${schemaId}`)
16
+ const pointerParts = pointer.split('/').filter(p => !!p)
17
+ const { value: fragment } = pointerParts.reduce((a, pointerPart) => {
18
+ a.path.push(pointerPart)
19
+ if (!(pointerPart in a.value)) throw new Error(`reference not found ${schemaId}#${a.path.join('/')}`)
20
+ a.value = a.value[pointerPart]
21
+ return a
22
+ }, { path: /** @type {string[]} */([]), value: schemas[schemaId] })
23
+ return [fragment, schemaId, fullRef]
24
+ }
25
+ }
26
+
27
+ /**
28
+ * mutates a schema by replacing ~$locale~ in all refs
29
+ * @param {import('ajv').SchemaObject} schema
30
+ * @param {ajvModule.default} ajv
31
+ * @param {string} locale
32
+ * @returns {(schemaId: string, ref: string) => [any, string, string]}
33
+ */
34
+ export function resolveLocaleRefs (schema, ajv, locale = 'en') {
35
+ if (!schema.$id) throw new Error('missing schema id')
36
+ const getJSONRef = prepareGetJSONRef({ [schema.$id]: schema }, ajv)
37
+ /** @type {any[]} */
38
+ const recursed = []
39
+ recurseResolveLocale(schema, schema.$id, getJSONRef, locale, recursed)
40
+ return getJSONRef
23
41
  }
24
42
 
25
43
  /**
26
- * @param {Record<string, import('ajv').SchemaObject>} schemas
27
44
  * @param {import('ajv').SchemaObject} schemaFragment
28
45
  * @param {string} schemaId
29
- * @param {ajvModule.default} ajv
46
+ * @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
30
47
  * @param {string} locale
31
- * @returns {import('ajv').SchemaObject}
48
+ * @param {any[]} recursed
32
49
  */
33
- const recurse = (schemas, schemaFragment, schemaId, ajv, locale = 'en') => {
50
+
51
+ const recurseResolveLocale = (schemaFragment, schemaId, getJSONRef, locale, recursed) => {
52
+ if (recursed.includes(schemaFragment)) return
53
+ recursed.push(schemaFragment)
34
54
  for (const key of Object.keys(schemaFragment)) {
35
55
  if (schemaFragment[key] && typeof schemaFragment[key] === 'object') {
36
56
  if ('$ref' in schemaFragment[key]) {
37
- const fullRef = ajv.opts.uriResolver.resolve(schemaId, schemaFragment[key].$ref).replace('~$locale~', locale)
38
- const fullRefDefaultLocale = ajv.opts.uriResolver.resolve(schemaId, schemaFragment[key].$ref).replace('~$locale~', 'en')
57
+ const ref = schemaFragment[key].$ref.replace('~$locale~', locale)
58
+ const refDefaultLocale = schemaFragment[key].$ref.replace('~$locale~', 'en')
39
59
  let refFragment, refSchemaId
40
60
  try {
41
- [refFragment, refSchemaId] = getJSONRef(schemas, fullRef, ajv)
61
+ [refFragment, refSchemaId] = getJSONRef(schemaId, ref)
62
+ schemaFragment[key].$ref = ref
42
63
  } catch (err) {
43
- [refFragment, refSchemaId] = getJSONRef(schemas, fullRefDefaultLocale, ajv)
64
+ [refFragment, refSchemaId] = getJSONRef(schemaId, refDefaultLocale)
65
+ schemaFragment[key].$ref = refDefaultLocale
44
66
  }
45
- if (typeof refFragment === 'object' && !Array.isArray(refFragment)) {
46
- schemaFragment[key] = { ...refFragment, ...schemaFragment[key] }
47
- delete schemaFragment[key].$ref
48
- } else {
67
+ if (typeof refFragment === 'string') {
49
68
  schemaFragment[key] = refFragment
69
+ } else {
70
+ recurseResolveLocale(refFragment, refSchemaId, getJSONRef, locale, recursed)
50
71
  }
51
- recurse(schemas, schemaFragment[key], refSchemaId, ajv, locale)
52
72
  } else {
53
- recurse(schemas, schemaFragment[key], schemaId, ajv, locale)
73
+ recurseResolveLocale(schemaFragment[key], schemaId, getJSONRef, locale, recursed)
54
74
  }
55
75
  }
56
76
  }
57
- return schemaFragment
58
77
  }
59
78
 
60
79
  /**
80
+ * partially resolve a schema but not recursively, used for layout normalization
61
81
  * @param {import('ajv').SchemaObject} schema
62
- * @param {ajvModule.default} ajv
63
- * @param {string} locale
64
- * @returns {import('ajv').SchemaObject}
82
+ * @param {string} schemaId
83
+ * @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
65
84
  */
66
- export function resolveRefs (schema, ajv, locale = 'en') {
67
- if (!schema.$id) throw new Error('missing schema id')
68
- return recurse({ [schema.$id]: schema }, schema, schema.$id, ajv ?? new Ajv(), locale)
69
- }
85
+ export function partialResolveRefs (schema, schemaId, getJSONRef) {
86
+ let clonedSchema = null
87
+ if (schema.items && schema.items.$ref) {
88
+ const [refFragment] = getJSONRef(schemaId, schema.items.$ref)
89
+ clonedSchema = clonedSchema ?? clone(schema)
90
+ clonedSchema.items = { ...refFragment, ... schema.items }
91
+ }
92
+ if (schema.properties) {
93
+ for (const key in schema.properties) {
94
+ if (schema.properties[key].$ref) {
95
+ const [refFragment] = getJSONRef(schemaId, schema.properties[key].$ref)
96
+ clonedSchema = clonedSchema ?? clone(schema)
97
+ clonedSchema.properties[key] = { ...refFragment, ... schema.properties[key] }
98
+ }
99
+ }
100
+ }
101
+ if (schema.oneOf) {
102
+ for (let i = 0; i < schema.oneOf.length; i++) {
103
+ if (schema.oneOf[i].$ref) {
104
+ const [refFragment] = getJSONRef(schemaId, schema.oneOf[i].$ref)
105
+ clonedSchema = clonedSchema ?? clone(schema)
106
+ clonedSchema.oneOf[i] = { ...refFragment, ... schema.oneOf[i] }
107
+ }
108
+ }
109
+ }
110
+ if (schema.anyOf) {
111
+ for (let i = 0; i < schema.anyOf.length; i++) {
112
+ if (schema.anyOf[i].$ref) {
113
+ const [refFragment] = getJSONRef(schemaId, schema.anyOf[i].$ref)
114
+ clonedSchema = clonedSchema ?? clone(schema)
115
+ clonedSchema.anyOf[i] = { ...refFragment, ... schema.anyOf[i] }
116
+ }
117
+ }
118
+ }
119
+ if (schema.allOf) {
120
+ for (let i = 0; i < schema.allOf.length; i++) {
121
+ if (schema.allOf[i].$ref) {
122
+ const [refFragment] = getJSONRef(schemaId, schema.allOf[i].$ref)
123
+ clonedSchema = clonedSchema ?? clone(schema)
124
+ clonedSchema.allOf[i] = { ...refFragment, ... schema.allOf[i] }
125
+ }
126
+ }
127
+ }
128
+
129
+ return clonedSchema ?? schema
130
+ }
@@ -227,7 +227,7 @@ export class StatefulLayout {
227
227
  this._previousAutofocusTarget = null
228
228
  this._data = data
229
229
  this.initValidationState()
230
- this.activeItems = {}
230
+ this.activatedItems = {}
231
231
  this.updateState()
232
232
  this.handleAutofocus()
233
233
  }
@@ -295,7 +295,7 @@ export class StatefulLayout {
295
295
  createStateTree (rehydrate = false) {
296
296
  /** @type {CreateStateTreeContext} */
297
297
  const createStateTreeContext = {
298
- activeItems: this.activeItems,
298
+ activatedItems: this.activatedItems,
299
299
  autofocusTarget: this._autofocusTarget,
300
300
  initial: !this._lastCreateStateTreeContext,
301
301
  rehydrate,
@@ -322,7 +322,6 @@ export class StatefulLayout {
322
322
  validatedChildren: createStateTreeContext.nodes.filter(n => n.validated).map(n => n.fullKey)
323
323
  }
324
324
  }
325
- this.activeItems = createStateTreeContext.activeItems
326
325
  this.files = shallowProduceArray(this.files, createStateTreeContext.files)
327
326
  }
328
327
 
@@ -416,7 +415,7 @@ export class StatefulLayout {
416
415
  }
417
416
 
418
417
  if (activateKey !== undefined) {
419
- this.activeItems = produce(this.activeItems, draft => { draft[node.fullKey] = activateKey })
418
+ this.activatedItems = produce(this.activatedItems, draft => { draft[node.fullKey] = activateKey })
420
419
  this._autofocusTarget = node.fullKey + '/' + activateKey
421
420
  }
422
421
  if (node.parentFullKey === null) {
@@ -625,14 +624,14 @@ export class StatefulLayout {
625
624
  /**
626
625
  * @type {Record<string, number>}
627
626
  */
628
- activeItems
627
+ activatedItems
629
628
 
630
629
  /**
631
630
  * @param {StateNode} node
632
631
  * @param {number} key
633
632
  */
634
633
  activateItem (node, key) {
635
- this.activeItems = produce(this.activeItems, draft => { draft[node.fullKey] = key })
634
+ this.activatedItems = produce(this.activatedItems, draft => { draft[node.fullKey] = key })
636
635
  this._autofocusTarget = node.fullKey + '/' + key
637
636
  if (node.key === '$oneOf') {
638
637
  this.input(node, undefined)
@@ -646,7 +645,12 @@ export class StatefulLayout {
646
645
  * @param {StateNode} node
647
646
  */
648
647
  deactivateItem (node) {
649
- this.activeItems = produce(this.activeItems, draft => { delete draft[node.fullKey] })
648
+ // also deactivate children oneOf for example
649
+ this.activatedItems = produce(this.activatedItems, draft => {
650
+ for (const key in draft) {
651
+ if (key.startsWith(node.fullKey)) delete draft[key]
652
+ }
653
+ })
650
654
  this.updateState()
651
655
  }
652
656