@btst/stack 2.11.4 → 2.11.6

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.
Files changed (55) hide show
  1. package/dist/components/form-builder/index.d.cts +8 -0
  2. package/dist/components/form-builder/index.d.mts +8 -0
  3. package/dist/components/form-builder/index.d.ts +8 -0
  4. package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.cjs +58 -34
  5. package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.mjs +58 -34
  6. package/dist/packages/stack/src/plugins/cms/client/components/inverse-relations-panel.cjs +29 -7
  7. package/dist/packages/stack/src/plugins/cms/client/components/inverse-relations-panel.mjs +29 -7
  8. package/dist/packages/ui/src/components/auto-form/helpers.cjs +24 -1
  9. package/dist/packages/ui/src/components/auto-form/helpers.mjs +24 -1
  10. package/dist/packages/ui/src/components/form-builder/schema-utils.cjs +11 -5
  11. package/dist/packages/ui/src/components/form-builder/schema-utils.mjs +11 -5
  12. package/dist/plugins/blog/api/index.d.cts +3 -2
  13. package/dist/plugins/blog/api/index.d.mts +3 -2
  14. package/dist/plugins/blog/api/index.d.ts +3 -2
  15. package/dist/plugins/blog/client/components/index.cjs +21 -0
  16. package/dist/plugins/blog/client/components/index.d.cts +71 -1
  17. package/dist/plugins/blog/client/components/index.d.mts +71 -1
  18. package/dist/plugins/blog/client/components/index.d.ts +71 -1
  19. package/dist/plugins/blog/client/components/index.mjs +15 -1
  20. package/dist/plugins/blog/client/hooks/index.d.cts +3 -2
  21. package/dist/plugins/blog/client/hooks/index.d.mts +3 -2
  22. package/dist/plugins/blog/client/hooks/index.d.ts +3 -2
  23. package/dist/plugins/blog/client/index.d.cts +3 -2
  24. package/dist/plugins/blog/client/index.d.mts +3 -2
  25. package/dist/plugins/blog/client/index.d.ts +3 -2
  26. package/dist/plugins/blog/query-keys.d.cts +3 -2
  27. package/dist/plugins/blog/query-keys.d.mts +3 -2
  28. package/dist/plugins/blog/query-keys.d.ts +3 -2
  29. package/dist/plugins/kanban/api/index.d.cts +1 -1
  30. package/dist/plugins/kanban/api/index.d.mts +1 -1
  31. package/dist/plugins/kanban/api/index.d.ts +1 -1
  32. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  33. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  34. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  35. package/dist/shared/{stack.6mEHS2WH.d.mts → stack.BnSAhKJi.d.ts} +4 -3
  36. package/dist/shared/{stack.C1jLzFZ6.d.mts → stack.CLWhMntf.d.cts} +6 -5
  37. package/dist/shared/{stack.BQmuNl5p.d.mts → stack.CuhcFsAx.d.cts} +3 -38
  38. package/dist/shared/{stack.BQmuNl5p.d.ts → stack.CuhcFsAx.d.mts} +3 -38
  39. package/dist/shared/{stack.BQmuNl5p.d.cts → stack.CuhcFsAx.d.ts} +3 -38
  40. package/dist/shared/{stack.AJTXI7kw.d.cts → stack.DNLuzDb3.d.cts} +4 -3
  41. package/dist/shared/{stack.QYn-Px94.d.ts → stack.Dfc44JiE.d.mts} +4 -3
  42. package/dist/shared/{stack.DF1ewpVE.d.ts → stack.DiodAFAa.d.mts} +6 -5
  43. package/dist/shared/stack.Dsatjh0B.d.cts +35 -0
  44. package/dist/shared/stack.Dsatjh0B.d.mts +35 -0
  45. package/dist/shared/stack.Dsatjh0B.d.ts +35 -0
  46. package/dist/shared/{stack.XulPeAD3.d.cts → stack.ufcExQIR.d.ts} +6 -5
  47. package/package.json +14 -1
  48. package/src/plugins/blog/client/components/index.tsx +52 -0
  49. package/src/plugins/cms/__tests__/auto-form-nested-config.test.ts +177 -0
  50. package/src/plugins/cms/client/components/forms/content-form.tsx +93 -47
  51. package/src/plugins/cms/client/components/inverse-relations-panel.tsx +49 -1
  52. package/src/plugins/form-builder/__tests__/schema-utils.test.ts +362 -0
  53. package/dist/shared/{stack.j5SFLC1d.d.ts → stack.9dFHtvXR.d.ts} +15 -15
  54. package/dist/shared/{stack.Bzfx-_lq.d.mts → stack.BzKW7ToW.d.mts} +15 -15
  55. package/dist/shared/{stack.B6S3cgwN.d.cts → stack.f6MrG7ys.d.cts} +15 -15
