@btst/stack 1.8.0 → 1.9.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.
Files changed (44) hide show
  1. package/dist/packages/better-stack/src/plugins/cms/api/plugin.cjs +445 -16
  2. package/dist/packages/better-stack/src/plugins/cms/api/plugin.mjs +445 -16
  3. package/dist/packages/better-stack/src/plugins/cms/client/components/forms/content-form.cjs +24 -7
  4. package/dist/packages/better-stack/src/plugins/cms/client/components/forms/content-form.mjs +25 -8
  5. package/dist/packages/better-stack/src/plugins/cms/client/components/forms/relation-field.cjs +224 -0
  6. package/dist/packages/better-stack/src/plugins/cms/client/components/forms/relation-field.mjs +222 -0
  7. package/dist/packages/better-stack/src/plugins/cms/client/components/inverse-relations-panel.cjs +243 -0
  8. package/dist/packages/better-stack/src/plugins/cms/client/components/inverse-relations-panel.mjs +241 -0
  9. package/dist/packages/better-stack/src/plugins/cms/client/components/pages/content-editor-page.internal.cjs +56 -2
  10. package/dist/packages/better-stack/src/plugins/cms/client/components/pages/content-editor-page.internal.mjs +56 -2
  11. package/dist/packages/better-stack/src/plugins/cms/client/hooks/cms-hooks.cjs +190 -0
  12. package/dist/packages/better-stack/src/plugins/cms/client/hooks/cms-hooks.mjs +187 -1
  13. package/dist/packages/better-stack/src/plugins/cms/db.cjs +38 -0
  14. package/dist/packages/better-stack/src/plugins/cms/db.mjs +38 -0
  15. package/dist/packages/ui/src/components/auto-form/fields/object.cjs +81 -1
  16. package/dist/packages/ui/src/components/auto-form/fields/object.mjs +81 -1
  17. package/dist/packages/ui/src/components/dialog.cjs +6 -0
  18. package/dist/packages/ui/src/components/dialog.mjs +6 -1
  19. package/dist/plugins/cms/api/index.d.cts +67 -3
  20. package/dist/plugins/cms/api/index.d.mts +67 -3
  21. package/dist/plugins/cms/api/index.d.ts +67 -3
  22. package/dist/plugins/cms/client/hooks/index.cjs +4 -0
  23. package/dist/plugins/cms/client/hooks/index.d.cts +82 -3
  24. package/dist/plugins/cms/client/hooks/index.d.mts +82 -3
  25. package/dist/plugins/cms/client/hooks/index.d.ts +82 -3
  26. package/dist/plugins/cms/client/hooks/index.mjs +1 -1
  27. package/dist/plugins/cms/query-keys.d.cts +1 -1
  28. package/dist/plugins/cms/query-keys.d.mts +1 -1
  29. package/dist/plugins/cms/query-keys.d.ts +1 -1
  30. package/dist/plugins/form-builder/api/index.d.cts +1 -1
  31. package/dist/plugins/form-builder/api/index.d.mts +1 -1
  32. package/dist/plugins/form-builder/api/index.d.ts +1 -1
  33. package/dist/shared/{stack.L-UFwz2G.d.cts → stack.oGOteE6g.d.cts} +27 -5
  34. package/dist/shared/{stack.L-UFwz2G.d.mts → stack.oGOteE6g.d.mts} +27 -5
  35. package/dist/shared/{stack.L-UFwz2G.d.ts → stack.oGOteE6g.d.ts} +27 -5
  36. package/package.json +1 -1
  37. package/src/plugins/cms/api/plugin.ts +667 -21
  38. package/src/plugins/cms/client/components/forms/content-form.tsx +60 -18
  39. package/src/plugins/cms/client/components/forms/relation-field.tsx +299 -0
  40. package/src/plugins/cms/client/components/inverse-relations-panel.tsx +329 -0
  41. package/src/plugins/cms/client/components/pages/content-editor-page.internal.tsx +127 -1
  42. package/src/plugins/cms/client/hooks/cms-hooks.tsx +344 -0
  43. package/src/plugins/cms/db.ts +38 -0
  44. package/src/plugins/cms/types.ts +99 -10
