@tinacms/schema-tools 0.0.6 → 0.0.9
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/LICENSE +176 -0
- package/dist/index.es.js +715 -0
- package/dist/index.js +690 -705
- package/dist/types/SchemaTypes.d.ts +10 -3
- package/dist/validate/schema.d.ts +34 -0
- package/dist/validate/tinaCloudSchemaConfig.d.ts +18 -0
- package/package.json +19 -12
- package/CHANGELOG.md +0 -59
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
import * as yup from "yup";
|
|
2
|
+
import z$1, { z, ZodError } from "zod";
|
|
3
|
+
function addNamespaceToSchema(maybeNode, namespace = []) {
|
|
4
|
+
if (typeof maybeNode === "string") {
|
|
5
|
+
return maybeNode;
|
|
6
|
+
}
|
|
7
|
+
if (typeof maybeNode === "boolean") {
|
|
8
|
+
return maybeNode;
|
|
9
|
+
}
|
|
10
|
+
const newNode = { ...maybeNode };
|
|
11
|
+
const keys = Object.keys(maybeNode);
|
|
12
|
+
Object.values(maybeNode).map((m, index) => {
|
|
13
|
+
const key = keys[index];
|
|
14
|
+
if (Array.isArray(m)) {
|
|
15
|
+
newNode[key] = m.map((element) => {
|
|
16
|
+
if (!element) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!element.hasOwnProperty("name")) {
|
|
20
|
+
return element;
|
|
21
|
+
}
|
|
22
|
+
const value = element.name || element.value;
|
|
23
|
+
return addNamespaceToSchema(element, [...namespace, value]);
|
|
24
|
+
});
|
|
25
|
+
} else {
|
|
26
|
+
if (!m) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!m.hasOwnProperty("name")) {
|
|
30
|
+
newNode[key] = m;
|
|
31
|
+
} else {
|
|
32
|
+
newNode[key] = addNamespaceToSchema(m, [...namespace, m.name]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return { ...newNode, namespace };
|
|
37
|
+
}
|
|
38
|
+
function assertShape(value, yupSchema, errorMessage) {
|
|
39
|
+
const shape = yupSchema(yup);
|
|
40
|
+
try {
|
|
41
|
+
shape.validateSync(value);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
const message = errorMessage || `Failed to assertShape - ${e.message}`;
|
|
44
|
+
throw new Error(message);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const lastItem = (arr) => {
|
|
48
|
+
if (typeof arr === "undefined") {
|
|
49
|
+
throw new Error("Can not call lastItem when arr is undefined");
|
|
50
|
+
}
|
|
51
|
+
return arr[arr.length - 1];
|
|
52
|
+
};
|
|
53
|
+
const capitalize = (s) => {
|
|
54
|
+
if (typeof s !== "string")
|
|
55
|
+
return "";
|
|
56
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
57
|
+
};
|
|
58
|
+
const generateNamespacedFieldName = (names, suffix = "") => {
|
|
59
|
+
return (suffix ? [...names, suffix] : names).map(capitalize).join("");
|
|
60
|
+
};
|
|
61
|
+
const NAMER = {
|
|
62
|
+
dataFilterTypeNameOn: (namespace) => {
|
|
63
|
+
return generateNamespacedFieldName(namespace, "_FilterOn");
|
|
64
|
+
},
|
|
65
|
+
dataFilterTypeName: (namespace) => {
|
|
66
|
+
return generateNamespacedFieldName(namespace, "Filter");
|
|
67
|
+
},
|
|
68
|
+
dataMutationTypeNameOn: (namespace) => {
|
|
69
|
+
return generateNamespacedFieldName(namespace, "_MutationOn");
|
|
70
|
+
},
|
|
71
|
+
dataMutationTypeName: (namespace) => {
|
|
72
|
+
return generateNamespacedFieldName(namespace, "Mutation");
|
|
73
|
+
},
|
|
74
|
+
updateName: (namespace) => {
|
|
75
|
+
return "update" + generateNamespacedFieldName(namespace, "Document");
|
|
76
|
+
},
|
|
77
|
+
createName: (namespace) => {
|
|
78
|
+
return "create" + generateNamespacedFieldName(namespace, "Document");
|
|
79
|
+
},
|
|
80
|
+
queryName: (namespace) => {
|
|
81
|
+
return "get" + generateNamespacedFieldName(namespace, "Document");
|
|
82
|
+
},
|
|
83
|
+
generateQueryListName: (namespace) => {
|
|
84
|
+
return "get" + generateNamespacedFieldName(namespace, "List");
|
|
85
|
+
},
|
|
86
|
+
fragmentName: (namespace) => {
|
|
87
|
+
return generateNamespacedFieldName(namespace, "") + "Parts";
|
|
88
|
+
},
|
|
89
|
+
collectionTypeName: (namespace) => {
|
|
90
|
+
return generateNamespacedFieldName(namespace, "Collection");
|
|
91
|
+
},
|
|
92
|
+
documentTypeName: (namespace) => {
|
|
93
|
+
return generateNamespacedFieldName(namespace, "Document");
|
|
94
|
+
},
|
|
95
|
+
dataTypeName: (namespace) => {
|
|
96
|
+
return generateNamespacedFieldName(namespace, "");
|
|
97
|
+
},
|
|
98
|
+
referenceConnectionType: (namespace) => {
|
|
99
|
+
return generateNamespacedFieldName(namespace, "Connection");
|
|
100
|
+
},
|
|
101
|
+
referenceConnectionEdgesTypeName: (namespace) => {
|
|
102
|
+
return generateNamespacedFieldName(namespace, "ConnectionEdges");
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
function hasDuplicates(array) {
|
|
106
|
+
if (!array) {
|
|
107
|
+
return false;
|
|
108
|
+
} else {
|
|
109
|
+
return new Set(array).size !== array.length;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
class TinaSchema {
|
|
113
|
+
constructor(config) {
|
|
114
|
+
this.config = config;
|
|
115
|
+
this.getIsTitleFieldName = (collection) => {
|
|
116
|
+
var _a;
|
|
117
|
+
const col = this.getCollection(collection);
|
|
118
|
+
const field = (_a = col == null ? void 0 : col.fields) == null ? void 0 : _a.find((x) => x.type === "string" && x.isTitle);
|
|
119
|
+
return field == null ? void 0 : field.name;
|
|
120
|
+
};
|
|
121
|
+
this.getCollectionsByName = (collectionNames) => {
|
|
122
|
+
return this.schema.collections.filter((collection) => collectionNames.includes(collection.name));
|
|
123
|
+
};
|
|
124
|
+
this.getAllCollectionPaths = () => {
|
|
125
|
+
const paths = this.getCollections().map((collection) => `${collection.path}${collection.match || ""}`);
|
|
126
|
+
return paths;
|
|
127
|
+
};
|
|
128
|
+
this.getCollection = (collectionName) => {
|
|
129
|
+
const collection = this.schema.collections.find((collection2) => collection2.name === collectionName);
|
|
130
|
+
if (!collection) {
|
|
131
|
+
throw new Error(`Expected to find collection named ${collectionName}`);
|
|
132
|
+
}
|
|
133
|
+
const extraFields = {};
|
|
134
|
+
const templateInfo = this.getTemplatesForCollectable(collection);
|
|
135
|
+
switch (templateInfo.type) {
|
|
136
|
+
case "object":
|
|
137
|
+
extraFields["fields"] = templateInfo.template.fields;
|
|
138
|
+
break;
|
|
139
|
+
case "union":
|
|
140
|
+
extraFields["templates"] = templateInfo.templates;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
slug: collection.name,
|
|
145
|
+
...extraFields,
|
|
146
|
+
...collection,
|
|
147
|
+
format: collection.format || "md"
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
this.getCollections = () => {
|
|
151
|
+
return this.schema.collections.map((collection) => this.getCollection(collection.name)) || [];
|
|
152
|
+
};
|
|
153
|
+
this.getGlobalTemplate = (templateName) => {
|
|
154
|
+
var _a;
|
|
155
|
+
const globalTemplate = (_a = this.schema.templates) == null ? void 0 : _a.find((template) => template.name === templateName);
|
|
156
|
+
if (!globalTemplate) {
|
|
157
|
+
throw new Error(`Expected to find global template of name ${templateName}`);
|
|
158
|
+
}
|
|
159
|
+
return globalTemplate;
|
|
160
|
+
};
|
|
161
|
+
this.getCollectionByFullPath = (filepath) => {
|
|
162
|
+
const collection = this.getCollections().find((collection2) => {
|
|
163
|
+
return filepath.replace("\\", "/").startsWith(collection2.path);
|
|
164
|
+
});
|
|
165
|
+
if (!collection) {
|
|
166
|
+
throw new Error(`Unable to find collection for file at ${filepath}`);
|
|
167
|
+
}
|
|
168
|
+
return collection;
|
|
169
|
+
};
|
|
170
|
+
this.getCollectionAndTemplateByFullPath = (filepath, templateName) => {
|
|
171
|
+
let template;
|
|
172
|
+
const collection = this.getCollections().find((collection2) => {
|
|
173
|
+
return filepath.replace("\\", "/").startsWith(collection2.path);
|
|
174
|
+
});
|
|
175
|
+
if (!collection) {
|
|
176
|
+
throw new Error(`Unable to find collection for file at ${filepath}`);
|
|
177
|
+
}
|
|
178
|
+
const templates = this.getTemplatesForCollectable(collection);
|
|
179
|
+
if (templates.type === "union") {
|
|
180
|
+
if (templateName) {
|
|
181
|
+
template = templates.templates.find((template2) => lastItem(template2.namespace) === templateName);
|
|
182
|
+
if (!template) {
|
|
183
|
+
throw new Error(`Unable to determine template for item at ${filepath}`);
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
throw new Error(`Unable to determine template for item at ${filepath}, no template name provided for collection with multiple templates`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (templates.type === "object") {
|
|
190
|
+
template = templates.template;
|
|
191
|
+
}
|
|
192
|
+
if (!template) {
|
|
193
|
+
throw new Error(`Something went wrong while trying to determine template for ${filepath}`);
|
|
194
|
+
}
|
|
195
|
+
return { collection, template };
|
|
196
|
+
};
|
|
197
|
+
this.getTemplateForData = ({
|
|
198
|
+
data,
|
|
199
|
+
collection
|
|
200
|
+
}) => {
|
|
201
|
+
const templateInfo = this.getTemplatesForCollectable(collection);
|
|
202
|
+
switch (templateInfo.type) {
|
|
203
|
+
case "object":
|
|
204
|
+
return templateInfo.template;
|
|
205
|
+
case "union":
|
|
206
|
+
assertShape(data, (yup2) => yup2.object({ _template: yup2.string().required() }));
|
|
207
|
+
const template = templateInfo.templates.find((template2) => template2.namespace[template2.namespace.length - 1] === data._template);
|
|
208
|
+
if (!template) {
|
|
209
|
+
throw new Error(`Expected to find template named '${data._template}' for collection '${lastItem(collection.namespace)}'`);
|
|
210
|
+
}
|
|
211
|
+
return template;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
this.isMarkdownCollection = (collectionName) => {
|
|
215
|
+
const collection = this.getCollection(collectionName);
|
|
216
|
+
const format = collection.format;
|
|
217
|
+
if (!format) {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
if (["markdown", "md"].includes(format)) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
224
|
+
};
|
|
225
|
+
this.getTemplatesForCollectable = (collection) => {
|
|
226
|
+
let extraFields = [];
|
|
227
|
+
if (collection.references) {
|
|
228
|
+
extraFields = collection.references;
|
|
229
|
+
}
|
|
230
|
+
if (collection.fields) {
|
|
231
|
+
const template = typeof collection.fields === "string" ? this.getGlobalTemplate(collection.fields) : collection;
|
|
232
|
+
if (typeof template.fields === "string" || typeof template.fields === "undefined") {
|
|
233
|
+
throw new Error("Exptected template to have fields but none were found");
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
namespace: collection.namespace,
|
|
237
|
+
type: "object",
|
|
238
|
+
template: {
|
|
239
|
+
...template,
|
|
240
|
+
fields: [...template.fields, ...extraFields]
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
} else {
|
|
244
|
+
if (collection.templates) {
|
|
245
|
+
return {
|
|
246
|
+
namespace: collection.namespace,
|
|
247
|
+
type: "union",
|
|
248
|
+
templates: collection.templates.map((templateOrTemplateString) => {
|
|
249
|
+
const template = typeof templateOrTemplateString === "string" ? this.getGlobalTemplate(templateOrTemplateString) : templateOrTemplateString;
|
|
250
|
+
return {
|
|
251
|
+
...template,
|
|
252
|
+
fields: [...template.fields, ...extraFields]
|
|
253
|
+
};
|
|
254
|
+
})
|
|
255
|
+
};
|
|
256
|
+
} else {
|
|
257
|
+
throw new Error(`Expected either fields or templates array to be defined on collection ${collection.namespace.join("_")}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
this.schema = config;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const resolveField = ({ namespace, ...field }, schema) => {
|
|
265
|
+
var _a;
|
|
266
|
+
field.parentTypename = NAMER.dataTypeName(namespace.filter((_, i) => i < namespace.length - 1));
|
|
267
|
+
const extraFields = field.ui || {};
|
|
268
|
+
switch (field.type) {
|
|
269
|
+
case "number":
|
|
270
|
+
return {
|
|
271
|
+
component: "number",
|
|
272
|
+
...field,
|
|
273
|
+
...extraFields
|
|
274
|
+
};
|
|
275
|
+
case "datetime":
|
|
276
|
+
return {
|
|
277
|
+
component: "date",
|
|
278
|
+
...field,
|
|
279
|
+
...extraFields
|
|
280
|
+
};
|
|
281
|
+
case "boolean":
|
|
282
|
+
return {
|
|
283
|
+
component: "toggle",
|
|
284
|
+
...field,
|
|
285
|
+
...extraFields
|
|
286
|
+
};
|
|
287
|
+
case "image":
|
|
288
|
+
return {
|
|
289
|
+
component: "image",
|
|
290
|
+
clearable: true,
|
|
291
|
+
...field,
|
|
292
|
+
...extraFields
|
|
293
|
+
};
|
|
294
|
+
case "string":
|
|
295
|
+
if (field.options) {
|
|
296
|
+
if (field.list) {
|
|
297
|
+
return {
|
|
298
|
+
component: "checkbox-group",
|
|
299
|
+
...field,
|
|
300
|
+
...extraFields,
|
|
301
|
+
options: field.options
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
component: "select",
|
|
306
|
+
...field,
|
|
307
|
+
...extraFields,
|
|
308
|
+
options: [{ label: `Choose an option`, value: "" }, ...field.options]
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (field.list) {
|
|
312
|
+
return {
|
|
313
|
+
component: "list",
|
|
314
|
+
field: {
|
|
315
|
+
component: "text"
|
|
316
|
+
},
|
|
317
|
+
...field,
|
|
318
|
+
...extraFields
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
component: "text",
|
|
323
|
+
...field,
|
|
324
|
+
...extraFields
|
|
325
|
+
};
|
|
326
|
+
case "object":
|
|
327
|
+
const templateInfo = schema.getTemplatesForCollectable({
|
|
328
|
+
...field,
|
|
329
|
+
namespace
|
|
330
|
+
});
|
|
331
|
+
if (templateInfo.type === "object") {
|
|
332
|
+
return {
|
|
333
|
+
...field,
|
|
334
|
+
component: field.list ? "group-list" : "group",
|
|
335
|
+
fields: templateInfo.template.fields.map((field2) => resolveField(field2, schema)),
|
|
336
|
+
...extraFields
|
|
337
|
+
};
|
|
338
|
+
} else if (templateInfo.type === "union") {
|
|
339
|
+
const templates2 = {};
|
|
340
|
+
const typeMap2 = {};
|
|
341
|
+
templateInfo.templates.forEach((template) => {
|
|
342
|
+
const extraFields2 = template.ui || {};
|
|
343
|
+
const templateName = lastItem(template.namespace);
|
|
344
|
+
typeMap2[templateName] = NAMER.dataTypeName(template.namespace);
|
|
345
|
+
templates2[lastItem(template.namespace)] = {
|
|
346
|
+
label: template.label || templateName,
|
|
347
|
+
key: templateName,
|
|
348
|
+
fields: template.fields.map((field2) => resolveField(field2, schema)),
|
|
349
|
+
...extraFields2
|
|
350
|
+
};
|
|
351
|
+
return true;
|
|
352
|
+
});
|
|
353
|
+
return {
|
|
354
|
+
...field,
|
|
355
|
+
typeMap: typeMap2,
|
|
356
|
+
component: field.list ? "blocks" : "not-implemented",
|
|
357
|
+
templates: templates2,
|
|
358
|
+
...extraFields
|
|
359
|
+
};
|
|
360
|
+
} else {
|
|
361
|
+
throw new Error(`Unknown object for resolveField function`);
|
|
362
|
+
}
|
|
363
|
+
case "rich-text":
|
|
364
|
+
const templates = {};
|
|
365
|
+
(_a = field.templates) == null ? void 0 : _a.forEach((template) => {
|
|
366
|
+
if (typeof template === "string") {
|
|
367
|
+
throw new Error(`Global templates not yet supported for rich-text`);
|
|
368
|
+
} else {
|
|
369
|
+
const extraFields2 = template.ui || {};
|
|
370
|
+
const templateName = lastItem(template.namespace);
|
|
371
|
+
NAMER.dataTypeName(template.namespace);
|
|
372
|
+
templates[lastItem(template.namespace)] = {
|
|
373
|
+
label: template.label || templateName,
|
|
374
|
+
key: templateName,
|
|
375
|
+
inline: template.inline,
|
|
376
|
+
name: templateName,
|
|
377
|
+
fields: template.fields.map((field2) => resolveField(field2, schema)),
|
|
378
|
+
...extraFields2
|
|
379
|
+
};
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
return {
|
|
384
|
+
...field,
|
|
385
|
+
templates: Object.values(templates),
|
|
386
|
+
component: "rich-text",
|
|
387
|
+
...extraFields
|
|
388
|
+
};
|
|
389
|
+
case "reference":
|
|
390
|
+
return {
|
|
391
|
+
...field,
|
|
392
|
+
component: "reference",
|
|
393
|
+
...extraFields
|
|
394
|
+
};
|
|
395
|
+
default:
|
|
396
|
+
throw new Error(`Unknown field type ${field.type}`);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const resolveForm = ({
|
|
400
|
+
collection,
|
|
401
|
+
basename,
|
|
402
|
+
template,
|
|
403
|
+
schema
|
|
404
|
+
}) => {
|
|
405
|
+
return {
|
|
406
|
+
id: basename,
|
|
407
|
+
label: collection.label,
|
|
408
|
+
name: basename,
|
|
409
|
+
fields: template.fields.map((field) => {
|
|
410
|
+
return resolveField(field, schema);
|
|
411
|
+
})
|
|
412
|
+
};
|
|
413
|
+
};
|
|
414
|
+
const parseZodError = ({ zodError }) => {
|
|
415
|
+
var _a, _b, _c, _d;
|
|
416
|
+
const errors = zodError.flatten((issue) => {
|
|
417
|
+
const moreInfo = [];
|
|
418
|
+
if (issue.code === "invalid_union") {
|
|
419
|
+
issue.unionErrors.map((unionError) => {
|
|
420
|
+
moreInfo.push(parseZodError({ zodError: unionError }));
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(".")}`;
|
|
424
|
+
const errorMessages = [errorMessage, ...moreInfo];
|
|
425
|
+
return {
|
|
426
|
+
errors: errorMessages
|
|
427
|
+
};
|
|
428
|
+
});
|
|
429
|
+
const formErrors = errors.formErrors.flatMap((x) => x.errors);
|
|
430
|
+
const parsedErrors = [
|
|
431
|
+
...((_b = (_a = errors.fieldErrors) == null ? void 0 : _a.collections) == null ? void 0 : _b.flatMap((x) => x.errors)) || [],
|
|
432
|
+
...((_d = (_c = errors.fieldErrors) == null ? void 0 : _c.config) == null ? void 0 : _d.flatMap((x) => x.errors)) || [],
|
|
433
|
+
...formErrors
|
|
434
|
+
];
|
|
435
|
+
return parsedErrors;
|
|
436
|
+
};
|
|
437
|
+
const name = z.string({
|
|
438
|
+
required_error: "Name is required but not provided",
|
|
439
|
+
invalid_type_error: "Name must be a string"
|
|
440
|
+
});
|
|
441
|
+
const TypeName = [
|
|
442
|
+
"string",
|
|
443
|
+
"boolean",
|
|
444
|
+
"number",
|
|
445
|
+
"datetime",
|
|
446
|
+
"image",
|
|
447
|
+
"object",
|
|
448
|
+
"reference",
|
|
449
|
+
"rich-text"
|
|
450
|
+
];
|
|
451
|
+
const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
|
|
452
|
+
const typeRequiredError = `type is required and must be one of ${TypeName.join(", ")}`;
|
|
453
|
+
const nameProp = z.string({
|
|
454
|
+
required_error: "name must be provided",
|
|
455
|
+
invalid_type_error: "name must be a sting"
|
|
456
|
+
}).superRefine((val, ctx) => {
|
|
457
|
+
if (val.includes(" "))
|
|
458
|
+
ctx.addIssue({
|
|
459
|
+
message: "name cannot contain spaces",
|
|
460
|
+
code: z.ZodIssueCode.custom,
|
|
461
|
+
fatal: true
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
const Option = z.union([z.string(), z.object({ label: z.string(), value: z.string() })], {
|
|
465
|
+
errorMap: () => {
|
|
466
|
+
return {
|
|
467
|
+
message: "Invalid option array. Must be a string[] or {label: string, value: string}[]"
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
const TinaField = z.object({
|
|
472
|
+
name: nameProp,
|
|
473
|
+
label: z.string().optional(),
|
|
474
|
+
description: z.string().optional(),
|
|
475
|
+
required: z.boolean().optional()
|
|
476
|
+
});
|
|
477
|
+
const FieldWithList = TinaField.extend({ list: z.boolean().optional() });
|
|
478
|
+
const TinaScalerBase = FieldWithList.extend({
|
|
479
|
+
options: z.array(Option).optional()
|
|
480
|
+
});
|
|
481
|
+
const StringField = TinaScalerBase.extend({
|
|
482
|
+
type: z.literal("string", {
|
|
483
|
+
invalid_type_error: typeTypeError,
|
|
484
|
+
required_error: typeRequiredError
|
|
485
|
+
}),
|
|
486
|
+
isTitle: z.boolean().optional()
|
|
487
|
+
});
|
|
488
|
+
const BooleanField = TinaScalerBase.extend({
|
|
489
|
+
type: z.literal("boolean", {
|
|
490
|
+
invalid_type_error: typeTypeError,
|
|
491
|
+
required_error: typeRequiredError
|
|
492
|
+
})
|
|
493
|
+
});
|
|
494
|
+
const NumberField = TinaScalerBase.extend({
|
|
495
|
+
type: z.literal("number", {
|
|
496
|
+
invalid_type_error: typeTypeError,
|
|
497
|
+
required_error: typeRequiredError
|
|
498
|
+
})
|
|
499
|
+
});
|
|
500
|
+
const ImageField = TinaScalerBase.extend({
|
|
501
|
+
type: z.literal("image", {
|
|
502
|
+
invalid_type_error: typeTypeError,
|
|
503
|
+
required_error: typeRequiredError
|
|
504
|
+
})
|
|
505
|
+
});
|
|
506
|
+
const DateTimeField = TinaScalerBase.extend({
|
|
507
|
+
type: z.literal("datetime", {
|
|
508
|
+
invalid_type_error: typeTypeError,
|
|
509
|
+
required_error: typeRequiredError
|
|
510
|
+
}),
|
|
511
|
+
dateFormat: z.string().optional(),
|
|
512
|
+
timeFormat: z.string().optional()
|
|
513
|
+
});
|
|
514
|
+
const ReferenceField = FieldWithList.extend({
|
|
515
|
+
type: z.literal("reference", {
|
|
516
|
+
invalid_type_error: typeTypeError,
|
|
517
|
+
required_error: typeRequiredError
|
|
518
|
+
})
|
|
519
|
+
});
|
|
520
|
+
const TinaFieldZod = z.lazy(() => {
|
|
521
|
+
const TemplateTemp = z.object({
|
|
522
|
+
label: z.string(),
|
|
523
|
+
name: nameProp,
|
|
524
|
+
fields: z.array(TinaFieldZod)
|
|
525
|
+
}).refine((val) => {
|
|
526
|
+
var _a;
|
|
527
|
+
return !hasDuplicates((_a = val.fields) == null ? void 0 : _a.map((x) => x.name));
|
|
528
|
+
}, {
|
|
529
|
+
message: "Fields must have a unique name"
|
|
530
|
+
});
|
|
531
|
+
const ObjectField = FieldWithList.extend({
|
|
532
|
+
type: z.literal("object", {
|
|
533
|
+
invalid_type_error: typeTypeError,
|
|
534
|
+
required_error: typeRequiredError
|
|
535
|
+
}),
|
|
536
|
+
fields: z.array(TinaFieldZod).min(1).optional().refine((val) => !hasDuplicates(val == null ? void 0 : val.map((x) => x.name)), {
|
|
537
|
+
message: "Fields must have a unique name"
|
|
538
|
+
}),
|
|
539
|
+
templates: z.array(TemplateTemp).min(1).optional().refine((val) => !hasDuplicates(val == null ? void 0 : val.map((x) => x.name)), {
|
|
540
|
+
message: "Templates must have a unique name"
|
|
541
|
+
})
|
|
542
|
+
});
|
|
543
|
+
const RichTextField = FieldWithList.extend({
|
|
544
|
+
type: z.literal("rich-text", {
|
|
545
|
+
invalid_type_error: typeTypeError,
|
|
546
|
+
required_error: typeRequiredError
|
|
547
|
+
}),
|
|
548
|
+
templates: z.array(TemplateTemp).optional().refine((val) => !hasDuplicates(val == null ? void 0 : val.map((x) => x.name)), {
|
|
549
|
+
message: "Templates must have a unique name"
|
|
550
|
+
})
|
|
551
|
+
});
|
|
552
|
+
return z.discriminatedUnion("type", [
|
|
553
|
+
StringField,
|
|
554
|
+
BooleanField,
|
|
555
|
+
NumberField,
|
|
556
|
+
ImageField,
|
|
557
|
+
DateTimeField,
|
|
558
|
+
ReferenceField,
|
|
559
|
+
ObjectField,
|
|
560
|
+
RichTextField
|
|
561
|
+
], {
|
|
562
|
+
errorMap: (issue, ctx) => {
|
|
563
|
+
var _a;
|
|
564
|
+
if (issue.code === "invalid_union_discriminator") {
|
|
565
|
+
return {
|
|
566
|
+
message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
return {
|
|
570
|
+
message: issue.message
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
}).superRefine((val, ctx) => {
|
|
574
|
+
if (val.type === "string") {
|
|
575
|
+
if (val.isTitle) {
|
|
576
|
+
if (val.list) {
|
|
577
|
+
ctx.addIssue({
|
|
578
|
+
code: z.ZodIssueCode.custom,
|
|
579
|
+
message: "You can not have `list: true` when using `isTitle`"
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
if (!val.required) {
|
|
583
|
+
ctx.addIssue({
|
|
584
|
+
code: z.ZodIssueCode.custom,
|
|
585
|
+
message: "You must have { required: true } when using `isTitle`"
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (val.type === "object") {
|
|
591
|
+
const message = "Must provide one of templates or fields in your collection";
|
|
592
|
+
let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
|
|
593
|
+
if (!isValid) {
|
|
594
|
+
ctx.addIssue({
|
|
595
|
+
code: z.ZodIssueCode.custom,
|
|
596
|
+
message
|
|
597
|
+
});
|
|
598
|
+
return false;
|
|
599
|
+
} else {
|
|
600
|
+
isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
|
|
601
|
+
if (!isValid) {
|
|
602
|
+
ctx.addIssue({
|
|
603
|
+
code: z.ZodIssueCode.custom,
|
|
604
|
+
message
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
return isValid;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return true;
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
const tinaConfigKey = z$1.object({
|
|
614
|
+
publicFolder: z$1.string(),
|
|
615
|
+
mediaRoot: z$1.string()
|
|
616
|
+
}).strict().optional();
|
|
617
|
+
const tinaConfigZod = z$1.object({
|
|
618
|
+
client: z$1.object({ referenceDepth: z$1.number().optional() }).optional(),
|
|
619
|
+
media: z$1.object({
|
|
620
|
+
tina: tinaConfigKey,
|
|
621
|
+
loadCustomStore: z$1.function().optional()
|
|
622
|
+
}).optional()
|
|
623
|
+
});
|
|
624
|
+
const validateTinaCloudSchemaConfig = (config) => {
|
|
625
|
+
const newConfig = tinaConfigZod.parse(config);
|
|
626
|
+
return newConfig;
|
|
627
|
+
};
|
|
628
|
+
const FORMATS = ["json", "md", "markdown", "mdx"];
|
|
629
|
+
const Template = z.object({
|
|
630
|
+
label: z.string({
|
|
631
|
+
invalid_type_error: "label must be a string",
|
|
632
|
+
required_error: "label was not provided but is required"
|
|
633
|
+
}),
|
|
634
|
+
name,
|
|
635
|
+
fields: z.array(TinaFieldZod)
|
|
636
|
+
}).refine((val) => {
|
|
637
|
+
var _a;
|
|
638
|
+
return !hasDuplicates((_a = val.fields) == null ? void 0 : _a.map((x) => x.name));
|
|
639
|
+
}, {
|
|
640
|
+
message: "Fields must have a unique name"
|
|
641
|
+
});
|
|
642
|
+
const TinaCloudCollectionBase = z.object({
|
|
643
|
+
label: z.string().optional(),
|
|
644
|
+
name,
|
|
645
|
+
format: z.enum(FORMATS).optional()
|
|
646
|
+
});
|
|
647
|
+
const TinaCloudCollection = TinaCloudCollectionBase.extend({
|
|
648
|
+
fields: z.array(TinaFieldZod).min(1).optional().refine((val) => !hasDuplicates(val == null ? void 0 : val.map((x) => x.name)), {
|
|
649
|
+
message: "Fields must have a unique name"
|
|
650
|
+
}).refine((val) => {
|
|
651
|
+
const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
|
|
652
|
+
return arr.length < 2;
|
|
653
|
+
}, {
|
|
654
|
+
message: "Fields can only have one use of `isTitle`"
|
|
655
|
+
}),
|
|
656
|
+
templates: z.array(Template).min(1).optional().refine((val) => !hasDuplicates(val == null ? void 0 : val.map((x) => x.name)), {
|
|
657
|
+
message: "Templates must have a unique name"
|
|
658
|
+
})
|
|
659
|
+
}).refine((val) => {
|
|
660
|
+
let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
|
|
661
|
+
if (!isValid) {
|
|
662
|
+
return false;
|
|
663
|
+
} else {
|
|
664
|
+
isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
|
|
665
|
+
return isValid;
|
|
666
|
+
}
|
|
667
|
+
}, { message: "Must provide one of templates or fields in your collection" });
|
|
668
|
+
const TinaCloudSchemaZod = z.object({
|
|
669
|
+
collections: z.array(TinaCloudCollection),
|
|
670
|
+
config: tinaConfigZod.optional()
|
|
671
|
+
}).superRefine((val, ctx) => {
|
|
672
|
+
var _a, _b;
|
|
673
|
+
if (hasDuplicates(val.collections.map((x) => x.name))) {
|
|
674
|
+
ctx.addIssue({
|
|
675
|
+
code: z.ZodIssueCode.custom,
|
|
676
|
+
message: "can not have two collections with the same name",
|
|
677
|
+
fatal: true
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
(_a = val == null ? void 0 : val.collections) == null ? void 0 : _a.map((x) => {
|
|
681
|
+
if (!x.format) {
|
|
682
|
+
console.warn(`No format provided for collection ${x.name}, defaulting to .md`);
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
const media = (_b = val == null ? void 0 : val.config) == null ? void 0 : _b.media;
|
|
686
|
+
if (media && media.tina && media.loadCustomStore) {
|
|
687
|
+
ctx.addIssue({
|
|
688
|
+
code: z.ZodIssueCode.custom,
|
|
689
|
+
message: "can not have both loadCustomStore and tina. Must use one or the other",
|
|
690
|
+
fatal: true,
|
|
691
|
+
path: ["config", "media"]
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
class TinaSchemaValidationError extends Error {
|
|
696
|
+
constructor(message) {
|
|
697
|
+
super(message);
|
|
698
|
+
this.name = "TinaSchemaValidationError";
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const validateSchema = ({
|
|
702
|
+
config
|
|
703
|
+
}) => {
|
|
704
|
+
try {
|
|
705
|
+
TinaCloudSchemaZod.parse(config);
|
|
706
|
+
} catch (e) {
|
|
707
|
+
if (e instanceof ZodError) {
|
|
708
|
+
const errors = parseZodError({ zodError: e });
|
|
709
|
+
throw new TinaSchemaValidationError(errors.join(", \n"));
|
|
710
|
+
} else {
|
|
711
|
+
throw new Error(e);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
export { TinaSchema, TinaSchemaValidationError, addNamespaceToSchema, resolveField, resolveForm, validateSchema, validateTinaCloudSchemaConfig };
|