@@ -23,6 +23,14 @@ interface JSONSchema {
23
23
  $schema?: string;
24
24
  /** Step definitions for multi-step forms (stored in schema meta) */
25
25
  steps?: FormStep[];
26
+ /**
27
+ * Optional root-level field-name → step-index map. This is the format produced
28
+ * by `zodToFormSchema(schema, { steps, stepGroupMap })` and consumed by
29
+ * `SteppedAutoForm`. The visual FormBuilder writes per-property `stepGroup`
30
+ * instead, but we accept both on read so a Zod-seeded form opens with its
31
+ * fields correctly assigned to their steps.
32
+ */
33
+ stepGroupMap?: Record<string, number>;
26
34
  additionalProperties?: boolean;
27
35
  }
28
36
  /**
@@ -23,6 +23,14 @@ interface JSONSchema {
23
23
  $schema?: string;
24
24
  /** Step definitions for multi-step forms (stored in schema meta) */
25
25
  steps?: FormStep[];
26
+ /**
27
+ * Optional root-level field-name → step-index map. This is the format produced
28
+ * by `zodToFormSchema(schema, { steps, stepGroupMap })` and consumed by
29
+ * `SteppedAutoForm`. The visual FormBuilder writes per-property `stepGroup`
30
+ * instead, but we accept both on read so a Zod-seeded form opens with its
31
+ * fields correctly assigned to their steps.
32
+ */
33
+ stepGroupMap?: Record<string, number>;
26
34
  additionalProperties?: boolean;
27
35
  }
28
36
  /**
@@ -23,6 +23,14 @@ interface JSONSchema {
23
23
  $schema?: string;
24
24
  /** Step definitions for multi-step forms (stored in schema meta) */
25
25
  steps?: FormStep[];
26
+ /**
27
+ * Optional root-level field-name → step-index map. This is the format produced
28
+ * by `zodToFormSchema(schema, { steps, stepGroupMap })` and consumed by
29
+ * `SteppedAutoForm`. The visual FormBuilder writes per-property `stepGroup`
30
+ * instead, but we accept both on read so a Zod-seeded form opens with its
31
+ * fields correctly assigned to their steps.
32
+ */
33
+ stepGroupMap?: Record<string, number>;
26
34
  additionalProperties?: boolean;
27
35
  }