@@ -388,15 +388,205 @@ function useDeleteContent(typeSlug) {
388
388
  }
389
389
  });
390
390
  }
391
+ function useContentItemPopulated(typeSlug, id) {
392
+ const { apiBaseURL, apiBasePath, headers } = context.usePluginOverrides("cms");
393
+ const client$1 = client.createApiClient({
394
+ baseURL: apiBaseURL,
395
+ basePath: apiBasePath
396
+ });
397
+ const { data, isLoading, error, refetch } = reactQuery.useQuery({
398
+ queryKey: ["cmsContent", typeSlug, id, "populated"],
399
+ queryFn: async () => {
400
+ const response = await client$1(
401
+ "/content/:typeSlug/:id/populated",
402
+ {
403
+ method: "GET",
404
+ params: { typeSlug, id },
405
+ headers
406
+ }
407
+ );
408
+ if (isErrorResponse(response)) {
409
+ throw toError(response.error);
410
+ }
411
+ return response.data;
412
+ },
413
+ ...SHARED_QUERY_CONFIG,
414
+ enabled: !!typeSlug && !!id
415
+ });
416
+ return {
417
+ item: data ?? null,
418
+ isLoading,
419
+ error,
420
+ refetch
421
+ };
422
+ }
423
+ function useSuspenseContentItemPopulated(typeSlug, id) {
424
+ const { apiBaseURL, apiBasePath, headers } = context.usePluginOverrides("cms");
425
+ const client$1 = client.createApiClient({
426
+ baseURL: apiBaseURL,
427
+ basePath: apiBasePath
428
+ });
429
+ const { data, refetch, error, isFetching } = reactQuery.useSuspenseQuery({
430
+ queryKey: ["cmsContent", typeSlug, id, "populated"],
431
+ queryFn: async () => {
432
+ const response = await client$1(
433
+ "/content/:typeSlug/:id/populated",
434
+ {
435
+ method: "GET",
436
+ params: { typeSlug, id },
437
+ headers
438
+ }
439
+ );
440
+ if (isErrorResponse(response)) {
441
+ throw toError(response.error);
442
+ }
443
+ return response.data;
444
+ },
445
+ ...SHARED_QUERY_CONFIG
446
+ });
447
+ if (error && !isFetching) {
448
+ throw error;
449
+ }
450
+ return {
451
+ item: data ?? null,
452
+ refetch
453
+ };
454
+ }
455
+ function useContentByRelation(typeSlug, fieldName, targetId, options = {}) {
456
+ const { apiBaseURL, apiBasePath, headers } = context.usePluginOverrides("cms");
457
+ const client$1 = client.createApiClient({
458
+ baseURL: apiBaseURL,
459
+ basePath: apiBasePath
460
+ });
461
+ const { limit = 20, enabled = true } = options;
462
+ const {
463
+ data,
464
+ isLoading,
465
+ error,
466
+ fetchNextPage,
467
+ hasNextPage,
468
+ isFetchingNextPage,
469
+ refetch
470
+ } = reactQuery.useInfiniteQuery({
471
+ queryKey: ["cmsContent", typeSlug, "by-relation", fieldName, targetId],
472
+ queryFn: async ({ pageParam = 0 }) => {
473
+ const response = await client$1("/content/:typeSlug/by-relation", {
474
+ method: "GET",
475
+ params: { typeSlug },
476
+ query: { field: fieldName, targetId, limit, offset: pageParam },
477
+ headers
478
+ });
479
+ if (isErrorResponse(response)) {
480
+ throw toError(response.error);
481
+ }
482
+ return response.data;
483
+ },
484
+ ...SHARED_QUERY_CONFIG,
485
+ initialPageParam: 0,
486
+ getNextPageParam: (lastPage, allPages) => {
487
+ if (!lastPage || typeof lastPage !== "object") return void 0;
488
+ const items2 = lastPage?.items;
489
+ if (!Array.isArray(items2) || items2.length < limit) return void 0;
490
+ const loadedCount = (allPages || []).reduce(
491
+ (sum, page) => sum + (Array.isArray(page?.items) ? page.items.length : 0),
492
+ 0
493
+ );
494
+ const total2 = lastPage?.total ?? 0;
495
+ if (loadedCount >= total2) return void 0;
496
+ return loadedCount;
497
+ },
498
+ enabled: enabled && !!typeSlug && !!fieldName && !!targetId
499
+ });
500
+ const pages = data?.pages;
501
+ const items = pages?.flatMap(
502
+ (page) => Array.isArray(page?.items) ? page.items : []
503
+ ) ?? [];
504
+ const total = pages?.[0]?.total ?? 0;
505
+ return {
506
+ items,
507
+ total,
508
+ isLoading,
509
+ error,
510
+ loadMore: fetchNextPage,
511
+ hasMore: !!hasNextPage,
512
+ isLoadingMore: isFetchingNextPage,
513
+ refetch
514
+ };
515
+ }
516
+ function useSuspenseContentByRelation(typeSlug, fieldName, targetId, options = {}) {
517
+ const { apiBaseURL, apiBasePath, headers } = context.usePluginOverrides("cms");
518
+ const client$1 = client.createApiClient({
519
+ baseURL: apiBaseURL,
520
+ basePath: apiBasePath
521
+ });
522
+ const { limit = 20 } = options;
523
+ const {
524
+ data,
525
+ fetchNextPage,
526
+ hasNextPage,
527
+ isFetchingNextPage,
528
+ refetch,
529
+ error,
530
+ isFetching
531
+ } = reactQuery.useSuspenseInfiniteQuery({
532
+ queryKey: ["cmsContent", typeSlug, "by-relation", fieldName, targetId],
533
+ queryFn: async ({ pageParam = 0 }) => {
534
+ const response = await client$1("/content/:typeSlug/by-relation", {
535
+ method: "GET",
536
+ params: { typeSlug },
537
+ query: { field: fieldName, targetId, limit, offset: pageParam },
538
+ headers
539
+ });
540
+ if (isErrorResponse(response)) {
541
+ throw toError(response.error);
542
+ }
543
+ return response.data;
544
+ },
545
+ ...SHARED_QUERY_CONFIG,
546
+ initialPageParam: 0,
547
+ getNextPageParam: (lastPage, allPages) => {
548
+ if (!lastPage || typeof lastPage !== "object") return void 0;
549
+ const items2 = lastPage?.items;
550
+ if (!Array.isArray(items2) || items2.length < limit) return void 0;
551
+ const loadedCount = (allPages || []).reduce(
552
+ (sum, page) => sum + (Array.isArray(page?.items) ? page.items.length : 0),
553
+ 0
554
+ );
555
+ const total2 = lastPage?.total ?? 0;
556
+ if (loadedCount >= total2) return void 0;
557
+ return loadedCount;
558
+ }
559
+ });
560
+ if (error && !isFetching) {
561
+ throw error;
562
+ }
563
+ const pages = data.pages;
564
+ const items = pages?.flatMap(
565
+ (page) => Array.isArray(page?.items) ? page.items : []
566
+ ) ?? [];
567
+ const total = pages?.[0]?.total ?? 0;
568
+ return {
569
+ items,
570
+ total,
571
+ loadMore: fetchNextPage,
572
+ hasMore: !!hasNextPage,
573
+ isLoadingMore: isFetchingNextPage,
574
+ refetch
575
+ };
576
+ }
391
577
 
