@rebasepro/admin 0.2.1 → 0.2.3
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/{CollectionEditorDialog-BXIh2AXg.js → CollectionEditorDialog-CmGXXSY9.js} +6 -182
- package/dist/CollectionEditorDialog-CmGXXSY9.js.map +1 -0
- package/dist/{CollectionsStudioView-jR8iz_ja.js → CollectionsStudioView-DcLHT5bU.js} +4 -4
- package/dist/{CollectionsStudioView-jR8iz_ja.js.map → CollectionsStudioView-DcLHT5bU.js.map} +1 -1
- package/dist/{ContentHomePage-BQZWuOFb.js → ContentHomePage-C7vFqKSe.js} +2 -2
- package/dist/{ContentHomePage-BQZWuOFb.js.map → ContentHomePage-C7vFqKSe.js.map} +1 -1
- package/dist/{ExportCollectionAction-CMdiiv1L.js → ExportCollectionAction-BfN34eWX.js} +2 -2
- package/dist/{ExportCollectionAction-CMdiiv1L.js.map → ExportCollectionAction-BfN34eWX.js.map} +1 -1
- package/dist/{ImportCollectionAction-C05lE0IW.js → ImportCollectionAction-SZrInjhx.js} +2 -2
- package/dist/{ImportCollectionAction-C05lE0IW.js.map → ImportCollectionAction-SZrInjhx.js.map} +1 -1
- package/dist/{PropertyEditView-BB5xjnhZ.js → PropertyEditView-Cvryrb3B.js} +304 -326
- package/dist/PropertyEditView-Cvryrb3B.js.map +1 -0
- package/dist/{RolesView-CULIHWZ9.js → RolesView-BCb7qwWs.js} +2 -2
- package/dist/{RolesView-CULIHWZ9.js.map → RolesView-BCb7qwWs.js.map} +1 -1
- package/dist/{UsersView-D7_AtJ44.js → UsersView-Cex24r8H.js} +2 -2
- package/dist/{UsersView-D7_AtJ44.js.map → UsersView-Cex24r8H.js.map} +1 -1
- package/dist/collection_editor/ui/collection_editor/properties/RelationPropertyField.d.ts +1 -7
- package/dist/collection_editor_ui.js +3 -3
- package/dist/{index-D5OQhv-T.js → index-DjduZG1T.js} +3 -3
- package/dist/index-DjduZG1T.js.map +1 -0
- package/dist/{index-CoSNm3e3.js → index-MKPc70-v.js} +3 -3
- package/dist/index-MKPc70-v.js.map +1 -0
- package/dist/{index-BAM9KCmM.js → index-PLIQXpTt.js} +2 -2
- package/dist/{index-BAM9KCmM.js.map → index-PLIQXpTt.js.map} +1 -1
- package/dist/index.js +4 -4
- package/dist/{util-DtbWD7LF.js → util-DbWax_sV.js} +95 -15
- package/dist/{util-DtbWD7LF.js.map → util-DbWax_sV.js.map} +1 -1
- package/package.json +10 -9
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +1 -10
- package/src/collection_editor/ui/collection_editor/properties/RelationPropertyField.tsx +37 -57
- package/src/collection_editor/validateCollectionJson.ts +88 -1
- package/dist/CollectionEditorDialog-BXIh2AXg.js.map +0 -1
- package/dist/PropertyEditView-BB5xjnhZ.js.map +0 -1
- package/dist/index-CoSNm3e3.js.map +0 -1
- package/dist/index-D5OQhv-T.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/admin",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"description": "Rebase CMS — content management views, forms, and routing",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"@floating-ui/dom": "^1.7.6",
|
|
58
58
|
"cmdk": "^1.1.1",
|
|
59
59
|
"date-fns": "^3.6.0",
|
|
60
|
+
"exceljs": "^4.4.0",
|
|
60
61
|
"fast-equals": "6.0.0",
|
|
61
62
|
"fractional-indexing": "3.2.0",
|
|
62
63
|
"fuse.js": "^7.3.0",
|
|
@@ -79,17 +80,17 @@
|
|
|
79
80
|
"prosemirror-tables": "^1.8.5",
|
|
80
81
|
"prosemirror-transform": "^1.12.0",
|
|
81
82
|
"prosemirror-view": "^1.41.8",
|
|
83
|
+
"react-compiler-runtime": "1.0.0",
|
|
82
84
|
"react-dropzone": "^14.4.1",
|
|
83
85
|
"react-use-measure": "^2.1.7",
|
|
84
|
-
"exceljs": "^4.4.0",
|
|
85
86
|
"zod": "^3.25.76",
|
|
86
|
-
"@rebasepro/common": "0.2.
|
|
87
|
-
"@rebasepro/
|
|
88
|
-
"@rebasepro/
|
|
89
|
-
"@rebasepro/
|
|
90
|
-
"@rebasepro/
|
|
91
|
-
"@rebasepro/
|
|
92
|
-
"@rebasepro/
|
|
87
|
+
"@rebasepro/common": "0.2.3",
|
|
88
|
+
"@rebasepro/formex": "0.2.3",
|
|
89
|
+
"@rebasepro/core": "0.2.3",
|
|
90
|
+
"@rebasepro/schema-inference": "0.2.3",
|
|
91
|
+
"@rebasepro/ui": "0.2.3",
|
|
92
|
+
"@rebasepro/utils": "0.2.3",
|
|
93
|
+
"@rebasepro/types": "0.2.3"
|
|
93
94
|
},
|
|
94
95
|
"peerDependencies": {
|
|
95
96
|
"react": ">=19.0.0",
|
|
@@ -29,7 +29,6 @@ import { CollectionEditorSchema } from "./CollectionYupValidation";
|
|
|
29
29
|
import { GeneralSettingsForm } from "./GeneralSettingsForm";
|
|
30
30
|
import { DisplaySettingsForm } from "./DisplaySettingsForm";
|
|
31
31
|
import { CollectionPropertiesEditorForm } from "./CollectionPropertiesEditorForm";
|
|
32
|
-
import { CollectionRelationsTab } from "./CollectionRelationsTab";
|
|
33
32
|
import { CollectionsConfigController } from "../../types/config_controller";
|
|
34
33
|
import { CollectionEditorWelcomeView } from "./CollectionEditorWelcomeView";
|
|
35
34
|
import { CollectionInference } from "../../types/collection_inference";
|
|
@@ -163,7 +162,6 @@ type EditorView = "welcome"
|
|
|
163
162
|
| "properties"
|
|
164
163
|
| "loading"
|
|
165
164
|
| "extra_view"
|
|
166
|
-
| "relations"
|
|
167
165
|
| "rls";
|
|
168
166
|
|
|
169
167
|
export function CollectionEditor(props: CollectionEditorDialogProps & {
|
|
@@ -490,7 +488,7 @@ function CollectionEditorInternal<M extends Record<string, unknown>>({
|
|
|
490
488
|
const validation = (col: EntityCollection<M>) => {
|
|
491
489
|
|
|
492
490
|
let errors: Record<string, string> = {};
|
|
493
|
-
const schema = (currentView === "properties" || currentView === "
|
|
491
|
+
const schema = (currentView === "properties" || currentView === "general") && CollectionEditorSchema;
|
|
494
492
|
if (schema) {
|
|
495
493
|
const result = schema.safeParse(col);
|
|
496
494
|
if (!result.success) {
|
|
@@ -644,9 +642,6 @@ function CollectionEditorInternal<M extends Record<string, unknown>>({
|
|
|
644
642
|
{getDataSourceCapabilities(values.driver).supportsRLS && <Tab value={"rls"}>
|
|
645
643
|
RLS
|
|
646
644
|
</Tab>}
|
|
647
|
-
{getDataSourceCapabilities(values.driver).supportsRelations && <Tab value={"relations"}>
|
|
648
|
-
Relations
|
|
649
|
-
</Tab>}
|
|
650
645
|
</Tabs>
|
|
651
646
|
</div>
|
|
652
647
|
<div className="flex items-center gap-4">
|
|
@@ -752,10 +747,6 @@ function CollectionEditorInternal<M extends Record<string, unknown>>({
|
|
|
752
747
|
<DisplaySettingsForm expandKanban={expandKanban}/>
|
|
753
748
|
}
|
|
754
749
|
|
|
755
|
-
{currentView === "relations" && getDataSourceCapabilities(values.driver).supportsRelations &&
|
|
756
|
-
<CollectionRelationsTab/>
|
|
757
|
-
}
|
|
758
|
-
|
|
759
750
|
{currentView === "rls" && getDataSourceCapabilities(values.driver).supportsRLS &&
|
|
760
751
|
<CollectionRLSTab/>
|
|
761
752
|
}
|
|
@@ -14,16 +14,25 @@ import { CollectionsSelect } from "./ReferencePropertyField";
|
|
|
14
14
|
|
|
15
15
|
const ON_ACTION_OPTIONS: OnAction[] = ["cascade", "restrict", "no action", "set null", "set default"];
|
|
16
16
|
|
|
17
|
+
function getTargetSlug(target?: string | (() => string | { slug: string } | Record<string, unknown>)): string {
|
|
18
|
+
if (!target) return "";
|
|
19
|
+
if (typeof target === "string") return target;
|
|
20
|
+
try {
|
|
21
|
+
const resolved = target();
|
|
22
|
+
if (typeof resolved === "string") return resolved;
|
|
23
|
+
if (resolved && typeof resolved === "object" && "slug" in resolved && typeof resolved.slug === "string") {
|
|
24
|
+
return resolved.slug;
|
|
25
|
+
}
|
|
26
|
+
return "";
|
|
27
|
+
} catch {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
17
32
|
/**
|
|
18
33
|
* Property editor form for `type: "relation"` properties.
|
|
19
34
|
*
|
|
20
|
-
* This component edits
|
|
21
|
-
* 1. The `RelationProperty` fields on the property itself (relationName, etc.)
|
|
22
|
-
* 2. The matching `Relation` entry in `collection.relations[]`
|
|
23
|
-
*
|
|
24
|
-
* When a user configures a relation property, we sync the relation config
|
|
25
|
-
* back to the parent collection's `relations` array so saving the collection
|
|
26
|
-
* persists everything in one go.
|
|
35
|
+
* This component edits the `RelationProperty` fields on the property itself (target, relationName, etc.)
|
|
27
36
|
*/
|
|
28
37
|
export function RelationPropertyField({
|
|
29
38
|
disabled,
|
|
@@ -36,37 +45,22 @@ export function RelationPropertyField({
|
|
|
36
45
|
values,
|
|
37
46
|
errors,
|
|
38
47
|
setFieldValue
|
|
39
|
-
} = useFormex<RelationProperty & { id?: string
|
|
48
|
+
} = useFormex<RelationProperty & { id?: string }>();
|
|
40
49
|
|
|
41
50
|
const collectionRegistry = useCollectionRegistryController();
|
|
42
51
|
|
|
43
|
-
// ─── Read the parent collection form to sync `relations[]` ───
|
|
44
|
-
// The PropertyForm is nested inside a parent Formex for the whole collection.
|
|
45
|
-
// We reach it via a second useFormex keyed to the parent context.
|
|
46
|
-
// However, the PropertyForm uses its own isolated Formex, so we can't
|
|
47
|
-
// directly access the parent. Instead, we store the relation config
|
|
48
|
-
// on the property itself and let the save logic merge it.
|
|
49
|
-
//
|
|
50
|
-
// We store the full relation config on a transient `_relationConfig` key
|
|
51
|
-
// so the consumer (CollectionPropertiesEditorForm / save logic) can
|
|
52
|
-
// extract it and place it in `collection.relations[]`.
|
|
53
|
-
|
|
54
52
|
const relationName = values.relationName ?? "";
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const direction = (relationConfig.direction as string) ?? "owning";
|
|
62
|
-
const localKey = (relationConfig.localKey as string) ?? "";
|
|
63
|
-
const foreignKeyOnTarget = (relationConfig.foreignKeyOnTarget as string) ?? "";
|
|
64
|
-
const through = relationConfig.through as Record<string, string> | undefined;
|
|
53
|
+
const targetSlug = getTargetSlug(values.target);
|
|
54
|
+
const cardinality = values.cardinality ?? "one";
|
|
55
|
+
const direction = values.direction ?? "owning";
|
|
56
|
+
const localKey = values.localKey ?? "";
|
|
57
|
+
const foreignKeyOnTarget = values.foreignKeyOnTarget ?? "";
|
|
58
|
+
const through = values.through;
|
|
65
59
|
const throughTable = through?.table ?? "";
|
|
66
60
|
const throughSourceColumn = through?.sourceColumn ?? "";
|
|
67
61
|
const throughTargetColumn = through?.targetColumn ?? "";
|
|
68
|
-
const onUpdate =
|
|
69
|
-
const onDelete =
|
|
62
|
+
const onUpdate = values.onUpdate ?? "no action";
|
|
63
|
+
const onDelete = values.onDelete ?? "no action";
|
|
70
64
|
|
|
71
65
|
// Whether to show the junction table section
|
|
72
66
|
const showThrough = cardinality === "many" && direction === "owning";
|
|
@@ -75,26 +69,12 @@ export function RelationPropertyField({
|
|
|
75
69
|
// Whether to show the foreign key on target field
|
|
76
70
|
const showForeignKey = direction === "inverse";
|
|
77
71
|
|
|
78
|
-
const updateRelationConfig = useCallback(
|
|
79
|
-
(patch: Record<string, unknown>) => {
|
|
80
|
-
const current = (values._relationConfig as Record<string, unknown>) ?? {};
|
|
81
|
-
setFieldValue("_relationConfig" as keyof (RelationProperty & { _relationConfig?: unknown }), { ...current,
|
|
82
|
-
...patch });
|
|
83
|
-
},
|
|
84
|
-
[values, setFieldValue]
|
|
85
|
-
);
|
|
86
|
-
|
|
87
72
|
const updateThrough = useCallback(
|
|
88
73
|
(patch: Record<string, unknown>) => {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
setFieldValue("_relationConfig" as keyof (RelationProperty & { _relationConfig?: unknown }), {
|
|
92
|
-
...current,
|
|
93
|
-
through: { ...currentThrough,
|
|
94
|
-
...patch }
|
|
95
|
-
});
|
|
74
|
+
const currentThrough = values.through ?? { table: "", sourceColumn: "", targetColumn: "" };
|
|
75
|
+
setFieldValue("through", { ...currentThrough, ...patch });
|
|
96
76
|
},
|
|
97
|
-
[values, setFieldValue]
|
|
77
|
+
[values.through, setFieldValue]
|
|
98
78
|
);
|
|
99
79
|
|
|
100
80
|
// Auto-generate relationName from target collection slug
|
|
@@ -102,7 +82,7 @@ export function RelationPropertyField({
|
|
|
102
82
|
if (targetSlug && !relationName) {
|
|
103
83
|
setFieldValue("relationName", targetSlug);
|
|
104
84
|
}
|
|
105
|
-
}, [targetSlug]);
|
|
85
|
+
}, [targetSlug, relationName, setFieldValue]);
|
|
106
86
|
|
|
107
87
|
const collections: EntityCollection[] = collectionRegistry?.collections ?? [];
|
|
108
88
|
|
|
@@ -112,10 +92,10 @@ export function RelationPropertyField({
|
|
|
112
92
|
<div className={"col-span-12"}>
|
|
113
93
|
<CollectionsSelect
|
|
114
94
|
disabled={disabled}
|
|
115
|
-
pathPath={"
|
|
95
|
+
pathPath={"target"}
|
|
116
96
|
value={targetSlug}
|
|
117
97
|
setFieldValue={(_, value) => {
|
|
118
|
-
|
|
98
|
+
setFieldValue("target", value);
|
|
119
99
|
// Auto-generate relation name from target
|
|
120
100
|
if (!relationName || relationName === targetSlug) {
|
|
121
101
|
setFieldValue("relationName", value);
|
|
@@ -152,7 +132,7 @@ export function RelationPropertyField({
|
|
|
152
132
|
<div className={"col-span-12 sm:col-span-6"}>
|
|
153
133
|
<Select
|
|
154
134
|
value={cardinality}
|
|
155
|
-
onValueChange={(v) =>
|
|
135
|
+
onValueChange={(v) => setFieldValue("cardinality", v as "one" | "many")}
|
|
156
136
|
label={"Cardinality"}
|
|
157
137
|
disabled={disabled}
|
|
158
138
|
fullWidth
|
|
@@ -184,7 +164,7 @@ export function RelationPropertyField({
|
|
|
184
164
|
<div className={"col-span-12 sm:col-span-6"}>
|
|
185
165
|
<Select
|
|
186
166
|
value={direction}
|
|
187
|
-
onValueChange={(v) =>
|
|
167
|
+
onValueChange={(v) => setFieldValue("direction", v as "owning" | "inverse")}
|
|
188
168
|
label={"Direction"}
|
|
189
169
|
disabled={disabled}
|
|
190
170
|
fullWidth
|
|
@@ -218,7 +198,7 @@ export function RelationPropertyField({
|
|
|
218
198
|
<TextField
|
|
219
199
|
value={localKey}
|
|
220
200
|
onChange={(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
|
|
221
|
-
|
|
201
|
+
setFieldValue("localKey", e.target.value)
|
|
222
202
|
}
|
|
223
203
|
label={"Local key (foreign key column on this table)"}
|
|
224
204
|
disabled={disabled}
|
|
@@ -236,7 +216,7 @@ export function RelationPropertyField({
|
|
|
236
216
|
<TextField
|
|
237
217
|
value={foreignKeyOnTarget}
|
|
238
218
|
onChange={(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
|
|
239
|
-
|
|
219
|
+
setFieldValue("foreignKeyOnTarget", e.target.value)
|
|
240
220
|
}
|
|
241
221
|
label={"Foreign key on target table"}
|
|
242
222
|
disabled={disabled}
|
|
@@ -305,7 +285,7 @@ export function RelationPropertyField({
|
|
|
305
285
|
<div className={"col-span-12 sm:col-span-6"}>
|
|
306
286
|
<Select
|
|
307
287
|
value={onUpdate}
|
|
308
|
-
onValueChange={(v) =>
|
|
288
|
+
onValueChange={(v) => setFieldValue("onUpdate", v as OnAction)}
|
|
309
289
|
label={"On update"}
|
|
310
290
|
disabled={disabled}
|
|
311
291
|
fullWidth
|
|
@@ -325,7 +305,7 @@ export function RelationPropertyField({
|
|
|
325
305
|
<div className={"col-span-12 sm:col-span-6"}>
|
|
326
306
|
<Select
|
|
327
307
|
value={onDelete}
|
|
328
|
-
onValueChange={(v) =>
|
|
308
|
+
onValueChange={(v) => setFieldValue("onDelete", v as OnAction)}
|
|
329
309
|
label={"On delete"}
|
|
330
310
|
disabled={disabled}
|
|
331
311
|
fullWidth
|
|
@@ -10,8 +10,11 @@ const VALID_DATA_TYPES = [
|
|
|
10
10
|
"date",
|
|
11
11
|
"geopoint",
|
|
12
12
|
"reference",
|
|
13
|
+
"relation",
|
|
13
14
|
"array",
|
|
14
|
-
"map"
|
|
15
|
+
"map",
|
|
16
|
+
"vector",
|
|
17
|
+
"binary"
|
|
15
18
|
] as const;
|
|
16
19
|
|
|
17
20
|
type DataType = typeof VALID_DATA_TYPES[number];
|
|
@@ -109,6 +112,90 @@ function validateProperty(
|
|
|
109
112
|
}
|
|
110
113
|
}
|
|
111
114
|
|
|
115
|
+
// Validate relation property
|
|
116
|
+
if (property.dataType === "relation") {
|
|
117
|
+
if (property.target !== undefined && typeof property.target !== "string" && typeof property.target !== "function") {
|
|
118
|
+
errors.push({
|
|
119
|
+
path: `${path}.target`,
|
|
120
|
+
message: "Must be a string (collection slug) or a function"
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (property.cardinality !== undefined && property.cardinality !== "one" && property.cardinality !== "many") {
|
|
124
|
+
errors.push({
|
|
125
|
+
path: `${path}.cardinality`,
|
|
126
|
+
message: "Must be either 'one' or 'many'"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (property.direction !== undefined && property.direction !== "owning" && property.direction !== "inverse") {
|
|
130
|
+
errors.push({
|
|
131
|
+
path: `${path}.direction`,
|
|
132
|
+
message: "Must be either 'owning' or 'inverse'"
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (property.localKey !== undefined && typeof property.localKey !== "string") {
|
|
136
|
+
errors.push({
|
|
137
|
+
path: `${path}.localKey`,
|
|
138
|
+
message: "Must be a string"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (property.foreignKeyOnTarget !== undefined && typeof property.foreignKeyOnTarget !== "string") {
|
|
142
|
+
errors.push({
|
|
143
|
+
path: `${path}.foreignKeyOnTarget`,
|
|
144
|
+
message: "Must be a string"
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (property.through !== undefined) {
|
|
148
|
+
if (typeof property.through !== "object" || property.through === null) {
|
|
149
|
+
errors.push({
|
|
150
|
+
path: `${path}.through`,
|
|
151
|
+
message: "Must be an object"
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
const throughObj = property.through as Record<string, unknown>;
|
|
155
|
+
if (throughObj.table !== undefined && typeof throughObj.table !== "string") {
|
|
156
|
+
errors.push({
|
|
157
|
+
path: `${path}.through.table`,
|
|
158
|
+
message: "Must be a string"
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (throughObj.sourceColumn !== undefined && typeof throughObj.sourceColumn !== "string") {
|
|
162
|
+
errors.push({
|
|
163
|
+
path: `${path}.through.sourceColumn`,
|
|
164
|
+
message: "Must be a string"
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (throughObj.targetColumn !== undefined && typeof throughObj.targetColumn !== "string") {
|
|
168
|
+
errors.push({
|
|
169
|
+
path: `${path}.through.targetColumn`,
|
|
170
|
+
message: "Must be a string"
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (property.onUpdate !== undefined && typeof property.onUpdate !== "string") {
|
|
176
|
+
errors.push({
|
|
177
|
+
path: `${path}.onUpdate`,
|
|
178
|
+
message: "Must be a string"
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
if (property.onDelete !== undefined && typeof property.onDelete !== "string") {
|
|
182
|
+
errors.push({
|
|
183
|
+
path: `${path}.onDelete`,
|
|
184
|
+
message: "Must be a string"
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Validate vector property
|
|
190
|
+
if (property.dataType === "vector") {
|
|
191
|
+
if (property.dimensions !== undefined && (typeof property.dimensions !== "number" || isNaN(property.dimensions) || property.dimensions <= 0)) {
|
|
192
|
+
errors.push({
|
|
193
|
+
path: `${path}.dimensions`,
|
|
194
|
+
message: "Must be a positive number"
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
112
199
|
// Validate storage config for string
|
|
113
200
|
if (property.dataType === "string" && property.storage) {
|
|
114
201
|
if (typeof property.storage !== "object") {
|