emdash 0.3.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 (162) hide show
  1. package/dist/{adapters-BLMa4JGD.d.mts → adapters-C2BzVy0p.d.mts} +1 -1
  2. package/dist/{adapters-BLMa4JGD.d.mts.map → adapters-C2BzVy0p.d.mts.map} +1 -1
  3. package/dist/{apply-Bqoekfbe.mjs → apply-Cma_PiF6.mjs} +37 -22
  4. package/dist/apply-Cma_PiF6.mjs.map +1 -0
  5. package/dist/astro/index.d.mts +6 -6
  6. package/dist/astro/index.mjs +9 -8
  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 +2 -2
  10. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  11. package/dist/astro/middleware/redirect.mjs +19 -7
  12. package/dist/astro/middleware/redirect.mjs.map +1 -1
  13. package/dist/astro/middleware/request-context.mjs +12 -2
  14. package/dist/astro/middleware/request-context.mjs.map +1 -1
  15. package/dist/astro/middleware/setup.mjs +1 -1
  16. package/dist/astro/middleware.d.mts.map +1 -1
  17. package/dist/astro/middleware.mjs +45 -42
  18. package/dist/astro/middleware.mjs.map +1 -1
  19. package/dist/astro/types.d.mts +8 -8
  20. package/dist/{byline-BGj9p9Ht.mjs → byline-WuOq9MFJ.mjs} +3 -2
  21. package/dist/byline-WuOq9MFJ.mjs.map +1 -0
  22. package/dist/{bylines-BihaoIDY.mjs → bylines-C_Wsnz4L.mjs} +36 -4
  23. package/dist/bylines-C_Wsnz4L.mjs.map +1 -0
  24. package/dist/cache-E3Dts-yT.mjs +56 -0
  25. package/dist/cache-E3Dts-yT.mjs.map +1 -0
  26. package/dist/cli/index.mjs +11 -11
  27. package/dist/cli/index.mjs.map +1 -1
  28. package/dist/client/cf-access.d.mts +1 -1
  29. package/dist/client/index.d.mts +1 -1
  30. package/dist/client/index.mjs +1 -1
  31. package/dist/{config-Cq8H0SfX.mjs → config-DkxPrM9l.mjs} +1 -1
  32. package/dist/{config-Cq8H0SfX.mjs.map → config-DkxPrM9l.mjs.map} +1 -1
  33. package/dist/db/index.d.mts +3 -3
  34. package/dist/db/index.mjs +1 -1
  35. package/dist/db/libsql.d.mts +1 -1
  36. package/dist/db/postgres.d.mts +1 -1
  37. package/dist/db/sqlite.d.mts +1 -1
  38. package/dist/{default-WYlzADZL.mjs → default-PUx9RK6u.mjs} +1 -1
  39. package/dist/{default-WYlzADZL.mjs.map → default-PUx9RK6u.mjs.map} +1 -1
  40. package/dist/{error-DrxtnGPg.mjs → error-HBeQbVhV.mjs} +1 -1
  41. package/dist/{error-DrxtnGPg.mjs.map → error-HBeQbVhV.mjs.map} +1 -1
  42. package/dist/{index-Cff7AimE.d.mts → index-CRg3PWfZ.d.mts} +32 -30
  43. package/dist/index-CRg3PWfZ.d.mts.map +1 -0
  44. package/dist/index.d.mts +11 -11
  45. package/dist/index.mjs +17 -17
  46. package/dist/{load-Veizk2cT.mjs → load-BhSSm-TS.mjs} +1 -1
  47. package/dist/{load-Veizk2cT.mjs.map → load-BhSSm-TS.mjs.map} +1 -1
  48. package/dist/{loader-BmYdf3Dr.mjs → loader-BYzwzORf.mjs} +1 -1
  49. package/dist/{loader-BmYdf3Dr.mjs.map → loader-BYzwzORf.mjs.map} +1 -1
  50. package/dist/{manifest-schema-CuMio1A9.mjs → manifest-schema-BsXINkQD.mjs} +1 -1
  51. package/dist/{manifest-schema-CuMio1A9.mjs.map → manifest-schema-BsXINkQD.mjs.map} +1 -1
  52. package/dist/media/index.d.mts +1 -1
  53. package/dist/media/index.mjs +1 -1
  54. package/dist/media/local-runtime.d.mts +7 -7
  55. package/dist/{mode-C2EzN1uE.mjs → mode-CyPLdO3C.mjs} +1 -1
  56. package/dist/{mode-C2EzN1uE.mjs.map → mode-CyPLdO3C.mjs.map} +1 -1
  57. package/dist/page/index.d.mts +1 -1
  58. package/dist/patterns-CrCYkMBb.mjs +93 -0
  59. package/dist/patterns-CrCYkMBb.mjs.map +1 -0
  60. package/dist/{placeholder-SvFCKbz_.d.mts → placeholder-BBCtpTES.d.mts} +1 -1
  61. package/dist/{placeholder-SvFCKbz_.d.mts.map → placeholder-BBCtpTES.d.mts.map} +1 -1
  62. package/dist/{placeholder-aiCD8aSZ.mjs → placeholder-DntBEQo7.mjs} +1 -1
  63. package/dist/{placeholder-aiCD8aSZ.mjs.map → placeholder-DntBEQo7.mjs.map} +1 -1
  64. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  65. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  66. package/dist/{query-sesiOndV.mjs → query-B6Vu0d2i.mjs} +34 -15
  67. package/dist/{query-sesiOndV.mjs.map → query-B6Vu0d2i.mjs.map} +1 -1
  68. package/dist/{redirect-DUAk-Yl_.mjs → redirect-7lGhLBNZ.mjs} +2 -92
  69. package/dist/redirect-7lGhLBNZ.mjs.map +1 -0
  70. package/dist/{registry-DU18yVo0.mjs → registry-BgnP3ysR.mjs} +19 -35
  71. package/dist/registry-BgnP3ysR.mjs.map +1 -0
  72. package/dist/{runner-Biufrii2.mjs → runner-Cd-_WyDo.mjs} +16 -4
  73. package/dist/runner-Cd-_WyDo.mjs.map +1 -0
  74. package/dist/{runner-EAtf0ZIe.d.mts → runner-DYv3rX8P.d.mts} +10 -3
  75. package/dist/runner-DYv3rX8P.d.mts.map +1 -0
  76. package/dist/runtime.d.mts +6 -6
  77. package/dist/runtime.mjs +1 -1
  78. package/dist/{search-BXB-jfu2.mjs → search-B5p9D36n.mjs} +102 -53
  79. package/dist/search-B5p9D36n.mjs.map +1 -0
  80. package/dist/seed/index.d.mts +2 -2
  81. package/dist/seed/index.mjs +8 -8
  82. package/dist/seo/index.d.mts +1 -1
  83. package/dist/storage/local.d.mts +1 -1
  84. package/dist/storage/local.mjs +1 -1
  85. package/dist/storage/s3.d.mts +1 -1
  86. package/dist/storage/s3.mjs +1 -1
  87. package/dist/{tokens-DrB-W6Q-.mjs → tokens-DKHiCYCB.mjs} +1 -1
  88. package/dist/{tokens-DrB-W6Q-.mjs.map → tokens-DKHiCYCB.mjs.map} +1 -1
  89. package/dist/transaction-Cn2rjY78.mjs +28 -0
  90. package/dist/transaction-Cn2rjY78.mjs.map +1 -0
  91. package/dist/{transport-yxiQsi8I.mjs → transport-BtcQ-Z7T.mjs} +1 -1
  92. package/dist/{transport-yxiQsi8I.mjs.map → transport-BtcQ-Z7T.mjs.map} +1 -1
  93. package/dist/{transport-BFGblqwG.d.mts → transport-CKQA_G44.d.mts} +1 -1
  94. package/dist/{transport-BFGblqwG.d.mts.map → transport-CKQA_G44.d.mts.map} +1 -1
  95. package/dist/{types-DRjfYOEv.d.mts → types-B6BzlZxx.d.mts} +1 -1
  96. package/dist/{types-DRjfYOEv.d.mts.map → types-B6BzlZxx.d.mts.map} +1 -1
  97. package/dist/{types-CaKte3hR.d.mts → types-BYWYxLcp.d.mts} +10 -4
  98. package/dist/types-BYWYxLcp.d.mts.map +1 -0
  99. package/dist/{types-BbsYgi_R.d.mts → types-BmkQR1En.d.mts} +1 -1
  100. package/dist/{types-BbsYgi_R.d.mts.map → types-BmkQR1En.d.mts.map} +1 -1
  101. package/dist/{types-C1-PVaS_.d.mts → types-DNZpaCBk.d.mts} +1 -1
  102. package/dist/{types-C1-PVaS_.d.mts.map → types-DNZpaCBk.d.mts.map} +1 -1
  103. package/dist/{types-Bec-r_3_.mjs → types-Dz9_WMS6.mjs} +1 -1
  104. package/dist/{types-Bec-r_3_.mjs.map → types-Dz9_WMS6.mjs.map} +1 -1
  105. package/dist/{types-DPfzHnjW.d.mts → types-gLYVCXCQ.d.mts} +1 -1
  106. package/dist/{types-DPfzHnjW.d.mts.map → types-gLYVCXCQ.d.mts.map} +1 -1
  107. package/dist/{types-DuNbGKjF.mjs → types-xxCWI3j0.mjs} +1 -1
  108. package/dist/{types-DuNbGKjF.mjs.map → types-xxCWI3j0.mjs.map} +1 -1
  109. package/dist/{validate-bfg9OR6N.d.mts → validate-CcNRWH6I.d.mts} +4 -4
  110. package/dist/{validate-bfg9OR6N.d.mts.map → validate-CcNRWH6I.d.mts.map} +1 -1
  111. package/dist/{validate-CXnRKfJK.mjs → validate-DuZDIxfy.mjs} +2 -2
  112. package/dist/{validate-CXnRKfJK.mjs.map → validate-DuZDIxfy.mjs.map} +1 -1
  113. package/dist/version-DlTDRdpv.mjs +7 -0
  114. package/dist/{version-REAapfsU.mjs.map → version-DlTDRdpv.mjs.map} +1 -1
  115. package/package.json +7 -5
  116. package/src/api/handlers/content.ts +36 -25
  117. package/src/api/handlers/menus.ts +19 -16
  118. package/src/astro/integration/index.ts +2 -3
  119. package/src/astro/integration/runtime.ts +8 -14
  120. package/src/astro/integration/vite-config.ts +7 -0
  121. package/src/astro/middleware/redirect.ts +30 -15
  122. package/src/astro/middleware.ts +11 -19
  123. package/src/astro/routes/api/admin/bylines/[id]/index.ts +3 -0
  124. package/src/astro/routes/api/admin/bylines/index.ts +2 -0
  125. package/src/astro/routes/api/redirects/[id].ts +3 -0
  126. package/src/astro/routes/api/redirects/index.ts +2 -0
  127. package/src/astro/routes/api/schema/collections/[slug]/index.ts +2 -0
  128. package/src/astro/routes/api/schema/collections/index.ts +1 -0
  129. package/src/bylines/index.ts +48 -0
  130. package/src/cleanup.ts +3 -3
  131. package/src/cli/commands/bundle-utils.ts +5 -5
  132. package/src/database/migrations/011_sections.ts +2 -2
  133. package/src/database/migrations/runner.ts +23 -2
  134. package/src/database/repositories/byline.ts +2 -1
  135. package/src/emdash-runtime.ts +18 -8
  136. package/src/index.ts +2 -0
  137. package/src/mcp/server.ts +40 -67
  138. package/src/plugins/context.ts +28 -4
  139. package/src/plugins/cron.ts +29 -4
  140. package/src/plugins/hooks.ts +22 -10
  141. package/src/plugins/index.ts +1 -0
  142. package/src/plugins/manager.ts +6 -2
  143. package/src/plugins/marketplace.ts +33 -3
  144. package/src/plugins/routes.ts +3 -3
  145. package/src/plugins/types.ts +7 -0
  146. package/src/query.ts +37 -14
  147. package/src/redirects/cache.ts +68 -0
  148. package/src/search/fts-manager.ts +20 -11
  149. package/src/search/query.ts +8 -9
  150. package/src/seed/apply.ts +49 -28
  151. package/src/visual-editing/toolbar.ts +11 -1
  152. package/dist/apply-Bqoekfbe.mjs.map +0 -1
  153. package/dist/byline-BGj9p9Ht.mjs.map +0 -1
  154. package/dist/bylines-BihaoIDY.mjs.map +0 -1
  155. package/dist/index-Cff7AimE.d.mts.map +0 -1
  156. package/dist/redirect-DUAk-Yl_.mjs.map +0 -1
  157. package/dist/registry-DU18yVo0.mjs.map +0 -1
  158. package/dist/runner-Biufrii2.mjs.map +0 -1
  159. package/dist/runner-EAtf0ZIe.d.mts.map +0 -1
  160. package/dist/search-BXB-jfu2.mjs.map +0 -1
  161. package/dist/types-CaKte3hR.d.mts.map +0 -1
  162. package/dist/version-REAapfsU.mjs +0 -7
