@formspec/build 0.1.0-alpha.30 → 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/cli.cjs CHANGED
@@ -1012,9 +1012,9 @@ function collectFields(elements, properties, required, ctx) {
1012
1012
  for (const element of elements) {
1013
1013
  switch (element.kind) {
1014
1014
  case "field":
1015
- properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
1015
+ properties[getSerializedFieldName(element)] = generateFieldSchema(element, ctx);
1016
1016
  if (element.required) {
1017
- required.push(getSerializedName(element.name, element.metadata));
1017
+ required.push(getSerializedFieldName(element));
1018
1018
  }
1019
1019
  break;
1020
1020
  case "group":
@@ -1085,19 +1085,21 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
1085
1085
  schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
1086
1086
  return schema;
1087
1087
  }
1088
- const byTarget = /* @__PURE__ */ new Map();
1089
- for (const c of pathConstraints) {
1090
- const target = c.path?.segments[0];
1091
- if (!target) continue;
1092
- const group = byTarget.get(target) ?? [];
1093
- group.push(c);
1094
- byTarget.set(target, group);
1095
- }
1096
- const propertyOverrides = {};
1097
- for (const [target, constraints] of byTarget) {
1098
- const subSchema = {};
1099
- applyConstraints(subSchema, constraints, ctx);
1100
- propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
1088
+ const propertyOverrides = buildPropertyOverrides(pathConstraints, typeNode, ctx);
1089
+ const nullableValueBranch = getNullableUnionValueSchema(schema);
1090
+ if (nullableValueBranch !== void 0) {
1091
+ const updatedNullableValueBranch = applyPathTargetedConstraints(
1092
+ nullableValueBranch,
1093
+ pathConstraints,
1094
+ ctx,
1095
+ resolveTraversableTypeNode(typeNode, ctx)
1096
+ );
1097
+ if (schema.oneOf !== void 0) {
1098
+ schema.oneOf = schema.oneOf.map(
1099
+ (branch) => branch === nullableValueBranch ? updatedNullableValueBranch : branch
1100
+ );
1101
+ }
1102
+ return schema;
1101
1103
  }
1102
1104
  if (schema.$ref) {
1103
1105
  const { $ref, ...rest } = schema;
@@ -1112,7 +1114,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
1112
1114
  const missingOverrides = {};
1113
1115
  for (const [target, overrideSchema] of Object.entries(propertyOverrides)) {
1114
1116
  if (schema.properties[target]) {
1115
- Object.assign(schema.properties[target], overrideSchema);
1117
+ mergeSchemaOverride(schema.properties[target], overrideSchema);
1116
1118
  } else {
1117
1119
  missingOverrides[target] = overrideSchema;
1118
1120
  }
@@ -1186,7 +1188,7 @@ function generateObjectType(type, ctx) {
1186
1188
  const properties = {};
1187
1189
  const required = [];
1188
1190
  for (const prop of type.properties) {
1189
- const propertyName = getSerializedName(prop.name, prop.metadata);
1191
+ const propertyName = getSerializedObjectPropertyName(prop);
1190
1192
  properties[propertyName] = generatePropertySchema(prop, ctx);
1191
1193
  if (!prop.optional) {
1192
1194
  required.push(propertyName);
@@ -1240,7 +1242,16 @@ function isNullableUnion(type) {
1240
1242
  return nullCount === 1;
1241
1243
  }
1242
1244
  function generateReferenceType(type, ctx) {
1243
- return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
1245
+ return { $ref: `#/$defs/${getSerializedTypeName(type.name, ctx)}` };
1246
+ }
1247
+ function getSerializedFieldName(field) {
1248
+ return getSerializedName(field.name, field.metadata);
1249
+ }
1250
+ function getSerializedObjectPropertyName(property) {
1251
+ return getSerializedName(property.name, property.metadata);
1252
+ }
1253
+ function getSerializedTypeName(logicalName, ctx) {
1254
+ return ctx.typeNameMap[logicalName] ?? logicalName;
1244
1255
  }
1245
1256
  function applyResolvedMetadata(schema, metadata) {
1246
1257
  const displayName = getDisplayName(metadata);
@@ -1251,17 +1262,148 @@ function applyResolvedMetadata(schema, metadata) {
1251
1262
  function resolveReferencedType(type, ctx) {
1252
1263
  return ctx.typeRegistry[type.name]?.type;
1253
1264
  }
1265
+ function dereferenceTypeNode(typeNode, ctx) {
1266
+ if (typeNode?.kind !== "reference") {
1267
+ return typeNode;
1268
+ }
1269
+ return resolveReferencedType(typeNode, ctx);
1270
+ }
1271
+ function unwrapNullableTypeNode(typeNode) {
1272
+ if (typeNode?.kind !== "union" || !isNullableUnion(typeNode)) {
1273
+ return typeNode;
1274
+ }
1275
+ return typeNode.members.find(
1276
+ (member) => !(member.kind === "primitive" && member.primitiveKind === "null")
1277
+ );
1278
+ }
1279
+ function resolveTraversableTypeNode(typeNode, ctx) {
1280
+ const dereferenced = dereferenceTypeNode(typeNode, ctx);
1281
+ const unwrapped = unwrapNullableTypeNode(dereferenced);
1282
+ if (unwrapped !== dereferenced) {
1283
+ return resolveTraversableTypeNode(unwrapped, ctx);
1284
+ }
1285
+ return dereferenced;
1286
+ }
1254
1287
  function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
1255
- if (typeNode?.kind === "object") {
1256
- const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
1257
- return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
1288
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1289
+ if (effectiveType?.kind === "array") {
1290
+ return resolveSerializedPropertyName(logicalName, effectiveType.items, ctx);
1258
1291
  }
1259
- if (typeNode?.kind === "reference") {
1260
- const referencedType = resolveReferencedType(typeNode, ctx);
1261
- return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
1292
+ if (effectiveType?.kind === "object") {
1293
+ const property = effectiveType.properties.find((candidate) => candidate.name === logicalName);
1294
+ return property === void 0 ? logicalName : getSerializedObjectPropertyName(property);
1262
1295
  }
1263
1296
  return logicalName;
1264
1297
  }
1298
+ function resolveTargetTypeNode(logicalName, typeNode, ctx) {
1299
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1300
+ if (effectiveType?.kind === "array") {
1301
+ return resolveTargetTypeNode(logicalName, effectiveType.items, ctx);
1302
+ }
1303
+ if (effectiveType?.kind !== "object") {
1304
+ return void 0;
1305
+ }
1306
+ return effectiveType.properties.find((candidate) => candidate.name === logicalName)?.type;
1307
+ }
1308
+ function buildPropertyOverrides(pathConstraints, typeNode, ctx) {
1309
+ const byTarget = /* @__PURE__ */ new Map();
1310
+ for (const constraint of pathConstraints) {
1311
+ const target = constraint.path?.segments[0];
1312
+ if (!target) {
1313
+ continue;
1314
+ }
1315
+ const grouped = byTarget.get(target) ?? [];
1316
+ grouped.push(constraint);
1317
+ byTarget.set(target, grouped);
1318
+ }
1319
+ const overrides = {};
1320
+ for (const [target, constraints] of byTarget) {
1321
+ overrides[resolveSerializedPropertyName(target, typeNode, ctx)] = buildPathOverrideSchema(
1322
+ constraints.map(stripLeadingPathSegment),
1323
+ resolveTargetTypeNode(target, typeNode, ctx),
1324
+ ctx
1325
+ );
1326
+ }
1327
+ return overrides;
1328
+ }
1329
+ function buildPathOverrideSchema(constraints, typeNode, ctx) {
1330
+ const schema = {};
1331
+ const directConstraints = [];
1332
+ const nestedConstraints = [];
1333
+ for (const constraint of constraints) {
1334
+ if (constraint.path === void 0 || constraint.path.segments.length === 0) {
1335
+ directConstraints.push(constraint);
1336
+ } else {
1337
+ nestedConstraints.push(constraint);
1338
+ }
1339
+ }
1340
+ applyConstraints(schema, directConstraints, ctx);
1341
+ if (nestedConstraints.length === 0) {
1342
+ return schema;
1343
+ }
1344
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1345
+ if (effectiveType?.kind === "array") {
1346
+ schema.items = buildPathOverrideSchema(nestedConstraints, effectiveType.items, ctx);
1347
+ return schema;
1348
+ }
1349
+ schema.properties = buildPropertyOverrides(nestedConstraints, effectiveType, ctx);
1350
+ return schema;
1351
+ }
1352
+ function mergeSchemaOverride(target, override) {
1353
+ const nullableValueBranch = getNullableUnionValueSchema(target);
1354
+ if (nullableValueBranch !== void 0) {
1355
+ mergeSchemaOverride(nullableValueBranch, override);
1356
+ return;
1357
+ }
1358
+ if (override.properties !== void 0) {
1359
+ const mergedProperties = target.properties ?? {};
1360
+ for (const [name, propertyOverride] of Object.entries(override.properties)) {
1361
+ const existing = mergedProperties[name];
1362
+ if (existing === void 0) {
1363
+ mergedProperties[name] = propertyOverride;
1364
+ } else {
1365
+ mergeSchemaOverride(existing, propertyOverride);
1366
+ }
1367
+ }
1368
+ target.properties = mergedProperties;
1369
+ }
1370
+ if (override.items !== void 0) {
1371
+ if (target.items === void 0) {
1372
+ target.items = override.items;
1373
+ } else {
1374
+ mergeSchemaOverride(target.items, override.items);
1375
+ }
1376
+ }
1377
+ for (const [key, value] of Object.entries(override)) {
1378
+ if (key === "properties" || key === "items") {
1379
+ continue;
1380
+ }
1381
+ target[key] = value;
1382
+ }
1383
+ }
1384
+ function stripLeadingPathSegment(constraint) {
1385
+ const segments = constraint.path?.segments;
1386
+ if (segments === void 0 || segments.length === 0) {
1387
+ return constraint;
1388
+ }
1389
+ const [, ...rest] = segments;
1390
+ if (rest.length === 0) {
1391
+ const { path: _path, ...stripped } = constraint;
1392
+ return stripped;
1393
+ }
1394
+ return {
1395
+ ...constraint,
1396
+ path: { segments: rest }
1397
+ };
1398
+ }
1399
+ function getNullableUnionValueSchema(schema) {
1400
+ if (schema.oneOf?.length !== 2) {
1401
+ return void 0;
1402
+ }
1403
+ const valueSchema = schema.oneOf.find((branch) => branch.type !== "null");
1404
+ const nullSchema = schema.oneOf.find((branch) => branch.type === "null");
1405
+ return valueSchema !== void 0 && nullSchema !== void 0 ? valueSchema : void 0;
1406
+ }
1265
1407
  function generateDynamicType(type) {
1266
1408
  if (type.dynamicKind === "enum") {
1267
1409
  const schema = {