@aglyn/plugins-outreach 1.0.0-beta.143

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 (282) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +93 -0
  3. package/package.json +52 -0
  4. package/src/index.d.ts +22 -0
  5. package/src/index.js +29 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/compliance-section.d.ts +20 -0
  8. package/src/lib/components/compliance-section.js +291 -0
  9. package/src/lib/components/compliance-section.js.map +1 -0
  10. package/src/lib/components/enroll-dialog.d.ts +36 -0
  11. package/src/lib/components/enroll-dialog.js +656 -0
  12. package/src/lib/components/enroll-dialog.js.map +1 -0
  13. package/src/lib/components/enrollments-table.d.ts +28 -0
  14. package/src/lib/components/enrollments-table.js +375 -0
  15. package/src/lib/components/enrollments-table.js.map +1 -0
  16. package/src/lib/components/mailbox-card.d.ts +46 -0
  17. package/src/lib/components/mailbox-card.js +517 -0
  18. package/src/lib/components/mailbox-card.js.map +1 -0
  19. package/src/lib/components/mailboxes-section.d.ts +24 -0
  20. package/src/lib/components/mailboxes-section.js +258 -0
  21. package/src/lib/components/mailboxes-section.js.map +1 -0
  22. package/src/lib/components/outreach-console-page.d.ts +17 -0
  23. package/src/lib/components/outreach-console-page.js +81 -0
  24. package/src/lib/components/outreach-console-page.js.map +1 -0
  25. package/src/lib/components/outreach-console-sections.d.ts +43 -0
  26. package/src/lib/components/outreach-console-sections.js +55 -0
  27. package/src/lib/components/outreach-console-sections.js.map +1 -0
  28. package/src/lib/components/outreach-ui.d.ts +51 -0
  29. package/src/lib/components/outreach-ui.js +171 -0
  30. package/src/lib/components/outreach-ui.js.map +1 -0
  31. package/src/lib/components/send-window-fields.d.ts +25 -0
  32. package/src/lib/components/send-window-fields.js +178 -0
  33. package/src/lib/components/send-window-fields.js.map +1 -0
  34. package/src/lib/components/sequence-detail.d.ts +54 -0
  35. package/src/lib/components/sequence-detail.js +435 -0
  36. package/src/lib/components/sequence-detail.js.map +1 -0
  37. package/src/lib/components/sequence-editor.d.ts +37 -0
  38. package/src/lib/components/sequence-editor.js +485 -0
  39. package/src/lib/components/sequence-editor.js.map +1 -0
  40. package/src/lib/components/sequence-mailbox-picker.d.ts +44 -0
  41. package/src/lib/components/sequence-mailbox-picker.js +137 -0
  42. package/src/lib/components/sequence-mailbox-picker.js.map +1 -0
  43. package/src/lib/components/sequence-preview.d.ts +27 -0
  44. package/src/lib/components/sequence-preview.js +146 -0
  45. package/src/lib/components/sequence-preview.js.map +1 -0
  46. package/src/lib/components/sequence-step-card.d.ts +31 -0
  47. package/src/lib/components/sequence-step-card.js +326 -0
  48. package/src/lib/components/sequence-step-card.js.map +1 -0
  49. package/src/lib/components/sequences-section.d.ts +40 -0
  50. package/src/lib/components/sequences-section.js +310 -0
  51. package/src/lib/components/sequences-section.js.map +1 -0
  52. package/src/lib/components/use-outreach-api.d.ts +38 -0
  53. package/src/lib/components/use-outreach-api.js +139 -0
  54. package/src/lib/components/use-outreach-api.js.map +1 -0
  55. package/src/lib/components/use-outreach-crm.d.ts +50 -0
  56. package/src/lib/components/use-outreach-crm.js +200 -0
  57. package/src/lib/components/use-outreach-crm.js.map +1 -0
  58. package/src/lib/components/use-outreach-data.d.ts +55 -0
  59. package/src/lib/components/use-outreach-data.js +199 -0
  60. package/src/lib/components/use-outreach-data.js.map +1 -0
  61. package/src/lib/components/use-outreach-mailbox-api.d.ts +30 -0
  62. package/src/lib/components/use-outreach-mailbox-api.js +101 -0
  63. package/src/lib/components/use-outreach-mailbox-api.js.map +1 -0
  64. package/src/lib/components/use-outreach-mailboxes.d.ts +22 -0
  65. package/src/lib/components/use-outreach-mailboxes.js +68 -0
  66. package/src/lib/components/use-outreach-mailboxes.js.map +1 -0
  67. package/src/lib/components/use-outreach-settings.d.ts +17 -0
  68. package/src/lib/components/use-outreach-settings.js +64 -0
  69. package/src/lib/components/use-outreach-settings.js.map +1 -0
  70. package/src/lib/constants/api-routes.d.ts +68 -0
  71. package/src/lib/constants/api-routes.js +57 -0
  72. package/src/lib/constants/api-routes.js.map +1 -0
  73. package/src/lib/constants/bundle-common.d.ts +46 -0
  74. package/src/lib/constants/bundle-common.js +55 -0
  75. package/src/lib/constants/bundle-common.js.map +1 -0
  76. package/src/lib/constants/runtime-jobs.d.ts +24 -0
  77. package/src/lib/constants/runtime-jobs.js +24 -0
  78. package/src/lib/constants/runtime-jobs.js.map +1 -0
  79. package/src/lib/declarations.console-server.d.ts +38 -0
  80. package/src/lib/declarations.console-server.js +99 -0
  81. package/src/lib/declarations.console-server.js.map +1 -0
  82. package/src/lib/engine/compose.d.ts +86 -0
  83. package/src/lib/engine/compose.js +212 -0
  84. package/src/lib/engine/compose.js.map +1 -0
  85. package/src/lib/engine/delivery-status.d.ts +53 -0
  86. package/src/lib/engine/delivery-status.js +184 -0
  87. package/src/lib/engine/delivery-status.js.map +1 -0
  88. package/src/lib/engine/do-not-contact.d.ts +18 -0
  89. package/src/lib/engine/do-not-contact.js +34 -0
  90. package/src/lib/engine/do-not-contact.js.map +1 -0
  91. package/src/lib/engine/enrollment-state.d.ts +156 -0
  92. package/src/lib/engine/enrollment-state.js +303 -0
  93. package/src/lib/engine/enrollment-state.js.map +1 -0
  94. package/src/lib/engine/gates.d.ts +125 -0
  95. package/src/lib/engine/gates.js +232 -0
  96. package/src/lib/engine/gates.js.map +1 -0
  97. package/src/lib/engine/index.d.ts +28 -0
  98. package/src/lib/engine/index.js +33 -0
  99. package/src/lib/engine/index.js.map +1 -0
  100. package/src/lib/engine/mailbox-health.d.ts +49 -0
  101. package/src/lib/engine/mailbox-health.js +83 -0
  102. package/src/lib/engine/mailbox-health.js.map +1 -0
  103. package/src/lib/engine/opt-out-intent.d.ts +41 -0
  104. package/src/lib/engine/opt-out-intent.js +220 -0
  105. package/src/lib/engine/opt-out-intent.js.map +1 -0
  106. package/src/lib/engine/recipient-country.d.ts +41 -0
  107. package/src/lib/engine/recipient-country.js +171 -0
  108. package/src/lib/engine/recipient-country.js.map +1 -0
  109. package/src/lib/engine/schedule.d.ts +70 -0
  110. package/src/lib/engine/schedule.js +115 -0
  111. package/src/lib/engine/schedule.js.map +1 -0
  112. package/src/lib/engine/sending-capacity.d.ts +96 -0
  113. package/src/lib/engine/sending-capacity.js +172 -0
  114. package/src/lib/engine/sending-capacity.js.map +1 -0
  115. package/src/lib/engine/sequence-validation.d.ts +90 -0
  116. package/src/lib/engine/sequence-validation.js +291 -0
  117. package/src/lib/engine/sequence-validation.js.map +1 -0
  118. package/src/lib/engine/thread-classification.d.ts +78 -0
  119. package/src/lib/engine/thread-classification.js +224 -0
  120. package/src/lib/engine/thread-classification.js.map +1 -0
  121. package/src/lib/engine/thread-message.d.ts +80 -0
  122. package/src/lib/engine/thread-message.js +143 -0
  123. package/src/lib/engine/thread-message.js.map +1 -0
  124. package/src/lib/enrollment/enroll-people.d.ts +81 -0
  125. package/src/lib/enrollment/enroll-people.js +250 -0
  126. package/src/lib/enrollment/enroll-people.js.map +1 -0
  127. package/src/lib/enrollment/gate-lookups.d.ts +32 -0
  128. package/src/lib/enrollment/gate-lookups.js +154 -0
  129. package/src/lib/enrollment/gate-lookups.js.map +1 -0
  130. package/src/lib/mailboxes/mailbox-api.d.ts +126 -0
  131. package/src/lib/mailboxes/mailbox-api.js +66 -0
  132. package/src/lib/mailboxes/mailbox-api.js.map +1 -0
  133. package/src/lib/mailboxes/mailbox-credentials.d.ts +95 -0
  134. package/src/lib/mailboxes/mailbox-credentials.js +102 -0
  135. package/src/lib/mailboxes/mailbox-credentials.js.map +1 -0
  136. package/src/lib/mailboxes/mailbox-erasure.d.ts +69 -0
  137. package/src/lib/mailboxes/mailbox-erasure.js +148 -0
  138. package/src/lib/mailboxes/mailbox-erasure.js.map +1 -0
  139. package/src/lib/mailboxes/mailbox-gate.d.ts +75 -0
  140. package/src/lib/mailboxes/mailbox-gate.js +98 -0
  141. package/src/lib/mailboxes/mailbox-gate.js.map +1 -0
  142. package/src/lib/mailboxes/mailbox-revoke.d.ts +59 -0
  143. package/src/lib/mailboxes/mailbox-revoke.js +41 -0
  144. package/src/lib/mailboxes/mailbox-revoke.js.map +1 -0
  145. package/src/lib/mailboxes/mailbox-routes.d.ts +119 -0
  146. package/src/lib/mailboxes/mailbox-routes.js +605 -0
  147. package/src/lib/mailboxes/mailbox-routes.js.map +1 -0
  148. package/src/lib/mailboxes/mailbox-settings.d.ts +110 -0
  149. package/src/lib/mailboxes/mailbox-settings.js +212 -0
  150. package/src/lib/mailboxes/mailbox-settings.js.map +1 -0
  151. package/src/lib/mailboxes/mailbox-transport.d.ts +64 -0
  152. package/src/lib/mailboxes/mailbox-transport.js +94 -0
  153. package/src/lib/mailboxes/mailbox-transport.js.map +1 -0
  154. package/src/lib/mailboxes/oauth-redirect.d.ts +41 -0
  155. package/src/lib/mailboxes/oauth-redirect.js +63 -0
  156. package/src/lib/mailboxes/oauth-redirect.js.map +1 -0
  157. package/src/lib/mailboxes/oauth-state.d.ts +152 -0
  158. package/src/lib/mailboxes/oauth-state.js +212 -0
  159. package/src/lib/mailboxes/oauth-state.js.map +1 -0
  160. package/src/lib/mailboxes/outreach-config.d.ts +64 -0
  161. package/src/lib/mailboxes/outreach-config.js +77 -0
  162. package/src/lib/mailboxes/outreach-config.js.map +1 -0
  163. package/src/lib/mailboxes/register-mailbox-routes.d.ts +47 -0
  164. package/src/lib/mailboxes/register-mailbox-routes.js +135 -0
  165. package/src/lib/mailboxes/register-mailbox-routes.js.map +1 -0
  166. package/src/lib/model/compliance-settings.d.ts +78 -0
  167. package/src/lib/model/compliance-settings.js +155 -0
  168. package/src/lib/model/compliance-settings.js.map +1 -0
  169. package/src/lib/model/outreach-api.d.ts +210 -0
  170. package/src/lib/model/outreach-api.js +28 -0
  171. package/src/lib/model/outreach-api.js.map +1 -0
  172. package/src/lib/model/outreach.types.d.ts +563 -0
  173. package/src/lib/model/outreach.types.js +225 -0
  174. package/src/lib/model/outreach.types.js.map +1 -0
  175. package/src/lib/model/sequence-draft.d.ts +84 -0
  176. package/src/lib/model/sequence-draft.js +239 -0
  177. package/src/lib/model/sequence-draft.js.map +1 -0
  178. package/src/lib/model/step-preview.d.ts +45 -0
  179. package/src/lib/model/step-preview.js +98 -0
  180. package/src/lib/model/step-preview.js.map +1 -0
  181. package/src/lib/model/stored-records.d.ts +23 -0
  182. package/src/lib/model/stored-records.js +84 -0
  183. package/src/lib/model/stored-records.js.map +1 -0
  184. package/src/lib/plugin.d.ts +46 -0
  185. package/src/lib/plugin.js +86 -0
  186. package/src/lib/plugin.js.map +1 -0
  187. package/src/lib/routes/enroll-routes.d.ts +64 -0
  188. package/src/lib/routes/enroll-routes.js +350 -0
  189. package/src/lib/routes/enroll-routes.js.map +1 -0
  190. package/src/lib/routes/enrollment-routes.d.ts +19 -0
  191. package/src/lib/routes/enrollment-routes.js +171 -0
  192. package/src/lib/routes/enrollment-routes.js.map +1 -0
  193. package/src/lib/routes/platform-deps.d.ts +51 -0
  194. package/src/lib/routes/platform-deps.js +66 -0
  195. package/src/lib/routes/platform-deps.js.map +1 -0
  196. package/src/lib/routes/preview-routes.d.ts +33 -0
  197. package/src/lib/routes/preview-routes.js +117 -0
  198. package/src/lib/routes/preview-routes.js.map +1 -0
  199. package/src/lib/routes/register-routes.d.ts +22 -0
  200. package/src/lib/routes/register-routes.js +92 -0
  201. package/src/lib/routes/register-routes.js.map +1 -0
  202. package/src/lib/routes/route-deps.d.ts +49 -0
  203. package/src/lib/routes/route-deps.js +27 -0
  204. package/src/lib/routes/route-deps.js.map +1 -0
  205. package/src/lib/routes/route-gate.d.ts +94 -0
  206. package/src/lib/routes/route-gate.js +87 -0
  207. package/src/lib/routes/route-gate.js.map +1 -0
  208. package/src/lib/routes/route-http.d.ts +32 -0
  209. package/src/lib/routes/route-http.js +62 -0
  210. package/src/lib/routes/route-http.js.map +1 -0
  211. package/src/lib/routes/sequence-routes.d.ts +32 -0
  212. package/src/lib/routes/sequence-routes.js +359 -0
  213. package/src/lib/routes/sequence-routes.js.map +1 -0
  214. package/src/lib/routes/settings-routes.d.ts +30 -0
  215. package/src/lib/routes/settings-routes.js +73 -0
  216. package/src/lib/routes/settings-routes.js.map +1 -0
  217. package/src/lib/runtime/enrollment-events.d.ts +86 -0
  218. package/src/lib/runtime/enrollment-events.js +141 -0
  219. package/src/lib/runtime/enrollment-events.js.map +1 -0
  220. package/src/lib/runtime/fixtures/fake-gmail.d.ts +121 -0
  221. package/src/lib/runtime/fixtures/fake-gmail.js +312 -0
  222. package/src/lib/runtime/fixtures/fake-gmail.js.map +1 -0
  223. package/src/lib/runtime/mailbox-health-store.d.ts +47 -0
  224. package/src/lib/runtime/mailbox-health-store.js +168 -0
  225. package/src/lib/runtime/mailbox-health-store.js.map +1 -0
  226. package/src/lib/runtime/person-erasure.d.ts +39 -0
  227. package/src/lib/runtime/person-erasure.js +57 -0
  228. package/src/lib/runtime/person-erasure.js.map +1 -0
  229. package/src/lib/runtime/platform-runtime-deps.d.ts +29 -0
  230. package/src/lib/runtime/platform-runtime-deps.js +98 -0
  231. package/src/lib/runtime/platform-runtime-deps.js.map +1 -0
  232. package/src/lib/runtime/runtime-deps.d.ts +77 -0
  233. package/src/lib/runtime/runtime-deps.js +18 -0
  234. package/src/lib/runtime/runtime-deps.js.map +1 -0
  235. package/src/lib/runtime/send-job.d.ts +42 -0
  236. package/src/lib/runtime/send-job.js +780 -0
  237. package/src/lib/runtime/send-job.js.map +1 -0
  238. package/src/lib/runtime/sync-job.d.ts +41 -0
  239. package/src/lib/runtime/sync-job.js +480 -0
  240. package/src/lib/runtime/sync-job.js.map +1 -0
  241. package/src/lib/runtime/timeline.d.ts +67 -0
  242. package/src/lib/runtime/timeline.js +93 -0
  243. package/src/lib/runtime/timeline.js.map +1 -0
  244. package/src/lib/runtime/unsubscribe-link.d.ts +75 -0
  245. package/src/lib/runtime/unsubscribe-link.js +121 -0
  246. package/src/lib/runtime/unsubscribe-link.js.map +1 -0
  247. package/src/lib/runtime/unsubscribe-route.d.ts +24 -0
  248. package/src/lib/runtime/unsubscribe-route.js +103 -0
  249. package/src/lib/runtime/unsubscribe-route.js.map +1 -0
  250. package/src/lib/server.d.ts +53 -0
  251. package/src/lib/server.js +94 -0
  252. package/src/lib/server.js.map +1 -0
  253. package/src/lib/storage/compliance-settings-store.d.ts +44 -0
  254. package/src/lib/storage/compliance-settings-store.js +78 -0
  255. package/src/lib/storage/compliance-settings-store.js.map +1 -0
  256. package/src/lib/storage/do-not-contact-store.d.ts +60 -0
  257. package/src/lib/storage/do-not-contact-store.js +168 -0
  258. package/src/lib/storage/do-not-contact-store.js.map +1 -0
  259. package/src/lib/storage/outreach-records.d.ts +33 -0
  260. package/src/lib/storage/outreach-records.js +35 -0
  261. package/src/lib/storage/outreach-records.js.map +1 -0
  262. package/src/lib/subprocessors.d.ts +48 -0
  263. package/src/lib/subprocessors.js +70 -0
  264. package/src/lib/subprocessors.js.map +1 -0
  265. package/src/lib/transport/gmail-client.d.ts +145 -0
  266. package/src/lib/transport/gmail-client.js +229 -0
  267. package/src/lib/transport/gmail-client.js.map +1 -0
  268. package/src/lib/transport/gmail-errors.d.ts +84 -0
  269. package/src/lib/transport/gmail-errors.js +138 -0
  270. package/src/lib/transport/gmail-errors.js.map +1 -0
  271. package/src/lib/transport/google-oauth.d.ts +138 -0
  272. package/src/lib/transport/google-oauth.js +247 -0
  273. package/src/lib/transport/google-oauth.js.map +1 -0
  274. package/src/lib/transport/http.d.ts +53 -0
  275. package/src/lib/transport/http.js +79 -0
  276. package/src/lib/transport/http.js.map +1 -0
  277. package/src/lib/transport/rfc5322.d.ts +116 -0
  278. package/src/lib/transport/rfc5322.js +330 -0
  279. package/src/lib/transport/rfc5322.js.map +1 -0
  280. package/src/lib/transport/send-message.d.ts +69 -0
  281. package/src/lib/transport/send-message.js +77 -0
  282. package/src/lib/transport/send-message.js.map +1 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { outreachDoNotContactCollection } from "../storage/do-not-contact-store.js";
