@antelopejs/interface-dms 0.10.4 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +21 -10
  3. package/dist/attachments.d.ts +15 -2
  4. package/dist/attachments.js +14 -1
  5. package/dist/attachments.js.map +1 -1
  6. package/dist/auth/db/models/sessions.model.d.ts +7 -5
  7. package/dist/auth/db/models/sessions.model.js +2 -9
  8. package/dist/auth/db/models/sessions.model.js.map +1 -1
  9. package/dist/auth/db/models/userExternalIdentities.model.d.ts +7 -5
  10. package/dist/auth/db/models/users.model.d.ts +7 -5
  11. package/dist/auth/db/tables/users.table.d.ts +2 -2
  12. package/dist/auth/db/tables/users.table.js.map +1 -1
  13. package/dist/auth/index.d.ts +4 -4
  14. package/dist/auth/internal/jwt-auth-decorator.d.ts +1 -1
  15. package/dist/auth/internal/jwt-auth-decorator.js +6 -3
  16. package/dist/auth/internal/jwt-auth-decorator.js.map +1 -1
  17. package/dist/auth/internal/request-authenticators.d.ts +12 -0
  18. package/dist/auth/internal/request-authenticators.js +53 -10
  19. package/dist/auth/internal/request-authenticators.js.map +1 -1
  20. package/dist/base/activity-feed.d.ts +34 -32
  21. package/dist/base/activity-feed.js +2 -0
  22. package/dist/base/activity-feed.js.map +1 -1
  23. package/dist/base/banner.d.ts +12 -10
  24. package/dist/base/banner.js +2 -0
  25. package/dist/base/banner.js.map +1 -1
  26. package/dist/base/block-registry/internal/describe.js +4 -12
  27. package/dist/base/block-registry/internal/describe.js.map +1 -1
  28. package/dist/base/block-registry/internal/ui.js +1 -1
  29. package/dist/base/block-registry/internal/ui.js.map +1 -1
  30. package/dist/base/card.d.ts +11 -9
  31. package/dist/base/card.js +2 -0
  32. package/dist/base/card.js.map +1 -1
  33. package/dist/base/chart-card.d.ts +4 -2
  34. package/dist/base/chart-card.js +2 -0
  35. package/dist/base/chart-card.js.map +1 -1
  36. package/dist/base/chart-schemas.d.ts +148 -148
  37. package/dist/base/chart.d.ts +65 -0
  38. package/dist/base/chart.js +65 -0
  39. package/dist/base/chart.js.map +1 -1
  40. package/dist/base/code-block.d.ts +5 -3
  41. package/dist/base/code-block.js +2 -0
  42. package/dist/base/code-block.js.map +1 -1
  43. package/dist/base/custom.d.ts +5 -0
  44. package/dist/base/custom.js +5 -0
  45. package/dist/base/custom.js.map +1 -1
  46. package/dist/base/data-types/default/address.d.ts +5 -0
  47. package/dist/base/data-types/default/address.js +5 -0
  48. package/dist/base/data-types/default/address.js.map +1 -1
  49. package/dist/base/data-types/default/choices.d.ts +20 -0
  50. package/dist/base/data-types/default/choices.js +20 -0
  51. package/dist/base/data-types/default/choices.js.map +1 -1
  52. package/dist/base/data-types/default/files.d.ts +22 -5
  53. package/dist/base/data-types/default/files.js +20 -0
  54. package/dist/base/data-types/default/files.js.map +1 -1
  55. package/dist/base/data-types/default/numbers.d.ts +15 -0
  56. package/dist/base/data-types/default/numbers.js +25 -9
  57. package/dist/base/data-types/default/numbers.js.map +1 -1
  58. package/dist/base/data-types/default/relations.d.ts +10 -2
  59. package/dist/base/data-types/default/relations.js +9 -6
  60. package/dist/base/data-types/default/relations.js.map +1 -1
  61. package/dist/base/data-types/default/strings.d.ts +25 -0
  62. package/dist/base/data-types/default/strings.js +25 -0
  63. package/dist/base/data-types/default/strings.js.map +1 -1
  64. package/dist/base/data-types/default-types.d.ts +1 -1
  65. package/dist/base/data-types/field-types.d.ts +10 -0
  66. package/dist/base/data-types/field-types.js +10 -0
  67. package/dist/base/data-types/field-types.js.map +1 -1
  68. package/dist/base/data-types/internal/relation-endpoints.js +1 -0
  69. package/dist/base/data-types/internal/relation-endpoints.js.map +1 -1
  70. package/dist/base/data-types/status-type.d.ts +5 -0
  71. package/dist/base/data-types/status-type.js +5 -0
  72. package/dist/base/data-types/status-type.js.map +1 -1
  73. package/dist/base/display.d.ts +6 -6
  74. package/dist/base/empty-state.d.ts +15 -13
  75. package/dist/base/empty-state.js +2 -0
  76. package/dist/base/empty-state.js.map +1 -1
  77. package/dist/base/form-block-schema.d.ts +50 -50
  78. package/dist/base/form-schema.d.ts +2 -0
  79. package/dist/base/form-schema.js +2 -0
  80. package/dist/base/form-schema.js.map +1 -1
  81. package/dist/base/grid.d.ts +4 -0
  82. package/dist/base/grid.js +4 -0
  83. package/dist/base/grid.js.map +1 -1
  84. package/dist/base/guard.d.ts +30 -28
  85. package/dist/base/guard.js +2 -0
  86. package/dist/base/guard.js.map +1 -1
  87. package/dist/base/helpers/archive-mode-helpers.d.ts +1 -1
  88. package/dist/base/helpers/file-refs.js +15 -15
  89. package/dist/base/helpers/file-refs.js.map +1 -1
  90. package/dist/base/internal/form-schema-validation.js +7 -1
  91. package/dist/base/internal/form-schema-validation.js.map +1 -1
  92. package/dist/base/internal/realtime-topics.js.map +1 -1
  93. package/dist/base/internal/searchable.d.ts +2 -1
  94. package/dist/base/internal/searchable.js +1 -3
  95. package/dist/base/internal/searchable.js.map +1 -1
  96. package/dist/base/key-value-list.d.ts +21 -19
  97. package/dist/base/key-value-list.js +2 -0
  98. package/dist/base/key-value-list.js.map +1 -1
  99. package/dist/base/kpi-card.d.ts +4 -2
  100. package/dist/base/kpi-card.js +2 -0
  101. package/dist/base/kpi-card.js.map +1 -1
  102. package/dist/base/live-toggle.d.ts +2 -0
  103. package/dist/base/live-toggle.js +2 -0
  104. package/dist/base/live-toggle.js.map +1 -1
  105. package/dist/base/meter.d.ts +23 -21
  106. package/dist/base/meter.js +2 -0
  107. package/dist/base/meter.js.map +1 -1
  108. package/dist/base/nav-card-grid.d.ts +24 -19
  109. package/dist/base/nav-card-grid.js +5 -0
  110. package/dist/base/nav-card-grid.js.map +1 -1
  111. package/dist/base/page-toolbar.d.ts +2 -0
  112. package/dist/base/page-toolbar.js +2 -0
  113. package/dist/base/page-toolbar.js.map +1 -1
  114. package/dist/base/period-selector.d.ts +3 -1
  115. package/dist/base/period-selector.js +2 -0
  116. package/dist/base/period-selector.js.map +1 -1
  117. package/dist/base/placeholder.d.ts +2 -0
  118. package/dist/base/placeholder.js +2 -0
  119. package/dist/base/placeholder.js.map +1 -1
  120. package/dist/base/resource-form-schema.d.ts +2 -2
  121. package/dist/base/searchable.d.ts +3 -2
  122. package/dist/base/searchable.js +2 -2
  123. package/dist/base/searchable.js.map +1 -1
  124. package/dist/base/section.d.ts +6 -2
  125. package/dist/base/section.js +4 -0
  126. package/dist/base/section.js.map +1 -1
  127. package/dist/base/stack.d.ts +6 -0
  128. package/dist/base/stack.js +6 -0
  129. package/dist/base/stack.js.map +1 -1
  130. package/dist/base/stat-group.d.ts +16 -14
  131. package/dist/base/stat-group.js +2 -0
  132. package/dist/base/stat-group.js.map +1 -1
  133. package/dist/base/tab.d.ts +10 -8
  134. package/dist/base/tab.js +2 -0
  135. package/dist/base/tab.js.map +1 -1
  136. package/dist/base/table-view/column-display.d.ts +1 -1
  137. package/dist/base/table-view/column-display.js.map +1 -1
  138. package/dist/base/table-view/data-functions.d.ts +10 -4
  139. package/dist/base/table-view/data-functions.js.map +1 -1
  140. package/dist/base/table-view/factory.d.ts +5 -0
  141. package/dist/base/table-view/factory.js +8 -4
  142. package/dist/base/table-view/factory.js.map +1 -1
  143. package/dist/base/table-view/internal/auth.d.ts +17 -3
  144. package/dist/base/table-view/internal/auth.js +26 -5
  145. package/dist/base/table-view/internal/auth.js.map +1 -1
  146. package/dist/base/table-view/internal/column-conditions.d.ts +8 -1
  147. package/dist/base/table-view/internal/column-conditions.js +25 -13
  148. package/dist/base/table-view/internal/column-conditions.js.map +1 -1
  149. package/dist/base/table-view/internal/column-display.d.ts +1 -2
  150. package/dist/base/table-view/internal/column-display.js +3 -9
  151. package/dist/base/table-view/internal/column-display.js.map +1 -1
  152. package/dist/base/table-view/internal/factory-helpers.d.ts +1 -1
  153. package/dist/base/table-view/internal/factory-helpers.js +17 -14
  154. package/dist/base/table-view/internal/factory-helpers.js.map +1 -1
  155. package/dist/base/table-view/internal/factory-lifecycle-created.d.ts +2 -3
  156. package/dist/base/table-view/internal/factory-lifecycle-created.js +5 -5
  157. package/dist/base/table-view/internal/factory-lifecycle-created.js.map +1 -1
  158. package/dist/base/table-view/internal/factory-lifecycle-filter.d.ts +2 -2
  159. package/dist/base/table-view/internal/factory-private-actions.d.ts +2 -2
  160. package/dist/base/table-view/internal/files.d.ts +2 -1
  161. package/dist/base/table-view/internal/files.js +14 -5
  162. package/dist/base/table-view/internal/files.js.map +1 -1
  163. package/dist/base/table-view/internal/footer.d.ts +3 -3
  164. package/dist/base/table-view/internal/footer.js +16 -15
  165. package/dist/base/table-view/internal/footer.js.map +1 -1
  166. package/dist/base/table-view/internal/guards.d.ts +27 -5
  167. package/dist/base/table-view/internal/guards.js +133 -49
  168. package/dist/base/table-view/internal/guards.js.map +1 -1
  169. package/dist/base/table-view/internal/meta-helpers.d.ts +1 -1
  170. package/dist/base/table-view/internal/meta-helpers.js +3 -4
  171. package/dist/base/table-view/internal/meta-helpers.js.map +1 -1
  172. package/dist/base/table-view/internal/meta.js +2 -3
  173. package/dist/base/table-view/internal/meta.js.map +1 -1
  174. package/dist/base/table-view/internal/option-contract.d.ts +9 -1
  175. package/dist/base/table-view/internal/option-footer.d.ts +2 -2
  176. package/dist/base/table-view/internal/option-row-actions.d.ts +1 -1
  177. package/dist/base/table-view/internal/realtime.d.ts +52 -11
  178. package/dist/base/table-view/internal/realtime.js +23 -16
  179. package/dist/base/table-view/internal/realtime.js.map +1 -1
  180. package/dist/base/table-view/internal/resource-form.d.ts +1 -1
  181. package/dist/base/table-view/internal/resource-form.js +1 -1
  182. package/dist/base/table-view/internal/resource-form.js.map +1 -1
  183. package/dist/base/table-view/internal/row-actions-schema.d.ts +12 -12
  184. package/dist/base/table-view/internal/row-rule-shape.d.ts +11 -5
  185. package/dist/base/table-view/internal/row-rule-shape.js +60 -30
  186. package/dist/base/table-view/internal/row-rule-shape.js.map +1 -1
  187. package/dist/base/table-view/internal/row-rules.d.ts +28 -1
  188. package/dist/base/table-view/internal/row-rules.js +8 -19
  189. package/dist/base/table-view/internal/row-rules.js.map +1 -1
  190. package/dist/base/table-view/internal/tabs.d.ts +1 -1
  191. package/dist/base/table-view/internal/tabs.js +1 -4
  192. package/dist/base/table-view/internal/tabs.js.map +1 -1
  193. package/dist/base/table-view/internal/validation.d.ts +2 -2
  194. package/dist/base/table-view/internal/validation.js +7 -7
  195. package/dist/base/table-view/internal/validation.js.map +1 -1
  196. package/dist/base/table-view/meta.d.ts +7 -6
  197. package/dist/base/table-view/meta.js +12 -7
  198. package/dist/base/table-view/meta.js.map +1 -1
  199. package/dist/base/table-view/resource-form.d.ts +3 -1
  200. package/dist/base/table-view/resource-form.js +2 -0
  201. package/dist/base/table-view/resource-form.js.map +1 -1
  202. package/dist/base/table-view/routes.d.ts +73 -21
  203. package/dist/base/table-view/routes.js +13 -10
  204. package/dist/base/table-view/routes.js.map +1 -1
  205. package/dist/base/table-view/schema.d.ts +157 -152
  206. package/dist/base/table-view/schema.js +2 -1
  207. package/dist/base/table-view/schema.js.map +1 -1
  208. package/dist/base/table-view/source.js +4 -1
  209. package/dist/base/table-view/source.js.map +1 -1
  210. package/dist/base/table-view.d.ts +1 -1
  211. package/dist/base/table-view.js.map +1 -1
  212. package/dist/base/top-list-card.d.ts +35 -33
  213. package/dist/base/top-list-card.js +2 -0
  214. package/dist/base/top-list-card.js.map +1 -1
  215. package/dist/base/types/error.js +5 -2
  216. package/dist/base/types/error.js.map +1 -1
  217. package/dist/base/types/guards.d.ts +4 -4
  218. package/dist/base/types/json.d.ts +1 -10
  219. package/dist/base/types/row-action.d.ts +7 -7
  220. package/dist/component-options/confirm-dialog.d.ts +2 -0
  221. package/dist/component-options/confirm-dialog.js.map +1 -1
  222. package/dist/component-options/data-type.d.ts +2 -2
  223. package/dist/component-options/data-type.js +7 -7
  224. package/dist/component-options/data-type.js.map +1 -1
  225. package/dist/component-options/page-layout.d.ts +3 -2
  226. package/dist/component-options/page-layout.js.map +1 -1
  227. package/dist/component-options/row-action.d.ts +14 -9
  228. package/dist/component-slots.d.ts +11 -2
  229. package/dist/component-slots.js +1 -0
  230. package/dist/component-slots.js.map +1 -1
  231. package/dist/component.d.ts +7 -8
  232. package/dist/component.js +17 -25
  233. package/dist/component.js.map +1 -1
  234. package/dist/data-controllers/members.d.ts +11 -7
  235. package/dist/data-controllers/members.js +1 -1
  236. package/dist/data-controllers/members.js.map +1 -1
  237. package/dist/data-controllers/roles.d.ts +32 -10
  238. package/dist/db/internal/exportJobs.model.d.ts +7 -5
  239. package/dist/db/internal/inviteResolutions.model.d.ts +7 -5
  240. package/dist/db/internal/tenantLifecycle.model.d.ts +15 -7
  241. package/dist/db/internal/tenantLifecycle.model.js +16 -7
  242. package/dist/db/internal/tenantLifecycle.model.js.map +1 -1
  243. package/dist/db/models/roles.model.d.ts +7 -5
  244. package/dist/db/models/tenantMembers.model.d.ts +7 -5
  245. package/dist/db/models/tenants.model.d.ts +7 -5
  246. package/dist/db/models/user_invites.model.d.ts +7 -5
  247. package/dist/guards.d.ts +1 -1
  248. package/dist/hooks.d.ts +1 -2
  249. package/dist/hooks.js +33 -26
  250. package/dist/hooks.js.map +1 -1
  251. package/dist/internal/attachments.d.ts +2 -2
  252. package/dist/internal/component-builder.js +2 -1
  253. package/dist/internal/component-builder.js.map +1 -1
  254. package/dist/internal/component-serialization.d.ts +2 -4
  255. package/dist/internal/component-serialization.js +4 -3
  256. package/dist/internal/component-serialization.js.map +1 -1
  257. package/dist/internal/component-slots.d.ts +19 -2
  258. package/dist/internal/component-slots.js +35 -12
  259. package/dist/internal/component-slots.js.map +1 -1
  260. package/dist/internal/component-watch-filter.d.ts +11 -0
  261. package/dist/internal/component-watch-filter.js +22 -0
  262. package/dist/internal/component-watch-filter.js.map +1 -0
  263. package/dist/internal/component-watches.js +0 -1
  264. package/dist/internal/component-watches.js.map +1 -1
  265. package/dist/internal/own-entry.d.ts +7 -0
  266. package/dist/internal/own-entry.js +13 -0
  267. package/dist/internal/own-entry.js.map +1 -0
  268. package/dist/internal/uploads.d.ts +30 -0
  269. package/dist/internal/uploads.js +19 -0
  270. package/dist/internal/uploads.js.map +1 -1
  271. package/dist/invite-extension-payloads.d.ts +14 -2
  272. package/dist/invite-extensions/internal/delivery.js +1 -2
  273. package/dist/invite-extensions/internal/delivery.js.map +1 -1
  274. package/dist/invite-extensions/internal/edit.d.ts +2 -1
  275. package/dist/invite-extensions/internal/edit.js +8 -9
  276. package/dist/invite-extensions/internal/edit.js.map +1 -1
  277. package/dist/invite-extensions/internal/form-slot.js +8 -15
  278. package/dist/invite-extensions/internal/form-slot.js.map +1 -1
  279. package/dist/invite-extensions/internal/payload-slices.d.ts +33 -3
  280. package/dist/invite-extensions/internal/payload-slices.js +84 -19
  281. package/dist/invite-extensions/internal/payload-slices.js.map +1 -1
  282. package/dist/invite-extensions/internal/types.d.ts +10 -1
  283. package/dist/invite-resolution.js +6 -4
  284. package/dist/invite-resolution.js.map +1 -1
  285. package/dist/json.d.ts +10 -0
  286. package/dist/json.js +3 -0
  287. package/dist/json.js.map +1 -0
  288. package/dist/layout-banners.d.ts +5 -2
  289. package/dist/layout-banners.js.map +1 -1
  290. package/dist/page/controllers.d.ts +2 -2
  291. package/dist/page/internal/extension-validation.js +3 -2
  292. package/dist/page/internal/extension-validation.js.map +1 -1
  293. package/dist/page/internal/metadata-layout.js +1 -1
  294. package/dist/page/internal/metadata-layout.js.map +1 -1
  295. package/dist/page/internal/metadata-publish.js +3 -2
  296. package/dist/page/internal/metadata-publish.js.map +1 -1
  297. package/dist/page/internal/metadata-registration.js +5 -4
  298. package/dist/page/internal/metadata-registration.js.map +1 -1
  299. package/dist/page/internal/metadata-upload-registration.d.ts +4 -2
  300. package/dist/page/internal/metadata-upload-registration.js +13 -4
  301. package/dist/page/internal/metadata-upload-registration.js.map +1 -1
  302. package/dist/page/internal/metadata-uploads.d.ts +5 -5
  303. package/dist/page/internal/metadata-uploads.js +4 -8
  304. package/dist/page/internal/metadata-uploads.js.map +1 -1
  305. package/dist/page/internal/page-record.d.ts +2 -3
  306. package/dist/page/internal/page-record.js +23 -16
  307. package/dist/page/internal/page-record.js.map +1 -1
  308. package/dist/page/layout-filter.js +17 -6
  309. package/dist/page/layout-filter.js.map +1 -1
  310. package/dist/page/metadata.js +5 -7
  311. package/dist/page/metadata.js.map +1 -1
  312. package/dist/request-tenant.d.ts +10 -4
  313. package/dist/request-tenant.js +16 -34
  314. package/dist/request-tenant.js.map +1 -1
  315. package/dist/tenant-export.d.ts +2 -1
  316. package/dist/tenant-lifecycle.d.ts +43 -4
  317. package/dist/tenant-lifecycle.js +97 -18
  318. package/dist/tenant-lifecycle.js.map +1 -1
  319. package/dist/types.d.ts +20 -0
  320. package/dist/uploads.d.ts +1 -1
  321. package/dist/uploads.js +39 -60
  322. package/dist/uploads.js.map +1 -1
  323. package/dist/utils/fire-and-forget.js.map +1 -1
  324. package/dist/utils/internal/decorated-class.d.ts +14 -0
  325. package/dist/utils/internal/decorated-class.js +37 -0
  326. package/dist/utils/internal/decorated-class.js.map +1 -0
  327. package/dist/utils/internal/type-check.d.ts +9 -0
  328. package/dist/utils/internal/type-check.js +13 -5
  329. package/dist/utils/internal/type-check.js.map +1 -1
  330. package/dist/utils/internal/value-parser.d.ts +1 -1
  331. package/dist/utils/internal/value-parser.js +22 -9
  332. package/dist/utils/internal/value-parser.js.map +1 -1
  333. package/dist/watch-actions.d.ts +1 -1
  334. package/docs/1.core/1.introduction.md +82 -77
  335. package/docs/1.core/10.layout-banners.md +57 -45
  336. package/docs/1.core/11.modules-and-dynamic-menus.md +178 -0
  337. package/docs/1.core/12.frontend-modules-and-request-helpers.md +155 -0
  338. package/docs/1.core/13.tenant-data-export.md +120 -0
  339. package/docs/1.core/2.pages-and-modules.md +140 -367
  340. package/docs/1.core/3.components.md +21 -22
  341. package/docs/1.core/4.permissions.md +92 -71
  342. package/docs/1.core/5.tenancy.md +150 -86
  343. package/docs/1.core/6.invites.md +90 -65
  344. package/docs/1.core/7.hooks.md +96 -166
  345. package/docs/1.core/8.realtime.md +44 -45
  346. package/docs/1.core/9.quick-actions.md +37 -30
  347. package/docs/2.auth/1.introduction.md +33 -35
  348. package/docs/2.auth/2.authentication.md +60 -38
  349. package/docs/2.auth/3.tokens-and-sessions.md +56 -55
  350. package/docs/2.auth/4.users.md +79 -51
  351. package/docs/2.auth/5.external-identities.md +25 -26
  352. package/docs/3.base/1.introduction.md +64 -60
  353. package/docs/3.base/10.display-blocks.md +15 -16
  354. package/docs/3.base/11.page-layouts.md +270 -0
  355. package/docs/3.base/12.table-row-actions.md +122 -0
  356. package/docs/3.base/13.table-filters-and-forms.md +104 -0
  357. package/docs/3.base/14.table-events-and-helpers.md +114 -0
  358. package/docs/3.base/15.table-sources-and-gates.md +24 -0
  359. package/docs/3.base/2.types.md +44 -41
  360. package/docs/3.base/3.forms.md +27 -28
  361. package/docs/3.base/4.tables.md +35 -368
  362. package/docs/3.base/5.charts-and-widgets.md +28 -29
  363. package/docs/3.base/6.layout.md +21 -270
  364. package/docs/3.base/8.data-types.md +39 -40
  365. package/docs/3.base/9.helpers.md +36 -38
  366. package/docs/4.notifications/1.introduction.md +22 -26
  367. package/docs/4.notifications/2.categories-and-subjects.md +58 -55
  368. package/docs/4.notifications/3.sending.md +72 -68
  369. package/docs/5.html-render/1.introduction.md +119 -81
  370. package/docs/5.html-render/2.built-in-emails.md +71 -51
  371. package/docs/index.md +18 -29
  372. package/docs/tsconfig.json +4 -0
  373. package/package.json +11 -11
  374. package/docs/images/emails-admin-invite.png +0 -0
  375. package/docs/images/emails-change-notice.png +0 -0
  376. package/docs/images/emails-change-verification.png +0 -0
  377. package/docs/images/emails-digest-logo-dark.png +0 -0
  378. package/docs/images/emails-digest-logo-light.png +0 -0
  379. package/docs/images/emails-digest.png +0 -0
  380. package/docs/images/emails-export-partial.png +0 -0
  381. package/docs/images/emails-export-ready.png +0 -0
  382. package/docs/images/emails-reset-password.png +0 -0
  383. package/docs/images/emails-two-factor.png +0 -0
  384. package/docs/images/emails-user-validation.png +0 -0
