emdash 0.9.0 → 0.10.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 (195) hide show
  1. package/dist/{adapters-DoNJiveC.d.mts → adapters-BktHA7EO.d.mts} +1 -1
  2. package/dist/{adapters-DoNJiveC.d.mts.map → adapters-BktHA7EO.d.mts.map} +1 -1
  3. package/dist/{apply-BzltprvY.mjs → apply-UsrFuO7l.mjs} +156 -254
  4. package/dist/apply-UsrFuO7l.mjs.map +1 -0
  5. package/dist/astro/index.d.mts +6 -6
  6. package/dist/astro/index.mjs +10 -2
  7. package/dist/astro/index.mjs.map +1 -1
  8. package/dist/astro/middleware/auth.d.mts +5 -5
  9. package/dist/astro/middleware/auth.mjs +5 -5
  10. package/dist/astro/middleware/redirect.mjs +5 -5
  11. package/dist/astro/middleware/request-context.mjs +4 -4
  12. package/dist/astro/middleware/setup.mjs +1 -1
  13. package/dist/astro/middleware.mjs +35 -34
  14. package/dist/astro/middleware.mjs.map +1 -1
  15. package/dist/astro/types.d.mts +8 -9
  16. package/dist/astro/types.d.mts.map +1 -1
  17. package/dist/{base64-BRICGH2l.mjs → base64-MBPo9ozB.mjs} +1 -1
  18. package/dist/{base64-BRICGH2l.mjs.map → base64-MBPo9ozB.mjs.map} +1 -1
  19. package/dist/{byline-BSaNL1w7.mjs → byline-C3vnhIpU.mjs} +4 -4
  20. package/dist/{byline-BSaNL1w7.mjs.map → byline-C3vnhIpU.mjs.map} +1 -1
  21. package/dist/{bylines-CvJ3PYz2.mjs → bylines-esI7ioa9.mjs} +5 -5
  22. package/dist/{bylines-CvJ3PYz2.mjs.map → bylines-esI7ioa9.mjs.map} +1 -1
  23. package/dist/{cache-C6N_hhN7.mjs → cache-fTzxgMFJ.mjs} +3 -3
  24. package/dist/{cache-C6N_hhN7.mjs.map → cache-fTzxgMFJ.mjs.map} +1 -1
  25. package/dist/{chunks-NBQVDOci.mjs → chunks-Da2-b-oA.mjs} +2 -2
  26. package/dist/{chunks-NBQVDOci.mjs.map → chunks-Da2-b-oA.mjs.map} +1 -1
  27. package/dist/cli/index.mjs +251 -79
  28. package/dist/cli/index.mjs.map +1 -1
  29. package/dist/client/cf-access.d.mts +1 -1
  30. package/dist/client/index.d.mts +1 -1
  31. package/dist/client/index.mjs +1 -1
  32. package/dist/{config-BI0V3ICQ.mjs → config-CVssduLe.mjs} +1 -1
  33. package/dist/{config-BI0V3ICQ.mjs.map → config-CVssduLe.mjs.map} +1 -1
  34. package/dist/{content-8lOYF0pr.mjs → content-C7G4QXkK.mjs} +14 -3
  35. package/dist/content-C7G4QXkK.mjs.map +1 -0
  36. package/dist/db/index.d.mts +3 -3
  37. package/dist/db/index.mjs +1 -1
  38. package/dist/db/libsql.d.mts +1 -1
  39. package/dist/db/postgres.d.mts +1 -1
  40. package/dist/db/sqlite.d.mts +1 -1
  41. package/dist/{db-errors-WRezodiz.mjs → db-errors-B7P2pSCn.mjs} +1 -1
  42. package/dist/{db-errors-WRezodiz.mjs.map → db-errors-B7P2pSCn.mjs.map} +1 -1
  43. package/dist/{default-D8ksjWhO.mjs → default-pHuz9WF6.mjs} +1 -1
  44. package/dist/{default-D8ksjWhO.mjs.map → default-pHuz9WF6.mjs.map} +1 -1
  45. package/dist/{error-D_-tqP-I.mjs → error-DqnRMM5z.mjs} +1 -1
  46. package/dist/{error-D_-tqP-I.mjs.map → error-DqnRMM5z.mjs.map} +1 -1
  47. package/dist/{index-BFRaVcD6.d.mts → index-DjPMOfO0.d.mts} +82 -67
  48. package/dist/index-DjPMOfO0.d.mts.map +1 -0
  49. package/dist/index.d.mts +10 -10
  50. package/dist/index.mjs +28 -27
  51. package/dist/{load-DDqMMvZL.mjs → load-sXRuM7Us.mjs} +2 -2
  52. package/dist/{load-DDqMMvZL.mjs.map → load-sXRuM7Us.mjs.map} +1 -1
  53. package/dist/{loader-CKLbBnhK.mjs → loader-Bx2_9-5e.mjs} +31 -6
  54. package/dist/loader-Bx2_9-5e.mjs.map +1 -0
  55. package/dist/{manifest-schema-DqWNC3lM.mjs → manifest-schema-CXAbd1vH.mjs} +1 -1
  56. package/dist/{manifest-schema-DqWNC3lM.mjs.map → manifest-schema-CXAbd1vH.mjs.map} +1 -1
  57. package/dist/media/index.d.mts +1 -1
  58. package/dist/media/index.mjs +1 -1
  59. package/dist/media/local-runtime.d.mts +7 -7
  60. package/dist/media/local-runtime.mjs +3 -3
  61. package/dist/{media-BW32b4gi.mjs → media-D8FbNsl0.mjs} +2 -2
  62. package/dist/{media-BW32b4gi.mjs.map → media-D8FbNsl0.mjs.map} +1 -1
  63. package/dist/{mode-ier8jbBk.mjs → mode-YhqNVef_.mjs} +1 -1
  64. package/dist/{mode-ier8jbBk.mjs.map → mode-YhqNVef_.mjs.map} +1 -1
  65. package/dist/{options-BVp3UsTS.mjs → options-nPxWnrya.mjs} +1 -1
  66. package/dist/{options-BVp3UsTS.mjs.map → options-nPxWnrya.mjs.map} +1 -1
  67. package/dist/page/index.d.mts +2 -2
  68. package/dist/{patterns-CrCYkMBb.mjs → patterns-DsUZ4uxI.mjs} +1 -1
  69. package/dist/{patterns-CrCYkMBb.mjs.map → patterns-DsUZ4uxI.mjs.map} +1 -1
  70. package/dist/{placeholder-BE4o_2dc.d.mts → placeholder-CDPtkelt.d.mts} +1 -1
  71. package/dist/{placeholder-BE4o_2dc.d.mts.map → placeholder-CDPtkelt.d.mts.map} +1 -1
  72. package/dist/{placeholder-CIJejMlK.mjs → placeholder-Ci0RLeCk.mjs} +1 -1
  73. package/dist/{placeholder-CIJejMlK.mjs.map → placeholder-Ci0RLeCk.mjs.map} +1 -1
  74. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  75. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  76. package/dist/{public-url-DByxYjUw.mjs → public-url-B1AxbbbQ.mjs} +1 -1
  77. package/dist/{public-url-DByxYjUw.mjs.map → public-url-B1AxbbbQ.mjs.map} +1 -1
  78. package/dist/{query-Cg9ZKRQ0.mjs → query-Bo-msrmu.mjs} +13 -13
  79. package/dist/{query-Cg9ZKRQ0.mjs.map → query-Bo-msrmu.mjs.map} +1 -1
  80. package/dist/{redirect-BhUBKRc1.mjs → redirect-C5H7VGIX.mjs} +3 -3
  81. package/dist/{redirect-BhUBKRc1.mjs.map → redirect-C5H7VGIX.mjs.map} +1 -1
  82. package/dist/{registry-Dw70ChxB.mjs → registry-Beb7wxFc.mjs} +5 -5
  83. package/dist/{registry-Dw70ChxB.mjs.map → registry-Beb7wxFc.mjs.map} +1 -1
  84. package/dist/{request-cache-B-bmkipQ.mjs → request-cache-C-tIpYIw.mjs} +1 -1
  85. package/dist/{request-cache-B-bmkipQ.mjs.map → request-cache-C-tIpYIw.mjs.map} +1 -1
  86. package/dist/{runner-Bnoj7vjK.d.mts → runner-Clwe4Mme.d.mts} +2 -2
  87. package/dist/{runner-Bnoj7vjK.d.mts.map → runner-Clwe4Mme.d.mts.map} +1 -1
  88. package/dist/{runner-C7ADox5q.mjs → runner-DMnlIkh4.mjs} +433 -138
  89. package/dist/runner-DMnlIkh4.mjs.map +1 -0
  90. package/dist/runtime.d.mts +6 -6
  91. package/dist/runtime.mjs +3 -3
  92. package/dist/{search-dOGEccMa.mjs → search-DkN-BqsS.mjs} +164 -92
  93. package/dist/search-DkN-BqsS.mjs.map +1 -0
  94. package/dist/{secrets-CW3reAnU.mjs → secrets-CZ8rxLX3.mjs} +3 -3
  95. package/dist/{secrets-CW3reAnU.mjs.map → secrets-CZ8rxLX3.mjs.map} +1 -1
  96. package/dist/seed/index.d.mts +2 -2
  97. package/dist/seed/index.mjs +15 -14
  98. package/dist/seo/index.d.mts +1 -1
  99. package/dist/storage/local.d.mts +1 -1
  100. package/dist/storage/local.mjs +1 -1
  101. package/dist/storage/s3.d.mts +1 -1
  102. package/dist/storage/s3.mjs +1 -1
  103. package/dist/taxonomies-CTtewrSQ.mjs +407 -0
  104. package/dist/taxonomies-CTtewrSQ.mjs.map +1 -0
  105. package/dist/taxonomy-DSxx2K2L.mjs +218 -0
  106. package/dist/taxonomy-DSxx2K2L.mjs.map +1 -0
  107. package/dist/{tokens-D7zMmWi2.mjs → tokens-CyRDPVW2.mjs} +2 -2
  108. package/dist/{tokens-D7zMmWi2.mjs.map → tokens-CyRDPVW2.mjs.map} +1 -1
  109. package/dist/{transaction-Cn2rjY78.mjs → transaction-D44LBXvU.mjs} +1 -1
  110. package/dist/{transaction-Cn2rjY78.mjs.map → transaction-D44LBXvU.mjs.map} +1 -1
  111. package/dist/{transport-DNEfeMaU.d.mts → transport-DX_5rpsq.d.mts} +1 -1
  112. package/dist/{transport-DNEfeMaU.d.mts.map → transport-DX_5rpsq.d.mts.map} +1 -1
  113. package/dist/{transport-BeMCmin1.mjs → transport-xpzIjCIB.mjs} +1 -1
  114. package/dist/{transport-BeMCmin1.mjs.map → transport-xpzIjCIB.mjs.map} +1 -1
  115. package/dist/{types-CRxNbK-Z.mjs → types-BIgulNsW.mjs} +2 -2
  116. package/dist/{types-CRxNbK-Z.mjs.map → types-BIgulNsW.mjs.map} +1 -1
  117. package/dist/{types-CJsYGpco.d.mts → types-B_CXXnzh.d.mts} +1 -1
  118. package/dist/{types-CJsYGpco.d.mts.map → types-B_CXXnzh.d.mts.map} +1 -1
  119. package/dist/{types-M78DQ1lx.d.mts → types-C-aFbqmA.d.mts} +1 -1
  120. package/dist/{types-M78DQ1lx.d.mts.map → types-C-aFbqmA.d.mts.map} +1 -1
  121. package/dist/{types-4fVtCIm0.mjs → types-CoO6mpV3.mjs} +1 -1
  122. package/dist/{types-4fVtCIm0.mjs.map → types-CoO6mpV3.mjs.map} +1 -1
  123. package/dist/{types-BuBIptGk.d.mts → types-D19uBYWn.d.mts} +149 -4
  124. package/dist/types-D19uBYWn.d.mts.map +1 -0
  125. package/dist/{types-BSyXeCFW.d.mts → types-Dl1fgFjn.d.mts} +1 -1
  126. package/dist/{types-BSyXeCFW.d.mts.map → types-Dl1fgFjn.d.mts.map} +1 -1
  127. package/dist/{types-CrtWgIvl.d.mts → types-Dtx1mSMX.d.mts} +9 -1
  128. package/dist/types-Dtx1mSMX.d.mts.map +1 -0
  129. package/dist/{types-CIOg5AR8.mjs → types-Eg829jj9.mjs} +1 -1
  130. package/dist/{types-CIOg5AR8.mjs.map → types-Eg829jj9.mjs.map} +1 -1
  131. package/dist/{types-CDbKp7ND.mjs → types-K-EkEQCI.mjs} +1 -1
  132. package/dist/{types-CDbKp7ND.mjs.map → types-K-EkEQCI.mjs.map} +1 -1
  133. package/dist/{validate-Baqf0slj.mjs → validate-CBIbxM3L.mjs} +14 -10
  134. package/dist/validate-CBIbxM3L.mjs.map +1 -0
  135. package/dist/{validate-BfQh_C_y.d.mts → validate-DHGwADqO.d.mts} +18 -5
  136. package/dist/validate-DHGwADqO.d.mts.map +1 -0
  137. package/dist/{validation-BfEI7tNe.mjs → validation-B1NYiEos.mjs} +5 -5
  138. package/dist/{validation-BfEI7tNe.mjs.map → validation-B1NYiEos.mjs.map} +1 -1
  139. package/dist/version-CMD42IRC.mjs +7 -0
  140. package/dist/{version-DoxrVdYf.mjs.map → version-CMD42IRC.mjs.map} +1 -1
  141. package/dist/{zod-generator-CC0xNe_K.mjs → zod-generator-BNJDQBSZ.mjs} +8 -3
  142. package/dist/zod-generator-BNJDQBSZ.mjs.map +1 -0
  143. package/package.json +6 -6
  144. package/src/api/handlers/content.ts +11 -0
  145. package/src/api/handlers/dashboard.ts +29 -36
  146. package/src/api/handlers/menus.ts +256 -75
  147. package/src/api/handlers/taxonomies.ts +273 -97
  148. package/src/api/schemas/common.ts +7 -0
  149. package/src/api/schemas/menus.ts +23 -0
  150. package/src/api/schemas/taxonomies.ts +39 -0
  151. package/src/astro/integration/routes.ts +10 -0
  152. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +1 -1
  153. package/src/astro/routes/api/import/wordpress/rewrite-url-helpers.ts +196 -0
  154. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +9 -177
  155. package/src/astro/routes/api/menus/[name]/items.ts +16 -6
  156. package/src/astro/routes/api/menus/[name]/reorder.ts +8 -3
  157. package/src/astro/routes/api/menus/[name]/translations.ts +82 -0
  158. package/src/astro/routes/api/menus/[name].ts +19 -10
  159. package/src/astro/routes/api/menus/index.ts +9 -6
  160. package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +89 -0
  161. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +22 -22
  162. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +11 -14
  163. package/src/astro/routes/api/taxonomies/index.ts +9 -6
  164. package/src/cli/commands/export-seed.ts +82 -21
  165. package/src/cli/commands/plugin-init.ts +216 -90
  166. package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +477 -0
  167. package/src/database/migrations/runner.ts +2 -0
  168. package/src/database/repositories/content.ts +11 -0
  169. package/src/database/repositories/taxonomy.ts +193 -89
  170. package/src/database/types.ts +10 -2
  171. package/src/i18n/resolve.ts +37 -0
  172. package/src/loader.ts +49 -2
  173. package/src/mcp/server.ts +77 -18
  174. package/src/menus/index.ts +143 -124
  175. package/src/menus/types.ts +15 -1
  176. package/src/schema/zod-generator.ts +12 -2
  177. package/src/seed/apply.ts +140 -54
  178. package/src/seed/types.ts +14 -1
  179. package/src/seed/validate.ts +27 -13
  180. package/src/taxonomies/index.ts +230 -213
  181. package/src/taxonomies/types.ts +10 -0
  182. package/dist/apply-BzltprvY.mjs.map +0 -1
  183. package/dist/content-8lOYF0pr.mjs.map +0 -1
  184. package/dist/index-BFRaVcD6.d.mts.map +0 -1
  185. package/dist/loader-CKLbBnhK.mjs.map +0 -1
  186. package/dist/runner-C7ADox5q.mjs.map +0 -1
  187. package/dist/search-dOGEccMa.mjs.map +0 -1
  188. package/dist/taxonomies-ZlRtD6AG.mjs +0 -315
  189. package/dist/taxonomies-ZlRtD6AG.mjs.map +0 -1
  190. package/dist/types-BuBIptGk.d.mts.map +0 -1
  191. package/dist/types-CrtWgIvl.d.mts.map +0 -1
  192. package/dist/validate-Baqf0slj.mjs.map +0 -1
  193. package/dist/validate-BfQh_C_y.d.mts.map +0 -1
  194. package/dist/version-DoxrVdYf.mjs +0 -7
  195. package/dist/zod-generator-CC0xNe_K.mjs.map +0 -1