17
+ import { outreachOrgCollection } from "../storage/outreach-records.js";
18
+ /** Firestore refuses a batch of more than 500 writes. */ const BATCH_LIMIT = 450;
19
+ /** `in` takes this many values at most. */ const IN_LIMIT = 30;
20
+ export function createOutreachPersonEraser(deps) {
21
+ return async ({ orgId, email, key, contactIds, dryRun })=>{
22
+ const firestore = deps.firestore();
23
+ const enrollments = outreachOrgCollection(firestore, orgId, 'enrollments');
24
+ const found = new Map();
25
+ for (const doc of (await enrollments.where('email', '==', email).get()).docs)found.set(doc.id, doc.ref);
26
+ for(let start = 0; start < contactIds.length; start += IN_LIMIT){
27
+ const chunk = contactIds.slice(start, start + IN_LIMIT);
28
+ for (const doc of (await enrollments.where('contactId', 'in', chunk).get()).docs)found.set(doc.id, doc.ref);
29
+ }
30
+ const entry = outreachDoNotContactCollection(firestore, orgId).doc(key);
31
+ const listed = (await entry.get()).exists;
32
+ if (dryRun) return {
33
+ enrollments: found.size,
34
+ doNotContactKept: listed,
35
+ doNotContactScrubbed: null
36
+ };
37
+ const refs = [
38
+ ...found.values()
39
+ ];
40
+ for(let start = 0; start < refs.length; start += BATCH_LIMIT){
41
+ const batch = firestore.batch();
42
+ for (const ref of refs.slice(start, start + BATCH_LIMIT))batch.delete(ref);
43
+ await batch.commit();
44
+ }
45
+ if (listed) await entry.update({
46
+ detail: null,
47
+ enrollmentId: null
48
+ });
49
+ return {
50
+ enrollments: refs.length,
51
+ doNotContactKept: listed,
52
+ doNotContactScrubbed: listed
53
+ };
54
+ };
55
+ }
56
+
57
+ //# sourceMappingURL=person-erasure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/runtime/person-erasure.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n PluginPersonEraser,\n PluginPersonErasureReport,\n} from '@aglyn/aglyn/plugin-manager/plugin-person-erasure'\nimport { outreachDoNotContactCollection } from '../storage/do-not-contact-store'\nimport { outreachOrgCollection } from '../storage/outreach-records'\n\n/**\n * OUTREACH'S SHARE OF A PERSON ERASURE (AGL-2981).\n *\n * The person's ENROLLMENTS go: each names them — their address, their\n * contact, the personal line a rep wrote about them, the attestations — and\n * is about them. They are found by the address and by the contacts the\n * erasure is deleting, and deleted whatever their status.\n *\n * Their DO-NOT-CONTACT entry STAYS, on the rule every suppression list\n * follows through an erasure: it is keyed by `personKey`, carries no\n * address, and is the one promise the workspace made the person that has to\n * outlive their data. What in it could point back at them is cleared: the\n * detail — a member's note, a bounce's diagnostic — and the enrollment it\n * came from, which no longer exists.\n *\n * A dry run counts both and writes nothing.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\n/** Firestore refuses a batch of more than 500 writes. */\nconst BATCH_LIMIT = 450\n\n/** `in` takes this many values at most. */\nconst IN_LIMIT = 30\n\nexport function createOutreachPersonEraser(deps: { firestore(): Firestore }): PluginPersonEraser {\n return async ({ orgId, email, key, contactIds, dryRun }): Promise<PluginPersonErasureReport> => {\n const firestore = deps.firestore()\n const enrollments = outreachOrgCollection(firestore, orgId, 'enrollments')\n const found = new Map<string, FirebaseFirestore.DocumentReference>()\n for (const doc of (await enrollments.where('email', '==', email).get()).docs) found.set(doc.id, doc.ref)\n for (let start = 0; start < contactIds.length; start += IN_LIMIT) {\n const chunk = contactIds.slice(start, start + IN_LIMIT)\n for (const doc of (await enrollments.where('contactId', 'in', chunk).get()).docs) found.set(doc.id, doc.ref)\n }\n const entry = outreachDoNotContactCollection(firestore, orgId).doc(key)\n const listed = (await entry.get()).exists\n if (dryRun) return { enrollments: found.size, doNotContactKept: listed, doNotContactScrubbed: null }\n\n const refs = [...found.values()]\n for (let start = 0; start < refs.length; start += BATCH_LIMIT) {\n const batch = firestore.batch()\n for (const ref of refs.slice(start, start + BATCH_LIMIT)) batch.delete(ref)\n await batch.commit()\n }\n if (listed) await entry.update({ detail: null, enrollmentId: null })\n return { enrollments: refs.length, doNotContactKept: listed, doNotContactScrubbed: listed }\n }\n}\n"],"names":["outreachDoNotContactCollection","outreachOrgCollection","BATCH_LIMIT","IN_LIMIT","createOutreachPersonEraser","deps","orgId","email","key","contactIds","dryRun","firestore","enrollments","found","Map","doc","where","get","docs","set","id","ref","start","length","chunk","slice","entry","listed","exists","size","doNotContactKept","doNotContactScrubbed","refs","values","batch","delete","commit","update","detail","enrollmentId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAMD,SAASA,8BAA8B,QAAQ,qCAAiC;AAChF,SAASC,qBAAqB,QAAQ,iCAA6B;AAsBnE,uDAAuD,GACvD,MAAMC,cAAc;AAEpB,yCAAyC,GACzC,MAAMC,WAAW;AAEjB,OAAO,SAASC,2BAA2BC,IAAgC;IACzE,OAAO,OAAO,EAAEC,KAAK,EAAEC,KAAK,EAAEC,GAAG,EAAEC,UAAU,EAAEC,MAAM,EAAE;QACrD,MAAMC,YAAYN,KAAKM,SAAS;QAChC,MAAMC,cAAcX,sBAAsBU,WAAWL,OAAO;QAC5D,MAAMO,QAAQ,IAAIC;QAClB,KAAK,MAAMC,OAAO,AAAC,CAAA,MAAMH,YAAYI,KAAK,CAAC,SAAS,MAAMT,OAAOU,GAAG,EAAC,EAAGC,IAAI,CAAEL,MAAMM,GAAG,CAACJ,IAAIK,EAAE,EAAEL,IAAIM,GAAG;QACvG,IAAK,IAAIC,QAAQ,GAAGA,QAAQb,WAAWc,MAAM,EAAED,SAASnB,SAAU;YAChE,MAAMqB,QAAQf,WAAWgB,KAAK,CAACH,OAAOA,QAAQnB;YAC9C,KAAK,MAAMY,OAAO,AAAC,CAAA,MAAMH,YAAYI,KAAK,CAAC,aAAa,MAAMQ,OAAOP,GAAG,EAAC,EAAGC,IAAI,CAAEL,MAAMM,GAAG,CAACJ,IAAIK,EAAE,EAAEL,IAAIM,GAAG;QAC7G;QACA,MAAMK,QAAQ1B,+BAA+BW,WAAWL,OAAOS,GAAG,CAACP;QACnE,MAAMmB,SAAS,AAAC,CAAA,MAAMD,MAAMT,GAAG,EAAC,EAAGW,MAAM;QACzC,IAAIlB,QAAQ,OAAO;YAAEE,aAAaC,MAAMgB,IAAI;YAAEC,kBAAkBH;YAAQI,sBAAsB;QAAK;QAEnG,MAAMC,OAAO;eAAInB,MAAMoB,MAAM;SAAG;QAChC,IAAK,IAAIX,QAAQ,GAAGA,QAAQU,KAAKT,MAAM,EAAED,SAASpB,YAAa;YAC7D,MAAMgC,QAAQvB,UAAUuB,KAAK;YAC7B,KAAK,MAAMb,OAAOW,KAAKP,KAAK,CAACH,OAAOA,QAAQpB,aAAcgC,MAAMC,MAAM,CAACd;YACvE,MAAMa,MAAME,MAAM;QACpB;QACA,IAAIT,QAAQ,MAAMD,MAAMW,MAAM,CAAC;YAAEC,QAAQ;YAAMC,cAAc;QAAK;QAClE,OAAO;YAAE3B,aAAaoB,KAAKT,MAAM;YAAEO,kBAAkBH;YAAQI,sBAAsBJ;QAAO;IAC5F;AACF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { PluginWebApiHandler } from '@aglyn/aglyn/server';
18
+ import type { PluginPersonEraser } from '@aglyn/aglyn/plugin-manager/plugin-person-erasure';
19
+ import type { OutreachRuntimeDeps } from './runtime-deps';
20
+ /**
21
+ * The platform's own reach for the sending runtime (AGL-2981). Loaded only
22
+ * when a job runs or the unsubscribe route answers — never at boot — so the
23
+ * console pays for these modules when Outreach does work.
24
+ */
25
+ export declare function platformOutreachRuntimeDeps(): OutreachRuntimeDeps;
26
+ /** The unsubscribe route on the platform's own reach. */
27
+ export declare function platformOutreachUnsubscribeRoute(): PluginWebApiHandler;
28
+ /** The person eraser on the platform's own Firestore. */
29
+ export declare function platformOutreachPersonEraser(): PluginPersonEraser;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { EMAIL_TOPIC_SALES } from "@aglyn/aglyn/app-utils/email-topics";
17
+ import { checkEntitlement } from "@aglyn/aglyn/app-utils/plan-entitlements";
18
+ import { isPluginEnabled } from "@aglyn/aglyn/plugin-manager/enabled-plugins";
19
+ import { pluginRecordTimelineWriter } from "@aglyn/aglyn/plugin-manager/plugin-record-timeline";
20
+ import { suppressEmail } from "@aglyn/tenant-data-admin/server/email-suppression";
21
+ import { recordTopicOptOut } from "@aglyn/tenant-data-admin/server/email-topic-confirmation";
22
+ import { firebaseAdmin } from "@aglyn/tenant-data-admin/server/firebase-admin";
23
+ import { getLockdownVerdict } from "@aglyn/tenant-data-admin/server/lockdown";
24
+ import { logOrgActivity } from "@aglyn/tenant-data-admin/server/organizations";
25
+ import { filterEnabledPluginsByReleaseFlags } from "@aglyn/tenant-data-admin/server/release-flags";
26
+ import { OUTREACH_PLUGIN_ID } from "../constants/bundle-common.js";
27
+ import { openOutreachMailboxClient } from "../mailboxes/mailbox-transport.js";
28
+ import { canonicalConsoleOrigin } from "../mailboxes/oauth-redirect.js";
29
+ import { createOutreachPersonEraser } from "./person-erasure.js";
30
+ import { outreachUnsubscribeUrl } from "./unsubscribe-link.js";
31
+ import { createOutreachUnsubscribeRoute } from "./unsubscribe-route.js";
32
+ /**
33
+ * The platform's own reach for the sending runtime (AGL-2981). Loaded only
34
+ * when a job runs or the unsubscribe route answers — never at boot — so the
35
+ * console pays for these modules when Outreach does work.
36
+ */ export function platformOutreachRuntimeDeps() {
37
+ const firestore = ()=>firebaseAdmin.app().firestore();
38
+ return {
39
+ firestore,
40
+ now: Date.now,
41
+ random: Math.random,
42
+ openMailbox: (mailboxId)=>openOutreachMailboxClient(firestore(), {
43
+ mailboxId
44
+ }),
45
+ async orgRefusal (orgId, org) {
46
+ if (!isPluginEnabled(org, OUTREACH_PLUGIN_ID)) return 'plugin-disabled';
47
+ if (!checkEntitlement(org, 'outreach')) return 'not-entitled';
48
+ const released = await filterEnabledPluginsByReleaseFlags([
49
+ OUTREACH_PLUGIN_ID
50
+ ], {
51
+ orgId
52
+ });
53
+ return released.length ? null : 'not-released';
54
+ },
55
+ async sendRefusal ({ org, uid }) {
56
+ const locked = await getLockdownVerdict({
57
+ org,
58
+ uid,
59
+ intent: 'write'
60
+ });
61
+ return locked ? `locked:${locked.scope}` : null;
62
+ },
63
+ timeline: ()=>{
64
+ var _ref;
65
+ var _pluginRecordTimelineWriter;
66
+ return (_ref = (_pluginRecordTimelineWriter = pluginRecordTimelineWriter()) == null ? void 0 : _pluginRecordTimelineWriter.writer) != null ? _ref : null;
67
+ },
68
+ logOrgActivity: (orgId, actor, action, target)=>logOrgActivity(orgId, actor, action, target),
69
+ async optOutOfSalesTopic ({ hostId, email }) {
70
+ await recordTopicOptOut(hostId, email, EMAIL_TOPIC_SALES, {
71
+ firestore: firestore()
72
+ });
73
+ },
74
+ async suppressBouncedEmail ({ email, hostId }) {
75
+ await suppressEmail({
76
+ email,
77
+ reason: 'bounce',
78
+ context: 'outreach',
79
+ hostId,
80
+ firestore: firestore()
81
+ });
82
+ },
83
+ unsubscribeUrl: (target)=>outreachUnsubscribeUrl({
84
+ origin: canonicalConsoleOrigin(),
85
+ target
86
+ })
87
+ };
88
+ }
89
+ /** The unsubscribe route on the platform's own reach. */ export function platformOutreachUnsubscribeRoute() {
90
+ return createOutreachUnsubscribeRoute(platformOutreachRuntimeDeps());
91
+ }
92
+ /** The person eraser on the platform's own Firestore. */ export function platformOutreachPersonEraser() {
93
+ return createOutreachPersonEraser({
94
+ firestore: ()=>firebaseAdmin.app().firestore()
95
+ });
96
+ }
97
+
98
+ //# sourceMappingURL=platform-runtime-deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/runtime/platform-runtime-deps.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { EMAIL_TOPIC_SALES } from '@aglyn/aglyn/app-utils/email-topics'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport { pluginRecordTimelineWriter } from '@aglyn/aglyn/plugin-manager/plugin-record-timeline'\nimport { suppressEmail } from '@aglyn/tenant-data-admin/server/email-suppression'\nimport { recordTopicOptOut } from '@aglyn/tenant-data-admin/server/email-topic-confirmation'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { getLockdownVerdict } from '@aglyn/tenant-data-admin/server/lockdown'\nimport { logOrgActivity } from '@aglyn/tenant-data-admin/server/organizations'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { OUTREACH_PLUGIN_ID } from '../constants/bundle-common'\nimport { openOutreachMailboxClient } from '../mailboxes/mailbox-transport'\nimport { canonicalConsoleOrigin } from '../mailboxes/oauth-redirect'\nimport type { PluginWebApiHandler } from '@aglyn/aglyn/server'\nimport type { PluginPersonEraser } from '@aglyn/aglyn/plugin-manager/plugin-person-erasure'\nimport { createOutreachPersonEraser } from './person-erasure'\nimport type { OutreachRuntimeDeps } from './runtime-deps'\nimport { outreachUnsubscribeUrl } from './unsubscribe-link'\nimport { createOutreachUnsubscribeRoute } from './unsubscribe-route'\n\n/**\n * The platform's own reach for the sending runtime (AGL-2981). Loaded only\n * when a job runs or the unsubscribe route answers — never at boot — so the\n * console pays for these modules when Outreach does work.\n */\nexport function platformOutreachRuntimeDeps(): OutreachRuntimeDeps {\n const firestore = () => firebaseAdmin.app().firestore()\n return {\n firestore,\n now: Date.now,\n random: Math.random,\n openMailbox: (mailboxId) => openOutreachMailboxClient(firestore(), { mailboxId }),\n async orgRefusal(orgId, org) {\n if (!isPluginEnabled(org as { enabledPlugins?: string[] }, OUTREACH_PLUGIN_ID)) return 'plugin-disabled'\n if (!checkEntitlement(org, 'outreach')) return 'not-entitled'\n const released = await filterEnabledPluginsByReleaseFlags([OUTREACH_PLUGIN_ID], { orgId })\n return released.length ? null : 'not-released'\n },\n async sendRefusal({ org, uid }) {\n const locked = await getLockdownVerdict({ org, uid, intent: 'write' })\n return locked ? `locked:${locked.scope}` : null\n },\n timeline: () => pluginRecordTimelineWriter()?.writer ?? null,\n logOrgActivity: (orgId, actor, action, target) => logOrgActivity(orgId, actor, action, target),\n async optOutOfSalesTopic({ hostId, email }) {\n await recordTopicOptOut(hostId, email, EMAIL_TOPIC_SALES, { firestore: firestore() })\n },\n async suppressBouncedEmail({ email, hostId }) {\n await suppressEmail({ email, reason: 'bounce', context: 'outreach', hostId, firestore: firestore() })\n },\n unsubscribeUrl: (target) => outreachUnsubscribeUrl({ origin: canonicalConsoleOrigin(), target }),\n }\n}\n\n/** The unsubscribe route on the platform's own reach. */\nexport function platformOutreachUnsubscribeRoute(): PluginWebApiHandler {\n return createOutreachUnsubscribeRoute(platformOutreachRuntimeDeps())\n}\n\n/** The person eraser on the platform's own Firestore. */\nexport function platformOutreachPersonEraser(): PluginPersonEraser {\n return createOutreachPersonEraser({ firestore: () => firebaseAdmin.app().firestore() })\n}\n"],"names":["EMAIL_TOPIC_SALES","checkEntitlement","isPluginEnabled","pluginRecordTimelineWriter","suppressEmail","recordTopicOptOut","firebaseAdmin","getLockdownVerdict","logOrgActivity","filterEnabledPluginsByReleaseFlags","OUTREACH_PLUGIN_ID","openOutreachMailboxClient","canonicalConsoleOrigin","createOutreachPersonEraser","outreachUnsubscribeUrl","createOutreachUnsubscribeRoute","platformOutreachRuntimeDeps","firestore","app","now","Date","random","Math","openMailbox","mailboxId","orgRefusal","orgId","org","released","length","sendRefusal","uid","locked","intent","scope","timeline","writer","actor","action","target","optOutOfSalesTopic","hostId","email","suppressBouncedEmail","reason","context","unsubscribeUrl","origin","platformOutreachUnsubscribeRoute","platformOutreachPersonEraser"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,iBAAiB,QAAQ,sCAAqC;AACvE,SAASC,gBAAgB,QAAQ,2CAA0C;AAC3E,SAASC,eAAe,QAAQ,8CAA6C;AAC7E,SAASC,0BAA0B,QAAQ,qDAAoD;AAC/F,SAASC,aAAa,QAAQ,oDAAmD;AACjF,SAASC,iBAAiB,QAAQ,2DAA0D;AAC5F,SAASC,aAAa,QAAQ,iDAAgD;AAC9E,SAASC,kBAAkB,QAAQ,2CAA0C;AAC7E,SAASC,cAAc,QAAQ,gDAA+C;AAC9E,SAASC,kCAAkC,QAAQ,gDAA+C;AAClG,SAASC,kBAAkB,QAAQ,gCAA4B;AAC/D,SAASC,yBAAyB,QAAQ,oCAAgC;AAC1E,SAASC,sBAAsB,QAAQ,iCAA6B;AAGpE,SAASC,0BAA0B,QAAQ,sBAAkB;AAE7D,SAASC,sBAAsB,QAAQ,wBAAoB;AAC3D,SAASC,8BAA8B,QAAQ,yBAAqB;AAEpE;;;;CAIC,GACD,OAAO,SAASC;IACd,MAAMC,YAAY,IAAMX,cAAcY,GAAG,GAAGD,SAAS;IACrD,OAAO;QACLA;QACAE,KAAKC,KAAKD,GAAG;QACbE,QAAQC,KAAKD,MAAM;QACnBE,aAAa,CAACC,YAAcb,0BAA0BM,aAAa;gBAAEO;YAAU;QAC/E,MAAMC,YAAWC,KAAK,EAAEC,GAAG;YACzB,IAAI,CAACzB,gBAAgByB,KAAsCjB,qBAAqB,OAAO;YACvF,IAAI,CAACT,iBAAiB0B,KAAK,aAAa,OAAO;YAC/C,MAAMC,WAAW,MAAMnB,mCAAmC;gBAACC;aAAmB,EAAE;gBAAEgB;YAAM;YACxF,OAAOE,SAASC,MAAM,GAAG,OAAO;QAClC;QACA,MAAMC,aAAY,EAAEH,GAAG,EAAEI,GAAG,EAAE;YAC5B,MAAMC,SAAS,MAAMzB,mBAAmB;gBAAEoB;gBAAKI;gBAAKE,QAAQ;YAAQ;YACpE,OAAOD,SAAS,CAAC,OAAO,EAAEA,OAAOE,KAAK,EAAE,GAAG;QAC7C;QACAC,UAAU;;gBAAMhC;4BAAAA,8BAAAA,iDAAAA,4BAA8BiC,MAAM,mBAAI;;QACxD5B,gBAAgB,CAACkB,OAAOW,OAAOC,QAAQC,SAAW/B,eAAekB,OAAOW,OAAOC,QAAQC;QACvF,MAAMC,oBAAmB,EAAEC,MAAM,EAAEC,KAAK,EAAE;YACxC,MAAMrC,kBAAkBoC,QAAQC,OAAO1C,mBAAmB;gBAAEiB,WAAWA;YAAY;QACrF;QACA,MAAM0B,sBAAqB,EAAED,KAAK,EAAED,MAAM,EAAE;YAC1C,MAAMrC,cAAc;gBAAEsC;gBAAOE,QAAQ;gBAAUC,SAAS;gBAAYJ;gBAAQxB,WAAWA;YAAY;QACrG;QACA6B,gBAAgB,CAACP,SAAWzB,uBAAuB;gBAAEiC,QAAQnC;gBAA0B2B;YAAO;IAChG;AACF;AAEA,uDAAuD,GACvD,OAAO,SAASS;IACd,OAAOjC,+BAA+BC;AACxC;AAEA,uDAAuD,GACvD,OAAO,SAASiC;IACd,OAAOpC,2BAA2B;QAAEI,WAAW,IAAMX,cAAcY,GAAG,GAAGD,SAAS;IAAG;AACvF"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { PluginRecordTimelineWriter } from '@aglyn/aglyn/plugin-manager/plugin-record-timeline';
18
+ import type { OpenedOutreachMailbox } from '../mailboxes/mailbox-transport';
19
+ import type { OutreachUnsubscribeTarget } from './unsubscribe-link';
20
+ /**
21
+ * EVERYTHING THE SENDING RUNTIME REACHES OUTSIDE ITS OWN DOCUMENTS (AGL-2981).
22
+ *
23
+ * The send and sync jobs and the unsubscribe route read and write Outreach's
24
+ * collections through the Firestore they are handed, and reach everything
25
+ * else through these: the mailbox's Gmail, the record system, the platform's
26
+ * suppression and topic lists, the activity feed and the gates that decide
27
+ * whether Outreach may act for a workspace at all. The platform's own are
28
+ * `platform-runtime-deps.ts`; the emulator specs hand in a fake Gmail and
29
+ * recorders, so no test reaches Google or sends mail.
30
+ */
31
+ /** A target an org activity line written by the runtime points at. */
32
+ export interface OutreachRuntimeActivityTarget {
33
+ type: `${string}:${string}`;
34
+ id: string;
35
+ name: string;
36
+ }
37
+ export interface OutreachRuntimeDeps {
38
+ firestore(): FirebaseFirestore.Firestore;
39
+ now(): number;
40
+ /** A source of numbers in `[0, 1)` — the scheduler's jitter. */
41
+ random(): number;
42
+ /** Opens a mailbox's Gmail client from its sealed grant. */
43
+ openMailbox(mailboxId: string): Promise<OpenedOutreachMailbox>;
44
+ /**
45
+ * Why Outreach may not act for this workspace now, or `null` when it may:
46
+ * the plugin switched on and entitled, and its release flag on for the
47
+ * organization.
48
+ */
49
+ orgRefusal(orgId: string, org: Record<string, unknown>): Promise<string | null>;
50
+ /**
51
+ * Why nothing may be sent as this member now — a lockdown of the platform,
52
+ * the workspace or the member — or `null`.
53
+ */
54
+ sendRefusal(input: {
55
+ orgId: string;
56
+ org: Record<string, unknown>;
57
+ uid: string;
58
+ }): Promise<string | null>;
59
+ /** The workspace's record system on the timeline seam, when a plugin keeps one. */
60
+ timeline(): PluginRecordTimelineWriter | null;
61
+ logOrgActivity(orgId: string, actor: {
62
+ uid: string | null;
63
+ email?: string | null;
64
+ }, action: string, target: OutreachRuntimeActivityTarget): Promise<void>;
65
+ /** Takes an address off one site's `sales` email topic. */
66
+ optOutOfSalesTopic(input: {
67
+ hostId: string;
68
+ email: string;
69
+ }): Promise<void>;
70
+ /** Files a hard bounce on the platform's suppression list. */
71
+ suppressBouncedEmail(input: {
72
+ email: string;
73
+ hostId: string | null;
74
+ }): Promise<void>;
75
+ /** The signed one-click link for an enrollment, or `null` when none can be minted. */
76
+ unsubscribeUrl(target: OutreachUnsubscribeTarget): string | null;
77
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ export { };
17
+
18
+ //# sourceMappingURL=runtime-deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/runtime/runtime-deps.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginRecordTimelineWriter } from '@aglyn/aglyn/plugin-manager/plugin-record-timeline'\nimport type { OpenedOutreachMailbox } from '../mailboxes/mailbox-transport'\nimport type { OutreachUnsubscribeTarget } from './unsubscribe-link'\n\n/**\n * EVERYTHING THE SENDING RUNTIME REACHES OUTSIDE ITS OWN DOCUMENTS (AGL-2981).\n *\n * The send and sync jobs and the unsubscribe route read and write Outreach's\n * collections through the Firestore they are handed, and reach everything\n * else through these: the mailbox's Gmail, the record system, the platform's\n * suppression and topic lists, the activity feed and the gates that decide\n * whether Outreach may act for a workspace at all. The platform's own are\n * `platform-runtime-deps.ts`; the emulator specs hand in a fake Gmail and\n * recorders, so no test reaches Google or sends mail.\n */\n\n/** A target an org activity line written by the runtime points at. */\nexport interface OutreachRuntimeActivityTarget {\n type: `${string}:${string}`\n id: string\n name: string\n}\n\nexport interface OutreachRuntimeDeps {\n firestore(): FirebaseFirestore.Firestore\n now(): number\n /** A source of numbers in `[0, 1)` — the scheduler's jitter. */\n random(): number\n /** Opens a mailbox's Gmail client from its sealed grant. */\n openMailbox(mailboxId: string): Promise<OpenedOutreachMailbox>\n /**\n * Why Outreach may not act for this workspace now, or `null` when it may:\n * the plugin switched on and entitled, and its release flag on for the\n * organization.\n */\n orgRefusal(orgId: string, org: Record<string, unknown>): Promise<string | null>\n /**\n * Why nothing may be sent as this member now — a lockdown of the platform,\n * the workspace or the member — or `null`.\n */\n sendRefusal(input: { orgId: string; org: Record<string, unknown>; uid: string }): Promise<string | null>\n /** The workspace's record system on the timeline seam, when a plugin keeps one. */\n timeline(): PluginRecordTimelineWriter | null\n logOrgActivity(\n orgId: string,\n actor: { uid: string | null; email?: string | null },\n action: string,\n target: OutreachRuntimeActivityTarget,\n ): Promise<void>\n /** Takes an address off one site's `sales` email topic. */\n optOutOfSalesTopic(input: { hostId: string; email: string }): Promise<void>\n /** Files a hard bounce on the platform's suppression list. */\n suppressBouncedEmail(input: { email: string; hostId: string | null }): Promise<void>\n /** The signed one-click link for an enrollment, or `null` when none can be minted. */\n unsubscribeUrl(target: OutreachUnsubscribeTarget): string | null\n}\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAyBD,WAgCC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { OutreachRuntimeDeps } from './runtime-deps';
18
+ export { OUTREACH_SEND_JOB_ID } from '../constants/runtime-jobs';
19
+ /** The most due enrollments one run reads. */
20
+ export declare const OUTREACH_SEND_SCAN_LIMIT = 300;
21
+ /** How long a claim holds before a later run settles it. */
22
+ export declare const OUTREACH_CLAIM_STALE_MS: number;
23
+ /** What one run did, for the tick's answer. */
24
+ export interface OutreachSendReport {
25
+ [key: string]: number;
26
+ due: number;
27
+ sent: number;
28
+ tasks: number;
29
+ /** Stopped by a gate or an unusable mailbox. */
30
+ stopped: number;
31
+ /** Paused for the rep to fix, or failed for good. */
32
+ failed: number;
33
+ /** Due, and left for a later run: the allowance, a lookup, a hold. */
34
+ held: number;
35
+ /** Claims a dead run left, settled. */
36
+ recovered: number;
37
+ }
38
+ /** Runs the send job once — see the module note. */
39
+ export declare function runOutreachSendJob(deps: OutreachRuntimeDeps, context: {
40
+ nowMs: number;
41
+ deadlineMs: number;
42
+ }): Promise<OutreachSendReport>;