@@ -1,24 +1,20 @@
1
1
  ---
2
2
  title: "Hooks"
3
- description: "A hook is a named platform event with a typed argument list and a typed result."
3
+ description: "A hook is a named platform event with a typed argument list and a typed result. The Hook enum, its payloads, registration, and the dispatchers."
4
+ mode: reference
4
5
  ---
5
6
 
6
- # Hooks
7
-
8
- ## Overview
9
-
10
- A hook is a named platform event with a typed argument list and a typed result. Modules register handlers with `RegisterHook`, and the code that owns the event dispatches it: `ExecuteHooks` runs every handler and discards what they return, `CollectHooks` gathers their return values, and `GetRegisteredHooks` hands back the handlers themselves for a caller that must invoke each one differently. `HookSignatures` ties each `Hook` member to its arguments and result, so the compiler checks a handler against the hook it registers for.
7
+ A hook is a named platform event with a typed argument list and a typed result. Modules register handlers with `RegisterHook`, and the code that owns the event dispatches it. `ExecuteHooks` runs every handler and discards what they return. `CollectHooks` gathers their return values. `GetRegisteredHooks` returns the handlers themselves, for a caller that must call each one with its own arguments. `HookSignatures` maps each `Hook` member to its arguments and result, so the compiler checks a handler against the hook it registers for.
11
8
 
