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
  #!/usr/bin/env node
2
- import { i as __exportAll, r as runMigrations, t as getMigrationStatus } from "../runner-C7ADox5q.mjs";
2
+ import { i as __exportAll, r as runMigrations, t as getMigrationStatus } from "../runner-DMnlIkh4.mjs";
3
3
  import { n as createDatabase } from "../connection-2igzM-AT.mjs";
4
4
  import { c as listTablesLike } from "../dialect-helpers-BKCvISIQ.mjs";
5
- import { t as ContentRepository } from "../content-8lOYF0pr.mjs";
6
- import { i as encodeBase64url } from "../base64-BRICGH2l.mjs";
7
- import "../types-CRxNbK-Z.mjs";
8
- import { t as MediaRepository } from "../media-BW32b4gi.mjs";
9
- import { p as TaxonomyRepository, t as applySeed } from "../apply-BzltprvY.mjs";
10
- import { t as OptionsRepository } from "../options-BVp3UsTS.mjs";
11
- import "../redirect-BhUBKRc1.mjs";
12
- import "../byline-BSaNL1w7.mjs";
13
- import { r as isI18nEnabled } from "../config-BI0V3ICQ.mjs";
14
- import { n as SchemaRegistry } from "../registry-Dw70ChxB.mjs";
15
- import "../loader-CKLbBnhK.mjs";
16
- import "../request-cache-B-bmkipQ.mjs";
17
- import { i as pluginManifestSchema } from "../manifest-schema-DqWNC3lM.mjs";
18
- import { n as isDeprecatedCapability, t as CAPABILITY_RENAMES } from "../types-4fVtCIm0.mjs";
19
- import { t as validateSeed } from "../validate-Baqf0slj.mjs";
20
- import { n as fingerprintKey, r as generateEncryptionKey, t as EmDashSecretsError } from "../secrets-CW3reAnU.mjs";
5
+ import { r as isI18nEnabled } from "../config-CVssduLe.mjs";
6
+ import { t as ContentRepository } from "../content-C7G4QXkK.mjs";
7
+ import { i as encodeBase64url } from "../base64-MBPo9ozB.mjs";
8
+ import "../types-BIgulNsW.mjs";
9
+ import { t as MediaRepository } from "../media-D8FbNsl0.mjs";
10
+ import { t as TaxonomyRepository } from "../taxonomy-DSxx2K2L.mjs";
11
+ import { t as OptionsRepository } from "../options-nPxWnrya.mjs";
12
+ import "../redirect-C5H7VGIX.mjs";
13
+ import "../byline-C3vnhIpU.mjs";
14
+ import { n as SchemaRegistry } from "../registry-Beb7wxFc.mjs";
15
+ import "../loader-Bx2_9-5e.mjs";
16
+ import "../request-cache-C-tIpYIw.mjs";
17
+ import { t as applySeed } from "../apply-UsrFuO7l.mjs";
18
+ import { i as pluginManifestSchema } from "../manifest-schema-CXAbd1vH.mjs";
19
+ import { n as isDeprecatedCapability, t as CAPABILITY_RENAMES } from "../types-CoO6mpV3.mjs";
20
+ import { t as validateSeed } from "../validate-CBIbxM3L.mjs";
21
+ import { n as fingerprintKey, r as generateEncryptionKey, t as EmDashSecretsError } from "../secrets-CZ8rxLX3.mjs";
21
22
  import { LocalStorage } from "../storage/local.mjs";
22
- import { o as convertDataForRead } from "../transport-BeMCmin1.mjs";
23
+ import { o as convertDataForRead } from "../transport-xpzIjCIB.mjs";
23
24
  import { createHeaderAwareFetch, customHeadersInterceptor, getCachedAccessToken, isAccessRedirect, resolveCustomHeaders, runCloudflaredLogin } from "../client/cf-access.mjs";
24
25
  import { EmDashClient } from "../client/index.mjs";
25
26
  import { imageSize } from "image-size";
