emdash 0.2.0 → 0.4.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 (197) hide show
  1. package/dist/{adapters-N6BF7RCD.d.mts → adapters-C2BzVy0p.d.mts} +1 -1
  2. package/dist/{adapters-N6BF7RCD.d.mts.map → adapters-C2BzVy0p.d.mts.map} +1 -1
  3. package/dist/{apply-wmVEOSbR.mjs → apply-Cma_PiF6.mjs} +38 -23
  4. package/dist/apply-Cma_PiF6.mjs.map +1 -0
  5. package/dist/astro/index.d.mts +25 -11
  6. package/dist/astro/index.d.mts.map +1 -1
  7. package/dist/astro/index.mjs +38 -25
  8. package/dist/astro/index.mjs.map +1 -1
  9. package/dist/astro/middleware/auth.d.mts +5 -5
  10. package/dist/astro/middleware/auth.mjs +2 -2
  11. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  12. package/dist/astro/middleware/redirect.mjs +20 -8
  13. package/dist/astro/middleware/redirect.mjs.map +1 -1
  14. package/dist/astro/middleware/request-context.mjs +12 -2
  15. package/dist/astro/middleware/request-context.mjs.map +1 -1
  16. package/dist/astro/middleware/setup.mjs +1 -1
  17. package/dist/astro/middleware.d.mts.map +1 -1
  18. package/dist/astro/middleware.mjs +52 -45
  19. package/dist/astro/middleware.mjs.map +1 -1
  20. package/dist/astro/types.d.mts +9 -9
  21. package/dist/astro/types.d.mts.map +1 -1
  22. package/dist/{byline-1WQPlISL.mjs → byline-WuOq9MFJ.mjs} +5 -4
  23. package/dist/byline-WuOq9MFJ.mjs.map +1 -0
  24. package/dist/{bylines-BYdTYmia.mjs → bylines-C_Wsnz4L.mjs} +38 -6
  25. package/dist/bylines-C_Wsnz4L.mjs.map +1 -0
  26. package/dist/cache-E3Dts-yT.mjs +56 -0
  27. package/dist/cache-E3Dts-yT.mjs.map +1 -0
  28. package/dist/cli/index.mjs +13 -13
  29. package/dist/cli/index.mjs.map +1 -1
  30. package/dist/client/cf-access.d.mts +1 -1
  31. package/dist/client/index.d.mts +1 -1
  32. package/dist/client/index.mjs +1 -1
  33. package/dist/{config-Cq8H0SfX.mjs → config-DkxPrM9l.mjs} +1 -1
  34. package/dist/{config-Cq8H0SfX.mjs.map → config-DkxPrM9l.mjs.map} +1 -1
  35. package/dist/{content-BmXndhdi.mjs → content-BsBoyj8G.mjs} +20 -3
  36. package/dist/content-BsBoyj8G.mjs.map +1 -0
  37. package/dist/db/index.d.mts +3 -3
  38. package/dist/db/index.mjs +2 -2
  39. package/dist/db/libsql.d.mts +1 -1
  40. package/dist/db/postgres.d.mts +1 -1
  41. package/dist/db/sqlite.d.mts +1 -1
  42. package/dist/{default-WYlzADZL.mjs → default-PUx9RK6u.mjs} +1 -1
  43. package/dist/{default-WYlzADZL.mjs.map → default-PUx9RK6u.mjs.map} +1 -1
  44. package/dist/{dialect-helpers-B9uSp2GJ.mjs → dialect-helpers-DhTzaUxP.mjs} +4 -1
  45. package/dist/dialect-helpers-DhTzaUxP.mjs.map +1 -0
  46. package/dist/{error-DrxtnGPg.mjs → error-HBeQbVhV.mjs} +1 -1
  47. package/dist/{error-DrxtnGPg.mjs.map → error-HBeQbVhV.mjs.map} +1 -1
  48. package/dist/{index-UHEVQMus.d.mts → index-CRg3PWfZ.d.mts} +59 -33
  49. package/dist/index-CRg3PWfZ.d.mts.map +1 -0
  50. package/dist/index.d.mts +11 -11
  51. package/dist/index.mjs +20 -20
  52. package/dist/{load-Veizk2cT.mjs → load-BhSSm-TS.mjs} +1 -1
  53. package/dist/{load-Veizk2cT.mjs.map → load-BhSSm-TS.mjs.map} +1 -1
  54. package/dist/{loader-CHb2v0jm.mjs → loader-BYzwzORf.mjs} +4 -2
  55. package/dist/loader-BYzwzORf.mjs.map +1 -0
  56. package/dist/{manifest-schema-CuMio1A9.mjs → manifest-schema-BsXINkQD.mjs} +1 -1
  57. package/dist/{manifest-schema-CuMio1A9.mjs.map → manifest-schema-BsXINkQD.mjs.map} +1 -1
  58. package/dist/media/index.d.mts +1 -1
  59. package/dist/media/index.mjs +1 -1
  60. package/dist/media/local-runtime.d.mts +7 -7
  61. package/dist/{mode-CYeM2rPt.mjs → mode-CyPLdO3C.mjs} +1 -1
  62. package/dist/{mode-CYeM2rPt.mjs.map → mode-CyPLdO3C.mjs.map} +1 -1
  63. package/dist/page/index.d.mts +1 -1
  64. package/dist/patterns-CrCYkMBb.mjs +93 -0
  65. package/dist/patterns-CrCYkMBb.mjs.map +1 -0
  66. package/dist/{placeholder-bOx1xCTY.d.mts → placeholder-BBCtpTES.d.mts} +1 -1
  67. package/dist/{placeholder-bOx1xCTY.d.mts.map → placeholder-BBCtpTES.d.mts.map} +1 -1
  68. package/dist/{placeholder-aiCD8aSZ.mjs → placeholder-DntBEQo7.mjs} +1 -1
  69. package/dist/{placeholder-aiCD8aSZ.mjs.map → placeholder-DntBEQo7.mjs.map} +1 -1
  70. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  71. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  72. package/dist/{query-5Hcv_5ER.mjs → query-B6Vu0d2i.mjs} +35 -16
  73. package/dist/{query-5Hcv_5ER.mjs.map → query-B6Vu0d2i.mjs.map} +1 -1
  74. package/dist/{redirect-DIfIni3r.mjs → redirect-7lGhLBNZ.mjs} +10 -93
  75. package/dist/redirect-7lGhLBNZ.mjs.map +1 -0
  76. package/dist/{registry-1EvbAfsC.mjs → registry-BgnP3ysR.mjs} +27 -37
  77. package/dist/registry-BgnP3ysR.mjs.map +1 -0
  78. package/dist/{runner-BoN0-FPi.mjs → runner-Cd-_WyDo.mjs} +18 -6
  79. package/dist/runner-Cd-_WyDo.mjs.map +1 -0
  80. package/dist/{runner-DTqkzOzc.d.mts → runner-DYv3rX8P.d.mts} +10 -3
  81. package/dist/runner-DYv3rX8P.d.mts.map +1 -0
  82. package/dist/runtime.d.mts +6 -6
  83. package/dist/runtime.mjs +2 -2
  84. package/dist/{search-BsYMed12.mjs → search-B5p9D36n.mjs} +108 -57
  85. package/dist/search-B5p9D36n.mjs.map +1 -0
  86. package/dist/seed/index.d.mts +2 -2
  87. package/dist/seed/index.mjs +10 -10
  88. package/dist/seo/index.d.mts +1 -1
  89. package/dist/storage/local.d.mts +1 -1
  90. package/dist/storage/local.mjs +1 -1
  91. package/dist/storage/s3.d.mts +11 -3
  92. package/dist/storage/s3.d.mts.map +1 -1
  93. package/dist/storage/s3.mjs +76 -15
  94. package/dist/storage/s3.mjs.map +1 -1
  95. package/dist/{tokens-DrB-W6Q-.mjs → tokens-DKHiCYCB.mjs} +1 -1
  96. package/dist/{tokens-DrB-W6Q-.mjs.map → tokens-DKHiCYCB.mjs.map} +1 -1
  97. package/dist/transaction-Cn2rjY78.mjs +28 -0
  98. package/dist/transaction-Cn2rjY78.mjs.map +1 -0
  99. package/dist/{transport-Bl8cTdYt.mjs → transport-BtcQ-Z7T.mjs} +1 -1
  100. package/dist/{transport-Bl8cTdYt.mjs.map → transport-BtcQ-Z7T.mjs.map} +1 -1
  101. package/dist/{transport-COOs9GSE.d.mts → transport-CKQA_G44.d.mts} +1 -1
  102. package/dist/{transport-COOs9GSE.d.mts.map → transport-CKQA_G44.d.mts.map} +1 -1
  103. package/dist/{types-7-UjSEyB.d.mts → types-B6BzlZxx.d.mts} +1 -1
  104. package/dist/{types-7-UjSEyB.d.mts.map → types-B6BzlZxx.d.mts.map} +1 -1
  105. package/dist/{types-6dqxBqsH.d.mts → types-BYWYxLcp.d.mts} +109 -5
  106. package/dist/types-BYWYxLcp.d.mts.map +1 -0
  107. package/dist/{types-CIsTnQvJ.d.mts → types-BmkQR1En.d.mts} +1 -1
  108. package/dist/{types-CIsTnQvJ.d.mts.map → types-BmkQR1En.d.mts.map} +1 -1
  109. package/dist/{types-BljtYPSd.d.mts → types-DNZpaCBk.d.mts} +14 -6
  110. package/dist/types-DNZpaCBk.d.mts.map +1 -0
  111. package/dist/{types-Bec-r_3_.mjs → types-Dz9_WMS6.mjs} +1 -1
  112. package/dist/types-Dz9_WMS6.mjs.map +1 -0
  113. package/dist/{types-CcreFIIH.d.mts → types-gLYVCXCQ.d.mts} +1 -1
  114. package/dist/{types-CcreFIIH.d.mts.map → types-gLYVCXCQ.d.mts.map} +1 -1
  115. package/dist/{types-DuNbGKjF.mjs → types-xxCWI3j0.mjs} +1 -1
  116. package/dist/{types-DuNbGKjF.mjs.map → types-xxCWI3j0.mjs.map} +1 -1
  117. package/dist/{validate-B7KP7VLM.d.mts → validate-CcNRWH6I.d.mts} +4 -4
  118. package/dist/{validate-B7KP7VLM.d.mts.map → validate-CcNRWH6I.d.mts.map} +1 -1
  119. package/dist/{validate-CXnRKfJK.mjs → validate-DuZDIxfy.mjs} +2 -2
  120. package/dist/{validate-CXnRKfJK.mjs.map → validate-DuZDIxfy.mjs.map} +1 -1
  121. package/dist/{validate-CqRJb_xU.mjs → validate-VPnKoIzW.mjs} +11 -11
  122. package/dist/{validate-CqRJb_xU.mjs.map → validate-VPnKoIzW.mjs.map} +1 -1
  123. package/dist/version-DlTDRdpv.mjs +7 -0
  124. package/dist/version-DlTDRdpv.mjs.map +1 -0
  125. package/package.json +7 -5
  126. package/src/api/handlers/content.ts +36 -25
  127. package/src/api/handlers/menus.ts +19 -16
  128. package/src/api/handlers/redirects.ts +95 -3
  129. package/src/api/schemas/redirects.ts +1 -0
  130. package/src/astro/integration/index.ts +2 -3
  131. package/src/astro/integration/runtime.ts +8 -14
  132. package/src/astro/integration/vite-config.ts +14 -4
  133. package/src/astro/middleware/redirect.ts +30 -15
  134. package/src/astro/middleware.ts +11 -19
  135. package/src/astro/routes/admin.astro +2 -2
  136. package/src/astro/routes/api/admin/bylines/[id]/index.ts +3 -0
  137. package/src/astro/routes/api/admin/bylines/index.ts +2 -0
  138. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +2 -0
  139. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +2 -0
  140. package/src/astro/routes/api/manifest.ts +3 -1
  141. package/src/astro/routes/api/redirects/[id].ts +3 -0
  142. package/src/astro/routes/api/redirects/index.ts +2 -0
  143. package/src/astro/routes/api/schema/collections/[slug]/index.ts +2 -0
  144. package/src/astro/routes/api/schema/collections/index.ts +1 -0
  145. package/src/astro/storage/adapters.ts +19 -5
  146. package/src/astro/storage/types.ts +12 -4
  147. package/src/astro/types.ts +1 -0
  148. package/src/bylines/index.ts +50 -2
  149. package/src/cleanup.ts +3 -3
  150. package/src/cli/commands/bundle-utils.ts +5 -5
  151. package/src/database/dialect-helpers.ts +3 -0
  152. package/src/database/migrations/011_sections.ts +2 -2
  153. package/src/database/migrations/runner.ts +23 -2
  154. package/src/database/repositories/byline.ts +2 -1
  155. package/src/database/repositories/content.ts +5 -0
  156. package/src/database/repositories/redirect.ts +13 -0
  157. package/src/database/validate.ts +10 -10
  158. package/src/emdash-runtime.ts +23 -9
  159. package/src/index.ts +3 -0
  160. package/src/loader.ts +2 -0
  161. package/src/mcp/server.ts +40 -67
  162. package/src/menus/index.ts +4 -0
  163. package/src/plugins/context.ts +28 -4
  164. package/src/plugins/cron.ts +29 -4
  165. package/src/plugins/hooks.ts +22 -10
  166. package/src/plugins/index.ts +1 -0
  167. package/src/plugins/manager.ts +6 -2
  168. package/src/plugins/marketplace.ts +33 -3
  169. package/src/plugins/routes.ts +3 -3
  170. package/src/plugins/types.ts +7 -0
  171. package/src/query.ts +37 -14
  172. package/src/redirects/cache.ts +68 -0
  173. package/src/redirects/loops.ts +318 -0
  174. package/src/schema/registry.ts +3 -0
  175. package/src/search/fts-manager.ts +24 -11
  176. package/src/search/query.ts +8 -9
  177. package/src/seed/apply.ts +49 -28
  178. package/src/storage/s3.ts +94 -25
  179. package/src/storage/types.ts +13 -5
  180. package/src/utils/slugify.ts +11 -0
  181. package/src/version.ts +12 -0
  182. package/src/visual-editing/toolbar.ts +11 -1
  183. package/dist/apply-wmVEOSbR.mjs.map +0 -1
  184. package/dist/byline-1WQPlISL.mjs.map +0 -1
  185. package/dist/bylines-BYdTYmia.mjs.map +0 -1
  186. package/dist/content-BmXndhdi.mjs.map +0 -1
  187. package/dist/dialect-helpers-B9uSp2GJ.mjs.map +0 -1
  188. package/dist/index-UHEVQMus.d.mts.map +0 -1
  189. package/dist/loader-CHb2v0jm.mjs.map +0 -1
  190. package/dist/redirect-DIfIni3r.mjs.map +0 -1
  191. package/dist/registry-1EvbAfsC.mjs.map +0 -1
  192. package/dist/runner-BoN0-FPi.mjs.map +0 -1
  193. package/dist/runner-DTqkzOzc.d.mts.map +0 -1
  194. package/dist/search-BsYMed12.mjs.map +0 -1
  195. package/dist/types-6dqxBqsH.d.mts.map +0 -1
  196. package/dist/types-Bec-r_3_.mjs.map +0 -1
  197. package/dist/types-BljtYPSd.d.mts.map +0 -1