12
9
  Everything on this page lives in `@antelopejs/interface-dms/hooks`.
13
10
 
14
- ## Register a Handler
11
+ ## Register a handler
15
12
 
16
- ```typescript
13
+ ```typescript fragment
17
14
  import { Hook, RegisterHook } from "@antelopejs/interface-dms/hooks";
18
15
 
19
16
  export function registerBillingHooks(): void {
20
17
  RegisterHook(Hook.MEMBER_ADDED, async (payload) => {
21
- // payload: { tenantId, userId, isTenantOwner }
22
18
  await seedBillingProfile(payload.tenantId, payload.userId);
23
19
  });
24
20
 
@@ -28,13 +24,13 @@ export function registerBillingHooks(): void {
28
24
  }
29
25
  ```
30
26
 
31
- `RegisterHook<H>(name: H, callback: HookHandler<H>, options?: HookRegistrationOptions)` appends the callback to the hook's list. The check is a TypeScript one: `HookHandler<H>` derives the parameter and return types from `HookSignatures[H]`, so a mismatched handler is a compile error. At runtime the call performs no validation.
27
+ `RegisterHook<H>(name: H, callback: HookHandler<H>, options?: HookRegistrationOptions)` appends the callback to the hook's list. The check happens at compile time. `HookHandler<H>` derives the parameter and return types from `HookSignatures[H]`, so a mismatched handler is a compile error. At runtime the call validates nothing.
32
28
 
33
- A hook whose result is `undefined` is an event: its dispatchers discard what the handlers return, so such a handler may return anything — nothing, the result of the write it awaited, or a promise of it. `HookHandlerResult<H>` is that rule: `unknown` for these hooks, the hook's result (or a promise of it) for a hook that has one, such as `Hook.TENANT_DATA_EXPORT`.
29
+ A hook whose result is `undefined` is an event. Its dispatchers discard what the handlers return, so such a handler may return anything, including nothing, the result of a write it awaited, or a promise of it. `HookHandlerResult<H>` encodes that rule. It is `unknown` for an event hook. For a hook that has a result, such as `Hook.TENANT_DATA_EXPORT`, it is that result or a promise of it.
34
30
 
35
- A registration belongs to the module that makes it, and goes away with it. When the module is destroyed — it stops for good, or it reloads in development — its handlers are released, and a reloaded module registers its own again from its next `construct()`. A module therefore has nothing to unregister on its way out.
31
+ A registration belongs to the module that makes it. When that module is destroyed, because it stops for good or reloads in development, its handlers are released. A reloaded module registers its own again from its next `construct()`. A module therefore has nothing to unregister when it stops.
36
32
 
37
- Every registration records the module it came from, resolved from the registering call site. `HookRegistrationOptions.moduleId` overrides that resolution:
33
+ Every registration records a module id, resolved from the module that makes the call. `HookRegistrationOptions.moduleId` overrides it:
38
34
 
39
35
  ```typescript
40
36
  interface HookRegistrationOptions {
@@ -42,11 +38,11 @@ interface HookRegistrationOptions {
42
38
  }
43
39
  ```
44
40
 
45
- The id only matters to a dispatcher that treats handlers per module rather than as one flat list — the tenant data export namespaces each contributor's archive entries by it. Set it explicitly whenever the registration happens somewhere the runtime would attribute to another module, such as a shared helper called during your module's construction. It does not change when the registration is released: that follows the module that made it.
41
+ The id matters only to a dispatcher that treats handlers per module rather than as one list. The tenant data export namespaces each contributor's archive entries by it. Set it when the registration runs somewhere the runtime attributes to another module, such as a shared helper called while your module constructs. It does not change when the registration is released, which follows the module that made it.
46
42
 
47
- `UnregisterHook<H>(name: H, callback: HookHandler<H>)` removes a previously registered callback by identity, for a module that stops listening while it stays loaded. Keep a reference to the function you registered:
43
+ `UnregisterHook<H>(name: H, callback: HookHandler<H>)` removes a registered callback by identity, for a module that stops listening while it stays loaded. When the same function is registered more than once, it removes the oldest registration. Keep a reference to the function you registered:
48
44
 
49
- ```typescript
45
+ ```typescript fragment
50
46
  import {
51
47
  Hook,
52
48
  type HookHandler,
@@ -62,42 +58,56 @@ RegisterHook(Hook.MEMBER_ADDED, onMemberAdded);
62
58
  UnregisterHook(Hook.MEMBER_ADDED, onMemberAdded);
63
59
  ```
64
60
 
65
- `HookCallback` is the untyped shape the registry stores internally, `(...args: any[]) => Promise<any> | any`. Register through `HookHandler<H>` instead so the payload keeps its type.
66
-
67
- ## The `Hook` Enum
61
+ ## The `Hook` enum
68
62
 
69
63
  | Member | Value | Arguments | Result |
70
64
  | ------ | ----- | --------- | ------ |
71
- | `Hook.DATABASE_INITIALIZED` | `"database:initialized"` | none | `undefined` — fires on every DMS start; a handler registered later runs at once (see [Initialise Against the Database](#initialise-against-the-database)) |
65
+ | `Hook.DATABASE_INITIALIZED` | `"database:initialized"` | none | `undefined` |
72
66
  | `Hook.TENANT_BEING_PROVISIONED` | `"tenant:being-provisioned"` | `TenantProvisioningHookPayload` | `undefined` |
73
- | `Hook.TENANT_DELETED` | `"tenant:deleted"` | `tenantId: string` | `undefined` |
67
+ | `Hook.TENANT_DELETED` | `"tenant:deleted"` | `tenantId: string`, `context?: TenantDeletionContext` | `undefined` |
74
68
  | `Hook.TENANT_DATA_EXPORT` | `"tenant:data-export"` | `tenantId: string`, `archive: TenantExportArchive`, `signal: AbortSignal` | `TenantDataExportContribution \| void` |
75
69
  | `Hook.INVITE_BEING_CREATED` | `"invite:being-created"` | `InviteHookPayload` | `undefined` |
76
70
  | `Hook.INVITE_CREATED` | `"invite:created"` | `InviteCreatedHookPayload` | `undefined` |
77
71
  | `Hook.INVITE_DELETED` | `"invite:deleted"` | `InviteDeletedHookPayload` | `undefined` |
78
72
  | `Hook.MEMBER_BEING_ADDED` | `"member:being-added"` | `MemberHookPayload` | `undefined` |
79
- | `Hook.MEMBER_BEING_CHANGED` | `"member:being-changed"` | `MemberChangeHookPayload` | `undefined` — fired by `changeTenantMembers` under tenant lifecycle admission before every membership write (`kind`: `added`, `removed`, `roles`, `ownership`); throwing vetoes the write |
73
+ | `Hook.MEMBER_BEING_CHANGED` | `"member:being-changed"` | `MemberChangeHookPayload` | `undefined` |
80
74
  | `Hook.MEMBER_ADDED` | `"member:added"` | `MemberHookPayload` | `undefined` |
81
75
  | `Hook.MEMBER_REMOVED` | `"member:removed"` | `MemberRemovedHookPayload` | `undefined` |
82
76
  | `Hook.USER_REGISTERED` | `"user:registered"` | `UserRegisteredHookPayload` | `undefined` |
83
- | `Hook.USER_DELETED` | `"user:deleted"` | `UserDeletedHookPayload` | `undefined` — fires after every user deletion the DMS performs (today: a user deleting their own account), once its rows are gone; a failing handler is logged and does not bring the account back |
77
+ | `Hook.USER_DELETED` | `"user:deleted"` | `UserDeletedHookPayload` | `undefined` |
78
+
79
+ The `*_BEING_*` members fire before the write they precede. The past-tense members fire after it. `TENANT_BEING_PROVISIONED` has no past-tense counterpart. See [Enrich a workspace being provisioned](#enrich-a-workspace-being-provisioned).
80
+
81
+ Some members have timing of their own:
84
82
 
85
- The `*_BEING_*` members fire before the write they precede; the past-tense members fire after it. `TENANT_BEING_PROVISIONED` has no past-tense counterpart — see [Enrich a Workspace Being Provisioned](#enrich-a-workspace-being-provisioned).
83
+ - `DATABASE_INITIALIZED` fires on every DMS start, and a handler registered later runs at once. See [Initialise against the database](#initialise-against-the-database).
84
+ - `MEMBER_BEING_CHANGED` is fired by `changeTenantMembers`, under tenant lifecycle admission, before every membership write. It fires once per change, with `kind` set to `added`, `removed`, `roles` or `ownership`. A handler that throws refuses the write.
85
+ - `USER_DELETED` fires after every user deletion the DMS performs, once the user's rows are gone. Today the only such deletion is a user deleting their own account. The DMS catches and logs a failing handler, and the account stays deleted.
86
+ - `USER_REGISTERED` fires after the account is created. The DMS catches and logs a failing handler, so a subscriber cannot strand a user whose account already exists.
87
+ - `TENANT_DELETED` may fire more than once for one deletion. A handler deduplicates effects that are not idempotent by `context.operationId`.
86
88
 
87
- ## Payload Interfaces
89
+ ## Payload interfaces
88
90
 
89
91
  ```typescript
92
+ import type { InviteExtensionPayloads } from "@antelopejs/interface-dms/invite-extension-payloads";
93
+
90
94
  interface TenantProvisioningHookPayload {
91
95
  tenantId: string;
92
- userId?: string; // absent for the `default` tenant
96
+ /** Absent for the `default` tenant. */
97
+ userId?: string;
93
98
  extras: Record<string, unknown>;
94
99
  }
95
100
 
101
+ interface TenantDeletionContext {
102
+ operationId: string;
103
+ }
104
+
96
105
  interface InviteHookPayload {
97
106
  tenantId: string;
98
107
  email: string;
99
108
  asTenantOwner: boolean;
100
109
  roleIds: string[];
110
+ deliveryId?: string;
101
111
  }
102
112
 
103
113
  interface InviteCreatedHookPayload extends InviteHookPayload {
@@ -105,16 +115,27 @@ interface InviteCreatedHookPayload extends InviteHookPayload {
105
115
  token: string;
106
116
  }
107
117
 
118
+ type InviteDeletedReason =
119
+ | "accepted"
120
+ | "cancelled"
121
+ | "replaced"
122
+ | "expired"
123
+ | "resent";
124
+
108
125
  interface InviteDeletedHookPayload {
109
126
  tenantId: string;
110
127
  inviteId: string;
111
128
  email: string;
129
+ reason: InviteDeletedReason;
130
+ deliveryId?: string;
131
+ extensions?: InviteExtensionPayloads | null;
112
132
  }
113
133
 
114
134
  interface MemberHookPayload {
115
135
  tenantId: string;
116
136
  userId: string;
117
137
  isTenantOwner: boolean;
138
+ deliveryId?: string;
118
139
  }
119
140
 
120
141
  interface MemberRemovedHookPayload {
@@ -139,9 +160,13 @@ interface UserDeletedHookPayload {
139
160
  }
140
161
  ```
141
162
 
142
- The shapes are not uniform, and the difference between the addition and removal payloads is deliberate: `MEMBER_BEING_ADDED` and `MEMBER_ADDED` describe one member with `userId`, while `MEMBER_REMOVED` carries `userIds`, because a removal can cover several members at once. Let the handler's inferred parameter type guide you.
163
+ The shapes differ on purpose. `MEMBER_BEING_ADDED` and `MEMBER_ADDED` describe one member with `userId`. `MEMBER_REMOVED` carries `userIds`, because one removal can cover several members. The handler's inferred parameter type shows which shape a hook passes.
143
164
 
144
- `MemberChangeHookPayload` is `{ tenantId } & MemberChange`, a union discriminated by `kind`:
165
+ `deliveryId` is a stable identity for an invitation decision. It is set when the event comes from a durable invitation step, which can replay after a failure. A handler uses it to recognise a replay of an event it already handled.
166
+
167
+ `InviteDeletedHookPayload.reason` says why the invitation row went away. Two reasons delete the row without ending the invitation. `accepted` means the invitee joined and the invitation was consumed. `resent` means the same invitation was reissued under a new token. `extensions` holds the payloads the invitation carried, for a handler that must undo its own work. [Invites](/docs/interfaces/core/invites) covers how an invitation is resolved and replaced.
168
+
169
+ `MemberChangeHookPayload` is `MemberChange & { tenantId: string }`, where `MemberChange` is a union discriminated by `kind`:
145
170
 
146
171
  ```typescript
147
172
  type MemberChange =
@@ -151,21 +176,21 @@ type MemberChange =
151
176
  | { kind: "ownership"; userId: string; isTenantOwner: boolean; previousIsTenantOwner: boolean };
152
177
  ```
153
178
 
154
- Read `kind` first and return early for the kinds your policy does not limit. An invitation acceptance is an `added` change carrying its `deliveryId`, vetoed before the acceptance is persisted, including on replay: a `deliveryId` is not a policy exemption. See [`changeTenantMembers`](/docs/interfaces/core/tenancy#changetenantmembers) for which writes announce which kind.
179
+ Read `kind` first and return early for the kinds your policy does not limit. An invitation acceptance is an `added` change that carries its `deliveryId`. The veto runs before the acceptance is stored, on a replay too, so a `deliveryId` does not exempt a change from policy. See [`changeTenantMembers`](/docs/interfaces/core/tenancy#changetenantmembers) for which writes announce which kind.
155
180
 
156
- `UserDeletedHookPayload.tenantIds` lists the workspaces the user was a member of, all left before the deletion; delete the per-user rows your module keeps in them. `reason` is `"self"` today, the user deleting their own account.
181
+ `UserDeletedHookPayload.tenantIds` lists the workspaces the user was a member of. The user left all of them before the deletion. A module deletes the per-user rows it keeps in them. `reason` is `"self"` today, a user deleting their own account.
157
182
 
158
- `InviteCreatedHookPayload` carries the invite `token`. It is a signup credential: keep it inside the handler rather than forwarding it to a wider surface.
183
+ `InviteCreatedHookPayload` carries the invite `token`. The token is a signup credential, so keep it inside the handler and do not forward it.
159
184
 
160
- `TenantProvisioningHookPayload.extras` is opaque: the DMS reads no key of it, validates nothing and stores nothing. See [Enrich a Workspace Being Provisioned](#enrich-a-workspace-being-provisioned).
185
+ `TenantProvisioningHookPayload.extras` is opaque. The DMS reads no key of it, validates nothing and stores nothing. See [Enrich a workspace being provisioned](#enrich-a-workspace-being-provisioned).
161
186
 
162
- `TENANT_DATA_EXPORT` is the one hook whose handler produces something rather than only reacting. Its argument and return types live in `@antelopejs/interface-dms/tenant-export` and are re-exported from this module, so either import path works. They are covered in [Contribute to a Tenant Data Export](#contribute-to-a-tenant-data-export).
187
+ `TENANT_DATA_EXPORT` is the one hook whose handler returns a result. Its argument and result types live in `@antelopejs/interface-dms/tenant-export`, and this module re-exports them. [Tenant data export](/docs/interfaces/core/tenant-data-export) covers them.
163
188
 
164
- ## Dispatch: `ExecuteHooks` vs `CollectHooks`
189
+ ## Dispatch with `ExecuteHooks` or `CollectHooks`
165
190
 
166
- Both take the hook and its arguments and await each handler in registration order. They differ in what they do with the outcome.
191
+ Both take the hook and its arguments, and await each handler in registration order. They differ in what they do with the outcome.
167
192
 
168
- ```typescript
193
+ ```typescript fragment
169
194
  async function ExecuteHooks<H extends Hook>(
170
195
  name: H,
171
196
  ...args: HookSignatures[H]["args"]
@@ -174,42 +199,40 @@ async function ExecuteHooks<H extends Hook>(
174
199
  async function CollectHooks<H extends Hook>(
175
200
  name: H,
176
201
  ...args: HookSignatures[H]["args"]
177
- ): Promise<HookSignatures[H]["result"][]>;
202
+ ): Promise<Awaited<HookHandlerResult<H>>[]>;
178
203
  ```
179
204
 
180
205
  | | `ExecuteHooks` | `CollectHooks` |
181
206
  | --- | --- | --- |
182
- | Return value | `Promise<void>` — handler results are discarded | `Promise<Result[]>` — one entry per handler that returned |
183
- | A handler that throws | The rejection propagates to the caller and the remaining handlers do not run | The error is logged and the handler contributes nothing; the remaining handlers still run |
184
- | Use for | Notifying that something happened | Asking every handler to contribute something |
207
+ | Return value | `Promise<void>`. Handler results are discarded. | `Promise<Result[]>`, one entry per handler that did not throw. `Result` is the hook's declared result, and `unknown` for an event hook, whose handlers may return anything. |
208
+ | A handler that throws | The rejection reaches the caller, and the remaining handlers do not run. | The error is logged and the handler adds no entry. The remaining handlers still run. |
209
+ | Use for | Announcing that something happened | Asking every handler to contribute something |
185
210
 
186
- Use `ExecuteHooks` for the notification hooks — a failing handler should be able to fail the operation that fired it. Use `CollectHooks` for contribution hooks, where one broken module must not sink the whole collection.
211
+ Use `ExecuteHooks` for the notification hooks, where a failing handler fails the operation that fired it. Use `CollectHooks` where one broken module must not stop the others. On an event hook, `CollectHooks` still runs every handler and returns whatever each one returned, typed `unknown`.
187
212
 
188
- ```typescript
213
+ ```typescript fragment
189
214
  import {
190
215
  CollectHooks,
191
216
  ExecuteHooks,
192
217
  Hook,
193
218
  } from "@antelopejs/interface-dms/hooks";
194
219
 
195
- // notify: every handler runs, nothing is returned
196
220
  await ExecuteHooks(Hook.TENANT_DELETED, tenantId);
197
221
 
198
- // collect: gather what every handler returned, skipping the ones that threw
199
- const payloads = await CollectHooks(Hook.MEMBER_ADDED, {
222
+ const results = await CollectHooks(Hook.MEMBER_ADDED, {
200
223
  tenantId,
201
224
  userId,
202
225
  isTenantOwner: false,
203
226
  });
204
227
  ```
205
228
 
206
- A caller that must not be broken by a subscriber wraps `ExecuteHooks` in its own `try`/`catch` — the DMS does this for `USER_REGISTERED`, where a failing subscriber would otherwise strand a user whose account was already created.
229
+ A caller that a subscriber must not break wraps `ExecuteHooks` in its own `try`/`catch`. The DMS does so for `USER_REGISTERED` and `USER_DELETED`.
207
230
 
208
- ## Dispatch Handler by Handler: `GetRegisteredHooks`
231
+ ## Dispatch handler by handler with `GetRegisteredHooks`
209
232
 
210
233
  Both dispatchers pass the same arguments to every handler. When each handler needs arguments of its own, take the list instead:
211
234
 
212
- ```typescript
235
+ ```typescript fragment
213
236
  function GetRegisteredHooks<H extends Hook>(name: H): RegisteredHook<H>[];
214
237
 
215
238
  interface RegisteredHook<H extends Hook> {
@@ -218,27 +241,27 @@ interface RegisteredHook<H extends Hook> {
218
241
  }
219
242
  ```
220
243
 
221
- `moduleId` is what the registration recorded — the `HookRegistrationOptions.moduleId` that was passed, or the module the runtime attributed the call to — and is `undefined` when neither resolved. Handling the failures is the caller's job here: nothing wraps the handler for you.
244
+ `moduleId` is the id the registration recorded. It is the `HookRegistrationOptions.moduleId` passed, or else the module the runtime attributed the call to, and `undefined` when neither resolved. The caller handles failures, because nothing wraps the handler.
222
245
 
223
- This is how the tenant data export runs. Each contributor needs an archive namespaced under its own module id, which no shared argument list can express.
246
+ The tenant data export runs this way. Each contributor needs an archive namespaced under its own module id, which no shared argument list can express.
224
247
 
225
- ## Initialise Against the Database
248
+ ## Initialise against the database
226
249
 
227
- `DATABASE_INITIALIZED` behaves like an already-resolved promise rather than a one-off event. The DMS fires it from its `start()`, once the database schemas are registered, and the hook then stays settled until the DMS stops:
250
+ `DATABASE_INITIALIZED` behaves like a promise that has already resolved. The DMS fires it from its `start()`, once the database schemas are registered, and the hook stays settled until the DMS stops:
228
251
 
229
- - A handler registered before that runs when the DMS starts, in series with the others, as `ExecuteHooks` runs them. One that throws fails the DMS start.
230
- - A handler registered while it is settled — by a module loaded after the DMS started, or one reloaded in development — runs at once, in the background. The registration does not wait for it, and a failure is logged with the hook and module name rather than thrown into the registering module's `construct()` or `start()`.
252
+ - A handler registered before the DMS starts runs when it starts, in series with the others, as `ExecuteHooks` runs them. A handler that throws fails the DMS start.
253
+ - A handler registered while the hook is settled runs at once, in the background. This covers a module loaded after the DMS started, and a module reloaded in development. The registration does not wait for the handler. A failure is logged with the hook and module name, and is not thrown into the registering module's `construct()` or `start()`.
231
254
  - A handler registered while the DMS is stopped waits for its next start.
232
255
 
233
- A handler therefore runs once per DMS start and once per registration made while the DMS runs, which adds up to several times over the life of a process: the DMS itself reloading replays every handler, and a module that reloads registers — and so runs — its own again. Write it to be safe to replay against a live database: create what is missing, never insert unconditionally, and never send a message or charge from it without a record that it already happened.
256
+ A handler therefore runs once per DMS start, and once per registration made while the DMS runs. Over the life of a process that adds up to several runs. A DMS reload replays every handler, and a reloaded module registers its handler again, which runs it again. Write the handler so it is safe to replay against a live database. Create what is missing, never insert unconditionally, and never send a message or charge from it without a record that it already happened.
234
257
 
235
- The settled state is what `SettleHook` and `UnsettleHook` manage. Only the hooks typed `StickyHook` accept them, and only `DATABASE_INITIALIZED` is one; the DMS is the only caller.
258
+ `SettleHook` and `UnsettleHook` manage the settled state. They accept only the hooks typed `StickyHook`, and `DATABASE_INITIALIZED` is the only one. The DMS is the only caller.
236
259
 
237
- ## Enrich a Workspace Being Provisioned
260
+ ## Enrich a workspace being provisioned
238
261
 
239
- A SaaS signup provisions a workspace, and the product running it usually collects more at that moment than the DMS models — an acquisition source, a team size, a referral code. `TENANT_BEING_PROVISIONED` is where a module writes those to its own tables, while the provisioning is still undone.
262
+ A SaaS signup provisions a workspace, and the product often collects more at that moment than the DMS models, such as an acquisition source, a team size or a referral code. A module writes those to its own tables in a `TENANT_BEING_PROVISIONED` handler, while the provisioning is not yet final.
240
263
 
241
- ```typescript
264
+ ```typescript fragment
242
265
  import {
243
266
  Hook,
244
267
  RegisterHook,
@@ -252,126 +275,33 @@ RegisterHook(Hook.TENANT_BEING_PROVISIONED, async ({ tenantId, userId, extras })
252
275
  });
253
276
  ```
254
277
 
255
- The hook is dispatched with `ExecuteHooks`, so handlers run in series, each awaited, and **a handler that throws aborts the provisioning**: the rejection reaches the emitter, later handlers do not run, and the emitter unwinds the workspace it was creating. A workspace therefore never ends up provisioned while the enrichment a consumer needed failed to be written — that direction is the guarantee, and the reason to fire before the commit rather than after it.
278
+ The hook is dispatched with `ExecuteHooks`, so handlers run in series, each awaited. A handler that throws aborts the provisioning. The rejection reaches the emitter, later handlers do not run, and the emitter removes the workspace it was creating. A workspace therefore never ends up provisioned while an enrichment a consumer needed failed to be written. The hook fires before the commit to give that guarantee.
256
279
 
257
- The other direction is not guaranteed, and no hook could provide it: there is no shared transaction to enlist in. A handler that already returned has committed its own rows, and neither a later handler throwing nor the emitter failing afterwards takes them back. Two habits make that survivable:
280
+ The reverse does not hold, because there is no shared transaction. A handler that already returned has committed its own rows. A later handler that throws, or an emitter that fails afterwards, does not take them back. Two habits make that safe:
258
281
 
259
- - Key the write on `tenantId`, so it can be cleaned up along with the workspace — subscribing to `TENANT_DELETED` covers the ordinary deletion path.
260
- - Make it idempotent, so a retried signup does not double it.
282
+ - Key the write on `tenantId`, so it can be cleaned up with the workspace. A `TENANT_DELETED` handler covers the normal deletion path.
283
+ - Make the write idempotent, so a retried signup does not write it twice.
261
284
 
262
- The flip side is the usual one for a `*_BEING_*` hook: a handler that throws for its own reasons fails somebody's signup, so keep it to what genuinely must not be lost.
285
+ A handler that throws for its own reasons fails someone's signup. Keep its work to what must not be lost.
263
286
 
264
- `extras` is opaque by design. The DMS reads no key of it, validates nothing, and stores nothing — it carries the object from the caller of the provisioning flow to the handlers, and each handler writes what it recognises. The shape is a contract between that caller and that handler, one the DMS is deliberately not a party to, so a product can start collecting a new field without waiting on a DMS release.
287
+ `extras` is opaque. The DMS reads no key of it, validates nothing, and stores nothing. It carries the object from the caller of the provisioning flow to the handlers, and each handler writes the keys it recognises. The shape is a contract between that caller and that handler, and the DMS is not part of it. A product can therefore collect a new field without a DMS release.
265
288
 
266
- It fires once per tenant, when the tenant is created, and never again on a later start once it has succeeded. The DMS fires it for the `default` tenant once every module has started, so a module's tables and settings are ready; that payload has no `userId`, since the `default` tenant has no owner until onboarding. Replicas sharing a new database elect a holder through a lease on the tenant's row, and the others serve meanwhile. A holder that stops renewing for `provisioningLease.staleAfterMs` is taken over, even if its handlers are still running. If that attempt fails, or its process dies, the next start or another replica runs every handler again, so a handler skips what it already wrote. A SaaS module such as **`dms-saas`** fires it for each workspace its signup flow creates.
289
+ The hook fires once per tenant, when the tenant is created, and never again on a later start once it has succeeded. The DMS fires it for the `default` tenant once every module has started, so the tables and settings that modules registered are ready. That payload has no `userId`, because the `default` tenant has no owner until onboarding. Replicas that share a new database elect one holder through a lease on the tenant's row, and the others serve meanwhile. A holder that stops renewing for `provisioningLease.staleAfterMs` is taken over, even if its handlers are still running. If that attempt fails, or its process dies, the next start or another replica runs every handler again, so a handler skips what it already wrote. Stopping the DMS does not wait for the handlers. An attempt still running then stops renewing its lease and is taken over the same way, even if its handlers are still running. A SaaS module such as `dms-saas` fires the hook for each workspace its signup flow creates.
267
290
 
268
- This is where a module writes the **default data** a tenant starts with — templates, folders, a sender, a demo history. Written once, that data belongs to the tenant: the module does not write it again on a later start, does not compare it with what members changed since, and does not wait for the tenant from `start()` or `DATABASE_INITIALIZED`.
291
+ A module writes the default data a tenant starts with in this hook, such as templates, folders, a sender or a demo history. Once written, that data belongs to the tenant. The module does not write it again on a later start, does not compare it with what members changed since, and does not wait for the tenant from `start()` or `DATABASE_INITIALIZED`.
269
292
 
270
- ```typescript
293
+ ```typescript fragment
271
294
  RegisterHook(Hook.TENANT_BEING_PROVISIONED, async ({ tenantId }) => {
272
295
  await writeDefaultFolders(tenantId);
273
296
  });
274
297
  ```
275
298
 
276
- An emitter fires the hook through `ProvisionTenant` from `@antelopejs/interface-dms/tenant-provisioning`, never with a bare `ExecuteHooks`. It registers the tenant as an instance of the `dms-tenant` schema first, then runs the handlers, so no handler runs before the instance exists, whatever order the modules registered in.
277
-
278
- ## Contribute to a Tenant Data Export
279
-
280
- `TENANT_DATA_EXPORT` asks every module for its slice of one tenant's data, which is assembled into a single ZIP archive. A handler receives the tenant id, an archive to write into, and an `AbortSignal`. It has two ways to contribute, and may use both:
281
-
282
- - Return a `TenantDataExportContribution` — its `data` is serialized to `modules/<moduleId>.json`.
283
- - Write entries into the `archive` — for anything too large to hold in memory, or that is not JSON.
284
-
285
- Register with `RegisterTenantDataExportContributor` rather than a bare `RegisterHook`. It names the module id explicitly, and that id is what namespaces the contributor's archive entries:
286
-
287
- ```typescript
288
- import { RegisterTenantDataExportContributor } from "@antelopejs/interface-dms/hooks";
289
-
290
- RegisterTenantDataExportContributor(
291
- "@acme/dms-billing",
292
- async (tenantId, archive, signal) => {
293
- await archive.addStream(
294
- "invoices.ndjson",
295
- streamInvoices(tenantId, signal),
296
- );
297
- return {
298
- moduleId: "@acme/dms-billing",
299
- data: await collectBillingSettings(tenantId),
300
- };
301
- },
302
- );
303
-
304
- ```
305
-
306
- The helper's signature is shown separately for reference:
307
-
308
- ```typescript
309
- import type { Hook, HookHandler } from "@antelopejs/interface-dms/hooks";
310
-
311
- declare function RegisterTenantDataExportContributor(
312
- moduleId: string,
313
- handler: HookHandler<Hook.TENANT_DATA_EXPORT>,
314
- ): void;
315
- ```
316
-
317
- A handler that only writes archive entries returns nothing.
318
-
319
- ```typescript
320
- interface TenantDataExportContribution {
321
- moduleId: string;
322
- data: unknown;
323
- }
324
- ```
325
-
326
- ### `TenantExportArchive`
327
-
328
- ```typescript
329
- import type { Readable } from "node:stream";
330
-
331
- interface TenantExportArchive {
332
- addJson(path: string, data: unknown): Promise<void>;
333
- addFile(path: string, localPath: string): Promise<void>;
334
- addStream(path: string, stream: Readable): Promise<void>;
335
- }
336
- ```
337
-
338
- | Method | Adds |
339
- | ------ | ---- |
340
- | `addJson` | `data` serialized as a JSON entry. |
341
- | `addFile` | An artifact already materialized on disk, read from `localPath`. |
342
- | `addStream` | A remote or generated dump, without ever holding it in memory. |
343
-
344
- Paths are relative: the implementation prefixes every entry with `modules/<moduleId>/`, so two contributors can both write `invoices.ndjson` without colliding, and none has to defend against another's paths.
345
-
346
- **Await every call.** Each method resolves only once the archiver has consumed the entry — awaiting is what propagates back-pressure to whatever produces the data, and is what keeps memory flat however large the export grows. Firing them without `await` gives up both.
347
-
348
- The `signal` aborts when the export is cancelled or times out. Pass it down to whatever produces your data so a cancelled export stops reading rather than finishing into a discarded archive.
349
-
350
- ### The Manifest
351
-
352
- Every run writes a `manifest.json` at the archive root recording what each contributor produced:
353
-
354
- ```typescript
355
- interface TenantExportManifest {
356
- tenantId: string;
357
- exportedAt: string;
358
- partial: boolean;
359
- modules: TenantExportModuleReport[];
360
- }
361
-
362
- interface TenantExportModuleReport {
363
- moduleId: string;
364
- declaredModuleId?: string;
365
- entries: string[];
366
- failed: boolean;
367
- error?: string;
368
- }
369
- ```
299
+ An emitter fires the hook through `ProvisionTenant` from `@antelopejs/interface-dms/tenant-provisioning`, never with a bare `ExecuteHooks`. `ProvisionTenant` registers the tenant as an instance of the `dms-tenant` schema first, then runs the handlers. No handler runs before the instance exists, whatever order the modules registered in.
370
300
 
371
- A failing contributor does not fail the export: its report carries `failed: true` and the error message, and `partial` goes `true` on the manifest so a consumer can tell an incomplete archive from a complete one. `declaredModuleId` appears when the returned contribution named a different id than the one the archive namespaced the entries under — a sign the registration and the return value disagree.
301
+ ## Contribute to a tenant data export
372
302
 
373
- Running the export is the other side of this contract: `buildTenantExportArchive` drives the contributors and writes the ZIP, and `startTenantExportJob` runs it as a background export job. Both belong to the [Base](/docs/interfaces/base/introduction) domain, under its helpers.
303
+ `TENANT_DATA_EXPORT` asks every module for its part of one tenant's data, assembled into a single ZIP archive. Register a contributor with `RegisterTenantDataExportContributor`. [Tenant data export](/docs/interfaces/core/tenant-data-export) covers the handler, the archive it writes into, and the manifest.
374
304
 
375
- ## Next Steps
305
+ ## Next steps
376
306
 
377
- [Realtime](/docs/interfaces/core/realtime) covers pushing an event to an open page instead of reacting to a platform event.
307
+ [Realtime](/docs/interfaces/core/realtime) covers pushing an event to an open page, as opposed to reacting to a platform event.