@cratis/scene.engine 4.5.0 → 4.6.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.
@@ -44,6 +44,14 @@ function toInspectedItems(raw, local) {
44
44
  };
45
45
  });
46
46
  }
47
+ /**
48
+ * The value a property takes when nothing configures it. A stored `null` is a real value for a `json` property
49
+ * (it is valid JSON, and clearing it to the default would hide it), so only that type keeps it; every other
50
+ * type falls back to its default for `null` and `undefined` alike.
51
+ */ function effectiveValueOf(descriptor, currentValue) {
52
+ if (currentValue === null && descriptor.valueType === scene_model.PropertyValueType.Json) return null;
53
+ return currentValue ?? descriptor.default;
54
+ }
47
55
  function inspectProperty(descriptor, record, provenance, scopeInstance, grants, configuredValues) {
48
56
  const currentValue = describeNode.readNodeValue(record, descriptor.path);
49
57
  const configured = configuredValues?.find((candidate)=>candidate.path === descriptor.path);
@@ -52,7 +60,7 @@ function inspectProperty(descriptor, record, provenance, scopeInstance, grants,
52
60
  const inspected = {
53
61
  descriptor,
54
62
  currentValue,
55
- effectiveValue: configured ? configured.value : currentValue ?? descriptor.default,
63
+ effectiveValue: configured ? configured.value : effectiveValueOf(descriptor, currentValue),
56
64
  source: configured?.source ?? (currentValue === undefined ? scene_model.ValueSource.Default : scene_model.ValueSource.Local),
57
65
  editable: false
58
66
  };
@@ -1 +1 @@
1
- {"version":3,"file":"inspect.js","sources":["../../../editing/inspect.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport {\n DiagnosticCode, DiagnosticSeverity, EditTarget, InspectedCollectionItem, InspectedProperty, NodeInspection,\n NodeProvenance, ProvenanceKind, PropertyDescriptor, PropertyValueType, SceneDiagnostic, SceneDocument, SceneNodeKind, ValueSource,\n} from '@cratis/scene.model';\nimport { describeNode, readNodeValue } from './describeNode';\nimport { EffectiveIconCatalog } from '../icons/EffectiveIconCatalog';\nimport { EditingContext } from './EditingContext';\nimport { computeExposureGrants, ExposureGrant, grantKey } from './exposureGrants';\nimport { iconProblemsOfItems, iconProblemsOfProperty } from './iconDiagnostics';\nimport { indexDocument } from './indexDocument';\nimport { NodeRecord } from './DocumentIndex';\nimport { resolveEffectiveConfiguration } from './resolveEffectiveConfiguration';\nimport { resolveTemplateChain } from './resolveTemplateChain';\nimport { TemplateChain } from './TemplateChain';\n\n/**\n * Works out whether a node is the editing scope's own, and when it is not, where it comes from and whether any of\n * it is configurable here.\n *\n * @param record The node.\n * @param chain The chain the scope sits in.\n * @param scopeGrants What the scope's own instance may configure.\n */\nexport function provenanceOf(record: NodeRecord, chain: TemplateChain, scopeGrants: Map<string, ExposureGrant> | undefined): NodeProvenance {\n const scopeLevel = chain.levels.at(-1);\n if (scopeLevel && record.owner === scopeLevel.owner && record.ownerKind === scopeLevel.kind) return { kind: ProvenanceKind.Local };\n\n const configurable = record.kind === SceneNodeKind.Element\n && [...(scopeGrants?.values() ?? [])].some(grant => grant.component === (record.value as { id: string }).id);\n return { kind: configurable ? ProvenanceKind.ConfigurableInherited : ProvenanceKind.Inherited, source: record.owner };\n}\n\nfunction isRemovable(record: NodeRecord, provenance: NodeProvenance): boolean {\n return provenance.kind === ProvenanceKind.Local\n && (record.list !== undefined || record.leafId !== undefined || record.placementIds !== undefined);\n}\n\nfunction toInspectedItems(raw: unknown, local: boolean): InspectedCollectionItem[] {\n return (Array.isArray(raw) ? raw : []).map((item: Record<string, unknown>, position) => {\n const { id, ...values } = item;\n return { id: typeof id === 'string' ? id : `owner-${position}`, values, origin: 'owner', editable: local };\n });\n}\n\nfunction inspectProperty(\n descriptor: PropertyDescriptor,\n record: NodeRecord,\n provenance: NodeProvenance,\n scopeInstance: string | undefined,\n grants: Map<string, ExposureGrant> | undefined,\n configuredValues: { path: string; value: unknown; source: string; items?: { id: string; values: Record<string, unknown>; origin: string }[] }[] | undefined,\n): InspectedProperty {\n const currentValue = readNodeValue(record, descriptor.path);\n const configured = configuredValues?.find(candidate => candidate.path === descriptor.path);\n const local = provenance.kind === ProvenanceKind.Local;\n const isCollection = descriptor.valueType === PropertyValueType.Collection;\n\n const inspected: InspectedProperty = {\n descriptor,\n currentValue,\n effectiveValue: configured ? configured.value : (currentValue ?? descriptor.default),\n source: (configured?.source ?? (currentValue === undefined ? ValueSource.Default : ValueSource.Local)) as ValueSource,\n editable: false,\n };\n\n const grant = !local && record.kind === SceneNodeKind.Element\n ? grants?.get(grantKey((record.value as { id: string }).id, descriptor.path))\n : undefined;\n\n if (local) {\n inspected.editable = descriptor.readOnly !== true;\n if (inspected.editable) inspected.editTarget = EditTarget.Node;\n else inspected.reason = 'This property is read-only.';\n } else if (grant) {\n inspected.editable = true;\n inspected.editTarget = EditTarget.Instance;\n if (isCollection) inspected.operations = grant.operations;\n } else {\n inspected.reason = `Inherited from '${provenance.source}', which does not expose it.`;\n }\n\n if (isCollection) {\n const items = configured?.items\n ? configured.items.map(item => ({ ...item, editable: local || (item.origin === scopeInstance && grant !== undefined && grant.operations.length > 0) }))\n : toInspectedItems(currentValue, local);\n inspected.items = items;\n }\n\n return inspected;\n}\n\n/**\n * Reports icons the profile cannot supply - on the node itself and in configuration contributed to it. The values\n * stay where they are; these are warnings.\n */\nfunction iconDiagnosticsOf(properties: InspectedProperty[], iconCatalog: EffectiveIconCatalog, nodeId: string): SceneDiagnostic[] {\n return properties.flatMap(property => {\n const descriptor = property.descriptor;\n if (descriptor.valueType !== PropertyValueType.Collection) {\n return iconProblemsOfProperty(iconCatalog, descriptor, property.effectiveValue, DiagnosticSeverity.Warning, { nodeId });\n }\n\n const items = (property.items ?? []).map(item => ({ id: item.id, values: item.values, instance: item.origin === 'owner' ? undefined : item.origin }));\n return iconProblemsOfItems(iconCatalog, descriptor.item, items, DiagnosticSeverity.Warning, { nodeId, path: descriptor.path });\n });\n}\n\n/**\n * Describes one node for an editor: what it is, where it comes from, what can be edited on it - with current and\n * effective values and effective editability - and what can be done with it.\n *\n * A property of a node the scope owns is edited on the node. A property of an inherited node is editable only\n * when its owner exposed it, and then the edit goes to an instance contribution, never into the inherited node.\n * Nothing is mutated.\n *\n * @param document The document to read.\n * @param nodeId The node to describe; see the node id functions.\n * @param context The catalog and the editing scope.\n * @returns The inspection, or `undefined` when the document has no such node.\n */\nexport function inspect(document: SceneDocument, nodeId: string, context: EditingContext): NodeInspection | undefined {\n const index = indexDocument(document);\n const record = index.nodes.get(nodeId);\n if (!record) return undefined;\n\n const chain = resolveTemplateChain(document, context.scope);\n const grants = computeExposureGrants(chain, context.catalog);\n const scopeInstance = chain.levels.at(-1)?.instance;\n const scopeGrants = scopeInstance === undefined ? undefined : grants.byInstance.get(scopeInstance);\n const configuration = resolveEffectiveConfiguration(chain, document.instanceContributions, context.catalog);\n\n const provenance = provenanceOf(record, chain, scopeGrants);\n const description = describeNode(record, context.catalog);\n const componentId = record.kind === SceneNodeKind.Element ? (record.value as { id: string }).id : undefined;\n const configuredValues = configuration.components.find(candidate => candidate.component === componentId)?.values;\n\n const diagnostics: SceneDiagnostic[] = [\n ...description.diagnostics,\n ...configuration.diagnostics.filter(diagnostic => diagnostic.component !== undefined && diagnostic.component === componentId),\n ];\n if (chain.levels.length === 0) {\n diagnostics.push(...chain.diagnostics.filter(diagnostic => diagnostic.code === DiagnosticCode.UnknownScope));\n }\n\n const properties = description.properties.map(descriptor => inspectProperty(descriptor, record, provenance, scopeInstance, scopeGrants, configuredValues));\n if (context.iconCatalog) {\n diagnostics.push(...iconDiagnosticsOf(properties, context.iconCatalog, nodeId));\n }\n\n return {\n nodeId,\n kind: record.kind,\n typeId: record.typeId,\n label: record.label,\n parentId: record.parentId,\n provenance,\n properties,\n capabilities: description.capabilities,\n removable: isRemovable(record, provenance),\n diagnostics,\n };\n}\n"],"names":["provenanceOf","record","chain","scopeGrants","scopeLevel","levels","at","owner","ownerKind","kind","ProvenanceKind","Local","configurable","SceneNodeKind","Element","values","some","grant","component","value","id","ConfigurableInherited","Inherited","source","isRemovable","provenance","list","undefined","leafId","placementIds","toInspectedItems","raw","local","Array","isArray","map","item","position","origin","editable","inspectProperty","descriptor","scopeInstance","grants","configuredValues","currentValue","readNodeValue","path","configured","find","candidate","isCollection","valueType","PropertyValueType","Collection","inspected","effectiveValue","default","ValueSource","Default","get","grantKey","readOnly","editTarget","EditTarget","Node","reason","Instance","operations","items","length","iconDiagnosticsOf","properties","iconCatalog","nodeId","flatMap","property","iconProblemsOfProperty","DiagnosticSeverity","Warning","instance","iconProblemsOfItems","inspect","document","context","index","indexDocument","nodes","resolveTemplateChain","scope","computeExposureGrants","catalog","byInstance","configuration","resolveEffectiveConfiguration","instanceContributions","description","describeNode","componentId","components","diagnostics","filter","diagnostic","push","code","DiagnosticCode","UnknownScope","typeId","label","parentId","capabilities","removable"],"mappings":"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"}
1
+ {"version":3,"file":"inspect.js","sources":["../../../editing/inspect.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport {\n DiagnosticCode, DiagnosticSeverity, EditTarget, InspectedCollectionItem, InspectedProperty, NodeInspection,\n NodeProvenance, ProvenanceKind, PropertyDescriptor, PropertyValueType, SceneDiagnostic, SceneDocument, SceneNodeKind, ValueSource,\n} from '@cratis/scene.model';\nimport { describeNode, readNodeValue } from './describeNode';\nimport { EffectiveIconCatalog } from '../icons/EffectiveIconCatalog';\nimport { EditingContext } from './EditingContext';\nimport { computeExposureGrants, ExposureGrant, grantKey } from './exposureGrants';\nimport { iconProblemsOfItems, iconProblemsOfProperty } from './iconDiagnostics';\nimport { indexDocument } from './indexDocument';\nimport { NodeRecord } from './DocumentIndex';\nimport { resolveEffectiveConfiguration } from './resolveEffectiveConfiguration';\nimport { resolveTemplateChain } from './resolveTemplateChain';\nimport { TemplateChain } from './TemplateChain';\n\n/**\n * Works out whether a node is the editing scope's own, and when it is not, where it comes from and whether any of\n * it is configurable here.\n *\n * @param record The node.\n * @param chain The chain the scope sits in.\n * @param scopeGrants What the scope's own instance may configure.\n */\nexport function provenanceOf(record: NodeRecord, chain: TemplateChain, scopeGrants: Map<string, ExposureGrant> | undefined): NodeProvenance {\n const scopeLevel = chain.levels.at(-1);\n if (scopeLevel && record.owner === scopeLevel.owner && record.ownerKind === scopeLevel.kind) return { kind: ProvenanceKind.Local };\n\n const configurable = record.kind === SceneNodeKind.Element\n && [...(scopeGrants?.values() ?? [])].some(grant => grant.component === (record.value as { id: string }).id);\n return { kind: configurable ? ProvenanceKind.ConfigurableInherited : ProvenanceKind.Inherited, source: record.owner };\n}\n\nfunction isRemovable(record: NodeRecord, provenance: NodeProvenance): boolean {\n return provenance.kind === ProvenanceKind.Local\n && (record.list !== undefined || record.leafId !== undefined || record.placementIds !== undefined);\n}\n\nfunction toInspectedItems(raw: unknown, local: boolean): InspectedCollectionItem[] {\n return (Array.isArray(raw) ? raw : []).map((item: Record<string, unknown>, position) => {\n const { id, ...values } = item;\n return { id: typeof id === 'string' ? id : `owner-${position}`, values, origin: 'owner', editable: local };\n });\n}\n\n/**\n * The value a property takes when nothing configures it. A stored `null` is a real value for a `json` property\n * (it is valid JSON, and clearing it to the default would hide it), so only that type keeps it; every other\n * type falls back to its default for `null` and `undefined` alike.\n */\nfunction effectiveValueOf(descriptor: PropertyDescriptor, currentValue: unknown): unknown {\n if (currentValue === null && descriptor.valueType === PropertyValueType.Json) return null;\n return currentValue ?? descriptor.default;\n}\n\nfunction inspectProperty(\n descriptor: PropertyDescriptor,\n record: NodeRecord,\n provenance: NodeProvenance,\n scopeInstance: string | undefined,\n grants: Map<string, ExposureGrant> | undefined,\n configuredValues: { path: string; value: unknown; source: string; items?: { id: string; values: Record<string, unknown>; origin: string }[] }[] | undefined,\n): InspectedProperty {\n const currentValue = readNodeValue(record, descriptor.path);\n const configured = configuredValues?.find(candidate => candidate.path === descriptor.path);\n const local = provenance.kind === ProvenanceKind.Local;\n const isCollection = descriptor.valueType === PropertyValueType.Collection;\n\n const inspected: InspectedProperty = {\n descriptor,\n currentValue,\n effectiveValue: configured ? configured.value : effectiveValueOf(descriptor, currentValue),\n source: (configured?.source ?? (currentValue === undefined ? ValueSource.Default : ValueSource.Local)) as ValueSource,\n editable: false,\n };\n\n const grant = !local && record.kind === SceneNodeKind.Element\n ? grants?.get(grantKey((record.value as { id: string }).id, descriptor.path))\n : undefined;\n\n if (local) {\n inspected.editable = descriptor.readOnly !== true;\n if (inspected.editable) inspected.editTarget = EditTarget.Node;\n else inspected.reason = 'This property is read-only.';\n } else if (grant) {\n inspected.editable = true;\n inspected.editTarget = EditTarget.Instance;\n if (isCollection) inspected.operations = grant.operations;\n } else {\n inspected.reason = `Inherited from '${provenance.source}', which does not expose it.`;\n }\n\n if (isCollection) {\n const items = configured?.items\n ? configured.items.map(item => ({ ...item, editable: local || (item.origin === scopeInstance && grant !== undefined && grant.operations.length > 0) }))\n : toInspectedItems(currentValue, local);\n inspected.items = items;\n }\n\n return inspected;\n}\n\n/**\n * Reports icons the profile cannot supply - on the node itself and in configuration contributed to it. The values\n * stay where they are; these are warnings.\n */\nfunction iconDiagnosticsOf(properties: InspectedProperty[], iconCatalog: EffectiveIconCatalog, nodeId: string): SceneDiagnostic[] {\n return properties.flatMap(property => {\n const descriptor = property.descriptor;\n if (descriptor.valueType !== PropertyValueType.Collection) {\n return iconProblemsOfProperty(iconCatalog, descriptor, property.effectiveValue, DiagnosticSeverity.Warning, { nodeId });\n }\n\n const items = (property.items ?? []).map(item => ({ id: item.id, values: item.values, instance: item.origin === 'owner' ? undefined : item.origin }));\n return iconProblemsOfItems(iconCatalog, descriptor.item, items, DiagnosticSeverity.Warning, { nodeId, path: descriptor.path });\n });\n}\n\n/**\n * Describes one node for an editor: what it is, where it comes from, what can be edited on it - with current and\n * effective values and effective editability - and what can be done with it.\n *\n * A property of a node the scope owns is edited on the node. A property of an inherited node is editable only\n * when its owner exposed it, and then the edit goes to an instance contribution, never into the inherited node.\n * Nothing is mutated.\n *\n * @param document The document to read.\n * @param nodeId The node to describe; see the node id functions.\n * @param context The catalog and the editing scope.\n * @returns The inspection, or `undefined` when the document has no such node.\n */\nexport function inspect(document: SceneDocument, nodeId: string, context: EditingContext): NodeInspection | undefined {\n const index = indexDocument(document);\n const record = index.nodes.get(nodeId);\n if (!record) return undefined;\n\n const chain = resolveTemplateChain(document, context.scope);\n const grants = computeExposureGrants(chain, context.catalog);\n const scopeInstance = chain.levels.at(-1)?.instance;\n const scopeGrants = scopeInstance === undefined ? undefined : grants.byInstance.get(scopeInstance);\n const configuration = resolveEffectiveConfiguration(chain, document.instanceContributions, context.catalog);\n\n const provenance = provenanceOf(record, chain, scopeGrants);\n const description = describeNode(record, context.catalog);\n const componentId = record.kind === SceneNodeKind.Element ? (record.value as { id: string }).id : undefined;\n const configuredValues = configuration.components.find(candidate => candidate.component === componentId)?.values;\n\n const diagnostics: SceneDiagnostic[] = [\n ...description.diagnostics,\n ...configuration.diagnostics.filter(diagnostic => diagnostic.component !== undefined && diagnostic.component === componentId),\n ];\n if (chain.levels.length === 0) {\n diagnostics.push(...chain.diagnostics.filter(diagnostic => diagnostic.code === DiagnosticCode.UnknownScope));\n }\n\n const properties = description.properties.map(descriptor => inspectProperty(descriptor, record, provenance, scopeInstance, scopeGrants, configuredValues));\n if (context.iconCatalog) {\n diagnostics.push(...iconDiagnosticsOf(properties, context.iconCatalog, nodeId));\n }\n\n return {\n nodeId,\n kind: record.kind,\n typeId: record.typeId,\n label: record.label,\n parentId: record.parentId,\n provenance,\n properties,\n capabilities: description.capabilities,\n removable: isRemovable(record, provenance),\n diagnostics,\n };\n}\n"],"names":["provenanceOf","record","chain","scopeGrants","scopeLevel","levels","at","owner","ownerKind","kind","ProvenanceKind","Local","configurable","SceneNodeKind","Element","values","some","grant","component","value","id","ConfigurableInherited","Inherited","source","isRemovable","provenance","list","undefined","leafId","placementIds","toInspectedItems","raw","local","Array","isArray","map","item","position","origin","editable","effectiveValueOf","descriptor","currentValue","valueType","PropertyValueType","Json","default","inspectProperty","scopeInstance","grants","configuredValues","readNodeValue","path","configured","find","candidate","isCollection","Collection","inspected","effectiveValue","ValueSource","Default","get","grantKey","readOnly","editTarget","EditTarget","Node","reason","Instance","operations","items","length","iconDiagnosticsOf","properties","iconCatalog","nodeId","flatMap","property","iconProblemsOfProperty","DiagnosticSeverity","Warning","instance","iconProblemsOfItems","inspect","document","context","index","indexDocument","nodes","resolveTemplateChain","scope","computeExposureGrants","catalog","byInstance","configuration","resolveEffectiveConfiguration","instanceContributions","description","describeNode","componentId","components","diagnostics","filter","diagnostic","push","code","DiagnosticCode","UnknownScope","typeId","label","parentId","capabilities","removable"],"mappings":"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"}
@@ -54,10 +54,34 @@ function isNonEmptyString(value) {
54
54
  return Array.isArray(value.arguments) && Array.isArray(value.results) ? undefined : 'a query binding needs arguments and results lists';
55
55
  case scene_model.PropertyValueType.Collection:
56
56
  return validateCollection(descriptor, value);
57
+ case scene_model.PropertyValueType.Json:
58
+ return isJsonValue(value) ? undefined : 'expected a JSON value';
57
59
  default:
58
60
  return isRecord(value) || Array.isArray(value) ? undefined : 'expected a structured value';
59
61
  }
60
62
  }
63
+ /**
64
+ * The deepest nesting a JSON value may have. Authored configuration (chart datasets, option lists) is shallow;
65
+ * the limit turns a runaway or hostile structure into a validation message instead of a stack overflow.
66
+ */ const maximumJsonDepth = 64;
67
+ /**
68
+ * Whether a value is JSON: `null`, a string, a boolean, a finite number, or an array or plain object of those.
69
+ * The value is only inspected, never serialized, so what an editor stores is exactly what was passed in.
70
+ */ function isJsonValue(value, parents = new Set()) {
71
+ if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;
72
+ if (typeof value === 'number') return Number.isFinite(value);
73
+ if (typeof value !== 'object' || parents.has(value) || parents.size >= maximumJsonDepth) return false;
74
+ const isArray = Array.isArray(value);
75
+ const prototype = Object.getPrototypeOf(value);
76
+ if (isArray ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) return false;
77
+ parents.add(value);
78
+ const values = isArray ? Array.from({
79
+ length: value.length
80
+ }, (_, index)=>index in value ? value[index] : undefined) : Object.values(value);
81
+ const valid = values.every((candidate)=>isJsonValue(candidate, parents));
82
+ parents.delete(value);
83
+ return valid;
84
+ }
61
85
  function validateCollection(descriptor, value) {
62
86
  if (!Array.isArray(value)) return 'expected a list';
63
87
  const constraints = descriptor.constraints ?? {};
@@ -1 +1 @@
1
- {"version":3,"file":"validateValue.js","sources":["../../../editing/validateValue.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { CollectionItemDescriptor, PropertyDescriptor, PropertyValueType } from '@cratis/scene.model';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Checks a value against a property descriptor.\n *\n * @param descriptor The property the value is for.\n * @param value The value to check. `undefined` means \"not set\", which is fine unless the property is required.\n * @returns The reason the value is not acceptable, or `undefined` when it is.\n */\nexport function validateValue(descriptor: PropertyDescriptor, value: unknown): string | undefined {\n const constraints = descriptor.constraints ?? {};\n if (value === undefined || value === null) {\n return constraints.required ? 'a value is required' : undefined;\n }\n\n switch (descriptor.valueType) {\n case PropertyValueType.String: {\n if (typeof value !== 'string') return 'expected a string';\n if (constraints.minimumLength !== undefined && value.length < constraints.minimumLength) return `must be at least ${constraints.minimumLength} characters`;\n if (constraints.maximumLength !== undefined && value.length > constraints.maximumLength) return `must be at most ${constraints.maximumLength} characters`;\n if (constraints.pattern !== undefined && !new RegExp(`^(?:${constraints.pattern})$`).test(value)) return `must match ${constraints.pattern}`;\n return undefined;\n }\n\n case PropertyValueType.Number: {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 'expected a number';\n if (constraints.integer && !Number.isInteger(value)) return 'expected a whole number';\n if (constraints.minimum !== undefined && value < constraints.minimum) return `must be at least ${constraints.minimum}`;\n if (constraints.maximum !== undefined && value > constraints.maximum) return `must be at most ${constraints.maximum}`;\n return undefined;\n }\n\n case PropertyValueType.Boolean:\n return typeof value === 'boolean' ? undefined : 'expected true or false';\n\n case PropertyValueType.Enum:\n return (descriptor.choices ?? []).some(choice => choice.value === value) ? undefined : 'not one of the allowed choices';\n\n case PropertyValueType.Icon:\n if (!isRecord(value) || !isNonEmptyString(value.library) || !isNonEmptyString(value.key)) return 'expected an icon reference with a library and a key';\n return value.variant === undefined || typeof value.variant === 'string' ? undefined : 'the icon variant must be a string';\n\n case PropertyValueType.Destination:\n if (!isRecord(value) || !isNonEmptyString(value.screen)) return 'expected a destination naming a screen';\n return value.routeParameterBindings === undefined || isRecord(value.routeParameterBindings)\n ? undefined\n : 'routeParameterBindings must be an object';\n\n case PropertyValueType.QueryReference:\n if (isNonEmptyString(value)) return undefined;\n if (!isRecord(value) || !isNonEmptyString(value.queryId) || !isNonEmptyString(value.query)) return 'expected a query name or a query binding';\n return Array.isArray(value.arguments) && Array.isArray(value.results) ? undefined : 'a query binding needs arguments and results lists';\n\n case PropertyValueType.Collection:\n return validateCollection(descriptor, value);\n\n default:\n return isRecord(value) || Array.isArray(value) ? undefined : 'expected a structured value';\n }\n}\n\nfunction validateCollection(descriptor: PropertyDescriptor, value: unknown): string | undefined {\n if (!Array.isArray(value)) return 'expected a list';\n const constraints = descriptor.constraints ?? {};\n if (constraints.minimumItems !== undefined && value.length < constraints.minimumItems) return `needs at least ${constraints.minimumItems} items`;\n if (constraints.maximumItems !== undefined && value.length > constraints.maximumItems) return `allows at most ${constraints.maximumItems} items`;\n\n const identities = new Set<string>();\n for (const item of value) {\n if (!isRecord(item) || !isNonEmptyString(item.id)) return 'every item needs an id';\n if (identities.has(item.id)) return `the item id '${item.id}' is used twice`;\n identities.add(item.id);\n\n const { id: _identity, ...values } = item;\n const problem = validateItemValues(descriptor.item, values).at(0);\n if (problem) return `item '${item.id}': ${problem.field} ${problem.problem}`;\n }\n\n return undefined;\n}\n\n/**\n * One field of a collection item that does not fit its descriptor.\n */\nexport interface ItemFieldProblem {\n field: string;\n problem: string;\n}\n\n/**\n * Checks a collection item's field values against the collection's item descriptor.\n *\n * @param itemDescriptor What the items look like.\n * @param values The item's field values, keyed by field path.\n * @returns Every field that is unknown or has an unacceptable value.\n */\nexport function validateItemValues(itemDescriptor: CollectionItemDescriptor | undefined, values: Record<string, unknown>): ItemFieldProblem[] {\n const fields = itemDescriptor?.properties ?? [];\n const problems: ItemFieldProblem[] = [];\n\n for (const [field, fieldValue] of Object.entries(values)) {\n const fieldDescriptor = fields.find(candidate => candidate.path === field);\n if (!fieldDescriptor) {\n problems.push({ field, problem: 'is not a field of this collection\\'s items' });\n continue;\n }\n\n const problem = validateValue(fieldDescriptor, fieldValue);\n if (problem) problems.push({ field, problem });\n }\n\n for (const fieldDescriptor of fields) {\n if (fieldDescriptor.constraints?.required && values[fieldDescriptor.path] === undefined) {\n problems.push({ field: fieldDescriptor.path, problem: 'a value is required' });\n }\n }\n\n return problems;\n}\n"],"names":["isRecord","value","Array","isArray","isNonEmptyString","length","validateValue","descriptor","constraints","undefined","required","valueType","PropertyValueType","String","minimumLength","maximumLength","pattern","RegExp","test","Number","isFinite","integer","isInteger","minimum","maximum","Boolean","Enum","choices","some","choice","Icon","library","key","variant","Destination","screen","routeParameterBindings","QueryReference","queryId","query","arguments","results","Collection","validateCollection","minimumItems","maximumItems","identities","Set","item","id","has","add","_identity","values","problem","validateItemValues","at","field","itemDescriptor","fields","properties","problems","fieldValue","Object","entries","fieldDescriptor","find","candidate","path","push"],"mappings":"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"}
1
+ {"version":3,"file":"validateValue.js","sources":["../../../editing/validateValue.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { CollectionItemDescriptor, PropertyDescriptor, PropertyValueType } from '@cratis/scene.model';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Checks a value against a property descriptor.\n *\n * @param descriptor The property the value is for.\n * @param value The value to check. `undefined` means \"not set\", which is fine unless the property is required.\n * @returns The reason the value is not acceptable, or `undefined` when it is.\n */\nexport function validateValue(descriptor: PropertyDescriptor, value: unknown): string | undefined {\n const constraints = descriptor.constraints ?? {};\n if (value === undefined || value === null) {\n return constraints.required ? 'a value is required' : undefined;\n }\n\n switch (descriptor.valueType) {\n case PropertyValueType.String: {\n if (typeof value !== 'string') return 'expected a string';\n if (constraints.minimumLength !== undefined && value.length < constraints.minimumLength) return `must be at least ${constraints.minimumLength} characters`;\n if (constraints.maximumLength !== undefined && value.length > constraints.maximumLength) return `must be at most ${constraints.maximumLength} characters`;\n if (constraints.pattern !== undefined && !new RegExp(`^(?:${constraints.pattern})$`).test(value)) return `must match ${constraints.pattern}`;\n return undefined;\n }\n\n case PropertyValueType.Number: {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 'expected a number';\n if (constraints.integer && !Number.isInteger(value)) return 'expected a whole number';\n if (constraints.minimum !== undefined && value < constraints.minimum) return `must be at least ${constraints.minimum}`;\n if (constraints.maximum !== undefined && value > constraints.maximum) return `must be at most ${constraints.maximum}`;\n return undefined;\n }\n\n case PropertyValueType.Boolean:\n return typeof value === 'boolean' ? undefined : 'expected true or false';\n\n case PropertyValueType.Enum:\n return (descriptor.choices ?? []).some(choice => choice.value === value) ? undefined : 'not one of the allowed choices';\n\n case PropertyValueType.Icon:\n if (!isRecord(value) || !isNonEmptyString(value.library) || !isNonEmptyString(value.key)) return 'expected an icon reference with a library and a key';\n return value.variant === undefined || typeof value.variant === 'string' ? undefined : 'the icon variant must be a string';\n\n case PropertyValueType.Destination:\n if (!isRecord(value) || !isNonEmptyString(value.screen)) return 'expected a destination naming a screen';\n return value.routeParameterBindings === undefined || isRecord(value.routeParameterBindings)\n ? undefined\n : 'routeParameterBindings must be an object';\n\n case PropertyValueType.QueryReference:\n if (isNonEmptyString(value)) return undefined;\n if (!isRecord(value) || !isNonEmptyString(value.queryId) || !isNonEmptyString(value.query)) return 'expected a query name or a query binding';\n return Array.isArray(value.arguments) && Array.isArray(value.results) ? undefined : 'a query binding needs arguments and results lists';\n\n case PropertyValueType.Collection:\n return validateCollection(descriptor, value);\n\n case PropertyValueType.Json:\n return isJsonValue(value) ? undefined : 'expected a JSON value';\n\n default:\n return isRecord(value) || Array.isArray(value) ? undefined : 'expected a structured value';\n }\n}\n\n/**\n * The deepest nesting a JSON value may have. Authored configuration (chart datasets, option lists) is shallow;\n * the limit turns a runaway or hostile structure into a validation message instead of a stack overflow.\n */\nconst maximumJsonDepth = 64;\n\n/**\n * Whether a value is JSON: `null`, a string, a boolean, a finite number, or an array or plain object of those.\n * The value is only inspected, never serialized, so what an editor stores is exactly what was passed in.\n */\nfunction isJsonValue(value: unknown, parents = new Set<object>()): boolean {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;\n if (typeof value === 'number') return Number.isFinite(value);\n if (typeof value !== 'object' || parents.has(value) || parents.size >= maximumJsonDepth) return false;\n\n const isArray = Array.isArray(value);\n const prototype = Object.getPrototypeOf(value);\n if (isArray ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) return false;\n\n parents.add(value);\n const values = isArray ? Array.from({ length: value.length }, (_, index) => (index in value ? value[index] : undefined)) : Object.values(value);\n const valid = values.every(candidate => isJsonValue(candidate, parents));\n parents.delete(value);\n return valid;\n}\n\nfunction validateCollection(descriptor: PropertyDescriptor, value: unknown): string | undefined {\n if (!Array.isArray(value)) return 'expected a list';\n const constraints = descriptor.constraints ?? {};\n if (constraints.minimumItems !== undefined && value.length < constraints.minimumItems) return `needs at least ${constraints.minimumItems} items`;\n if (constraints.maximumItems !== undefined && value.length > constraints.maximumItems) return `allows at most ${constraints.maximumItems} items`;\n\n const identities = new Set<string>();\n for (const item of value) {\n if (!isRecord(item) || !isNonEmptyString(item.id)) return 'every item needs an id';\n if (identities.has(item.id)) return `the item id '${item.id}' is used twice`;\n identities.add(item.id);\n\n const { id: _identity, ...values } = item;\n const problem = validateItemValues(descriptor.item, values).at(0);\n if (problem) return `item '${item.id}': ${problem.field} ${problem.problem}`;\n }\n\n return undefined;\n}\n\n/**\n * One field of a collection item that does not fit its descriptor.\n */\nexport interface ItemFieldProblem {\n field: string;\n problem: string;\n}\n\n/**\n * Checks a collection item's field values against the collection's item descriptor.\n *\n * @param itemDescriptor What the items look like.\n * @param values The item's field values, keyed by field path.\n * @returns Every field that is unknown or has an unacceptable value.\n */\nexport function validateItemValues(itemDescriptor: CollectionItemDescriptor | undefined, values: Record<string, unknown>): ItemFieldProblem[] {\n const fields = itemDescriptor?.properties ?? [];\n const problems: ItemFieldProblem[] = [];\n\n for (const [field, fieldValue] of Object.entries(values)) {\n const fieldDescriptor = fields.find(candidate => candidate.path === field);\n if (!fieldDescriptor) {\n problems.push({ field, problem: 'is not a field of this collection\\'s items' });\n continue;\n }\n\n const problem = validateValue(fieldDescriptor, fieldValue);\n if (problem) problems.push({ field, problem });\n }\n\n for (const fieldDescriptor of fields) {\n if (fieldDescriptor.constraints?.required && values[fieldDescriptor.path] === undefined) {\n problems.push({ field: fieldDescriptor.path, problem: 'a value is required' });\n }\n }\n\n return problems;\n}\n"],"names":["isRecord","value","Array","isArray","isNonEmptyString","length","validateValue","descriptor","constraints","undefined","required","valueType","PropertyValueType","String","minimumLength","maximumLength","pattern","RegExp","test","Number","isFinite","integer","isInteger","minimum","maximum","Boolean","Enum","choices","some","choice","Icon","library","key","variant","Destination","screen","routeParameterBindings","QueryReference","queryId","query","arguments","results","Collection","validateCollection","Json","isJsonValue","maximumJsonDepth","parents","Set","has","size","prototype","Object","getPrototypeOf","add","values","from","_","index","valid","every","candidate","delete","minimumItems","maximumItems","identities","item","id","_identity","problem","validateItemValues","at","field","itemDescriptor","fields","properties","problems","fieldValue","entries","fieldDescriptor","find","path","push"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../../editing/inspect.ts"],"names":[],"mappings":"AAGA,OAAO,EACyF,cAAc,EAC1G,cAAc,EAA0E,aAAa,EACxG,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAyB,aAAa,EAAY,MAAM,kBAAkB,CAAC;AAGlF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAUhD,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,GAAG,cAAc,CAO1I;AA0FD,wBAAgB,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,cAAc,GAAG,SAAS,CAyCpH"}
1
+ {"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../../editing/inspect.ts"],"names":[],"mappings":"AAGA,OAAO,EACyF,cAAc,EAC1G,cAAc,EAA0E,aAAa,EACxG,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAyB,aAAa,EAAY,MAAM,kBAAkB,CAAC;AAGlF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAUhD,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,GAAG,cAAc,CAO1I;AAoGD,wBAAgB,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,cAAc,GAAG,SAAS,CAyCpH"}
@@ -42,6 +42,14 @@ function toInspectedItems(raw, local) {
42
42
  };
43
43
  });
44
44
  }
