@orion-studios/payload-studio 0.6.0-beta.184 → 0.6.0-beta.186

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/README.md +6 -1
  2. package/bin/init.js +329 -0
  3. package/dist/admin/index.d.mts +1 -1
  4. package/dist/admin/index.d.ts +1 -1
  5. package/dist/admin/index.js +61 -20
  6. package/dist/admin/index.mjs +1 -1
  7. package/dist/blocks/index.mjs +1 -2
  8. package/dist/builder-v2/client.js +21 -1
  9. package/dist/builder-v2/client.mjs +21 -1
  10. package/dist/builder-v2/index.js +33 -2
  11. package/dist/builder-v2/index.mjs +33 -2
  12. package/dist/{chunk-7HME6R2V.mjs → chunk-DTHBPJXU.mjs} +191 -21
  13. package/dist/{chunk-JQAHXYAM.mjs → chunk-KMYMSR7W.mjs} +163 -3
  14. package/dist/{chunk-KHK6RTGC.mjs → chunk-MMJNHBOF.mjs} +61 -20
  15. package/dist/{chunk-ZADL33R6.mjs → chunk-OF6BATTO.mjs} +56 -111
  16. package/dist/chunk-VA545CHJ.mjs +202 -0
  17. package/dist/forms/index.d.mts +25 -0
  18. package/dist/forms/index.d.ts +25 -0
  19. package/dist/forms/index.js +237 -0
  20. package/dist/forms/index.mjs +25 -0
  21. package/dist/forms/server.d.mts +35 -0
  22. package/dist/forms/server.d.ts +35 -0
  23. package/dist/forms/server.js +442 -0
  24. package/dist/forms/server.mjs +228 -0
  25. package/dist/{index-Dv-Alx4h.d.ts → index-0xXWuOuz.d.ts} +35 -5
  26. package/dist/{index-D5zrOdyv.d.mts → index-BU82akSL.d.mts} +35 -5
  27. package/dist/{index-DLfPOqYA.d.mts → index-BbTcimgH.d.mts} +1 -0
  28. package/dist/{index-D8BNfUJb.d.mts → index-Cen-9wcc.d.mts} +17 -5
  29. package/dist/{index-BV0vEGl6.d.ts → index-DV-nW43e.d.ts} +1 -0
  30. package/dist/{index-DD_E2UfJ.d.ts → index-gxcMPpm-.d.ts} +17 -5
  31. package/dist/index.d.mts +3 -3
  32. package/dist/index.d.ts +3 -3
  33. package/dist/index.js +302 -151
  34. package/dist/index.mjs +10 -11
  35. package/dist/nextjs/index.d.mts +1 -1
  36. package/dist/nextjs/index.d.ts +1 -1
  37. package/dist/nextjs/index.js +56 -111
  38. package/dist/nextjs/index.mjs +1 -1
  39. package/dist/studio-pages/index.d.mts +1 -1
  40. package/dist/studio-pages/index.d.ts +1 -1
  41. package/dist/studio-pages/index.js +1802 -20
  42. package/dist/studio-pages/index.mjs +10 -2
  43. package/package.json +18 -2
  44. package/dist/chunk-OQSEJXC4.mjs +0 -166
