@formspec/build 0.1.0-alpha.29 → 0.1.0-alpha.31

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.
package/dist/browser.cjs CHANGED
@@ -757,9 +757,9 @@ function collectFields(elements, properties, required, ctx) {
757
757
  for (const element of elements) {
758
758
  switch (element.kind) {
759
759
  case "field":
760
- properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
760
+ properties[getSerializedFieldName(element)] = generateFieldSchema(element, ctx);
761
761
  if (element.required) {
762
- required.push(getSerializedName(element.name, element.metadata));
762
+ required.push(getSerializedFieldName(element));
763
763
  }
764
764
  break;
765
765
  case "group":
@@ -830,19 +830,21 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
830
830
  schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
831
831
  return schema;
832
832
  }
833
- const byTarget = /* @__PURE__ */ new Map();
834
- for (const c of pathConstraints) {
835
- const target = c.path?.segments[0];
836
- if (!target) continue;
837
- const group = byTarget.get(target) ?? [];
838
- group.push(c);
839
- byTarget.set(target, group);
840
- }
841
- const propertyOverrides = {};
842
- for (const [target, constraints] of byTarget) {
843
- const subSchema = {};
844
- applyConstraints(subSchema, constraints, ctx);
845
- propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
833
+ const propertyOverrides = buildPropertyOverrides(pathConstraints, typeNode, ctx);
834
+ const nullableValueBranch = getNullableUnionValueSchema(schema);
835
+ if (nullableValueBranch !== void 0) {
836
+ const updatedNullableValueBranch = applyPathTargetedConstraints(
837
+ nullableValueBranch,
838
+ pathConstraints,
839
+ ctx,
840
+ resolveTraversableTypeNode(typeNode, ctx)
841
+ );
842
+ if (schema.oneOf !== void 0) {
843
+ schema.oneOf = schema.oneOf.map(
844
+ (branch) => branch === nullableValueBranch ? updatedNullableValueBranch : branch
845
+ );
846
+ }
847
+ return schema;
846
848
  }
847
849
  if (schema.$ref) {
848
850
  const { $ref, ...rest } = schema;
@@ -857,7 +859,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
857
859
  const missingOverrides = {};
858
860
  for (const [target, overrideSchema] of Object.entries(propertyOverrides)) {
859
861
  if (schema.properties[target]) {
860
- Object.assign(schema.properties[target], overrideSchema);
862
+ mergeSchemaOverride(schema.properties[target], overrideSchema);
861
863
  } else {
862
864
  missingOverrides[target] = overrideSchema;
863
865
  }
@@ -931,7 +933,7 @@ function generateObjectType(type, ctx) {
931
933
  const properties = {};
932
934
  const required = [];
933
935
  for (const prop of type.properties) {
934
- const propertyName = getSerializedName(prop.name, prop.metadata);
936
+ const propertyName = getSerializedObjectPropertyName(prop);
935
937
  properties[propertyName] = generatePropertySchema(prop, ctx);
936
938
  if (!prop.optional) {
937
939
  required.push(propertyName);
@@ -985,7 +987,16 @@ function isNullableUnion(type) {
985
987
  return nullCount === 1;
986
988
  }
987
989
  function generateReferenceType(type, ctx) {
988
- return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
990
+ return { $ref: `#/$defs/${getSerializedTypeName(type.name, ctx)}` };
991
+ }
992
+ function getSerializedFieldName(field) {
993
+ return getSerializedName(field.name, field.metadata);
994
+ }
995
+ function getSerializedObjectPropertyName(property) {
996
+ return getSerializedName(property.name, property.metadata);
997
+ }
998
+ function getSerializedTypeName(logicalName, ctx) {
999
+ return ctx.typeNameMap[logicalName] ?? logicalName;
989
1000
  }
990
1001
  function applyResolvedMetadata(schema, metadata) {
991
1002
  const displayName = getDisplayName(metadata);
@@ -996,17 +1007,148 @@ function applyResolvedMetadata(schema, metadata) {
996
1007
  function resolveReferencedType(type, ctx) {
997
1008
  return ctx.typeRegistry[type.name]?.type;
998
1009
  }
1010
+ function dereferenceTypeNode(typeNode, ctx) {
1011
+ if (typeNode?.kind !== "reference") {
1012
+ return typeNode;
1013
+ }
1014
+ return resolveReferencedType(typeNode, ctx);
1015
+ }
1016
+ function unwrapNullableTypeNode(typeNode) {
1017
+ if (typeNode?.kind !== "union" || !isNullableUnion(typeNode)) {
1018
+ return typeNode;
1019
+ }
1020
+ return typeNode.members.find(
1021
+ (member) => !(member.kind === "primitive" && member.primitiveKind === "null")
1022
+ );
1023
+ }
1024
+ function resolveTraversableTypeNode(typeNode, ctx) {
1025
+ const dereferenced = dereferenceTypeNode(typeNode, ctx);
1026
+ const unwrapped = unwrapNullableTypeNode(dereferenced);
1027
+ if (unwrapped !== dereferenced) {
1028
+ return resolveTraversableTypeNode(unwrapped, ctx);
1029
+ }
1030
+ return dereferenced;
1031
+ }
999
1032
  function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
1000
- if (typeNode?.kind === "object") {
1001
- const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
1002
- return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
1033
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1034
+ if (effectiveType?.kind === "array") {
1035
+ return resolveSerializedPropertyName(logicalName, effectiveType.items, ctx);
1003
1036
  }
1004
- if (typeNode?.kind === "reference") {
1005
- const referencedType = resolveReferencedType(typeNode, ctx);
1006
- return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
1037
+ if (effectiveType?.kind === "object") {
1038
+ const property = effectiveType.properties.find((candidate) => candidate.name === logicalName);
1039
+ return property === void 0 ? logicalName : getSerializedObjectPropertyName(property);
1007
1040
  }
1008
1041
  return logicalName;
1009
1042
  }
1043
+ function resolveTargetTypeNode(logicalName, typeNode, ctx) {
1044
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1045
+ if (effectiveType?.kind === "array") {
1046
+ return resolveTargetTypeNode(logicalName, effectiveType.items, ctx);
1047
+ }
1048
+ if (effectiveType?.kind !== "object") {
1049
+ return void 0;
1050
+ }
1051
+ return effectiveType.properties.find((candidate) => candidate.name === logicalName)?.type;
1052
+ }
1053
+ function buildPropertyOverrides(pathConstraints, typeNode, ctx) {
1054
+ const byTarget = /* @__PURE__ */ new Map();
1055
+ for (const constraint of pathConstraints) {
1056
+ const target = constraint.path?.segments[0];
1057
+ if (!target) {
1058
+ continue;
1059
+ }
1060
+ const grouped = byTarget.get(target) ?? [];
1061
+ grouped.push(constraint);
1062
+ byTarget.set(target, grouped);
1063
+ }
1064
+ const overrides = {};
1065
+ for (const [target, constraints] of byTarget) {
1066
+ overrides[resolveSerializedPropertyName(target, typeNode, ctx)] = buildPathOverrideSchema(
1067
+ constraints.map(stripLeadingPathSegment),
1068
+ resolveTargetTypeNode(target, typeNode, ctx),
1069
+ ctx
1070
+ );
1071
+ }
1072
+ return overrides;
1073
+ }
1074
+ function buildPathOverrideSchema(constraints, typeNode, ctx) {
1075
+ const schema = {};
1076
+ const directConstraints = [];
1077
+ const nestedConstraints = [];
1078
+ for (const constraint of constraints) {
1079
+ if (constraint.path === void 0 || constraint.path.segments.length === 0) {
1080
+ directConstraints.push(constraint);
1081
+ } else {
1082
+ nestedConstraints.push(constraint);
1083
+ }
1084
+ }
1085
+ applyConstraints(schema, directConstraints, ctx);
1086
+ if (nestedConstraints.length === 0) {
1087
+ return schema;
1088
+ }
1089
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1090
+ if (effectiveType?.kind === "array") {
1091
+ schema.items = buildPathOverrideSchema(nestedConstraints, effectiveType.items, ctx);
1092
+ return schema;
1093
+ }
1094
+ schema.properties = buildPropertyOverrides(nestedConstraints, effectiveType, ctx);
1095
+ return schema;
1096
+ }
1097
+ function mergeSchemaOverride(target, override) {
1098
+ const nullableValueBranch = getNullableUnionValueSchema(target);
1099
+ if (nullableValueBranch !== void 0) {
1100
+ mergeSchemaOverride(nullableValueBranch, override);
1101
+ return;
1102
+ }
1103
+ if (override.properties !== void 0) {
1104
+ const mergedProperties = target.properties ?? {};
1105
+ for (const [name, propertyOverride] of Object.entries(override.properties)) {
1106
+ const existing = mergedProperties[name];
1107
+ if (existing === void 0) {
1108
+ mergedProperties[name] = propertyOverride;
1109
+ } else {
1110
+ mergeSchemaOverride(existing, propertyOverride);
1111
+ }
1112
+ }
1113
+ target.properties = mergedProperties;
1114
+ }
1115
+ if (override.items !== void 0) {
1116
+ if (target.items === void 0) {
1117
+ target.items = override.items;
1118
+ } else {
1119
+ mergeSchemaOverride(target.items, override.items);
1120
+ }
1121
+ }
1122
+ for (const [key, value] of Object.entries(override)) {
1123
+ if (key === "properties" || key === "items") {
1124
+ continue;
1125
+ }
1126
+ target[key] = value;
1127
+ }
1128
+ }
1129
+ function stripLeadingPathSegment(constraint) {
1130
+ const segments = constraint.path?.segments;
1131
+ if (segments === void 0 || segments.length === 0) {
1132
+ return constraint;
1133
+ }
1134
+ const [, ...rest] = segments;
1135
+ if (rest.length === 0) {
1136
+ const { path: _path, ...stripped } = constraint;
1137
+ return stripped;
1138
+ }
1139
+ return {
1140
+ ...constraint,
1141
+ path: { segments: rest }
1142
+ };
1143
+ }
1144
+ function getNullableUnionValueSchema(schema) {
1145
+ if (schema.oneOf?.length !== 2) {
1146
+ return void 0;
1147
+ }
1148
+ const valueSchema = schema.oneOf.find((branch) => branch.type !== "null");
1149
+ const nullSchema = schema.oneOf.find((branch) => branch.type === "null");
1150
+ return valueSchema !== void 0 && nullSchema !== void 0 ? valueSchema : void 0;
1151
+ }
1010
1152
  function generateDynamicType(type) {
1011
1153
  if (type.dynamicKind === "enum") {
1012
1154
  const schema = {