@aphexcms/cms-core 9.6.0 → 9.7.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 (259) hide show
  1. package/dist/api/documents.d.ts +28 -0
  2. package/dist/api/documents.d.ts.map +1 -1
  3. package/dist/api/documents.js +18 -0
  4. package/dist/api/schemas/documents.d.ts +19 -0
  5. package/dist/api/schemas/documents.d.ts.map +1 -1
  6. package/dist/api/schemas/documents.js +18 -0
  7. package/dist/client/ui.d.ts +1 -0
  8. package/dist/client/ui.d.ts.map +1 -1
  9. package/dist/client/ui.js +2 -0
  10. package/dist/components/admin/ActivityView.svelte +227 -0
  11. package/dist/components/admin/ActivityView.svelte.d.ts +4 -0
  12. package/dist/components/admin/ActivityView.svelte.d.ts.map +1 -0
  13. package/dist/components/admin/DocumentEditor.svelte +196 -10
  14. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
  15. package/dist/components/admin/ScheduleDialog.svelte +174 -0
  16. package/dist/components/admin/ScheduleDialog.svelte.d.ts +20 -0
  17. package/dist/components/admin/ScheduleDialog.svelte.d.ts.map +1 -0
  18. package/dist/components/layout/OrganizationSwitcher.svelte +3 -3
  19. package/dist/components/layout/sidebar/NavUser.svelte +5 -5
  20. package/dist/db/interfaces/events.d.ts +55 -0
  21. package/dist/db/interfaces/events.d.ts.map +1 -0
  22. package/dist/db/interfaces/events.js +1 -0
  23. package/dist/db/interfaces/index.d.ts +10 -2
  24. package/dist/db/interfaces/index.d.ts.map +1 -1
  25. package/dist/db/interfaces/plugin-storage.d.ts +39 -0
  26. package/dist/db/interfaces/plugin-storage.d.ts.map +1 -0
  27. package/dist/db/interfaces/plugin-storage.js +1 -0
  28. package/dist/events/catalog.d.ts +8 -0
  29. package/dist/events/catalog.d.ts.map +1 -0
  30. package/dist/events/catalog.js +11 -0
  31. package/dist/events/consumer.d.ts +79 -0
  32. package/dist/events/consumer.d.ts.map +1 -0
  33. package/dist/events/consumer.js +76 -0
  34. package/dist/events/define-event.d.ts +17 -0
  35. package/dist/events/define-event.d.ts.map +1 -0
  36. package/dist/events/define-event.js +7 -0
  37. package/dist/events/emit.d.ts +11 -0
  38. package/dist/events/emit.d.ts.map +1 -0
  39. package/dist/events/emit.js +20 -0
  40. package/dist/events/index.d.ts +4 -0
  41. package/dist/events/index.d.ts.map +1 -0
  42. package/dist/events/index.js +3 -0
  43. package/dist/field-validation/rule.d.ts.map +1 -1
  44. package/dist/field-validation/rule.js +15 -0
  45. package/dist/forms/define-form.d.ts +25 -0
  46. package/dist/forms/define-form.d.ts.map +1 -0
  47. package/dist/forms/define-form.js +22 -0
  48. package/dist/forms/index.d.ts +4 -0
  49. package/dist/forms/index.d.ts.map +1 -0
  50. package/dist/forms/index.js +5 -0
  51. package/dist/forms/types.d.ts +49 -0
  52. package/dist/forms/types.d.ts.map +1 -0
  53. package/dist/forms/types.js +1 -0
  54. package/dist/forms/validate.d.ts +9 -0
  55. package/dist/forms/validate.d.ts.map +1 -0
  56. package/dist/forms/validate.js +18 -0
  57. package/dist/hooks.d.ts.map +1 -1
  58. package/dist/hooks.js +15 -0
  59. package/dist/index.d.ts +2 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +6 -0
  62. package/dist/jobs/document-jobs.d.ts +27 -0
  63. package/dist/jobs/document-jobs.d.ts.map +1 -0
  64. package/dist/jobs/document-jobs.js +42 -0
  65. package/dist/jobs/embedded-runner.d.ts +25 -0
  66. package/dist/jobs/embedded-runner.d.ts.map +1 -0
  67. package/dist/jobs/embedded-runner.js +46 -0
  68. package/dist/jobs/index.d.ts +7 -0
  69. package/dist/jobs/index.d.ts.map +1 -0
  70. package/dist/jobs/index.js +8 -0
  71. package/dist/jobs/relay.d.ts +27 -0
  72. package/dist/jobs/relay.d.ts.map +1 -0
  73. package/dist/jobs/relay.js +60 -0
  74. package/dist/jobs/run-batch.d.ts +52 -0
  75. package/dist/jobs/run-batch.d.ts.map +1 -0
  76. package/dist/jobs/run-batch.js +61 -0
  77. package/dist/jobs/run-due-jobs.d.ts +30 -0
  78. package/dist/jobs/run-due-jobs.d.ts.map +1 -0
  79. package/dist/jobs/run-due-jobs.js +69 -0
  80. package/dist/jobs/types.d.ts +18 -0
  81. package/dist/jobs/types.d.ts.map +1 -0
  82. package/dist/jobs/types.js +1 -0
  83. package/dist/lib/api/documents.d.ts +28 -0
  84. package/dist/lib/api/documents.d.ts.map +1 -1
  85. package/dist/lib/api/documents.js +18 -0
  86. package/dist/lib/api/documents.js.map +1 -1
  87. package/dist/lib/api/schemas/documents.d.ts +19 -0
  88. package/dist/lib/api/schemas/documents.d.ts.map +1 -1
  89. package/dist/lib/api/schemas/documents.js +18 -0
  90. package/dist/lib/api/schemas/documents.js.map +1 -1
  91. package/dist/lib/client/ui.d.ts +1 -0
  92. package/dist/lib/client/ui.d.ts.map +1 -1
  93. package/dist/lib/client/ui.js +2 -0
  94. package/dist/lib/client/ui.js.map +1 -1
  95. package/dist/lib/db/interfaces/events.d.ts +55 -0
  96. package/dist/lib/db/interfaces/events.d.ts.map +1 -0
  97. package/dist/lib/db/interfaces/events.js +2 -0
  98. package/dist/lib/db/interfaces/events.js.map +1 -0
  99. package/dist/lib/db/interfaces/index.d.ts +10 -2
  100. package/dist/lib/db/interfaces/index.d.ts.map +1 -1
  101. package/dist/lib/db/interfaces/plugin-storage.d.ts +39 -0
  102. package/dist/lib/db/interfaces/plugin-storage.d.ts.map +1 -0
  103. package/dist/lib/db/interfaces/plugin-storage.js +2 -0
  104. package/dist/lib/db/interfaces/plugin-storage.js.map +1 -0
  105. package/dist/lib/events/catalog.d.ts +8 -0
  106. package/dist/lib/events/catalog.d.ts.map +1 -0
  107. package/dist/lib/events/catalog.js +12 -0
  108. package/dist/lib/events/catalog.js.map +1 -0
  109. package/dist/lib/events/consumer.d.ts +79 -0
  110. package/dist/lib/events/consumer.d.ts.map +1 -0
  111. package/dist/lib/events/consumer.js +77 -0
  112. package/dist/lib/events/consumer.js.map +1 -0
  113. package/dist/lib/events/define-event.d.ts +17 -0
  114. package/dist/lib/events/define-event.d.ts.map +1 -0
  115. package/dist/lib/events/define-event.js +8 -0
  116. package/dist/lib/events/define-event.js.map +1 -0
  117. package/dist/lib/events/emit.d.ts +11 -0
  118. package/dist/lib/events/emit.d.ts.map +1 -0
  119. package/dist/lib/events/emit.js +21 -0
  120. package/dist/lib/events/emit.js.map +1 -0
  121. package/dist/lib/events/index.d.ts +4 -0
  122. package/dist/lib/events/index.d.ts.map +1 -0
  123. package/dist/lib/events/index.js +4 -0
  124. package/dist/lib/events/index.js.map +1 -0
  125. package/dist/lib/field-validation/rule.d.ts.map +1 -1
  126. package/dist/lib/field-validation/rule.js +15 -0
  127. package/dist/lib/field-validation/rule.js.map +1 -1
  128. package/dist/lib/forms/define-form.d.ts +25 -0
  129. package/dist/lib/forms/define-form.d.ts.map +1 -0
  130. package/dist/lib/forms/define-form.js +23 -0
  131. package/dist/lib/forms/define-form.js.map +1 -0
  132. package/dist/lib/forms/index.d.ts +4 -0
  133. package/dist/lib/forms/index.d.ts.map +1 -0
  134. package/dist/lib/forms/index.js +6 -0
  135. package/dist/lib/forms/index.js.map +1 -0
  136. package/dist/lib/forms/types.d.ts +49 -0
  137. package/dist/lib/forms/types.d.ts.map +1 -0
  138. package/dist/lib/forms/types.js +2 -0
  139. package/dist/lib/forms/types.js.map +1 -0
  140. package/dist/lib/forms/validate.d.ts +9 -0
  141. package/dist/lib/forms/validate.d.ts.map +1 -0
  142. package/dist/lib/forms/validate.js +19 -0
  143. package/dist/lib/forms/validate.js.map +1 -0
  144. package/dist/lib/hooks.d.ts.map +1 -1
  145. package/dist/lib/hooks.js +15 -0
  146. package/dist/lib/hooks.js.map +1 -1
  147. package/dist/lib/index.d.ts +2 -0
  148. package/dist/lib/index.d.ts.map +1 -1
  149. package/dist/lib/index.js +6 -0
  150. package/dist/lib/index.js.map +1 -1
  151. package/dist/lib/jobs/document-jobs.d.ts +27 -0
  152. package/dist/lib/jobs/document-jobs.d.ts.map +1 -0
  153. package/dist/lib/jobs/document-jobs.js +43 -0
  154. package/dist/lib/jobs/document-jobs.js.map +1 -0
  155. package/dist/lib/jobs/embedded-runner.d.ts +25 -0
  156. package/dist/lib/jobs/embedded-runner.d.ts.map +1 -0
  157. package/dist/lib/jobs/embedded-runner.js +47 -0
  158. package/dist/lib/jobs/embedded-runner.js.map +1 -0
  159. package/dist/lib/jobs/index.d.ts +7 -0
  160. package/dist/lib/jobs/index.d.ts.map +1 -0
  161. package/dist/lib/jobs/index.js +9 -0
  162. package/dist/lib/jobs/index.js.map +1 -0
  163. package/dist/lib/jobs/relay.d.ts +27 -0
  164. package/dist/lib/jobs/relay.d.ts.map +1 -0
  165. package/dist/lib/jobs/relay.js +61 -0
  166. package/dist/lib/jobs/relay.js.map +1 -0
  167. package/dist/lib/jobs/run-batch.d.ts +52 -0
  168. package/dist/lib/jobs/run-batch.d.ts.map +1 -0
  169. package/dist/lib/jobs/run-batch.js +62 -0
  170. package/dist/lib/jobs/run-batch.js.map +1 -0
  171. package/dist/lib/jobs/run-due-jobs.d.ts +30 -0
  172. package/dist/lib/jobs/run-due-jobs.d.ts.map +1 -0
  173. package/dist/lib/jobs/run-due-jobs.js +70 -0
  174. package/dist/lib/jobs/run-due-jobs.js.map +1 -0
  175. package/dist/lib/jobs/types.d.ts +18 -0
  176. package/dist/lib/jobs/types.d.ts.map +1 -0
  177. package/dist/lib/jobs/types.js +2 -0
  178. package/dist/lib/jobs/types.js.map +1 -0
  179. package/dist/lib/local-api/collection-api.d.ts +42 -0
  180. package/dist/lib/local-api/collection-api.d.ts.map +1 -1
  181. package/dist/lib/local-api/collection-api.js +179 -20
  182. package/dist/lib/local-api/collection-api.js.map +1 -1
  183. package/dist/lib/plugins/resolver.d.ts +12 -1
  184. package/dist/lib/plugins/resolver.d.ts.map +1 -1
  185. package/dist/lib/plugins/resolver.js +4 -1
  186. package/dist/lib/plugins/resolver.js.map +1 -1
  187. package/dist/lib/plugins/types.d.ts +47 -2
  188. package/dist/lib/plugins/types.d.ts.map +1 -1
  189. package/dist/lib/plugins/types.js.map +1 -1
  190. package/dist/lib/server/api/index.d.ts.map +1 -1
  191. package/dist/lib/server/api/index.js +8 -0
  192. package/dist/lib/server/api/index.js.map +1 -1
  193. package/dist/lib/server/api/routes/documents-publish.d.ts.map +1 -1
  194. package/dist/lib/server/api/routes/documents-publish.js +110 -0
  195. package/dist/lib/server/api/routes/documents-publish.js.map +1 -1
  196. package/dist/lib/server/api/routes/jobs.d.ts +4 -0
  197. package/dist/lib/server/api/routes/jobs.d.ts.map +1 -0
  198. package/dist/lib/server/api/routes/jobs.js +93 -0
  199. package/dist/lib/server/api/routes/jobs.js.map +1 -0
  200. package/dist/lib/server/api/routes/workers-run.d.ts +4 -0
  201. package/dist/lib/server/api/routes/workers-run.d.ts.map +1 -0
  202. package/dist/lib/server/api/routes/workers-run.js +41 -0
  203. package/dist/lib/server/api/routes/workers-run.js.map +1 -0
  204. package/dist/lib/server/index.d.ts +1 -0
  205. package/dist/lib/server/index.d.ts.map +1 -1
  206. package/dist/lib/server/index.js +2 -0
  207. package/dist/lib/server/index.js.map +1 -1
  208. package/dist/lib/services/version-service.d.ts +16 -0
  209. package/dist/lib/services/version-service.d.ts.map +1 -1
  210. package/dist/lib/services/version-service.js +68 -66
  211. package/dist/lib/services/version-service.js.map +1 -1
  212. package/dist/lib/types/config.d.ts +43 -0
  213. package/dist/lib/types/config.d.ts.map +1 -1
  214. package/dist/lib/types/events.d.ts +134 -0
  215. package/dist/lib/types/events.d.ts.map +1 -0
  216. package/dist/lib/types/events.js +4 -0
  217. package/dist/lib/types/events.js.map +1 -0
  218. package/dist/lib/types/index.d.ts +1 -0
  219. package/dist/lib/types/index.d.ts.map +1 -1
  220. package/dist/lib/types/index.js +1 -0
  221. package/dist/lib/types/index.js.map +1 -1
  222. package/dist/lib/utils/index.d.ts +1 -1
  223. package/dist/lib/utils/index.d.ts.map +1 -1
  224. package/dist/lib/utils/index.js.map +1 -1
  225. package/dist/local-api/collection-api.d.ts +42 -0
  226. package/dist/local-api/collection-api.d.ts.map +1 -1
  227. package/dist/local-api/collection-api.js +179 -20
  228. package/dist/plugins/resolver.d.ts +12 -1
  229. package/dist/plugins/resolver.d.ts.map +1 -1
  230. package/dist/plugins/resolver.js +4 -1
  231. package/dist/plugins/types.d.ts +47 -2
  232. package/dist/plugins/types.d.ts.map +1 -1
  233. package/dist/server/api/index.d.ts.map +1 -1
  234. package/dist/server/api/index.js +8 -0
  235. package/dist/server/api/routes/documents-publish.d.ts.map +1 -1
  236. package/dist/server/api/routes/documents-publish.js +110 -0
  237. package/dist/server/api/routes/jobs.d.ts +4 -0
  238. package/dist/server/api/routes/jobs.d.ts.map +1 -0
  239. package/dist/server/api/routes/jobs.js +92 -0
  240. package/dist/server/api/routes/workers-run.d.ts +4 -0
  241. package/dist/server/api/routes/workers-run.d.ts.map +1 -0
  242. package/dist/server/api/routes/workers-run.js +40 -0
  243. package/dist/server/index.d.ts +1 -0
  244. package/dist/server/index.d.ts.map +1 -1
  245. package/dist/server/index.js +2 -0
  246. package/dist/services/version-service.d.ts +16 -0
  247. package/dist/services/version-service.d.ts.map +1 -1
  248. package/dist/services/version-service.js +68 -66
  249. package/dist/types/config.d.ts +43 -0
  250. package/dist/types/config.d.ts.map +1 -1
  251. package/dist/types/events.d.ts +134 -0
  252. package/dist/types/events.d.ts.map +1 -0
  253. package/dist/types/events.js +3 -0
  254. package/dist/types/index.d.ts +1 -0
  255. package/dist/types/index.d.ts.map +1 -1
  256. package/dist/types/index.js +1 -0
  257. package/dist/utils/index.d.ts +1 -1
  258. package/dist/utils/index.d.ts.map +1 -1
  259. package/package.json +2 -1
