emdash 0.10.0 → 0.11.0

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 (139) hide show
  1. package/dist/{apply-UsrFuO7l.mjs → apply-Ded_1vng.mjs} +36 -25
  2. package/dist/{apply-UsrFuO7l.mjs.map → apply-Ded_1vng.mjs.map} +1 -1
  3. package/dist/astro/index.d.mts +5 -5
  4. package/dist/astro/index.mjs +1 -1
  5. package/dist/astro/middleware/auth.d.mts +5 -5
  6. package/dist/astro/middleware/redirect.mjs +2 -2
  7. package/dist/astro/middleware.d.mts.map +1 -1
  8. package/dist/astro/middleware.mjs +83 -33
  9. package/dist/astro/middleware.mjs.map +1 -1
  10. package/dist/astro/types.d.mts +9 -7
  11. package/dist/astro/types.d.mts.map +1 -1
  12. package/dist/{byline-C3vnhIpU.mjs → byline-gFn1r0vA.mjs} +2 -2
  13. package/dist/{byline-C3vnhIpU.mjs.map → byline-gFn1r0vA.mjs.map} +1 -1
  14. package/dist/{bylines-esI7ioa9.mjs → bylines-DTFI8nDM.mjs} +4 -4
  15. package/dist/{bylines-esI7ioa9.mjs.map → bylines-DTFI8nDM.mjs.map} +1 -1
  16. package/dist/{cache-fTzxgMFJ.mjs → cache-BAJbeoZ8.mjs} +2 -2
  17. package/dist/{cache-fTzxgMFJ.mjs.map → cache-BAJbeoZ8.mjs.map} +1 -1
  18. package/dist/{chunks-Da2-b-oA.mjs → chunks-BK1oZS-l.mjs} +2 -2
  19. package/dist/{chunks-Da2-b-oA.mjs.map → chunks-BK1oZS-l.mjs.map} +1 -1
  20. package/dist/cli/index.mjs +102 -27
  21. package/dist/cli/index.mjs.map +1 -1
  22. package/dist/{content-C7G4QXkK.mjs → content-CERxPUN0.mjs} +2 -2
  23. package/dist/{content-C7G4QXkK.mjs.map → content-CERxPUN0.mjs.map} +1 -1
  24. package/dist/database/instrumentation.d.mts +6 -4
  25. package/dist/database/instrumentation.d.mts.map +1 -1
  26. package/dist/database/instrumentation.mjs +19 -7
  27. package/dist/database/instrumentation.mjs.map +1 -1
  28. package/dist/db/index.d.mts +2 -2
  29. package/dist/db/index.mjs +1 -1
  30. package/dist/{index-DjPMOfO0.d.mts → index-Cg-rC4Gj.d.mts} +32 -24
  31. package/dist/index-Cg-rC4Gj.d.mts.map +1 -0
  32. package/dist/index.d.mts +7 -7
  33. package/dist/index.mjs +19 -19
  34. package/dist/{load-sXRuM7Us.mjs → load-DR1VwFXR.mjs} +2 -2
  35. package/dist/{load-sXRuM7Us.mjs.map → load-DR1VwFXR.mjs.map} +1 -1
  36. package/dist/{loader-Bx2_9-5e.mjs → loader-ou_PXAjg.mjs} +2 -2
  37. package/dist/{loader-Bx2_9-5e.mjs.map → loader-ou_PXAjg.mjs.map} +1 -1
  38. package/dist/media/local-runtime.d.mts +5 -5
  39. package/dist/media/local-runtime.mjs +1 -1
  40. package/dist/{media-D8FbNsl0.mjs → media-1fFhub9c.mjs} +21 -9
  41. package/dist/media-1fFhub9c.mjs.map +1 -0
  42. package/dist/page/index.d.mts +2 -2
  43. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  44. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  45. package/dist/{query-Bo-msrmu.mjs → query-8c_meo_K.mjs} +10 -10
  46. package/dist/{query-Bo-msrmu.mjs.map → query-8c_meo_K.mjs.map} +1 -1
  47. package/dist/{registry-Beb7wxFc.mjs → registry-Do34mz_P.mjs} +6 -5
  48. package/dist/registry-Do34mz_P.mjs.map +1 -0
  49. package/dist/{request-cache-C-tIpYIw.mjs → request-cache-D4I69LeL.mjs} +6 -2
  50. package/dist/request-cache-D4I69LeL.mjs.map +1 -0
  51. package/dist/request-context.d.mts +27 -1
  52. package/dist/request-context.d.mts.map +1 -1
  53. package/dist/request-context.mjs +16 -3
  54. package/dist/request-context.mjs.map +1 -1
  55. package/dist/{runner-DMnlIkh4.mjs → runner-DIcU2UCC.mjs} +174 -152
  56. package/dist/runner-DIcU2UCC.mjs.map +1 -0
  57. package/dist/{runner-Clwe4Mme.d.mts → runner-Iu3IZSDM.d.mts} +2 -2
  58. package/dist/{runner-Clwe4Mme.d.mts.map → runner-Iu3IZSDM.d.mts.map} +1 -1
  59. package/dist/runtime.d.mts +5 -5
  60. package/dist/runtime.mjs +1 -1
  61. package/dist/{search-DkN-BqsS.mjs → search-DuWhx4NG.mjs} +172 -30
  62. package/dist/search-DuWhx4NG.mjs.map +1 -0
  63. package/dist/seed/index.d.mts +2 -2
  64. package/dist/seed/index.mjs +10 -10
  65. package/dist/{taxonomies-CTtewrSQ.mjs → taxonomies-Bw76xAxo.mjs} +6 -6
  66. package/dist/{taxonomies-CTtewrSQ.mjs.map → taxonomies-Bw76xAxo.mjs.map} +1 -1
  67. package/dist/{taxonomy-DSxx2K2L.mjs → taxonomy-D6NvlKo8.mjs} +3 -3
  68. package/dist/{taxonomy-DSxx2K2L.mjs.map → taxonomy-D6NvlKo8.mjs.map} +1 -1
  69. package/dist/{types-Eg829jj9.mjs → types-56BKbld_.mjs} +1 -1
  70. package/dist/types-56BKbld_.mjs.map +1 -0
  71. package/dist/{types-Dtx1mSMX.d.mts → types-BQx6ZXpR.d.mts} +2 -1
  72. package/dist/types-BQx6ZXpR.d.mts.map +1 -0
  73. package/dist/types-DiI8NOG_.mjs +16 -0
  74. package/dist/types-DiI8NOG_.mjs.map +1 -0
  75. package/dist/{types-D19uBYWn.d.mts → types-IN5z_S3P.d.mts} +19 -98
  76. package/dist/types-IN5z_S3P.d.mts.map +1 -0
  77. package/dist/{types-Dl1fgFjn.d.mts → types-IZSZfEwv.d.mts} +4 -3
  78. package/dist/types-IZSZfEwv.d.mts.map +1 -0
  79. package/dist/{validate-DHGwADqO.d.mts → validate-CO3JjFV5.d.mts} +7 -3
  80. package/dist/validate-CO3JjFV5.d.mts.map +1 -0
  81. package/dist/{validate-CBIbxM3L.mjs → validate-UK4Ja1uo.mjs} +3 -3
  82. package/dist/{validate-CBIbxM3L.mjs.map → validate-UK4Ja1uo.mjs.map} +1 -1
  83. package/dist/{validation-B1NYiEos.mjs → validation-Vc5DQkJa.mjs} +4 -4
  84. package/dist/{validation-B1NYiEos.mjs.map → validation-Vc5DQkJa.mjs.map} +1 -1
  85. package/dist/version-Bg31I_Ff.mjs +7 -0
  86. package/dist/{version-CMD42IRC.mjs.map → version-Bg31I_Ff.mjs.map} +1 -1
  87. package/dist/{zod-generator-BNJDQBSZ.mjs → zod-generator-CHnJUP2l.mjs} +1 -1
  88. package/dist/{zod-generator-BNJDQBSZ.mjs.map → zod-generator-CHnJUP2l.mjs.map} +1 -1
  89. package/package.json +9 -8
  90. package/src/api/errors.ts +5 -0
  91. package/src/api/handlers/content.ts +9 -0
  92. package/src/api/handlers/media-allowlist.ts +40 -0
  93. package/src/api/handlers/media.ts +1 -1
  94. package/src/api/handlers/menus.ts +158 -28
  95. package/src/api/handlers/validate-media-fields.ts +125 -0
  96. package/src/api/schemas/media.ts +23 -3
  97. package/src/api/schemas/schema.ts +11 -2
  98. package/src/astro/middleware.ts +46 -11
  99. package/src/astro/routes/api/media/upload-url.ts +10 -4
  100. package/src/astro/routes/api/media.ts +12 -4
  101. package/src/astro/types.ts +5 -1
  102. package/src/auth/rate-limit.ts +3 -3
  103. package/src/cli/commands/bundle-utils.ts +81 -6
  104. package/src/cli/commands/bundle.ts +18 -15
  105. package/src/cli/commands/export-seed.ts +57 -3
  106. package/src/database/instrumentation.ts +22 -8
  107. package/src/database/migrations/016_api_tokens.ts +18 -3
  108. package/src/database/migrations/037_credential_algorithm.ts +18 -0
  109. package/src/database/migrations/runner.ts +2 -0
  110. package/src/database/repositories/media.ts +40 -10
  111. package/src/database/types.ts +2 -1
  112. package/src/emdash-runtime.ts +16 -3
  113. package/src/fields/file.ts +7 -6
  114. package/src/fields/image.ts +12 -11
  115. package/src/fields/types.ts +3 -0
  116. package/src/index.ts +1 -1
  117. package/src/mcp/server.ts +37 -8
  118. package/src/media/mime.ts +75 -0
  119. package/src/plugins/types.ts +81 -191
  120. package/src/request-cache.ts +6 -2
  121. package/src/request-context.ts +42 -2
  122. package/src/schema/registry.ts +5 -5
  123. package/src/schema/types.ts +3 -2
  124. package/src/seed/apply.ts +25 -8
  125. package/src/seed/types.ts +4 -0
  126. package/dist/index-DjPMOfO0.d.mts.map +0 -1
  127. package/dist/media-D8FbNsl0.mjs.map +0 -1
  128. package/dist/registry-Beb7wxFc.mjs.map +0 -1
  129. package/dist/request-cache-C-tIpYIw.mjs.map +0 -1
  130. package/dist/runner-DMnlIkh4.mjs.map +0 -1
  131. package/dist/search-DkN-BqsS.mjs.map +0 -1
  132. package/dist/types-CoO6mpV3.mjs +0 -68
  133. package/dist/types-CoO6mpV3.mjs.map +0 -1
  134. package/dist/types-D19uBYWn.d.mts.map +0 -1
  135. package/dist/types-Dl1fgFjn.d.mts.map +0 -1
  136. package/dist/types-Dtx1mSMX.d.mts.map +0 -1
  137. package/dist/types-Eg829jj9.mjs.map +0 -1
  138. package/dist/validate-DHGwADqO.d.mts.map +0 -1
  139. package/dist/version-CMD42IRC.mjs +0 -7