@@ -231,23 +231,33 @@ function configureAdmin(config) {
231
231
  if (cssSources.length === 0) {
232
232
  cssPath = sourceCssPath;
233
233
  } else {
234
- let css = cssSources.map((filePath) => fs.readFileSync(filePath, "utf-8")).join("\n\n");
235
- css = css.replace(
236
- "--orion-cms-brand-primary-fallback: #3b82f6;",
237
- `--orion-cms-brand-primary-fallback: ${brandPrimary};`
238
- );
239
- css = css.replace(
240
- "--orion-cms-brand-secondary-fallback: #8b5cf6;",
241
- `--orion-cms-brand-secondary-fallback: ${brandSecondary};`
242
- );
243
- const outputBasePath = config.basePath || process.cwd();
244
- const genDir = path.resolve(outputBasePath, ".generated");
245
- if (!fs.existsSync(genDir)) {
246
- fs.mkdirSync(genDir, { recursive: true });
234
+ try {
235
+ let css = cssSources.map((filePath) => fs.readFileSync(filePath, "utf-8")).join("\n\n");
236
+ css = css.replace(
237
+ "--orion-cms-brand-primary-fallback: #3b82f6;",
238
+ `--orion-cms-brand-primary-fallback: ${brandPrimary};`
239
+ );
240
+ css = css.replace(
241
+ "--orion-cms-brand-secondary-fallback: #8b5cf6;",
242
+ `--orion-cms-brand-secondary-fallback: ${brandSecondary};`
243
+ );
244
+ const outputBasePath = config.basePath || process.cwd();
245
+ const genDir = path.resolve(outputBasePath, ".generated");
246
+ const genPath = path.resolve(genDir, "admin.css");
247
+ let existing = null;
248
+ try {
249
+ existing = fs.readFileSync(genPath, "utf-8");
250
+ } catch {
251
+ existing = null;
252
+ }
253
+ if (existing !== css) {
254
+ fs.mkdirSync(genDir, { recursive: true });
255
+ fs.writeFileSync(genPath, css);
256
+ }
257
+ cssPath = genPath;
258
+ } catch {
259
+ cssPath = sourceCssPath;
247
260
  }
248
- const genPath = path.resolve(genDir, "admin.css");
249
- fs.writeFileSync(genPath, css);
250
- cssPath = genPath;
251
261
  }
252
262
  const clientPath = "@orion-studios/payload-studio/admin/client";
253
263
  const studioNavClientProps = {
@@ -700,16 +710,47 @@ function configureAdmin(config) {
700
710
  });
701
711
  },
702
712
  wrapGlobals(globals2) {
703
- const labelMap = {
713
+ const defaultLabelMap = {
704
714
  header: { group: "Site Design", label: "Header & Navigation" },
705
715
  footer: { group: "Site Design", label: "Footer" },
706
716
  "site-settings": { group: "Site Design", label: "Website Settings" },
707
717
  "social-media": { group: "Site Design", label: "Social Media" },
708
718
  "contact-form": { group: "Lead Forms", label: "Contact Form" }
709
719
  };
720
+ const configuredBySlug = new Map(
721
+ (config.studio?.globals || []).map((link) => [link.slug, link])
722
+ );
723
+ const attachBreadcrumbToGlobal = (global) => {
724
+ if (!studioEnabled) {
725
+ return global;
726
+ }
727
+ const existingControls = global.admin?.components?.elements?.beforeDocumentControls;
728
+ return {
729
+ ...global,
730
+ admin: {
731
+ ...global.admin,
732
+ components: {
733
+ ...global.admin?.components,
734
+ elements: {
735
+ ...global.admin?.components?.elements,
736
+ beforeDocumentControls: appendComponent(
737
+ existingControls,
738
+ studioBackBreadcrumbComponent,
739
+ "StudioBackBreadcrumb"
740
+ )
741
+ }
742
+ }
743
+ }
744
+ };
745
+ };
710
746
  return globals2.map((global) => {
711
- const mapping = labelMap[global.slug];
712
- if (!mapping) return global;
747
+ const configured = configuredBySlug.get(global.slug);
748
+ const defaults = defaultLabelMap[global.slug];
749
+ const mapping = configured || defaults ? {
750
+ group: configured?.group || defaults?.group,
751
+ label: configured?.label || defaults?.label || global.slug
752
+ } : void 0;
753
+ if (!mapping) return attachBreadcrumbToGlobal(global);
713
754
  const shouldAttachSiteSettingsEditView = studioEnabled && global.slug === "site-settings";
714
755
  const shouldAttachSocialMediaEditView = studioEnabled && global.slug === "social-media";
715
756
  const shouldAttachContactFormRedirect = studioEnabled && global.slug === "contact-form";
@@ -818,7 +859,7 @@ function configureAdmin(config) {
818
859
  ...global,
819
860
  admin: {
820
861
  ...global.admin,
821
- group: mapping.group,
862
+ ...mapping.group ? { group: mapping.group } : {},
822
863
  components: {
823
864
  ...global.admin?.components,
824
865
  elements: {
@@ -26,12 +26,15 @@ __export(nextjs_exports, {
26
26
 
27
27
  // src/nextjs/client/payload.ts
28
28
  import { getPayload } from "payload";
29
- var payloadPromise = null;
30
29
  var WEBSITE_CONTENT_TAG = "website-content";
31
30
  function createPayloadClient(config) {
31
+ let payloadPromise = null;
32
32
  return function getPayloadClient() {
33
33
  if (!payloadPromise) {
34
- payloadPromise = getPayload({ config });
34
+ payloadPromise = Promise.resolve(config).then((resolvedConfig) => getPayload({ config: resolvedConfig })).catch((error) => {
35
+ payloadPromise = null;
36
+ throw error;
37
+ });
35
38
  }
36
39
  return payloadPromise;
37
40
  };
@@ -64,7 +67,7 @@ function normalizePath(segments) {
64
67
  const cleaned = segments.map((segment) => segment.trim()).filter(Boolean).join("/");
65
68
  return cleaned.length > 0 ? `/${cleaned}` : "/";
66
69
  }
67
- async function queryPageByPath(payload, path, draft) {
70
+ async function queryPageByPath(payload, path, draft, collectionSlug, homeSlug) {
68
71
  const pathWhere = {
69
72
  path: {
70
73
  equals: path
@@ -76,7 +79,7 @@ async function queryPageByPath(payload, path, draft) {
76
79
  }
77
80
  };
78
81
  const result = await payload.find({
79
- collection: "pages",
82
+ collection: collectionSlug,
80
83
  depth: 2,
81
84
  draft,
82
85
  limit: 1,
@@ -89,11 +92,11 @@ async function queryPageByPath(payload, path, draft) {
89
92
  if (path === "/") {
90
93
  const homeWhere = {
91
94
  slug: {
92
- equals: "home"
95
+ equals: homeSlug
93
96
  }
94
97
  };
95
98
  const homeResult = await payload.find({
96
- collection: "pages",
99
+ collection: collectionSlug,
97
100
  depth: 2,
98
101
  draft,
99
102
  limit: 1,
@@ -104,27 +107,31 @@ async function queryPageByPath(payload, path, draft) {
104
107
  }
105
108
  return null;
106
109
  }
107
- function createPageQueries(getPayloadClient, contentTag = "website-content") {
110
+ function createPageQueries(getPayloadClient, contentTagOrOptions = "website-content") {
111
+ const options = typeof contentTagOrOptions === "string" ? { contentTag: contentTagOrOptions } : contentTagOrOptions;
112
+ const contentTag = options.contentTag || "website-content";
113
+ const pagesCollectionSlug = options.pagesCollectionSlug || "pages";
114
+ const homeSlug = options.homeSlug || "home";
108
115
  const getPublishedPageByPathCached = unstable_cache(
109
116
  async (path) => {
110
117
  const payload = await getPayloadClient();
111
- return queryPageByPath(payload, path, false);
118
+ return queryPageByPath(payload, path, false, pagesCollectionSlug, homeSlug);
112
119
  },
113
- ["page-by-path", PAGE_QUERY_CACHE_VERSION],
120
+ ["page-by-path", PAGE_QUERY_CACHE_VERSION, pagesCollectionSlug, homeSlug, contentTag],
114
121
  { tags: [contentTag] }
115
122
  );
116
123
  async function getPageBySegments(segments, draft = false) {
117
124
  const path = normalizePath(segments);
118
125
  const payload = await getPayloadClient();
119
126
  if (draft) {
120
- return queryPageByPath(payload, path, true);
127
+ return queryPageByPath(payload, path, true, pagesCollectionSlug, homeSlug);
121
128
  }
122
129
  return getPublishedPageByPathCached(path);
123
130
  }
124
131
  async function listPublishedPagePaths() {
125
132
  const payload = await getPayloadClient();
126
133
  const pages = await payload.find({
127
- collection: "pages",
134
+ collection: pagesCollectionSlug,
128
135
  depth: 0,
129
136
  draft: false,
130
137
  limit: 1e3,
@@ -153,108 +160,46 @@ function createPageQueries(getPayloadClient, contentTag = "website-content") {
153
160
 
154
161
  // src/nextjs/queries/site.ts
155
162
  import { unstable_cache as unstable_cache2 } from "next/cache";
156
- function createSiteQueries(getPayloadClient, contentTag = "website-content") {
157
- const getSiteSettingsCached = unstable_cache2(
158
- async () => {
159
- const payload = await getPayloadClient();
160
- const settings = await payload.findGlobal({
161
- slug: "site-settings",
162
- depth: 1
163
- });
164
- return settings;
165
- },
166
- ["site-settings-global"],
167
- { tags: [contentTag] }
168
- );
169
- const getHeaderCached = unstable_cache2(
170
- async () => {
171
- const payload = await getPayloadClient();
172
- const header = await payload.findGlobal({
173
- slug: "header",
174
- depth: 1
175
- });
176
- return header;
177
- },
178
- ["header-global"],
179
- { tags: [contentTag] }
180
- );
181
- const getFooterCached = unstable_cache2(
182
- async () => {
183
- const payload = await getPayloadClient();
184
- const footer = await payload.findGlobal({
185
- slug: "footer",
186
- depth: 1
187
- });
188
- return footer;
189
- },
190
- ["footer-global"],
191
- { tags: [contentTag] }
192
- );
193
- const getSocialMediaCached = unstable_cache2(
194
- async () => {
195
- const payload = await getPayloadClient();
196
- const socialMedia = await payload.findGlobal({
197
- slug: "social-media",
198
- depth: 1
199
- });
200
- return socialMedia;
201
- },
202
- ["social-media-global"],
203
- { tags: [contentTag] }
204
- );
205
- async function getSiteSettings(draft = false) {
206
- if (draft) {
207
- const payload = await getPayloadClient();
208
- const settings = await payload.findGlobal({
209
- slug: "site-settings",
210
- depth: 1,
211
- draft: true
212
- });
213
- return settings;
214
- }
215
- return getSiteSettingsCached();
216
- }
217
- async function getHeader(draft = false) {
218
- if (draft) {
219
- const payload = await getPayloadClient();
220
- const header = await payload.findGlobal({
221
- slug: "header",
222
- depth: 1,
223
- draft: true
224
- });
225
- return header;
226
- }
227
- return getHeaderCached();
228
- }
229
- async function getFooter(draft = false) {
230
- if (draft) {
231
- const payload = await getPayloadClient();
232
- const footer = await payload.findGlobal({
233
- slug: "footer",
234
- depth: 1,
235
- draft: true
236
- });
237
- return footer;
238
- }
239
- return getFooterCached();
240
- }
241
- async function getSocialMedia(draft = false) {
242
- if (draft) {
243
- const payload = await getPayloadClient();
244
- const socialMedia = await payload.findGlobal({
245
- slug: "social-media",
246
- depth: 1,
247
- draft: true
248
- });
249
- return socialMedia;
250
- }
251
- return getSocialMediaCached();
163
+ function createSiteQueries(getPayloadClient, contentTagOrOptions = "website-content") {
164
+ const options = typeof contentTagOrOptions === "string" ? { contentTag: contentTagOrOptions } : contentTagOrOptions;
165
+ const contentTag = options.contentTag || "website-content";
166
+ const siteSettingsSlug = options.siteSettingsSlug || "site-settings";
167
+ const headerSlug = options.headerSlug || "header";
168
+ const footerSlug = options.footerSlug || "footer";
169
+ const socialMediaSlug = options.socialMediaSlug || "social-media";
170
+ function createGlobalQuery(slug, cacheKey) {
171
+ const getCached = unstable_cache2(
172
+ async () => {
173
+ const payload = await getPayloadClient();
174
+ const data = await payload.findGlobal({
175
+ slug,
176
+ depth: 1,
177
+ overrideAccess: false
178
+ });
179
+ return data;
180
+ },
181
+ [cacheKey, slug, contentTag],
182
+ { tags: [contentTag] }
183
+ );
184
+ return async function getGlobal(draft = false) {
185
+ if (draft) {
186
+ const payload = await getPayloadClient();
187
+ const data = await payload.findGlobal({
188
+ slug,
189
+ depth: 1,
190
+ draft: true,
191
+ overrideAccess: false
192
+ });
193
+ return data;
194
+ }
195
+ return getCached();
196
+ };
252
197
  }
253
198
  return {
254
- getSiteSettings,
255
- getHeader,
256
- getFooter,
257
- getSocialMedia
199
+ getSiteSettings: createGlobalQuery(siteSettingsSlug, "site-settings-global"),
200
+ getHeader: createGlobalQuery(headerSlug, "header-global"),
201
+ getFooter: createGlobalQuery(footerSlug, "footer-global"),
202
+ getSocialMedia: createGlobalQuery(socialMediaSlug, "social-media-global")
258
203
  };
259
204
  }
260
205
 
@@ -0,0 +1,202 @@
1
+ // src/forms/validation.ts
2
+ var MAJOR_EMAIL_PROVIDERS = [
3
+ "gmail.com",
4
+ "yahoo.com",
5
+ "outlook.com",
6
+ "hotmail.com",
7
+ "icloud.com",
8
+ "live.com",
9
+ "protonmail.com"
10
+ ];
11
+ var KNOWN_GOOD_DOMAINS = /* @__PURE__ */ new Set([
12
+ ...MAJOR_EMAIL_PROVIDERS,
13
+ "aol.com",
14
+ "gmx.com",
15
+ "googlemail.com",
16
+ "hey.com",
17
+ "mac.com",
18
+ "mail.com",
19
+ "me.com",
20
+ "msn.com",
21
+ "pm.me",
22
+ "proton.me",
23
+ "ymail.com",
24
+ "zoho.com"
25
+ ]);
26
+ var EXPLICIT_TYPO_MAP = {
27
+ "gamil.com": "gmail.com",
28
+ "gmial.com": "gmail.com",
29
+ "gmai.com": "gmail.com",
30
+ "gmaill.com": "gmail.com",
31
+ "gmail.co": "gmail.com",
32
+ "gmail.con": "gmail.com",
33
+ "gmail.cmo": "gmail.com",
34
+ "hotmial.com": "hotmail.com",
35
+ "hotmall.com": "hotmail.com",
36
+ "hotmail.co": "hotmail.com",
37
+ "hotmail.con": "hotmail.com",
38
+ "iclod.com": "icloud.com",
39
+ "icloud.co": "icloud.com",
40
+ "icoud.com": "icloud.com",
41
+ "outlok.com": "outlook.com",
42
+ "outloook.com": "outlook.com",
43
+ "outlook.co": "outlook.com",
44
+ "outlook.con": "outlook.com",
45
+ "protonmail.co": "protonmail.com",
46
+ "yaho.com": "yahoo.com",
47
+ "yahooo.com": "yahoo.com",
48
+ "yahoo.co": "yahoo.com",
49
+ "yahoo.con": "yahoo.com"
50
+ };
51
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
52
+ function levenshtein(a, b) {
53
+ if (a === b) return 0;
54
+ const rows = a.length + 1;
55
+ const cols = b.length + 1;
56
+ const dist = Array.from({ length: cols }, (_, j) => j);
57
+ for (let i = 1; i < rows; i += 1) {
58
+ let prevDiagonal = dist[0];
59
+ dist[0] = i;
60
+ for (let j = 1; j < cols; j += 1) {
61
+ const temp = dist[j];
62
+ dist[j] = Math.min(
63
+ dist[j] + 1,
64
+ dist[j - 1] + 1,
65
+ prevDiagonal + (a[i - 1] === b[j - 1] ? 0 : 1)
66
+ );
67
+ prevDiagonal = temp;
68
+ }
69
+ }
70
+ return dist[cols - 1];
71
+ }
72
+ function normalizeEmail(value) {
73
+ return value.trim().toLowerCase();
74
+ }
75
+ function validateEmail(value, options = {}) {
76
+ const normalized = normalizeEmail(value);
77
+ if (normalized.length === 0) {
78
+ return { valid: false, normalized, message: "Email is required." };
79
+ }
80
+ if (!EMAIL_PATTERN.test(normalized)) {
81
+ return { valid: false, normalized, message: "Enter a valid email address." };
82
+ }
83
+ const [localPart, domain] = normalized.split("@");
84
+ const knownGood = options.knownGoodDomains ? /* @__PURE__ */ new Set([...KNOWN_GOOD_DOMAINS, ...options.knownGoodDomains.map((entry) => entry.toLowerCase())]) : KNOWN_GOOD_DOMAINS;
85
+ if (knownGood.has(domain)) {
86
+ return { valid: true, normalized };
87
+ }
88
+ const explicitCorrection = EXPLICIT_TYPO_MAP[domain];
89
+ if (explicitCorrection) {
90
+ const suggestion = `${localPart}@${explicitCorrection}`;
91
+ return {
92
+ valid: false,
93
+ normalized,
94
+ message: `Did you mean ${suggestion}?`,
95
+ suggestion
96
+ };
97
+ }
98
+ for (const provider of MAJOR_EMAIL_PROVIDERS) {
99
+ if (levenshtein(domain, provider) === 1) {
100
+ const suggestion = `${localPart}@${provider}`;
101
+ return {
102
+ valid: false,
103
+ normalized,
104
+ message: `Did you mean ${suggestion}?`,
105
+ suggestion
106
+ };
107
+ }
108
+ }
109
+ return { valid: true, normalized };
110
+ }
111
+ function normalizePhone(value) {
112
+ const digits = value.replace(/\D+/g, "");
113
+ if (digits.length === 11 && digits.startsWith("1")) {
114
+ return digits.slice(1);
115
+ }
116
+ return digits;
117
+ }
118
+ function formatPhoneUS(value) {
119
+ const digits = normalizePhone(value).slice(0, 10);
120
+ if (digits.length === 0) return "";
121
+ if (digits.length < 4) return `(${digits}`;
122
+ if (digits.length < 7) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
123
+ return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
124
+ }
125
+ function validatePhoneUS(value) {
126
+ const digits = normalizePhone(value);
127
+ if (digits.length === 0) {
128
+ return { valid: false, normalized: "", message: "Phone number is required." };
129
+ }
130
+ if (digits.length !== 10) {
131
+ return { valid: false, normalized: digits, message: "Enter a 10-digit phone number." };
132
+ }
133
+ return { valid: true, normalized: digits };
134
+ }
135
+ function normalizeUrl(value) {
136
+ const trimmed = value.trim();
137
+ if (trimmed.length === 0) return "";
138
+ if (/^https?:\/\//i.test(trimmed)) return trimmed;
139
+ return `https://${trimmed}`;
140
+ }
141
+ function validateUrl(value) {
142
+ const normalized = normalizeUrl(value);
143
+ if (normalized.length === 0) {
144
+ return { valid: false, normalized, message: "URL is required." };
145
+ }
146
+ try {
147
+ const parsed = new URL(normalized);
148
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
149
+ return { valid: false, normalized, message: "Enter a valid web address." };
150
+ }
151
+ if (!parsed.hostname.includes(".")) {
152
+ return { valid: false, normalized, message: "Enter a valid web address." };
153
+ }
154
+ return { valid: true, normalized };
155
+ } catch {
156
+ return { valid: false, normalized, message: "Enter a valid web address." };
157
+ }
158
+ }
159
+ function validateRequired(value, label = "This field") {
160
+ const normalized = typeof value === "string" ? value.trim() : "";
161
+ const present = typeof value === "string" ? normalized.length > 0 : Array.isArray(value) ? value.length > 0 : value !== null && value !== void 0 && value !== false;
162
+ if (!present) {
163
+ return { valid: false, normalized, message: `${label} is required.` };
164
+ }
165
+ return { valid: true, normalized: typeof value === "string" ? normalized : String(value) };
166
+ }
167
+ function normalizeFieldValue(value, fieldType) {
168
+ switch (fieldType) {
169
+ case "email":
170
+ return normalizeEmail(value);
171
+ case "phone":
172
+ return normalizePhone(value);
173
+ case "url":
174
+ return normalizeUrl(value);
175
+ default:
176
+ return value.trim();
177
+ }
178
+ }
179
+ function inferFieldType(field) {
180
+ const type = typeof field.type === "string" ? field.type.toLowerCase() : "";
181
+ if (type === "email" || type === "phone" || type === "tel" || type === "url") {
182
+ return type === "tel" ? "phone" : type;
183
+ }
184
+ const name = typeof field.name === "string" ? field.name.toLowerCase() : "";
185
+ if (name.includes("email")) return "email";
186
+ if (name.includes("phone") || name.includes("mobile")) return "phone";
187
+ if (name.includes("website") || name.endsWith("url")) return "url";
188
+ return "text";
189
+ }
190
+
191
+ export {
192
+ normalizeEmail,
193
+ validateEmail,
194
+ normalizePhone,
195
+ formatPhoneUS,
196
+ validatePhoneUS,
197
+ normalizeUrl,
198
+ validateUrl,
199
+ validateRequired,
200
+ normalizeFieldValue,
201
+ inferFieldType
202
+ };
@@ -0,0 +1,25 @@
1
+ type FieldValidationResult = {
2
+ valid: boolean;
3
+ normalized: string;
4
+ message?: string;
5
+ suggestion?: string;
6
+ };
7
+ declare function normalizeEmail(value: string): string;
8
+ type ValidateEmailOptions = {
9
+ knownGoodDomains?: string[];
10
+ };
11
+ declare function validateEmail(value: string, options?: ValidateEmailOptions): FieldValidationResult;
12
+ declare function normalizePhone(value: string): string;
13
+ declare function formatPhoneUS(value: string): string;
14
+ declare function validatePhoneUS(value: string): FieldValidationResult;
15
+ declare function normalizeUrl(value: string): string;
16
+ declare function validateUrl(value: string): FieldValidationResult;
17
+ declare function validateRequired(value: unknown, label?: string): FieldValidationResult;
18
+ type StudioFormFieldType = 'email' | 'phone' | 'url' | 'text';
19
+ declare function normalizeFieldValue(value: string, fieldType: StudioFormFieldType): string;
20
+ declare function inferFieldType(field: {
21
+ name?: unknown;
22
+ type?: unknown;
23
+ }): StudioFormFieldType;
24
+
25
+ export { type FieldValidationResult, type StudioFormFieldType, type ValidateEmailOptions, formatPhoneUS, inferFieldType, normalizeEmail, normalizeFieldValue, normalizePhone, normalizeUrl, validateEmail, validatePhoneUS, validateRequired, validateUrl };
@@ -0,0 +1,25 @@
1
+ type FieldValidationResult = {
2
+ valid: boolean;
3
+ normalized: string;
4
+ message?: string;
5
+ suggestion?: string;
6
+ };
7
+ declare function normalizeEmail(value: string): string;
8
+ type ValidateEmailOptions = {
9
+ knownGoodDomains?: string[];
10
+ };
11
+ declare function validateEmail(value: string, options?: ValidateEmailOptions): FieldValidationResult;
12
+ declare function normalizePhone(value: string): string;
13
+ declare function formatPhoneUS(value: string): string;
14
+ declare function validatePhoneUS(value: string): FieldValidationResult;
15
+ declare function normalizeUrl(value: string): string;
16
+ declare function validateUrl(value: string): FieldValidationResult;
17
+ declare function validateRequired(value: unknown, label?: string): FieldValidationResult;
18
+ type StudioFormFieldType = 'email' | 'phone' | 'url' | 'text';
19
+ declare function normalizeFieldValue(value: string, fieldType: StudioFormFieldType): string;
20
+ declare function inferFieldType(field: {
21
+ name?: unknown;
22
+ type?: unknown;
23
+ }): StudioFormFieldType;
24
+
25
+ export { type FieldValidationResult, type StudioFormFieldType, type ValidateEmailOptions, formatPhoneUS, inferFieldType, normalizeEmail, normalizeFieldValue, normalizePhone, normalizeUrl, validateEmail, validatePhoneUS, validateRequired, validateUrl };