@@ -1,17 +1,19 @@
1
- import { o as jsonExtractExpr } from "./dialect-helpers-B9uSp2GJ.mjs";
2
- import { n as validateJsonFieldName, r as validatePluginIdentifier, t as validateIdentifier } from "./validate-CqRJb_xU.mjs";
3
- import { i as slugify, r as RevisionRepository, t as ContentRepository } from "./content-BmXndhdi.mjs";
1
+ import { n as validateJsonFieldName, r as validatePluginIdentifier, t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
2
+ import { o as jsonExtractExpr } from "./dialect-helpers-DhTzaUxP.mjs";
3
+ import { a as slugify, r as RevisionRepository, t as ContentRepository } from "./content-BsBoyj8G.mjs";
4
4
  import { r as encodeBase64, t as decodeBase64 } from "./base64-MBPo9ozB.mjs";
5
5
  import { n as decodeCursor, r as encodeCursor, t as EmDashValidationError } from "./types-CMMN0pNg.mjs";
6
6
  import { t as MediaRepository } from "./media-DqHVh136.mjs";
7
- import { a as stripCredentialHeaders, f as OptionsRepository, i as ssrfSafeFetch, o as validateExternalUrl, r as SsrfError } from "./apply-wmVEOSbR.mjs";
8
- import { a as withTransaction, i as FTSManager, n as SchemaRegistry } from "./registry-1EvbAfsC.mjs";
9
- import { t as RedirectRepository } from "./redirect-DIfIni3r.mjs";
10
- import { n as SQL_BATCH_SIZE, r as chunks, t as BylineRepository } from "./byline-1WQPlISL.mjs";
11
- import { r as isI18nEnabled } from "./config-Cq8H0SfX.mjs";
12
- import { n as getDb } from "./loader-CHb2v0jm.mjs";
13
- import { i as pluginManifestSchema } from "./manifest-schema-CuMio1A9.mjs";
14
- import { t as generatePreviewToken } from "./tokens-DrB-W6Q-.mjs";
7
+ import { a as stripCredentialHeaders, f as OptionsRepository, i as ssrfSafeFetch, o as validateExternalUrl, r as SsrfError } from "./apply-Cma_PiF6.mjs";
8
+ import { t as withTransaction } from "./transaction-Cn2rjY78.mjs";
9
+ import { t as RedirectRepository } from "./redirect-7lGhLBNZ.mjs";
10
+ import { n as SQL_BATCH_SIZE, r as chunks, t as BylineRepository } from "./byline-WuOq9MFJ.mjs";
11
+ import { r as isI18nEnabled } from "./config-DkxPrM9l.mjs";
12
+ import { r as invalidateRedirectCache } from "./cache-E3Dts-yT.mjs";
13
+ import { i as FTSManager, n as SchemaRegistry } from "./registry-BgnP3ysR.mjs";
14
+ import { n as getDb } from "./loader-BYzwzORf.mjs";
15
+ import { i as pluginManifestSchema } from "./manifest-schema-BsXINkQD.mjs";
16
+ import { t as generatePreviewToken } from "./tokens-DKHiCYCB.mjs";
15
17
  import { sql } from "kysely";
16
18
  import { ulid } from "ulidx";
17
19
  import { z } from "astro/zod";
@@ -1278,34 +1280,18 @@ async function handleContentUpdate(db, collection, id, body) {
1278
1280
  message: `Collection "${collection}" does not have SEO enabled. Remove the seo field or enable SEO on this collection.`
1279
1281
  }
1280
1282
  };