@@ -1248,49 +1249,88 @@ async function exportCollections(db) {
1248
1249
  * Export taxonomy definitions and terms
1249
1250
  */
1250
1251
  async function exportTaxonomies(db) {
1251
- const defs = await db.selectFrom("_emdash_taxonomy_defs").selectAll().execute();
1252
+ const i18nEnabled = isI18nEnabled();
1253
+ const defs = await db.selectFrom("_emdash_taxonomy_defs").selectAll().orderBy(["name", "locale"]).execute();
1252
1254
  const result = [];
1253
1255
  const termRepo = new TaxonomyRepository(db);
1256
+ const defGroupToSeedId = /* @__PURE__ */ new Map();
1254
1257
  for (const def of defs) {
1255
- const terms = await termRepo.findByName(def.name);
1256
- const seedTerms = [];
1258
+ const defSeedId = i18nEnabled && def.locale ? `tax:${def.name}:${def.locale}` : `tax:${def.name}`;
1259
+ const terms = await termRepo.findByName(def.name, { locale: def.locale });
1257
1260
  const idToSlug = /* @__PURE__ */ new Map();
1258
1261
  for (const term of terms) idToSlug.set(term.id, term.slug);
1262
+ const termGroupToSeedId = /* @__PURE__ */ new Map();
1263
+ const seedTerms = [];
1259
1264
  for (const term of terms) {
1265
+ const termSeedId = i18nEnabled && term.locale ? `term:${def.name}:${term.slug}:${term.locale}` : `term:${def.name}:${term.slug}`;
1260
1266
  const seedTerm = {
1267
+ id: termSeedId,
1261
1268
  slug: term.slug,
1262
1269
  label: term.label,
1263
1270
  description: typeof term.data?.description === "string" ? term.data.description : void 0
1264
1271
  };
1265
1272
  if (term.parentId) seedTerm.parent = idToSlug.get(term.parentId);
1273
+ if (i18nEnabled && term.locale) {
1274
+ seedTerm.locale = term.locale;
1275
+ if (term.translationGroup) {
1276
+ const anchor = termGroupToSeedId.get(term.translationGroup);
1277
+ if (anchor) seedTerm.translationOf = anchor;
1278
+ else termGroupToSeedId.set(term.translationGroup, termSeedId);
1279
+ }
1280
+ }
1266
1281
  seedTerms.push(seedTerm);
1267
1282
  }
1283
+ seedTerms.sort((a, b) => Number(!!a.translationOf) - Number(!!b.translationOf));
1268
1284
  const taxonomy = {
1285
+ id: defSeedId,
1269
1286
  name: def.name,
1270
1287
  label: def.label,
1271
1288
  labelSingular: def.label_singular || void 0,
1272
1289
  hierarchical: def.hierarchical === 1,
1273
1290
  collections: def.collections ? JSON.parse(def.collections) : []
1274
1291
  };
1292
+ if (i18nEnabled && def.locale) {
1293
+ taxonomy.locale = def.locale;
1294
+ if (def.translation_group) {
1295
+ const anchor = defGroupToSeedId.get(def.translation_group);
1296
+ if (anchor) taxonomy.translationOf = anchor;
1297
+ else defGroupToSeedId.set(def.translation_group, defSeedId);
1298
+ }
1299
+ }
1275
1300
  if (seedTerms.length > 0) taxonomy.terms = seedTerms;
1276
1301
  result.push(taxonomy);
1277
1302
  }
1303
+ result.sort((a, b) => Number(!!a.translationOf) - Number(!!b.translationOf));
1278
1304
  return result;
1279
1305
  }
1280
1306
  /**
1281
1307
  * Export menus with their items
1282
1308
  */
1283
1309
  async function exportMenus(db) {
1284
- const menus = await db.selectFrom("_emdash_menus").selectAll().execute();
1310
+ const i18nEnabled = isI18nEnabled();
1311
+ const menus = await db.selectFrom("_emdash_menus").selectAll().orderBy(["name", "locale"]).execute();
1285
1312
  const result = [];
1313
+ const groupToSeedId = /* @__PURE__ */ new Map();
1286
1314
  for (const menu of menus) {
1315
+ const seedId = i18nEnabled && menu.locale ? `menu:${menu.name}:${menu.locale}` : `menu:${menu.name}`;
1287
1316
  const seedItems = buildMenuItemTree(await db.selectFrom("_emdash_menu_items").selectAll().where("menu_id", "=", menu.id).orderBy("sort_order", "asc").execute());
1288
- result.push({
1317
+ const seedMenu = {
1318
+ id: seedId,
1289
1319
  name: menu.name,
1290
1320
  label: menu.label,
1291
1321
  items: seedItems
1292
- });
1322
+ };
1323
+ if (i18nEnabled && menu.locale) {
1324
+ seedMenu.locale = menu.locale;
1325
+ if (menu.translation_group) {
1326
+ const anchor = groupToSeedId.get(menu.translation_group);
1327
+ if (anchor) seedMenu.translationOf = anchor;
1328
+ else groupToSeedId.set(menu.translation_group, seedId);
1329
+ }
1330
+ }
1331
+ result.push(seedMenu);
1293
1332
  }
1333
+ result.sort((a, b) => Number(!!a.translationOf) - Number(!!b.translationOf));
1294
1334
  return result;
1295
1335
  }
1296
1336
  /** Type guard for valid widget types */
@@ -2726,13 +2766,15 @@ const bundleCommand = defineCommand({
2726
2766
  /**
2727
2767
  * emdash plugin init
2728
2768
  *
2729
- * Scaffold a new EmDash plugin. Generates the standard-format boilerplate:
2769
+ * Scaffold a new EmDash plugin. Generates the sandboxed-format boilerplate:
2730
2770
  * src/index.ts -- descriptor factory
2731
2771
  * src/sandbox-entry.ts -- definePlugin({ hooks, routes })
2732
2772
  * package.json
2733
2773
  * tsconfig.json
2734
2774
  *
2735
- * Use --native to generate native-format boilerplate instead (createPlugin + React admin).
2775
+ * Use --format=native (or --native) to generate native-format boilerplate
2776
+ * instead (createPlugin + React admin). When neither is passed and stdout
2777
+ * is a TTY, the user is prompted to choose.
2736
2778
  *
2737
2779
  */
2738
2780
  const SLUG_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
@@ -2752,15 +2794,25 @@ const pluginInitCommand = defineCommand({
2752
2794
  type: "string",
2753
2795
  description: "Plugin name/id (e.g. my-plugin or @org/my-plugin)"
2754
2796
  },
2797
+ format: {
2798
+ type: "string",
2799
+ description: "Plugin format: sandboxed or native. Prompts when running interactively if not set.",
2800
+ valueHint: "sandboxed|native"
2801
+ },
2755
2802
  native: {
2756
2803
  type: "boolean",
2757
- description: "Generate native-format plugin (createPlugin + React admin)",
2804
+ description: "Shortcut for --format=native",
2758
2805
  default: false
2759
2806
  }
2760
2807
  },
2761
2808
  async run({ args }) {
2762
2809
  const targetDir = resolve(args.dir);
2763
- const isNative = args.native;
2810
+ const format = await resolveFormat(args.format, args.native);
2811
+ if (!format) {
2812
+ consola.info("Cancelled");
2813
+ return;
2814
+ }
2815
+ const isNative = format === "native";
2764
2816
  let pluginName = args.name || basename(targetDir);
2765
2817
  if (!pluginName || pluginName === ".") pluginName = basename(resolve("."));
2766
2818
  const slug = pluginName.replace(SCOPE_RE, "");
@@ -2773,69 +2825,162 @@ const pluginInitCommand = defineCommand({
2773
2825
  consola.error(`package.json already exists in ${targetDir}`);
2774
2826
  process.exit(1);
2775
2827
  }
2776
- consola.start(`Scaffolding ${isNative ? "native" : "standard"} plugin: ${pluginName}`);
2828
+ consola.start(`Scaffolding ${isNative ? "native" : "sandboxed"} plugin: ${pluginName}`);
2777
2829
  await mkdir(srcDir, { recursive: true });
2778
2830
  if (isNative) await scaffoldNative(targetDir, srcDir, pluginName, slug);
2779
2831
  else await scaffoldStandard(targetDir, srcDir, pluginName, slug);
2780
2832
  consola.success(`Plugin scaffolded in ${targetDir}`);
2781
2833
  consola.info("Next steps:");
2782
- if (args.dir !== ".") consola.info(` 1. cd ${args.dir}`);
2783
- consola.info(` ${args.dir !== "." ? "2" : "1"}. pnpm install`);
2784
- if (isNative) consola.info(` ${args.dir !== "." ? "3" : "2"}. Edit src/index.ts to add hooks and routes`);
2785
- else consola.info(` ${args.dir !== "." ? "3" : "2"}. Edit src/sandbox-entry.ts to add hooks and routes`);
2786
- consola.info(` ${args.dir !== "." ? "4" : "3"}. emdash plugin validate --dir .`);
2834
+ const steps = [];
2835
+ if (args.dir !== ".") steps.push(`cd ${args.dir}`);
2836
+ steps.push("pnpm install");
2837
+ steps.push(isNative ? "Edit src/index.ts to add hooks and routes" : "Edit src/sandbox-entry.ts to add hooks and routes");
2838
+ steps.push("pnpm build");
2839
+ if (!isNative) steps.push("emdash plugin validate --dir .");
2840
+ steps.forEach((step, i) => consola.info(` ${i + 1}. ${step}`));
2787
2841
  }
2788
2842
  });
2843
+ async function resolveFormat(formatArg, nativeFlag) {
2844
+ if (formatArg) {
2845
+ const normalized = formatArg.toLowerCase();
2846
+ let parsed;
2847
+ if (normalized === "native") parsed = "native";
2848
+ else if (normalized === "sandboxed" || normalized === "standard") parsed = "standard";
2849
+ else {
2850
+ consola.error(`Invalid --format "${formatArg}". Use "sandboxed" or "native".`);
2851
+ process.exit(1);
2852
+ }
2853
+ if (nativeFlag && parsed !== "native") {
2854
+ consola.error(`Conflicting flags: --native and --format=${formatArg}. Pass only one.`);
2855
+ process.exit(1);
2856
+ }
2857
+ return parsed;
2858
+ }
2859
+ if (nativeFlag) return "native";
2860
+ if (!process.stdout.isTTY) return "standard";
2861
+ const choice = await consola.prompt("Which plugin format?", {
2862
+ type: "select",
2863
+ initial: "standard",
2864
+ options: [{
2865
+ label: "Sandboxed",
2866
+ value: "standard",
2867
+ hint: "runs in an isolated sandbox; safe to install from the marketplace"
2868
+ }, {
2869
+ label: "Native",
2870
+ value: "native",
2871
+ hint: "full runtime access; install from npm"
2872
+ }],
2873
+ cancel: "null"
2874
+ });
2875
+ if (choice === null) return null;
2876
+ return choice;
2877
+ }
2878
+ function camelCase(slug) {
2879
+ return slug.split("-").map((s, i) => i === 0 ? s : s[0].toUpperCase() + s.slice(1)).join("");
2880
+ }
2881
+ function pascalCase(slug) {
2882
+ return slug.split("-").map((s) => s[0].toUpperCase() + s.slice(1)).join("");
2883
+ }
2884
+ const TSCONFIG = {
2885
+ compilerOptions: {
2886
+ target: "ES2022",
2887
+ module: "preserve",
2888
+ moduleResolution: "bundler",
2889
+ strict: true,
2890
+ esModuleInterop: true,
2891
+ declaration: true,
2892
+ outDir: "./dist",
2893
+ rootDir: "./src"
2894
+ },
2895
+ include: ["src/**/*"],
2896
+ exclude: ["node_modules", "dist"]
2897
+ };
2898
+ const TSDOWN_VERSION = "^0.20.0";
2899
+ const TYPESCRIPT_VERSION = "^5.9.0";
2789
2900
  async function scaffoldStandard(targetDir, srcDir, pluginName, slug) {
2790
- const fnName = slug.split("-").map((s, i) => i === 0 ? s : s[0].toUpperCase() + s.slice(1)).join("");
2901
+ const fnName = camelCase(slug);
2791
2902
  await writeFile(join(targetDir, "package.json"), JSON.stringify({
2792
2903
  name: pluginName,
2793
2904
  version: "0.1.0",
2794
2905
  type: "module",
2906
+ main: "./dist/index.mjs",
2795
2907
  exports: {
2796
- ".": "./src/index.ts",
2797
- "./sandbox": "./src/sandbox-entry.ts"
2908
+ ".": {
2909
+ types: "./dist/index.d.mts",
2910
+ import: "./dist/index.mjs"
2911
+ },
2912
+ "./sandbox": {
2913
+ types: "./dist/sandbox-entry.d.mts",
2914
+ import: "./dist/sandbox-entry.mjs"
2915
+ }
2798
2916
  },
2799
- files: ["src"],
2800
- peerDependencies: { emdash: "*" }
2801
- }, null, " ") + "\n");
2802
- await writeFile(join(targetDir, "tsconfig.json"), JSON.stringify({
2803
- compilerOptions: {
2804
- target: "ES2022",
2805
- module: "preserve",
2806
- moduleResolution: "bundler",
2807
- strict: true,
2808
- esModuleInterop: true,
2809
- declaration: true,
2810
- outDir: "./dist",
2811
- rootDir: "./src"
2917
+ files: ["dist"],
2918
+ scripts: {
2919
+ build: "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --clean",
2920
+ dev: "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --watch",
2921
+ typecheck: "tsc --noEmit"
2812
2922
  },
2813
- include: ["src/**/*"],
2814
- exclude: ["node_modules", "dist"]
2923
+ keywords: ["emdash", "emdash-plugin"],
2924
+ license: "MIT",
2925
+ peerDependencies: { emdash: "*" },
2926
+ devDependencies: {
2927
+ emdash: "*",
2928
+ tsdown: TSDOWN_VERSION,
2929
+ typescript: TYPESCRIPT_VERSION
2930
+ }
2815
2931
  }, null, " ") + "\n");
2932
+ await writeFile(join(targetDir, "tsconfig.json"), JSON.stringify(TSCONFIG, null, " ") + "\n");
2816
2933
  await writeFile(join(srcDir, "index.ts"), `import type { PluginDescriptor } from "emdash";
2817
2934
 
2818
2935
  export function ${fnName}Plugin(): PluginDescriptor {
2819
2936
  \treturn {
2820
- \t\tid: "${pluginName}",
2937
+ \t\tid: "${slug}",
2821
2938
  \t\tversion: "0.1.0",
2822
2939
  \t\tformat: "standard",
2823
2940
  \t\tentrypoint: "${pluginName}/sandbox",
2824
- \t\tcapabilities: [],
2941
+
2942
+ \t\tcapabilities: ["content:read"],
2943
+ \t\tstorage: {
2944
+ \t\t\tevents: { indexes: ["timestamp"] },
2945
+ \t\t},
2825
2946
  \t};
2826
2947
  }
2827
2948
  `);
2828
2949
  await writeFile(join(srcDir, "sandbox-entry.ts"), `import { definePlugin } from "emdash";
2829
2950
  import type { PluginContext } from "emdash";
2830
2951
 
2952
+ interface ContentSaveEvent {
2953
+ \tcollection: string;
2954
+ \tcontent: { id: string };
2955
+ \tisNew: boolean;
2956
+ }
2957
+
2831
2958
  export default definePlugin({
2832
2959
  \thooks: {
2833
2960
  \t\t"content:afterSave": {
2834
- \t\t\thandler: async (event: any, ctx: PluginContext) => {
2961
+ \t\t\thandler: async (event: ContentSaveEvent, ctx: PluginContext) => {
2835
2962
  \t\t\t\tctx.log.info("Content saved", {
2836
2963
  \t\t\t\t\tcollection: event.collection,
2837
2964
  \t\t\t\t\tid: event.content.id,
2838
2965
  \t\t\t\t});
2966
+
2967
+ \t\t\t\tawait ctx.storage.events.put(\`save-\${Date.now()}\`, {
2968
+ \t\t\t\t\ttimestamp: new Date().toISOString(),
2969
+ \t\t\t\t\tcollection: event.collection,
2970
+ \t\t\t\t\tcontentId: event.content.id,
2971
+ \t\t\t\t});
2972
+ \t\t\t},
2973
+ \t\t},
2974
+ \t},
2975
+
2976
+ \troutes: {
2977
+ \t\trecent: {
2978
+ \t\t\thandler: async (_routeCtx, ctx: PluginContext) => {
2979
+ \t\t\t\tconst result = await ctx.storage.events.query({
2980
+ \t\t\t\t\torderBy: { timestamp: "desc" },
2981
+ \t\t\t\t\tlimit: 10,
2982
+ \t\t\t\t});
2983
+ \t\t\t\treturn { events: result.items };
2839
2984
  \t\t\t},
2840
2985
  \t\t},
2841
2986
  \t},
@@ -2843,55 +2988,82 @@ export default definePlugin({
2843
2988
  `);
2844
2989
  }
2845
2990
  async function scaffoldNative(targetDir, srcDir, pluginName, slug) {
2846
- const fnName = slug.split("-").map((s, i) => i === 0 ? s : s[0].toUpperCase() + s.slice(1)).join("");
2991
+ const fnName = camelCase(slug);
2992
+ const typeName = pascalCase(slug);
2847
2993
  await writeFile(join(targetDir, "package.json"), JSON.stringify({
2848
2994
  name: pluginName,
2849
2995
  version: "0.1.0",
2850
2996
  type: "module",
2851
- exports: { ".": "./src/index.ts" },
2852
- files: ["src"],
2853
- peerDependencies: { emdash: "*" }
2854
- }, null, " ") + "\n");
2855
- await writeFile(join(targetDir, "tsconfig.json"), JSON.stringify({
2856
- compilerOptions: {
2857
- target: "ES2022",
2858
- module: "preserve",
2859
- moduleResolution: "bundler",
2860
- strict: true,
2861
- esModuleInterop: true,
2862
- declaration: true,
2863
- outDir: "./dist",
2864
- rootDir: "./src"
2997
+ main: "./dist/index.mjs",
2998
+ exports: { ".": {
2999
+ types: "./dist/index.d.mts",
3000
+ import: "./dist/index.mjs"
3001
+ } },
3002
+ files: ["dist"],
3003
+ scripts: {
3004
+ build: "tsdown src/index.ts --format esm --dts --clean",
3005
+ dev: "tsdown src/index.ts --format esm --dts --watch",
3006
+ typecheck: "tsc --noEmit"
2865
3007
  },
2866
- include: ["src/**/*"],
2867
- exclude: ["node_modules", "dist"]
3008
+ keywords: ["emdash", "emdash-plugin"],
3009
+ license: "MIT",
3010
+ peerDependencies: { emdash: "*" },
3011
+ devDependencies: {
3012
+ emdash: "*",
3013
+ tsdown: TSDOWN_VERSION,
3014
+ typescript: TYPESCRIPT_VERSION
3015
+ }
2868
3016
  }, null, " ") + "\n");
3017
+ await writeFile(join(targetDir, "tsconfig.json"), JSON.stringify(TSCONFIG, null, " ") + "\n");
2869
3018
  await writeFile(join(srcDir, "index.ts"), `import { definePlugin } from "emdash";
2870
3019
  import type { PluginDescriptor } from "emdash";
2871
3020
 
2872
- export function ${fnName}Plugin(): PluginDescriptor {
3021
+ export interface ${typeName}Options {
3022
+ \tenabled?: boolean;
3023
+ }
3024
+
3025
+ export function ${fnName}Plugin(options: ${typeName}Options = {}): PluginDescriptor<${typeName}Options> {
2873
3026
  \treturn {
2874
- \t\tid: "${pluginName}",
3027
+ \t\tid: "${slug}",
2875
3028
  \t\tversion: "0.1.0",
2876
3029
  \t\tformat: "native",
2877
3030
  \t\tentrypoint: "${pluginName}",
2878
- \t\toptions: {},
3031
+ \t\toptions,
2879
3032
  \t};
2880
3033
  }
2881
3034
 
2882
- export function createPlugin() {
3035
+ export function createPlugin(options: ${typeName}Options = {}) {
2883
3036
  \treturn definePlugin({
2884
- \t\tid: "${pluginName}",
3037
+ \t\tid: "${slug}",
2885
3038
  \t\tversion: "0.1.0",
2886
3039
 
3040
+ \t\tcapabilities: ["content:read"],
3041
+ \t\tstorage: {
3042
+ \t\t\tevents: { indexes: ["createdAt"] },
3043
+ \t\t},
3044
+
2887
3045
  \t\thooks: {
2888
3046
  \t\t\t"content:afterSave": async (event, ctx) => {
2889
- \t\t\t\tctx.log.info("Content saved", {
3047
+ \t\t\t\tif (options.enabled === false) return;
3048
+ \t\t\t\tawait ctx.storage.events.put(\`evt_\${Date.now()}\`, {
2890
3049
  \t\t\t\t\tcollection: event.collection,
2891
- \t\t\t\t\tid: event.content.id,
3050
+ \t\t\t\t\tcontentId: event.content.id,
3051
+ \t\t\t\t\tcreatedAt: new Date().toISOString(),
2892
3052
  \t\t\t\t});
2893
3053
  \t\t\t},
2894
3054
  \t\t},
3055
+
3056
+ \t\troutes: {
3057
+ \t\t\trecent: {
3058
+ \t\t\t\thandler: async (ctx) => {
3059
+ \t\t\t\t\tconst result = await ctx.storage.events.query({
3060
+ \t\t\t\t\t\torderBy: { createdAt: "desc" },
3061
+ \t\t\t\t\t\tlimit: 10,
3062
+ \t\t\t\t\t});
3063
+ \t\t\t\t\treturn { events: result.items };
3064
+ \t\t\t\t},
3065
+ \t\t\t},
3066
+ \t\t},
2895
3067
  \t});
2896
3068
  }
2897
3069