@@ -11,6 +11,7 @@ import type { Logger } from '../utils/logger.js';
11
11
  import type { CMSPlugin } from '../plugins/types.js';
12
12
  import type { Auth } from './auth.js';
13
13
  import type { PreviewPerspective } from '../preview/perspective.js';
14
+ import type { JobHandlerMap } from '../jobs/types.js';
14
15
  export type { GraphQLConfig };
15
16
  export interface CMSConfig {
16
17
  schemaTypes: SchemaType[];
@@ -77,6 +78,48 @@ export interface CMSConfig {
77
78
  versioning?: {
78
79
  maxVersions?: number;
79
80
  };
81
+ /**
82
+ * Background job execution (the DB-backed job queue). Jobs are scheduled via the
83
+ * database adapter (`scheduleJob`) and driven by `runDueJobs`, which the protected
84
+ * worker endpoint (`POST /api/internal/workers/run`) and any self-hosted worker loop
85
+ * both call. Events and scheduling work without this; only *executing* jobs needs it.
86
+ */
87
+ jobs?: {
88
+ /**
89
+ * Map of job `type` → handler. A claimed job whose type has no handler is
90
+ * dead-lettered (an unknown type can never become runnable).
91
+ */
92
+ handlers?: JobHandlerMap;
93
+ /**
94
+ * Shared secret authorizing `POST /api/internal/workers/run`
95
+ * (`Authorization: Bearer <secret>`). Machine-to-machine — platform cron or a
96
+ * self-hosted worker loop, NOT the user capability system. When unset the endpoint
97
+ * is disabled (404), so it's never an unauthenticated surface by default; jobs can
98
+ * still be scheduled, they just won't run until a secret (and a driver) exist.
99
+ */
100
+ workerSecret?: string;
101
+ /** Max jobs claimed per worker run. Default 10. */
102
+ batchSize?: number;
103
+ /** Lease duration (ms) per claimed job before it's reclaimable. Default 30000. */
104
+ leaseMs?: number;
105
+ /**
106
+ * Max outbox rows the relay drains per worker run (events fanned out to consumer
107
+ * delivery jobs). Runs at the top of the same tick as job execution. Default 100 —
108
+ * fan-out is cheap (a few inserts), so it can clear more per pass than it executes.
109
+ */
110
+ relayBatchSize?: number;
111
+ /**
112
+ * Run an in-process job loop inside the app itself — no separate worker process, no
113
+ * `workerSecret`, no cron. It calls `runJobsBatch` directly on `embeddedIntervalMs`, so
114
+ * scheduled publishes and event consumers "just work" the moment the app is running. Ideal
115
+ * for local dev and single-instance self-hosting; for horizontally-scaled prod prefer the
116
+ * dedicated worker loop / platform cron so N app replicas don't each run a loop. Ticks never
117
+ * overlap (a slow tick is skipped, not stacked). Default off.
118
+ */
119
+ embedded?: boolean;
120
+ /** Interval (ms) between embedded loop ticks when `embedded` is on. Default 3000. */
121
+ embeddedIntervalMs?: number;
122
+ };
80
123
  /**
81
124
  * Live preview configuration.
82
125
  * - `stega`: Enable stega encoding in preview data pushes so field overlays work
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/lib/types/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,MAAM,WAAW,SAAS;IACzB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE;QACf,QAAQ,CAAC,EAAE;YACV,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,OAAO,CAAC,EAAE,MAAM,CAAC;SACjB,CAAC;QACF,KAAK,CAAC,EAAE;YACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;SAC/B,CAAC;KACF,CAAC;IACF;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACxD;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACN,QAAQ,EAAE,YAAY,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF;;;;OAIG;IACH,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE;QACZ,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;YAAE,IAAI,CAAC,EAAE,IAAI,CAAC;YAAC,GAAG,EAAE,GAAG,CAAA;SAAE,KAAK,kBAAkB,CAAC;KAC7E,CAAC;IACF,QAAQ,CAAC,EAAE;QACV;;;;WAIG;QACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B;;;;;;WAMG;QACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;CACpC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/lib/types/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * A durable business fact. Append-only: written once (typically inside the same
3
+ * transaction as the state change that caused it) and never mutated. Payload carries
4
+ * identifiers + intentional metadata only — never secrets or full document copies.
5
+ */
6
+ export interface DomainEvent {
7
+ id: string;
8
+ organizationId: string;
9
+ type: string;
10
+ payload: Record<string, unknown>;
11
+ correlationId: string | null;
12
+ causationId: string | null;
13
+ createdBy: string | null;
14
+ createdAt: Date;
15
+ }
16
+ /** Input to append a domain event. */
17
+ export interface AppendEventInput {
18
+ organizationId: string;
19
+ type: string;
20
+ payload?: Record<string, unknown>;
21
+ correlationId?: string | null;
22
+ causationId?: string | null;
23
+ createdBy?: string | null;
24
+ }
25
+ export type JobStatus = 'pending' | 'leased' | 'completed' | 'failed' | 'cancelled';
26
+ /** A command to run now or later. Lifecycle: pending → leased → completed/failed/cancelled. */
27
+ export interface Job {
28
+ id: string;
29
+ organizationId: string;
30
+ type: string;
31
+ payload: Record<string, unknown>;
32
+ status: JobStatus;
33
+ runAt: Date;
34
+ attempts: number;
35
+ maxAttempts: number;
36
+ leaseOwner: string | null;
37
+ leaseExpiresAt: Date | null;
38
+ lastError: string | null;
39
+ idempotencyKey: string | null;
40
+ correlationId: string | null;
41
+ causationId: string | null;
42
+ createdBy: string | null;
43
+ createdAt: Date;
44
+ updatedAt: Date;
45
+ completedAt: Date | null;
46
+ }
47
+ /** Input to schedule a job. `idempotencyKey` (unique per org) makes enqueue safe to retry. */
48
+ export interface ScheduleJobInput {
49
+ organizationId: string;
50
+ type: string;
51
+ payload?: Record<string, unknown>;
52
+ runAt?: Date;
53
+ maxAttempts?: number;
54
+ idempotencyKey?: string | null;
55
+ correlationId?: string | null;
56
+ causationId?: string | null;
57
+ createdBy?: string | null;
58
+ }
59
+ /**
60
+ * Options for claiming due jobs with a lease. Omit `organizationId` to claim across all
61
+ * orgs (worker context — runs with override access); provide it to scope to one tenant.
62
+ */
63
+ export interface ClaimJobsOptions {
64
+ organizationId?: string;
65
+ limit: number;
66
+ workerId: string;
67
+ leaseMs: number;
68
+ now?: Date;
69
+ }
70
+ /**
71
+ * An outbox worklist row — the relay's unit of work. Written in the SAME transaction as
72
+ * the `domain_events` row it mirrors (so it can never exist without its event, nor be
73
+ * missed if the event committed), then drained by the relay: for each subscribed consumer
74
+ * it enqueues one delivery job, then stamps `processedAt`.
75
+ *
76
+ * Kept SEPARATE from `cms_domain_events` on purpose. The event log is an immutable ledger;
77
+ * this is a mutable, prunable worklist. And the relay claims rows by `processedAt IS NULL`
78
+ * (status), never by log position — so an event whose transaction commits late (with an
79
+ * early timestamp) is still picked up, which a cursor-scan over the append-only log would
80
+ * silently skip. `eventType`/`payload` are denormalized here so the relay fans out from a
81
+ * single-table read with no join back to the event log.
82
+ */
83
+ export interface OutboxRow {
84
+ id: string;
85
+ organizationId: string;
86
+ /** The `cms_domain_events` row this mirrors (FK; the canonical, immutable copy). */
87
+ eventId: string;
88
+ eventType: string;
89
+ payload: Record<string, unknown>;
90
+ correlationId: string | null;
91
+ causationId: string | null;
92
+ createdBy: string | null;
93
+ createdAt: Date;
94
+ /** Null until the relay has fanned this event out to every subscriber. */
95
+ processedAt: Date | null;
96
+ }
97
+ /**
98
+ * Options for reading the relay's pending work. Omit `organizationId` to read across all
99
+ * orgs (worker context — runs with override access), mirroring `claimDueJobs`. No lease:
100
+ * the relay's only side effect is idempotent job enqueue (keyed per event×consumer), so two
101
+ * workers processing the same row can't double-deliver — the job's unique idempotency key
102
+ * absorbs the duplicate. Oldest-first, so events relay in roughly causal order.
103
+ */
104
+ export interface ListUnprocessedOutboxOptions {
105
+ organizationId?: string;
106
+ /** Max rows returned per relay pass. */
107
+ limit: number;
108
+ }
109
+ /** A page of rows plus the unfiltered total, for offset pagination in the admin history views. */
110
+ export interface Page<T> {
111
+ items: T[];
112
+ total: number;
113
+ limit: number;
114
+ offset: number;
115
+ }
116
+ /** Query for the read-only event history. Newest first. */
117
+ export interface ListEventsOptions {
118
+ organizationId: string;
119
+ /** Filter by exact event type (e.g. `document.published`). */
120
+ type?: string;
121
+ limit?: number;
122
+ offset?: number;
123
+ }
124
+ /** Query for the read-only job history. Newest first. */
125
+ export interface ListJobsOptions {
126
+ organizationId: string;
127
+ /** Filter by one status or several (e.g. only `failed`). */
128
+ status?: JobStatus | JobStatus[];
129
+ /** Filter by exact job type. */
130
+ type?: string;
131
+ limit?: number;
132
+ offset?: number;
133
+ }
134
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/lib/types/events.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;CAChB;AAED,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEpF,+FAA+F;AAC/F,MAAM,WAAW,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED,8FAA8F;AAC9F,MAAM,WAAW,gBAAgB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,IAAI,CAAC;CACX;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,SAAS;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;IAChB,0EAA0E;IAC1E,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,4BAA4B;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;CACd;AAED,kGAAkG;AAClG,MAAM,WAAW,IAAI,CAAC,CAAC;IACtB,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IACjC,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,4 @@
1
+ // Universal event + job types — dialect-agnostic shapes the adapters map their rows to.
2
+ // These mirror the `cms_domain_events` and `cms_jobs` tables but never depend on Drizzle.
3
+ export {};
4
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/lib/types/events.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,0FAA0F"}
@@ -10,4 +10,5 @@ export * from './capabilities.js';
10
10
  export * from './filters.js';
11
11
  export * from './instance.js';
12
12
  export * from './version.js';
13
+ export * from './events.js';
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
@@ -10,4 +10,5 @@ export * from './capabilities.js';
10
10
  export * from './filters.js';
11
11
  export * from './instance.js';
12
12
  export * from './version.js';
13
+ export * from './events.js';
13
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
@@ -1,4 +1,4 @@
1
- export { cmsLogger, setLogLevel, type LogLevel } from './logger.js';
1
+ export { cmsLogger, setLogLevel, type LogLevel, type Logger } from './logger.js';
2
2
  export * from '../schema-utils/index.js';
3
3
  export * from '../field-validation/utils.js';
4
4
  export * from './content-hash.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGjE,cAAc,uBAAuB,CAAC;AAGtC,cAAc,2BAA2B,CAAC;AAG1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAG9E,cAAc,uBAAuB,CAAC;AAGtC,cAAc,2BAA2B,CAAC;AAG1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,yBAAyB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/utils/index.ts"],"names":[],"mappings":"AAAA,sBAAsB;AAEtB,SAAS;AACT,OAAO,EAAE,SAAS,EAAE,WAAW,EAAiB,MAAM,UAAU,CAAC;AAEjE,mBAAmB;AACnB,cAAc,uBAAuB,CAAC;AAEtC,uBAAuB;AACvB,cAAc,2BAA2B,CAAC;AAE1C,kBAAkB;AAClB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/utils/index.ts"],"names":[],"mappings":"AAAA,sBAAsB;AAEtB,SAAS;AACT,OAAO,EAAE,SAAS,EAAE,WAAW,EAA8B,MAAM,UAAU,CAAC;AAE9E,mBAAmB;AACnB,cAAc,uBAAuB,CAAC;AAEtC,uBAAuB;AACvB,cAAc,2BAA2B,CAAC;AAE1C,kBAAkB;AAClB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,yBAAyB,CAAC"}
@@ -9,6 +9,7 @@ import type { LocalAPIContext } from './types.js';
9
9
  import type { SchemaType } from '../types/schemas.js';
10
10
  import { PermissionChecker } from './permissions.js';
11
11
  import { type DocumentValidationResult } from '../field-validation/utils.js';
12
+ import type { Job } from '../types/events.js';
12
13
  /**
13
14
  * Result from create/update operations that includes validation
14
15
  */
@@ -182,6 +183,13 @@ export declare class CollectionAPI<T = Document> {
182
183
  * );
183
184
  * ```
184
185
  */
186
+ /**
187
+ * Publish without a version snapshot — the branch taken when there's no version service or
188
+ * the caller passed `skipVersioning`. Still emits `document.published` (and its outbox row)
189
+ * atomically with the publish, so the domain fact fires on EVERY publish path, not only the
190
+ * versioned one. The versioned branch emits the same event from `versionService.publishTx`.
191
+ */
192
+ private publishWithoutVersion;
185
193
  publish(context: LocalAPIContext, id: string): Promise<T | null>;
186
194
  /**
187
195
  * Unpublish a document
@@ -195,5 +203,39 @@ export declare class CollectionAPI<T = Document> {
195
203
  * ```
196
204
  */
197
205
  unpublish(context: LocalAPIContext, id: string): Promise<T | null>;
206
+ /**
207
+ * Pending scheduled publish/unpublish jobs for one document. Scheduled jobs are few
208
+ * and `pending` is a small set, so filtering the org's pending jobs by documentId in
209
+ * memory is cheap and avoids a dialect-specific JSON query on the hot editor path.
210
+ */
211
+ private pendingScheduledFor;
212
+ /**
213
+ * Cancel any pending schedule of ONE direction for a document — used when a manual
214
+ * publish/unpublish supersedes a schedule of the same kind. Only same-direction is
215
+ * cancelled on purpose: "publish now, auto-unpublish Friday" and "unpublish now,
216
+ * republish Monday" are legitimate future transitions, so a manual publish leaves a
217
+ * pending unpublish (and vice versa) alone. Without this, the queued job would fire at
218
+ * `runAt` and re-run the same transition — re-emitting `document.published`/`unpublished`
219
+ * and firing every consumer a second time (duplicate notifications, webhooks, cache busts).
220
+ */
221
+ private cancelPendingScheduleOfType;
222
+ /**
223
+ * Schedule a publish for a future `runAt`. Enqueues a `document.publish` job; the
224
+ * worker runs `publish()` at that time (re-validating, guarding references, emitting
225
+ * `document.published`). The permission check happens NOW — you must be able to publish
226
+ * to schedule one — so an unauthorized caller can't queue work to run later as the system.
227
+ * Actual publish-time validation still runs then, so a doc that goes invalid before
228
+ * `runAt` simply fails/retries rather than publishing bad content.
229
+ *
230
+ * Replace semantics: any existing pending schedule for this document is cancelled first,
231
+ * so a document has at most one pending schedule (rescheduling can't double-publish).
232
+ */
233
+ schedulePublish(context: LocalAPIContext, id: string, runAt: Date): Promise<Job>;
234
+ /** Schedule an unpublish for a future `runAt`. Permission-checked now; replaces any existing pending schedule. */
235
+ scheduleUnpublish(context: LocalAPIContext, id: string, runAt: Date): Promise<Job>;
236
+ /** Pending scheduled publish/unpublish jobs for a document (read-gated) — for the editor's schedule indicator. */
237
+ getScheduled(context: LocalAPIContext, id: string): Promise<Job[]>;
238
+ /** Cancel all pending scheduled publish/unpublish jobs for a document. Returns how many were cancelled. */
239
+ cancelScheduled(context: LocalAPIContext, id: string): Promise<number>;
198
240
  }
199
241
  //# sourceMappingURL=collection-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collection-api.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/collection-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"collection-api.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/collection-api.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,8 @@ import { singletonId } from '../schema-utils/singleton.js';
5
5
  import { validateDocumentData } from '../field-validation/utils.js';
6
6
  import { runDocumentHooks } from './hooks.js';
7
7
  import { collectReferenceIds } from '../utils/reference-walk.js';
8
+ import { emitDocumentPublished } from '../events/emit.js';
9
+ import { DOCUMENT_PUBLISH_JOB, DOCUMENT_UNPUBLISH_JOB } from '../jobs/document-jobs.js';
8
10
  import { hiddenReadFields, hiddenWriteFields, dropLockedWrites, stripHiddenFields } from '../field-access.js';
9
11
  const EMPTY_SET = new Set();
10
12
  /**
@@ -346,24 +348,45 @@ export class CollectionAPI {
346
348
  throw new Error(`Cannot publish: validation errors - ${errorMessage}`);
347
349
  }
348
350
  }
349
- // Create document with normalized data (dates in ISO format)
350
- const document = await this.databaseAdapter.createDocument({
351
- organizationId: context.organizationId,
352
- type: this.collectionName,
353
- draftData: validationResult.normalizedData,
354
- createdBy: context.user?.id,
355
- id: options?.id
356
- });
357
- await this.syncReferences(context.organizationId, document.id, validationResult.normalizedData);
358
- // Create initial draft version
359
- if (this.versionService && !options?.skipVersioning) {
360
- await this.versionService.createVersion(this.databaseAdapter, context.organizationId, document.id, 'draft', validationResult.normalizedData, context.user?.id);
361
- }
362
- // Publish immediately if requested (validation already done above)
351
+ // versionService present unless the caller opted out of versioning; capturing it
352
+ // here narrows the type so the tx callbacks don't need non-null assertions.
353
+ const versionService = options?.skipVersioning ? undefined : this.versionService;
354
+ // Publish path: create + draft-version + publish + publish-version must commit
355
+ // together, or a crash between them leaves an inconsistent document (a draft that
356
+ // was meant to be published, or a published doc with no version row). Run all of
357
+ // it in one transaction. Validation already passed the block-check above, so we
358
+ // never enter the tx with invalid data.
363
359
  if (options?.publish) {
364
- const published = this.versionService && !options?.skipVersioning
365
- ? await this.versionService.publishWithVersion(this.databaseAdapter, context.organizationId, document.id)
366
- : await this.databaseAdapter.publishDoc(context.organizationId, document.id);
360
+ const { document, published } = await this.databaseAdapter.withTransaction(async (tx) => {
361
+ const document = await tx.createDocument({
362
+ organizationId: context.organizationId,
363
+ type: this.collectionName,
364
+ draftData: validationResult.normalizedData,
365
+ createdBy: context.user?.id,
366
+ id: options?.id
367
+ });
368
+ if (versionService) {
369
+ await versionService.snapshotTx(tx, context.organizationId, document.id, 'draft', validationResult.normalizedData, context.user?.id);
370
+ }
371
+ // Either branch emits `document.published` in this same tx: the versioned path
372
+ // from publishTx, the non-versioned path via emitDocumentPublished here.
373
+ let published;
374
+ if (versionService) {
375
+ published = await versionService.publishTx(tx, context.organizationId, document.id);
376
+ }
377
+ else {
378
+ published = await tx.publishDoc(context.organizationId, document.id);
379
+ if (published)
380
+ await emitDocumentPublished(tx, context.organizationId, published);
381
+ }
382
+ return { document, published };
383
+ });
384
+ // Post-commit side effects: ref index is best-effort, retention is one pass
385
+ // covering both the draft and publish snapshots written inside the tx.
386
+ await this.syncReferences(context.organizationId, document.id, validationResult.normalizedData);
387
+ if (versionService) {
388
+ await versionService.enforceRetentionFor(this.databaseAdapter, context.organizationId, document.id);
389
+ }
367
390
  if (published) {
368
391
  if (this.documentCache) {
369
392
  await this.documentCache.invalidateDocument(context.organizationId, document.id);
@@ -374,6 +397,27 @@ export class CollectionAPI {
374
397
  validation: validationResult
375
398
  };
376
399
  }
400
+ // publishDoc returned null (unexpected): fall back to returning the draft.
401
+ if (this.documentCache) {
402
+ await this.documentCache.invalidateCollection(context.organizationId, this.collectionName);
403
+ }
404
+ return {
405
+ document: transformDocument(document, 'draft'),
406
+ validation: validationResult
407
+ };
408
+ }
409
+ // Draft-only path.
410
+ const document = await this.databaseAdapter.createDocument({
411
+ organizationId: context.organizationId,
412
+ type: this.collectionName,
413
+ draftData: validationResult.normalizedData,
414
+ createdBy: context.user?.id,
415
+ id: options?.id
416
+ });
417
+ await this.syncReferences(context.organizationId, document.id, validationResult.normalizedData);
418
+ // Create initial draft version
419
+ if (versionService) {
420
+ await versionService.createVersion(this.databaseAdapter, context.organizationId, document.id, 'draft', validationResult.normalizedData, context.user?.id);
377
421
  }
378
422
  if (this.documentCache) {
379
423
  await this.documentCache.invalidateCollection(context.organizationId, this.collectionName);
@@ -454,7 +498,7 @@ export class CollectionAPI {
454
498
  }
455
499
  const published = this.versionService && !options?.skipVersioning
456
500
  ? await this.versionService.publishWithVersion(this.databaseAdapter, context.organizationId, document.id)
457
- : await this.databaseAdapter.publishDoc(context.organizationId, document.id);
501
+ : await this.publishWithoutVersion(context.organizationId, document.id);
458
502
  if (published) {
459
503
  // Invalidate cache
460
504
  if (this.documentCache) {
@@ -516,6 +560,20 @@ export class CollectionAPI {
516
560
  * );
517
561
  * ```
518
562
  */
563
+ /**
564
+ * Publish without a version snapshot — the branch taken when there's no version service or
565
+ * the caller passed `skipVersioning`. Still emits `document.published` (and its outbox row)
566
+ * atomically with the publish, so the domain fact fires on EVERY publish path, not only the
567
+ * versioned one. The versioned branch emits the same event from `versionService.publishTx`.
568
+ */
569
+ async publishWithoutVersion(organizationId, id) {
570
+ return this.databaseAdapter.withTransaction(async (tx) => {
571
+ const published = await tx.publishDoc(organizationId, id);
572
+ if (published)
573
+ await emitDocumentPublished(tx, organizationId, published);
574
+ return published;
575
+ });
576
+ }
519
577
  async publish(context, id) {
520
578
  // Fetch first so policies can inspect the target. Order matters here:
521
579
  // 404s beat 403s for missing docs.
@@ -549,10 +607,12 @@ export class CollectionAPI {
549
607
  throw new Error(`Cannot publish — ${unpublished.length} referenced document(s) are not published: ${names}`);
550
608
  }
551
609
  }
552
- // Validation passed - proceed with publish (with version if service available)
610
+ // Validation passed - proceed with publish (with version if service available). Either
611
+ // branch emits `document.published`: the versioned path from publishTx, the non-versioned
612
+ // path from publishWithoutVersion.
553
613
  const publishedDocument = this.versionService
554
614
  ? await this.versionService.publishWithVersion(this.databaseAdapter, context.organizationId, id)
555
- : await this.databaseAdapter.publishDoc(context.organizationId, id);
615
+ : await this.publishWithoutVersion(context.organizationId, id);
556
616
  if (!publishedDocument) {
557
617
  return null;
558
618
  }
@@ -561,6 +621,9 @@ export class CollectionAPI {
561
621
  await this.documentCache.invalidateDocument(context.organizationId, id);
562
622
  await this.documentCache.invalidateCollection(context.organizationId, this.collectionName);
563
623
  }
624
+ // This manual publish supersedes any pending scheduled publish — cancel it so the queued
625
+ // job doesn't fire later and re-emit `document.published` (a pending unpublish is left as-is).
626
+ await this.cancelPendingScheduleOfType(context.organizationId, id, DOCUMENT_PUBLISH_JOB);
564
627
  return transformDocument(publishedDocument, 'published');
565
628
  }
