@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,51 @@
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 { lockdownRefusal } from '@aglyn/tenant-data-admin/server/lockdown';
18
+ import { logOrgActivity } from '@aglyn/tenant-data-admin/server/organizations';
19
+ import { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions';
20
+ /**
21
+ * The platform's heavier server modules the Outreach routes reach
22
+ * (AGL-2980): the permission resolver, the lockdown verdict, the activity
23
+ * log and the saved-view sweep.
24
+ *
25
+ * Imported here statically, and this module is loaded by
26
+ * `register-routes.ts` the first time a request needs one of them: the
27
+ * manifest loads Outreach's server bundle into every console API process
28
+ * whether or not an Outreach route is ever called, and `org-permissions`
29
+ * alone reaches the whole tenant data barrel. Loading them through one
30
+ * module of this plugin keeps every import of the platform's libraries a
31
+ * static one.
32
+ */
33
+ export { lockdownRefusal, logOrgActivity, resolveOrgPermissions };
34
+ /**
35
+ * Whether the member holds a CATALOG permission (`data.manage`) as the
36
+ * org's roles resolve it — custom role and per-member overrides included.
37
+ * A lookup that failed has not shown the member holds it.
38
+ */
39
+ export declare function holdsOrgCatalogPermission(uid: string, orgId: string, key: string): Promise<boolean>;
40
+ /**
41
+ * The addresses a saved Contacts view selects among one site's contacts,
42
+ * read by the dynamic-list sweep's own scan, and whether it reached the
43
+ * whole view.
44
+ */
45
+ export declare function crmViewEmails(input: {
46
+ hostId: string;
47
+ viewId: string;
48
+ }): Promise<{
49
+ emails: string[];
50
+ complete: boolean;
51
+ }>;
@@ -0,0 +1,66 @@
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 { collectDynamicListCandidates } from "@aglyn/tenant-data-admin/server/dynamic-list-materialize";
17
+ import { lockdownRefusal } from "@aglyn/tenant-data-admin/server/lockdown";
18
+ import { logOrgActivity, memberHasOrgPermission, resolveOrgMembership } from "@aglyn/tenant-data-admin/server/organizations";
19
+ import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
20
+ /**
21
+ * The platform's heavier server modules the Outreach routes reach
22
+ * (AGL-2980): the permission resolver, the lockdown verdict, the activity
23
+ * log and the saved-view sweep.
24
+ *
25
+ * Imported here statically, and this module is loaded by
26
+ * `register-routes.ts` the first time a request needs one of them: the
27
+ * manifest loads Outreach's server bundle into every console API process
28
+ * whether or not an Outreach route is ever called, and `org-permissions`
29
+ * alone reaches the whole tenant data barrel. Loading them through one
30
+ * module of this plugin keeps every import of the platform's libraries a
31
+ * static one.
32
+ */ export { lockdownRefusal, logOrgActivity, resolveOrgPermissions };
33
+ /**
34
+ * Whether the member holds a CATALOG permission (`data.manage`) as the
35
+ * org's roles resolve it — custom role and per-member overrides included.
36
+ * A lookup that failed has not shown the member holds it.
37
+ */ export async function holdsOrgCatalogPermission(uid, orgId, key) {
38
+ try {
39
+ const membership = await resolveOrgMembership(uid, orgId);
40
+ if (!(membership == null ? void 0 : membership.member)) return false;
41
+ return await memberHasOrgPermission(orgId, membership.member, key) === true;
42
+ } catch (unused) {
43
+ return false;
44
+ }
45
+ }
46
+ /**
47
+ * The addresses a saved Contacts view selects among one site's contacts,
48
+ * read by the dynamic-list sweep's own scan, and whether it reached the
49
+ * whole view.
50
+ */ export async function crmViewEmails(input) {
51
+ const scan = await collectDynamicListCandidates({
52
+ hostId: input.hostId,
53
+ rule: {
54
+ sources: [
55
+ 'contacts'
56
+ ],
57
+ viewId: input.viewId
58
+ }
59
+ });
60
+ return {
61
+ emails: scan.candidates.map((candidate)=>candidate.email),
62
+ complete: scan.complete
63
+ };
64
+ }
65
+
66
+ //# sourceMappingURL=platform-deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/routes/platform-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 { collectDynamicListCandidates } from '@aglyn/tenant-data-admin/server/dynamic-list-materialize'\nimport { lockdownRefusal } from '@aglyn/tenant-data-admin/server/lockdown'\nimport {\n logOrgActivity,\n memberHasOrgPermission,\n resolveOrgMembership,\n} from '@aglyn/tenant-data-admin/server/organizations'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\n\n/**\n * The platform's heavier server modules the Outreach routes reach\n * (AGL-2980): the permission resolver, the lockdown verdict, the activity\n * log and the saved-view sweep.\n *\n * Imported here statically, and this module is loaded by\n * `register-routes.ts` the first time a request needs one of them: the\n * manifest loads Outreach's server bundle into every console API process\n * whether or not an Outreach route is ever called, and `org-permissions`\n * alone reaches the whole tenant data barrel. Loading them through one\n * module of this plugin keeps every import of the platform's libraries a\n * static one.\n */\n\nexport { lockdownRefusal, logOrgActivity, resolveOrgPermissions }\n\n/**\n * Whether the member holds a CATALOG permission (`data.manage`) as the\n * org's roles resolve it — custom role and per-member overrides included.\n * A lookup that failed has not shown the member holds it.\n */\nexport async function holdsOrgCatalogPermission(uid: string, orgId: string, key: string): Promise<boolean> {\n try {\n const membership = await resolveOrgMembership(uid, orgId)\n if (!membership?.member) return false\n return (\n (await memberHasOrgPermission(\n orgId,\n membership.member,\n key as Parameters<typeof memberHasOrgPermission>[2],\n )) === true\n )\n } catch {\n return false\n }\n}\n\n/**\n * The addresses a saved Contacts view selects among one site's contacts,\n * read by the dynamic-list sweep's own scan, and whether it reached the\n * whole view.\n */\nexport async function crmViewEmails(input: {\n hostId: string\n viewId: string\n}): Promise<{ emails: string[]; complete: boolean }> {\n const scan = await collectDynamicListCandidates({\n hostId: input.hostId,\n rule: { sources: ['contacts'], viewId: input.viewId },\n })\n return { emails: scan.candidates.map((candidate) => candidate.email), complete: scan.complete }\n}\n"],"names":["collectDynamicListCandidates","lockdownRefusal","logOrgActivity","memberHasOrgPermission","resolveOrgMembership","resolveOrgPermissions","holdsOrgCatalogPermission","uid","orgId","key","membership","member","crmViewEmails","input","scan","hostId","rule","sources","viewId","emails","candidates","map","candidate","email","complete"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,4BAA4B,QAAQ,2DAA0D;AACvG,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SACEC,cAAc,EACdC,sBAAsB,EACtBC,oBAAoB,QACf,gDAA+C;AACtD,SAASC,qBAAqB,QAAQ,wCAAuC;AAE7E;;;;;;;;;;;;CAYC,GAED,SAASJ,eAAe,EAAEC,cAAc,EAAEG,qBAAqB,GAAE;AAEjE;;;;CAIC,GACD,OAAO,eAAeC,0BAA0BC,GAAW,EAAEC,KAAa,EAAEC,GAAW;IACrF,IAAI;QACF,MAAMC,aAAa,MAAMN,qBAAqBG,KAAKC;QACnD,IAAI,EAACE,8BAAAA,WAAYC,MAAM,GAAE,OAAO;QAChC,OACE,AAAC,MAAMR,uBACLK,OACAE,WAAWC,MAAM,EACjBF,SACK;IAEX,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA;;;;CAIC,GACD,OAAO,eAAeG,cAAcC,KAGnC;IACC,MAAMC,OAAO,MAAMd,6BAA6B;QAC9Ce,QAAQF,MAAME,MAAM;QACpBC,MAAM;YAAEC,SAAS;gBAAC;aAAW;YAAEC,QAAQL,MAAMK,MAAM;QAAC;IACtD;IACA,OAAO;QAAEC,QAAQL,KAAKM,UAAU,CAACC,GAAG,CAAC,CAACC,YAAcA,UAAUC,KAAK;QAAGC,UAAUV,KAAKU,QAAQ;IAAC;AAChG"}
@@ -0,0 +1,33 @@
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 { OutreachRouteDeps } from './route-deps';
19
+ /**
20
+ * ONE EMAIL OF A SAVED SEQUENCE, AS A PERSON WOULD GET IT (AGL-2980):
21
+ * `outreach/preview`.
22
+ *
23
+ * Written from what is stored — the sequence, the CRM template a step names,
24
+ * the mailbox it sends as, the organization's footer — by the engine's own
25
+ * composer (`previewOutreachStep`), for a real contact with the personal
26
+ * line the rep is writing, or for a sample person. The enroll dialog shows
27
+ * it beside each person before Confirm, so the rep reads the sentence they
28
+ * wrote in the email it goes into.
29
+ *
30
+ * A real contact is read from the CRM, so naming one asks for `data.manage`
31
+ * as enrolling does; the sample person asks for nothing more.
32
+ */
33
+ export declare function createOutreachPreviewRoute(deps: OutreachRouteDeps): PluginWebApiHandler;
@@ -0,0 +1,117 @@
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 { consentGroupForHost } from "@aglyn/aglyn/app-utils/consent-groups";
17
+ import { normalizeContactEmail } from "@aglyn/aglyn/app-utils/contacts";
18
+ import { CRM_COLLECTIONS } from "@aglyn/aglyn/app-utils/crm";
19
+ import { normalizeCrmEmailTemplate } from "@aglyn/aglyn/app-utils/crm-email-templates";
20
+ import { visibleToHost } from "@aglyn/aglyn/app-utils/scope-tokens";
21
+ import { firstEmailStepIndex } from "../engine/sequence-validation.js";
22
+ import { OUTREACH_SAMPLE_PERSON, outreachSampleMergeContext, previewOutreachStep } from "../model/step-preview.js";
23
+ import { readOutreachComplianceSettingsDoc } from "../storage/compliance-settings-store.js";
24
+ import { outreachOrgCollection, readStoredOutreachMailbox, readStoredOutreachSequence } from "../storage/outreach-records.js";
25
+ import { OUTREACH_READS_PEOPLE } from "./enroll-routes.js";
26
+ import { outreachRouteGate } from "./route-gate.js";
27
+ import { outreachMethodNotAllowed, outreachOk, outreachRefusal, readOutreachDocumentId, readOutreachJsonBody } from "./route-http.js";
28
+ /**
29
+ * ONE EMAIL OF A SAVED SEQUENCE, AS A PERSON WOULD GET IT (AGL-2980):
30
+ * `outreach/preview`.
31
+ *
32
+ * Written from what is stored — the sequence, the CRM template a step names,
33
+ * the mailbox it sends as, the organization's footer — by the engine's own
34
+ * composer (`previewOutreachStep`), for a real contact with the personal
35
+ * line the rep is writing, or for a sample person. The enroll dialog shows
36
+ * it beside each person before Confirm, so the rep reads the sentence they
37
+ * wrote in the email it goes into.
38
+ *
39
+ * A real contact is read from the CRM, so naming one asks for `data.manage`
40
+ * as enrolling does; the sample person asks for nothing more.
41
+ */ export function createOutreachPreviewRoute(deps) {
42
+ return async (request)=>{
43
+ var _host_get, _normalizeContactEmail, _ref, _ref1;
44
+ var _result_email, _result_email1;
45
+ if (request.method !== 'POST') return outreachMethodNotAllowed('POST');
46
+ const body = await readOutreachJsonBody(request);
47
+ const contactId = body['contactId'] === undefined ? null : readOutreachDocumentId(body['contactId']);
48
+ if (body['contactId'] !== undefined && !contactId) {
49
+ return outreachRefusal(400, 'invalid-request', 'Name the contact to preview for.');
50
+ }
51
+ const caller = await outreachRouteGate(request, body['orgId'], deps.gate, contactId ? [
52
+ OUTREACH_READS_PEOPLE
53
+ ] : []);
54
+ if (caller instanceof Response) return caller;
55
+ const sequenceId = readOutreachDocumentId(body['sequenceId']);
56
+ if (!sequenceId) return outreachRefusal(400, 'invalid-request', 'Name the sequence to preview.');
57
+ const firestore = deps.firestore();
58
+ const org = firestore.collection('orgs').doc(caller.orgId);
59
+ const snapshot = await outreachOrgCollection(firestore, caller.orgId, 'sequences').doc(sequenceId).get();
60
+ const sequence = readStoredOutreachSequence(sequenceId, snapshot.exists ? snapshot.data() : undefined);
61
+ if (!sequence) return outreachRefusal(404, 'sequence-not-found', 'That sequence no longer exists.');
62
+ const requested = body['stepIndex'];
63
+ const stepIndex = typeof requested === 'number' ? requested : firstEmailStepIndex(sequence.steps);
64
+ const step = sequence.steps[stepIndex];
65
+ if ((step == null ? void 0 : step.kind) !== 'email') {
66
+ return outreachRefusal(400, 'invalid-request', 'That step does not send an email.');
67
+ }
68
+ const [orgSettings, mailboxSnapshot, host, template, contact] = await Promise.all([
69
+ readOutreachComplianceSettingsDoc(firestore, caller.orgId),
70
+ sequence.mailboxId ? outreachOrgCollection(firestore, caller.orgId, 'mailboxes').doc(sequence.mailboxId).get() : null,
71
+ sequence.hostId ? firestore.collection('hosts').doc(sequence.hostId).get() : null,
72
+ step.templateId ? org.collection(CRM_COLLECTIONS.emailTemplates).doc(step.templateId).get() : null,
73
+ contactId ? org.collection('contacts').doc(contactId).get() : null
74
+ ]);
75
+ const contactData = (contact == null ? void 0 : contact.exists) ? contact.data() : null;
76
+ if (contactId && (!contactData || !visibleToHost(contactData['visibleTo'], sequence.hostId))) {
77
+ return outreachRefusal(404, 'contact-not-found', "That contact isn't in this sequence's site's CRM.");
78
+ }
79
+ const mailbox = readStoredOutreachMailbox(sequence.mailboxId, (mailboxSnapshot == null ? void 0 : mailboxSnapshot.exists) ? mailboxSnapshot.data() : undefined);
80
+ const sender = mailbox ? {
81
+ name: mailbox.displayName,
82
+ email: mailbox.sendAs || mailbox.email
83
+ } : null;
84
+ const siteName = (host == null ? void 0 : host.exists) ? String((_host_get = host.get('name')) != null ? _host_get : '') : '';
85
+ const merge = contactData ? {
86
+ contact: contactData,
87
+ contactGroupId: consentGroupForHost(caller.org, sequence.hostId).groupId,
88
+ sender,
89
+ site: {
90
+ name: siteName
91
+ }
92
+ } : outreachSampleMergeContext({
93
+ sender,
94
+ siteName
95
+ });
96
+ const personalLine = typeof body['personalLine'] === 'string' ? body['personalLine'] : contactData ? '' : OUTREACH_SAMPLE_PERSON.personalLine;
97
+ const result = previewOutreachStep({
98
+ steps: sequence.steps,
99
+ stepIndex,
100
+ orgSettings,
101
+ merge,
102
+ email: contactData ? (_normalizeContactEmail = normalizeContactEmail(contactData['email'])) != null ? _normalizeContactEmail : '' : OUTREACH_SAMPLE_PERSON.email,
103
+ personalLine,
104
+ templateBody: (template == null ? void 0 : template.exists) ? normalizeCrmEmailTemplate(template.data()).body : null
105
+ });
106
+ return outreachOk({
107
+ ok: true,
108
+ stepIndex,
109
+ subject: (_ref = (_result_email = result.email) == null ? void 0 : _result_email.subject) != null ? _ref : '',
110
+ text: (_ref1 = (_result_email1 = result.email) == null ? void 0 : _result_email1.text) != null ? _ref1 : '',
111
+ unresolvedFields: result.unresolvedFields,
112
+ error: result.error
113
+ });
114
+ };
115
+ }
116
+
117
+ //# sourceMappingURL=preview-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/routes/preview-routes.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 { consentGroupForHost } from '@aglyn/aglyn/app-utils/consent-groups'\nimport { normalizeContactEmail } from '@aglyn/aglyn/app-utils/contacts'\nimport { CRM_COLLECTIONS } from '@aglyn/aglyn/app-utils/crm'\nimport { normalizeCrmEmailTemplate } from '@aglyn/aglyn/app-utils/crm-email-templates'\nimport { visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport type { PluginWebApiHandler } from '@aglyn/aglyn/server'\nimport { firstEmailStepIndex } from '../engine/sequence-validation'\nimport type { OutreachPreviewResponse } from '../model/outreach-api'\nimport {\n OUTREACH_SAMPLE_PERSON,\n outreachSampleMergeContext,\n previewOutreachStep,\n} from '../model/step-preview'\nimport { readOutreachComplianceSettingsDoc } from '../storage/compliance-settings-store'\nimport {\n outreachOrgCollection,\n readStoredOutreachMailbox,\n readStoredOutreachSequence,\n} from '../storage/outreach-records'\nimport { OUTREACH_READS_PEOPLE } from './enroll-routes'\nimport type { OutreachRouteDeps } from './route-deps'\nimport { outreachRouteGate } from './route-gate'\nimport {\n outreachMethodNotAllowed,\n outreachOk,\n outreachRefusal,\n readOutreachDocumentId,\n readOutreachJsonBody,\n} from './route-http'\n\n/**\n * ONE EMAIL OF A SAVED SEQUENCE, AS A PERSON WOULD GET IT (AGL-2980):\n * `outreach/preview`.\n *\n * Written from what is stored — the sequence, the CRM template a step names,\n * the mailbox it sends as, the organization's footer — by the engine's own\n * composer (`previewOutreachStep`), for a real contact with the personal\n * line the rep is writing, or for a sample person. The enroll dialog shows\n * it beside each person before Confirm, so the rep reads the sentence they\n * wrote in the email it goes into.\n *\n * A real contact is read from the CRM, so naming one asks for `data.manage`\n * as enrolling does; the sample person asks for nothing more.\n */\n\nexport function createOutreachPreviewRoute(deps: OutreachRouteDeps): PluginWebApiHandler {\n return async (request) => {\n if (request.method !== 'POST') return outreachMethodNotAllowed('POST')\n const body = await readOutreachJsonBody(request)\n const contactId = body['contactId'] === undefined ? null : readOutreachDocumentId(body['contactId'])\n if (body['contactId'] !== undefined && !contactId) {\n return outreachRefusal(400, 'invalid-request', 'Name the contact to preview for.')\n }\n const caller = await outreachRouteGate(\n request,\n body['orgId'],\n deps.gate,\n contactId ? [OUTREACH_READS_PEOPLE] : [],\n )\n if (caller instanceof Response) return caller\n const sequenceId = readOutreachDocumentId(body['sequenceId'])\n if (!sequenceId) return outreachRefusal(400, 'invalid-request', 'Name the sequence to preview.')\n const firestore = deps.firestore()\n const org = firestore.collection('orgs').doc(caller.orgId)\n const snapshot = await outreachOrgCollection(firestore, caller.orgId, 'sequences').doc(sequenceId).get()\n const sequence = readStoredOutreachSequence(sequenceId, snapshot.exists ? snapshot.data() : undefined)\n if (!sequence) return outreachRefusal(404, 'sequence-not-found', 'That sequence no longer exists.')\n\n const requested = body['stepIndex']\n const stepIndex = typeof requested === 'number' ? requested : firstEmailStepIndex(sequence.steps)\n const step = sequence.steps[stepIndex]\n if (step?.kind !== 'email') {\n return outreachRefusal(400, 'invalid-request', 'That step does not send an email.')\n }\n\n const [orgSettings, mailboxSnapshot, host, template, contact] = await Promise.all([\n readOutreachComplianceSettingsDoc(firestore, caller.orgId),\n sequence.mailboxId ? outreachOrgCollection(firestore, caller.orgId, 'mailboxes').doc(sequence.mailboxId).get() : null,\n sequence.hostId ? firestore.collection('hosts').doc(sequence.hostId).get() : null,\n step.templateId ? org.collection(CRM_COLLECTIONS.emailTemplates).doc(step.templateId).get() : null,\n contactId ? org.collection('contacts').doc(contactId).get() : null,\n ])\n const contactData = contact?.exists ? (contact.data() as Record<string, unknown>) : null\n if (\n contactId &&\n (!contactData || !visibleToHost(contactData['visibleTo'] as string[] | undefined, sequence.hostId))\n ) {\n return outreachRefusal(404, 'contact-not-found', \"That contact isn't in this sequence's site's CRM.\")\n }\n const mailbox = readStoredOutreachMailbox(\n sequence.mailboxId,\n mailboxSnapshot?.exists ? mailboxSnapshot.data() : undefined,\n )\n const sender = mailbox ? { name: mailbox.displayName, email: mailbox.sendAs || mailbox.email } : null\n const siteName = host?.exists ? String(host.get('name') ?? '') : ''\n const merge = contactData\n ? {\n contact: contactData,\n contactGroupId: consentGroupForHost(caller.org, sequence.hostId).groupId,\n sender,\n site: { name: siteName },\n }\n : outreachSampleMergeContext({ sender, siteName })\n const personalLine =\n typeof body['personalLine'] === 'string'\n ? body['personalLine']\n : contactData\n ? ''\n : OUTREACH_SAMPLE_PERSON.personalLine\n const result = previewOutreachStep({\n steps: sequence.steps,\n stepIndex,\n orgSettings,\n merge,\n email: contactData ? (normalizeContactEmail(contactData['email']) ?? '') : OUTREACH_SAMPLE_PERSON.email,\n personalLine,\n templateBody: template?.exists\n ? normalizeCrmEmailTemplate(template.data() as Record<string, unknown>).body\n : null,\n })\n return outreachOk({\n ok: true,\n stepIndex,\n subject: result.email?.subject ?? '',\n text: result.email?.text ?? '',\n unresolvedFields: result.unresolvedFields,\n error: result.error,\n } satisfies OutreachPreviewResponse)\n }\n}\n"],"names":["consentGroupForHost","normalizeContactEmail","CRM_COLLECTIONS","normalizeCrmEmailTemplate","visibleToHost","firstEmailStepIndex","OUTREACH_SAMPLE_PERSON","outreachSampleMergeContext","previewOutreachStep","readOutreachComplianceSettingsDoc","outreachOrgCollection","readStoredOutreachMailbox","readStoredOutreachSequence","OUTREACH_READS_PEOPLE","outreachRouteGate","outreachMethodNotAllowed","outreachOk","outreachRefusal","readOutreachDocumentId","readOutreachJsonBody","createOutreachPreviewRoute","deps","request","host","result","method","body","contactId","undefined","caller","gate","Response","sequenceId","firestore","org","collection","doc","orgId","snapshot","get","sequence","exists","data","requested","stepIndex","steps","step","kind","orgSettings","mailboxSnapshot","template","contact","Promise","all","mailboxId","hostId","templateId","emailTemplates","contactData","mailbox","sender","name","displayName","email","sendAs","siteName","String","merge","contactGroupId","groupId","site","personalLine","templateBody","ok","subject","text","unresolvedFields","error"],"mappings":"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"}
@@ -0,0 +1,22 @@
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 OutreachEnrollRouteDeps } from './enroll-routes';
18
+ import type { OutreachRouteGateDeps } from './route-gate';
19
+ /** The gate's reach into the platform. */
20
+ export declare function defaultOutreachRouteGateDeps(): OutreachRouteGateDeps;
21
+ export declare function defaultOutreachRouteDeps(): OutreachEnrollRouteDeps;
22
+ export declare function registerOutreachRoutes(deps?: OutreachEnrollRouteDeps): void;
@@ -0,0 +1,92 @@
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 { registerPluginApiRoute } from "@aglyn/aglyn/server";
17
+ import { firebaseAdmin } from "@aglyn/tenant-data-admin/server/firebase-admin";
18
+ import { OUTREACH_API_ROUTES } from "../constants/api-routes.js";
19
+ import { outreachOrgSubject } from "../mailboxes/register-mailbox-routes.js";
20
+ import { createOutreachEnrollRoutes } from "./enroll-routes.js";
21
+ import { createOutreachEnrollmentActionRoute } from "./enrollment-routes.js";
22
+ import { createOutreachPreviewRoute } from "./preview-routes.js";
23
+ import { createOutreachSequenceRoutes } from "./sequence-routes.js";
24
+ import { createOutreachSettingsRoute } from "./settings-routes.js";
25
+ /**
26
+ * Wires the settings, sequence and enrollment routes into the console
27
+ * dispatcher (AGL-2980) with the platform's own dependencies. Specs build
28
+ * their own.
29
+ *
30
+ * Every one names its organization (`orgId`, in the query or the JSON body)
31
+ * and none names a site, so each declares the mailbox routes' subject reader:
32
+ * the dispatcher's release gate then asks about that organization, and a
33
+ * `release_outreach` override for one organization reaches these routes as it
34
+ * reaches Mailboxes. Without it the gate reads the request as anonymous, which
35
+ * only a fully enabled flag or a staff session passes.
36
+ */ /** The platform's heavier modules, loaded the first time a request needs one. */ const platform = ()=>import("./platform-deps.js");
37
+ /** The gate's reach into the platform. */ export function defaultOutreachRouteGateDeps() {
38
+ return {
39
+ verifyIdToken: (idToken)=>firebaseAdmin.app().auth().verifyIdToken(idToken),
40
+ resolveOrgPermissions: async (uid, context)=>(await platform()).resolveOrgPermissions(uid, context),
41
+ holdsOrgCatalogPermission: async (uid, orgId, key)=>(await platform()).holdsOrgCatalogPermission(uid, orgId, key),
42
+ readOrg: async (orgId)=>{
43
+ var _snapshot_data;
44
+ const snapshot = await firebaseAdmin.app().firestore().collection('orgs').doc(orgId).get();
45
+ return snapshot.exists ? (_snapshot_data = snapshot.data()) != null ? _snapshot_data : {} : null;
46
+ },
47
+ lockdownRefusal: async (options)=>(await platform()).lockdownRefusal(options)
48
+ };
49
+ }
50
+ export function defaultOutreachRouteDeps() {
51
+ return {
52
+ firestore: ()=>firebaseAdmin.app().firestore(),
53
+ gate: defaultOutreachRouteGateDeps(),
54
+ now: Date.now,
55
+ random: Math.random,
56
+ logOrgActivity: async (orgId, actor, action, target)=>(await platform()).logOrgActivity(orgId, actor, action, target),
57
+ crmViewEmails: async (input)=>(await platform()).crmViewEmails(input)
58
+ };
59
+ }
60
+ export function registerOutreachRoutes(deps = defaultOutreachRouteDeps()) {
61
+ const sequences = createOutreachSequenceRoutes(deps);
62
+ const enroll = createOutreachEnrollRoutes(deps);
63
+ const orgSubject = {
64
+ subject: outreachOrgSubject
65
+ };
66
+ registerPluginApiRoute(OUTREACH_API_ROUTES.settings, {
67
+ web: createOutreachSettingsRoute(deps)
68
+ }, orgSubject);
69
+ registerPluginApiRoute(OUTREACH_API_ROUTES.sequencesSave, {
70
+ web: sequences.save
71
+ }, orgSubject);
72
+ registerPluginApiRoute(OUTREACH_API_ROUTES.sequencesStatus, {
73
+ web: sequences.status
74
+ }, orgSubject);
75
+ registerPluginApiRoute(OUTREACH_API_ROUTES.sequencesDelete, {
76
+ web: sequences.remove
77
+ }, orgSubject);
78
+ registerPluginApiRoute(OUTREACH_API_ROUTES.enrollPreview, {
79
+ web: enroll.preview
80
+ }, orgSubject);
81
+ registerPluginApiRoute(OUTREACH_API_ROUTES.enroll, {
82
+ web: enroll.confirm
83
+ }, orgSubject);
84
+ registerPluginApiRoute(OUTREACH_API_ROUTES.enrollmentsAction, {
85
+ web: createOutreachEnrollmentActionRoute(deps)
86
+ }, orgSubject);
87
+ registerPluginApiRoute(OUTREACH_API_ROUTES.preview, {
88
+ web: createOutreachPreviewRoute(deps)
89
+ }, orgSubject);
90
+ }
91
+
92
+ //# sourceMappingURL=register-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/routes/register-routes.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { OUTREACH_API_ROUTES } from '../constants/api-routes'\nimport { outreachOrgSubject } from '../mailboxes/register-mailbox-routes'\nimport { createOutreachEnrollRoutes, type OutreachEnrollRouteDeps } from './enroll-routes'\nimport { createOutreachEnrollmentActionRoute } from './enrollment-routes'\nimport { createOutreachPreviewRoute } from './preview-routes'\nimport type { OutreachRouteGateDeps } from './route-gate'\nimport { createOutreachSequenceRoutes } from './sequence-routes'\nimport { createOutreachSettingsRoute } from './settings-routes'\n\n/**\n * Wires the settings, sequence and enrollment routes into the console\n * dispatcher (AGL-2980) with the platform's own dependencies. Specs build\n * their own.\n *\n * Every one names its organization (`orgId`, in the query or the JSON body)\n * and none names a site, so each declares the mailbox routes' subject reader:\n * the dispatcher's release gate then asks about that organization, and a\n * `release_outreach` override for one organization reaches these routes as it\n * reaches Mailboxes. Without it the gate reads the request as anonymous, which\n * only a fully enabled flag or a staff session passes.\n */\n\n/** The platform's heavier modules, loaded the first time a request needs one. */\nconst platform = () => import('./platform-deps')\n\n/** The gate's reach into the platform. */\nexport function defaultOutreachRouteGateDeps(): OutreachRouteGateDeps {\n return {\n verifyIdToken: (idToken) => firebaseAdmin.app().auth().verifyIdToken(idToken),\n resolveOrgPermissions: async (uid, context) => (await platform()).resolveOrgPermissions(uid, context),\n holdsOrgCatalogPermission: async (uid, orgId, key) =>\n (await platform()).holdsOrgCatalogPermission(uid, orgId, key),\n readOrg: async (orgId) => {\n const snapshot = await firebaseAdmin.app().firestore().collection('orgs').doc(orgId).get()\n return snapshot.exists ? (snapshot.data() ?? {}) : null\n },\n lockdownRefusal: async (options) => (await platform()).lockdownRefusal(options),\n }\n}\n\nexport function defaultOutreachRouteDeps(): OutreachEnrollRouteDeps {\n return {\n firestore: () => firebaseAdmin.app().firestore(),\n gate: defaultOutreachRouteGateDeps(),\n now: Date.now,\n random: Math.random,\n logOrgActivity: async (orgId, actor, action, target) =>\n (await platform()).logOrgActivity(orgId, actor, action, target),\n crmViewEmails: async (input) => (await platform()).crmViewEmails(input),\n }\n}\n\nexport function registerOutreachRoutes(\n deps: OutreachEnrollRouteDeps = defaultOutreachRouteDeps(),\n): void {\n const sequences = createOutreachSequenceRoutes(deps)\n const enroll = createOutreachEnrollRoutes(deps)\n const orgSubject = { subject: outreachOrgSubject }\n registerPluginApiRoute(OUTREACH_API_ROUTES.settings, { web: createOutreachSettingsRoute(deps) }, orgSubject)\n registerPluginApiRoute(OUTREACH_API_ROUTES.sequencesSave, { web: sequences.save }, orgSubject)\n registerPluginApiRoute(OUTREACH_API_ROUTES.sequencesStatus, { web: sequences.status }, orgSubject)\n registerPluginApiRoute(OUTREACH_API_ROUTES.sequencesDelete, { web: sequences.remove }, orgSubject)\n registerPluginApiRoute(OUTREACH_API_ROUTES.enrollPreview, { web: enroll.preview }, orgSubject)\n registerPluginApiRoute(OUTREACH_API_ROUTES.enroll, { web: enroll.confirm }, orgSubject)\n registerPluginApiRoute(\n OUTREACH_API_ROUTES.enrollmentsAction,\n { web: createOutreachEnrollmentActionRoute(deps) },\n orgSubject,\n )\n registerPluginApiRoute(OUTREACH_API_ROUTES.preview, { web: createOutreachPreviewRoute(deps) }, orgSubject)\n}\n"],"names":["registerPluginApiRoute","firebaseAdmin","OUTREACH_API_ROUTES","outreachOrgSubject","createOutreachEnrollRoutes","createOutreachEnrollmentActionRoute","createOutreachPreviewRoute","createOutreachSequenceRoutes","createOutreachSettingsRoute","platform","defaultOutreachRouteGateDeps","verifyIdToken","idToken","app","auth","resolveOrgPermissions","uid","context","holdsOrgCatalogPermission","orgId","key","readOrg","snapshot","firestore","collection","doc","get","exists","data","lockdownRefusal","options","defaultOutreachRouteDeps","gate","now","Date","random","Math","logOrgActivity","actor","action","target","crmViewEmails","input","registerOutreachRoutes","deps","sequences","enroll","orgSubject","subject","settings","web","sequencesSave","save","sequencesStatus","status","sequencesDelete","remove","enrollPreview","preview","confirm","enrollmentsAction"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,sBAAqB;AAC5D,SAASC,aAAa,QAAQ,iDAAgD;AAC9E,SAASC,mBAAmB,QAAQ,6BAAyB;AAC7D,SAASC,kBAAkB,QAAQ,0CAAsC;AACzE,SAASC,0BAA0B,QAAsC,qBAAiB;AAC1F,SAASC,mCAAmC,QAAQ,yBAAqB;AACzE,SAASC,0BAA0B,QAAQ,sBAAkB;AAE7D,SAASC,4BAA4B,QAAQ,uBAAmB;AAChE,SAASC,2BAA2B,QAAQ,uBAAmB;AAE/D;;;;;;;;;;;CAWC,GAED,+EAA+E,GAC/E,MAAMC,WAAW,IAAM,MAAM,CAAC;AAE9B,wCAAwC,GACxC,OAAO,SAASC;IACd,OAAO;QACLC,eAAe,CAACC,UAAYX,cAAcY,GAAG,GAAGC,IAAI,GAAGH,aAAa,CAACC;QACrEG,uBAAuB,OAAOC,KAAKC,UAAY,AAAC,CAAA,MAAMR,UAAS,EAAGM,qBAAqB,CAACC,KAAKC;QAC7FC,2BAA2B,OAAOF,KAAKG,OAAOC,MAC5C,AAAC,CAAA,MAAMX,UAAS,EAAGS,yBAAyB,CAACF,KAAKG,OAAOC;QAC3DC,SAAS,OAAOF;gBAEYG;YAD1B,MAAMA,WAAW,MAAMrB,cAAcY,GAAG,GAAGU,SAAS,GAAGC,UAAU,CAAC,QAAQC,GAAG,CAACN,OAAOO,GAAG;YACxF,OAAOJ,SAASK,MAAM,IAAIL,iBAAAA,SAASM,IAAI,cAAbN,iBAAmB,CAAC,IAAK;QACrD;QACAO,iBAAiB,OAAOC,UAAY,AAAC,CAAA,MAAMrB,UAAS,EAAGoB,eAAe,CAACC;IACzE;AACF;AAEA,OAAO,SAASC;IACd,OAAO;QACLR,WAAW,IAAMtB,cAAcY,GAAG,GAAGU,SAAS;QAC9CS,MAAMtB;QACNuB,KAAKC,KAAKD,GAAG;QACbE,QAAQC,KAAKD,MAAM;QACnBE,gBAAgB,OAAOlB,OAAOmB,OAAOC,QAAQC,SAC3C,AAAC,CAAA,MAAM/B,UAAS,EAAG4B,cAAc,CAAClB,OAAOmB,OAAOC,QAAQC;QAC1DC,eAAe,OAAOC,QAAU,AAAC,CAAA,MAAMjC,UAAS,EAAGgC,aAAa,CAACC;IACnE;AACF;AAEA,OAAO,SAASC,uBACdC,OAAgCb,0BAA0B;IAE1D,MAAMc,YAAYtC,6BAA6BqC;IAC/C,MAAME,SAAS1C,2BAA2BwC;IAC1C,MAAMG,aAAa;QAAEC,SAAS7C;IAAmB;IACjDH,uBAAuBE,oBAAoB+C,QAAQ,EAAE;QAAEC,KAAK1C,4BAA4BoC;IAAM,GAAGG;IACjG/C,uBAAuBE,oBAAoBiD,aAAa,EAAE;QAAED,KAAKL,UAAUO,IAAI;IAAC,GAAGL;IACnF/C,uBAAuBE,oBAAoBmD,eAAe,EAAE;QAAEH,KAAKL,UAAUS,MAAM;IAAC,GAAGP;IACvF/C,uBAAuBE,oBAAoBqD,eAAe,EAAE;QAAEL,KAAKL,UAAUW,MAAM;IAAC,GAAGT;IACvF/C,uBAAuBE,oBAAoBuD,aAAa,EAAE;QAAEP,KAAKJ,OAAOY,OAAO;IAAC,GAAGX;IACnF/C,uBAAuBE,oBAAoB4C,MAAM,EAAE;QAAEI,KAAKJ,OAAOa,OAAO;IAAC,GAAGZ;IAC5E/C,uBACEE,oBAAoB0D,iBAAiB,EACrC;QAAEV,KAAK7C,oCAAoCuC;IAAM,GACjDG;IAEF/C,uBAAuBE,oBAAoBwD,OAAO,EAAE;QAAER,KAAK5C,2BAA2BsC;IAAM,GAAGG;AACjG"}
@@ -0,0 +1,49 @@
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 { OutreachRouteGateDeps } from './route-gate';
18
+ /**
19
+ * What the settings, sequence and enrollment routes reach outside this
20
+ * plugin (AGL-2980). The platform's own are `register-routes.ts`; specs
21
+ * build their own.
22
+ */
23
+ /**
24
+ * The org activity target a sequence's rows are filed under: Outreach's own
25
+ * namespaced resource (`pluginId:noun`), which the feed reads as "Sequence".
26
+ * Core's target list names only core's resources, so a plugin's never joins
27
+ * it.
28
+ */
29
+ export declare const OUTREACH_SEQUENCE_ACTIVITY_TARGET = "outreach:sequence";
30
+ /** What an org activity line written by these routes points at. */
31
+ export type OutreachActivityTarget = {
32
+ type: 'org';
33
+ id: string;
34
+ } | {
35
+ type: typeof OUTREACH_SEQUENCE_ACTIVITY_TARGET;
36
+ id: string;
37
+ name: string;
38
+ };
39
+ export interface OutreachRouteDeps {
40
+ firestore(): FirebaseFirestore.Firestore;
41
+ gate: OutreachRouteGateDeps;
42
+ now(): number;
43
+ /** A source of numbers in `[0, 1)` — the scheduler's jitter. */
44
+ random(): number;
45
+ logOrgActivity(orgId: string, actor: {
46
+ uid: string;
47
+ email?: string | null;
48
+ }, action: string, target: OutreachActivityTarget): Promise<void>;
49
+ }
@@ -0,0 +1,27 @@
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
+ * What the settings, sequence and enrollment routes reach outside this
18
+ * plugin (AGL-2980). The platform's own are `register-routes.ts`; specs
19
+ * build their own.
20
+ */ /**
21
+ * The org activity target a sequence's rows are filed under: Outreach's own
22
+ * namespaced resource (`pluginId:noun`), which the feed reads as "Sequence".
23
+ * Core's target list names only core's resources, so a plugin's never joins
24
+ * it.
25
+ */ export const OUTREACH_SEQUENCE_ACTIVITY_TARGET = 'outreach:sequence';
26
+
27
+ //# sourceMappingURL=route-deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/routes/route-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 { OutreachRouteGateDeps } from './route-gate'\n\n/**\n * What the settings, sequence and enrollment routes reach outside this\n * plugin (AGL-2980). The platform's own are `register-routes.ts`; specs\n * build their own.\n */\n\n/**\n * The org activity target a sequence's rows are filed under: Outreach's own\n * namespaced resource (`pluginId:noun`), which the feed reads as \"Sequence\".\n * Core's target list names only core's resources, so a plugin's never joins\n * it.\n */\nexport const OUTREACH_SEQUENCE_ACTIVITY_TARGET = 'outreach:sequence'\n\n/** What an org activity line written by these routes points at. */\nexport type OutreachActivityTarget =\n | { type: 'org'; id: string }\n | { type: typeof OUTREACH_SEQUENCE_ACTIVITY_TARGET; id: string; name: string }\n\nexport interface OutreachRouteDeps {\n firestore(): FirebaseFirestore.Firestore\n gate: OutreachRouteGateDeps\n now(): number\n /** A source of numbers in `[0, 1)` — the scheduler's jitter. */\n random(): number\n logOrgActivity(\n orgId: string,\n actor: { uid: string; email?: string | null },\n action: string,\n target: OutreachActivityTarget,\n ): Promise<void>\n}\n"],"names":["OUTREACH_SEQUENCE_ACTIVITY_TARGET"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;CAIC,GAED;;;;;CAKC,GACD,OAAO,MAAMA,oCAAoC,oBAAmB"}
@@ -0,0 +1,94 @@
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 { DecodedIdToken } from 'firebase-admin/auth';
18
+ /**
19
+ * THE GATE EVERY SETTINGS, SEQUENCE AND ENROLLMENT ROUTE CLIMBS (AGL-2980).
20
+ *
21
+ * The console dispatcher has already refused a request while
22
+ * `release_outreach` is off for it, an unverified account's token, a
23
+ * lockdown with no org in scope, and a caller over the console rate limit —
24
+ * every route here is registered under the `outreach/` prefix, which is what
25
+ * puts it behind those. What the dispatcher cannot know is anything about
26
+ * the member, so each rung below is this plugin's:
27
+ *
28
+ * 401 no bearer token, or one the verifier refused
29
+ * 403 the account's address is unverified (an impersonation session is exempt)
30
+ * 400 the request did not name an organization
31
+ * 403 not a member of it, a site collaborator without org-wide reach, or
32
+ * without `outreach.use` — or without a permission the route adds
33
+ * 403 the organization does not hold the `outreach` entitlement
34
+ * 423 lockdown, now with the organization and the account in scope
35
+ *
36
+ * The reach is org-wide for the reason the rules give: a sequence enrolls
37
+ * people from every site of the organization, so none of it is a fact
38
+ * about the one site a collaborator was invited to.
39
+ *
40
+ * `resolveOrgPermissions` is always given the concrete org id. Called
41
+ * without one it resolves a caller with no org at all as that future org's
42
+ * owner, which is right for a fresh signup and wrong for every question
43
+ * asked here.
44
+ */
45
+ export interface OutreachRouteGateDeps {
46
+ verifyIdToken(idToken: string): Promise<DecodedIdToken>;
47
+ resolveOrgPermissions(uid: string, context: {
48
+ orgId: string;
49
+ }): Promise<{
50
+ orgId: string | null;
51
+ /** `null` when the account is not on the organization's roster. */
52
+ role: string | null;
53
+ isOwner: boolean;
54
+ orgWide: boolean;
55
+ permissions: Partial<Record<string, boolean | undefined>>;
56
+ }>;
57
+ /**
58
+ * Whether the member holds a CATALOG permission (`data.manage`) as the
59
+ * org's roles resolve it. Asked separately because the map
60
+ * `resolveOrgPermissions` answers carries the legacy keys and the plugins'
61
+ * own, never a catalog key: read there, a catalog key refuses everyone.
62
+ */
63
+ holdsOrgCatalogPermission(uid: string, orgId: string, key: string): Promise<boolean>;
64
+ readOrg(orgId: string): Promise<Record<string, unknown> | null>;
65
+ lockdownRefusal(options: {
66
+ request: Request;
67
+ staff: boolean;
68
+ uid: string;
69
+ org: Record<string, unknown>;
70
+ }): Promise<Response | null>;
71
+ }
72
+ /** Who is asking, once the gate let them through. */
73
+ export interface OutreachRouteCaller {
74
+ uid: string;
75
+ /** The account's sign-in address, lower-cased, when the token carries one. */
76
+ email: string | null;
77
+ staff: boolean;
78
+ orgId: string;
79
+ /** The organization document the entitlement was read from. */
80
+ org: Record<string, unknown>;
81
+ /** An owner or admin of the organization, who may send from any member's mailbox. */
82
+ isOrgAdmin: boolean;
83
+ }
84
+ /**
85
+ * A catalog permission a route asks for beyond `outreach.use`, with its
86
+ * refusal — `data.manage` for a route that reads the CRM's people, which
87
+ * the contacts' own rules gate on.
88
+ */
89
+ export interface OutreachRouteExtraPermission {
90
+ key: string;
91
+ /** Why the route needs it, as the refusal says it. */
92
+ refusal: string;
93
+ }
94
+ export declare function outreachRouteGate(request: Request, rawOrgId: unknown, deps: OutreachRouteGateDeps, extra?: readonly OutreachRouteExtraPermission[]): Promise<Response | OutreachRouteCaller>;