@pipelex/mthds-form 0.32.0 → 0.33.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/README.md CHANGED
@@ -4,7 +4,7 @@ The input form for MTHDS methods, as a library: a headless kernel that maps the
4
4
 
5
5
  It exists because "the form" is one problem that keeps being solved separately. A method declares its inputs; something has to decide what widget each input deserves, whether the Run button may light up, what shape the values take on the wire, and what a validation failure says to a person. That logic belongs in one place with tests around it, not copied into each surface that happens to need a form.
6
6
 
7
- ## Five entry points
7
+ ## Entry points
8
8
 
9
9
  ```ts
10
10
  import { buildRunFields, computeReadiness } from '@pipelex/mthds-form'; // headless
@@ -778,59 +778,77 @@ var COMPONENT_NAMES = [
778
778
  ...CUSTOM_COMPONENTS,
779
779
  ...PRODUCT_COMPONENTS
780
780
  ];
781
- var FRAME_EXCLUDED = {
782
- page: [...FRAME_COMPONENTS],
783
- composer: [
784
- "AppBar",
785
- "Footer",
786
- "Workspace",
787
- "Rail",
788
- "Cta",
789
- "Button",
790
- "Hero",
791
- "SummaryRow",
792
- "MthdsResult",
793
- "DataTable",
794
- "Metric",
795
- "Progress",
796
- // `TextList` stays: a composer may list short texts too, such as what a
797
- // run gives back.
798
- ...RESULT_COMPONENTS.filter((name) => name !== "TextList")
799
- ],
800
- result: [
801
- "AppBar",
802
- "Footer",
803
- "Workspace",
804
- "Rail",
805
- "Cta",
806
- "Button",
807
- "Hero",
808
- "MthdsField",
781
+ var FRAME_VOCABULARIES = {
782
+ composer: Object.freeze([
783
+ "Card",
784
+ "Stack",
785
+ "Grid",
786
+ "Separator",
787
+ "Heading",
788
+ "Text",
789
+ "Badge",
790
+ "Alert",
791
+ "Collapsible",
792
+ "Avatar",
809
793
  "Input",
810
794
  "Textarea",
811
795
  "Select",
812
796
  "Radio",
813
797
  "Checkbox",
814
798
  "Switch",
799
+ "Split",
800
+ "Tabs",
801
+ "Steps",
802
+ "Icon",
815
803
  "Segmented",
816
804
  "NumberInput",
817
- "Steps",
818
- ...COMPOSER_COMPONENTS
819
- ]
805
+ "MthdsField",
806
+ "Section",
807
+ ...COMPOSER_COMPONENTS,
808
+ "TextList"
809
+ ]),
810
+ result: Object.freeze([
811
+ "Card",
812
+ "Stack",
813
+ "Grid",
814
+ "Separator",
815
+ "Heading",
816
+ "Text",
817
+ "Badge",
818
+ "Alert",
819
+ "Collapsible",
820
+ "Progress",
821
+ "Avatar",
822
+ "Split",
823
+ "Tabs",
824
+ "Icon",
825
+ "MthdsResult",
826
+ "DataTable",
827
+ "Metric",
828
+ "Section",
829
+ "SummaryRow",
830
+ ...RESULT_COMPONENTS
831
+ ])
820
832
  };
833
+ var PAGE_VOCABULARY = Object.freeze(
834
+ COMPONENT_NAMES.filter((name) => !FRAME_COMPONENTS.includes(name))
835
+ );
821
836
  function componentNamesFor(frame) {
822
- const excluded = new Set(FRAME_EXCLUDED[frame]);
823
- const own = frame === "page" ? [] : [...FRAME_COMPONENTS];
824
- return [...COMPONENT_NAMES, ...own].filter((name) => !excluded.has(name));
837
+ return frame === "page" ? PAGE_VOCABULARY : FRAME_VOCABULARIES[frame];
825
838
  }
839
+ var frameCatalogs = /* @__PURE__ */ new Map();
826
840
  function frameCatalog(frame) {
827
- return {
841
+ const built = frameCatalogs.get(frame);
842
+ if (built) return built;
843
+ const made = Object.freeze({
828
844
  componentNames: componentNamesFor(frame),
829
845
  actionNames: catalog.actionNames,
830
846
  data: catalog.data,
831
847
  schema: catalog.schema,
832
848
  renders: COMPONENT_RENDERINGS
833
- };
849
+ });
850
+ frameCatalogs.set(frame, made);
851
+ return made;
834
852
  }
835
853
 
836
854
  // src/generative/props-signature.ts
@@ -917,18 +935,106 @@ ${JSON.stringify(catalogs)}`;
917
935
  }
