@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.js CHANGED
@@ -990,9 +990,9 @@ function collectFields(elements, properties, required, ctx) {
990
990
  for (const element of elements) {
991
991
  switch (element.kind) {
992
992
  case "field":
993
- properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
993
+ properties[getSerializedFieldName(element)] = generateFieldSchema(element, ctx);
994
994
  if (element.required) {
995
- required.push(getSerializedName(element.name, element.metadata));
995
+ required.push(getSerializedFieldName(element));
996
996
  }
997
997
  break;
998
998
  case "group":
@@ -1063,19 +1063,21 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
1063
1063
  schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
1064
1064
  return schema;
1065
1065
  }
1066
- const byTarget = /* @__PURE__ */ new Map();
1067
- for (const c of pathConstraints) {
1068
- const target = c.path?.segments[0];
1069
- if (!target) continue;
1070
- const group = byTarget.get(target) ?? [];
1071
- group.push(c);
1072
- byTarget.set(target, group);
1073
- }
1074
- const propertyOverrides = {};
1075
- for (const [target, constraints] of byTarget) {
1076
- const subSchema = {};
1077
- applyConstraints(subSchema, constraints, ctx);
1078
- propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
1066
+ const propertyOverrides = buildPropertyOverrides(pathConstraints, typeNode, ctx);
1067
+ const nullableValueBranch = getNullableUnionValueSchema(schema);
1068
+ if (nullableValueBranch !== void 0) {
1069
+ const updatedNullableValueBranch = applyPathTargetedConstraints(
1070
+ nullableValueBranch,
1071
+ pathConstraints,
1072
+ ctx,
1073
+ resolveTraversableTypeNode(typeNode, ctx)
1074
+ );
1075
+ if (schema.oneOf !== void 0) {
1076
+ schema.oneOf = schema.oneOf.map(
1077
+ (branch) => branch === nullableValueBranch ? updatedNullableValueBranch : branch
1078
+ );
1079
+ }
1080
+ return schema;
1079
1081
  }
1080
1082
  if (schema.$ref) {
1081
1083
  const { $ref, ...rest } = schema;
@@ -1090,7 +1092,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
1090
1092
  const missingOverrides = {};
1091
1093
  for (const [target, overrideSchema] of Object.entries(propertyOverrides)) {
1092
1094
  if (schema.properties[target]) {
1093
- Object.assign(schema.properties[target], overrideSchema);
1095
+ mergeSchemaOverride(schema.properties[target], overrideSchema);
1094
1096
  } else {
1095
1097
  missingOverrides[target] = overrideSchema;
1096
1098
  }
@@ -1164,7 +1166,7 @@ function generateObjectType(type, ctx) {
1164
1166
  const properties = {};
1165
1167
  const required = [];
1166
1168
  for (const prop of type.properties) {
1167
- const propertyName = getSerializedName(prop.name, prop.metadata);
1169
+ const propertyName = getSerializedObjectPropertyName(prop);
1168
1170
  properties[propertyName] = generatePropertySchema(prop, ctx);
1169
1171
  if (!prop.optional) {
1170
1172
  required.push(propertyName);
@@ -1218,7 +1220,16 @@ function isNullableUnion(type) {
1218
1220
  return nullCount === 1;
1219
1221
  }
1220
1222
  function generateReferenceType(type, ctx) {
1221
- return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
1223
+ return { $ref: `#/$defs/${getSerializedTypeName(type.name, ctx)}` };
1224
+ }
1225
+ function getSerializedFieldName(field) {
1226
+ return getSerializedName(field.name, field.metadata);
1227
+ }
1228
+ function getSerializedObjectPropertyName(property) {
1229
+ return getSerializedName(property.name, property.metadata);
1230
+ }
1231
+ function getSerializedTypeName(logicalName, ctx) {
1232
+ return ctx.typeNameMap[logicalName] ?? logicalName;
1222
1233
  }
1223
1234
  function applyResolvedMetadata(schema, metadata) {
1224
1235
  const displayName = getDisplayName(metadata);
@@ -1229,17 +1240,148 @@ function applyResolvedMetadata(schema, metadata) {
1229
1240
  function resolveReferencedType(type, ctx) {
1230
1241
  return ctx.typeRegistry[type.name]?.type;
1231
1242
  }
1243
+ function dereferenceTypeNode(typeNode, ctx) {
1244
+ if (typeNode?.kind !== "reference") {
1245
+ return typeNode;
1246
+ }
1247
+ return resolveReferencedType(typeNode, ctx);
1248
+ }
1249
+ function unwrapNullableTypeNode(typeNode) {
1250
+ if (typeNode?.kind !== "union" || !isNullableUnion(typeNode)) {
1251
+ return typeNode;
1252
+ }
1253
+ return typeNode.members.find(
1254
+ (member) => !(member.kind === "primitive" && member.primitiveKind === "null")
1255
+ );
1256
+ }
1257
+ function resolveTraversableTypeNode(typeNode, ctx) {
1258
+ const dereferenced = dereferenceTypeNode(typeNode, ctx);
1259
+ const unwrapped = unwrapNullableTypeNode(dereferenced);
1260
+ if (unwrapped !== dereferenced) {
1261
+ return resolveTraversableTypeNode(unwrapped, ctx);
1262
+ }
1263
+ return dereferenced;
1264
+ }
1232
1265
  function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
1233
- if (typeNode?.kind === "object") {
1234
- const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
1235
- return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
1266
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1267
+ if (effectiveType?.kind === "array") {
1268
+ return resolveSerializedPropertyName(logicalName, effectiveType.items, ctx);
1236
1269
  }
1237
- if (typeNode?.kind === "reference") {
1238
- const referencedType = resolveReferencedType(typeNode, ctx);
1239
- return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
1270
+ if (effectiveType?.kind === "object") {
1271
+ const property = effectiveType.properties.find((candidate) => candidate.name === logicalName);
1272
+ return property === void 0 ? logicalName : getSerializedObjectPropertyName(property);
1240
1273
  }
1241
1274
  return logicalName;
1242
1275
  }
1276
+ function resolveTargetTypeNode(logicalName, typeNode, ctx) {
1277
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1278
+ if (effectiveType?.kind === "array") {
1279
+ return resolveTargetTypeNode(logicalName, effectiveType.items, ctx);
1280
+ }
1281
+ if (effectiveType?.kind !== "object") {
1282
+ return void 0;
1283
+ }
1284
+ return effectiveType.properties.find((candidate) => candidate.name === logicalName)?.type;
1285
+ }
1286
+ function buildPropertyOverrides(pathConstraints, typeNode, ctx) {
1287
+ const byTarget = /* @__PURE__ */ new Map();
1288
+ for (const constraint of pathConstraints) {
1289
+ const target = constraint.path?.segments[0];
1290
+ if (!target) {
1291
+ continue;
1292
+ }
1293
+ const grouped = byTarget.get(target) ?? [];
1294
+ grouped.push(constraint);
1295
+ byTarget.set(target, grouped);
1296
+ }
1297
+ const overrides = {};
1298
+ for (const [target, constraints] of byTarget) {
1299
+ overrides[resolveSerializedPropertyName(target, typeNode, ctx)] = buildPathOverrideSchema(
1300
+ constraints.map(stripLeadingPathSegment),
1301
+ resolveTargetTypeNode(target, typeNode, ctx),
1302
+ ctx
1303
+ );
1304
+ }
1305
+ return overrides;
1306
+ }
1307
+ function buildPathOverrideSchema(constraints, typeNode, ctx) {
1308
+ const schema = {};
1309
+ const directConstraints = [];
1310
+ const nestedConstraints = [];
1311
+ for (const constraint of constraints) {
1312
+ if (constraint.path === void 0 || constraint.path.segments.length === 0) {
1313
+ directConstraints.push(constraint);
1314
+ } else {
1315
+ nestedConstraints.push(constraint);
1316
+ }
1317
+ }
1318
+ applyConstraints(schema, directConstraints, ctx);
1319
+ if (nestedConstraints.length === 0) {
1320
+ return schema;
1321
+ }
1322
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1323
+ if (effectiveType?.kind === "array") {
1324
+ schema.items = buildPathOverrideSchema(nestedConstraints, effectiveType.items, ctx);
1325
+ return schema;
1326
+ }
1327
+ schema.properties = buildPropertyOverrides(nestedConstraints, effectiveType, ctx);
1328
+ return schema;
1329
+ }
1330
+ function mergeSchemaOverride(target, override) {
1331
+ const nullableValueBranch = getNullableUnionValueSchema(target);
1332
+ if (nullableValueBranch !== void 0) {
1333
+ mergeSchemaOverride(nullableValueBranch, override);
1334
+ return;
1335
+ }
1336
+ if (override.properties !== void 0) {
1337
+ const mergedProperties = target.properties ?? {};
1338
+ for (const [name, propertyOverride] of Object.entries(override.properties)) {
1339
+ const existing = mergedProperties[name];
1340
+ if (existing === void 0) {
1341
+ mergedProperties[name] = propertyOverride;
1342
+ } else {
1343
+ mergeSchemaOverride(existing, propertyOverride);
1344
+ }
1345
+ }
1346
+ target.properties = mergedProperties;
1347
+ }
1348
+ if (override.items !== void 0) {
1349
+ if (target.items === void 0) {
1350
+ target.items = override.items;
1351
+ } else {
1352
+ mergeSchemaOverride(target.items, override.items);
1353
+ }
1354
+ }
1355
+ for (const [key, value] of Object.entries(override)) {
1356
+ if (key === "properties" || key === "items") {
1357
+ continue;
1358
+ }
1359
+ target[key] = value;
1360
+ }
1361
+ }
1362
+ function stripLeadingPathSegment(constraint) {
1363
+ const segments = constraint.path?.segments;
1364
+ if (segments === void 0 || segments.length === 0) {
1365
+ return constraint;
1366
+ }
1367
+ const [, ...rest] = segments;
1368
+ if (rest.length === 0) {
1369
+ const { path: _path, ...stripped } = constraint;
1370
+ return stripped;
1371
+ }
1372
+ return {
1373
+ ...constraint,
1374
+ path: { segments: rest }
1375
+ };
1376
+ }
1377
+ function getNullableUnionValueSchema(schema) {
1378
+ if (schema.oneOf?.length !== 2) {
1379
+ return void 0;
1380
+ }
1381
+ const valueSchema = schema.oneOf.find((branch) => branch.type !== "null");
1382
+ const nullSchema = schema.oneOf.find((branch) => branch.type === "null");
1383
+ return valueSchema !== void 0 && nullSchema !== void 0 ? valueSchema : void 0;
1384
+ }
1243
1385
  function generateDynamicType(type) {
1244
1386
  if (type.dynamicKind === "enum") {
1245
1387
  const schema = {