@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/index.cjs CHANGED
@@ -978,9 +978,9 @@ function collectFields(elements, properties, required, ctx) {
978
978
  for (const element of elements) {
979
979
  switch (element.kind) {
980
980
  case "field":
981
- properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
981
+ properties[getSerializedFieldName(element)] = generateFieldSchema(element, ctx);
982
982
  if (element.required) {
983
- required.push(getSerializedName(element.name, element.metadata));
983
+ required.push(getSerializedFieldName(element));
984
984
  }
985
985
  break;
986
986
  case "group":
@@ -1051,19 +1051,21 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
1051
1051
  schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
1052
1052
  return schema;
1053
1053
  }
1054
- const byTarget = /* @__PURE__ */ new Map();
1055
- for (const c of pathConstraints) {
1056
- const target = c.path?.segments[0];
1057
- if (!target) continue;
1058
- const group = byTarget.get(target) ?? [];
1059
- group.push(c);
1060
- byTarget.set(target, group);
1061
- }
1062
- const propertyOverrides = {};
1063
- for (const [target, constraints] of byTarget) {
1064
- const subSchema = {};
1065
- applyConstraints(subSchema, constraints, ctx);
1066
- propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
1054
+ const propertyOverrides = buildPropertyOverrides(pathConstraints, typeNode, ctx);
1055
+ const nullableValueBranch = getNullableUnionValueSchema(schema);
1056
+ if (nullableValueBranch !== void 0) {
1057
+ const updatedNullableValueBranch = applyPathTargetedConstraints(
1058
+ nullableValueBranch,
1059
+ pathConstraints,
1060
+ ctx,
1061
+ resolveTraversableTypeNode(typeNode, ctx)
1062
+ );
1063
+ if (schema.oneOf !== void 0) {
1064
+ schema.oneOf = schema.oneOf.map(
1065
+ (branch) => branch === nullableValueBranch ? updatedNullableValueBranch : branch
1066
+ );
1067
+ }
1068
+ return schema;
1067
1069
  }
1068
1070
  if (schema.$ref) {
1069
1071
  const { $ref, ...rest } = schema;
@@ -1078,7 +1080,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
1078
1080
  const missingOverrides = {};
1079
1081
  for (const [target, overrideSchema] of Object.entries(propertyOverrides)) {
1080
1082
  if (schema.properties[target]) {
1081
- Object.assign(schema.properties[target], overrideSchema);
1083
+ mergeSchemaOverride(schema.properties[target], overrideSchema);
1082
1084
  } else {
1083
1085
  missingOverrides[target] = overrideSchema;
1084
1086
  }
@@ -1152,7 +1154,7 @@ function generateObjectType(type, ctx) {
1152
1154
  const properties = {};
1153
1155
  const required = [];
1154
1156
  for (const prop of type.properties) {
1155
- const propertyName = getSerializedName(prop.name, prop.metadata);
1157
+ const propertyName = getSerializedObjectPropertyName(prop);
1156
1158
  properties[propertyName] = generatePropertySchema(prop, ctx);
1157
1159
  if (!prop.optional) {
1158
1160
  required.push(propertyName);
@@ -1206,7 +1208,16 @@ function isNullableUnion(type) {
1206
1208
  return nullCount === 1;
1207
1209
  }
1208
1210
  function generateReferenceType(type, ctx) {
1209
- return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
1211
+ return { $ref: `#/$defs/${getSerializedTypeName(type.name, ctx)}` };
1212
+ }
1213
+ function getSerializedFieldName(field) {
1214
+ return getSerializedName(field.name, field.metadata);
1215
+ }
1216
+ function getSerializedObjectPropertyName(property) {
1217
+ return getSerializedName(property.name, property.metadata);
1218
+ }
1219
+ function getSerializedTypeName(logicalName, ctx) {
1220
+ return ctx.typeNameMap[logicalName] ?? logicalName;
1210
1221
  }
1211
1222
  function applyResolvedMetadata(schema, metadata) {
1212
1223
  const displayName = getDisplayName(metadata);
@@ -1217,17 +1228,148 @@ function applyResolvedMetadata(schema, metadata) {
1217
1228
  function resolveReferencedType(type, ctx) {
1218
1229
  return ctx.typeRegistry[type.name]?.type;
1219
1230
  }
1231
+ function dereferenceTypeNode(typeNode, ctx) {
1232
+ if (typeNode?.kind !== "reference") {
1233
+ return typeNode;
1234
+ }
1235
+ return resolveReferencedType(typeNode, ctx);
1236
+ }
1237
+ function unwrapNullableTypeNode(typeNode) {
1238
+ if (typeNode?.kind !== "union" || !isNullableUnion(typeNode)) {
1239
+ return typeNode;
1240
+ }
1241
+ return typeNode.members.find(
1242
+ (member) => !(member.kind === "primitive" && member.primitiveKind === "null")
1243
+ );
1244
+ }
1245
+ function resolveTraversableTypeNode(typeNode, ctx) {
1246
+ const dereferenced = dereferenceTypeNode(typeNode, ctx);
1247
+ const unwrapped = unwrapNullableTypeNode(dereferenced);
1248
+ if (unwrapped !== dereferenced) {
1249
+ return resolveTraversableTypeNode(unwrapped, ctx);
1250
+ }
1251
+ return dereferenced;
1252
+ }
1220
1253
  function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
1221
- if (typeNode?.kind === "object") {
1222
- const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
1223
- return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
1254
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1255
+ if (effectiveType?.kind === "array") {
1256
+ return resolveSerializedPropertyName(logicalName, effectiveType.items, ctx);
1224
1257
  }
1225
- if (typeNode?.kind === "reference") {
1226
- const referencedType = resolveReferencedType(typeNode, ctx);
1227
- return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
1258
+ if (effectiveType?.kind === "object") {
1259
+ const property = effectiveType.properties.find((candidate) => candidate.name === logicalName);
1260
+ return property === void 0 ? logicalName : getSerializedObjectPropertyName(property);
1228
1261
  }
1229
1262
  return logicalName;
1230
1263
  }
1264
+ function resolveTargetTypeNode(logicalName, typeNode, ctx) {
1265
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1266
+ if (effectiveType?.kind === "array") {
1267
+ return resolveTargetTypeNode(logicalName, effectiveType.items, ctx);
1268
+ }
1269
+ if (effectiveType?.kind !== "object") {
1270
+ return void 0;
1271
+ }
1272
+ return effectiveType.properties.find((candidate) => candidate.name === logicalName)?.type;
1273
+ }
1274
+ function buildPropertyOverrides(pathConstraints, typeNode, ctx) {
1275
+ const byTarget = /* @__PURE__ */ new Map();
1276
+ for (const constraint of pathConstraints) {
1277
+ const target = constraint.path?.segments[0];
1278
+ if (!target) {
1279
+ continue;
1280
+ }
1281
+ const grouped = byTarget.get(target) ?? [];
1282
+ grouped.push(constraint);
1283
+ byTarget.set(target, grouped);
1284
+ }
1285
+ const overrides = {};
1286
+ for (const [target, constraints] of byTarget) {
1287
+ overrides[resolveSerializedPropertyName(target, typeNode, ctx)] = buildPathOverrideSchema(
1288
+ constraints.map(stripLeadingPathSegment),
1289
+ resolveTargetTypeNode(target, typeNode, ctx),
1290
+ ctx
1291
+ );
1292
+ }
1293
+ return overrides;
1294
+ }
1295
+ function buildPathOverrideSchema(constraints, typeNode, ctx) {
1296
+ const schema = {};
1297
+ const directConstraints = [];
1298
+ const nestedConstraints = [];
1299
+ for (const constraint of constraints) {
1300
+ if (constraint.path === void 0 || constraint.path.segments.length === 0) {
1301
+ directConstraints.push(constraint);
1302
+ } else {
1303
+ nestedConstraints.push(constraint);
1304
+ }
1305
+ }
1306
+ applyConstraints(schema, directConstraints, ctx);
1307
+ if (nestedConstraints.length === 0) {
1308
+ return schema;
1309
+ }
1310
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1311
+ if (effectiveType?.kind === "array") {
1312
+ schema.items = buildPathOverrideSchema(nestedConstraints, effectiveType.items, ctx);
1313
+ return schema;
1314
+ }
1315
+ schema.properties = buildPropertyOverrides(nestedConstraints, effectiveType, ctx);
1316
+ return schema;
1317
+ }
1318
+ function mergeSchemaOverride(target, override) {
1319
+ const nullableValueBranch = getNullableUnionValueSchema(target);
1320
+ if (nullableValueBranch !== void 0) {
1321
+ mergeSchemaOverride(nullableValueBranch, override);
1322
+ return;
1323
+ }
1324
+ if (override.properties !== void 0) {
1325
+ const mergedProperties = target.properties ?? {};
1326
+ for (const [name, propertyOverride] of Object.entries(override.properties)) {
1327
+ const existing = mergedProperties[name];
1328
+ if (existing === void 0) {
1329
+ mergedProperties[name] = propertyOverride;
1330
+ } else {
1331
+ mergeSchemaOverride(existing, propertyOverride);
1332
+ }
1333
+ }
1334
+ target.properties = mergedProperties;
1335
+ }
1336
+ if (override.items !== void 0) {
1337
+ if (target.items === void 0) {
1338
+ target.items = override.items;
1339
+ } else {
1340
+ mergeSchemaOverride(target.items, override.items);
1341
+ }
1342
+ }
1343
+ for (const [key, value] of Object.entries(override)) {
1344
+ if (key === "properties" || key === "items") {
1345
+ continue;
1346
+ }
1347
+ target[key] = value;
1348
+ }
1349
+ }
1350
+ function stripLeadingPathSegment(constraint) {
1351
+ const segments = constraint.path?.segments;
1352
+ if (segments === void 0 || segments.length === 0) {
1353
+ return constraint;
1354
+ }
1355
+ const [, ...rest] = segments;
1356
+ if (rest.length === 0) {
1357
+ const { path: _path, ...stripped } = constraint;
1358
+ return stripped;
1359
+ }
1360
+ return {
1361
+ ...constraint,
1362
+ path: { segments: rest }
1363
+ };
1364
+ }
1365
+ function getNullableUnionValueSchema(schema) {
1366
+ if (schema.oneOf?.length !== 2) {
1367
+ return void 0;
1368
+ }
1369
+ const valueSchema = schema.oneOf.find((branch) => branch.type !== "null");
1370
+ const nullSchema = schema.oneOf.find((branch) => branch.type === "null");
1371
+ return valueSchema !== void 0 && nullSchema !== void 0 ? valueSchema : void 0;
1372
+ }
1231
1373
  function generateDynamicType(type) {
1232
1374
  if (type.dynamicKind === "enum") {
1233
1375
  const schema = {