918
936
 
919
937
  // src/generative/expressions.ts
920
- var EXPRESSION_KEYS = [
921
- "$state",
922
- "$item",
923
- "$index",
924
- "$bindState",
925
- "$bindItem",
926
- "$cond",
927
- "$computed",
928
- "$template"
929
- ];
930
938
  function isExpression(value) {
931
- return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).some((key) => EXPRESSION_KEYS.includes(key));
939
+ return expressionForm(value) !== void 0;
940
+ }
941
+ function expressionForm(value) {
942
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
943
+ const record = value;
944
+ if (typeof record.$state === "string") return "$state";
945
+ if (typeof record.$item === "string") return "$item";
946
+ if (record.$index === true) return "$index";
947
+ if (typeof record.$bindState === "string") return "$bindState";
948
+ if (typeof record.$bindItem === "string") return "$bindItem";
949
+ if ("$cond" in record && "$then" in record && "$else" in record) return "$cond";
950
+ if (typeof record.$computed === "string") return "$computed";
951
+ if (typeof record.$template === "string") return "$template";
952
+ return void 0;
953
+ }
954
+ function condBranches(value) {
955
+ if (expressionForm(value) !== "$cond") return void 0;
956
+ const record = value;
957
+ return [record.$then, record.$else];
958
+ }
959
+
960
+ // src/generative/share-amount.ts
961
+ function shareAmount(raw) {
962
+ if (typeof raw === "number") return raw;
963
+ if (typeof raw === "string" && raw.trim() !== "") return Number(raw);
964
+ return Number.NaN;
965
+ }
966
+
967
+ // src/generative/spec-shape.ts
968
+ function shown(value) {
969
+ if (Array.isArray(value)) return "a list";
970
+ if (typeof value === "bigint") return String(value);
971
+ if (typeof value === "symbol" || typeof value === "function") return `a ${typeof value}`;
972
+ try {
973
+ return String(JSON.stringify(value ?? null));
974
+ } catch {
975
+ return "a value that cannot be written out";
976
+ }
977
+ }
978
+ function isRecord(value) {
979
+ return typeof value === "object" && value !== null && !Array.isArray(value);
980
+ }
981
+ function specMalformation(spec) {
982
+ if (!isRecord(spec)) return `it is ${shown(spec)}, not a structure`;
983
+ if (!isRecord(spec.elements)) return 'it has no "elements" map';
984
+ for (const [key, element] of Object.entries(spec.elements)) {
985
+ const at = JSON.stringify(key);
986
+ if (!isRecord(element)) return `the element ${at} is not a structure`;
987
+ const { type, props, children, slots, repeat } = element;
988
+ if (typeof type !== "string") return `the element ${at} names no component "type"`;
989
+ if (props != null && !isRecord(props))
990
+ return `the element ${at} has "props" that are not a structure`;
991
+ if (children != null && !Array.isArray(children))
992
+ return `the element ${at} has "children" that are not a list`;
993
+ if (slots != null && (!isRecord(slots) || !Object.values(slots).every(Array.isArray))) {
994
+ return `the element ${at} has "slots" that are not lists`;
995
+ }
996
+ if (repeat !== void 0 && !repeatsOver(repeat)) {
997
+ return `the element ${at} has a "repeat" that names no "statePath"`;
998
+ }
999
+ }
1000
+ return void 0;
1001
+ }
1002
+ function repeatsOver(repeat) {
1003
+ if (!isRecord(repeat)) return false;
1004
+ const { statePath } = repeat;
1005
+ return typeof statePath === "string" || isRecord(statePath) && typeof statePath.$item === "string";
1006
+ }
1007
+ function malformedLine(what) {
1008
+ return `the layout is not a well-formed spec: ${what}`;
1009
+ }
1010
+ function checksFailedLine(error) {
1011
+ let said;
1012
+ try {
1013
+ said = String(error);
1014
+ } catch {
1015
+ said = "an error that cannot be written out";
1016
+ }
1017
+ return `the checks failed on this layout (${said}): a defect in the checks, which no repair of the layout can fix`;
1018
+ }
1019
+
1020
+ // src/generative/zod-definition.ts
1021
+ function definitionOf2(schema) {
1022
+ if (typeof schema !== "object" || schema === null) return void 0;
1023
+ return schema._zod?.def;
1024
+ }
1025
+ function definitionChain(schema) {
1026
+ const chain = [];
1027
+ let def = definitionOf2(schema);
1028
+ for (let depth = 0; depth < 8 && def; depth += 1) {
1029
+ chain.push(def);
1030
+ if (def.innerType === void 0) return chain;
1031
+ def = definitionOf2(def.innerType);
1032
+ }
1033
+ return [];
1034
+ }
1035
+ function unwrapped(schema) {
1036
+ const chain = definitionChain(schema);
1037
+ return chain[chain.length - 1];
932
1038
  }