28
36
  /**
@@ -20,43 +20,67 @@ function buildFieldConfigFromJsonSchema(jsonSchema, uploadImage, fieldComponents
20
20
  const baseConfig = helpers.buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents);
21
21
  const properties = jsonSchema.properties;
22
22
  if (!properties) return baseConfig;
23
- for (const [key, prop] of Object.entries(properties)) {
24
- if (prop.fieldType === "file" && !fieldComponents?.["file"]) {
25
- if (!uploadImage && !imageInputField) {
26
- baseConfig[key] = {
27
- ...baseConfig[key],
28
- fieldType: () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive", children: [
29
- "File upload requires an ",
30
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: "uploadImage" }),
31
- " or",
32
- " ",
33
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: "imageInputField" }),
34
- " function in CMS overrides."
35
- ] })
36
- };
37
- } else {
38
- baseConfig[key] = {
39
- ...baseConfig[key],
40
- fieldType: (props) => /* @__PURE__ */ jsxRuntime.jsx(
41
- fileUpload.CMSFileUpload,
42
- {
43
- ...props,
44
- uploadImage: uploadImage ?? (() => Promise.resolve("")),
45
- imageInputField,
46
- imagePicker
47
- }
48
- )
23
+ const injectCustomFieldTypes = (props, targetConfig) => {
24
+ for (const [key, prop] of Object.entries(props)) {
25
+ const existing = targetConfig[key] ?? {};
26
+ let updated = existing;
27
+ if (prop.fieldType === "file" && !fieldComponents?.["file"]) {
28
+ if (!uploadImage && !imageInputField) {
29
+ updated = {
30
+ ...updated,
31
+ fieldType: () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive", children: [
32
+ "File upload requires an ",
33
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "uploadImage" }),
34
+ " or",
35
+ " ",
36
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "imageInputField" }),
37
+ " function in CMS overrides."
38
+ ] })
39
+ };
40
+ } else {
41
+ updated = {
42
+ ...updated,
43
+ fieldType: (componentProps) => /* @__PURE__ */ jsxRuntime.jsx(
44
+ fileUpload.CMSFileUpload,
45
+ {
46
+ ...componentProps,
47
+ uploadImage: uploadImage ?? (() => Promise.resolve("")),
48
+ imageInputField,
49
+ imagePicker
50
+ }
51
+ )
52
+ };
53
+ }
54
+ }
55
+ if (prop.fieldType === "relation" && prop.relation && !fieldComponents?.["relation"]) {
56
+ const relationConfig = prop.relation;
57
+ updated = {
58
+ ...updated,
59
+ fieldType: (componentProps) => /* @__PURE__ */ jsxRuntime.jsx(relationField.RelationField, { ...componentProps, relation: relationConfig })
49
60
  };
50
61
  }
62
+ if (prop.properties) {
63
+ injectCustomFieldTypes(
64
+ prop.properties,
65
+ updated
66
+ );
67
+ }
68
+ const items = prop.items;
69
+ if (items?.properties) {
70
+ injectCustomFieldTypes(
71
+ items.properties,
72
+ updated
73
+ );
74
+ }
75
+ if (Object.keys(updated).length > 0) {
76
+ targetConfig[key] = updated;
77
+ }
51
78
  }
52
- if (prop.fieldType === "relation" && prop.relation && !fieldComponents?.["relation"]) {
53
- const relationConfig = prop.relation;
54
- baseConfig[key] = {
55
- ...baseConfig[key],
56
- fieldType: (props) => /* @__PURE__ */ jsxRuntime.jsx(relationField.RelationField, { ...props, relation: relationConfig })
57
- };
58
- }
59
- }
79
+ };
80
+ injectCustomFieldTypes(
81
+ properties,
82
+ baseConfig
83
+ );
60
84
  return baseConfig;
61
85
  }