392
578
  exports.useContent = useContent;
579
+ exports.useContentByRelation = useContentByRelation;
393
580
  exports.useContentItem = useContentItem;
394
581
  exports.useContentItemBySlug = useContentItemBySlug;
582
+ exports.useContentItemPopulated = useContentItemPopulated;
395
583
  exports.useContentType = useContentType;
396
584
  exports.useContentTypes = useContentTypes;
397
585
  exports.useCreateContent = useCreateContent;
398
586
  exports.useDeleteContent = useDeleteContent;
399
587
  exports.useSuspenseContent = useSuspenseContent;
588
+ exports.useSuspenseContentByRelation = useSuspenseContentByRelation;
400
589
  exports.useSuspenseContentItem = useSuspenseContentItem;
590
+ exports.useSuspenseContentItemPopulated = useSuspenseContentItemPopulated;
401
591
  exports.useSuspenseContentTypes = useSuspenseContentTypes;
402
592
  exports.useUpdateContent = useUpdateContent;
@@ -386,5 +386,191 @@ function useDeleteContent(typeSlug) {
386
386
  }
387
387
  });
388
388
  }
389
+ function useContentItemPopulated(typeSlug, id) {
390
+ const { apiBaseURL, apiBasePath, headers } = usePluginOverrides("cms");
391
+ const client = createApiClient({
392
+ baseURL: apiBaseURL,
393
+ basePath: apiBasePath
394
+ });
395
+ const { data, isLoading, error, refetch } = useQuery({
396
+ queryKey: ["cmsContent", typeSlug, id, "populated"],
397
+ queryFn: async () => {
398
+ const response = await client(
399
+ "/content/:typeSlug/:id/populated",
400
+ {
401
+ method: "GET",
402
+ params: { typeSlug, id },
403
+ headers
404
+ }
405
+ );
406
+ if (isErrorResponse(response)) {
407
+ throw toError(response.error);
408
+ }
409
+ return response.data;
410
+ },
411
+ ...SHARED_QUERY_CONFIG,
412
+ enabled: !!typeSlug && !!id
413
+ });
414
+ return {
415
+ item: data ?? null,
416
+ isLoading,
417
+ error,
418
+ refetch
419
+ };
420
+ }
421
+ function useSuspenseContentItemPopulated(typeSlug, id) {
422
+ const { apiBaseURL, apiBasePath, headers } = usePluginOverrides("cms");
423
+ const client = createApiClient({
424
+ baseURL: apiBaseURL,
425
+ basePath: apiBasePath
426
+ });
427
+ const { data, refetch, error, isFetching } = useSuspenseQuery({
428
+ queryKey: ["cmsContent", typeSlug, id, "populated"],
429
+ queryFn: async () => {
430
+ const response = await client(
431
+ "/content/:typeSlug/:id/populated",
432
+ {
433
+ method: "GET",
434
+ params: { typeSlug, id },
435
+ headers
436
+ }
437
+ );
438
+ if (isErrorResponse(response)) {
439
+ throw toError(response.error);
440
+ }
441
+ return response.data;
442
+ },
443
+ ...SHARED_QUERY_CONFIG
444
+ });
445
+ if (error && !isFetching) {
446
+ throw error;
447
+ }
448
+ return {
449
+ item: data ?? null,
450
+ refetch
451
+ };
452
+ }
453
+ function useContentByRelation(typeSlug, fieldName, targetId, options = {}) {
454
+ const { apiBaseURL, apiBasePath, headers } = usePluginOverrides("cms");
455
+ const client = createApiClient({
456
+ baseURL: apiBaseURL,
457
+ basePath: apiBasePath
458
+ });
459
+ const { limit = 20, enabled = true } = options;
460
+ const {
461
+ data,
462
+ isLoading,
463
+ error,
464
+ fetchNextPage,
465
+ hasNextPage,
466
+ isFetchingNextPage,
467
+ refetch
468
+ } = useInfiniteQuery({
469
+ queryKey: ["cmsContent", typeSlug, "by-relation", fieldName, targetId],
470
+ queryFn: async ({ pageParam = 0 }) => {
471
+ const response = await client("/content/:typeSlug/by-relation", {
472
+ method: "GET",
473
+ params: { typeSlug },
474
+ query: { field: fieldName, targetId, limit, offset: pageParam },
475
+ headers
476
+ });
477
+ if (isErrorResponse(response)) {
478
+ throw toError(response.error);
479
+ }
480
+ return response.data;
481
+ },
482
+ ...SHARED_QUERY_CONFIG,
483
+ initialPageParam: 0,
484
+ getNextPageParam: (lastPage, allPages) => {
485
+ if (!lastPage || typeof lastPage !== "object") return void 0;
486
+ const items2 = lastPage?.items;
487
+ if (!Array.isArray(items2) || items2.length < limit) return void 0;
488
+ const loadedCount = (allPages || []).reduce(
489
+ (sum, page) => sum + (Array.isArray(page?.items) ? page.items.length : 0),
490
+ 0
491
+ );
492
+ const total2 = lastPage?.total ?? 0;
493
+ if (loadedCount >= total2) return void 0;
494
+ return loadedCount;
495
+ },
496
+ enabled: enabled && !!typeSlug && !!fieldName && !!targetId
497
+ });
498
+ const pages = data?.pages;
499
+ const items = pages?.flatMap(
500
+ (page) => Array.isArray(page?.items) ? page.items : []
501
+ ) ?? [];
502
+ const total = pages?.[0]?.total ?? 0;
503
+ return {
504
+ items,
505
+ total,
506
+ isLoading,
507
+ error,
508
+ loadMore: fetchNextPage,
509
+ hasMore: !!hasNextPage,
510
+ isLoadingMore: isFetchingNextPage,
511
+ refetch
512
+ };
513
+ }
514
+ function useSuspenseContentByRelation(typeSlug, fieldName, targetId, options = {}) {
515
+ const { apiBaseURL, apiBasePath, headers } = usePluginOverrides("cms");
516
+ const client = createApiClient({
517
+ baseURL: apiBaseURL,
518
+ basePath: apiBasePath
519
+ });
520
+ const { limit = 20 } = options;
521
+ const {
522
+ data,
523
+ fetchNextPage,
524
+ hasNextPage,
525
+ isFetchingNextPage,
526
+ refetch,
527
+ error,
528
+ isFetching
529
+ } = useSuspenseInfiniteQuery({
530
+ queryKey: ["cmsContent", typeSlug, "by-relation", fieldName, targetId],
531
+ queryFn: async ({ pageParam = 0 }) => {
532
+ const response = await client("/content/:typeSlug/by-relation", {
533
+ method: "GET",
534
+ params: { typeSlug },
535
+ query: { field: fieldName, targetId, limit, offset: pageParam },
536
+ headers
537
+ });
538
+ if (isErrorResponse(response)) {
539
+ throw toError(response.error);
540
+ }
541
+ return response.data;
542
+ },
543
+ ...SHARED_QUERY_CONFIG,
544
+ initialPageParam: 0,
545
+ getNextPageParam: (lastPage, allPages) => {
546
+ if (!lastPage || typeof lastPage !== "object") return void 0;
547
+ const items2 = lastPage?.items;
548
+ if (!Array.isArray(items2) || items2.length < limit) return void 0;
549
+ const loadedCount = (allPages || []).reduce(
550
+ (sum, page) => sum + (Array.isArray(page?.items) ? page.items.length : 0),
551
+ 0
552
+ );
553
+ const total2 = lastPage?.total ?? 0;
554
+ if (loadedCount >= total2) return void 0;
555
+ return loadedCount;
556
+ }
557
+ });
558
+ if (error && !isFetching) {
559
+ throw error;
560
+ }
561
+ const pages = data.pages;
562
+ const items = pages?.flatMap(
563
+ (page) => Array.isArray(page?.items) ? page.items : []
564
+ ) ?? [];
565
+ const total = pages?.[0]?.total ?? 0;
566
+ return {
567
+ items,
568
+ total,
569
+ loadMore: fetchNextPage,
570
+ hasMore: !!hasNextPage,
571
+ isLoadingMore: isFetchingNextPage,
572
+ refetch
573
+ };
574
+ }
389
575
 