@@ -1,25 +1,26 @@
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
- import { a as slugify, r as RevisionRepository, t as ContentRepository } from "./content-8lOYF0pr.mjs";
4
- import { r as encodeBase64, t as decodeBase64 } from "./base64-BRICGH2l.mjs";
5
- import { i as encodeCursor, n as InvalidCursorError, r as decodeCursor, t as EmDashValidationError } from "./types-CRxNbK-Z.mjs";
6
- import { t as MediaRepository } from "./media-BW32b4gi.mjs";
7
- import { a as ssrfSafeFetch, i as resolveAndValidateExternalUrl, o as stripCredentialHeaders, r as SsrfError, s as validateExternalUrl } from "./apply-BzltprvY.mjs";
8
- import { t as OptionsRepository } from "./options-BVp3UsTS.mjs";
9
- import { t as withTransaction } from "./transaction-Cn2rjY78.mjs";
10
- import { t as RedirectRepository } from "./redirect-BhUBKRc1.mjs";
11
- import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-NBQVDOci.mjs";
12
- import { t as BylineRepository } from "./byline-BSaNL1w7.mjs";
13
- import { r as isI18nEnabled } from "./config-BI0V3ICQ.mjs";
14
- import { r as invalidateRedirectCache } from "./cache-C6N_hhN7.mjs";
15
- import { t as isMissingTableError } from "./db-errors-WRezodiz.mjs";
16
- import { r as hashString } from "./zod-generator-CC0xNe_K.mjs";
17
- import { i as FTSManager, n as SchemaRegistry } from "./registry-Dw70ChxB.mjs";
18
- import { r as getDb } from "./loader-CKLbBnhK.mjs";
19
- import { n as requestCached } from "./request-cache-B-bmkipQ.mjs";
20
- import { i as pluginManifestSchema } from "./manifest-schema-DqWNC3lM.mjs";
21
- import { i as normalizeCapabilities } from "./types-4fVtCIm0.mjs";
22
- import { t as generatePreviewToken } from "./tokens-D7zMmWi2.mjs";
3
+ import { r as isI18nEnabled } from "./config-CVssduLe.mjs";
4
+ import { a as slugify, r as RevisionRepository, t as ContentRepository } from "./content-C7G4QXkK.mjs";
5
+ import { r as encodeBase64, t as decodeBase64 } from "./base64-MBPo9ozB.mjs";
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";
8
+ import { t as OptionsRepository } from "./options-nPxWnrya.mjs";
9
+ import { t as withTransaction } from "./transaction-D44LBXvU.mjs";
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";
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";
21
+ import { i as pluginManifestSchema } from "./manifest-schema-CXAbd1vH.mjs";
22
+ import { i as normalizeCapabilities } from "./types-CoO6mpV3.mjs";
23
+ import { t as generatePreviewToken } from "./tokens-CyRDPVW2.mjs";
23
24
  import { sql } from "kysely";