933
1039
 
934
1040
  // src/generative/paths.ts
@@ -1017,6 +1123,7 @@ function repeatListPathOf(spec, key, parents = parentMapOf(spec)) {
1017
1123
  }
1018
1124
 
1019
1125
  // src/generative/layout-fits.ts
1126
+ var MAX_COND_DEPTH = 8;
1020
1127
  function isUnder(root, path) {
1021
1128
  return path === root || path.startsWith(`${root}/`);
1022
1129
  }
@@ -1095,13 +1202,15 @@ function staleRepeats(spec, parents, root, fieldAt, side) {
1095
1202
  }
1096
1203
  return problems;
1097
1204
  }
1205
+ function withArticle(word) {
1206
+ return `${/^["']?[aeiou]/i.test(word) ? "an" : "a"} ${word}`;
1207
+ }
1098
1208
  function memberWords(name) {
1099
- return `${/^[aeiou]/i.test(name) ? "an" : "a"} ${JSON.stringify(name)}`;
1209
+ return withArticle(JSON.stringify(name));
1100
1210
  }
1101
1211
  function kindWords(kind) {
1102
- if (kind === "object") return "a structure";
1103
- if (kind === "document" || kind === "image") return "a file";
1104
- return `${/^[aeiou]/i.test(kind) ? "an" : "a"} ${kind} value`;
1212
+ const wrong = hasOwnProp(NOT_A_VALUE, kind) ? NOT_A_VALUE[kind] : void 0;
1213
+ return wrong ? wrong.what : withArticle(`${kind} value`);
1105
1214
  }
1106
1215
  var NOT_A_VALUE = {
1107
1216
  object: { what: "a structure", instead: "a structure is shown through its members" },
@@ -1135,16 +1244,6 @@ function listProps() {
1135
1244
  return found;
1136
1245
  }
1137
1246
  var VALUE_SCHEMAS = /* @__PURE__ */ new Set(["string", "number", "boolean", "enum", "literal"]);
1138
- function unwrapped(schema) {
1139
- let current = schema;
1140
- for (let depth = 0; depth < 8; depth += 1) {
1141
- const def = current.def;
1142
- if (!def) return void 0;
1143
- if (def.innerType === void 0) return def;
1144
- current = def.innerType;
1145
- }
1146
- return void 0;
1147
- }
1148
1247
  function listItemsOf(schema) {
1149
1248
  const def = unwrapped(schema);
1150
1249
  if (def?.type !== "array") return void 0;
@@ -1214,27 +1313,84 @@ function nonScalarReads(spec, parents, root, fieldAt) {
1214
1313
  );
1215
1314
  }
1216
1315
  };
