@headless-lms/core 0.3.0 → 0.5.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 (307) hide show
  1. package/dist/assets/events.d.ts +24 -18
  2. package/dist/assets/file-urls.d.ts +11 -7
  3. package/dist/assets/file-urls.js +14 -4
  4. package/dist/assets/file-urls.js.map +1 -1
  5. package/dist/assets/index.d.ts +5 -4
  6. package/dist/assets/index.js +4 -3
  7. package/dist/assets/jobs.d.ts +15 -0
  8. package/dist/assets/jobs.js +30 -0
  9. package/dist/assets/jobs.js.map +1 -0
  10. package/dist/assets/ports.d.ts +37 -17
  11. package/dist/assets/service.d.ts +30 -15
  12. package/dist/assets/service.js +229 -59
  13. package/dist/assets/service.js.map +1 -1
  14. package/dist/automations/actions.js +2 -63
  15. package/dist/automations/actions.js.map +1 -1
  16. package/dist/automations/catalog.d.ts +1 -2
  17. package/dist/automations/catalog.js +137 -65
  18. package/dist/automations/catalog.js.map +1 -1
  19. package/dist/automations/community-derivations.js +20 -20
  20. package/dist/automations/community-derivations.js.map +1 -1
  21. package/dist/automations/community-mail.d.ts +36 -20
  22. package/dist/automations/community-mail.js +32 -24
  23. package/dist/automations/community-mail.js.map +1 -1
  24. package/dist/automations/event-permissions.d.ts +12 -0
  25. package/dist/automations/event-permissions.js +38 -0
  26. package/dist/automations/event-permissions.js.map +1 -0
  27. package/dist/automations/events.d.ts +0 -10
  28. package/dist/automations/index.d.ts +12 -4
  29. package/dist/automations/index.js +9 -1
  30. package/dist/automations/notifications/applicable.d.ts +8 -0
  31. package/dist/automations/notifications/applicable.js +12 -0
  32. package/dist/automations/notifications/applicable.js.map +1 -0
  33. package/dist/automations/notifications/debounce.d.ts +25 -0
  34. package/dist/automations/notifications/debounce.js +93 -0
  35. package/dist/automations/notifications/debounce.js.map +1 -0
  36. package/dist/automations/notifications/defaults.d.ts +8 -0
  37. package/dist/automations/notifications/defaults.js +346 -0
  38. package/dist/automations/notifications/defaults.js.map +1 -0
  39. package/dist/automations/notifications/delivery.d.ts +28 -0
  40. package/dist/automations/notifications/delivery.js +88 -0
  41. package/dist/automations/notifications/delivery.js.map +1 -0
  42. package/dist/automations/notifications/processor.d.ts +64 -0
  43. package/dist/automations/notifications/processor.js +270 -0
  44. package/dist/automations/notifications/processor.js.map +1 -0
  45. package/dist/automations/notifications/rules/content.d.ts +6 -0
  46. package/dist/automations/notifications/rules/content.js +96 -0
  47. package/dist/automations/notifications/rules/content.js.map +1 -0
  48. package/dist/automations/notifications/rules/discussion.d.ts +7 -0
  49. package/dist/automations/notifications/rules/discussion.js +678 -0
  50. package/dist/automations/notifications/rules/discussion.js.map +1 -0
  51. package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
  52. package/dist/automations/notifications/rules/entitlements.js +276 -0
  53. package/dist/automations/notifications/rules/entitlements.js.map +1 -0
  54. package/dist/automations/notifications/rules/events.d.ts +9 -0
  55. package/dist/automations/notifications/rules/events.js +27 -0
  56. package/dist/automations/notifications/rules/events.js.map +1 -0
  57. package/dist/automations/notifications/rules/index.d.ts +6 -0
  58. package/dist/automations/notifications/rules/index.js +15 -0
  59. package/dist/automations/notifications/rules/index.js.map +1 -0
  60. package/dist/automations/notifications/rules/organizations.d.ts +6 -0
  61. package/dist/automations/notifications/rules/organizations.js +35 -0
  62. package/dist/automations/notifications/rules/organizations.js.map +1 -0
  63. package/dist/automations/notifications/rules/recipients.d.ts +32 -0
  64. package/dist/automations/notifications/rules/recipients.js +98 -0
  65. package/dist/automations/notifications/rules/recipients.js.map +1 -0
  66. package/dist/automations/notifications/service.d.ts +29 -0
  67. package/dist/automations/notifications/service.js +45 -0
  68. package/dist/automations/notifications/service.js.map +1 -0
  69. package/dist/automations/notifications/types.d.ts +7 -0
  70. package/dist/automations/notifications/types.js +560 -0
  71. package/dist/automations/notifications/types.js.map +1 -0
  72. package/dist/automations/ports.d.ts +134 -21
  73. package/dist/automations/service.d.ts +30 -30
  74. package/dist/automations/service.js +111 -144
  75. package/dist/automations/service.js.map +1 -1
  76. package/dist/content/event-sight.d.ts +11 -0
  77. package/dist/content/event-sight.js +11 -0
  78. package/dist/content/event-sight.js.map +1 -0
  79. package/dist/content/events.d.ts +437 -46
  80. package/dist/content/events.js +45 -1
  81. package/dist/content/events.js.map +1 -1
  82. package/dist/content/index.d.ts +8 -4
  83. package/dist/content/index.js +5 -1
  84. package/dist/content/jobs.d.ts +16 -0
  85. package/dist/content/jobs.js +37 -0
  86. package/dist/content/jobs.js.map +1 -0
  87. package/dist/content/model.d.ts +2 -2
  88. package/dist/content/notification-categories.d.ts +37 -0
  89. package/dist/content/notification-categories.js +73 -0
  90. package/dist/content/notification-categories.js.map +1 -0
  91. package/dist/content/ports.d.ts +132 -68
  92. package/dist/content/service.d.ts +100 -68
  93. package/dist/content/service.js +703 -146
  94. package/dist/content/service.js.map +1 -1
  95. package/dist/content/titles.d.ts +25 -0
  96. package/dist/content/titles.js +82 -0
  97. package/dist/content/titles.js.map +1 -0
  98. package/dist/content/types.d.ts +2 -2
  99. package/dist/discussion/authors.d.ts +4 -2
  100. package/dist/discussion/authors.js +9 -5
  101. package/dist/discussion/authors.js.map +1 -1
  102. package/dist/discussion/chats.d.ts +63 -39
  103. package/dist/discussion/chats.js +455 -190
  104. package/dist/discussion/chats.js.map +1 -1
  105. package/dist/discussion/digests.d.ts +24 -0
  106. package/dist/discussion/digests.js +104 -0
  107. package/dist/discussion/digests.js.map +1 -0
  108. package/dist/discussion/events.d.ts +227 -23
  109. package/dist/discussion/events.js +114 -19
  110. package/dist/discussion/events.js.map +1 -1
  111. package/dist/discussion/index.d.ts +12 -4
  112. package/dist/discussion/index.js +9 -2
  113. package/dist/discussion/jobs.d.ts +16 -0
  114. package/dist/discussion/jobs.js +40 -0
  115. package/dist/discussion/jobs.js.map +1 -0
  116. package/dist/discussion/live.d.ts +106 -0
  117. package/dist/discussion/live.js +642 -0
  118. package/dist/discussion/live.js.map +1 -0
  119. package/dist/discussion/mentions.d.ts +11 -0
  120. package/dist/discussion/mentions.js +35 -0
  121. package/dist/discussion/mentions.js.map +1 -0
  122. package/dist/discussion/ports.d.ts +279 -114
  123. package/dist/discussion/ports.js.map +1 -1
  124. package/dist/discussion/posts.d.ts +60 -44
  125. package/dist/discussion/posts.js +453 -287
  126. package/dist/discussion/posts.js.map +1 -1
  127. package/dist/discussion/rules.d.ts +18 -0
  128. package/dist/discussion/rules.js +22 -0
  129. package/dist/discussion/rules.js.map +1 -0
  130. package/dist/discussion/service.d.ts +50 -35
  131. package/dist/discussion/service.js +333 -205
  132. package/dist/discussion/service.js.map +1 -1
  133. package/dist/discussion/subscriptions.d.ts +37 -0
  134. package/dist/discussion/subscriptions.js +115 -0
  135. package/dist/discussion/subscriptions.js.map +1 -0
  136. package/dist/discussion/titles.d.ts +28 -0
  137. package/dist/discussion/titles.js +98 -0
  138. package/dist/discussion/titles.js.map +1 -0
  139. package/dist/entitlements/events.d.ts +20 -20
  140. package/dist/entitlements/index.d.ts +5 -4
  141. package/dist/entitlements/index.js +3 -2
  142. package/dist/entitlements/jobs.d.ts +12 -0
  143. package/dist/entitlements/jobs.js +18 -0
  144. package/dist/entitlements/jobs.js.map +1 -0
  145. package/dist/entitlements/model.d.ts +2 -2
  146. package/dist/entitlements/ports.d.ts +68 -38
  147. package/dist/entitlements/rules.d.ts +3 -6
  148. package/dist/entitlements/rules.js +5 -10
  149. package/dist/entitlements/rules.js.map +1 -1
  150. package/dist/entitlements/service.d.ts +43 -28
  151. package/dist/entitlements/service.js +387 -160
  152. package/dist/entitlements/service.js.map +1 -1
  153. package/dist/identity/devices.d.ts +30 -0
  154. package/dist/identity/devices.js +109 -0
  155. package/dist/identity/devices.js.map +1 -0
  156. package/dist/identity/index.d.ts +6 -5
  157. package/dist/identity/index.js +4 -3
  158. package/dist/identity/model.d.ts +2 -2
  159. package/dist/identity/notification-settings.d.ts +28 -0
  160. package/dist/identity/notification-settings.js +114 -0
  161. package/dist/identity/notification-settings.js.map +1 -0
  162. package/dist/identity/ports.d.ts +80 -56
  163. package/dist/identity/service.d.ts +8 -7
  164. package/dist/identity/service.js +17 -13
  165. package/dist/identity/service.js.map +1 -1
  166. package/dist/integrations/ports.d.ts +9 -8
  167. package/dist/integrations/service.d.ts +9 -8
  168. package/dist/integrations/service.js +33 -16
  169. package/dist/integrations/service.js.map +1 -1
  170. package/dist/organizations/default-roles.d.ts +22 -0
  171. package/dist/organizations/default-roles.js +89 -0
  172. package/dist/organizations/default-roles.js.map +1 -0
  173. package/dist/organizations/effective-permissions.d.ts +45 -0
  174. package/dist/organizations/effective-permissions.js +113 -0
  175. package/dist/organizations/effective-permissions.js.map +1 -0
  176. package/dist/organizations/events.d.ts +320 -181
  177. package/dist/organizations/events.js +90 -18
  178. package/dist/organizations/events.js.map +1 -1
  179. package/dist/organizations/index.d.ts +15 -10
  180. package/dist/organizations/index.js +8 -3
  181. package/dist/organizations/members.d.ts +66 -8
  182. package/dist/organizations/members.js.map +1 -1
  183. package/dist/organizations/notification-catalog.d.ts +31 -0
  184. package/dist/organizations/notification-catalog.js +32 -0
  185. package/dist/organizations/notification-catalog.js.map +1 -0
  186. package/dist/organizations/notification-categories.d.ts +34 -0
  187. package/dist/organizations/notification-categories.js +66 -0
  188. package/dist/organizations/notification-categories.js.map +1 -0
  189. package/dist/organizations/org-roles.d.ts +33 -0
  190. package/dist/organizations/org-roles.js +49 -0
  191. package/dist/organizations/org-roles.js.map +1 -0
  192. package/dist/organizations/permissions.d.ts +311 -0
  193. package/dist/organizations/permissions.js +327 -0
  194. package/dist/organizations/permissions.js.map +1 -0
  195. package/dist/organizations/ports.d.ts +187 -65
  196. package/dist/organizations/service.d.ts +104 -35
  197. package/dist/organizations/service.js +1247 -323
  198. package/dist/organizations/service.js.map +1 -1
  199. package/dist/organizations/testing.d.ts +18 -0
  200. package/dist/organizations/testing.js +37 -0
  201. package/dist/organizations/testing.js.map +1 -0
  202. package/dist/organizations/types.d.ts +5 -16
  203. package/dist/progress/index.d.ts +2 -2
  204. package/dist/progress/ports.d.ts +11 -3
  205. package/dist/progress/service.d.ts +6 -5
  206. package/dist/progress/service.js +23 -8
  207. package/dist/progress/service.js.map +1 -1
  208. package/dist/reporting/admin/index.d.ts +6 -4
  209. package/dist/reporting/admin/model.d.ts +36 -3
  210. package/dist/reporting/admin/ports.d.ts +39 -14
  211. package/dist/reporting/admin/service.d.ts +32 -11
  212. package/dist/reporting/admin/service.js +102 -18
  213. package/dist/reporting/admin/service.js.map +1 -1
  214. package/dist/reporting/events/index.d.ts +27 -0
  215. package/dist/reporting/events/index.js +20 -0
  216. package/dist/reporting/events/index.js.map +1 -0
  217. package/dist/reporting/member/calendar.d.ts +11 -0
  218. package/dist/reporting/member/calendar.js +75 -0
  219. package/dist/reporting/member/calendar.js.map +1 -0
  220. package/dist/reporting/member/index.d.ts +3 -2
  221. package/dist/reporting/member/model.d.ts +80 -26
  222. package/dist/reporting/member/ports.d.ts +50 -28
  223. package/dist/reporting/member/service.d.ts +81 -35
  224. package/dist/reporting/member/service.js +438 -145
  225. package/dist/reporting/member/service.js.map +1 -1
  226. package/dist/shared/actor.d.ts +22 -3
  227. package/dist/shared/errors.d.ts +15 -2
  228. package/dist/shared/errors.js +21 -2
  229. package/dist/shared/errors.js.map +1 -1
  230. package/dist/shared/id.d.ts +7 -0
  231. package/dist/shared/id.js +8 -1
  232. package/dist/shared/id.js.map +1 -1
  233. package/dist/shared/logger.d.ts +2 -1
  234. package/dist/shared/logger.js +10 -1
  235. package/dist/shared/logger.js.map +1 -1
  236. package/dist/shared/mailer.d.ts +11 -1
  237. package/dist/shared/mailer.js +32 -5
  238. package/dist/shared/mailer.js.map +1 -1
  239. package/dist/shared/notification-categories.d.ts +16 -0
  240. package/dist/shared/notification-categories.js +94 -0
  241. package/dist/shared/notification-categories.js.map +1 -0
  242. package/dist/shared/notification-settings.d.ts +14 -0
  243. package/dist/shared/notification-settings.js +61 -0
  244. package/dist/shared/notification-settings.js.map +1 -0
  245. package/dist/shared/ports.d.ts +68 -2
  246. package/dist/shared/settings.d.ts +2 -1
  247. package/dist/shared/settings.js +1 -0
  248. package/dist/shared/settings.js.map +1 -1
  249. package/dist/types/automations.d.ts +8 -2
  250. package/dist/types/content.d.ts +2 -2
  251. package/dist/types/discussion.d.ts +4 -5
  252. package/dist/types/email-templates.d.ts +119 -6
  253. package/dist/types/entitlements.d.ts +2 -2
  254. package/dist/types/identity.d.ts +14 -17
  255. package/dist/types/index.d.ts +13 -12
  256. package/dist/types/index.js +2 -1
  257. package/dist/types/notifications.d.ts +157 -0
  258. package/dist/types/notifications.js +1 -0
  259. package/dist/types/organizations.d.ts +3 -2
  260. package/dist/types/ports.d.ts +8 -1
  261. package/dist/types/ports.js +7 -1
  262. package/dist/types/ports.js.map +1 -0
  263. package/dist/types/schemas/assets.d.ts +5 -5
  264. package/dist/types/schemas/assets.js +4 -3
  265. package/dist/types/schemas/assets.js.map +1 -1
  266. package/dist/types/schemas/automations.d.ts +1 -10
  267. package/dist/types/schemas/automations.js +1 -3
  268. package/dist/types/schemas/automations.js.map +1 -1
  269. package/dist/types/schemas/content.d.ts +596 -11
  270. package/dist/types/schemas/content.js +225 -9
  271. package/dist/types/schemas/content.js.map +1 -1
  272. package/dist/types/schemas/discussion.d.ts +362 -66
  273. package/dist/types/schemas/discussion.js +123 -15
  274. package/dist/types/schemas/discussion.js.map +1 -1
  275. package/dist/types/schemas/entitlements.d.ts +14 -1
  276. package/dist/types/schemas/entitlements.js +11 -4
  277. package/dist/types/schemas/entitlements.js.map +1 -1
  278. package/dist/types/schemas/identity.d.ts +1 -85
  279. package/dist/types/schemas/identity.js +1 -42
  280. package/dist/types/schemas/identity.js.map +1 -1
  281. package/dist/types/schemas/index.d.ts +8 -7
  282. package/dist/types/schemas/index.js +8 -7
  283. package/dist/types/schemas/notifications.d.ts +96 -0
  284. package/dist/types/schemas/notifications.js +57 -0
  285. package/dist/types/schemas/notifications.js.map +1 -0
  286. package/dist/types/schemas/organizations.d.ts +96 -60
  287. package/dist/types/schemas/organizations.js +78 -27
  288. package/dist/types/schemas/organizations.js.map +1 -1
  289. package/dist/types/schemas/progress.d.ts +0 -1
  290. package/dist/types/schemas/progress.js +0 -1
  291. package/dist/types/schemas/progress.js.map +1 -1
  292. package/package.json +1 -1
  293. package/dist/automations/community-notifications.d.ts +0 -16
  294. package/dist/automations/community-notifications.js +0 -84
  295. package/dist/automations/community-notifications.js.map +0 -1
  296. package/dist/automations/seeds.d.ts +0 -17
  297. package/dist/automations/seeds.js +0 -235
  298. package/dist/automations/seeds.js.map +0 -1
  299. package/dist/identity/notifications.d.ts +0 -27
  300. package/dist/identity/notifications.js +0 -101
  301. package/dist/identity/notifications.js.map +0 -1
  302. package/dist/organizations/access-scopes.d.ts +0 -19
  303. package/dist/organizations/access-scopes.js +0 -89
  304. package/dist/organizations/access-scopes.js.map +0 -1
  305. package/dist/organizations/roles.d.ts +0 -19
  306. package/dist/organizations/roles.js +0 -82
  307. package/dist/organizations/roles.js.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import { deliverWebhookInputSchema } from "../types/schemas/automations.js";