566
629
  /**
@@ -589,6 +652,102 @@ export class CollectionAPI {
589
652
  await this.documentCache.invalidateDocument(context.organizationId, id);
590
653
  await this.documentCache.invalidateCollection(context.organizationId, this.collectionName);
591
654
  }
655
+ // This manual unpublish supersedes any pending scheduled unpublish — cancel it so the
656
+ // queued job doesn't fire later (a pending publish is left as-is: "unpublish now,
657
+ // republish Monday" is a legitimate transition).
658
+ await this.cancelPendingScheduleOfType(context.organizationId, id, DOCUMENT_UNPUBLISH_JOB);
592
659
  return transformDocument(document, 'draft');
593
660
  }
661
+ /**
662
+ * Pending scheduled publish/unpublish jobs for one document. Scheduled jobs are few
663
+ * and `pending` is a small set, so filtering the org's pending jobs by documentId in
664
+ * memory is cheap and avoids a dialect-specific JSON query on the hot editor path.
665
+ */
666
+ async pendingScheduledFor(organizationId, documentId) {
667
+ const page = await this.databaseAdapter.listJobs({
668
+ organizationId,
669
+ status: 'pending',
670
+ limit: 200
671
+ });
672
+ return page.items.filter((j) => (j.type === DOCUMENT_PUBLISH_JOB || j.type === DOCUMENT_UNPUBLISH_JOB) &&
673
+ j.payload.documentId === documentId);
674
+ }
675
+ /**
676
+ * Cancel any pending schedule of ONE direction for a document — used when a manual
677
+ * publish/unpublish supersedes a schedule of the same kind. Only same-direction is
678
+ * cancelled on purpose: "publish now, auto-unpublish Friday" and "unpublish now,
679
+ * republish Monday" are legitimate future transitions, so a manual publish leaves a
680
+ * pending unpublish (and vice versa) alone. Without this, the queued job would fire at
681
+ * `runAt` and re-run the same transition — re-emitting `document.published`/`unpublished`
682
+ * and firing every consumer a second time (duplicate notifications, webhooks, cache busts).
683
+ */
684
+ async cancelPendingScheduleOfType(organizationId, documentId, jobType) {
685
+ const pending = await this.pendingScheduledFor(organizationId, documentId);
686
+ for (const job of pending) {
687
+ if (job.type === jobType) {
688
+ await this.databaseAdapter.cancelJob(organizationId, job.id);
689
+ }
690
+ }
691
+ }
692
+ /**
693
+ * Schedule a publish for a future `runAt`. Enqueues a `document.publish` job; the
694
+ * worker runs `publish()` at that time (re-validating, guarding references, emitting
695
+ * `document.published`). The permission check happens NOW — you must be able to publish
696
+ * to schedule one — so an unauthorized caller can't queue work to run later as the system.
697
+ * Actual publish-time validation still runs then, so a doc that goes invalid before
698
+ * `runAt` simply fails/retries rather than publishing bad content.
699
+ *
700
+ * Replace semantics: any existing pending schedule for this document is cancelled first,
701
+ * so a document has at most one pending schedule (rescheduling can't double-publish).
702
+ */
703
+ async schedulePublish(context, id, runAt) {
704
+ const document = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
705
+ if (!document)
706
+ throw new Error('Document not found');
707
+ await this.permissions.canPublish(context, this.collectionName, document);
708
+ for (const existing of await this.pendingScheduledFor(context.organizationId, id)) {
709
+ await this.databaseAdapter.cancelJob(context.organizationId, existing.id);
710
+ }
711
+ return this.databaseAdapter.scheduleJob({
712
+ organizationId: context.organizationId,
713
+ type: DOCUMENT_PUBLISH_JOB,
714
+ payload: { documentId: id, documentType: this.collectionName },
715
+ runAt,
716
+ createdBy: context.user?.id ?? null
717
+ });
718
+ }
719
+ /** Schedule an unpublish for a future `runAt`. Permission-checked now; replaces any existing pending schedule. */
720
+ async scheduleUnpublish(context, id, runAt) {
721
+ const document = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
722
+ if (!document)
723
+ throw new Error('Document not found');
724
+ await this.permissions.canUnpublish(context, this.collectionName, document);
725
+ for (const existing of await this.pendingScheduledFor(context.organizationId, id)) {
726
+ await this.databaseAdapter.cancelJob(context.organizationId, existing.id);
727
+ }
728
+ return this.databaseAdapter.scheduleJob({
729
+ organizationId: context.organizationId,
730
+ type: DOCUMENT_UNPUBLISH_JOB,
731
+ payload: { documentId: id, documentType: this.collectionName },
732
+ runAt,
733
+ createdBy: context.user?.id ?? null
734
+ });
735
+ }
736
+ /** Pending scheduled publish/unpublish jobs for a document (read-gated) — for the editor's schedule indicator. */
737
+ async getScheduled(context, id) {
738
+ await this.permissions.canRead(context, this.collectionName);
739
+ return this.pendingScheduledFor(context.organizationId, id);
740
+ }
741
+ /** Cancel all pending scheduled publish/unpublish jobs for a document. Returns how many were cancelled. */
742
+ async cancelScheduled(context, id) {
743
+ const document = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
744
+ if (!document)
745
+ throw new Error('Document not found');
746
+ await this.permissions.canPublish(context, this.collectionName, document);
747
+ const pending = await this.pendingScheduledFor(context.organizationId, id);
748
+ for (const job of pending) {
749
+ await this.databaseAdapter.cancelJob(context.organizationId, job.id);
750
+ }
751
+ return pending.length;
752
+ }
594
753
  }