62
86
  function findSlugSourceField(jsonSchema) {
@@ -18,43 +18,67 @@ function buildFieldConfigFromJsonSchema(jsonSchema, uploadImage, fieldComponents
18
18
  const baseConfig = buildFieldConfigFromJsonSchema$1(jsonSchema, fieldComponents);
19
19
  const properties = jsonSchema.properties;
20
20
  if (!properties) return baseConfig;
21
- for (const [key, prop] of Object.entries(properties)) {
22
- if (prop.fieldType === "file" && !fieldComponents?.["file"]) {
23
- if (!uploadImage && !imageInputField) {
24
- baseConfig[key] = {
25
- ...baseConfig[key],
26
- fieldType: () => /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive", children: [
27
- "File upload requires an ",
28
- /* @__PURE__ */ jsx("code", { children: "uploadImage" }),
29
- " or",
30
- " ",
31
- /* @__PURE__ */ jsx("code", { children: "imageInputField" }),
32
- " function in CMS overrides."
33
- ] })
34
- };
35
- } else {
36
- baseConfig[key] = {
37
- ...baseConfig[key],
38
- fieldType: (props) => /* @__PURE__ */ jsx(
39
- CMSFileUpload,
40
- {
41
- ...props,
42
- uploadImage: uploadImage ?? (() => Promise.resolve("")),
43
- imageInputField,
44
- imagePicker
45
- }
46
- )
21
+ const injectCustomFieldTypes = (props, targetConfig) => {
22
+ for (const [key, prop] of Object.entries(props)) {
23
+ const existing = targetConfig[key] ?? {};
24
+ let updated = existing;
25
+ if (prop.fieldType === "file" && !fieldComponents?.["file"]) {
26
+ if (!uploadImage && !imageInputField) {
27
+ updated = {
28
+ ...updated,
29
+ fieldType: () => /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive", children: [
30
+ "File upload requires an ",
31
+ /* @__PURE__ */ jsx("code", { children: "uploadImage" }),
32
+ " or",
33
+ " ",
34
+ /* @__PURE__ */ jsx("code", { children: "imageInputField" }),
35
+ " function in CMS overrides."
36
+ ] })
37
+ };
38
+ } else {
39
+ updated = {
40
+ ...updated,
41
+ fieldType: (componentProps) => /* @__PURE__ */ jsx(
42
+ CMSFileUpload,
43
+ {
44
+ ...componentProps,
45
+ uploadImage: uploadImage ?? (() => Promise.resolve("")),
46
+ imageInputField,
47
+ imagePicker
48
+ }
49
+ )
50
+ };
51
+ }
52
+ }
53
+ if (prop.fieldType === "relation" && prop.relation && !fieldComponents?.["relation"]) {
54
+ const relationConfig = prop.relation;
55
+ updated = {
56
+ ...updated,
57
+ fieldType: (componentProps) => /* @__PURE__ */ jsx(RelationField, { ...componentProps, relation: relationConfig })
47
58
  };
48
59
  }
60
+ if (prop.properties) {
61
+ injectCustomFieldTypes(
62
+ prop.properties,
63
+ updated
64
+ );
65
+ }
66
+ const items = prop.items;
67
+ if (items?.properties) {
68
+ injectCustomFieldTypes(
69
+ items.properties,
70
+ updated
71
+ );
72
+ }
73
+ if (Object.keys(updated).length > 0) {
74
+ targetConfig[key] = updated;
75
+ }
49
76
  }
50
- if (prop.fieldType === "relation" && prop.relation && !fieldComponents?.["relation"]) {
51
- const relationConfig = prop.relation;
52
- baseConfig[key] = {
53
- ...baseConfig[key],
54
- fieldType: (props) => /* @__PURE__ */ jsx(RelationField, { ...props, relation: relationConfig })
55
- };
56
- }
57
- }
77
+ };
78
+ injectCustomFieldTypes(
79
+ properties,
80
+ baseConfig
81
+ );
58
82
  return baseConfig;
59
83
  }
60
84
  function findSlugSourceField(jsonSchema) {
@@ -42,6 +42,13 @@ function InverseRelationsPanel({
42
42
  if (inverseRelations.length === 0) {
43
43
  return null;
44
44
  }
45
+ const sourceTypeCounts = /* @__PURE__ */ new Map();
46
+ for (const rel of inverseRelations) {
47
+ sourceTypeCounts.set(
48
+ rel.sourceType,
49
+ (sourceTypeCounts.get(rel.sourceType) ?? 0) + 1
50
+ );
51
+ }
45
52
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
46
53
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold", children: "Related Items" }),
47
54
  inverseRelations.map((relation) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -54,12 +61,18 @@ function InverseRelationsPanel({
54
61
  navigate,
55
62
  Link,
56
63
  client: client$1,
57
- headers
64
+ headers,
65
+ ambiguous: (sourceTypeCounts.get(relation.sourceType) ?? 0) > 1
58
66
  },
59
67
  `${relation.sourceType}-${relation.fieldName}`
60
68
  ))
61
69
  ] });
62
70
  }