24
25
  import { AsyncLocalStorage } from "node:async_hooks";
25
26
  import { ulid } from "ulidx";
@@ -1278,6 +1279,10 @@ async function handleContentCreate(db, collection, body) {
1278
1279
  created.primaryBylineId = body.bylines[0]?.bylineId ?? null;
1279
1280
  }
1280
1281
  await hydrateBylines(trx, collection, created);
1282
+ if (body.translationOf) {
1283
+ const { TaxonomyRepository } = await import("./taxonomy-DSxx2K2L.mjs").then((n) => n.n);
1284
+ await new TaxonomyRepository(trx).copyEntryTerms(collection, body.translationOf, created.id);
1285
+ }
1281
1286
  if (body.seo && hasSeo) created.seo = await new SeoRepository(trx).upsert(collection, created.id, body.seo);
1282
1287
  else if (hasSeo) created.seo = { ...SEO_DEFAULTS };
1283
1288
  return created;
@@ -2659,6 +2664,8 @@ const HTTP_SCHEME_RE = /^https?:\/\//i;
2659
2664
  const httpUrl = z$1.string().url().refine((url) => HTTP_SCHEME_RE.test(url), "URL must use http or https");
2660
2665
  /** BCP 47 locale code — language with optional script/region subtags (e.g. en, en-US, pt-BR, es-419, zh-Hant) */