1281
- const repo = new ContentRepository(db);
1282
- const resolvedId = await resolveId(repo, collection, id) ?? id;
1283
- if (body._rev) {
1284
- const existing = await repo.findById(collection, resolvedId);
1285
- if (!existing) return {
1286
- success: false,
1287
- error: {
1288
- code: "NOT_FOUND",
1289
- message: `Content item not found: ${id}`
1290
- }
1291
- };
1292
- const revCheck = validateRev(body._rev, existing);
1293
- if (!revCheck.valid) return {
1294
- success: false,
1295
- error: {
1296
- code: "CONFLICT",
1297
- message: revCheck.message
1298
- }
1299
- };
1300
- }
1283
+ const resolvedId = await resolveId(new ContentRepository(db), collection, id) ?? id;
1301
1284
  const item = await withTransaction(db, async (trx) => {
1302
1285
  const trxRepo = new ContentRepository(trx);
1303
1286
  const bylineRepo = new BylineRepository(trx);
1304
- let oldSlug;
1305
- if (body.slug) {
1306
- const existing = await trxRepo.findById(collection, resolvedId);
1307
- if (existing?.slug && existing.slug !== body.slug) oldSlug = existing.slug;
1287
+ const existing = body._rev || body.slug ? await trxRepo.findById(collection, resolvedId) : null;
1288
+ if (body._rev) {
1289
+ if (!existing) throw Object.assign(/* @__PURE__ */ new Error(`Content item not found: ${id}`), { apiError: { code: "NOT_FOUND" } });
1290
+ const revCheck = validateRev(body._rev, existing);
1291
+ if (!revCheck.valid) throw Object.assign(new Error(revCheck.message), { apiError: { code: "CONFLICT" } });
1308
1292
  }
1293
+ let oldSlug;
1294
+ if (body.slug && existing?.slug && existing.slug !== body.slug) oldSlug = existing.slug;
1309
1295
  const updated = await trxRepo.update(collection, resolvedId, {
1310
1296
  data: body.data,
1311
1297
  slug: body.slug,
@@ -1319,6 +1305,7 @@ async function handleContentUpdate(db, collection, id, body) {
1319
1305
  if (oldSlug && body.slug) {
1320
1306
  const collectionRow = await trx.selectFrom("_emdash_collections").select("url_pattern").where("slug", "=", collection).executeTakeFirst();
1321
1307
  await new RedirectRepository(trx).createAutoRedirect(collection, oldSlug, body.slug, resolvedId, collectionRow?.url_pattern ?? null);
1308
+ invalidateRedirectCache();
1322
1309
  }
1323
1310
  if (isI18nEnabled() && body.data && updated.translationGroup) await syncNonTranslatableFields(trx, collection, updated.id, updated.translationGroup, body.data);
1324
1311
  if (body.seo && hasSeo) updated.seo = await new SeoRepository(trx).upsert(collection, resolvedId, body.seo);
@@ -1334,6 +1321,16 @@ async function handleContentUpdate(db, collection, id, body) {
1334
1321
  }
1335
1322
  };
1336
1323
  } catch (error) {
1324
+ if (error instanceof Error && "apiError" in error) {
1325
+ const { code } = error.apiError;
1326
+ return {
1327
+ success: false,
1328
+ error: {
1329
+ code,
1330
+ message: error.message
1331
+ }
1332
+ };
1333
+ }
1337
1334
  console.error("Content update error:", error);
1338
1335
  return {
1339
1336
  success: false,
@@ -3662,7 +3659,8 @@ const redirectSchema = z$1.object({
3662
3659
  }).meta({ id: "Redirect" });
3663
3660
  const redirectListResponseSchema = z$1.object({
3664
3661
  items: z$1.array(redirectSchema),
3665
- nextCursor: z$1.string().optional()
3662
+ nextCursor: z$1.string().optional(),
3663
+ loopRedirectIds: z$1.array(z$1.string()).optional()
3666
3664
  }).meta({ id: "RedirectListResponse" });
3667
3665
  const notFoundEntrySchema = z$1.object({
3668
3666
  id: z$1.string(),
@@ -5130,12 +5128,31 @@ var CronExecutor = class {
5130
5128
  hookFailed = true;
5131
5129
  console.error(`[cron] Hook failed for ${task.plugin_id}:${task.task_name}:`, error);
5132
5130
  }
5133
- if (task.is_oneshot) if (hookFailed) await sql`
5131
+ if (task.is_oneshot) if (hookFailed) {
5132
+ const meta = parsedData?.__emdash != null && typeof parsedData.__emdash === "object" ? parsedData.__emdash : void 0;
5133
+ const raw = meta?.retryCount;
5134
+ const retryCount = typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 0;
5135
+ const MAX_ONESHOT_RETRIES = 5;
5136
+ if (retryCount >= MAX_ONESHOT_RETRIES) {
5137
+ console.error(`[cron] One-shot task ${task.plugin_id}:${task.task_name} exceeded ${MAX_ONESHOT_RETRIES} retries, removing`);
5138
+ await sql`
5139
+ DELETE FROM _emdash_cron_tasks WHERE id = ${task.id}
5140
+ `.execute(this.db);
5141
+ } else {
5142
+ const backoffMs = 6e4 * Math.pow(2, retryCount);
5143
+ await sql`
5134
5144
  UPDATE _emdash_cron_tasks
5135
- SET status = 'idle', locked_at = NULL, next_run_at = ${new Date(Date.now() + 6e4).toISOString()}
5145
+ SET status = 'idle', locked_at = NULL, next_run_at = ${new Date(Date.now() + backoffMs).toISOString()}, data = ${JSON.stringify({
5146
+ ...parsedData,
5147
+ __emdash: {
5148
+ ...meta,
5149
+ retryCount: retryCount + 1
5150
+ }
5151
+ })}
5136
5152
  WHERE id = ${task.id}
5137
5153
  `.execute(this.db);
5138
- else await sql`
5154
+ }
5155
+ } else await sql`
5139
5156
  DELETE FROM _emdash_cron_tasks WHERE id = ${task.id}
5140
5157
  `.execute(this.db);
5141
5158
  else await sql`
@@ -5399,9 +5416,13 @@ function createContentAccess(db) {
5399
5416
  const result = {
5400
5417
  id: item.id,
5401
5418
  type: item.type,
5419
+ slug: item.slug,
5420
+ status: item.status,
5402
5421
  data: item.data,
5403
5422
  createdAt: item.createdAt,
5404
- updatedAt: item.updatedAt
5423
+ updatedAt: item.updatedAt,
5424
+ locale: item.locale,
5425
+ publishedAt: item.publishedAt
5405
5426
  };
5406
5427
  if (await seoRepo.isEnabled(collection)) result.seo = await seoRepo.get(collection, item.id);
5407
5428
  return result;
@@ -5423,9 +5444,13 @@ function createContentAccess(db) {
5423
5444
  const items = result.items.map((item) => ({
5424
5445
  id: item.id,
5425
5446
  type: item.type,
5447
+ slug: item.slug,
5448
+ status: item.status,
5426
5449
  data: item.data,
5427
5450
  createdAt: item.createdAt,
5428
- updatedAt: item.updatedAt
5451
+ updatedAt: item.updatedAt,
5452
+ locale: item.locale,
5453
+ publishedAt: item.publishedAt
5429
5454
  }));
5430
5455
  if (items.length > 0 && await seoRepo.isEnabled(collection)) {
5431
5456
  const seoMap = await seoRepo.getMany(collection, items.map((i) => i.id));
@@ -5467,9 +5492,13 @@ function createContentAccessWithWrite(db) {
5467
5492
  const result = {
5468
5493
  id: item.id,
5469
5494
  type: item.type,
5495
+ slug: item.slug,
5496
+ status: item.status,
5470
5497
  data: item.data,
5471
5498
  createdAt: item.createdAt,
5472
- updatedAt: item.updatedAt
5499
+ updatedAt: item.updatedAt,
5500
+ locale: item.locale,
5501
+ publishedAt: item.publishedAt
5473
5502
  };
5474
5503
  if (hasSeo) result.seo = seo !== void 0 ? await trxSeoRepo.upsert(collection, item.id, seo) : await trxSeoRepo.get(collection, item.id);
5475
5504
  return result;
@@ -5489,9 +5518,13 @@ function createContentAccessWithWrite(db) {
5489
5518
  const result = {
5490
5519
  id: item.id,
5491
5520
  type: item.type,
5521
+ slug: item.slug,
5522
+ status: item.status,
5492
5523
  data: item.data,
5493
5524
  createdAt: item.createdAt,
5494
- updatedAt: item.updatedAt
5525
+ updatedAt: item.updatedAt,
5526
+ locale: item.locale,
5527
+ publishedAt: item.publishedAt
5495
5528
  };
5496
5529
  if (hasSeo) result.seo = seo !== void 0 ? await trxSeoRepo.upsert(collection, item.id, seo) : await trxSeoRepo.get(collection, item.id);
5497
5530
  return result;
@@ -5552,17 +5585,18 @@ function createMediaAccessWithWrite(db, getUploadUrlFn, storage) {
5552
5585
  getUploadUrl: getUploadUrlFn,
5553
5586
  async upload(filename, contentType, bytes) {
5554
5587
  if (!storage) throw new Error("Media upload() requires a storage backend. Configure storage in PluginContextFactoryOptions.");
5555
- const mediaId = ulid();
5588
+ const keyPrefix = ulid();
5556
5589
  const basename = filename.split("/").pop() ?? filename;
5557
5590
  const dotIdx = basename.lastIndexOf(".");
5558
- const storageKey = `${mediaId}${dotIdx > 0 ? basename.slice(dotIdx).toLowerCase() : ""}`;
5591
+ const storageKey = `${keyPrefix}${dotIdx > 0 ? basename.slice(dotIdx).toLowerCase() : ""}`;
5559
5592
  await storage.upload({
5560
5593
  key: storageKey,
5561
5594
  body: new Uint8Array(bytes),
5562
5595
  contentType
5563
5596
  });
5597
+ let media;
5564
5598
  try {
5565
- await mediaRepo.create({
5599
+ media = await mediaRepo.create({
5566
5600
  filename: basename,
5567
5601
  mimeType: contentType,
5568
5602
  size: bytes.byteLength,
@@ -5576,7 +5610,7 @@ function createMediaAccessWithWrite(db, getUploadUrlFn, storage) {
5576
5610
  throw error;
5577
5611
  }
5578
5612
  return {
5579
- mediaId,
5613
+ mediaId: media.id,
5580
5614
  storageKey,
5581
5615
  url: `/_emdash/api/media/file/${storageKey}`
5582
5616
  };
@@ -5588,6 +5622,11 @@ function createMediaAccessWithWrite(db, getUploadUrlFn, storage) {
5588
5622
  }
5589
5623
  /** Maximum number of redirects to follow in plugin HTTP access */
5590
5624
  const MAX_PLUGIN_REDIRECTS = 5;
5625
+ /**
5626
+ * Check if a hostname matches any pattern in the allowed list.
5627
+ * Patterns: "*" matches all, "*.example.com" matches subdomains AND bare "example.com",
5628
+ * "api.example.com" matches exactly.
5629
+ */
5591
5630
  function isHostAllowed(host, allowedHosts) {
5592
5631
  return allowedHosts.some((pattern) => {
5593
5632
  if (pattern === "*") return true;
@@ -5985,6 +6024,8 @@ var HookPipeline = class HookPipeline {
5985
6024
  while (remaining.length > 0) {
5986
6025
  const ready = remaining.filter((hook) => hook.dependencies.every((dep) => sorted.some((s) => s.pluginId === dep)));
5987
6026
  if (ready.length === 0) {
6027
+ const pluginIds = remaining.map((h) => h.pluginId).join(", ");
6028
+ console.warn(`[hooks] Hook dependency cycle or missing dependency detected among plugins: ${pluginIds}. Falling back to priority order.`);
5988
6029
  remaining.sort((a, b) => a.priority - b.priority);
5989
6030
  sorted.push(...remaining);
5990
6031
  break;
@@ -6000,7 +6041,13 @@ var HookPipeline = class HookPipeline {
6000
6041
  * Execute a hook with timeout
6001
6042
  */
6002
6043
  async executeWithTimeout(fn, timeout) {
6003
- return Promise.race([fn(), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Hook timeout after ${timeout}ms`)), timeout))]);
6044
+ let timer;
6045
+ const timeoutPromise = new Promise((_, reject) => timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Hook timeout after ${timeout}ms`)), timeout));
6046
+ try {
6047
+ return await Promise.race([fn(), timeoutPromise]);
6048
+ } finally {
6049
+ clearTimeout(timer);
6050
+ }
6004
6051
  }
6005
6052
  /**
6006
6053
  * Run plugin:install hooks
@@ -6162,7 +6209,8 @@ var HookPipeline = class HookPipeline {
6162
6209
  const { handler } = hook;
6163
6210
  const event = {
6164
6211
  id,
6165
- collection
6212
+ collection,
6213
+ permanent: false
6166
6214
  };
6167
6215
  const ctx = this.getContext(hook.pluginId);
6168
6216
  const start = Date.now();
@@ -6193,14 +6241,15 @@ var HookPipeline = class HookPipeline {
6193
6241
  /**
6194
6242
  * Run content:afterDelete hooks
6195
6243
  */
6196
- async runContentAfterDelete(id, collection) {
6244
+ async runContentAfterDelete(id, collection, permanent) {
6197
6245
  const hooks = this.getTypedHooks("content:afterDelete");
6198
6246
  const results = [];
6199
6247
  for (const hook of hooks) {
6200
6248
  const { handler } = hook;
6201
6249
  const event = {
6202
6250
  id,
6203
- collection
6251
+ collection,
6252
+ permanent
6204
6253
  };
6205
6254
  const ctx = this.getContext(hook.pluginId);
6206
6255
  const start = Date.now();
@@ -6968,11 +7017,12 @@ var PluginRouteHandler = class {
6968
7017
  },
6969
7018
  status: error.status
6970
7019
  };
7020
+ console.error(`[plugin:${this.plugin.id}] Route handler failed:`, error);
6971
7021
  return {
6972
7022
  success: false,
6973
7023
  error: {
6974
7024
  code: "INTERNAL_ERROR",
6975
- message: `Route handler failed: ${error instanceof Error ? error.message : String(error)}`
7025
+ message: "An internal error occurred"
6976
7026
  },
6977
7027
  status: 500
6978
7028
  };
@@ -7257,9 +7307,9 @@ var PluginManager = class {
7257
7307
  /**
7258
7308
  * Run content:afterDelete hooks across all active plugins
7259
7309
  */
7260
- async runContentAfterDelete(id, collection) {
7310
+ async runContentAfterDelete(id, collection, permanent) {
7261
7311
  this.ensureInitialized();
7262
- return this.hookPipeline.runContentAfterDelete(id, collection);
7312
+ return this.hookPipeline.runContentAfterDelete(id, collection, permanent);
7263
7313
  }
7264
7314
  /**
7265
7315
  * Run content:afterPublish hooks across all active plugins
@@ -8762,6 +8812,7 @@ function interpolateUrlPattern(pattern, slug, id) {
8762
8812
  async function resolveContentUrl(collection, entryId, db, urlPatterns) {
8763
8813
  if (!entryId) return null;
8764
8814
  try {
8815
+ validateIdentifier(collection, "menu item collection");
8765
8816
  const row = (await sql`
8766
8817
  SELECT slug FROM ${sql.ref(`ec_${collection}`)} WHERE id = ${entryId} LIMIT 1
8767
8818
  `.execute(db)).rows[0];
@@ -8936,7 +8987,7 @@ async function getTermsForEntries(collection, entryIds, taxonomyName) {
8936
8987
  * Get entries by term (wraps getEmDashCollection)
8937
8988
  */
8938
8989
  async function getEntriesByTerm(collection, taxonomyName, termSlug) {
8939
- const { getEmDashCollection } = await import("./query-5Hcv_5ER.mjs").then((n) => n.a);
8990
+ const { getEmDashCollection } = await import("./query-B6Vu0d2i.mjs").then((n) => n.o);
8940
8991
  const { entries } = await getEmDashCollection(collection, { where: { [taxonomyName]: termSlug } });
8941
8992
  return entries;
8942
8993
  }
@@ -9342,9 +9393,9 @@ function escapeQuery(query) {
9342
9393
  if (!query || typeof query !== "string") return "";
9343
9394
  query = query.trim();
9344
9395
  if (query.length === 0) return "";
9396
+ if (query.startsWith("\"") && query.endsWith("\"") && query.length >= 2) return `"${query.slice(1, -1).replace(DOUBLE_QUOTE_PATTERN, "\"\"")}"`;
9345
9397
  const escaped = query.replace(DOUBLE_QUOTE_PATTERN, "\"\"");