71
+ function humanizeFieldName(fieldName) {
72
+ const stripped = fieldName.replace(/Ids?$/, "") || fieldName;
73
+ const words = stripped.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim().split(/\s+/);
74
+ return words.map((w) => w ? w[0].toUpperCase() + w.slice(1) : w).join(" ");
75
+ }
63
76
  function InverseRelationSection({
64
77
  relation,
65
78
  contentTypeSlug,
@@ -68,7 +81,8 @@ function InverseRelationSection({
68
81
  navigate,
69
82
  Link,
70
83
  client,
71
- headers
84
+ headers,
85
+ ambiguous
72
86
  }) {
73
87
  const [isExpanded, setIsExpanded] = React.useState(true);
74
88
  const [deleteItemId, setDeleteItemId] = React.useState(null);
@@ -117,6 +131,7 @@ function InverseRelationSection({
117
131
  navigate(createUrl);
118
132
  };
119
133
  const LinkComponent = Link ?? "a";
134
+ const fieldLabel = ambiguous ? humanizeFieldName(relation.fieldName) : null;
120
135
  return /* @__PURE__ */ jsxRuntime.jsxs(card.Card, { children: [
121
136
  /* @__PURE__ */ jsxRuntime.jsx(card.CardHeader, { className: "py-3", children: /* @__PURE__ */ jsxRuntime.jsx(
122
137
  "button",
@@ -126,10 +141,16 @@ function InverseRelationSection({
126
141
  className: "flex items-center justify-between w-full text-left",
127
142
  children: /* @__PURE__ */ jsxRuntime.jsxs(card.CardTitle, { className: "text-base flex items-center gap-2", children: [
128
143
  isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronRight, { className: "h-4 w-4" }),
129
- relation.sourceTypeName,
130
- " (",
131
- total,
132
- ")"
144
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: relation.sourceTypeName }),
145
+ fieldLabel && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-normal text-muted-foreground", children: [
146
+ "\xB7 ",
147
+ fieldLabel
148
+ ] }),
149
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-normal text-muted-foreground", children: [
150
+ "(",
151
+ total,
152
+ ")"
153
+ ] })
133
154
  ] })
134
155
  }
135
156
  ) }),
@@ -182,7 +203,8 @@ function InverseRelationSection({
182
203
  children: [
183
204
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Plus, { className: "h-4 w-4 mr-2" }),
184
205
  "Add ",
185
- relation.sourceTypeName
206
+ relation.sourceTypeName,
207
+ fieldLabel ? ` (${fieldLabel})` : ""
186
208
  ]
187
209
  }
188
210
  ) })
@@ -40,6 +40,13 @@ function InverseRelationsPanel({
40
40
  if (inverseRelations.length === 0) {
41
41
  return null;
42
42
  }
43
+ const sourceTypeCounts = /* @__PURE__ */ new Map();
44
+ for (const rel of inverseRelations) {
45
+ sourceTypeCounts.set(
46
+ rel.sourceType,
47
+ (sourceTypeCounts.get(rel.sourceType) ?? 0) + 1
48
+ );
49
+ }
43
50
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
44
51
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: "Related Items" }),
45
52
  inverseRelations.map((relation) => /* @__PURE__ */ jsx(
@@ -52,12 +59,18 @@ function InverseRelationsPanel({
52
59
  navigate,
53
60
  Link,
54
61
  client,
55
- headers
62
+ headers,
63
+ ambiguous: (sourceTypeCounts.get(relation.sourceType) ?? 0) > 1
56
64
  },
57
65
  `${relation.sourceType}-${relation.fieldName}`
58
66
  ))
59
67
  ] });
60
68
  }
