@makehq/forman-schema 1.9.3 → 1.9.5

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
@@ -524,6 +524,7 @@ function udtspecCollapse(field) {
524
524
  }
525
525
 
526
526
  // src/forman.ts
527
+ var EMPTY_OPTION_DESCRIPTION = "Optional field, can be left empty.";
527
528
  var SchemaConversionError = class extends Error {
528
529
  /** Field that caused the error */
529
530
  field;
@@ -886,11 +887,10 @@ function handleSelectOrPathType(field, result, context) {
886
887
  const placeholder = field.options.placeholder;
887
888
  if (isObject(placeholder) && placeholder.nested) {
888
889
  (options ||= []).push({
889
- value: null,
890
+ value: "",
890
891
  label: placeholder.label,
891
892
  nested: placeholder.nested
892
893
  });
893
- result.type = [result.type, "null"];
894
894
  }
895
895
  }
896
896
  if (options?.some((option) => {
@@ -944,6 +944,19 @@ function handleSelectOrPathType(field, result, context) {
944
944
  result.enum = (options || []).map((option) => option.value);
945
945
  }
946
946
  }
947
+ if (!field.required) {
948
+ if (result.enum && !result.enum.includes("")) {
949
+ result.enum = ["", ...result.enum];
950
+ } else if (result.oneOf && !result.oneOf.some((entry) => isObject(entry) && entry.const === "")) {
951
+ result.oneOf = [{ title: "Empty", const: "" }, ...result.oneOf];
952
+ }
953
+ if (result.default === void 0) {
954
+ result.default = "";
955
+ }
956
+ if (!result.description) {
957
+ result.description = EMPTY_OPTION_DESCRIPTION;
958
+ }
959
+ }
947
960
  result = handleNestedWithDomain(field, nested, domain, result, context);
948
961
  if (field.rpc) result = processRpcDirective(field, result, context);
949
962
  return result;
@@ -1048,6 +1061,11 @@ function hasPlaceholderNested(field) {
1048
1061
  const placeholder = field.options.placeholder;
1049
1062
  return isObject(placeholder) && placeholder.nested != null;
1050
1063
  }
1064
+ function extractNestedFromField(field) {
1065
+ if (field.nested) return field.nested;
1066
+ if (isObject(field.options) && field.options.nested) return field.options.nested;
1067
+ return void 0;
1068
+ }
1051
1069
  var FORMAN_TYPE_MAP2 = {
1052
1070
  account: "number",
1053
1071
  hook: "number",
@@ -1202,7 +1220,7 @@ async function validateFormanValue(value, field, context) {
1202
1220
  ]
1203
1221
  };
1204
1222
  }
1205
- if (value == null) {
1223
+ if (value == null || value === "") {
1206
1224
  if (normalizedField.type === "select" && hasPlaceholderNested(normalizedField)) {
1207
1225
  return handleSelectType(value, normalizedField, context);
1208
1226
  }
@@ -1245,6 +1263,10 @@ async function validateFormanValue(value, field, context) {
1245
1263
  state: { mode: "edit" }
1246
1264
  });
1247
1265
  }
1266
+ const nested = extractNestedFromField(normalizedField);
1267
+ if (nested) {
1268
+ return handleNestedFields(nested, value, normalizedField, context);
1269
+ }
1248
1270
  return {
1249
1271
  valid: true,
1250
1272
  errors: []
@@ -1602,7 +1624,7 @@ async function handlePathType(value, field, context) {
1602
1624
  async function handleSelectType(value, field, context) {
1603
1625
  const errors = [];
1604
1626
  let optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
1605
- let nested = field.nested ? field.nested : isObject(field.options) ? field.options.nested : void 0;
1627
+ let nested = extractNestedFromField(field);
1606
1628
  if (typeof optionsOrGroups === "string") {
1607
1629
  try {
1608
1630
  const resolved = await context.resolveRemote(optionsOrGroups, context);
@@ -1678,7 +1700,7 @@ async function handleSelectType(value, field, context) {
1678
1700
  });
1679
1701
  }
1680
1702
  }
1681
- } else if (value == null && hasPlaceholderNested(field)) {
1703
+ } else if ((value == null || value === "") && hasPlaceholderNested(field)) {
1682
1704
  const placeholder = field.options.placeholder;
1683
1705
  if (placeholder.label) {
1684
1706
  context.roots[context.domain].fieldStates.push({
@@ -1836,8 +1858,9 @@ async function handlePrimitiveType2(value, field, context) {
1836
1858
  }
1837
1859
  }
1838
1860
  }
1839
- if (field.nested) {
1840
- const result = await handleNestedFields(field.nested, value, field, context);
1861
+ const nested = extractNestedFromField(field);
1862
+ if (nested) {
1863
+ const result = await handleNestedFields(nested, value, field, context);
1841
1864
  errors.push(...result.errors);
1842
1865
  }
1843
1866
  return {
@@ -1909,10 +1932,11 @@ function toFormanSchema(field) {
1909
1932
  case "string":
1910
1933
  const pathInfo = Object.getOwnPropertyDescriptor(field, "x-path");
1911
1934
  if (pathInfo) {
1935
+ const help = field.description === EMPTY_OPTION_DESCRIPTION ? void 0 : noEmpty(field.description);
1912
1936
  return {
1913
1937
  type: pathInfo.value.type,
1914
1938
  label: noEmpty(field.title),
1915
- help: noEmpty(field.description),
1939
+ help,
1916
1940
  options: {
1917
1941
  store: Object.getOwnPropertyDescriptor(field, "x-fetch")?.value,
1918
1942
  showRoot: pathInfo.value.showRoot,
@@ -1921,18 +1945,20 @@ function toFormanSchema(field) {
1921
1945
  };
1922
1946
  }
1923
1947
  if (field.enum) {
1948
+ const help = field.description === EMPTY_OPTION_DESCRIPTION ? void 0 : noEmpty(field.description);
1924
1949
  return {
1925
1950
  type: "select",
1926
1951
  label: noEmpty(field.title),
1927
- help: noEmpty(field.description),
1928
- options: field.enum.map((value) => ({ value }))
1952
+ help,
1953
+ options: field.enum.filter((value) => value !== "").map((value) => ({ value }))
1929
1954
  };
1930
1955
  } else if (field.oneOf) {
1956
+ const help = field.description === EMPTY_OPTION_DESCRIPTION ? void 0 : noEmpty(field.description);
1931
1957
  return {
1932
1958
  type: "select",
1933
1959
  label: noEmpty(field.title),
1934
- help: noEmpty(field.description),
1935
- options: field.oneOf.filter((value) => value).map((value) => ({ value: value.const }))
1960
+ help,
1961
+ options: field.oneOf.filter((value) => value && value.const !== "").map((value) => ({ value: value.const }))
1936
1962
  };
1937
1963
  }
1938
1964
  let textField = {
package/dist/index.js CHANGED
@@ -495,6 +495,7 @@ function udtspecCollapse(field) {
495
495
  }
496
496
 
497
497
  // src/forman.ts
498
+ var EMPTY_OPTION_DESCRIPTION = "Optional field, can be left empty.";
498
499
  var SchemaConversionError = class extends Error {
499
500
  /** Field that caused the error */
500
501
  field;
@@ -857,11 +858,10 @@ function handleSelectOrPathType(field, result, context) {
857
858
  const placeholder = field.options.placeholder;
858
859
  if (isObject(placeholder) && placeholder.nested) {
859
860
  (options ||= []).push({
860
- value: null,
861
+ value: "",
861
862
  label: placeholder.label,
862
863
  nested: placeholder.nested
863
864
  });
864
- result.type = [result.type, "null"];
865
865
  }
866
866
  }
867
867
  if (options?.some((option) => {
@@ -915,6 +915,19 @@ function handleSelectOrPathType(field, result, context) {
915
915
  result.enum = (options || []).map((option) => option.value);
916
916
  }
917
917
  }
918
+ if (!field.required) {
919
+ if (result.enum && !result.enum.includes("")) {
920
+ result.enum = ["", ...result.enum];
921
+ } else if (result.oneOf && !result.oneOf.some((entry) => isObject(entry) && entry.const === "")) {
922
+ result.oneOf = [{ title: "Empty", const: "" }, ...result.oneOf];
923
+ }
924
+ if (result.default === void 0) {
925
+ result.default = "";
926
+ }
927
+ if (!result.description) {
928
+ result.description = EMPTY_OPTION_DESCRIPTION;
929
+ }
930
+ }
918
931
  result = handleNestedWithDomain(field, nested, domain, result, context);
919
932
  if (field.rpc) result = processRpcDirective(field, result, context);
920
933
  return result;
@@ -1019,6 +1032,11 @@ function hasPlaceholderNested(field) {
1019
1032
  const placeholder = field.options.placeholder;
1020
1033
  return isObject(placeholder) && placeholder.nested != null;
1021
1034
  }
1035
+ function extractNestedFromField(field) {
1036
+ if (field.nested) return field.nested;
1037
+ if (isObject(field.options) && field.options.nested) return field.options.nested;
1038
+ return void 0;
1039
+ }
1022
1040
  var FORMAN_TYPE_MAP2 = {
1023
1041
  account: "number",
1024
1042
  hook: "number",
@@ -1173,7 +1191,7 @@ async function validateFormanValue(value, field, context) {
1173
1191
  ]
1174
1192
  };
1175
1193
  }
1176
- if (value == null) {
1194
+ if (value == null || value === "") {
1177
1195
  if (normalizedField.type === "select" && hasPlaceholderNested(normalizedField)) {
1178
1196
  return handleSelectType(value, normalizedField, context);
1179
1197
  }
@@ -1216,6 +1234,10 @@ async function validateFormanValue(value, field, context) {
1216
1234
  state: { mode: "edit" }
1217
1235
  });
1218
1236
  }
1237
+ const nested = extractNestedFromField(normalizedField);
1238
+ if (nested) {
1239
+ return handleNestedFields(nested, value, normalizedField, context);
1240
+ }
1219
1241
  return {
1220
1242
  valid: true,
1221
1243
  errors: []
@@ -1573,7 +1595,7 @@ async function handlePathType(value, field, context) {
1573
1595
  async function handleSelectType(value, field, context) {
1574
1596
  const errors = [];
1575
1597
  let optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
1576
- let nested = field.nested ? field.nested : isObject(field.options) ? field.options.nested : void 0;
1598
+ let nested = extractNestedFromField(field);
1577
1599
  if (typeof optionsOrGroups === "string") {
1578
1600
  try {
1579
1601
  const resolved = await context.resolveRemote(optionsOrGroups, context);
@@ -1649,7 +1671,7 @@ async function handleSelectType(value, field, context) {
1649
1671
  });
1650
1672
  }
1651
1673
  }
1652
- } else if (value == null && hasPlaceholderNested(field)) {
1674
+ } else if ((value == null || value === "") && hasPlaceholderNested(field)) {
1653
1675
  const placeholder = field.options.placeholder;
1654
1676
  if (placeholder.label) {
1655
1677
  context.roots[context.domain].fieldStates.push({
@@ -1807,8 +1829,9 @@ async function handlePrimitiveType2(value, field, context) {
1807
1829
  }
1808
1830
  }
1809
1831
  }
1810
- if (field.nested) {
1811
- const result = await handleNestedFields(field.nested, value, field, context);
1832
+ const nested = extractNestedFromField(field);
1833
+ if (nested) {
1834
+ const result = await handleNestedFields(nested, value, field, context);
1812
1835
  errors.push(...result.errors);
1813
1836
  }
1814
1837
  return {
@@ -1880,10 +1903,11 @@ function toFormanSchema(field) {
1880
1903
  case "string":
1881
1904
  const pathInfo = Object.getOwnPropertyDescriptor(field, "x-path");
1882
1905
  if (pathInfo) {
1906
+ const help = field.description === EMPTY_OPTION_DESCRIPTION ? void 0 : noEmpty(field.description);
1883
1907
  return {
1884
1908
  type: pathInfo.value.type,
1885
1909
  label: noEmpty(field.title),
1886
- help: noEmpty(field.description),
1910
+ help,
1887
1911
  options: {
1888
1912
  store: Object.getOwnPropertyDescriptor(field, "x-fetch")?.value,
1889
1913
  showRoot: pathInfo.value.showRoot,
@@ -1892,18 +1916,20 @@ function toFormanSchema(field) {
1892
1916
  };
1893
1917
  }
1894
1918
  if (field.enum) {
1919
+ const help = field.description === EMPTY_OPTION_DESCRIPTION ? void 0 : noEmpty(field.description);
1895
1920
  return {
1896
1921
  type: "select",
1897
1922
  label: noEmpty(field.title),
1898
- help: noEmpty(field.description),
1899
- options: field.enum.map((value) => ({ value }))
1923
+ help,
1924
+ options: field.enum.filter((value) => value !== "").map((value) => ({ value }))
1900
1925
  };
1901
1926
  } else if (field.oneOf) {
1927
+ const help = field.description === EMPTY_OPTION_DESCRIPTION ? void 0 : noEmpty(field.description);
1902
1928
  return {
1903
1929
  type: "select",
1904
1930
  label: noEmpty(field.title),
1905
- help: noEmpty(field.description),
1906
- options: field.oneOf.filter((value) => value).map((value) => ({ value: value.const }))
1931
+ help,
1932
+ options: field.oneOf.filter((value) => value && value.const !== "").map((value) => ({ value: value.const }))
1907
1933
  };
1908
1934
  }
1909
1935
  let textField = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makehq/forman-schema",
3
- "version": "1.9.3",
3
+ "version": "1.9.5",
4
4
  "description": "Forman Schema Tools",
5
5
  "license": "MIT",
6
6
  "author": "Make",