390
- export { useContent, useContentItem, useContentItemBySlug, useContentType, useContentTypes, useCreateContent, useDeleteContent, useSuspenseContent, useSuspenseContentItem, useSuspenseContentTypes, useUpdateContent };
576
+ export { useContent, useContentByRelation, useContentItem, useContentItemBySlug, useContentItemPopulated, useContentType, useContentTypes, useCreateContent, useDeleteContent, useSuspenseContent, useSuspenseContentByRelation, useSuspenseContentItem, useSuspenseContentItemPopulated, useSuspenseContentTypes, useUpdateContent };
@@ -74,6 +74,44 @@ const cmsSchema = db.createDbPlugin("cms", {
74
74
  defaultValue: () => /* @__PURE__ */ new Date()
75
75
  }
76
76
  }
77
+ },
78
+ /**
79
+ * Junction table for content item relationships
80
+ * Stores many-to-many and one-to-many relations between content items
81
+ */
82
+ contentRelation: {
83
+ modelName: "contentRelation",
84
+ fields: {
85
+ /** The content item that has the relation field */
86
+ sourceId: {
87
+ type: "string",
88
+ required: true,
89
+ references: {
90
+ model: "contentItem",
91
+ field: "id",
92
+ onDelete: "cascade"
93
+ }
94
+ },
95
+ /** The content item being referenced */
96
+ targetId: {
97
+ type: "string",
98
+ required: true,
99
+ references: {
100
+ model: "contentItem",
101
+ field: "id",
102
+ onDelete: "cascade"
103
+ }
104
+ },
105
+ /** The field name in the source content type schema (e.g., "categoryIds") */
106
+ fieldName: {
107
+ type: "string",
108
+ required: true
109
+ },
110
+ createdAt: {
111
+ type: "date",
112
+ defaultValue: () => /* @__PURE__ */ new Date()
113
+ }
114
+ }
77
115
  }