69
+ function humanizeFieldName(fieldName) {
70
+ const stripped = fieldName.replace(/Ids?$/, "") || fieldName;
71
+ const words = stripped.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim().split(/\s+/);
72
+ return words.map((w) => w ? w[0].toUpperCase() + w.slice(1) : w).join(" ");
73
+ }
61
74
  function InverseRelationSection({
62
75
  relation,
63
76
  contentTypeSlug,
@@ -66,7 +79,8 @@ function InverseRelationSection({
66
79
  navigate,
67
80
  Link,
68
81
  client,
69
- headers
82
+ headers,
83
+ ambiguous
70
84
  }) {
71
85
  const [isExpanded, setIsExpanded] = useState(true);
72
86
  const [deleteItemId, setDeleteItemId] = useState(null);
@@ -115,6 +129,7 @@ function InverseRelationSection({
115
129
  navigate(createUrl);
116
130
  };
117
131
  const LinkComponent = Link ?? "a";
132
+ const fieldLabel = ambiguous ? humanizeFieldName(relation.fieldName) : null;
118
133
  return /* @__PURE__ */ jsxs(Card, { children: [
119
134
  /* @__PURE__ */ jsx(CardHeader, { className: "py-3", children: /* @__PURE__ */ jsx(
120
135
  "button",
@@ -124,10 +139,16 @@ function InverseRelationSection({
124
139
  className: "flex items-center justify-between w-full text-left",
125
140
  children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-base flex items-center gap-2", children: [
126
141
  isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }),
127
- relation.sourceTypeName,
128
- " (",
129
- total,
130
- ")"
142
+ /* @__PURE__ */ jsx("span", { children: relation.sourceTypeName }),
143
+ fieldLabel && /* @__PURE__ */ jsxs("span", { className: "font-normal text-muted-foreground", children: [
144
+ "\xB7 ",
145
+ fieldLabel
146
+ ] }),
147
+ /* @__PURE__ */ jsxs("span", { className: "font-normal text-muted-foreground", children: [
148
+ "(",
149
+ total,
150
+ ")"
151
+ ] })
131
152
  ] })
132
153
  }
133
154
  ) }),
@@ -180,7 +201,8 @@ function InverseRelationSection({
180
201
  children: [
181
202
  /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-2" }),
182
203
  "Add ",
183
- relation.sourceTypeName
204
+ relation.sourceTypeName,
205
+ fieldLabel ? ` (${fieldLabel})` : ""
184
206
  ]
185
207
  }
186
208
  ) })
@@ -216,12 +216,19 @@ function buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents) {
216
216
  );
217
217
  }
218
218
  }
219
+ const reservedProps = /* @__PURE__ */ new Set([
220
+ "description",
221
+ "label",
222
+ "inputProps",
223
+ "fieldType",
224
+ "renderParent",
225
+ "order"
226
+ ]);
219
227
  if (value.properties) {
220
228
  const nestedConfig = buildFieldConfigFromJsonSchema(
221
229
  { properties: value.properties },
222
230
  fieldComponents
223
231
  );
224
- const reservedProps = /* @__PURE__ */ new Set(["description", "label", "inputProps", "fieldType", "renderParent", "order"]);
225
232
  for (const [nestedKey, nestedValue] of Object.entries(nestedConfig)) {
226
233
  if (!reservedProps.has(nestedKey)) {
227
234
  config[nestedKey] = nestedValue;
@@ -232,6 +239,22 @@ function buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents) {
232
239
  }
233
240
  }
234
241
  }
242
+ const items = value.items;
243
+ if (items?.properties) {
244
+ const itemConfig = buildFieldConfigFromJsonSchema(
245
+ { properties: items.properties },
246
+ fieldComponents
247
+ );
248
+ for (const [itemKey, itemValue] of Object.entries(itemConfig)) {
249
+ if (!reservedProps.has(itemKey)) {
250
+ config[itemKey] = itemValue;
251
+ } else {
252
+ console.warn(
253
+ `Array field "${key}" has an item property named "${itemKey}" which conflicts with a reserved FieldConfigItem property. The item field's config will not be accessible inside array items.`
254
+ );
255
+ }
256
+ }
257
+ }
235
258
  if (Object.keys(config).length > 0) {
236
259
  fieldConfig[key] = config;
237
260
  }
@@ -214,12 +214,19 @@ function buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents) {
214
214
  );
215
215
  }
216
216
  }