2661
2666
  const localeCode = z$1.string().regex(/^[a-z]{2,3}(-[a-z0-9]{2,8})*$/i, "Invalid locale code").transform((v) => v.toLowerCase());
2667
+ /** Shared `?locale=xx` query shape for endpoints that filter by locale. */
2668
+ const localeFilterQuery = z$1.object({ locale: z$1.string().min(1).optional() }).meta({ id: "LocaleFilterQuery" });
2662
2669
  /** Standard API error response */
2663
2670
  const apiErrorSchema = z$1.object({ error: z$1.object({
2664
2671
  code: z$1.string().meta({
@@ -3292,7 +3299,9 @@ const menuItemType = z$1.string().min(1);
3292
3299
  const safeHref = z$1.string().trim().refine(isSafeHref, "URL must use http, https, mailto, tel, a relative path, or a fragment identifier");
3293
3300
  const createMenuBody = z$1.object({
3294
3301
  name: z$1.string().min(1),
3295
- label: z$1.string().min(1)
3302
+ label: z$1.string().min(1),
3303
+ locale: z$1.string().min(1).optional(),
3304
+ translationOf: z$1.string().min(1).optional()
3296
3305
  }).meta({ id: "CreateMenuBody" });
3297
3306
  const updateMenuBody = z$1.object({ label: z$1.string().min(1).optional() }).meta({ id: "UpdateMenuBody" });
3298
3307
  const createMenuItemBody = z$1.object({
@@ -3328,7 +3337,9 @@ const menuSchema = z$1.object({
3328
3337
  name: z$1.string(),
3329
3338
  label: z$1.string(),
3330
3339
  created_at: z$1.string(),
3331
- updated_at: z$1.string()
3340
+ updated_at: z$1.string(),
3341
+ locale: z$1.string(),
3342
+ translation_group: z$1.string().nullable()
3332
3343
  }).meta({ id: "Menu" });
3333
3344
  const menuItemSchema = z$1.object({
3334
3345
  id: z$1.string(),
@@ -3343,8 +3354,20 @@ const menuItemSchema = z$1.object({
3343
3354
  title_attr: z$1.string().nullable(),
3344
3355
  target: z$1.string().nullable(),
3345
3356
  css_classes: z$1.string().nullable(),
3346
- created_at: z$1.string()
3357
+ created_at: z$1.string(),
3358
+ locale: z$1.string(),
3359
+ translation_group: z$1.string().nullable()
3347
3360
  }).meta({ id: "MenuItem" });
3361
+ const menuTranslationsSchema = z$1.object({
3362
+ translationGroup: z$1.string().nullable(),
3363
+ translations: z$1.array(z$1.object({
3364
+ id: z$1.string(),
3365
+ name: z$1.string(),
3366
+ label: z$1.string(),
3367
+ locale: z$1.string(),
3368
+ updatedAt: z$1.string()
3369
+ }))
3370
+ }).meta({ id: "MenuTranslations" });
3348
3371
  const menuListItemSchema = menuSchema.extend({ itemCount: z$1.number().int() }).meta({ id: "MenuListItem" });
3349
3372
  const menuWithItemsSchema = menuSchema.extend({ items: z$1.array(menuItemSchema) }).meta({ id: "MenuWithItems" });
3350
3373
 
@@ -3355,14 +3378,19 @@ const collectionSlugPattern = /^[a-z][a-z0-9_]*$/;
3355
3378
  const createTaxonomyDefBody = z$1.object({
3356
3379
  name: z$1.string().min(1).max(63).regex(/^[a-z][a-z0-9_]*$/, "Name must be lowercase alphanumeric with underscores"),
3357
3380
  label: z$1.string().min(1).max(200),
3381
+ labelSingular: z$1.string().min(1).max(200).optional(),
3358
3382
  hierarchical: z$1.boolean().optional().default(false),
3359
- collections: z$1.array(z$1.string().min(1).max(63).regex(collectionSlugPattern, "Invalid collection slug format")).max(100).optional().default([])
3383
+ collections: z$1.array(z$1.string().min(1).max(63).regex(collectionSlugPattern, "Invalid collection slug format")).max(100).optional().default([]),
3384
+ locale: z$1.string().min(1).optional(),
3385
+ translationOf: z$1.string().min(1).optional()
3360
3386
  }).meta({ id: "CreateTaxonomyDefBody" });
3361
3387
  const createTermBody = z$1.object({
3362
3388
  slug: z$1.string().min(1),
3363
3389
  label: z$1.string().min(1),
3364
3390
  parentId: z$1.string().nullish(),
3365
- description: z$1.string().optional()
3391
+ description: z$1.string().optional(),
3392
+ locale: z$1.string().min(1).optional(),
3393
+ translationOf: z$1.string().min(1).optional()
3366
3394
  }).meta({ id: "CreateTermBody" });
3367
3395
  const updateTermBody = z$1.object({
3368
3396
  slug: z$1.string().min(1).optional(),
@@ -3376,8 +3404,19 @@ const taxonomyDefSchema = z$1.object({
3376
3404
  label: z$1.string(),
3377
3405
  labelSingular: z$1.string().optional(),
3378
3406
  hierarchical: z$1.boolean(),
3379
- collections: z$1.array(z$1.string())
3407
+ collections: z$1.array(z$1.string()),
3408
+ locale: z$1.string(),
3409
+ translationGroup: z$1.string().nullable()
3380
3410
  }).meta({ id: "TaxonomyDef" });
3411
+ const taxonomyDefTranslationsSchema = z$1.object({
3412
+ translationGroup: z$1.string().nullable(),
3413
+ translations: z$1.array(z$1.object({
3414
+ id: z$1.string(),
3415
+ name: z$1.string(),
3416
+ label: z$1.string(),
3417
+ locale: z$1.string()
3418
+ }))
3419
+ }).meta({ id: "TaxonomyDefTranslations" });
3381
3420
  const taxonomyListResponseSchema = z$1.object({ taxonomies: z$1.array(taxonomyDefSchema) }).meta({ id: "TaxonomyListResponse" });
3382
3421
  const termSchema = z$1.object({
3383
3422
  id: z$1.string(),
@@ -3385,8 +3424,19 @@ const termSchema = z$1.object({
3385
3424
  slug: z$1.string(),
3386
3425
  label: z$1.string(),
3387
3426
  parentId: z$1.string().nullable(),
3388
- description: z$1.string().optional()
3427
+ description: z$1.string().optional(),
3428
+ locale: z$1.string(),
3429
+ translationGroup: z$1.string().nullable()
3389
3430
  }).meta({ id: "Term" });
3431
+ const termTranslationsSchema = z$1.object({
3432
+ translationGroup: z$1.string().nullable(),
3433
+ translations: z$1.array(z$1.object({
3434
+ id: z$1.string(),
3435
+ slug: z$1.string(),
3436
+ label: z$1.string(),
3437
+ locale: z$1.string()
3438
+ }))
3439
+ }).meta({ id: "TermTranslations" });
3390
3440
  const termWithCountSchema = z$1.object({
3391
3441
  id: z$1.string(),
3392
3442
  name: z$1.string(),
@@ -3395,7 +3445,9 @@ const termWithCountSchema = z$1.object({
3395
3445
  parentId: z$1.string().nullable(),
3396
3446
  description: z$1.string().optional(),
3397
3447
  count: z$1.number().int(),
3398
- children: z$1.array(z$1.lazy(() => termWithCountSchema))
3448
+ children: z$1.array(z$1.lazy(() => termWithCountSchema)),
3449
+ locale: z$1.string(),
3450
+ translationGroup: z$1.string().nullable()
3399
3451
  }).meta({ id: "TermWithCount" });
3400
3452
  const termListResponseSchema = z$1.object({ terms: z$1.array(termWithCountSchema) }).meta({ id: "TermListResponse" });
3401
3453
  const termResponseSchema = z$1.object({ term: termSchema }).meta({ id: "TermResponse" });
@@ -8926,53 +8978,53 @@ async function getCommentCountWithDb(db, collection, contentId) {
8926
8978
  //#endregion
8927
8979
  //#region src/menus/index.ts
8928
8980
  /**
8929
- * Get menu by name with resolved URLs
8981
+ * Get a menu by name with resolved URLs.
8930
8982
  *
8931
8983
  * @example
8932
8984
  * ```ts
8933
- * import { getMenu } from "emdash";
8934
- *
8935
8985
  * const menu = await getMenu("primary");
8936
- * if (menu) {
8937
- * console.log(menu.items); // Array of MenuItem with resolved URLs
8938
- * }
8986
+ * const menuEs = await getMenu("primary", { locale: "es" });
8939
8987
  * ```
8940
8988
  */
8941
- function getMenu(name) {
8942
- return requestCached(`menu:${name}`, async () => {
8943
- return getMenuWithDb(name, await getDb());
8989
+ function getMenu(name, options = {}) {
8990
+ const locale = resolveLocale(options.locale);
8991
+ return requestCached(`menu:${name}:${locale ?? "*"}`, async () => {
8992
+ return getMenuWithDb(name, await getDb(), { locale });
8944
8993
  });
8945
8994
  }
8946
8995
  /**
8947
- * Get menu by name with resolved URLs (with explicit db)
8948
- *
8949
- * @internal Use `getMenu()` in templates. This variant is for admin routes
8950
- * that already have a database handle.
8996
+ * Get menu by name with resolved URLs (with explicit db). Internal helper for
8997
+ * admin routes that already have a database handle.
8951
8998
  */
8952
- async function getMenuWithDb(name, db) {
8953
- const menuRow = await db.selectFrom("_emdash_menus").selectAll().where("name", "=", name).executeTakeFirst();
8999
+ async function getMenuWithDb(name, db, options = {}) {
9000
+ const chain = resolveLocaleChain(options.locale);
9001
+ const selectMenu = () => db.selectFrom("_emdash_menus").selectAll().where("name", "=", name);
9002
+ let menuRow;
9003
+ if (chain.length === 0) menuRow = await selectMenu().orderBy("locale", "asc").executeTakeFirst();
9004
+ else {
9005
+ menuRow = void 0;
9006
+ for (const locale of chain) {
9007
+ menuRow = await selectMenu().where("locale", "=", locale).executeTakeFirst();
9008
+ if (menuRow) break;
9009
+ }
9010
+ }
8954
9011
  if (!menuRow) return null;
8955
- const items = await buildMenuTree(await db.selectFrom("_emdash_menu_items").selectAll().$castTo().where("menu_id", "=", menuRow.id).orderBy("sort_order", "asc").execute(), db);
9012
+ const items = await buildMenuTree(await db.selectFrom("_emdash_menu_items").selectAll().$castTo().where("menu_id", "=", menuRow.id).orderBy("sort_order", "asc").execute(), db, menuRow.locale);
8956
9013
  return {
8957
9014
  id: menuRow.id,
8958
9015
  name: menuRow.name,
8959
9016
  label: menuRow.label,
8960
- items
9017
+ items,
9018
+ locale: menuRow.locale,
9019
+ translationGroup: menuRow.translation_group
8961
9020
  };
8962
9021
  }
8963
9022
  /**
8964
- * Get all menus (without items - for admin list)
8965
- *
8966
- * @example
8967
- * ```ts
8968
- * import { getMenus } from "emdash";
8969
- *
8970
- * const menus = await getMenus();
8971
- * console.log(menus); // [{ id, name, label }]
8972
- * ```
9023
+ * Get all menus (without items, locale-filtered for admin list / site nav
9024
+ * summaries). When no locale is configured, returns menus across all locales.
8973
9025
  */
8974
- async function getMenus() {
8975
- return getMenusWithDb(await getDb());
9026
+ async function getMenus(options = {}) {
9027
+ return getMenusWithDb(await getDb(), options);
8976
9028
  }
8977
9029
  /**
8978
9030
  * Get all menus (with explicit db)
@@ -8980,17 +9032,23 @@ async function getMenus() {
8980
9032
  * @internal Use `getMenus()` in templates. This variant is for admin routes
8981
9033
  * that already have a database handle.
8982
9034
  */
8983
- async function getMenusWithDb(db) {
8984
- return await db.selectFrom("_emdash_menus").select([
9035
+ async function getMenusWithDb(db, options = {}) {
9036
+ const locale = resolveLocale(options.locale);
9037
+ let query = db.selectFrom("_emdash_menus").select([
8985
9038
  "id",
8986
9039
  "name",
8987
- "label"
8988
- ]).orderBy("name", "asc").execute();
9040
+ "label",
9041
+ "locale"
9042
+ ]).orderBy("name", "asc");
9043
+ if (locale !== void 0) query = query.where("locale", "=", locale);
9044
+ return query.execute();
8989
9045
  }
8990
9046
  /**
8991
- * Build hierarchical menu tree from flat array of items
9047
+ * Build a hierarchical menu tree from a flat list of items. Items are
9048
+ * resolved against the given `locale` so references land on the right
9049
+ * per-locale content rows.
8992
9050
  */
8993
- async function buildMenuTree(items, db) {
9051
+ async function buildMenuTree(items, db, locale) {
8994
9052
  const collectionSlugs = /* @__PURE__ */ new Set();
8995
9053
  for (const item of items) {
8996
9054
  if (item.reference_collection) collectionSlugs.add(item.reference_collection);
@@ -9001,7 +9059,7 @@ async function buildMenuTree(items, db) {
9001
9059
  const rows = await db.selectFrom("_emdash_collections").select(["slug", "url_pattern"]).where("slug", "in", [...collectionSlugs]).execute();
9002
9060
  for (const row of rows) urlPatterns.set(row.slug, row.url_pattern);
9003
9061
  }
9004
- const validItems = (await Promise.all(items.map((item) => resolveMenuItem(item, db, urlPatterns)))).filter((item) => item !== null);
9062
+ const validItems = (await Promise.all(items.map((item) => resolveMenuItem(item, db, urlPatterns, locale)))).filter((item) => item !== null);
9005
9063
  const itemMap = /* @__PURE__ */ new Map();
9006
9064
  const rootItems = [];
9007
9065
  for (const item of validItems) itemMap.set(item.id, {
@@ -9020,11 +9078,11 @@ async function buildMenuTree(items, db) {
9020
9078
  return rootItems;
9021
9079
  }
9022
9080
  /**
9023
- * Resolve a single menu item's URL
9024
- *
9025
- * Returns null if the referenced content no longer exists (item should be skipped)
9081
+ * Resolve a single menu item's URL. `reference_id` is a translation_group
9082
+ * (migration 036 remapped all existing references); we join it against
9083
+ * the per-locale ec_* row or per-locale taxonomy row.
9026
9084
  */
9027
- async function resolveMenuItem(item, db, urlPatterns) {
9085
+ async function resolveMenuItem(item, db, urlPatterns, locale) {
9028
9086
  let url;
9029
9087
  try {
9030
9088
  switch (item.type) {
@@ -9033,18 +9091,18 @@ async function resolveMenuItem(item, db, urlPatterns) {
9033
9091
  break;
9034
9092
  case "page":
9035
9093
  case "post":
9036
- url = await resolveContentUrl(item.reference_collection || `${item.type}s`, item.reference_id, db, urlPatterns);
9094
+ url = await resolveContentUrl(item.reference_collection || `${item.type}s`, item.reference_id, db, urlPatterns, locale);
9037
9095
  if (url === null) return null;
9038
9096
  break;
9039
9097
  case "taxonomy":
9040
- url = await resolveTaxonomyUrl(item.reference_id, db);
9098
+ url = await resolveTaxonomyUrl(item.reference_id, db, locale);
9041
9099
  if (url === null) return null;
9042
9100
  break;
9043
9101
  case "collection":
9044
9102
  url = `/${item.reference_collection}/`;
9045
9103
  break;
9046
9104
  default: if (item.reference_collection && item.reference_id) {
9047
- url = await resolveContentUrl(item.reference_collection, item.reference_id, db, urlPatterns);
9105
+ url = await resolveContentUrl(item.reference_collection, item.reference_id, db, urlPatterns, locale);
9048
9106
  if (url === null) return null;
9049
9107
  } else url = "#";
9050
9108
  }
@@ -9073,37 +9131,51 @@ function interpolateUrlPattern(pattern, slug, id) {
9073
9131
  return pattern.replace(SLUG_PLACEHOLDER, slug).replace(ID_PLACEHOLDER, id);
9074
9132
  }
9075
9133
  /**
9076
- * Resolve URL for a content entry (page/post)
9077
- *
9078
- * Uses the collection's url_pattern if set, otherwise falls back to /{collection}/{slug}.
9079
- * Returns null if content not found (item should be skipped).
9134
+ * Resolve the URL for a content reference. `referenceGroup` is the content
9135
+ * row's translation_group; we look up the row in the requested locale
9136
+ * (falling back to the source if no translation exists so the menu link is
9137
+ * still clickable).
9080
9138
  */
9081
- async function resolveContentUrl(collection, entryId, db, urlPatterns) {
9082
- if (!entryId) return null;
9139
+ async function resolveContentUrl(collection, referenceGroup, db, urlPatterns, locale) {
9140
+ if (!referenceGroup) return null;
9083
9141
  try {
9084
9142
  validateIdentifier(collection, "menu item collection");
9085
- const row = (await sql`
9086
- SELECT slug FROM ${sql.ref(`ec_${collection}`)} WHERE id = ${entryId} LIMIT 1
9087
- `.execute(db)).rows[0];
9088
- if (row) {
9089
- const pattern = urlPatterns.get(collection);
9090
- if (pattern) return interpolateUrlPattern(pattern, row.slug, entryId);
9091
- return `/${collection}/${row.slug}`;
9143
+ let result = await sql`
9144
+ SELECT id, slug FROM ${sql.ref(`ec_${collection}`)}
9145
+ WHERE translation_group = ${referenceGroup} AND locale = ${locale}
9146
+ LIMIT 1
9147
+ `.execute(db);
9148
+ let row = result.rows[0];
9149
+ if (!row) {
9150
+ result = await sql`
9151
+ SELECT id, slug FROM ${sql.ref(`ec_${collection}`)}
9152
+ WHERE translation_group = ${referenceGroup}
9153
+ ORDER BY locale ASC LIMIT 1
9154
+ `.execute(db);
9155
+ row = result.rows[0];
9092
9156
  }
9093
- return null;
9157
+ if (!row) row = (await sql`
9158
+ SELECT id, slug FROM ${sql.ref(`ec_${collection}`)}
9159
+ WHERE id = ${referenceGroup} LIMIT 1
9160
+ `.execute(db)).rows[0];
9161
+ if (!row) return null;
9162
+ const pattern = urlPatterns.get(collection);
9163
+ if (pattern) return interpolateUrlPattern(pattern, row.slug, row.id);
9164
+ return `/${collection}/${row.slug}`;
9094
9165
  } catch (error) {
9095
- console.error(`Failed to resolve content URL for ${collection}/${entryId}:`, error);
9166
+ console.error(`Failed to resolve content URL for ${collection}/${referenceGroup}:`, error);
9096
9167
  return null;
9097
9168
  }
9098
9169
  }
9099
9170
  /**
9100
- * Resolve URL for a taxonomy term
9101
- *
9102
- * Returns null if taxonomy not found (item should be skipped)
9171
+ * Resolve URL for a taxonomy term reference. `referenceGroup` is the term's
9172
+ * translation_group; we pick the row in the active locale (or fall back).
9103
9173
  */
9104
- async function resolveTaxonomyUrl(taxonomyId, db) {
9105
- if (!taxonomyId) return null;
9106
- const taxonomy = await db.selectFrom("taxonomies").select(["name", "slug"]).where("id", "=", taxonomyId).executeTakeFirst();
9174
+ async function resolveTaxonomyUrl(referenceGroup, db, locale) {
9175
+ if (!referenceGroup) return null;
9176
+ let taxonomy = await db.selectFrom("taxonomies").select(["name", "slug"]).where("translation_group", "=", referenceGroup).where("locale", "=", locale).executeTakeFirst();
9177
+ if (!taxonomy) taxonomy = await db.selectFrom("taxonomies").select(["name", "slug"]).where("translation_group", "=", referenceGroup).orderBy("locale", "asc").executeTakeFirst();
9178
+ if (!taxonomy) taxonomy = await db.selectFrom("taxonomies").select(["name", "slug"]).where("id", "=", referenceGroup).executeTakeFirst();
9107
9179
  if (!taxonomy) return null;
9108
9180
  return `/${taxonomy.name}/${taxonomy.slug}`;
9109
9181
  }
@@ -9680,4 +9752,4 @@ function extractSearchableFields(entry, fields) {
9680
9752
 
9681
9753
  //#endregion
9682
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 };
9683
- //# sourceMappingURL=search-dOGEccMa.mjs.map
9755
+ //# sourceMappingURL=search-DkN-BqsS.mjs.map