78
116
  });
79
117
 
@@ -72,6 +72,44 @@ const cmsSchema = createDbPlugin("cms", {
72
72
  defaultValue: () => /* @__PURE__ */ new Date()
73
73
  }
74
74
  }
75
+ },
76
+ /**
77
+ * Junction table for content item relationships
78
+ * Stores many-to-many and one-to-many relations between content items
79
+ */
80
+ contentRelation: {
81
+ modelName: "contentRelation",
82
+ fields: {
83
+ /** The content item that has the relation field */
84
+ sourceId: {
85
+ type: "string",
86
+ required: true,
87
+ references: {
88
+ model: "contentItem",
89
+ field: "id",
90
+ onDelete: "cascade"
91
+ }
92
+ },
93
+ /** The content item being referenced */
94
+ targetId: {
95
+ type: "string",
96
+ required: true,
97
+ references: {
98
+ model: "contentItem",
99
+ field: "id",
100
+ onDelete: "cascade"
101
+ }
102
+ },
103
+ /** The field name in the source content type schema (e.g., "categoryIds") */
104
+ fieldName: {
105
+ type: "string",
106
+ required: true
107
+ },
108
+ createdAt: {
109
+ type: "date",
110
+ defaultValue: () => /* @__PURE__ */ new Date()
111
+ }
112
+ }
75
113
  }