217
+ const reservedProps = /* @__PURE__ */ new Set([
218
+ "description",
219
+ "label",
220
+ "inputProps",
221
+ "fieldType",
222
+ "renderParent",
223
+ "order"
224
+ ]);
217
225
  if (value.properties) {
218
226
  const nestedConfig = buildFieldConfigFromJsonSchema(
219
227
  { properties: value.properties },
220
228
  fieldComponents
221
229
  );
222
- const reservedProps = /* @__PURE__ */ new Set(["description", "label", "inputProps", "fieldType", "renderParent", "order"]);
223
230
  for (const [nestedKey, nestedValue] of Object.entries(nestedConfig)) {
224
231
  if (!reservedProps.has(nestedKey)) {
225
232
  config[nestedKey] = nestedValue;
@@ -230,6 +237,22 @@ function buildFieldConfigFromJsonSchema(jsonSchema, fieldComponents) {
230
237
  }
231
238
  }
232
239
  }
240
+ const items = value.items;
241
+ if (items?.properties) {
242
+ const itemConfig = buildFieldConfigFromJsonSchema(
243
+ { properties: items.properties },
244
+ fieldComponents
245
+ );
246
+ for (const [itemKey, itemValue] of Object.entries(itemConfig)) {
247
+ if (!reservedProps.has(itemKey)) {
248
+ config[itemKey] = itemValue;
249
+ } else {
250
+ console.warn(
251
+ `Array field "${key}" has an item property named "${itemKey}" which conflicts with a reserved FieldConfigItem property. The item field's config will not be accessible inside array items.`
252
+ );
253
+ }
254
+ }
255
+ }
233
256
  if (Object.keys(config).length > 0) {
234
257
  fieldConfig[key] = config;
235
258
  }
@@ -49,7 +49,7 @@ function fieldsToJSONSchema(fields, components, steps) {
49
49
  ...hasMultipleSteps ? { steps } : {}
50
50
  };
51
51
  }
52
- function propertiesToFields(properties, requiredSet, components) {
52
+ function propertiesToFields(properties, requiredSet, components, stepGroupMap) {
53
53
  const fields = [];
54
54
  for (const [key, prop] of Object.entries(properties)) {
55
55
  const isRequired = requiredSet.has(key);
@@ -60,9 +60,10 @@ function propertiesToFields(properties, requiredSet, components) {
60
60
  break;
61
61
  }
62
62
  }
63
+ const resolvedStepGroup = prop.stepGroup !== void 0 ? prop.stepGroup : stepGroupMap?.[key];
63
64
  if (field) {
64
- if (prop.stepGroup !== void 0) {
65
- field.stepGroup = prop.stepGroup;
65
+ if (resolvedStepGroup !== void 0) {
66
+ field.stepGroup = resolvedStepGroup;
66
67
  }
67
68
  if (field.type === "object" && prop.properties) {
68
69
  const childRequiredSet = new Set(prop.required || []);
@@ -85,7 +86,7 @@ function propertiesToFields(properties, requiredSet, components) {
85
86
  required: isRequired
86
87
  },
87
88
  // Include stepGroup even for fallback fields
88
- ...prop.stepGroup !== void 0 ? { stepGroup: prop.stepGroup } : {}
89
+ ...resolvedStepGroup !== void 0 ? { stepGroup: resolvedStepGroup } : {}
89
90
  });
90
91
  }
91
92
  }
@@ -96,7 +97,12 @@ function jsonSchemaToFieldsAndSteps(schema, components) {
96
97
  return { fields: [], steps: [] };
97
98
  }
98
99
  const requiredSet = new Set(schema.required || []);
99
- const fields = propertiesToFields(schema.properties, requiredSet, components);
100
+ const fields = propertiesToFields(
101
+ schema.properties,
102
+ requiredSet,
103
+ components,
104
+ schema.stepGroupMap
105
+ );
100
106
  const steps = schema.steps || [];
101
107
  return { fields, steps };
102
108
  }
@@ -47,7 +47,7 @@ function fieldsToJSONSchema(fields, components, steps) {
47
47
  ...hasMultipleSteps ? { steps } : {}
48
48
  };