1316
+ const readPath = (value) => {
1317
+ const form = expressionForm(value);
1318
+ const record = value;
1319
+ if (form === "$state" || form === "$bindState") return record[form];
1320
+ if (form === "$item" || form === "$bindItem") return itemPath(record[form]);
1321
+ return void 0;
1322
+ };
1323
+ const readsIndex = (value) => expressionForm(value) === "$index";
1324
+ const outcomes = (value, depth = 0) => {
1325
+ const branches = depth < MAX_COND_DEPTH ? condBranches(value) : void 0;
1326
+ return branches ? branches.flatMap((branch) => outcomes(branch, depth + 1)) : [value];
1327
+ };
1217
1328
  const inlineShares = (entries) => {
1218
1329
  if (element.type !== "ProportionBar") return entries;
1219
1330
  const { labelKey, valueKey } = element.props ?? {};
1220
1331
  if (typeof labelKey !== "string" || typeof valueKey !== "string") return entries;
1221
- const how = `each part is written as a structure with ${memberWords(labelKey)} member and ${memberWords(valueKey)} member, or the whole list is read with { "$state" }`;
1222
- return entries.filter((entry, index) => {
1223
- const part = `${key}: ProportionBar's part ${index + 1}`;
1332
+ const how = `each part is written as a structure with ${memberWords(labelKey)} member and ${memberWords(valueKey)} member holding a number, or the whole list is read with { "$state" }`;
1333
+ const amountRefused = (part, amount) => {
1334
+ if (Number.isFinite(shareAmount(amount)) || readsIndex(amount)) return false;
1335
+ const path = readPath(amount);
1336
+ if (path === void 0) {
1337
+ const shown2 = isExpression(amount) ? "an expression that reads no number" : String(JSON.stringify(amount ?? null));
1338
+ problems.push(`${part}'s ${JSON.stringify(valueKey)} is ${shown2}, not a number; ${how}`);
1339
+ return true;
1340
+ }
1341
+ const field = isUnder(root, path) ? fieldAt(path) : void 0;
1342
+ if (field && field.kind !== "number" && field.kind !== "unknown") {
1343
+ problems.push(
1344
+ `${part}'s ${JSON.stringify(valueKey)} reads ${path}, which holds ${kindWords(field.kind)}, not a number; ${how}`
1345
+ );
1346
+ return true;
1347
+ }
1348
+ return false;
1349
+ };
1350
+ const sharePart = (part, entry) => {
1224
1351
  if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
1225
- const shown = Array.isArray(entry) ? "a list" : String(JSON.stringify(entry ?? null));
1226
- problems.push(`${part} is ${shown}, not a structure; ${how}`);
1227
- return false;
1352
+ const shown2 = Array.isArray(entry) ? "a list" : String(JSON.stringify(entry ?? null));
1353
+ problems.push(`${part} is ${shown2}, not a structure; ${how}`);
1354
+ return [];
1228
1355
  }
1229
1356
  if (isExpression(entry)) {
1230
- problems.push(`${part} is an expression, not a structure; ${how}`);
1231
- return false;
1357
+ const path = readPath(entry);
1358
+ if (path === void 0) {
1359
+ problems.push(`${part} is an expression that reads no structure; ${how}`);
1360
+ return [];
1361
+ }
1362
+ const field = isUnder(root, path) ? fieldAt(path) : void 0;
1363
+ if (!field || field.kind === "unknown") return [];
1364
+ if (field.kind !== "object") {
1365
+ problems.push(`${part} reads ${path}, which holds ${kindWords(field.kind)}; ${how}`);
1366
+ return [];
1367
+ }
1368
+ for (const name of [labelKey, valueKey]) {
1369
+ const member = field.fields.find((m) => m.name === name);
1370
+ if (!member) {
1371
+ problems.push(
1372
+ `${part} reads ${path}, which has no ${JSON.stringify(name)} member; ${how}`
1373
+ );
1374
+ } else if (name === valueKey && member.kind !== "number" && member.kind !== "unknown") {
1375
+ problems.push(
1376
+ `${part} reads ${path}, whose ${JSON.stringify(name)} member holds ${kindWords(member.kind)}, not a number; ${how}`
1377
+ );
1378
+ }
1379
+ }
1380
+ return [];
1232
1381
  }
1233
1382
  const missing = [labelKey, valueKey].find((name) => !hasOwnProp(entry, name));
1234
1383
  if (missing !== void 0) {
1235
1384
  problems.push(`${part} has no ${JSON.stringify(missing)} member; ${how}`);
1385
+ return [entry];
1236
1386
  }
1237
- return true;
1387
+ const { [valueKey]: amount, ...rest } = entry;
1388
+ const refused = outcomes(amount).map((outcome) => amountRefused(part, outcome)).some(Boolean);
1389
+ return refused ? [rest] : [entry];
1390
+ };
1391
+ return entries.flatMap((entry, index) => {
1392
+ const part = `${key}: ProportionBar's part ${index + 1}`;
1393
+ return outcomes(entry).flatMap((outcome) => sharePart(part, outcome));
1238
1394
  });
1239
1395
  };
1240
1396
  const walk = (prop, value) => {
@@ -1324,10 +1480,38 @@ function uncoveredUnder(field, path, offered) {
1324
1480
  return [path];
1325
1481
  }
1326
1482
  function layoutProblems(descriptor, spec) {
1483
+ const misshapen = descriptorMalformation(descriptor);
1484
+ if (misshapen !== void 0) return [misshapen];
1485
+ const malformed = specMalformation(spec);
1486
+ if (malformed !== void 0) return [malformedLine(malformed)];
1487
+ return fitCheck(descriptor, spec).problems;
1488
+ }
1489
+ function descriptorMalformation(descriptor) {
1490
+ if (typeof descriptor !== "object" || descriptor === null) {
1491
+ return "the layout has no descriptor to be checked against";
1492
+ }
1493
+ const { inputs, result } = descriptor;
1494
+ if (inputs != null && !Array.isArray(inputs)) {
1495
+ return `the descriptor's "inputs" is not a list of fields`;
1496
+ }
1497
+ if (result != null && (typeof result !== "object" || Array.isArray(result) || typeof result.kind !== "string")) {
1498
+ return `the descriptor's "result" is not a field`;
1499
+ }
1500
+ return void 0;
1501
+ }
1502
+ function fitCheck(descriptor, spec) {
1327
1503
  try {
1328
- return collectLayoutProblems(descriptor, spec);
1504
+ return { problems: collectLayoutProblems(descriptor, spec), checksFailed: false };
1329
1505
  } catch (error) {
1330
- return [`the layout is not a well-formed spec: ${String(error)}`];
1506
+ return { problems: [checksFailedLine(error)], checksFailed: true };
1507
+ }
1508
+ }
1509
+ function shownAs(value) {
1510
+ if (typeof value === "string") return value;
1511
+ try {
1512
+ return String(JSON.stringify(value ?? null));
1513
+ } catch {
1514
+ return "a value that cannot be written out";
1331
1515
  }
1332
1516
  }
1333
1517
  function collectLayoutProblems(descriptor, spec) {
@@ -1339,14 +1523,14 @@ function collectLayoutProblems(descriptor, spec) {
1339
1523
  const path = typeof written === "string" ? absoluteHatchPath(spec, key, written, parents) : void 0;
1340
1524
  if (element.type === "MthdsField") {
1341
1525
  if (!path || !inputFieldAtPath(descriptor.inputs ?? [], path)) {
1342
- problems.push(`${key}: MthdsField delegates ${String(written)}, which no input has`);
1526
+ problems.push(`${key}: MthdsField delegates ${shownAs(written)}, which no input has`);
1343
1527
  } else {
1344
1528
  delegated.push(path);
1345
1529
  }
1346
1530
  }
1347
1531
  if (element.type === "MthdsResult") {
1348
1532
  if (!path || !descriptor.result || !resultFieldAtPath(descriptor.result, path)) {
1349
- problems.push(`${key}: MthdsResult delegates ${String(written)}, which no result has`);
1533
+ problems.push(`${key}: MthdsResult delegates ${shownAs(written)}, which no result has`);
1350
1534
  }
1351
1535
  }
1352
1536
  const [onlyKey] = element.children?.length === 1 ? element.children : [];
@@ -1357,12 +1541,12 @@ function collectLayoutProblems(descriptor, spec) {
1357
1541
  );
1358
1542
  }
1359
1543
  if (element.type === "Picture") {
1360
- const shown = written === "" ? repeatBasePathOf(spec, key, parents) : path ?? void 0;
1361
- const field = shown && descriptor.result ? resultFieldAtPath(descriptor.result, shown) : void 0;
1544
+ const shown2 = written === "" ? repeatBasePathOf(spec, key, parents) : path ?? void 0;
1545
+ const field = shown2 && descriptor.result ? resultFieldAtPath(descriptor.result, shown2) : void 0;
1362
1546
  if (!field) {
1363
1547
  problems.push(`${key}: Picture shows ${JSON.stringify(written)}, which no result has`);
1364
1548
  } else if (field.kind !== "image") {
1365
- problems.push(`${key}: Picture shows ${shown}, which holds a ${field.kind}, not an image`);
1549
+ problems.push(`${key}: Picture shows ${shown2}, which holds a ${field.kind}, not an image`);
1366
1550
  }
1367
1551
  }
1368
1552
  }
@@ -1418,26 +1602,32 @@ function valueAt(value, path) {
1418
1602
  return at;
1419
1603
  }
1420
1604
  function isClosedVocabulary(schema) {
1421
- let current = schema;
1422
- for (let depth = 0; depth < 8; depth += 1) {
1423
- const def = current.def;
1424
- if (!def) return false;
1425
- if (def.type === "enum" || def.type === "literal") return true;
1426
- if (def.innerType === void 0) return false;
1427
- current = def.innerType;
1605
+ const type = unwrapped(schema)?.type;
1606
+ return type === "enum" || type === "literal";
1607
+ }
1608
+ function closedExpressionPaths(schema, value, path = []) {
1609
+ if (schema === void 0 || typeof value !== "object" || value === null) return [];
1610
+ if (path.length > 0 && isExpression(value)) return isClosedVocabulary(schema) ? [[...path]] : [];
1611
+ const def = unwrapped(schema);
1612
+ if (Array.isArray(value)) {
1613
+ return def?.type === "array" ? value.flatMap((entry, index) => closedExpressionPaths(def.element, entry, [...path, index])) : [];
1428
1614
  }
1429
- return false;
1615
+ if (def?.type !== "object" || !def.shape) return [];
1616
+ const shape = def.shape;
1617
+ return Object.entries(value).flatMap(
1618
+ ([name, member]) => hasOwnProp(shape, name) ? closedExpressionPaths(shape[name], member, [...path, name]) : []
1619
+ );
1430
1620
  }
1431
- function definitionOf2(catalog2, type) {
1621
+ function definitionOf3(catalog2, type) {
1432
1622
  const components2 = catalog2.data.components;
1433
1623
  return hasOwnProp(components2, type) ? components2[type] : void 0;
1434
1624
  }
1435
1625
  function propsSchemaOf(catalog2, type) {
1436
- const props = definitionOf2(catalog2, type)?.props;
1626
+ const props = definitionOf3(catalog2, type)?.props;
1437
1627
  return props && typeof props === "object" && "shape" in props ? props : void 0;
1438
1628
  }
1439
1629
  function eventsOf(catalog2, type) {
1440
- const definition = definitionOf2(catalog2, type);
1630
+ const definition = definitionOf3(catalog2, type);
1441
1631
  if (!definition) return void 0;
1442
1632
  return Array.isArray(definition.events) ? definition.events.filter((event) => typeof event === "string") : [];
1443
1633
  }
@@ -1484,12 +1674,19 @@ function deepestChain(spec) {
1484
1674
  return deepest;
1485
1675
  }
1486
1676
  function validateAgainstCatalog(spec, catalog2 = frameCatalog("page")) {
1677
+ const malformed = specMalformation(spec);
1678
+ if (malformed !== void 0) {
1679
+ return { ok: false, problems: [{ message: malformedLine(malformed) }] };
1680
+ }
1681
+ return catalogCheck(spec, catalog2).verdict;
1682
+ }
1683
+ function catalogCheck(spec, catalog2) {
1487
1684
  try {
1488
- return checkAgainstCatalog(spec, catalog2);
1685
+ return { verdict: checkAgainstCatalog(spec, catalog2), checksFailed: false };
1489
1686
  } catch (error) {
1490
1687
  return {
1491
- ok: false,
1492
- problems: [{ message: `the layout is not a well-formed spec: ${String(error)}` }]
1688
+ verdict: { ok: false, problems: [{ message: checksFailedLine(error) }] },
1689
+ checksFailed: true
1493
1690
  };
1494
1691
  }
1495
1692
  }
@@ -1541,6 +1738,20 @@ function checkAgainstCatalog(spec, catalog2) {
1541
1738
  problems.push({ elementKey: key, message: `unknown component type "${element.type}"` });
1542
1739
  }
1543
1740
  }
1741
+ for (const [key, element] of Object.entries(spec.elements)) {
1742
+ const slots = element.slots;
1743
+ if (typeof slots !== "object" || slots === null) continue;
1744
+ const definition = definitionOf3(catalog2, element.type);
1745
+ if (!definition) continue;
1746
+ const declared2 = Array.isArray(definition.slots) ? definition.slots : [];
1747
+ for (const name of Object.keys(slots)) {
1748
+ if (name !== "default" && declared2.includes(name)) continue;
1749
+ problems.push({
1750
+ elementKey: key,
1751
+ message: name === "default" ? `"${key}" puts children in a slot named "default"; a component's own content goes in "children".` : `"${key}" puts children in slot "${name}", which ${element.type} does not have; its content goes in "children".`
1752
+ });
1753
+ }
1754
+ }
1544
1755
  for (const [key, element] of Object.entries(spec.elements)) {
1545
1756
  const schema = propsSchemaOf(catalog2, element.type);
1546
1757
  if (!schema) continue;
@@ -1569,6 +1780,12 @@ function checkAgainstCatalog(spec, catalog2) {
1569
1780
  }
1570
1781
  continue;
1571
1782
  }
1783
+ for (const at of closedExpressionPaths(propSchema, value)) {
1784
+ problems.push({
1785
+ elementKey: key,
1786
+ message: `${element.type}.${name}.${at.map(String).join(".")} is one of a fixed set of names, so it must be written as a literal, never as an expression.`
1787
+ });
1788
+ }
1572
1789
  const parsed = propSchema.safeParse(value);
1573
1790
  const issues = parsed.success ? [] : parsed.error.issues.filter(
1574
1791
  (issue) => !issue.path.some((_, n) => isExpression(valueAt(value, issue.path.slice(0, n + 1))))
@@ -1852,26 +2069,35 @@ function seedLine(seed) {
1852
2069
  return `CREATIVE SEED (derive your direction from it; never reveal it): ${seed}`;
1853
2070
  }
1854
2071
  var REPAIR_ATTEMPTS = 2;
1855
- function layoutCheckProblems(spec, { frame, descriptor }) {
2072
+ function layoutCheck(spec, { frame, descriptor }) {
2073
+ const refused = (line) => ({ problems: [line], checksFailed: false });
1856
2074
  if (!FRAMES.includes(frame)) {
1857
- return [
2075
+ return refused(
1858
2076
  `the layout names the frame ${JSON.stringify(frame)}, which is none of ${FRAMES.join(", ")}`
1859
- ];
1860
- }
1861
- if (typeof descriptor !== "object" || descriptor === null) {
1862
- return ["the layout has no descriptor to be checked against"];
2077
+ );
1863
2078
  }
2079
+ const misshapen = descriptorMalformation(descriptor);
2080
+ if (misshapen !== void 0) return refused(misshapen);
2081
+ const malformed = specMalformation(spec);
2082
+ if (malformed !== void 0) return refused(malformedLine(malformed));
1864
2083
  try {
1865
- const verdict = validateAgainstCatalog(spec, frameCatalog(frame));
1866
- const invalid = verdict.ok ? [] : formatProblems(verdict.problems).split("\n");
2084
+ const catalogs = catalogCheck(spec, frameCatalog(frame));
2085
+ const invalid = catalogs.verdict.ok ? [] : formatProblems(catalogs.verdict.problems).split("\n");
1867
2086
  const runless = frame !== "result" && descriptor.inputs && !pressRuns(spec) ? [
1868
2087
  "the layout has no control that runs the method: bind `run` under a control's `on.press`"
1869
2088
  ] : [];
1870
- return [...invalid, ...runless, ...layoutProblems(descriptor, spec)];
2089
+ const fit = fitCheck(descriptor, spec);
2090
+ return {
2091
+ problems: [...invalid, ...runless, ...fit.problems],
2092
+ checksFailed: catalogs.checksFailed || fit.checksFailed
2093
+ };
1871
2094
  } catch (error) {
1872
- return [`the layout is not a well-formed spec: ${String(error)}`];
2095
+ return { problems: [checksFailedLine(error)], checksFailed: true };
1873
2096
  }
1874
2097
  }
2098
+ function layoutCheckProblems(spec, check) {
2099
+ return layoutCheck(spec, check).problems;
2100
+ }
1875
2101
  function pressRuns(spec) {
1876
2102
  const isRun = (binding) => typeof binding === "object" && binding !== null && binding.action === "run";
1877
2103
  if (typeof spec !== "object" || spec === null) return false;
@@ -2136,7 +2362,7 @@ function jsonlLines(jsonl) {
2136
2362
  }
2137
2363
 
2138
2364
  // src/generative/state.ts
2139
- function isRecord(value) {
2365
+ function isRecord2(value) {
2140
2366
  return typeof value === "object" && value !== null && !Array.isArray(value);
2141
2367
  }
2142
2368
  function payloadToState(field, payload) {
@@ -2148,12 +2374,12 @@ function payloadToState(field, payload) {
2148
2374
  return content ? formatDateContent(content) : payload;
2149
2375
  }
2150
2376
  case "list": {
2151
- const items = Array.isArray(payload) ? payload : isRecord(payload) && Array.isArray(payload.items) ? payload.items : void 0;
2377
+ const items = Array.isArray(payload) ? payload : isRecord2(payload) && Array.isArray(payload.items) ? payload.items : void 0;
2152
2378
  if (!items) return payload;
2153
2379
  return items.map((item) => payloadToState(field.item, item));
2154
2380
  }
2155
2381
  case "object": {
2156
- if (!isRecord(payload)) return payload;
2382
+ if (!isRecord2(payload)) return payload;
2157
2383
  const out = { ...payload };
2158
2384
  for (const child of field.fields) {
2159
2385
  if (hasOwnProp(payload, child.name)) {
@@ -2216,6 +2442,6 @@ function findFixture(fixtures, pipeRef, id) {
2216
2442
  return found;
2217
2443
  }
2218
2444
 
2219
- export { COMPONENT_NAMES, COMPONENT_RENDERINGS, COMPOSER_COMPONENTS, CUSTOM_COMPONENTS, DESIGNER_BRIEF_CONCEPT, DESIGNER_CATALOG_CONCEPT, FRAMES, FRAME_COMPONENTS, ICON_NAMES, INPUTS_ROOT, METRIC_FORMATS, PICKED_SHADCN, PRODUCT_COMPONENTS, PROMPT_HASH, REPAIR_ATTEMPTS, RESULT_COMPONENTS, RESULT_ROOT, absoluteHatchPath, baseCatalog, catalog, componentNamesFor, designerCatalog, fieldAtSegments, findFixture, fixtureId, fixtureLabel, formatProblems, frameCatalog, frameComponents, inputBrief, inputFieldAtPath, isDelegatedInput, isDelegatedResult, joinPath, jsonlLines, layoutCheckProblems, layoutFits, layoutProblems, payloadToState, promptHashSubject, repeatBasePathOf, resultBrief, resultFieldAtPath, seedLine, segmentsUnder, specFromJsonl, specToJsonl, validateAgainstCatalog };
2220
- //# sourceMappingURL=chunk-VBXFGUCG.js.map
2221
- //# sourceMappingURL=chunk-VBXFGUCG.js.map
2445
+ export { COMPONENT_NAMES, COMPONENT_RENDERINGS, COMPOSER_COMPONENTS, CUSTOM_COMPONENTS, DESIGNER_BRIEF_CONCEPT, DESIGNER_CATALOG_CONCEPT, FRAMES, FRAME_COMPONENTS, ICON_NAMES, INPUTS_ROOT, METRIC_FORMATS, PICKED_SHADCN, PRODUCT_COMPONENTS, PROMPT_HASH, REPAIR_ATTEMPTS, RESULT_COMPONENTS, RESULT_ROOT, absoluteHatchPath, baseCatalog, catalog, componentNamesFor, definitionChain, designerCatalog, fieldAtSegments, findFixture, fixtureId, fixtureLabel, formatProblems, frameCatalog, frameComponents, inputBrief, inputFieldAtPath, isDelegatedInput, isDelegatedResult, joinPath, jsonlLines, layoutCheck, layoutCheckProblems, layoutFits, layoutProblems, payloadToState, promptHashSubject, repeatBasePathOf, resultBrief, resultFieldAtPath, seedLine, segmentsUnder, shareAmount, specFromJsonl, specToJsonl, unwrapped, validateAgainstCatalog };
2446
+ //# sourceMappingURL=chunk-4SRSGZWM.js.map
2447
+ //# sourceMappingURL=chunk-4SRSGZWM.js.map