@sudajs/cli 0.10.2 → 0.10.4

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/index.js CHANGED
@@ -307,18 +307,29 @@ var contactFieldsSchema = z.array(contactFieldSchema).superRefine((fields, ctx)
307
307
  addUniqueContactIssue(field.options, "options", "value", ctx, [index, "options"]);
308
308
  }
309
309
  });
310
- var contactNotificationUpdateSchema = z.object({
310
+ var contactEmailNotificationUpdateSchema = z.object({
311
311
  id: z.string().trim().min(1).max(48),
312
- type: z.enum(["email", "slack", "wecom", "feishu", "dingtalk"]),
312
+ type: z.literal("email"),
313
313
  enabled: z.boolean().default(true),
314
314
  label: z.string().trim().min(1).max(80),
315
- recipients: z.array(z.string().trim().email()).default([]),
316
- url: z.string().trim().url().optional()
315
+ recipients: z.array(z.string().trim().email()).min(1)
317
316
  });
317
+ var contactChatNotificationUpdateSchema = z.object({
318
+ id: z.string().trim().min(1).max(48),
319
+ type: z.enum(["slack", "wecom", "feishu", "dingtalk"]),
320
+ enabled: z.boolean().default(true),
321
+ label: z.string().trim().min(1).max(80),
322
+ webhookUrl: z.string().trim().url().optional()
323
+ });
324
+ var contactNotificationUpdateSchema = z.discriminatedUnion("type", [
325
+ contactEmailNotificationUpdateSchema,
326
+ contactChatNotificationUpdateSchema
327
+ ]);
318
328
  var contactWebhookUpdateSchema = z.object({
319
329
  id: z.string().trim().min(1).max(48),
320
330
  enabled: z.boolean().default(true),
321
331
  label: z.string().trim().min(1).max(80),
332
+ event: z.literal("contact.submitted").default("contact.submitted"),
322
333
  url: z.string().trim().url().optional()
323
334
  });
324
335
  var updateContactFormInputSchema = z.object({
@@ -339,20 +350,29 @@ var contactFormViewSchema = z.object({
339
350
  submitLabel: z.string(),
340
351
  fields: z.array(contactFieldSchema),
341
352
  notifications: z.array(
342
- z.object({
343
- id: z.string(),
344
- type: z.enum(["email", "slack", "wecom", "feishu", "dingtalk"]),
345
- enabled: z.boolean(),
346
- label: z.string(),
347
- recipients: z.array(z.string()),
348
- urlMasked: z.string().nullable()
349
- })
353
+ z.discriminatedUnion("type", [
354
+ z.object({
355
+ id: z.string(),
356
+ type: z.literal("email"),
357
+ enabled: z.boolean(),
358
+ label: z.string(),
359
+ recipients: z.array(z.string())
360
+ }),
361
+ z.object({
362
+ id: z.string(),
363
+ type: z.enum(["slack", "wecom", "feishu", "dingtalk"]),
364
+ enabled: z.boolean(),
365
+ label: z.string(),
366
+ webhookUrlMasked: z.string().nullable()
367
+ })
368
+ ])
350
369
  ),
351
370
  webhooks: z.array(
352
371
  z.object({
353
372
  id: z.string(),
354
373
  enabled: z.boolean(),
355
374
  label: z.string(),
375
+ event: z.literal("contact.submitted"),
356
376
  urlMasked: z.string().nullable()
357
377
  })
358
378
  ),
@@ -1000,6 +1020,40 @@ var THEME_CHECK_ICP = {
1000
1020
  text: "\u4EACICP\u590712345678\u53F7",
1001
1021
  href: "https://beian.miit.gov.cn/"
1002
1022
  };
1023
+ var PREVIEW_CONTACT_FORM = {
1024
+ enabled: true,
1025
+ endpoint: "/api/contact",
1026
+ honeypotField: "suda_contact_company",
1027
+ title: "Contact us",
1028
+ successMessage: "Thanks, we received your message.",
1029
+ submitLabel: "Submit",
1030
+ fields: [
1031
+ {
1032
+ id: "name",
1033
+ type: "text",
1034
+ label: "Name",
1035
+ required: true,
1036
+ options: [],
1037
+ validation: { maxLength: 120 }
1038
+ },
1039
+ {
1040
+ id: "email",
1041
+ type: "text",
1042
+ label: "Email",
1043
+ required: true,
1044
+ options: [],
1045
+ validation: { format: "email", maxLength: 200 }
1046
+ },
1047
+ {
1048
+ id: "message",
1049
+ type: "textarea",
1050
+ label: "Message",
1051
+ required: true,
1052
+ options: [],
1053
+ validation: { maxLength: 2e3 }
1054
+ }
1055
+ ]
1056
+ };
1003
1057
  function stripHtmlTags(value) {
1004
1058
  return value.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
1005
1059
  }
@@ -1087,6 +1141,7 @@ var __testUtils = {
1087
1141
  performConfirmedPageOperation,
1088
1142
  performUpdateContactForm,
1089
1143
  renderDevStarterPageHtml,
1144
+ resolveDevStarterSlug,
1090
1145
  resolveScreenshotOptions,
1091
1146
  slugifyThemeName,
1092
1147
  validateThemeKey,
@@ -1183,24 +1238,25 @@ function createSudaPreviewVitePlugin(root) {
1183
1238
  next();
1184
1239
  return;
1185
1240
  }
1186
- if (url.pathname !== "/" && !url.pathname.startsWith("/pages/")) {
1241
+ const slug = resolveDevStarterSlug(url.pathname);
1242
+ if (slug === void 0) {
1187
1243
  next();
1188
1244
  return;
1189
1245
  }
1190
1246
  try {
1191
1247
  const theme = await loadViteDevTheme(root, server);
1192
1248
  const renderer = await loadThemeScopedRenderer(root);
1193
- if (url.pathname === "/") {
1194
- const fallbackSlug = pickStarterSlug(theme);
1195
- if (!fallbackSlug) {
1249
+ if (slug === null) {
1250
+ const homeSlug = pickStarterSlug(theme);
1251
+ if (!homeSlug) {
1196
1252
  response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
1197
1253
  response.end("No starter pages declared by this theme.");
1198
1254
  return;
1199
1255
  }
1200
- const starter2 = findStarterPage(theme, fallbackSlug);
1256
+ const starter2 = findStarterPage(theme, homeSlug);
1201
1257
  if (!starter2) {
1202
1258
  response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
1203
- response.end(`Unknown starter page: ${fallbackSlug}`);
1259
+ response.end(`Unknown starter page: ${homeSlug}`);
1204
1260
  return;
1205
1261
  }
1206
1262
  const html2 = await server.transformIndexHtml(
@@ -1211,7 +1267,6 @@ function createSudaPreviewVitePlugin(root) {
1211
1267
  response.end(html2);
1212
1268
  return;
1213
1269
  }
1214
- const slug = decodeURIComponent(url.pathname.slice("/pages/".length));
1215
1270
  const starter = findStarterPage(theme, slug);
1216
1271
  if (!starter) {
1217
1272
  response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
@@ -1259,6 +1314,16 @@ function pickStarterSlug(theme) {
1259
1314
  function findStarterPage(theme, slug) {
1260
1315
  return theme.module.starterPages.find((page) => page.slug === slug) ?? null;
1261
1316
  }
1317
+ function resolveDevStarterSlug(pathname) {
1318
+ if (pathname === "/") {
1319
+ return null;
1320
+ }
1321
+ const normalized = pathname.replace(/^\/+/, "").replace(/\/+$/, "");
1322
+ if (!normalized || normalized.includes("/")) {
1323
+ return void 0;
1324
+ }
1325
+ return decodeURIComponent(normalized);
1326
+ }
1262
1327
  function escapeHtml(value) {
1263
1328
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1264
1329
  }
@@ -1295,7 +1360,8 @@ async function loadThemeScopedRenderer(themeRoot) {
1295
1360
  function renderDevStarterPageHtml(theme, page, renderer) {
1296
1361
  const chrome = renderer.extractLayoutChrome(theme.module.defaultLayout);
1297
1362
  const metadata = {
1298
- resolveAssetUrl: createPreviewAssetResolver(renderer.resolveAssetPath)
1363
+ resolveAssetUrl: createPreviewAssetResolver(renderer.resolveAssetPath),
1364
+ contactForm: PREVIEW_CONTACT_FORM
1299
1365
  };
1300
1366
  const body = renderer.renderToString(
1301
1367
  renderer.createElement(renderer.ThemeRender, {