@@ -5,8 +5,9 @@
5
5
  * `config.plugins` and exposed on the engine so both server and admin consult the
6
6
  * same resolved model. Validates duplicate part ids up front — no silent overrides.
7
7
  */
8
- import type { AdminToolPart, CMSPlugin, DocumentActionPart, FieldComponentPart, PartKind, PluginPart, ServerRoutePart, SettingsPart } from './types.js';
8
+ import type { AdminToolPart, CMSPlugin, DocumentActionPart, EventConsumerPart, FieldComponentPart, PartKind, PluginPart, ServerRoutePart, SettingsPart } from './types.js';
9
9
  import type { SchemaType } from '../types/schemas.js';
10
+ import type { JobHandlerMap } from '../jobs/types.js';
10
11
  import { type CapabilityDefinition } from '../types/capabilities.js';
11
12
  type PartOf<K extends PartKind> = Extract<PluginPart, {
12
13
  implements: K;
@@ -43,6 +44,16 @@ export interface PartResolver {
43
44
  settingsDeclarations(): SettingsPart[];
44
45
  /** The settings declaration for a given plugin id, if any. */
45
46
  settingsDeclaration(pluginId: string): SettingsPart | undefined;
47
+ /**
48
+ * All plugin-contributed job handlers, merged into one map (later parts win on a
49
+ * type collision). The runner layers this between core built-ins and the app's
50
+ * `config.jobs.handlers`.
51
+ */
52
+ jobHandlers(): JobHandlerMap;
53
+ /** All registered event consumers, in registration order. The runner turns these into delivery job handlers. */
54
+ eventConsumers(): EventConsumerPart[];
55
+ /** Event consumers subscribed to a given event type — the relay's fan-out list. */
56
+ consumersForEvent(eventType: string): EventConsumerPart[];
46
57
  }
47
58
  export declare function createPartResolver(plugins?: CMSPlugin[]): PartResolver;
48
59
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/lib/plugins/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EACT,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,UAAU,EACV,eAAe,EACf,YAAY,EACZ,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAGN,KAAK,oBAAoB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE;IAAE,UAAU,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAEzE,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,yEAAyE;IACzE,WAAW,IAAI,UAAU,EAAE,CAAC;IAC5B,6EAA6E;IAC7E,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAAC;IAC3D,gDAAgD;IAChD,YAAY,IAAI,eAAe,EAAE,CAAC;IAClC,8DAA8D;IAC9D,YAAY,IAAI,MAAM,EAAE,CAAC;IACzB;oEACgE;IAChE,iBAAiB,IAAI,oBAAoB,EAAE,CAAC;IAC5C,sFAAsF;IACtF,eAAe,CAAC,IAAI,EAAE;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,cAAc,CAAC,EAAE,OAAO,CAAC;KACzB,GAAG,kBAAkB,EAAE,CAAC;IACzB,mDAAmD;IACnD,UAAU,CAAC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,aAAa,EAAE,CAAC;IAC1F,sEAAsE;IACtE,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAC;IAC9D,+DAA+D;IAC/D,oBAAoB,IAAI,YAAY,EAAE,CAAC;IACvC,8DAA8D;IAC9D,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;CAChE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,SAAS,EAAO,GAAG,YAAY,CAgF1E"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/lib/plugins/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,UAAU,EACV,eAAe,EACf,YAAY,EACZ,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAGN,KAAK,oBAAoB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE;IAAE,UAAU,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAEzE,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,yEAAyE;IACzE,WAAW,IAAI,UAAU,EAAE,CAAC;IAC5B,6EAA6E;IAC7E,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAAC;IAC3D,gDAAgD;IAChD,YAAY,IAAI,eAAe,EAAE,CAAC;IAClC,8DAA8D;IAC9D,YAAY,IAAI,MAAM,EAAE,CAAC;IACzB;oEACgE;IAChE,iBAAiB,IAAI,oBAAoB,EAAE,CAAC;IAC5C,sFAAsF;IACtF,eAAe,CAAC,IAAI,EAAE;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,cAAc,CAAC,EAAE,OAAO,CAAC;KACzB,GAAG,kBAAkB,EAAE,CAAC;IACzB,mDAAmD;IACnD,UAAU,CAAC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,aAAa,EAAE,CAAC;IAC1F,sEAAsE;IACtE,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAC;IAC9D,+DAA+D;IAC/D,oBAAoB,IAAI,YAAY,EAAE,CAAC;IACvC,8DAA8D;IAC9D,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IAChE;;;;OAIG;IACH,WAAW,IAAI,aAAa,CAAC;IAC7B,gHAAgH;IAChH,cAAc,IAAI,iBAAiB,EAAE,CAAC;IACtC,mFAAmF;IACnF,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAAC;CAC1D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,SAAS,EAAO,GAAG,YAAY,CAwF1E"}