45
+ /**
46
+ * The value a property takes when nothing configures it. A stored `null` is a real value for a `json` property
47
+ * (it is valid JSON, and clearing it to the default would hide it), so only that type keeps it; every other
48
+ * type falls back to its default for `null` and `undefined` alike.
49
+ */ function effectiveValueOf(descriptor, currentValue) {
50
+ if (currentValue === null && descriptor.valueType === PropertyValueType.Json) return null;
51
+ return currentValue ?? descriptor.default;
52
+ }
45
53
  function inspectProperty(descriptor, record, provenance, scopeInstance, grants, configuredValues) {
46
54
  const currentValue = readNodeValue(record, descriptor.path);
47
55
  const configured = configuredValues?.find((candidate)=>candidate.path === descriptor.path);
@@ -50,7 +58,7 @@ function inspectProperty(descriptor, record, provenance, scopeInstance, grants,
50
58
  const inspected = {
51
59
  descriptor,
52
60
  currentValue,
53
- effectiveValue: configured ? configured.value : currentValue ?? descriptor.default,
61
+ effectiveValue: configured ? configured.value : effectiveValueOf(descriptor, currentValue),
54
62
  source: configured?.source ?? (currentValue === undefined ? ValueSource.Default : ValueSource.Local),
55
63
  editable: false
56
64
  };
@@ -1 +1 @@
1
- {"version":3,"file":"inspect.js","sources":["../../../editing/inspect.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport {\n DiagnosticCode, DiagnosticSeverity, EditTarget, InspectedCollectionItem, InspectedProperty, NodeInspection,\n NodeProvenance, ProvenanceKind, PropertyDescriptor, PropertyValueType, SceneDiagnostic, SceneDocument, SceneNodeKind, ValueSource,\n} from '@cratis/scene.model';\nimport { describeNode, readNodeValue } from './describeNode';\nimport { EffectiveIconCatalog } from '../icons/EffectiveIconCatalog';\nimport { EditingContext } from './EditingContext';\nimport { computeExposureGrants, ExposureGrant, grantKey } from './exposureGrants';\nimport { iconProblemsOfItems, iconProblemsOfProperty } from './iconDiagnostics';\nimport { indexDocument } from './indexDocument';\nimport { NodeRecord } from './DocumentIndex';\nimport { resolveEffectiveConfiguration } from './resolveEffectiveConfiguration';\nimport { resolveTemplateChain } from './resolveTemplateChain';\nimport { TemplateChain } from './TemplateChain';\n\n/**\n * Works out whether a node is the editing scope's own, and when it is not, where it comes from and whether any of\n * it is configurable here.\n *\n * @param record The node.\n * @param chain The chain the scope sits in.\n * @param scopeGrants What the scope's own instance may configure.\n */\nexport function provenanceOf(record: NodeRecord, chain: TemplateChain, scopeGrants: Map<string, ExposureGrant> | undefined): NodeProvenance {\n const scopeLevel = chain.levels.at(-1);\n if (scopeLevel && record.owner === scopeLevel.owner && record.ownerKind === scopeLevel.kind) return { kind: ProvenanceKind.Local };\n\n const configurable = record.kind === SceneNodeKind.Element\n && [...(scopeGrants?.values() ?? [])].some(grant => grant.component === (record.value as { id: string }).id);\n return { kind: configurable ? ProvenanceKind.ConfigurableInherited : ProvenanceKind.Inherited, source: record.owner };\n}\n\nfunction isRemovable(record: NodeRecord, provenance: NodeProvenance): boolean {\n return provenance.kind === ProvenanceKind.Local\n && (record.list !== undefined || record.leafId !== undefined || record.placementIds !== undefined);\n}\n\nfunction toInspectedItems(raw: unknown, local: boolean): InspectedCollectionItem[] {\n return (Array.isArray(raw) ? raw : []).map((item: Record<string, unknown>, position) => {\n const { id, ...values } = item;\n return { id: typeof id === 'string' ? id : `owner-${position}`, values, origin: 'owner', editable: local };\n });\n}\n\nfunction inspectProperty(\n descriptor: PropertyDescriptor,\n record: NodeRecord,\n provenance: NodeProvenance,\n scopeInstance: string | undefined,\n grants: Map<string, ExposureGrant> | undefined,\n configuredValues: { path: string; value: unknown; source: string; items?: { id: string; values: Record<string, unknown>; origin: string }[] }[] | undefined,\n): InspectedProperty {\n const currentValue = readNodeValue(record, descriptor.path);\n const configured = configuredValues?.find(candidate => candidate.path === descriptor.path);\n const local = provenance.kind === ProvenanceKind.Local;\n const isCollection = descriptor.valueType === PropertyValueType.Collection;\n\n const inspected: InspectedProperty = {\n descriptor,\n currentValue,\n effectiveValue: configured ? configured.value : (currentValue ?? descriptor.default),\n source: (configured?.source ?? (currentValue === undefined ? ValueSource.Default : ValueSource.Local)) as ValueSource,\n editable: false,\n };\n\n const grant = !local && record.kind === SceneNodeKind.Element\n ? grants?.get(grantKey((record.value as { id: string }).id, descriptor.path))\n : undefined;\n\n if (local) {\n inspected.editable = descriptor.readOnly !== true;\n if (inspected.editable) inspected.editTarget = EditTarget.Node;\n else inspected.reason = 'This property is read-only.';\n } else if (grant) {\n inspected.editable = true;\n inspected.editTarget = EditTarget.Instance;\n if (isCollection) inspected.operations = grant.operations;\n } else {\n inspected.reason = `Inherited from '${provenance.source}', which does not expose it.`;\n }\n\n if (isCollection) {\n const items = configured?.items\n ? configured.items.map(item => ({ ...item, editable: local || (item.origin === scopeInstance && grant !== undefined && grant.operations.length > 0) }))\n : toInspectedItems(currentValue, local);\n inspected.items = items;\n }\n\n return inspected;\n}\n\n/**\n * Reports icons the profile cannot supply - on the node itself and in configuration contributed to it. The values\n * stay where they are; these are warnings.\n */\nfunction iconDiagnosticsOf(properties: InspectedProperty[], iconCatalog: EffectiveIconCatalog, nodeId: string): SceneDiagnostic[] {\n return properties.flatMap(property => {\n const descriptor = property.descriptor;\n if (descriptor.valueType !== PropertyValueType.Collection) {\n return iconProblemsOfProperty(iconCatalog, descriptor, property.effectiveValue, DiagnosticSeverity.Warning, { nodeId });\n }\n\n const items = (property.items ?? []).map(item => ({ id: item.id, values: item.values, instance: item.origin === 'owner' ? undefined : item.origin }));\n return iconProblemsOfItems(iconCatalog, descriptor.item, items, DiagnosticSeverity.Warning, { nodeId, path: descriptor.path });\n });\n}\n\n/**\n * Describes one node for an editor: what it is, where it comes from, what can be edited on it - with current and\n * effective values and effective editability - and what can be done with it.\n *\n * A property of a node the scope owns is edited on the node. A property of an inherited node is editable only\n * when its owner exposed it, and then the edit goes to an instance contribution, never into the inherited node.\n * Nothing is mutated.\n *\n * @param document The document to read.\n * @param nodeId The node to describe; see the node id functions.\n * @param context The catalog and the editing scope.\n * @returns The inspection, or `undefined` when the document has no such node.\n */\nexport function inspect(document: SceneDocument, nodeId: string, context: EditingContext): NodeInspection | undefined {\n const index = indexDocument(document);\n const record = index.nodes.get(nodeId);\n if (!record) return undefined;\n\n const chain = resolveTemplateChain(document, context.scope);\n const grants = computeExposureGrants(chain, context.catalog);\n const scopeInstance = chain.levels.at(-1)?.instance;\n const scopeGrants = scopeInstance === undefined ? undefined : grants.byInstance.get(scopeInstance);\n const configuration = resolveEffectiveConfiguration(chain, document.instanceContributions, context.catalog);\n\n const provenance = provenanceOf(record, chain, scopeGrants);\n const description = describeNode(record, context.catalog);\n const componentId = record.kind === SceneNodeKind.Element ? (record.value as { id: string }).id : undefined;\n const configuredValues = configuration.components.find(candidate => candidate.component === componentId)?.values;\n\n const diagnostics: SceneDiagnostic[] = [\n ...description.diagnostics,\n ...configuration.diagnostics.filter(diagnostic => diagnostic.component !== undefined && diagnostic.component === componentId),\n ];\n if (chain.levels.length === 0) {\n diagnostics.push(...chain.diagnostics.filter(diagnostic => diagnostic.code === DiagnosticCode.UnknownScope));\n }\n\n const properties = description.properties.map(descriptor => inspectProperty(descriptor, record, provenance, scopeInstance, scopeGrants, configuredValues));\n if (context.iconCatalog) {\n diagnostics.push(...iconDiagnosticsOf(properties, context.iconCatalog, nodeId));\n }\n\n return {\n nodeId,\n kind: record.kind,\n typeId: record.typeId,\n label: record.label,\n parentId: record.parentId,\n provenance,\n properties,\n capabilities: description.capabilities,\n removable: isRemovable(record, provenance),\n diagnostics,\n };\n}\n"],"names":["provenanceOf","record","chain","scopeGrants","scopeLevel","levels","at","owner","ownerKind","kind","ProvenanceKind","Local","configurable","SceneNodeKind","Element","values","some","grant","component","value","id","ConfigurableInherited","Inherited","source","isRemovable","provenance","list","undefined","leafId","placementIds","toInspectedItems","raw","local","Array","isArray","map","item","position","origin","editable","inspectProperty","descriptor","scopeInstance","grants","configuredValues","currentValue","readNodeValue","path","configured","find","candidate","isCollection","valueType","PropertyValueType","Collection","inspected","effectiveValue","default","ValueSource","Default","get","grantKey","readOnly","editTarget","EditTarget","Node","reason","Instance","operations","items","length","iconDiagnosticsOf","properties","iconCatalog","nodeId","flatMap","property","iconProblemsOfProperty","DiagnosticSeverity","Warning","instance","iconProblemsOfItems","inspect","document","context","index","indexDocument","nodes","resolveTemplateChain","scope","computeExposureGrants","catalog","byInstance","configuration","resolveEffectiveConfiguration","instanceContributions","description","describeNode","componentId","components","diagnostics","filter","diagnostic","push","code","DiagnosticCode","UnknownScope","typeId","label","parentId","capabilities","removable"],"mappings":"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"}
1
+ {"version":3,"file":"inspect.js","sources":["../../../editing/inspect.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport {\n DiagnosticCode, DiagnosticSeverity, EditTarget, InspectedCollectionItem, InspectedProperty, NodeInspection,\n NodeProvenance, ProvenanceKind, PropertyDescriptor, PropertyValueType, SceneDiagnostic, SceneDocument, SceneNodeKind, ValueSource,\n} from '@cratis/scene.model';\nimport { describeNode, readNodeValue } from './describeNode';\nimport { EffectiveIconCatalog } from '../icons/EffectiveIconCatalog';\nimport { EditingContext } from './EditingContext';\nimport { computeExposureGrants, ExposureGrant, grantKey } from './exposureGrants';\nimport { iconProblemsOfItems, iconProblemsOfProperty } from './iconDiagnostics';\nimport { indexDocument } from './indexDocument';\nimport { NodeRecord } from './DocumentIndex';\nimport { resolveEffectiveConfiguration } from './resolveEffectiveConfiguration';\nimport { resolveTemplateChain } from './resolveTemplateChain';\nimport { TemplateChain } from './TemplateChain';\n\n/**\n * Works out whether a node is the editing scope's own, and when it is not, where it comes from and whether any of\n * it is configurable here.\n *\n * @param record The node.\n * @param chain The chain the scope sits in.\n * @param scopeGrants What the scope's own instance may configure.\n */\nexport function provenanceOf(record: NodeRecord, chain: TemplateChain, scopeGrants: Map<string, ExposureGrant> | undefined): NodeProvenance {\n const scopeLevel = chain.levels.at(-1);\n if (scopeLevel && record.owner === scopeLevel.owner && record.ownerKind === scopeLevel.kind) return { kind: ProvenanceKind.Local };\n\n const configurable = record.kind === SceneNodeKind.Element\n && [...(scopeGrants?.values() ?? [])].some(grant => grant.component === (record.value as { id: string }).id);\n return { kind: configurable ? ProvenanceKind.ConfigurableInherited : ProvenanceKind.Inherited, source: record.owner };\n}\n\nfunction isRemovable(record: NodeRecord, provenance: NodeProvenance): boolean {\n return provenance.kind === ProvenanceKind.Local\n && (record.list !== undefined || record.leafId !== undefined || record.placementIds !== undefined);\n}\n\nfunction toInspectedItems(raw: unknown, local: boolean): InspectedCollectionItem[] {\n return (Array.isArray(raw) ? raw : []).map((item: Record<string, unknown>, position) => {\n const { id, ...values } = item;\n return { id: typeof id === 'string' ? id : `owner-${position}`, values, origin: 'owner', editable: local };\n });\n}\n\n/**\n * The value a property takes when nothing configures it. A stored `null` is a real value for a `json` property\n * (it is valid JSON, and clearing it to the default would hide it), so only that type keeps it; every other\n * type falls back to its default for `null` and `undefined` alike.\n */\nfunction effectiveValueOf(descriptor: PropertyDescriptor, currentValue: unknown): unknown {\n if (currentValue === null && descriptor.valueType === PropertyValueType.Json) return null;\n return currentValue ?? descriptor.default;\n}\n\nfunction inspectProperty(\n descriptor: PropertyDescriptor,\n record: NodeRecord,\n provenance: NodeProvenance,\n scopeInstance: string | undefined,\n grants: Map<string, ExposureGrant> | undefined,\n configuredValues: { path: string; value: unknown; source: string; items?: { id: string; values: Record<string, unknown>; origin: string }[] }[] | undefined,\n): InspectedProperty {\n const currentValue = readNodeValue(record, descriptor.path);\n const configured = configuredValues?.find(candidate => candidate.path === descriptor.path);\n const local = provenance.kind === ProvenanceKind.Local;\n const isCollection = descriptor.valueType === PropertyValueType.Collection;\n\n const inspected: InspectedProperty = {\n descriptor,\n currentValue,\n effectiveValue: configured ? configured.value : effectiveValueOf(descriptor, currentValue),\n source: (configured?.source ?? (currentValue === undefined ? ValueSource.Default : ValueSource.Local)) as ValueSource,\n editable: false,\n };\n\n const grant = !local && record.kind === SceneNodeKind.Element\n ? grants?.get(grantKey((record.value as { id: string }).id, descriptor.path))\n : undefined;\n\n if (local) {\n inspected.editable = descriptor.readOnly !== true;\n if (inspected.editable) inspected.editTarget = EditTarget.Node;\n else inspected.reason = 'This property is read-only.';\n } else if (grant) {\n inspected.editable = true;\n inspected.editTarget = EditTarget.Instance;\n if (isCollection) inspected.operations = grant.operations;\n } else {\n inspected.reason = `Inherited from '${provenance.source}', which does not expose it.`;\n }\n\n if (isCollection) {\n const items = configured?.items\n ? configured.items.map(item => ({ ...item, editable: local || (item.origin === scopeInstance && grant !== undefined && grant.operations.length > 0) }))\n : toInspectedItems(currentValue, local);\n inspected.items = items;\n }\n\n return inspected;\n}\n\n/**\n * Reports icons the profile cannot supply - on the node itself and in configuration contributed to it. The values\n * stay where they are; these are warnings.\n */\nfunction iconDiagnosticsOf(properties: InspectedProperty[], iconCatalog: EffectiveIconCatalog, nodeId: string): SceneDiagnostic[] {\n return properties.flatMap(property => {\n const descriptor = property.descriptor;\n if (descriptor.valueType !== PropertyValueType.Collection) {\n return iconProblemsOfProperty(iconCatalog, descriptor, property.effectiveValue, DiagnosticSeverity.Warning, { nodeId });\n }\n\n const items = (property.items ?? []).map(item => ({ id: item.id, values: item.values, instance: item.origin === 'owner' ? undefined : item.origin }));\n return iconProblemsOfItems(iconCatalog, descriptor.item, items, DiagnosticSeverity.Warning, { nodeId, path: descriptor.path });\n });\n}\n\n/**\n * Describes one node for an editor: what it is, where it comes from, what can be edited on it - with current and\n * effective values and effective editability - and what can be done with it.\n *\n * A property of a node the scope owns is edited on the node. A property of an inherited node is editable only\n * when its owner exposed it, and then the edit goes to an instance contribution, never into the inherited node.\n * Nothing is mutated.\n *\n * @param document The document to read.\n * @param nodeId The node to describe; see the node id functions.\n * @param context The catalog and the editing scope.\n * @returns The inspection, or `undefined` when the document has no such node.\n */\nexport function inspect(document: SceneDocument, nodeId: string, context: EditingContext): NodeInspection | undefined {\n const index = indexDocument(document);\n const record = index.nodes.get(nodeId);\n if (!record) return undefined;\n\n const chain = resolveTemplateChain(document, context.scope);\n const grants = computeExposureGrants(chain, context.catalog);\n const scopeInstance = chain.levels.at(-1)?.instance;\n const scopeGrants = scopeInstance === undefined ? undefined : grants.byInstance.get(scopeInstance);\n const configuration = resolveEffectiveConfiguration(chain, document.instanceContributions, context.catalog);\n\n const provenance = provenanceOf(record, chain, scopeGrants);\n const description = describeNode(record, context.catalog);\n const componentId = record.kind === SceneNodeKind.Element ? (record.value as { id: string }).id : undefined;\n const configuredValues = configuration.components.find(candidate => candidate.component === componentId)?.values;\n\n const diagnostics: SceneDiagnostic[] = [\n ...description.diagnostics,\n ...configuration.diagnostics.filter(diagnostic => diagnostic.component !== undefined && diagnostic.component === componentId),\n ];\n if (chain.levels.length === 0) {\n diagnostics.push(...chain.diagnostics.filter(diagnostic => diagnostic.code === DiagnosticCode.UnknownScope));\n }\n\n const properties = description.properties.map(descriptor => inspectProperty(descriptor, record, provenance, scopeInstance, scopeGrants, configuredValues));\n if (context.iconCatalog) {\n diagnostics.push(...iconDiagnosticsOf(properties, context.iconCatalog, nodeId));\n }\n\n return {\n nodeId,\n kind: record.kind,\n typeId: record.typeId,\n label: record.label,\n parentId: record.parentId,\n provenance,\n properties,\n capabilities: description.capabilities,\n removable: isRemovable(record, provenance),\n diagnostics,\n };\n}\n"],"names":["provenanceOf","record","chain","scopeGrants","scopeLevel","levels","at","owner","ownerKind","kind","ProvenanceKind","Local","configurable","SceneNodeKind","Element","values","some","grant","component","value","id","ConfigurableInherited","Inherited","source","isRemovable","provenance","list","undefined","leafId","placementIds","toInspectedItems","raw","local","Array","isArray","map","item","position","origin","editable","effectiveValueOf","descriptor","currentValue","valueType","PropertyValueType","Json","default","inspectProperty","scopeInstance","grants","configuredValues","readNodeValue","path","configured","find","candidate","isCollection","Collection","inspected","effectiveValue","ValueSource","Default","get","grantKey","readOnly","editTarget","EditTarget","Node","reason","Instance","operations","items","length","iconDiagnosticsOf","properties","iconCatalog","nodeId","flatMap","property","iconProblemsOfProperty","DiagnosticSeverity","Warning","instance","iconProblemsOfItems","inspect","document","context","index","indexDocument","nodes","resolveTemplateChain","scope","computeExposureGrants","catalog","byInstance","configuration","resolveEffectiveConfiguration","instanceContributions","description","describeNode","componentId","components","diagnostics","filter","diagnostic","push","code","DiagnosticCode","UnknownScope","typeId","label","parentId","capabilities","removable"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"validateValue.d.ts","sourceRoot":"","sources":["../../../editing/validateValue.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAqB,MAAM,qBAAqB,CAAC;AAiBtG,wBAAgB,aAAa,CAAC,UAAU,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAkDhG;AAyBD,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AASD,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,wBAAwB,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,EAAE,CAsB5I"}
1
+ {"version":3,"file":"validateValue.d.ts","sourceRoot":"","sources":["../../../editing/validateValue.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAqB,MAAM,qBAAqB,CAAC;AAiBtG,wBAAgB,aAAa,CAAC,UAAU,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAqDhG;AAmDD,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AASD,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,wBAAwB,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,EAAE,CAsB5I"}
@@ -52,10 +52,34 @@ function isNonEmptyString(value) {
52
52
  return Array.isArray(value.arguments) && Array.isArray(value.results) ? undefined : 'a query binding needs arguments and results lists';
53
53
  case PropertyValueType.Collection:
54
54
  return validateCollection(descriptor, value);
55
+ case PropertyValueType.Json:
56
+ return isJsonValue(value) ? undefined : 'expected a JSON value';
55
57
  default:
56
58
  return isRecord(value) || Array.isArray(value) ? undefined : 'expected a structured value';
57
59
  }
58
60
  }
61
+ /**
62
+ * The deepest nesting a JSON value may have. Authored configuration (chart datasets, option lists) is shallow;
63
+ * the limit turns a runaway or hostile structure into a validation message instead of a stack overflow.
64
+ */ const maximumJsonDepth = 64;
65
+ /**
66
+ * Whether a value is JSON: `null`, a string, a boolean, a finite number, or an array or plain object of those.
67
+ * The value is only inspected, never serialized, so what an editor stores is exactly what was passed in.
68
+ */ function isJsonValue(value, parents = new Set()) {
69
+ if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;
70
+ if (typeof value === 'number') return Number.isFinite(value);
71
+ if (typeof value !== 'object' || parents.has(value) || parents.size >= maximumJsonDepth) return false;
72
+ const isArray = Array.isArray(value);
73
+ const prototype = Object.getPrototypeOf(value);
74
+ if (isArray ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) return false;
75
+ parents.add(value);
76
+ const values = isArray ? Array.from({
77
+ length: value.length
78
+ }, (_, index)=>index in value ? value[index] : undefined) : Object.values(value);
79
+ const valid = values.every((candidate)=>isJsonValue(candidate, parents));
80
+ parents.delete(value);
81
+ return valid;
82
+ }
59
83
  function validateCollection(descriptor, value) {
60
84
  if (!Array.isArray(value)) return 'expected a list';
61
85
  const constraints = descriptor.constraints ?? {};
@@ -1 +1 @@
1
- {"version":3,"file":"validateValue.js","sources":["../../../editing/validateValue.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { CollectionItemDescriptor, PropertyDescriptor, PropertyValueType } from '@cratis/scene.model';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Checks a value against a property descriptor.\n *\n * @param descriptor The property the value is for.\n * @param value The value to check. `undefined` means \"not set\", which is fine unless the property is required.\n * @returns The reason the value is not acceptable, or `undefined` when it is.\n */\nexport function validateValue(descriptor: PropertyDescriptor, value: unknown): string | undefined {\n const constraints = descriptor.constraints ?? {};\n if (value === undefined || value === null) {\n return constraints.required ? 'a value is required' : undefined;\n }\n\n switch (descriptor.valueType) {\n case PropertyValueType.String: {\n if (typeof value !== 'string') return 'expected a string';\n if (constraints.minimumLength !== undefined && value.length < constraints.minimumLength) return `must be at least ${constraints.minimumLength} characters`;\n if (constraints.maximumLength !== undefined && value.length > constraints.maximumLength) return `must be at most ${constraints.maximumLength} characters`;\n if (constraints.pattern !== undefined && !new RegExp(`^(?:${constraints.pattern})$`).test(value)) return `must match ${constraints.pattern}`;\n return undefined;\n }\n\n case PropertyValueType.Number: {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 'expected a number';\n if (constraints.integer && !Number.isInteger(value)) return 'expected a whole number';\n if (constraints.minimum !== undefined && value < constraints.minimum) return `must be at least ${constraints.minimum}`;\n if (constraints.maximum !== undefined && value > constraints.maximum) return `must be at most ${constraints.maximum}`;\n return undefined;\n }\n\n case PropertyValueType.Boolean:\n return typeof value === 'boolean' ? undefined : 'expected true or false';\n\n case PropertyValueType.Enum:\n return (descriptor.choices ?? []).some(choice => choice.value === value) ? undefined : 'not one of the allowed choices';\n\n case PropertyValueType.Icon:\n if (!isRecord(value) || !isNonEmptyString(value.library) || !isNonEmptyString(value.key)) return 'expected an icon reference with a library and a key';\n return value.variant === undefined || typeof value.variant === 'string' ? undefined : 'the icon variant must be a string';\n\n case PropertyValueType.Destination:\n if (!isRecord(value) || !isNonEmptyString(value.screen)) return 'expected a destination naming a screen';\n return value.routeParameterBindings === undefined || isRecord(value.routeParameterBindings)\n ? undefined\n : 'routeParameterBindings must be an object';\n\n case PropertyValueType.QueryReference:\n if (isNonEmptyString(value)) return undefined;\n if (!isRecord(value) || !isNonEmptyString(value.queryId) || !isNonEmptyString(value.query)) return 'expected a query name or a query binding';\n return Array.isArray(value.arguments) && Array.isArray(value.results) ? undefined : 'a query binding needs arguments and results lists';\n\n case PropertyValueType.Collection:\n return validateCollection(descriptor, value);\n\n default:\n return isRecord(value) || Array.isArray(value) ? undefined : 'expected a structured value';\n }\n}\n\nfunction validateCollection(descriptor: PropertyDescriptor, value: unknown): string | undefined {\n if (!Array.isArray(value)) return 'expected a list';\n const constraints = descriptor.constraints ?? {};\n if (constraints.minimumItems !== undefined && value.length < constraints.minimumItems) return `needs at least ${constraints.minimumItems} items`;\n if (constraints.maximumItems !== undefined && value.length > constraints.maximumItems) return `allows at most ${constraints.maximumItems} items`;\n\n const identities = new Set<string>();\n for (const item of value) {\n if (!isRecord(item) || !isNonEmptyString(item.id)) return 'every item needs an id';\n if (identities.has(item.id)) return `the item id '${item.id}' is used twice`;\n identities.add(item.id);\n\n const { id: _identity, ...values } = item;\n const problem = validateItemValues(descriptor.item, values).at(0);\n if (problem) return `item '${item.id}': ${problem.field} ${problem.problem}`;\n }\n\n return undefined;\n}\n\n/**\n * One field of a collection item that does not fit its descriptor.\n */\nexport interface ItemFieldProblem {\n field: string;\n problem: string;\n}\n\n/**\n * Checks a collection item's field values against the collection's item descriptor.\n *\n * @param itemDescriptor What the items look like.\n * @param values The item's field values, keyed by field path.\n * @returns Every field that is unknown or has an unacceptable value.\n */\nexport function validateItemValues(itemDescriptor: CollectionItemDescriptor | undefined, values: Record<string, unknown>): ItemFieldProblem[] {\n const fields = itemDescriptor?.properties ?? [];\n const problems: ItemFieldProblem[] = [];\n\n for (const [field, fieldValue] of Object.entries(values)) {\n const fieldDescriptor = fields.find(candidate => candidate.path === field);\n if (!fieldDescriptor) {\n problems.push({ field, problem: 'is not a field of this collection\\'s items' });\n continue;\n }\n\n const problem = validateValue(fieldDescriptor, fieldValue);\n if (problem) problems.push({ field, problem });\n }\n\n for (const fieldDescriptor of fields) {\n if (fieldDescriptor.constraints?.required && values[fieldDescriptor.path] === undefined) {\n problems.push({ field: fieldDescriptor.path, problem: 'a value is required' });\n }\n }\n\n return problems;\n}\n"],"names":["isRecord","value","Array","isArray","isNonEmptyString","length","validateValue","descriptor","constraints","undefined","required","valueType","PropertyValueType","String","minimumLength","maximumLength","pattern","RegExp","test","Number","isFinite","integer","isInteger","minimum","maximum","Boolean","Enum","choices","some","choice","Icon","library","key","variant","Destination","screen","routeParameterBindings","QueryReference","queryId","query","arguments","results","Collection","validateCollection","minimumItems","maximumItems","identities","Set","item","id","has","add","_identity","values","problem","validateItemValues","at","field","itemDescriptor","fields","properties","problems","fieldValue","Object","entries","fieldDescriptor","find","candidate","path","push"],"mappings":"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"}
1
+ {"version":3,"file":"validateValue.js","sources":["../../../editing/validateValue.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { CollectionItemDescriptor, PropertyDescriptor, PropertyValueType } from '@cratis/scene.model';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Checks a value against a property descriptor.\n *\n * @param descriptor The property the value is for.\n * @param value The value to check. `undefined` means \"not set\", which is fine unless the property is required.\n * @returns The reason the value is not acceptable, or `undefined` when it is.\n */\nexport function validateValue(descriptor: PropertyDescriptor, value: unknown): string | undefined {\n const constraints = descriptor.constraints ?? {};\n if (value === undefined || value === null) {\n return constraints.required ? 'a value is required' : undefined;\n }\n\n switch (descriptor.valueType) {\n case PropertyValueType.String: {\n if (typeof value !== 'string') return 'expected a string';\n if (constraints.minimumLength !== undefined && value.length < constraints.minimumLength) return `must be at least ${constraints.minimumLength} characters`;\n if (constraints.maximumLength !== undefined && value.length > constraints.maximumLength) return `must be at most ${constraints.maximumLength} characters`;\n if (constraints.pattern !== undefined && !new RegExp(`^(?:${constraints.pattern})$`).test(value)) return `must match ${constraints.pattern}`;\n return undefined;\n }\n\n case PropertyValueType.Number: {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 'expected a number';\n if (constraints.integer && !Number.isInteger(value)) return 'expected a whole number';\n if (constraints.minimum !== undefined && value < constraints.minimum) return `must be at least ${constraints.minimum}`;\n if (constraints.maximum !== undefined && value > constraints.maximum) return `must be at most ${constraints.maximum}`;\n return undefined;\n }\n\n case PropertyValueType.Boolean:\n return typeof value === 'boolean' ? undefined : 'expected true or false';\n\n case PropertyValueType.Enum:\n return (descriptor.choices ?? []).some(choice => choice.value === value) ? undefined : 'not one of the allowed choices';\n\n case PropertyValueType.Icon:\n if (!isRecord(value) || !isNonEmptyString(value.library) || !isNonEmptyString(value.key)) return 'expected an icon reference with a library and a key';\n return value.variant === undefined || typeof value.variant === 'string' ? undefined : 'the icon variant must be a string';\n\n case PropertyValueType.Destination:\n if (!isRecord(value) || !isNonEmptyString(value.screen)) return 'expected a destination naming a screen';\n return value.routeParameterBindings === undefined || isRecord(value.routeParameterBindings)\n ? undefined\n : 'routeParameterBindings must be an object';\n\n case PropertyValueType.QueryReference:\n if (isNonEmptyString(value)) return undefined;\n if (!isRecord(value) || !isNonEmptyString(value.queryId) || !isNonEmptyString(value.query)) return 'expected a query name or a query binding';\n return Array.isArray(value.arguments) && Array.isArray(value.results) ? undefined : 'a query binding needs arguments and results lists';\n\n case PropertyValueType.Collection:\n return validateCollection(descriptor, value);\n\n case PropertyValueType.Json:\n return isJsonValue(value) ? undefined : 'expected a JSON value';\n\n default:\n return isRecord(value) || Array.isArray(value) ? undefined : 'expected a structured value';\n }\n}\n\n/**\n * The deepest nesting a JSON value may have. Authored configuration (chart datasets, option lists) is shallow;\n * the limit turns a runaway or hostile structure into a validation message instead of a stack overflow.\n */\nconst maximumJsonDepth = 64;\n\n/**\n * Whether a value is JSON: `null`, a string, a boolean, a finite number, or an array or plain object of those.\n * The value is only inspected, never serialized, so what an editor stores is exactly what was passed in.\n */\nfunction isJsonValue(value: unknown, parents = new Set<object>()): boolean {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;\n if (typeof value === 'number') return Number.isFinite(value);\n if (typeof value !== 'object' || parents.has(value) || parents.size >= maximumJsonDepth) return false;\n\n const isArray = Array.isArray(value);\n const prototype = Object.getPrototypeOf(value);\n if (isArray ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) return false;\n\n parents.add(value);\n const values = isArray ? Array.from({ length: value.length }, (_, index) => (index in value ? value[index] : undefined)) : Object.values(value);\n const valid = values.every(candidate => isJsonValue(candidate, parents));\n parents.delete(value);\n return valid;\n}\n\nfunction validateCollection(descriptor: PropertyDescriptor, value: unknown): string | undefined {\n if (!Array.isArray(value)) return 'expected a list';\n const constraints = descriptor.constraints ?? {};\n if (constraints.minimumItems !== undefined && value.length < constraints.minimumItems) return `needs at least ${constraints.minimumItems} items`;\n if (constraints.maximumItems !== undefined && value.length > constraints.maximumItems) return `allows at most ${constraints.maximumItems} items`;\n\n const identities = new Set<string>();\n for (const item of value) {\n if (!isRecord(item) || !isNonEmptyString(item.id)) return 'every item needs an id';\n if (identities.has(item.id)) return `the item id '${item.id}' is used twice`;\n identities.add(item.id);\n\n const { id: _identity, ...values } = item;\n const problem = validateItemValues(descriptor.item, values).at(0);\n if (problem) return `item '${item.id}': ${problem.field} ${problem.problem}`;\n }\n\n return undefined;\n}\n\n/**\n * One field of a collection item that does not fit its descriptor.\n */\nexport interface ItemFieldProblem {\n field: string;\n problem: string;\n}\n\n/**\n * Checks a collection item's field values against the collection's item descriptor.\n *\n * @param itemDescriptor What the items look like.\n * @param values The item's field values, keyed by field path.\n * @returns Every field that is unknown or has an unacceptable value.\n */\nexport function validateItemValues(itemDescriptor: CollectionItemDescriptor | undefined, values: Record<string, unknown>): ItemFieldProblem[] {\n const fields = itemDescriptor?.properties ?? [];\n const problems: ItemFieldProblem[] = [];\n\n for (const [field, fieldValue] of Object.entries(values)) {\n const fieldDescriptor = fields.find(candidate => candidate.path === field);\n if (!fieldDescriptor) {\n problems.push({ field, problem: 'is not a field of this collection\\'s items' });\n continue;\n }\n\n const problem = validateValue(fieldDescriptor, fieldValue);\n if (problem) problems.push({ field, problem });\n }\n\n for (const fieldDescriptor of fields) {\n if (fieldDescriptor.constraints?.required && values[fieldDescriptor.path] === undefined) {\n problems.push({ field: fieldDescriptor.path, problem: 'a value is required' });\n }\n }\n\n return problems;\n}\n"],"names":["isRecord","value","Array","isArray","isNonEmptyString","length","validateValue","descriptor","constraints","undefined","required","valueType","PropertyValueType","String","minimumLength","maximumLength","pattern","RegExp","test","Number","isFinite","integer","isInteger","minimum","maximum","Boolean","Enum","choices","some","choice","Icon","library","key","variant","Destination","screen","routeParameterBindings","QueryReference","queryId","query","arguments","results","Collection","validateCollection","Json","isJsonValue","maximumJsonDepth","parents","Set","has","size","prototype","Object","getPrototypeOf","add","values","from","_","index","valid","every","candidate","delete","minimumItems","maximumItems","identities","item","id","_identity","problem","validateItemValues","at","field","itemDescriptor","fields","properties","problems","fieldValue","entries","fieldDescriptor","find","path","push"],"mappings":"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"}