@lontar/core 0.1.1 → 0.1.2
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/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.js +98 -34
- package/dist/{zod-ClIdYqay.js → zod-DShD3pW7.js} +26 -8
- package/package.json +1 -1
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { A as ClientUploadField, C as ClientRichTextField, D as ClientTextField, E as ClientSlugField, O as ClientTextareaField, S as ClientRelationshipField, T as ClientSelectOption, _ as ClientDateField, a as WhereClause, b as ClientGlobal, c as SystemFieldName, d as UploadSystemFieldName, f as UploadSystemFields, g as ClientConfig, h as ClientCollection, i as Where, k as ClientUploadConfig, l as SystemFields, m as ClientCheckboxField, n as FilterOperator, o as WhereField, p as CLIENT_CONFIG_VERSION, r as SortItem, s as SYSTEM_FIELDS, t as FieldFilter, u as UPLOAD_SYSTEM_FIELDS, v as ClientEmailField, w as ClientSelectField, x as ClientNumberField, y as ClientField } from "../query-XQ3OKPOy.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
export declare function isRichTextEmpty(val: unknown): boolean;
|
|
3
4
|
/** Optional fields accept `null` and `undefined`, since their column is nullable. */
|
|
4
5
|
export declare function clientFieldToZod(field: ClientField): z.ZodType;
|
|
5
6
|
/**
|
package/dist/client/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { i as CLIENT_CONFIG_VERSION, a as SYSTEM_FIELDS, o as UPLOAD_SYSTEM_FIELDS, t as clientFieldToZod, n as clientFieldsToZod, r as isRichTextEmpty } from "../zod-DShD3pW7.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { a as WhereClause, b as ClientGlobal, c as SystemFieldName, d as UploadSystemFieldName, f as UploadSystemFields, g as ClientConfig, h as ClientCollection, i as Where, l as SystemFields, n as FilterOperator, r as SortItem, s as SYSTEM_FIELDS, t as FieldFilter, u as UPLOAD_SYSTEM_FIELDS } from "./query-XQ3OKPOy.js";
|
|
2
2
|
import { betterAuth } from "better-auth";
|
|
3
3
|
import { Context, Hono } from "hono";
|
|
4
|
+
import sharp from "sharp";
|
|
4
5
|
interface BaseField {
|
|
5
6
|
/** Shown in the admin. Defaults to the field name, humanized. */
|
|
6
7
|
label?: string;
|
|
@@ -127,7 +128,20 @@ interface ProcessedImageResult {
|
|
|
127
128
|
sizes: Record<string, ProcessedImageSize>;
|
|
128
129
|
}
|
|
129
130
|
export declare function isResizableImage(mimeType: string): boolean;
|
|
131
|
+
export declare function isAnimatedMimeType(mimeType: string): boolean;
|
|
132
|
+
export declare function getOrientedDimensions(meta: {
|
|
133
|
+
width?: number;
|
|
134
|
+
height?: number;
|
|
135
|
+
orientation?: number;
|
|
136
|
+
pageHeight?: number;
|
|
137
|
+
}): {
|
|
138
|
+
width?: number;
|
|
139
|
+
height?: number;
|
|
140
|
+
};
|
|
130
141
|
export declare function formatVariantFilename(originalFilename: string, sizeName: string, format?: string): string;
|
|
142
|
+
type SharpModule = typeof sharp;
|
|
143
|
+
export declare function getSharp(): Promise<SharpModule>;
|
|
144
|
+
export declare function setSharp(impl: SharpModule | null): void;
|
|
131
145
|
export declare function processImage(args: {
|
|
132
146
|
filename: string;
|
|
133
147
|
buffer: Buffer;
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as SYSTEM_FIELDS, n as clientFieldsToZod, o as UPLOAD_SYSTEM_FIELDS } from "./zod-DShD3pW7.js";
|
|
2
2
|
import { betterAuth } from "better-auth";
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import { bodyLimit } from "hono/body-limit";
|
|
5
5
|
import { fileTypeFromBuffer } from "file-type";
|
|
6
|
-
import sharp from "sharp";
|
|
7
6
|
// src/config/collection.ts
|
|
8
7
|
/**
|
|
9
8
|
* Returns the config unchanged. It exists to infer `F`, which types InferDoc and
|
|
@@ -30,6 +29,12 @@ const RESERVED_SLUGS = /* @__PURE__ */ new Set([
|
|
|
30
29
|
]);
|
|
31
30
|
const SYSTEM_FIELD_NAMES = new Set(SYSTEM_FIELDS);
|
|
32
31
|
const UPLOAD_SYSTEM_FIELD_NAMES = new Set(UPLOAD_SYSTEM_FIELDS);
|
|
32
|
+
const TEXT_LIKE_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
33
|
+
"text",
|
|
34
|
+
"textarea",
|
|
35
|
+
"email",
|
|
36
|
+
"select"
|
|
37
|
+
]);
|
|
33
38
|
const MAX_VARCHAR_LENGTH = 16383;
|
|
34
39
|
var LontarConfigError = class extends Error {
|
|
35
40
|
issues;
|
|
@@ -51,9 +56,9 @@ function defineConfig(config) {
|
|
|
51
56
|
function validateConfig(config) {
|
|
52
57
|
const issues = validateSecret(config.secret);
|
|
53
58
|
issues.push(...validatePagination("Config", config.pagination));
|
|
54
|
-
const
|
|
55
|
-
issues.push(...
|
|
56
|
-
issues.push(...validateGlobals(config.globals,
|
|
59
|
+
const { issues: collectionIssues, collectionMap } = validateCollections(config.collections);
|
|
60
|
+
issues.push(...collectionIssues);
|
|
61
|
+
issues.push(...validateGlobals(config.globals, collectionMap));
|
|
57
62
|
issues.push(...validateStorage(config));
|
|
58
63
|
return issues;
|
|
59
64
|
}
|
|
@@ -75,41 +80,48 @@ function validateCollections(collections) {
|
|
|
75
80
|
collectionMap.set(collection.slug, collection);
|
|
76
81
|
issues.push(...validateCollection(collection));
|
|
77
82
|
}
|
|
78
|
-
for (const collection of collections) issues.push(...
|
|
83
|
+
for (const collection of collections) issues.push(...validateFieldTargets(`Collection "${collection.slug}"`, collection.fields, collectionMap));
|
|
79
84
|
return {
|
|
80
85
|
issues,
|
|
81
|
-
|
|
86
|
+
collectionMap
|
|
82
87
|
};
|
|
83
88
|
}
|
|
84
|
-
function validateGlobals(globals,
|
|
89
|
+
function validateGlobals(globals, collectionMap) {
|
|
85
90
|
if (!globals) return [];
|
|
86
91
|
const issues = [];
|
|
87
92
|
const seen = /* @__PURE__ */ new Set();
|
|
88
93
|
for (const global of globals) {
|
|
89
94
|
if (seen.has(global.slug)) issues.push(`Global slug "${global.slug}" is used twice.`);
|
|
90
|
-
if (
|
|
95
|
+
if (collectionMap.has(global.slug)) issues.push(`Global slug "${global.slug}" collides with a collection slug.`);
|
|
91
96
|
seen.add(global.slug);
|
|
92
97
|
issues.push(...validateGlobal(global));
|
|
98
|
+
issues.push(...validateFieldTargets(`Global "${global.slug}"`, global.fields, collectionMap));
|
|
93
99
|
}
|
|
94
100
|
return issues;
|
|
95
101
|
}
|
|
96
|
-
function
|
|
102
|
+
function validateFieldTargets(where, fields, collectionMap) {
|
|
97
103
|
const issues = [];
|
|
98
|
-
for (const [name, field] of Object.entries(
|
|
99
|
-
|
|
100
|
-
const target = collectionMap.get(field.relationTo);
|
|
101
|
-
if (!target) issues.push(`Collection "${collection.slug}": upload field "${name}" refers to unknown collection "${field.relationTo}".`);
|
|
102
|
-
else if (!target.upload) issues.push(`Collection "${collection.slug}": upload field "${name}" refers to collection "${field.relationTo}" which does not have upload enabled.`);
|
|
103
|
-
}
|
|
104
|
+
for (const [name, field] of Object.entries(fields)) if (field.type === "upload") issues.push(...validateUploadTarget(where, name, field, collectionMap));
|
|
105
|
+
else if (field.type === "relationship") issues.push(...validateRelationshipTarget(where, name, field, collectionMap));
|
|
104
106
|
return issues;
|
|
105
107
|
}
|
|
108
|
+
function validateUploadTarget(where, name, field, collectionMap) {
|
|
109
|
+
const target = collectionMap.get(field.relationTo);
|
|
110
|
+
if (!target) return [`${where}: upload field "${name}" refers to unknown collection "${field.relationTo}".`];
|
|
111
|
+
if (!target.upload) return [`${where}: upload field "${name}" refers to collection "${field.relationTo}" which does not have upload enabled.`];
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
function validateRelationshipTarget(where, name, field, collectionMap) {
|
|
115
|
+
if (!collectionMap.has(field.relationTo)) return [`${where}: relationship field "${name}" refers to unknown collection "${field.relationTo}".`];
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
106
118
|
function validateCollection(collection) {
|
|
107
119
|
const where = `Collection "${collection.slug}"`;
|
|
108
120
|
const isUpload = Boolean(collection.upload);
|
|
109
121
|
return [
|
|
110
122
|
...validateSlug(collection.slug, "Collection"),
|
|
111
123
|
...Object.keys(collection.fields).flatMap((name) => validateFieldName(where, name, isUpload)),
|
|
112
|
-
...Object.entries(collection.fields).flatMap(([name, field]) => validateField(where, name, field)),
|
|
124
|
+
...Object.entries(collection.fields).flatMap(([name, field]) => validateField(where, name, field, collection.fields)),
|
|
113
125
|
...validatePagination(where, collection.pagination),
|
|
114
126
|
...validateAdmin(where, collection),
|
|
115
127
|
...validateAccess(where, collection)
|
|
@@ -132,13 +144,22 @@ function validateGlobal(global) {
|
|
|
132
144
|
return [
|
|
133
145
|
...validateSlug(global.slug, "Global"),
|
|
134
146
|
...Object.keys(global.fields).flatMap((name) => validateFieldName(where, name)),
|
|
135
|
-
...Object.entries(global.fields).flatMap(([name, field]) => validateField(where, name, field)),
|
|
147
|
+
...Object.entries(global.fields).flatMap(([name, field]) => validateField(where, name, field, global.fields)),
|
|
136
148
|
...validateGlobalAccess(where, global)
|
|
137
149
|
];
|
|
138
150
|
}
|
|
139
|
-
function validateField(where, name, field) {
|
|
151
|
+
function validateField(where, name, field, fields) {
|
|
140
152
|
if (field.type === "text") return validateTextField(where, name, field);
|
|
141
153
|
if (field.type === "textarea") return validateTextareaField(where, name, field);
|
|
154
|
+
if (field.type === "slug") return validateSlugField(where, name, field, fields);
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
function validateSlugField(where, name, field, fields) {
|
|
158
|
+
if (field.from === void 0) return [];
|
|
159
|
+
if (field.from === name) return [`${where}: slug field "${name}" cannot refer to itself in \`from\`.`];
|
|
160
|
+
const sourceField = fields[field.from];
|
|
161
|
+
if (!sourceField) return [`${where}: slug field "${name}" \`from\` refers to unknown field "${field.from}".`];
|
|
162
|
+
if (!TEXT_LIKE_FIELD_TYPES.has(sourceField.type)) return [`${where}: slug field "${name}" \`from\` must refer to a text-like field, but "${field.from}" is of type "${sourceField.type}".`];
|
|
142
163
|
return [];
|
|
143
164
|
}
|
|
144
165
|
function validateTextField(where, name, field) {
|
|
@@ -349,10 +370,18 @@ function clientLabels({ slug, labels = {} }) {
|
|
|
349
370
|
plural
|
|
350
371
|
};
|
|
351
372
|
}
|
|
373
|
+
function isTextLike(type) {
|
|
374
|
+
return type === "text" || type === "email" || type === "slug";
|
|
375
|
+
}
|
|
376
|
+
function resolveDefaultTitle(fields, upload) {
|
|
377
|
+
const textLikeField = fields.find((field) => isTextLike(field.type));
|
|
378
|
+
if (textLikeField) return textLikeField.name;
|
|
379
|
+
if (upload) return "filename";
|
|
380
|
+
return "id";
|
|
381
|
+
}
|
|
352
382
|
function clientAdmin(collection, fields) {
|
|
353
383
|
const { admin = {}, upload } = collection;
|
|
354
|
-
const
|
|
355
|
-
const useAsTitle = admin.useAsTitle ?? defaultTitle;
|
|
384
|
+
const useAsTitle = admin.useAsTitle ?? resolveDefaultTitle(fields, Boolean(upload));
|
|
356
385
|
const others = fields.map((field) => field.name).filter((name) => name !== useAsTitle);
|
|
357
386
|
return {
|
|
358
387
|
useAsTitle,
|
|
@@ -900,6 +929,22 @@ const FORMAT_TO_MIME = {
|
|
|
900
929
|
function isResizableImage(mimeType) {
|
|
901
930
|
return RESIZABLE_MIME_TYPES.has(mimeType.toLowerCase());
|
|
902
931
|
}
|
|
932
|
+
function isAnimatedMimeType(mimeType) {
|
|
933
|
+
const lower = mimeType.toLowerCase();
|
|
934
|
+
return lower === "image/gif" || lower === "image/webp";
|
|
935
|
+
}
|
|
936
|
+
function getOrientedDimensions(meta) {
|
|
937
|
+
if (meta.width === void 0 || meta.height === void 0) return {
|
|
938
|
+
width: meta.width,
|
|
939
|
+
height: meta.height
|
|
940
|
+
};
|
|
941
|
+
const isTransposed = typeof meta.orientation === "number" && meta.orientation >= 5;
|
|
942
|
+
const rawHeight = meta.pageHeight ?? meta.height;
|
|
943
|
+
return {
|
|
944
|
+
width: isTransposed ? rawHeight : meta.width,
|
|
945
|
+
height: isTransposed ? meta.width : rawHeight
|
|
946
|
+
};
|
|
947
|
+
}
|
|
903
948
|
function formatVariantFilename(originalFilename, sizeName, format) {
|
|
904
949
|
const lastDot = originalFilename.lastIndexOf(".");
|
|
905
950
|
const base = lastDot > 0 ? originalFilename.slice(0, lastDot) : originalFilename;
|
|
@@ -910,9 +955,25 @@ function resolveMimeType(originalMime, format) {
|
|
|
910
955
|
if (format && FORMAT_TO_MIME[format]) return FORMAT_TO_MIME[format];
|
|
911
956
|
return originalMime;
|
|
912
957
|
}
|
|
913
|
-
|
|
958
|
+
let cachedSharp = null;
|
|
959
|
+
async function getSharp() {
|
|
960
|
+
if (cachedSharp) return cachedSharp;
|
|
914
961
|
try {
|
|
915
|
-
|
|
962
|
+
const mod = await import("sharp");
|
|
963
|
+
cachedSharp = mod.default ?? mod;
|
|
964
|
+
return cachedSharp;
|
|
965
|
+
} catch (cause) {
|
|
966
|
+
throw new Error("The \"sharp\" image processing library is required to process images, but failed to load.", { cause });
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
function setSharp(impl) {
|
|
970
|
+
cachedSharp = impl;
|
|
971
|
+
}
|
|
972
|
+
async function resizeVariant(sharp, buffer, name, config, originalFilename, originalMimeType) {
|
|
973
|
+
try {
|
|
974
|
+
const targetMimeType = resolveMimeType(originalMimeType, config.format);
|
|
975
|
+
const isAnimated = isAnimatedMimeType(originalMimeType) && isAnimatedMimeType(targetMimeType);
|
|
976
|
+
let pipeline = sharp(buffer, isAnimated ? { animated: true } : void 0).rotate();
|
|
916
977
|
pipeline = pipeline.resize({
|
|
917
978
|
width: config.width,
|
|
918
979
|
height: config.height,
|
|
@@ -921,16 +982,17 @@ async function resizeVariant(buffer, name, config, originalFilename, originalMim
|
|
|
921
982
|
});
|
|
922
983
|
if (config.format) pipeline = pipeline.toFormat(config.format, { quality: config.quality });
|
|
923
984
|
const resizedBuffer = await pipeline.toBuffer();
|
|
924
|
-
const meta = await sharp(resizedBuffer).metadata();
|
|
925
|
-
const
|
|
985
|
+
const meta = await sharp(resizedBuffer, isAnimated ? { animated: true } : void 0).metadata();
|
|
986
|
+
const filename = formatVariantFilename(originalFilename, name, config.format);
|
|
987
|
+
const { width, height } = getOrientedDimensions(meta);
|
|
926
988
|
return {
|
|
927
989
|
name,
|
|
928
|
-
filename
|
|
990
|
+
filename,
|
|
929
991
|
buffer: resizedBuffer,
|
|
930
|
-
width
|
|
931
|
-
height
|
|
992
|
+
width,
|
|
993
|
+
height,
|
|
932
994
|
filesize: resizedBuffer.length,
|
|
933
|
-
mimeType
|
|
995
|
+
mimeType: targetMimeType
|
|
934
996
|
};
|
|
935
997
|
} catch (err) {
|
|
936
998
|
if (err instanceof InvalidImageError) throw err;
|
|
@@ -948,17 +1010,19 @@ async function processImage(args) {
|
|
|
948
1010
|
},
|
|
949
1011
|
sizes: {}
|
|
950
1012
|
};
|
|
1013
|
+
const sharp = await getSharp();
|
|
951
1014
|
let meta;
|
|
952
1015
|
try {
|
|
953
|
-
meta = await sharp(buffer).metadata();
|
|
1016
|
+
meta = await sharp(buffer, { animated: isAnimatedMimeType(mimeType) }).metadata();
|
|
954
1017
|
} catch {
|
|
955
1018
|
throw new InvalidImageError("Invalid image");
|
|
956
1019
|
}
|
|
1020
|
+
const { width, height } = getOrientedDimensions(meta);
|
|
957
1021
|
const original = {
|
|
958
1022
|
filename,
|
|
959
1023
|
buffer,
|
|
960
|
-
width
|
|
961
|
-
height
|
|
1024
|
+
width,
|
|
1025
|
+
height,
|
|
962
1026
|
filesize: buffer.length,
|
|
963
1027
|
mimeType
|
|
964
1028
|
};
|
|
@@ -967,7 +1031,7 @@ async function processImage(args) {
|
|
|
967
1031
|
sizes: {}
|
|
968
1032
|
};
|
|
969
1033
|
const processedSizes = {};
|
|
970
|
-
for (const [name, config] of Object.entries(sizes)) processedSizes[name] = await resizeVariant(buffer, name, config, filename, mimeType);
|
|
1034
|
+
for (const [name, config] of Object.entries(sizes)) processedSizes[name] = await resizeVariant(sharp, buffer, name, config, filename, mimeType);
|
|
971
1035
|
return {
|
|
972
1036
|
original,
|
|
973
1037
|
sizes: processedSizes
|
|
@@ -2622,4 +2686,4 @@ function getLontar(config) {
|
|
|
2622
2686
|
};
|
|
2623
2687
|
return Promise.resolve(client);
|
|
2624
2688
|
}
|
|
2625
|
-
export { AccessDeniedError, DEFAULT_LIMIT, DEFAULT_MAX_FILE_SIZE, DatabaseError, InvalidImageError, LontarConfigError, MAX_LIMIT, NotFoundError, SYSTEM_FIELDS, StorageUploadError, UPLOAD_SYSTEM_FIELDS, UniqueConstraintError, ValidationError, accessDenied, checkAccess, collection, countTotalUsers, createLontar, createOperation, createUserWithCredential, defaultAccess, defineConfig, deleteMedia, deleteOperation, fields, findByIDOperation, findGlobalOperation, findOperation, formatUniqueConstraintError, formatVariantFilename, getLontar, global, isResizableImage, parseDepth, parseSort, parseWhere, parseWhereFromSearchParams, populateDoc, populateDocs, processImage, runAfterChangeHooks, runAfterReadHooks, runBeforeChangeHooks, toClientCollection, toClientConfig, toClientGlobal, updateGlobalOperation, updateOperation, uploadMedia, validateWhere };
|
|
2689
|
+
export { AccessDeniedError, DEFAULT_LIMIT, DEFAULT_MAX_FILE_SIZE, DatabaseError, InvalidImageError, LontarConfigError, MAX_LIMIT, NotFoundError, SYSTEM_FIELDS, StorageUploadError, UPLOAD_SYSTEM_FIELDS, UniqueConstraintError, ValidationError, accessDenied, checkAccess, collection, countTotalUsers, createLontar, createOperation, createUserWithCredential, defaultAccess, defineConfig, deleteMedia, deleteOperation, fields, findByIDOperation, findGlobalOperation, findOperation, formatUniqueConstraintError, formatVariantFilename, getLontar, getOrientedDimensions, getSharp, global, isAnimatedMimeType, isResizableImage, parseDepth, parseSort, parseWhere, parseWhereFromSearchParams, populateDoc, populateDocs, processImage, runAfterChangeHooks, runAfterReadHooks, runBeforeChangeHooks, setSharp, toClientCollection, toClientConfig, toClientGlobal, updateGlobalOperation, updateOperation, uploadMedia, validateWhere };
|
|
@@ -24,13 +24,26 @@ const CLIENT_CONFIG_VERSION = 1;
|
|
|
24
24
|
// src/client/zod.ts
|
|
25
25
|
const MAX_INT_32 = 2147483647;
|
|
26
26
|
const DEFAULT_STRING_MAX_LENGTH = 255;
|
|
27
|
+
function isRichTextEmpty(val) {
|
|
28
|
+
if (!val || typeof val !== "object") return true;
|
|
29
|
+
const doc = val;
|
|
30
|
+
if (typeof doc.text === "string") return doc.text.trim().length === 0;
|
|
31
|
+
if (Array.isArray(doc.content)) {
|
|
32
|
+
if (doc.content.length === 0) return true;
|
|
33
|
+
return doc.content.every((child) => isRichTextEmpty(child));
|
|
34
|
+
}
|
|
35
|
+
if (doc.type && doc.type !== "doc" && doc.type !== "paragraph") return false;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
27
38
|
const zodByType = {
|
|
28
39
|
text: (field) => {
|
|
29
|
-
const minLength = Math.max(field.minLength ?? 0, field.required ? 1 : 0);
|
|
30
40
|
const maxLength = field.maxLength ?? DEFAULT_STRING_MAX_LENGTH;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
if (field.required) {
|
|
42
|
+
const minLength = Math.max(field.minLength ?? 1, 1);
|
|
43
|
+
return z.string().min(minLength).max(maxLength);
|
|
44
|
+
}
|
|
45
|
+
const schema = z.string().max(maxLength);
|
|
46
|
+
if (field.minLength !== void 0 && field.minLength > 0) return schema.min(field.minLength).or(z.literal(""));
|
|
34
47
|
return schema;
|
|
35
48
|
},
|
|
36
49
|
number: (field) => {
|
|
@@ -41,10 +54,15 @@ const zodByType = {
|
|
|
41
54
|
return schema;
|
|
42
55
|
},
|
|
43
56
|
textarea: (field) => {
|
|
44
|
-
|
|
57
|
+
if (field.required) {
|
|
58
|
+
const minLength = Math.max(field.minLength ?? 1, 1);
|
|
59
|
+
let schema = z.string().min(minLength);
|
|
60
|
+
if (field.maxLength !== void 0) schema = schema.max(field.maxLength);
|
|
61
|
+
return schema;
|
|
62
|
+
}
|
|
45
63
|
let schema = z.string();
|
|
46
|
-
if (minLength > 0) schema = schema.min(minLength);
|
|
47
64
|
if (field.maxLength !== void 0) schema = schema.max(field.maxLength);
|
|
65
|
+
if (field.minLength !== void 0 && field.minLength > 0) return schema.min(field.minLength).or(z.literal(""));
|
|
48
66
|
return schema;
|
|
49
67
|
},
|
|
50
68
|
email: (field) => {
|
|
@@ -75,7 +93,7 @@ const zodByType = {
|
|
|
75
93
|
},
|
|
76
94
|
richText: (field) => {
|
|
77
95
|
const base = z.record(z.string(), z.unknown());
|
|
78
|
-
if (field.required) return base.refine((val) =>
|
|
96
|
+
if (field.required) return base.refine((val) => !isRichTextEmpty(val), { message: "Rich text content is required" });
|
|
79
97
|
return base;
|
|
80
98
|
},
|
|
81
99
|
relationship: (field) => {
|
|
@@ -103,4 +121,4 @@ function clientFieldToZod(field) {
|
|
|
103
121
|
function clientFieldsToZod(fields) {
|
|
104
122
|
return z.object(Object.fromEntries(fields.map((field) => [field.name, clientFieldToZod(field)])));
|
|
105
123
|
}
|
|
106
|
-
export {
|
|
124
|
+
export { SYSTEM_FIELDS as a, CLIENT_CONFIG_VERSION as i, clientFieldsToZod as n, UPLOAD_SYSTEM_FIELDS as o, isRichTextEmpty as r, clientFieldToZod as t };
|