@@ -1,4 +1,4 @@
1
- import { t as Database } from "./types-DRjfYOEv.mjs";
1
+ import { t as Database } from "./types-B6BzlZxx.mjs";
2
2
  import { Kysely } from "kysely";
3
3
 
4
4
  //#region src/database/migrations/runner.d.ts
@@ -11,7 +11,14 @@ interface MigrationStatus {
11
11
  */
12
12
  declare function getMigrationStatus(db: Kysely<Database>): Promise<MigrationStatus>;
13
13
  /**
14
- * Run all pending migrations
14
+ * Run all pending migrations.
15
+ *
16
+ * Includes a fast-path: if the migration table already exists and contains
17
+ * exactly MIGRATION_COUNT rows, all migrations have been applied and we can
18
+ * skip the Kysely Migrator entirely. This avoids the expensive
19
+ * `pragma_table_info` introspection that Kysely runs for every table in the
20
+ * database (twice!) just to check if the migration tables exist.
21
+ * On D1 with ~57 tables, that's ~116 queries saved per init.
15
22
  */
16
23
  declare function runMigrations(db: Kysely<Database>): Promise<{
17
24
  applied: string[];
@@ -24,4 +31,4 @@ declare function rollbackMigration(db: Kysely<Database>): Promise<{
24
31
  }>;
25
32
  //#endregion
26
33
  export { runMigrations as i, getMigrationStatus as n, rollbackMigration as r, MigrationStatus as t };
27
- //# sourceMappingURL=runner-EAtf0ZIe.d.mts.map
34
+ //# sourceMappingURL=runner-DYv3rX8P.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-DYv3rX8P.d.mts","names":[],"sources":["../src/database/migrations/runner.ts"],"mappings":";;;;UAuFiB,eAAA;EAChB,OAAA;EACA,OAAA;AAAA;;AAUD;;iBAAsB,kBAAA,CAAmB,EAAA,EAAI,MAAA,CAAO,QAAA,IAAY,OAAA,CAAQ,eAAA;;;;;;;;;;;iBAkClD,aAAA,CAAc,EAAA,EAAI,MAAA,CAAO,QAAA,IAAY,OAAA;EAAU,OAAA;AAAA;AAArE;;;AAAA,iBA8CsB,iBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,IACT,OAAA;EAAU,UAAA;AAAA"}
@@ -1,9 +1,9 @@
1
- import { f as MediaProvider } from "./placeholder-SvFCKbz_.mjs";
2
- import "./types-DRjfYOEv.mjs";
3
- import { Fn as getDb, Mn as EntryData, Nn as EntryFilter, Pn as emdashLoader, jn as CollectionFilter } from "./index-Cff7AimE.mjs";
4
- import "./runner-EAtf0ZIe.mjs";
5
- import "./types-CaKte3hR.mjs";
6
- import "./validate-bfg9OR6N.mjs";
1
+ import { f as MediaProvider } from "./placeholder-BBCtpTES.mjs";
2
+ import "./types-B6BzlZxx.mjs";
3
+ import { Fn as getDb, Mn as EntryData, Nn as EntryFilter, Pn as emdashLoader, jn as CollectionFilter } from "./index-CRg3PWfZ.mjs";
4
+ import "./runner-DYv3rX8P.mjs";
5
+ import "./types-BYWYxLcp.mjs";
6
+ import "./validate-CcNRWH6I.mjs";
7
7
 
8
8
  //#region src/media/provider-loader.d.ts
9
9
  /**
package/dist/runtime.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./dialect-helpers-DhTzaUxP.mjs";
2
2
  import "./base64-MBPo9ozB.mjs";
3
3
  import "./types-CMMN0pNg.mjs";
4
- import { n as getDb, t as emdashLoader } from "./loader-BmYdf3Dr.mjs";
4
+ import { n as getDb, t as emdashLoader } from "./loader-BYzwzORf.mjs";
5
5
 
6
6
  //#region src/media/provider-loader.ts
7
7
  let virtualMediaProviders;
@@ -4,14 +4,16 @@ import { a as slugify, r as RevisionRepository, t as ContentRepository } from ".
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-Bqoekfbe.mjs";
8
- import { a as withTransaction, i as FTSManager, n as SchemaRegistry } from "./registry-DU18yVo0.mjs";
9
- import { t as RedirectRepository } from "./redirect-DUAk-Yl_.mjs";
10
- import { n as SQL_BATCH_SIZE, r as chunks, t as BylineRepository } from "./byline-BGj9p9Ht.mjs";
11
- import { r as isI18nEnabled } from "./config-Cq8H0SfX.mjs";
12
- import { n as getDb } from "./loader-BmYdf3Dr.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,
@@ -5131,12 +5128,31 @@ var CronExecutor = class {
5131
5128
  hookFailed = true;
5132
5129
  console.error(`[cron] Hook failed for ${task.plugin_id}:${task.task_name}:`, error);
5133
5130
  }
5134
- 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`
5135
5144
  UPDATE _emdash_cron_tasks
5136
- 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
+ })}
5137
5152
  WHERE id = ${task.id}
5138
5153
  `.execute(this.db);
5139
- else await sql`
5154
+ }
5155
+ } else await sql`
5140
5156
  DELETE FROM _emdash_cron_tasks WHERE id = ${task.id}
5141
5157
  `.execute(this.db);
5142
5158
  else await sql`
@@ -5400,9 +5416,13 @@ function createContentAccess(db) {
5400
5416
  const result = {
5401
5417
  id: item.id,
5402
5418
  type: item.type,
5419
+ slug: item.slug,
5420
+ status: item.status,
5403
5421
  data: item.data,
5404
5422
  createdAt: item.createdAt,
5405
- updatedAt: item.updatedAt
5423
+ updatedAt: item.updatedAt,
5424
+ locale: item.locale,
5425
+ publishedAt: item.publishedAt
5406
5426
  };
5407
5427
  if (await seoRepo.isEnabled(collection)) result.seo = await seoRepo.get(collection, item.id);
5408
5428
  return result;
@@ -5424,9 +5444,13 @@ function createContentAccess(db) {
5424
5444
  const items = result.items.map((item) => ({
5425
5445
  id: item.id,
5426
5446
  type: item.type,
5447
+ slug: item.slug,
5448
+ status: item.status,
5427
5449
  data: item.data,
5428
5450
  createdAt: item.createdAt,
5429
- updatedAt: item.updatedAt
5451
+ updatedAt: item.updatedAt,
5452
+ locale: item.locale,
5453
+ publishedAt: item.publishedAt
5430
5454
  }));
5431
5455
  if (items.length > 0 && await seoRepo.isEnabled(collection)) {
5432
5456
  const seoMap = await seoRepo.getMany(collection, items.map((i) => i.id));
@@ -5468,9 +5492,13 @@ function createContentAccessWithWrite(db) {
5468
5492
  const result = {
5469
5493
  id: item.id,
5470
5494
  type: item.type,
5495
+ slug: item.slug,
5496
+ status: item.status,
5471
5497
  data: item.data,
5472
5498
  createdAt: item.createdAt,
5473
- updatedAt: item.updatedAt
5499
+ updatedAt: item.updatedAt,
5500
+ locale: item.locale,
5501
+ publishedAt: item.publishedAt
5474
5502
  };
5475
5503
  if (hasSeo) result.seo = seo !== void 0 ? await trxSeoRepo.upsert(collection, item.id, seo) : await trxSeoRepo.get(collection, item.id);
5476
5504
  return result;
@@ -5490,9 +5518,13 @@ function createContentAccessWithWrite(db) {
5490
5518
  const result = {
5491
5519
  id: item.id,
5492
5520
  type: item.type,
5521
+ slug: item.slug,
5522
+ status: item.status,
5493
5523
  data: item.data,
5494
5524
  createdAt: item.createdAt,
5495
- updatedAt: item.updatedAt
5525
+ updatedAt: item.updatedAt,
5526
+ locale: item.locale,
5527
+ publishedAt: item.publishedAt
5496
5528
  };
5497
5529
  if (hasSeo) result.seo = seo !== void 0 ? await trxSeoRepo.upsert(collection, item.id, seo) : await trxSeoRepo.get(collection, item.id);
5498
5530
  return result;
@@ -5553,17 +5585,18 @@ function createMediaAccessWithWrite(db, getUploadUrlFn, storage) {
5553
5585
  getUploadUrl: getUploadUrlFn,
5554
5586
  async upload(filename, contentType, bytes) {
5555
5587
  if (!storage) throw new Error("Media upload() requires a storage backend. Configure storage in PluginContextFactoryOptions.");
5556
- const mediaId = ulid();
5588
+ const keyPrefix = ulid();
5557
5589
  const basename = filename.split("/").pop() ?? filename;
5558
5590
  const dotIdx = basename.lastIndexOf(".");
5559
- const storageKey = `${mediaId}${dotIdx > 0 ? basename.slice(dotIdx).toLowerCase() : ""}`;
5591
+ const storageKey = `${keyPrefix}${dotIdx > 0 ? basename.slice(dotIdx).toLowerCase() : ""}`;
5560
5592
  await storage.upload({
5561
5593
  key: storageKey,
5562
5594
  body: new Uint8Array(bytes),
5563
5595
  contentType
5564
5596
  });
5597
+ let media;
5565
5598
  try {
5566
- await mediaRepo.create({
5599
+ media = await mediaRepo.create({
5567
5600
  filename: basename,
5568
5601
  mimeType: contentType,
5569
5602
  size: bytes.byteLength,
@@ -5577,7 +5610,7 @@ function createMediaAccessWithWrite(db, getUploadUrlFn, storage) {
5577
5610
  throw error;
5578
5611
  }
5579
5612
  return {
5580
- mediaId,
5613
+ mediaId: media.id,
5581
5614
  storageKey,
5582
5615
  url: `/_emdash/api/media/file/${storageKey}`
5583
5616
  };
@@ -5589,6 +5622,11 @@ function createMediaAccessWithWrite(db, getUploadUrlFn, storage) {
5589
5622
  }
5590
5623
  /** Maximum number of redirects to follow in plugin HTTP access */
5591
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
+ */
5592
5630
  function isHostAllowed(host, allowedHosts) {
5593
5631
  return allowedHosts.some((pattern) => {
5594
5632
  if (pattern === "*") return true;
@@ -5986,6 +6024,8 @@ var HookPipeline = class HookPipeline {
5986
6024
  while (remaining.length > 0) {
5987
6025
  const ready = remaining.filter((hook) => hook.dependencies.every((dep) => sorted.some((s) => s.pluginId === dep)));
5988
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.`);
5989
6029
  remaining.sort((a, b) => a.priority - b.priority);
5990
6030
  sorted.push(...remaining);
5991
6031
  break;
@@ -6001,7 +6041,13 @@ var HookPipeline = class HookPipeline {
6001
6041
  * Execute a hook with timeout
6002
6042
  */
6003
6043
  async executeWithTimeout(fn, timeout) {
6004
- 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
+ }
6005
6051
  }
6006
6052
  /**
6007
6053
  * Run plugin:install hooks
@@ -6163,7 +6209,8 @@ var HookPipeline = class HookPipeline {
6163
6209
  const { handler } = hook;
6164
6210
  const event = {
6165
6211
  id,
6166
- collection
6212
+ collection,
6213
+ permanent: false
6167
6214
  };
6168
6215
  const ctx = this.getContext(hook.pluginId);
6169
6216
  const start = Date.now();
@@ -6194,14 +6241,15 @@ var HookPipeline = class HookPipeline {
6194
6241
  /**
6195
6242
  * Run content:afterDelete hooks
6196
6243
  */
6197
- async runContentAfterDelete(id, collection) {
6244
+ async runContentAfterDelete(id, collection, permanent) {
6198
6245
  const hooks = this.getTypedHooks("content:afterDelete");
6199
6246
  const results = [];
6200
6247
  for (const hook of hooks) {
6201
6248
  const { handler } = hook;
6202
6249
  const event = {
6203
6250
  id,
6204
- collection
6251
+ collection,
6252
+ permanent
6205
6253
  };
6206
6254
  const ctx = this.getContext(hook.pluginId);
6207
6255
  const start = Date.now();
@@ -6969,11 +7017,12 @@ var PluginRouteHandler = class {
6969
7017
  },
6970
7018
  status: error.status
6971
7019
  };
7020
+ console.error(`[plugin:${this.plugin.id}] Route handler failed:`, error);
6972
7021
  return {
6973
7022
  success: false,
6974
7023
  error: {
6975
7024
  code: "INTERNAL_ERROR",
6976
- message: `Route handler failed: ${error instanceof Error ? error.message : String(error)}`
7025
+ message: "An internal error occurred"
6977
7026
  },
6978
7027
  status: 500
6979
7028
  };
@@ -7258,9 +7307,9 @@ var PluginManager = class {
7258
7307
  /**
7259
7308
  * Run content:afterDelete hooks across all active plugins
7260
7309
  */
7261
- async runContentAfterDelete(id, collection) {
7310
+ async runContentAfterDelete(id, collection, permanent) {
7262
7311
  this.ensureInitialized();
7263
- return this.hookPipeline.runContentAfterDelete(id, collection);
7312
+ return this.hookPipeline.runContentAfterDelete(id, collection, permanent);
7264
7313
  }
7265
7314
  /**
7266
7315
  * Run content:afterPublish hooks across all active plugins
@@ -8938,7 +8987,7 @@ async function getTermsForEntries(collection, entryIds, taxonomyName) {
8938
8987
  * Get entries by term (wraps getEmDashCollection)
8939
8988
  */
8940
8989
  async function getEntriesByTerm(collection, taxonomyName, termSlug) {
8941
- const { getEmDashCollection } = await import("./query-sesiOndV.mjs").then((n) => n.a);
8990
+ const { getEmDashCollection } = await import("./query-B6Vu0d2i.mjs").then((n) => n.o);
8942
8991
  const { entries } = await getEmDashCollection(collection, { where: { [taxonomyName]: termSlug } });
8943
8992
  return entries;
8944
8993
  }
@@ -9344,9 +9393,9 @@ function escapeQuery(query) {
9344
9393
  if (!query || typeof query !== "string") return "";
9345
9394
  query = query.trim();
9346
9395
  if (query.length === 0) return "";
9396
+ if (query.startsWith("\"") && query.endsWith("\"") && query.length >= 2) return `"${query.slice(1, -1).replace(DOUBLE_QUOTE_PATTERN, "\"\"")}"`;
9347
9397
  const escaped = query.replace(DOUBLE_QUOTE_PATTERN, "\"\"");
9348
9398
  if (FTS_OPERATORS_PATTERN.test(query)) return escaped;
9349
- if (query.startsWith("\"") && query.endsWith("\"")) return query;
9350
9399
  const terms = escaped.split(WHITESPACE_SPLIT_PATTERN).filter((t) => t.length > 0);
9351
9400
  if (terms.length === 0) return "";
9352
9401
  return terms.map((t) => `"${t}"*`).join(" ");
@@ -9447,4 +9496,4 @@ function extractSearchableFields(entry, fields) {
9447
9496
 
9448
9497
  //#endregion
9449
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 };
9450
- //# sourceMappingURL=search-BXB-jfu2.mjs.map
9499
+ //# sourceMappingURL=search-B5p9D36n.mjs.map