76
114
  });
77
115
 
@@ -72,6 +72,46 @@ function AutoFormObject({
72
72
  return null;
73
73
  }
74
74
  if (zodBaseType === "ZodObject") {
75
+ const objectFieldConfig = fieldConfig?.[name] ?? {};
76
+ if (typeof objectFieldConfig.fieldType === "function") {
77
+ const zodInputProps2 = utils.zodToHtmlInputProps(item);
78
+ let isRequired2 = isRequiredByDependency || zodInputProps2.required || false;
79
+ if (objectFieldConfig.inputProps?.required !== void 0) {
80
+ isRequired2 = objectFieldConfig.inputProps.required;
81
+ }
82
+ const CustomComponent = objectFieldConfig.fieldType;
83
+ const ParentElement = objectFieldConfig.renderParent ?? DefaultParent;
84
+ return /* @__PURE__ */ jsxRuntime.jsx(
85
+ form.FormField,
86
+ {
87
+ control: form$1.control,
88
+ name: key,
89
+ render: ({ field }) => {
90
+ const fieldProps = {
91
+ ...zodInputProps2,
92
+ ...field,
93
+ ...objectFieldConfig.inputProps,
94
+ disabled: objectFieldConfig.inputProps?.disabled || isDisabled,
95
+ ref: void 0,
96
+ value: field.value
97
+ };
98
+ return /* @__PURE__ */ jsxRuntime.jsx(ParentElement, { children: /* @__PURE__ */ jsxRuntime.jsx(
99
+ CustomComponent,
100
+ {
101
+ zodInputProps: zodInputProps2,
102
+ field,
103
+ fieldConfigItem: objectFieldConfig,
104
+ label: objectFieldConfig.label || itemName,
105
+ isRequired: isRequired2,
106
+ zodItem: item,
107
+ fieldProps
108
+ }
109
+ ) }, `${key}.parent`);
110
+ }
111
+ },
112
+ key
113
+ );
114
+ }
75
115
  return /* @__PURE__ */ jsxRuntime.jsxs(accordion.AccordionItem, { value: name, className: "border-none", children: [
76
116
  /* @__PURE__ */ jsxRuntime.jsx(accordion.AccordionTrigger, { children: itemName }),
77
117
  /* @__PURE__ */ jsxRuntime.jsx(accordion.AccordionContent, { className: "p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -86,13 +126,53 @@ function AutoFormObject({
86
126
  ] }, key);
87
127
  }
88
128
  if (zodBaseType === "ZodArray") {
129
+ const arrayFieldConfig = fieldConfig?.[name] ?? {};
130
+ if (typeof arrayFieldConfig.fieldType === "function") {
131
+ const zodInputProps2 = utils.zodToHtmlInputProps(item);
132
+ let isRequired2 = isRequiredByDependency || zodInputProps2.required || false;
133
+ if (arrayFieldConfig.inputProps?.required !== void 0) {
134
+ isRequired2 = arrayFieldConfig.inputProps.required;
135
+ }
136
+ const CustomComponent = arrayFieldConfig.fieldType;
137
+ const ParentElement = arrayFieldConfig.renderParent ?? DefaultParent;
138
+ return /* @__PURE__ */ jsxRuntime.jsx(
139
+ form.FormField,
140
+ {
141
+ control: form$1.control,
142
+ name: key,
143
+ render: ({ field }) => {
144
+ const fieldProps = {
145
+ ...zodInputProps2,
146
+ ...field,
147
+ ...arrayFieldConfig.inputProps,
148
+ disabled: arrayFieldConfig.inputProps?.disabled || isDisabled,
149
+ ref: void 0,
150
+ value: field.value
151
+ };
152
+ return /* @__PURE__ */ jsxRuntime.jsx(ParentElement, { children: /* @__PURE__ */ jsxRuntime.jsx(
153
+ CustomComponent,
154
+ {
155
+ zodInputProps: zodInputProps2,
156
+ field,
157
+ fieldConfigItem: arrayFieldConfig,
158
+ label: arrayFieldConfig.label || itemName,
159
+ isRequired: isRequired2,
160
+ zodItem: item,
161
+ fieldProps
162
+ }
163
+ ) }, `${key}.parent`);
164
+ }
165
+ },
166
+ key
167
+ );
168
+ }
89
169
  return /* @__PURE__ */ jsxRuntime.jsx(
90
170
  array,
91
171
  {
92
172
  name,
93
173
  item,
94
174
  form: form$1,
95
- fieldConfig: fieldConfig?.[name] ?? {},
175
+ fieldConfig: arrayFieldConfig,
96
176
  path: [...path, name]
97
177
  },
98
178
  key
@@ -56,6 +56,46 @@ function AutoFormObject({
56
56
  return null;
57
57
  }
58
58
  if (zodBaseType === "ZodObject") {
59
+ const objectFieldConfig = fieldConfig?.[name] ?? {};
60
+ if (typeof objectFieldConfig.fieldType === "function") {
61
+ const zodInputProps2 = zodToHtmlInputProps(item);
62
+ let isRequired2 = isRequiredByDependency || zodInputProps2.required || false;
63
+ if (objectFieldConfig.inputProps?.required !== void 0) {
64
+ isRequired2 = objectFieldConfig.inputProps.required;
65
+ }
66
+ const CustomComponent = objectFieldConfig.fieldType;
67
+ const ParentElement = objectFieldConfig.renderParent ?? DefaultParent;
68
+ return /* @__PURE__ */ jsx(
69
+ FormField,
70
+ {
71
+ control: form.control,
72
+ name: key,
73
+ render: ({ field }) => {
74
+ const fieldProps = {
75
+ ...zodInputProps2,
76
+ ...field,
77
+ ...objectFieldConfig.inputProps,
78
+ disabled: objectFieldConfig.inputProps?.disabled || isDisabled,
79
+ ref: void 0,
80
+ value: field.value
81
+ };
82
+ return /* @__PURE__ */ jsx(ParentElement, { children: /* @__PURE__ */ jsx(
83
+ CustomComponent,
84
+ {
85
+ zodInputProps: zodInputProps2,
86
+ field,
87
+ fieldConfigItem: objectFieldConfig,
88
+ label: objectFieldConfig.label || itemName,
89
+ isRequired: isRequired2,
90
+ zodItem: item,
91
+ fieldProps
92
+ }
93
+ ) }, `${key}.parent`);
94
+ }
95
+ },
96
+ key
97
+ );
98
+ }
59
99
  return /* @__PURE__ */ jsxs(AccordionItem, { value: name, className: "border-none", children: [
60
100
  /* @__PURE__ */ jsx(AccordionTrigger, { children: itemName }),
61
101
  /* @__PURE__ */ jsx(AccordionContent, { className: "p-2", children: /* @__PURE__ */ jsx(
@@ -70,13 +110,53 @@ function AutoFormObject({
70
110
  ] }, key);
71
111
  }
72
112
  if (zodBaseType === "ZodArray") {
113
+ const arrayFieldConfig = fieldConfig?.[name] ?? {};
114
+ if (typeof arrayFieldConfig.fieldType === "function") {
115
+ const zodInputProps2 = zodToHtmlInputProps(item);
116
+ let isRequired2 = isRequiredByDependency || zodInputProps2.required || false;
117
+ if (arrayFieldConfig.inputProps?.required !== void 0) {
118
+ isRequired2 = arrayFieldConfig.inputProps.required;
119
+ }
120
+ const CustomComponent = arrayFieldConfig.fieldType;
121
+ const ParentElement = arrayFieldConfig.renderParent ?? DefaultParent;
122
+ return /* @__PURE__ */ jsx(
123
+ FormField,
124
+ {
125
+ control: form.control,
126
+ name: key,
127
+ render: ({ field }) => {
128
+ const fieldProps = {
129
+ ...zodInputProps2,
130
+ ...field,
131
+ ...arrayFieldConfig.inputProps,
132
+ disabled: arrayFieldConfig.inputProps?.disabled || isDisabled,
133
+ ref: void 0,
134
+ value: field.value
135
+ };
136
+ return /* @__PURE__ */ jsx(ParentElement, { children: /* @__PURE__ */ jsx(
137
+ CustomComponent,
138
+ {
139
+ zodInputProps: zodInputProps2,
140
+ field,
141
+ fieldConfigItem: arrayFieldConfig,
142
+ label: arrayFieldConfig.label || itemName,
143
+ isRequired: isRequired2,
144
+ zodItem: item,
145
+ fieldProps
146
+ }
147
+ ) }, `${key}.parent`);
148
+ }
149
+ },
150
+ key
151
+ );
152
+ }
73
153
  return /* @__PURE__ */ jsx(
74
154
  AutoFormArray,
75
155
  {
76
156
  name,
77
157
  item,
78
158
  form,
79
- fieldConfig: fieldConfig?.[name] ?? {},
159
+ fieldConfig: arrayFieldConfig,
80
160
  path: [...path, name]
81
161
  },
82
162
  key