9346
9398
  if (FTS_OPERATORS_PATTERN.test(query)) return escaped;
9347
- if (query.startsWith("\"") && query.endsWith("\"")) return query;
9348
9399
  const terms = escaped.split(WHITESPACE_SPLIT_PATTERN).filter((t) => t.length > 0);
9349
9400
  if (terms.length === 0) return "";
9350
9401
  return terms.map((t) => `"${t}"*`).join(" ");
@@ -9445,4 +9496,4 @@ function extractSearchableFields(entry, fields) {
9445
9496
 
9446
9497
  //#endregion
9447
9498
  export { sanitizeHeadersForSandbox as $, getAllSources as A, handleContentGet as At, createNoopSandboxRunner as B, handleContentUnschedule as Bt, isPreviewRequest as C, handleContentCompare as Ct, parseWxrDate as D, handleContentDelete as Dt, wordpressRestSource as E, handleContentCreate as Et, registerSource as F, handleContentPublish as Ft, DEV_CONSOLE_EMAIL_PLUGIN_ID as G, image as Gt, createPluginManager as H, validateRev as Ht, importReusableBlocksAsSections as I, handleContentRestore as It, HookPipeline as J, devConsoleEmailDeliver as K, isStandardPluginDefinition as L, handleContentSchedule as Lt, getSource as M, handleContentList as Mt, getUrlSources as N, handleContentListTrashed as Nt, wxrSource as O, handleContentDiscardDraft as Ot, probeUrl as P, handleContentPermanentDelete as Pt, extractRequestMeta as Q, NoopSandboxRunner as R, handleContentTranslations as Rt, getPreviewToken as S, hashString as St, getPreviewUrl as T, handleContentCountTrashed as Tt, PluginRouteError as U, portableText as Ut, PluginManager as V, handleContentUpdate as Vt, PluginRouteRegistry as W, reference as Wt, resolveExclusiveHooks as X, createHookPipeline as Y, CronExecutor as Z, getTermsForEntries as _, handleRevisionGet as _t, search as a, isSafeHref as at, getCommentCount as b, generateManifest as bt, getWidgetArea as c, getSection as ct, getEntriesByTerm as d, getCollectionInfo as dt, definePlugin as et, getEntryTerms as f, handleMediaCreate as ft, getTerm as g, handleMediaUpdate as gt, getTaxonomyTerms as h, handleMediaList as ht, getSuggestions as i, prosemirrorToPortableText as it, getFileSources as j, handleContentGetIncludingTrashed as jt, clearSources as k, handleContentDuplicate as kt, getWidgetAreas as l, getSections as lt, getTaxonomyDefs as m, handleMediaGet as mt, extractSearchableFields as n, parseWxrString as nt, searchCollection as o, sanitizeHref as ot, getTaxonomyDef as p, handleMediaDelete as pt, EmailPipeline as q, getSearchStats as r, portableTextToProsemirror as rt, searchWithDb as s, loadBundleFromR2 as st, extractPlainText as t, parseWxr as tt, getWidgetComponents as u, PluginStateRepository as ut, getMenu as v, handleRevisionList as vt, buildPreviewUrl as w, handleContentCountScheduled as wt, getComments as x, computeContentHash as xt, getMenus as y, handleRevisionRestore as yt, SandboxNotAvailableError as z, handleContentUnpublish as zt };
9448
- //# sourceMappingURL=search-BsYMed12.mjs.map
9499
+ //# sourceMappingURL=search-B5p9D36n.mjs.map