2
- import { ConflictError, NotFoundError } from "../shared/errors.js";
3
2
  import { noopLogger } from "../shared/logger.js";
3
+ import { ConflictError, InvalidInputError, NotFoundError } from "../shared/errors.js";
4
4
  import { ActionFailedError, AutomationKindMismatchError, InvalidTriggerError, ReservedActionError } from "./model.js";
5
5
  import { DELIVER_WEBHOOK_ACTION, catalogActions, catalogTriggers, publicDataSchema } from "./catalog.js";
6
6
  import { generateWebhookSecret } from "./webhook-signing.js";
7
7
  import { executeAction } from "./actions.js";
8
+ import { assertGrantsAllowed, assertReadsEvents } from "./event-permissions.js";
8
9
  import { automationEvents } from "./events.js";
9
- import { communitySeeds, orgSeeds } from "./seeds.js";
10
10
  import { cronSupported } from "./schedule.js";
11
11
  //#region src/automations/service.ts
12
12
  /** True only when `input` sets `enabled` and nothing else — emits automation.enabled|disabled instead of automation.updated. */
@@ -23,11 +23,6 @@ function floorMinute(at) {
23
23
  function unique(values) {
24
24
  return [...new Set(values)];
25
25
  }
26
- function recordString(value, key) {
27
- if (typeof value !== "object" || value === null || !(key in value)) return null;
28
- const found = value[key];
29
- return typeof found === "string" ? found : null;
30
- }
31
26
  function filterMatches(filter, data) {
32
27
  if (filter == null) return true;
33
28
  if (typeof data !== "object" || data === null || Array.isArray(data)) return false;
@@ -48,8 +43,18 @@ function assertCatalogTriggers(triggers, schedules) {
48
43
  const known = new Set(catalogTriggers().map((t) => t.type));
49
44
  for (const trigger of triggers) if (!(schedules && trigger.startsWith("schedule:")) && !known.has(trigger)) throw new InvalidTriggerError(trigger, "unknown");
50
45
  }
46
+ const REMOVED_ACTIONS = /* @__PURE__ */ new Set([
47
+ "notify",
48
+ "publishDuePosts",
49
+ "expireDueGrants",
50
+ "sendDigest",
51
+ "removeUnattachedUploads"
52
+ ]);
51
53
  function assertAuthorableActions(actions) {
52
- for (const action of actions) if (action.type === "deliverWebhook") throw new ReservedActionError(action.type);
54
+ for (const action of actions) {
55
+ if (action.type === "deliverWebhook") throw new ReservedActionError(action.type);
56
+ if (REMOVED_ACTIONS.has(action.type)) throw new InvalidInputError(`The action "${action.type}" is no longer available`);
57
+ }
53
58
  }
54
59
  const SCHEDULE_FIRED = "automation.schedule.fired";
55
60
  function publicEventData(event) {
@@ -124,12 +129,13 @@ var AutomationsServiceImpl = class {
124
129
  mailer;
125
130
  lookups;
126
131
  integrations;
132
+ systemActor;
127
133
  credentials;
128
134
  webhooks;
129
135
  mail;
130
136
  grants;
131
- desk;
132
- scheduledWork;
137
+ delivery;
138
+ processor;
133
139
  logger;
134
140
  constructor(params) {
135
141
  this.repo = params.repo;
@@ -139,6 +145,7 @@ var AutomationsServiceImpl = class {
139
145
  this.mailer = params.mailer;
140
146
  this.lookups = params.lookups;
141
147
  this.integrations = params.integrations;
148
+ this.systemActor = params.systemActor;
142
149
  this.credentials = params.credentials;
143
150
  this.webhooks = params.webhooks;
144
151
  this.mail = {
@@ -146,13 +153,12 @@ var AutomationsServiceImpl = class {
146
153
  recentMessageRecipients: (orgId, automationId, chatId, since) => this.runsRepo.recentRecipients(orgId, automationId, chatId, since)
147
154
  };
148
155
  this.grants = params.grants;
149
- this.desk = params.desk;
150
- this.scheduledWork = params.scheduledWork;
156
+ this.delivery = params.delivery;
157
+ this.processor = params.processor;
151
158
  this.logger = params.logger ?? noopLogger;
152
159
  }
153
160
  async handle(event) {
154
161
  if (event.type.startsWith("automation.")) return;
155
- await this.seedFor(event);
156
162
  const automations = await this.safeListByTrigger(event);
157
163
  for (const automation of automations) {
158
164
  const matches = filterMatches(automation.filter, event.data);
@@ -166,50 +172,7 @@ var AutomationsServiceImpl = class {
166
172
  }
167
173
  await this.dispatchOne(automation, event);
168
174
  }
169
- }
170
- seedsFor(event) {
171
- if (event.type === "organization.created") return orgSeeds();
172
- if (event.type !== "content.community.created") return null;
173
- const communityId = recordString(event.data, "id");
174
- if (!communityId) {
175
- this.logger.error("community seed missing id", {
176
- orgId: event.orgId,
177
- eventId: event.id
178
- });
179
- return null;
180
- }
181
- return communitySeeds(communityId);
182
- }
183
- async seedFor(event) {
184
- const rows = this.seedsFor(event);
185
- if (!rows) return;
186
- if (await this.uow.run(async ({ automations, outbox }) => {
187
- const created = [];
188
- for (const row of rows) {
189
- const inserted = await automations.insertSeed(event.orgId, {
190
- name: row.name,
191
- description: row.description,
192
- triggers: row.triggers,
193
- actions: row.actions,
194
- filter: row.filter,
195
- kind: "workflow",
196
- source: "system"
197
- });
198
- if (inserted) created.push(automationEvents.automationCreated.make({
199
- orgId: event.orgId,
200
- data: inserted
201
- }));
202
- }
203
- if (created.length > 0) {
204
- await outbox.append(created);
205
- this.logger.info("system automations seeded", {
206
- orgId: event.orgId,
207
- trigger: event.type,
208
- count: created.length
209
- });
210
- }
211
- return created.length;
212
- }) > 0) await this.reconcileSchedules(event.orgId);
175
+ await this.processor.process(event);
213
176
  }
214
177
  async reconcileSchedules(orgId) {
215
178
  try {
@@ -283,7 +246,7 @@ var AutomationsServiceImpl = class {
283
246
  event
284
247
  };
285
248
  }
286
- /** Best-effort: logs and never rethrows — `handle` must never throw. */
249
+ /** Best-effort: logs and never rethrows, so an org automation never fails `handle`. */
287
250
  async recordFailure(run, error) {
288
251
  try {
289
252
  return await this.uow.run(async ({ runs, outbox }) => {
@@ -321,8 +284,6 @@ var AutomationsServiceImpl = class {
321
284
  webhooks: this.webhooks,
322
285
  mail: this.mail,
323
286
  grants: this.grants,
324
- desk: this.desk,
325
- scheduledWork: this.scheduledWork,
326
287
  automationId: d.automationId,
327
288
  actionInput: action.input,
328
289
  logger: this.logger
@@ -356,6 +317,9 @@ var AutomationsServiceImpl = class {
356
317
  ...failed.length > 0 ? { error: `delivery failed for ${failed.length} recipients: ${failed.join(", ")}` } : {}
357
318
  };
358
319
  }
320
+ runDelivery(d, lastAttempt) {
321
+ return this.delivery.run(d, lastAttempt);
322
+ }
359
323
  async listSchedules() {
360
324
  return this.repo.listSchedules();
361
325
  }
@@ -369,13 +333,6 @@ var AutomationsServiceImpl = class {
369
333
  });
370
334
  return;
371
335
  }
372
- if (!await this.scheduleHasWork(orgId, automation, firedAt)) {
373
- this.logger.debug("schedule idle", {
374
- orgId,
375
- automationId
376
- });
377
- return;
378
- }
379
336
  const event = {
380
337
  type: SCHEDULE_FIRED,
381
338
  id: `schedule_${automationId}_${firedAt.toISOString()}`,
@@ -389,21 +346,6 @@ var AutomationsServiceImpl = class {
389
346
  };
390
347
  await this.dispatchOne(automation, event);
391
348
  }
392
- async scheduleHasWork(orgId, automation, firedAt) {
393
- const checks = automation.actions.map((action) => this.pendingWork(action.type));
394
- const known = checks.filter((check) => check !== null);
395
- if (checks.length === 0 || known.length < checks.length) return true;
396
- for (const check of known) if (await check(orgId, firedAt)) return true;
397
- return false;
398
- }
399
- pendingWork(type) {
400
- switch (type) {
401
- case "publishDuePosts": return (orgId, now) => this.scheduledWork.hasDuePosts(orgId, now);
402
- case "expireDueGrants": return (orgId, now) => this.scheduledWork.hasDueGrants(orgId, now);
403
- case "removeUnattachedUploads": return (orgId, now) => this.scheduledWork.hasUnattachedUploads(orgId, now);
404
- default: return null;
405
- }
406
- }
407
349
  async finalize(d, results) {
408
350
  const status = results.length === d.actions.length && results.every((r) => r.status === "completed") ? "completed" : "failed";
409
351
  await this.uow.run(async ({ runs, outbox }) => {
@@ -457,9 +399,12 @@ var AutomationsServiceImpl = class {
457
399
  }))
458
400
  });
459
401
  }
460
- async create(orgId, input) {
402
+ async create(input, actor) {
403
+ actor.permissions.require("automations:write");
404
+ const orgId = actor.orgId;
461
405
  assertCatalogTriggers(input.triggers, true);
462
406
  assertAuthorableActions(input.actions);
407
+ assertGrantsAllowed(actor, input.actions);
463
408
  const automation = await this.uow.run(async ({ automations, outbox }) => {
464
409
  const created = await automations.insert(orgId, {
465
410
  ...input,
@@ -474,16 +419,22 @@ var AutomationsServiceImpl = class {
474
419
  });
475
420
  this.logger.info("automation created", {
476
421
  orgId,
477
- automationId: automation.id
422
+ automationId: automation.id,
423
+ actorId: actor.id
478
424
  });
479
425
  await this.reconcileSchedules(orgId);
480
426
  return automation;
481
427
  }
482
- async update(orgId, id, input) {
428
+ async update(id, input, actor) {
429
+ actor.permissions.require("automations:write");
430
+ const orgId = actor.orgId;
483
431
  if (input.triggers !== void 0) assertCatalogTriggers(input.triggers, true);
484
- if (input.actions !== void 0) assertAuthorableActions(input.actions);
432
+ if (input.actions !== void 0) {
433
+ assertAuthorableActions(input.actions);
434
+ assertGrantsAllowed(actor, input.actions);
435
+ }
485
436
  const existing = await this.repo.findById(orgId, id);
486
- if (!existing) return null;
437
+ if (!existing) throw new NotFoundError("Automation", id);
487
438
  if (existing.kind !== "workflow") throw new AutomationKindMismatchError(id, existing.kind);
488
439
  const patch = input.triggers === void 0 ? input : {
489
440
  ...input,
@@ -495,21 +446,21 @@ var AutomationsServiceImpl = class {
495
446
  await outbox.append([updateEvent(orgId, input, updated)]);
496
447
  return updated;
497
448
  });
498
- if (automation) {
499
- this.logger.info("automation updated", {
500
- orgId,
501
- automationId: id
502
- });
503
- await this.reconcileSchedules(orgId);
504
- }
449
+ if (!automation) throw new NotFoundError("Automation", id);
450
+ this.logger.info("automation updated", {
451
+ orgId,
452
+ automationId: id
453
+ });
454
+ await this.reconcileSchedules(orgId);
505
455
  return automation;
506
456
  }
507
- async delete(orgId, id) {
457
+ async delete(id, actor) {
458
+ actor.permissions.require("automations:delete");
459
+ const orgId = actor.orgId;
508
460
  const existing = await this.repo.findById(orgId, id);
509
- if (!existing) return false;
461
+ if (!existing) throw new NotFoundError("Automation", id);
510
462
  if (existing.kind !== "workflow") throw new AutomationKindMismatchError(id, existing.kind);
511
- if (existing.source === "system") throw new ConflictError("A system automation cannot be deleted");
512
- const deleted = await this.uow.run(async ({ automations, outbox }) => {
463
+ if (!await this.uow.run(async ({ automations, outbox }) => {
513
464
  const removed = await automations.delete(orgId, id);
514
465
  if (!removed) return null;
515
466
  await outbox.append([automationEvents.automationDeleted.make({
@@ -517,35 +468,39 @@ var AutomationsServiceImpl = class {
517
468
  data: removed
518
469
  })]);
519
470
  return removed;
471
+ })) throw new NotFoundError("Automation", id);
472
+ this.logger.info("automation deleted", {
473
+ orgId,
474
+ automationId: id
520
475
  });
521
- if (deleted) {
522
- this.logger.info("automation deleted", {
523
- orgId,
524
- automationId: id
525
- });
526
- await this.reconcileSchedules(orgId);
527
- }
528
- return deleted !== null;
476
+ await this.reconcileSchedules(orgId);
529
477
  }
530
- async list(orgId, query) {
531
- return (await this.repo.listByOrg(orgId, query?.kind)).map(toAutomationView);
478
+ async list(query, actor) {
479
+ actor.permissions.require("automations:read");
480
+ return (await this.repo.listByOrg(actor.orgId, query?.kind)).map(toAutomationView);
532
481
  }
533
- async get(orgId, id) {
534
- const automation = await this.repo.findById(orgId, id);
535
- return automation ? toAutomationView(automation) : null;
482
+ async get(id, actor) {
483
+ actor.permissions.require("automations:read");
484
+ const automation = await this.repo.findById(actor.orgId, id);
485
+ if (!automation) throw new NotFoundError("Automation", id);
486
+ return toAutomationView(automation);
536
487
  }
537
- async listRuns(orgId, automationId, query) {
538
- const page = await this.runsRepo.list(orgId, automationId, query);
488
+ async listRuns(automationId, query, actor) {
489
+ actor.permissions.require("automations:read");
490
+ const page = await this.runsRepo.list(actor.orgId, automationId, query);
539
491
  return {
540
492
  ...page,
541
493
  rows: page.rows.map(toRunView)
542
494
  };
543
495
  }
544
- async rerun(orgId, automationId, runId) {
496
+ async rerun(automationId, runId, actor) {
497
+ actor.permissions.require("automations:write");
498
+ const orgId = actor.orgId;
545
499
  const existing = await this.runsRepo.findById(orgId, runId);
546
500
  if (!existing || existing.automationId !== automationId) throw new NotFoundError("Automation run", runId);
547
501
  const automation = await this.repo.findById(orgId, automationId);
548
502
  if (!automation) throw new NotFoundError("Automation", automationId);
503
+ assertGrantsAllowed(actor, automation.actions);
549
504
  const run = await this.uow.run(async ({ runs, outbox }) => {
550
505
  const restarted = await runs.restart(orgId, runId, /* @__PURE__ */ new Date());
551
506
  if (!restarted) return null;
@@ -575,16 +530,20 @@ var AutomationsServiceImpl = class {
575
530
  });
576
531
  return toRunView(run);
577
532
  }
578
- async listWebhooks(orgId) {
579
- return (await this.repo.listByOrg(orgId, "webhook")).map(toWebhook);
533
+ async listWebhooks(actor) {
534
+ actor.permissions.require("automations:read");
535
+ return (await this.repo.listByOrg(actor.orgId, "webhook")).map(toWebhook);
580
536
  }
581
- async getWebhook(orgId, id) {
582
- const automation = await this.findWebhook(orgId, id);
583
- return automation ? toWebhook(automation) : null;
537
+ async getWebhook(id, actor) {
538
+ actor.permissions.require("automations:read");
539
+ return toWebhook(await this.requireWebhook(actor.orgId, id));
584
540
  }
585
- async createWebhook(orgId, input) {
541
+ async createWebhook(input, actor) {
542
+ actor.permissions.require("automations:write");
543
+ const orgId = actor.orgId;
586
544
  const events = unique(input.events);
587
545
  assertCatalogTriggers(events, false);
546
+ assertReadsEvents(actor, events);
588
547
  const secret = generateWebhookSecret();
589
548
  const automation = await this.uow.run(async ({ automations, credentials, outbox }) => {
590
549
  const secretRef = await credentials.store(orgId, { secret });
@@ -616,11 +575,15 @@ var AutomationsServiceImpl = class {
616
575
  secret
617
576
  };
618
577
  }
619
- async updateWebhook(orgId, id, input) {
620
- const existing = await this.findWebhook(orgId, id);
621
- if (!existing) return null;
578
+ async updateWebhook(id, input, actor) {
579
+ actor.permissions.require("automations:write");
580
+ const orgId = actor.orgId;
581
+ const existing = await this.requireWebhook(orgId, id);
622
582
  const events = input.events === void 0 ? void 0 : unique(input.events);
623
- if (events) assertCatalogTriggers(events, false);
583
+ if (events) {
584
+ assertCatalogTriggers(events, false);
585
+ assertReadsEvents(actor, events);
586
+ }
624
587
  const { secretRef } = webhookTarget(existing);
625
588
  const patch = {
626
589
  name: input.url,
@@ -641,18 +604,18 @@ var AutomationsServiceImpl = class {
641
604
  await outbox.append([updateEvent(orgId, input, row)]);
642
605
  return row;
643
606
  });
644
- if (!updated) return null;
607
+ if (!updated) throw new NotFoundError("Webhook", id);
645
608
  this.logger.info("webhook updated", {
646
609
  orgId,
647
610
  automationId: id
648
611
  });
649
612
  return toWebhook(updated);
650
613
  }
651
- async deleteWebhook(orgId, id) {
652
- const existing = await this.findWebhook(orgId, id);
653
- if (!existing) return false;
654
- const { secretRef } = webhookTarget(existing);
655
- const deleted = await this.uow.run(async ({ automations, credentials, outbox }) => {
614
+ async deleteWebhook(id, actor) {
615
+ actor.permissions.require("automations:delete");
616
+ const orgId = actor.orgId;
617
+ const { secretRef } = webhookTarget(await this.requireWebhook(orgId, id));
618
+ if (!await this.uow.run(async ({ automations, credentials, outbox }) => {
656
619
  const removed = await automations.delete(orgId, id);
657
620
  if (!removed) return null;
658
621
  await credentials.destroy(orgId, secretRef);
@@ -661,23 +624,24 @@ var AutomationsServiceImpl = class {
661
624
  data: removed
662
625
  })]);
663
626
  return removed;
664
- });
665
- if (deleted) this.logger.info("webhook deleted", {
627
+ })) throw new NotFoundError("Webhook", id);
628
+ this.logger.info("webhook deleted", {
666
629
  orgId,
667
630
  automationId: id
668
631
  });
669
- return deleted !== null;
670
632
  }
671
- async revealWebhookSecret(orgId, id) {
672
- const existing = await this.findWebhook(orgId, id);
673
- if (!existing) return null;
633
+ async revealWebhookSecret(id, actor) {
634
+ actor.permissions.require("automations:write");
635
+ const orgId = actor.orgId;
636
+ const existing = await this.requireWebhook(orgId, id);
674
637
  const secret = (await this.credentials.reveal(orgId, webhookTarget(existing).secretRef))?.["secret"];
675
638
  if (typeof secret !== "string") throw new Error(`webhook ${id}: signing secret is missing`);
676
639
  return { secret };
677
640
  }
678
- async rotateWebhookSecret(orgId, id) {
679
- const existing = await this.findWebhook(orgId, id);
680
- if (!existing) return null;
641
+ async rotateWebhookSecret(id, actor) {
642
+ actor.permissions.require("automations:write");
643
+ const orgId = actor.orgId;
644
+ const existing = await this.requireWebhook(orgId, id);
681
645
  const secret = generateWebhookSecret();
682
646
  await this.credentials.update(orgId, webhookTarget(existing).secretRef, { secret });
683
647
  this.logger.info("webhook secret rotated", {
@@ -686,19 +650,22 @@ var AutomationsServiceImpl = class {
686
650
  });
687
651
  return { secret };
688
652
  }
689
- async findWebhook(orgId, id) {
653
+ async requireWebhook(orgId, id) {
690
654
  const automation = await this.repo.findById(orgId, id);
691
- return automation?.kind === "webhook" ? automation : null;
655
+ if (automation?.kind !== "webhook") throw new NotFoundError("Webhook", id);
656
+ return automation;
692
657
  }
693
- availableActions() {
694
- return [...catalogActions(), ...this.integrations.available().flatMap((integration) => integration.actions.map((action) => ({
658
+ availableActions(actor) {
659
+ actor.permissions.require("automations:read");
660
+ return [...catalogActions(), ...this.integrations.available(this.systemActor(actor.orgId)).flatMap((integration) => integration.actions.map((action) => ({
695
661
  type: `${integration.id}.${action.id}`,
696
662
  description: action.description,
697
663
  inputSchema: action.inputSchema,
698
664
  source: integration.id
699
665
  })))];
700
666
  }
701
- availableTriggers() {
667
+ availableTriggers(actor) {
668
+ actor.permissions.require("automations:read");
702
669
  return { triggers: catalogTriggers() };
703
670
  }
704
671
  };