@@ -1,25 +1,25 @@
1
1
  import { n as validateJsonFieldName, r as validatePluginIdentifier, t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
2
2
  import { s as jsonExtractExpr } from "./dialect-helpers-BKCvISIQ.mjs";
3
3
  import { r as isI18nEnabled } from "./config-CVssduLe.mjs";
4
- import { a as slugify, r as RevisionRepository, t as ContentRepository } from "./content-C7G4QXkK.mjs";
4
+ import { a as slugify, r as RevisionRepository, t as ContentRepository } from "./content-CERxPUN0.mjs";
5
5
  import { r as encodeBase64, t as decodeBase64 } from "./base64-MBPo9ozB.mjs";
6
6
  import { i as encodeCursor, n as InvalidCursorError, r as decodeCursor, t as EmDashValidationError } from "./types-BIgulNsW.mjs";
7
- import { t as MediaRepository } from "./media-D8FbNsl0.mjs";
7
+ import { t as MediaRepository } from "./media-1fFhub9c.mjs";
8
8
  import { t as OptionsRepository } from "./options-nPxWnrya.mjs";
9
9
  import { t as withTransaction } from "./transaction-D44LBXvU.mjs";
10
10
  import { t as RedirectRepository } from "./redirect-C5H7VGIX.mjs";
11
- import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-Da2-b-oA.mjs";
12
- import { t as BylineRepository } from "./byline-C3vnhIpU.mjs";
13
- import { r as invalidateRedirectCache } from "./cache-fTzxgMFJ.mjs";
11
+ import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-BK1oZS-l.mjs";
12
+ import { t as BylineRepository } from "./byline-gFn1r0vA.mjs";
13
+ import { r as invalidateRedirectCache } from "./cache-BAJbeoZ8.mjs";
14
14
  import { t as isMissingTableError } from "./db-errors-B7P2pSCn.mjs";
15
- import { r as hashString } from "./zod-generator-BNJDQBSZ.mjs";
16
- import { i as FTSManager, n as SchemaRegistry } from "./registry-Beb7wxFc.mjs";
17
- import { r as getDb } from "./loader-Bx2_9-5e.mjs";
18
- import { n as requestCached } from "./request-cache-C-tIpYIw.mjs";
19
- import { a as ssrfSafeFetch, i as resolveAndValidateExternalUrl, o as stripCredentialHeaders, r as SsrfError, s as validateExternalUrl } from "./apply-UsrFuO7l.mjs";
20
- import { d as resolveLocale, f as resolveLocaleChain } from "./taxonomies-CTtewrSQ.mjs";
15
+ import { n as requestCached } from "./request-cache-D4I69LeL.mjs";
16
+ import { r as hashString } from "./zod-generator-CHnJUP2l.mjs";
17
+ import { i as FTSManager, n as SchemaRegistry } from "./registry-Do34mz_P.mjs";
18
+ import { r as getDb } from "./loader-ou_PXAjg.mjs";
19
+ import { a as ssrfSafeFetch, i as resolveAndValidateExternalUrl, o as stripCredentialHeaders, r as SsrfError, s as validateExternalUrl } from "./apply-Ded_1vng.mjs";
20
+ import { d as resolveLocale, f as resolveLocaleChain } from "./taxonomies-Bw76xAxo.mjs";
21
21
  import { i as pluginManifestSchema } from "./manifest-schema-CXAbd1vH.mjs";
22
- import { i as normalizeCapabilities } from "./types-CoO6mpV3.mjs";
22
+ import { i as normalizeCapabilities } from "./types-DiI8NOG_.mjs";
23
23
  import { t as generatePreviewToken } from "./tokens-CyRDPVW2.mjs";
24
24
  import { sql } from "kysely";
25
25
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -739,9 +739,6 @@ var PluginStorageRepository = class {
739
739
 
740
740
  //#endregion
741
741
  //#region src/fields/image.ts
742
- /**
743
- * Image field schema
744
- */
745
742
  const imageSchema = z.object({
746
743
  id: z.string(),
747
744
  src: z.string(),
@@ -749,17 +746,39 @@ const imageSchema = z.object({
749
746
  width: z.number().optional(),
750
747
  height: z.number().optional()
751
748
  });
752
- /**
753
- * Image field
754
- * References media items from the media library
755
- */
756
- function image(options) {
749
+ function image(options = {}) {
750
+ const validation = options.allowedTypes && options.allowedTypes.length > 0 ? { allowedMimeTypes: [...options.allowedTypes] } : void 0;
757
751
  return {
758
752
  type: "image",
759
753
  columnType: "TEXT",
760
- schema: options?.required === false ? imageSchema.optional() : imageSchema,
754
+ schema: options.required === false ? imageSchema.optional() : imageSchema,
761
755
  options,
762
- ui: { widget: "image" }
756
+ ui: { widget: "image" },
757
+ validation
758
+ };
759
+ }
760
+
761
+ //#endregion
762
+ //#region src/fields/file.ts
763
+ function file(options = {}) {
764
+ const fileObjSchema = z.object({
765
+ id: z.string(),
766
+ url: z.string(),
767
+ filename: z.string(),
768
+ mimeType: z.string(),
769
+ size: z.number()
770
+ });
771
+ return {
772
+ type: "file",
773
+ columnType: "TEXT",
774
+ schema: options.required ? fileObjSchema : fileObjSchema.optional(),
775
+ options,
776
+ ui: {
777
+ widget: "file",
778
+ helpText: options.helpText,
779
+ maxSize: options.maxSize
780
+ },
781
+ validation: options.allowedTypes && options.allowedTypes.length > 0 ? { allowedMimeTypes: [...options.allowedTypes] } : void 0
763
782
  };
764
783
  }
765
784
 
@@ -978,6 +997,115 @@ function validateRev(rev, item) {
978
997
  return { valid: true };
979
998
  }
980
999
 
1000
+ //#endregion
1001
+ //#region src/media/mime.ts
1002
+ function normalizeMime(mime) {
1003
+ return mime.split(";")[0].trim().toLowerCase();
1004
+ }
1005
+ function matchesMimeAllowlist(mime, allowList) {
1006
+ const normalized = normalizeMime(mime);
1007
+ for (const entry of allowList) {
1008
+ if (!entry || !entry.includes("/")) continue;
1009
+ const normalizedEntry = normalizeMime(entry);
1010
+ if (normalizedEntry.endsWith("/")) {
1011
+ if (normalized.startsWith(normalizedEntry)) return true;
1012
+ } else if (normalized === normalizedEntry) return true;
1013
+ }
1014
+ return false;
1015
+ }
1016
+ /**
1017
+ * Extract the `allowedMimeTypes` list from a `_emdash_fields.validation` row
1018
+ * (raw JSON string). Returns null when the value is missing, malformed, or the
1019
+ * list is empty — callers treat that as "no field-specific constraint".
1020
+ */
1021
+ function parseAllowedMimeTypes(rawValidation) {
1022
+ if (!rawValidation) return null;
1023
+ try {
1024
+ const parsed = JSON.parse(rawValidation);
1025
+ if (typeof parsed !== "object" || parsed === null) return null;
1026
+ const list = parsed.allowedMimeTypes;
1027
+ if (!Array.isArray(list) || list.length === 0) return null;
1028
+ return list.filter((entry) => typeof entry === "string");
1029
+ } catch {
1030
+ return null;
1031
+ }
1032
+ }
1033
+
1034
+ //#endregion
1035
+ //#region src/api/handlers/validate-media-fields.ts
1036
+ function asMediaRef(value) {
1037
+ if (value === null || value === void 0) return null;
1038
+ if (typeof value !== "object" || Array.isArray(value)) return null;
1039
+ return value;
1040
+ }
1041
+ function fail(message) {
1042
+ return {
1043
+ success: false,
1044
+ error: {
1045
+ code: "INVALID_MIME_FOR_FIELD",
1046
+ message
1047
+ }
1048
+ };
1049
+ }
1050
+ async function loadMediaFieldsForCollection(db, collectionSlug) {
1051
+ const rows = await db.selectFrom("_emdash_fields").innerJoin("_emdash_collections", "_emdash_collections.id", "_emdash_fields.collection_id").select([
1052
+ "_emdash_fields.slug",
1053
+ "_emdash_fields.type",
1054
+ "_emdash_fields.validation"
1055
+ ]).where("_emdash_collections.slug", "=", collectionSlug).where("_emdash_fields.type", "in", ["file", "image"]).execute();
1056
+ const out = [];
1057
+ for (const row of rows) {
1058
+ const list = parseAllowedMimeTypes(row.validation);
1059
+ if (!list) continue;
1060
+ out.push({
1061
+ slug: row.slug,
1062
+ type: row.type,
1063
+ allowedMimeTypes: list
1064
+ });
1065
+ }
1066
+ return out;
1067
+ }
1068
+ async function validateMediaFields(db, collectionSlug, data) {
1069
+ const fields = await requestCached(`mediaFields:${collectionSlug}`, () => loadMediaFieldsForCollection(db, collectionSlug));
1070
+ if (fields.length === 0) return {
1071
+ success: true,
1072
+ data: true
1073
+ };
1074
+ const localIds = /* @__PURE__ */ new Set();
1075
+ for (const field of fields) {
1076
+ const ref = asMediaRef(data[field.slug]);
1077
+ if (!ref) continue;
1078
+ if ((typeof ref.provider === "string" ? ref.provider : "local") === "local" && typeof ref.id === "string") localIds.add(ref.id);
1079
+ }
1080
+ const idList = [...localIds];
1081
+ const mimeById = /* @__PURE__ */ new Map();
1082
+ if (idList.length > 0) for (const batch of chunks(idList, SQL_BATCH_SIZE)) {
1083
+ const rows = await db.selectFrom("media").select(["id", "mime_type"]).where("id", "in", batch).execute();
1084
+ for (const r of rows) mimeById.set(r.id, r.mime_type);
1085
+ }
1086
+ for (const field of fields) {
1087
+ const value = data[field.slug];
1088
+ if (value === null || value === void 0) continue;
1089
+ const ref = asMediaRef(value);
1090
+ if (!ref) continue;
1091
+ const provider = typeof ref.provider === "string" ? ref.provider : "local";
1092
+ let mime;
1093
+ if (provider === "local") {
1094
+ if (typeof ref.id !== "string") return fail(`Field '${field.slug}' references media with an invalid id`);
1095
+ mime = mimeById.get(ref.id);
1096
+ if (!mime) return fail(`Field '${field.slug}' references media with unknown MIME type`);
1097
+ } else {
1098
+ if (typeof ref.mimeType !== "string") return fail(`Field '${field.slug}' requires a mimeType declaration for non-local media`);
1099
+ mime = ref.mimeType;
1100
+ }
1101
+ if (!matchesMimeAllowlist(mime, field.allowedMimeTypes)) return fail(`Field '${field.slug}' does not accept ${mime}`);
1102
+ }
1103
+ return {
1104
+ success: true,
1105
+ data: true
1106
+ };
1107
+ }
1108
+
981
1109
  //#endregion
982
1110
  //#region src/api/handlers/content.ts
983
1111
  /**
@@ -1255,6 +1383,8 @@ async function handleContentCreate(db, collection, body) {
1255
1383
  message: `Collection "${collection}" does not have SEO enabled. Remove the seo field or enable SEO on this collection.`
1256
1384
  }
1257
1385
  };
1386
+ const mimeCheck = await validateMediaFields(db, collection, body.data);
1387
+ if (!mimeCheck.success) return mimeCheck;
1258
1388
  const item = await withTransaction(db, async (trx) => {
1259
1389
  const repo = new ContentRepository(trx);
1260
1390
  const bylineRepo = new BylineRepository(trx);
@@ -1280,7 +1410,7 @@ async function handleContentCreate(db, collection, body) {
1280
1410
  }
1281
1411
  await hydrateBylines(trx, collection, created);
1282
1412
  if (body.translationOf) {
1283
- const { TaxonomyRepository } = await import("./taxonomy-DSxx2K2L.mjs").then((n) => n.n);
1413
+ const { TaxonomyRepository } = await import("./taxonomy-D6NvlKo8.mjs").then((n) => n.n);
1284
1414
  await new TaxonomyRepository(trx).copyEntryTerms(collection, body.translationOf, created.id);
1285
1415
  }
1286
1416
  if (body.seo && hasSeo) created.seo = await new SeoRepository(trx).upsert(collection, created.id, body.seo);
@@ -1353,6 +1483,10 @@ async function handleContentUpdate(db, collection, id, body) {
1353
1483
  message: `Collection "${collection}" does not have SEO enabled. Remove the seo field or enable SEO on this collection.`
1354
1484
  }
1355
1485
  };
1486
+ if (body.data) {
1487
+ const mimeCheck = await validateMediaFields(db, collection, body.data);
1488
+ if (!mimeCheck.success) return mimeCheck;
1489
+ }
1356
1490
  const resolvedId = await resolveId(new ContentRepository(db), collection, id) ?? id;
1357
1491
  const item = await withTransaction(db, async (trx) => {
1358
1492
  const trxRepo = new ContentRepository(trx);
@@ -2869,7 +3003,14 @@ const contentTranslationsResponseSchema = z$1.object({
2869
3003
 
2870
3004
  //#endregion
2871
3005
  //#region src/api/schemas/media.ts
2872
- const mediaListQuery = cursorPaginationQuery.extend({ mimeType: z$1.string().optional() }).meta({ id: "MediaListQuery" });
3006
+ /**
3007
+ * Accepts a comma-separated string (from URL query params) or an array of
3008
+ * strings (from JSON body or programmatic use) and normalises to string[].
3009
+ */
3010
+ const mimeTypeFilter = z$1.union([z$1.string(), z$1.array(z$1.string())]).transform((v) => {
3011
+ return (Array.isArray(v) ? v : v.split(",")).map((s) => s.trim()).filter((s) => s.length > 0);
3012
+ }).optional();
3013
+ const mediaListQuery = cursorPaginationQuery.extend({ mimeType: mimeTypeFilter }).meta({ id: "MediaListQuery" });
2873
3014
  const mediaUpdateBody = z$1.object({
2874
3015
  alt: z$1.string().optional(),
2875
3016
  caption: z$1.string().optional(),
@@ -2885,7 +3026,7 @@ const mediaConfirmBody = z$1.object({
2885
3026
  }).meta({ id: "MediaConfirmBody" });
2886
3027
  const mediaProviderListQuery = cursorPaginationQuery.extend({
2887
3028
  query: z$1.string().optional(),
2888
- mimeType: z$1.string().optional()
3029
+ mimeType: mimeTypeFilter
2889
3030
  }).meta({ id: "MediaProviderListQuery" });
2890
3031
  const mediaStatusSchema = z$1.enum([
2891
3032
  "pending",
@@ -2983,7 +3124,8 @@ const fieldValidation = z$1.object({
2983
3124
  options: z$1.array(z$1.string()).optional(),
2984
3125
  subFields: z$1.array(repeaterSubFieldSchema).min(1).optional(),
2985
3126
  minItems: z$1.number().int().min(0).optional(),
2986
- maxItems: z$1.number().int().min(1).optional()
3127
+ maxItems: z$1.number().int().min(1).optional(),
3128
+ allowedMimeTypes: z$1.array(z$1.string().regex(/^[a-z0-9][a-z0-9!#$&^_+\-.]*\/[a-z0-9!#$&^_+\-.]*$/i, "Invalid MIME type")).min(1, "allowedMimeTypes must not be empty — omit the field to allow all types").max(64, "allowedMimeTypes may contain at most 64 entries").optional()
2987
3129
  }).optional();
2988
3130
  const fieldWidgetOptions = z$1.record(z$1.string(), z$1.unknown()).optional();
2989
3131
  const createCollectionBody = z$1.object({
@@ -3021,7 +3163,7 @@ const createFieldBody = z$1.object({
3021
3163
  required: z$1.boolean().optional(),
3022
3164
  unique: z$1.boolean().optional(),
3023
3165
  defaultValue: z$1.unknown().optional(),
3024
- validation: fieldValidation,
3166
+ validation: fieldValidation.nullable(),
3025
3167
  widget: z$1.string().optional(),
3026
3168
  options: fieldWidgetOptions,
3027
3169
  sortOrder: z$1.number().int().min(0).optional(),
@@ -3033,7 +3175,7 @@ const updateFieldBody = z$1.object({
3033
3175
  required: z$1.boolean().optional(),
3034
3176
  unique: z$1.boolean().optional(),
3035
3177
  defaultValue: z$1.unknown().optional(),
3036
- validation: fieldValidation,
3178
+ validation: fieldValidation.nullable(),
3037
3179
  widget: z$1.string().optional(),
3038
3180
  options: fieldWidgetOptions,
3039
3181
  sortOrder: z$1.number().int().min(0).optional(),
@@ -9751,5 +9893,5 @@ function extractSearchableFields(entry, fields) {
9751
9893
  }
9752
9894
 
9753
9895
  //#endregion
9754
- export { isSafeHref as $, NoopSandboxRunner as A, handleContentTranslations as At, HookPipeline as B, getAllSources as C, handleContentGetIncludingTrashed as Ct, probeUrl as D, handleContentPublish as Dt, getUrlSources as E, handleContentPermanentDelete as Et, PluginRouteError as F, portableText as Ft, sanitizeHeadersForSandbox as G, resolveExclusiveHooks as H, PluginRouteRegistry as I, reference as It, parseWxr as J, getTrustedProxyHeaders as K, DEV_CONSOLE_EMAIL_PLUGIN_ID as L, image as Lt, createNoopSandboxRunner as M, handleContentUnschedule as Mt, PluginManager as N, handleContentUpdate as Nt, registerSource as O, handleContentRestore as Ot, createPluginManager as P, validateRev as Pt, prosemirrorToPortableText as Q, devConsoleEmailDeliver as R, clearSources as S, handleContentGet as St, getSource as T, handleContentListTrashed as Tt, CronExecutor as U, createHookPipeline as V, extractRequestMeta as W, after as X, parseWxrString as Y, portableTextToProsemirror as Z, buildPreviewUrl as _, handleContentCountTrashed as _t, search as a, getCollectionInfo as at, parseWxrDate as b, handleContentDiscardDraft as bt, getWidgetArea as c, handleMediaGet as ct, getMenu as d, handleRevisionGet as dt, sanitizeHref as et, getMenus as f, handleRevisionList as ft, isPreviewRequest as g, handleContentCountScheduled as gt, getPreviewToken as h, handleContentCompare as ht, getSuggestions as i, PluginStateRepository as it, SandboxNotAvailableError as j, handleContentUnpublish as jt, importReusableBlocksAsSections as k, handleContentSchedule as kt, getWidgetAreas as l, handleMediaList as lt, getComments as m, generateManifest as mt, extractSearchableFields as n, getSection as nt, searchCollection as o, handleMediaCreate as ot, getCommentCount as p, handleRevisionRestore as pt, definePlugin as q, getSearchStats as r, getSections as rt, searchWithDb as s, handleMediaDelete as st, extractPlainText as t, loadBundleFromR2 as tt, getWidgetComponents as u, handleMediaUpdate as ut, getPreviewUrl as v, handleContentCreate as vt, getFileSources as w, handleContentList as wt, wxrSource as x, handleContentDuplicate as xt, wordpressRestSource as y, handleContentDelete as yt, EmailPipeline as z };
9755
- //# sourceMappingURL=search-DkN-BqsS.mjs.map
9896
+ export { isSafeHref as $, NoopSandboxRunner as A, handleContentTranslations as At, HookPipeline as B, getAllSources as C, handleContentGetIncludingTrashed as Ct, probeUrl as D, handleContentPublish as Dt, getUrlSources as E, handleContentPermanentDelete as Et, PluginRouteError as F, portableText as Ft, sanitizeHeadersForSandbox as G, resolveExclusiveHooks as H, PluginRouteRegistry as I, reference as It, parseWxr as J, getTrustedProxyHeaders as K, DEV_CONSOLE_EMAIL_PLUGIN_ID as L, file as Lt, createNoopSandboxRunner as M, handleContentUnschedule as Mt, PluginManager as N, handleContentUpdate as Nt, registerSource as O, handleContentRestore as Ot, createPluginManager as P, validateRev as Pt, prosemirrorToPortableText as Q, devConsoleEmailDeliver as R, image as Rt, clearSources as S, handleContentGet as St, getSource as T, handleContentListTrashed as Tt, CronExecutor as U, createHookPipeline as V, extractRequestMeta as W, after as X, parseWxrString as Y, portableTextToProsemirror as Z, buildPreviewUrl as _, handleContentCountTrashed as _t, search as a, getCollectionInfo as at, parseWxrDate as b, handleContentDiscardDraft as bt, getWidgetArea as c, handleMediaGet as ct, getMenu as d, handleRevisionGet as dt, sanitizeHref as et, getMenus as f, handleRevisionList as ft, isPreviewRequest as g, handleContentCountScheduled as gt, getPreviewToken as h, handleContentCompare as ht, getSuggestions as i, PluginStateRepository as it, SandboxNotAvailableError as j, handleContentUnpublish as jt, importReusableBlocksAsSections as k, handleContentSchedule as kt, getWidgetAreas as l, handleMediaList as lt, getComments as m, generateManifest as mt, extractSearchableFields as n, getSection as nt, searchCollection as o, handleMediaCreate as ot, getCommentCount as p, handleRevisionRestore as pt, definePlugin as q, getSearchStats as r, getSections as rt, searchWithDb as s, handleMediaDelete as st, extractPlainText as t, loadBundleFromR2 as tt, getWidgetComponents as u, handleMediaUpdate as ut, getPreviewUrl as v, handleContentCreate as vt, getFileSources as w, handleContentList as wt, wxrSource as x, handleContentDuplicate as xt, wordpressRestSource as y, handleContentDelete as yt, EmailPipeline as z };
9897
+ //# sourceMappingURL=search-DuWhx4NG.mjs.map