49
49
  }
50
- function propertiesToFields(properties, requiredSet, components) {
50
+ function propertiesToFields(properties, requiredSet, components, stepGroupMap) {
51
51
  const fields = [];
52
52
  for (const [key, prop] of Object.entries(properties)) {
53
53
  const isRequired = requiredSet.has(key);
@@ -58,9 +58,10 @@ function propertiesToFields(properties, requiredSet, components) {
58
58
  break;
59
59
  }
60
60
  }
61
+ const resolvedStepGroup = prop.stepGroup !== void 0 ? prop.stepGroup : stepGroupMap?.[key];
61
62
  if (field) {
62
- if (prop.stepGroup !== void 0) {
63
- field.stepGroup = prop.stepGroup;
63
+ if (resolvedStepGroup !== void 0) {
64
+ field.stepGroup = resolvedStepGroup;
64
65
  }
65
66
  if (field.type === "object" && prop.properties) {
66
67
  const childRequiredSet = new Set(prop.required || []);
@@ -83,7 +84,7 @@ function propertiesToFields(properties, requiredSet, components) {
83
84
  required: isRequired
84
85
  },
85
86
  // Include stepGroup even for fallback fields
86
- ...prop.stepGroup !== void 0 ? { stepGroup: prop.stepGroup } : {}
87
+ ...resolvedStepGroup !== void 0 ? { stepGroup: resolvedStepGroup } : {}
87
88
  });
88
89
  }
89
90
  }
@@ -94,7 +95,12 @@ function jsonSchemaToFieldsAndSteps(schema, components) {
94
95
  return { fields: [], steps: [] };
95
96
  }
96
97
  const requiredSet = new Set(schema.required || []);
97
- const fields = propertiesToFields(schema.properties, requiredSet, components);
98
+ const fields = propertiesToFields(
99
+ schema.properties,
100
+ requiredSet,
101
+ components,
102
+ schema.stepGroupMap
103
+ );
98
104
  const steps = schema.steps || [];
99
105
  return { fields, steps };
100
106
  }
@@ -1,11 +1,12 @@
1
- export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.XulPeAD3.cjs';
2
- import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BQmuNl5p.cjs';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.CLWhMntf.cjs';
2
+ import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.Dsatjh0B.cjs';
3
3
  import '@tanstack/react-query';
4
4
  import '@btst/stack/plugins/client';
5
5
  import '@btst/stack/plugins/api';
6
6
  import '@btst/db';
7
7
  import 'better-call';
8
8
  import 'zod';
9
+ import '../../../shared/stack.CuhcFsAx.cjs';
9
10
 
10
11
  /**
11
12
  * Serialize a Tag for SSR/SSG use (convert dates to strings).
@@ -1,11 +1,12 @@
1
- export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.C1jLzFZ6.mjs';
2
- import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BQmuNl5p.mjs';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.DiodAFAa.mjs';
2
+ import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.Dsatjh0B.mjs';
3
3
  import '@tanstack/react-query';
4
4
  import '@btst/stack/plugins/client';
5
5
  import '@btst/stack/plugins/api';
6
6
  import '@btst/db';
7
7
  import 'better-call';
8
8
  import 'zod';
9
+ import '../../../shared/stack.CuhcFsAx.mjs';
9
10
 
10
11
  /**
11
12
  * Serialize a Tag for SSR/SSG use (convert dates to strings).
@@ -1,11 +1,12 @@
1
- export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.DF1ewpVE.js';
2
- import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BQmuNl5p.js';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.ufcExQIR.js';
2
+ import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.Dsatjh0B.js';
3
3
  import '@tanstack/react-query';
4
4
  import '@btst/stack/plugins/client';
5
5
  import '@btst/stack/plugins/api';
6
6
  import '@btst/db';
7
7
  import 'better-call';
8
8
  import 'zod';
9
+ import '../../../shared/stack.CuhcFsAx.js';
9
10
 
10
11
  /**
11
12
  * Serialize a Tag for SSR/SSG use (convert dates to strings).