@makehq/forman-schema 1.9.5 → 1.10.0

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
@@ -1066,6 +1066,47 @@ function extractNestedFromField(field) {
1066
1066
  if (isObject(field.options) && field.options.nested) return field.options.nested;
1067
1067
  return void 0;
1068
1068
  }
1069
+ var SCHEMA_STRIP_KEYS = [
1070
+ "nested",
1071
+ "options",
1072
+ "help",
1073
+ "rpc",
1074
+ "sort",
1075
+ "disabled",
1076
+ "multiline",
1077
+ "tags",
1078
+ "extension",
1079
+ "codepage",
1080
+ "logic"
1081
+ ];
1082
+ function clampFieldForSchema(field) {
1083
+ const clamped = { ...field };
1084
+ for (const key of SCHEMA_STRIP_KEYS) {
1085
+ delete clamped[key];
1086
+ }
1087
+ if (field.spec != null) {
1088
+ clamped.spec = Array.isArray(field.spec) ? field.spec.map(clampFieldForSchema) : clampFieldForSchema(field.spec);
1089
+ }
1090
+ if (field.type === "select" && !field.validate?.enum) {
1091
+ const rawOptions = isObject(field.options) ? field.options.store : field.options;
1092
+ if (Array.isArray(rawOptions)) {
1093
+ const values = [];
1094
+ for (const item of rawOptions) {
1095
+ if ("options" in item) {
1096
+ for (const opt of item.options) {
1097
+ values.push(opt.value);
1098
+ }
1099
+ } else if ("value" in item) {
1100
+ values.push(item.value);
1101
+ }
1102
+ }
1103
+ if (values.length > 0) {
1104
+ clamped.validate = { ...clamped.validate, enum: values.map(String) };
1105
+ }
1106
+ }
1107
+ }
1108
+ return clamped;
1109
+ }
1069
1110
  var FORMAN_TYPE_MAP2 = {
1070
1111
  account: "number",
1071
1112
  hook: "number",
@@ -1115,6 +1156,7 @@ async function validateFormanWithDomainsInternal(domains, options) {
1115
1156
  acc[domain] = {
1116
1157
  seenFields: /* @__PURE__ */ new Set(),
1117
1158
  fieldStates: [],
1159
+ schemaFields: [],
1118
1160
  validateFields: (fields, context) => {
1119
1161
  return validateFormanValue(
1120
1162
  domains[domain].values,
@@ -1150,6 +1192,7 @@ async function validateFormanWithDomainsInternal(domains, options) {
1150
1192
  path: [],
1151
1193
  tail: [],
1152
1194
  strict: options?.strict === true,
1195
+ domainAliases: options?.domainAliases ?? {},
1153
1196
  validateNestedFields: () => {
1154
1197
  throw new Error("Cannot validate nested fields without parent field.");
1155
1198
  },
@@ -1192,12 +1235,25 @@ async function validateFormanWithDomainsInternal(domains, options) {
1192
1235
  }
1193
1236
  }
1194
1237
  }
1238
+ if (options?.strict) {
1239
+ for (const domain of Object.keys(domains)) {
1240
+ if (!domains[domain]) continue;
1241
+ const root = roots[domain];
1242
+ for (const key of Object.keys(domains[domain].values)) {
1243
+ if (!root.seenFields.has(key)) {
1244
+ root.seenFields.add(key);
1245
+ errors.push({ domain, path: "", message: `Unknown field '${key}'.` });
1246
+ }
1247
+ }
1248
+ }
1249
+ }
1195
1250
  return {
1196
1251
  valid: errors.length === 0,
1197
1252
  errors,
1198
1253
  states: errors.length === 0 && options?.states ? buildRestoreStructure(
1199
1254
  Object.keys(domains).map((domain) => roots[domain].fieldStates.map((state) => ({ domain, ...state }))).flat()
1200
- ) : void 0
1255
+ ) : void 0,
1256
+ schemas: errors.length === 0 && options?.schemas ? Object.fromEntries(Object.keys(domains).map((domain) => [domain, roots[domain].schemaFields])) : void 0
1201
1257
  };
1202
1258
  }
1203
1259
  async function validateFormanValue(value, field, context) {
@@ -1346,6 +1402,9 @@ async function handleCollectionType2(value, field, context) {
1346
1402
  continue;
1347
1403
  }
1348
1404
  if (context.strict && !seen.has(subField.name)) seen.add(subField.name);
1405
+ if (path.length === 0) {
1406
+ context.roots[context.domain].schemaFields.push(clampFieldForSchema(subField));
1407
+ }
1349
1408
  const result = await validateFormanValue(value[subField.name], subField, {
1350
1409
  ...context,
1351
1410
  path: [...path, subField.name],
@@ -1363,6 +1422,9 @@ async function handleCollectionType2(value, field, context) {
1363
1422
  continue;
1364
1423
  }
1365
1424
  if (context2.strict && !seen.has(subField2.name)) seen.add(subField2.name);
1425
+ if (path.length === 0) {
1426
+ context2.roots[context2.domain].schemaFields.push(clampFieldForSchema(subField2));
1427
+ }
1366
1428
  const result2 = await validateFormanValue(value[subField2.name], subField2, {
1367
1429
  ...context2,
1368
1430
  path: [...path, subField2.name]
@@ -1374,7 +1436,7 @@ async function handleCollectionType2(value, field, context) {
1374
1436
  errors.push(...result.errors);
1375
1437
  }
1376
1438
  }
1377
- if (context.strict) {
1439
+ if (context.strict && context.path.length > 0) {
1378
1440
  for (const key of Object.keys(value)) {
1379
1441
  if (!seen.has(key)) {
1380
1442
  seen.add(key);
@@ -1788,15 +1850,16 @@ async function handleNestedFields(nested, value, field, context) {
1788
1850
  }
1789
1851
  store = resolvedStore;
1790
1852
  }
1791
- if (store && domain && domain !== context.domain) {
1792
- if (!context.roots[domain]) {
1853
+ const resolvedDomain = domain ? context.domainAliases[domain] ?? domain : domain;
1854
+ if (store && resolvedDomain && resolvedDomain !== context.domain) {
1855
+ if (!context.roots[resolvedDomain]) {
1793
1856
  errors.push({
1794
1857
  domain: context.domain,
1795
1858
  path: context.path.join("."),
1796
- message: `Unable to process nested fields: Domain '${domain}' not found.`
1859
+ message: resolvedDomain !== domain ? `Unable to process nested fields: Resolved domain '${resolvedDomain}' (from alias '${domain}') not found.` : `Unable to process nested fields: Domain '${domain}' not found.`
1797
1860
  });
1798
1861
  } else {
1799
- const result = await context.roots[domain].validateFields(store, context);
1862
+ const result = await context.roots[resolvedDomain].validateFields(store, context);
1800
1863
  errors.push(...result.errors);
1801
1864
  }
1802
1865
  } else if (store) {
package/dist/index.d.cts CHANGED
@@ -199,6 +199,8 @@ type FormanValidationResult = {
199
199
  }[];
200
200
  /** States of fields grouped by domain */
201
201
  states?: Record<string, FormanSchemaFieldState>;
202
+ /** Resolved schema fields per domain */
203
+ schemas?: Record<string, FormanSchemaField[]>;
202
204
  };
203
205
  type FormanSchemaFieldState = {
204
206
  mode?: 'chose' | 'edit';
@@ -214,8 +216,12 @@ type FormanValidationOptions = {
214
216
  strict?: boolean;
215
217
  /** Whether to generate states for fields */
216
218
  states?: boolean;
219
+ /** Whether to collect resolved schema fields per domain */
220
+ schemas?: boolean;
217
221
  /** Remote resource resolver */
218
222
  resolveRemote?(path: string, data: Record<string, unknown>): Promise<unknown>;
223
+ /** Maps domain names used in nested.domain to actual domain keys passed to validateFormanWithDomains */
224
+ domainAliases?: Record<string, string>;
219
225
  };
220
226
 
221
227
  /**
package/dist/index.d.ts CHANGED
@@ -199,6 +199,8 @@ type FormanValidationResult = {
199
199
  }[];
200
200
  /** States of fields grouped by domain */
201
201
  states?: Record<string, FormanSchemaFieldState>;
202
+ /** Resolved schema fields per domain */
203
+ schemas?: Record<string, FormanSchemaField[]>;
202
204
  };
203
205
  type FormanSchemaFieldState = {
204
206
  mode?: 'chose' | 'edit';
@@ -214,8 +216,12 @@ type FormanValidationOptions = {
214
216
  strict?: boolean;
215
217
  /** Whether to generate states for fields */
216
218
  states?: boolean;
219
+ /** Whether to collect resolved schema fields per domain */
220
+ schemas?: boolean;
217
221
  /** Remote resource resolver */
218
222
  resolveRemote?(path: string, data: Record<string, unknown>): Promise<unknown>;
223
+ /** Maps domain names used in nested.domain to actual domain keys passed to validateFormanWithDomains */
224
+ domainAliases?: Record<string, string>;
219
225
  };
220
226
 
221
227
  /**
package/dist/index.js CHANGED
@@ -1037,6 +1037,47 @@ function extractNestedFromField(field) {
1037
1037
  if (isObject(field.options) && field.options.nested) return field.options.nested;
1038
1038
  return void 0;
1039
1039
  }
1040
+ var SCHEMA_STRIP_KEYS = [
1041
+ "nested",
1042
+ "options",
1043
+ "help",
1044
+ "rpc",
1045
+ "sort",
1046
+ "disabled",
1047
+ "multiline",
1048
+ "tags",
1049
+ "extension",
1050
+ "codepage",
1051
+ "logic"
1052
+ ];
1053
+ function clampFieldForSchema(field) {
1054
+ const clamped = { ...field };
1055
+ for (const key of SCHEMA_STRIP_KEYS) {
1056
+ delete clamped[key];
1057
+ }
1058
+ if (field.spec != null) {
1059
+ clamped.spec = Array.isArray(field.spec) ? field.spec.map(clampFieldForSchema) : clampFieldForSchema(field.spec);
1060
+ }
1061
+ if (field.type === "select" && !field.validate?.enum) {
1062
+ const rawOptions = isObject(field.options) ? field.options.store : field.options;
1063
+ if (Array.isArray(rawOptions)) {
1064
+ const values = [];
1065
+ for (const item of rawOptions) {
1066
+ if ("options" in item) {
1067
+ for (const opt of item.options) {
1068
+ values.push(opt.value);
1069
+ }
1070
+ } else if ("value" in item) {
1071
+ values.push(item.value);
1072
+ }
1073
+ }
1074
+ if (values.length > 0) {
1075
+ clamped.validate = { ...clamped.validate, enum: values.map(String) };
1076
+ }
1077
+ }
1078
+ }
1079
+ return clamped;
1080
+ }
1040
1081
  var FORMAN_TYPE_MAP2 = {
1041
1082
  account: "number",
1042
1083
  hook: "number",
@@ -1086,6 +1127,7 @@ async function validateFormanWithDomainsInternal(domains, options) {
1086
1127
  acc[domain] = {
1087
1128
  seenFields: /* @__PURE__ */ new Set(),
1088
1129
  fieldStates: [],
1130
+ schemaFields: [],
1089
1131
  validateFields: (fields, context) => {
1090
1132
  return validateFormanValue(
1091
1133
  domains[domain].values,
@@ -1121,6 +1163,7 @@ async function validateFormanWithDomainsInternal(domains, options) {
1121
1163
  path: [],
1122
1164
  tail: [],
1123
1165
  strict: options?.strict === true,
1166
+ domainAliases: options?.domainAliases ?? {},
1124
1167
  validateNestedFields: () => {
1125
1168
  throw new Error("Cannot validate nested fields without parent field.");
1126
1169
  },
@@ -1163,12 +1206,25 @@ async function validateFormanWithDomainsInternal(domains, options) {
1163
1206
  }
1164
1207
  }
1165
1208
  }
1209
+ if (options?.strict) {
1210
+ for (const domain of Object.keys(domains)) {
1211
+ if (!domains[domain]) continue;
1212
+ const root = roots[domain];
1213
+ for (const key of Object.keys(domains[domain].values)) {
1214
+ if (!root.seenFields.has(key)) {
1215
+ root.seenFields.add(key);
1216
+ errors.push({ domain, path: "", message: `Unknown field '${key}'.` });
1217
+ }
1218
+ }
1219
+ }
1220
+ }
1166
1221
  return {
1167
1222
  valid: errors.length === 0,
1168
1223
  errors,
1169
1224
  states: errors.length === 0 && options?.states ? buildRestoreStructure(
1170
1225
  Object.keys(domains).map((domain) => roots[domain].fieldStates.map((state) => ({ domain, ...state }))).flat()
1171
- ) : void 0
1226
+ ) : void 0,
1227
+ schemas: errors.length === 0 && options?.schemas ? Object.fromEntries(Object.keys(domains).map((domain) => [domain, roots[domain].schemaFields])) : void 0
1172
1228
  };
1173
1229
  }
1174
1230
  async function validateFormanValue(value, field, context) {
@@ -1317,6 +1373,9 @@ async function handleCollectionType2(value, field, context) {
1317
1373
  continue;
1318
1374
  }
1319
1375
  if (context.strict && !seen.has(subField.name)) seen.add(subField.name);
1376
+ if (path.length === 0) {
1377
+ context.roots[context.domain].schemaFields.push(clampFieldForSchema(subField));
1378
+ }
1320
1379
  const result = await validateFormanValue(value[subField.name], subField, {
1321
1380
  ...context,
1322
1381
  path: [...path, subField.name],
@@ -1334,6 +1393,9 @@ async function handleCollectionType2(value, field, context) {
1334
1393
  continue;
1335
1394
  }
1336
1395
  if (context2.strict && !seen.has(subField2.name)) seen.add(subField2.name);
1396
+ if (path.length === 0) {
1397
+ context2.roots[context2.domain].schemaFields.push(clampFieldForSchema(subField2));
1398
+ }
1337
1399
  const result2 = await validateFormanValue(value[subField2.name], subField2, {
1338
1400
  ...context2,
1339
1401
  path: [...path, subField2.name]
@@ -1345,7 +1407,7 @@ async function handleCollectionType2(value, field, context) {
1345
1407
  errors.push(...result.errors);
1346
1408
  }
1347
1409
  }
1348
- if (context.strict) {
1410
+ if (context.strict && context.path.length > 0) {
1349
1411
  for (const key of Object.keys(value)) {
1350
1412
  if (!seen.has(key)) {
1351
1413
  seen.add(key);
@@ -1759,15 +1821,16 @@ async function handleNestedFields(nested, value, field, context) {
1759
1821
  }
1760
1822
  store = resolvedStore;
1761
1823
  }
1762
- if (store && domain && domain !== context.domain) {
1763
- if (!context.roots[domain]) {
1824
+ const resolvedDomain = domain ? context.domainAliases[domain] ?? domain : domain;
1825
+ if (store && resolvedDomain && resolvedDomain !== context.domain) {
1826
+ if (!context.roots[resolvedDomain]) {
1764
1827
  errors.push({
1765
1828
  domain: context.domain,
1766
1829
  path: context.path.join("."),
1767
- message: `Unable to process nested fields: Domain '${domain}' not found.`
1830
+ message: resolvedDomain !== domain ? `Unable to process nested fields: Resolved domain '${resolvedDomain}' (from alias '${domain}') not found.` : `Unable to process nested fields: Domain '${domain}' not found.`
1768
1831
  });
1769
1832
  } else {
1770
- const result = await context.roots[domain].validateFields(store, context);
1833
+ const result = await context.roots[resolvedDomain].validateFields(store, context);
1771
1834
  errors.push(...result.errors);
1772
1835
  }
1773
1836
  } else if (store) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makehq/forman-schema",
3
- "version": "1.9.5",
3
+ "version": "1.10.0",
4
4
  "description": "Forman Schema Tools",
5
5
  "license": "MIT",
6
6
  "author": "Make",