@antelopejs/interface-dms 0.10.4 → 0.12.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.
- package/CHANGELOG.md +19 -0
- package/README.md +21 -10
- package/dist/attachments.d.ts +15 -2
- package/dist/attachments.js +14 -1
- package/dist/attachments.js.map +1 -1
- package/dist/auth/db/models/sessions.model.d.ts +7 -5
- package/dist/auth/db/models/sessions.model.js +2 -9
- package/dist/auth/db/models/sessions.model.js.map +1 -1
- package/dist/auth/db/models/userExternalIdentities.model.d.ts +7 -5
- package/dist/auth/db/models/users.model.d.ts +7 -5
- package/dist/auth/db/tables/users.table.d.ts +2 -2
- package/dist/auth/db/tables/users.table.js.map +1 -1
- package/dist/auth/index.d.ts +4 -4
- package/dist/auth/internal/jwt-auth-decorator.d.ts +1 -1
- package/dist/auth/internal/jwt-auth-decorator.js +6 -3
- package/dist/auth/internal/jwt-auth-decorator.js.map +1 -1
- package/dist/auth/internal/request-authenticators.d.ts +12 -0
- package/dist/auth/internal/request-authenticators.js +53 -10
- package/dist/auth/internal/request-authenticators.js.map +1 -1
- package/dist/base/activity-feed.d.ts +34 -32
- package/dist/base/activity-feed.js +2 -0
- package/dist/base/activity-feed.js.map +1 -1
- package/dist/base/banner.d.ts +12 -10
- package/dist/base/banner.js +2 -0
- package/dist/base/banner.js.map +1 -1
- package/dist/base/block-registry/internal/describe.js +3 -9
- package/dist/base/block-registry/internal/describe.js.map +1 -1
- package/dist/base/block-registry/internal/ui.js +1 -1
- package/dist/base/block-registry/internal/ui.js.map +1 -1
- package/dist/base/card.d.ts +11 -9
- package/dist/base/card.js +2 -0
- package/dist/base/card.js.map +1 -1
- package/dist/base/chart-card.d.ts +4 -2
- package/dist/base/chart-card.js +2 -0
- package/dist/base/chart-card.js.map +1 -1
- package/dist/base/chart-schemas.d.ts +148 -148
- package/dist/base/chart.d.ts +65 -0
- package/dist/base/chart.js +65 -0
- package/dist/base/chart.js.map +1 -1
- package/dist/base/code-block.d.ts +5 -3
- package/dist/base/code-block.js +2 -0
- package/dist/base/code-block.js.map +1 -1
- package/dist/base/custom.d.ts +5 -0
- package/dist/base/custom.js +5 -0
- package/dist/base/custom.js.map +1 -1
- package/dist/base/data-types/default/address.d.ts +5 -0
- package/dist/base/data-types/default/address.js +5 -0
- package/dist/base/data-types/default/address.js.map +1 -1
- package/dist/base/data-types/default/choices.d.ts +20 -0
- package/dist/base/data-types/default/choices.js +20 -0
- package/dist/base/data-types/default/choices.js.map +1 -1
- package/dist/base/data-types/default/files.d.ts +22 -5
- package/dist/base/data-types/default/files.js +20 -0
- package/dist/base/data-types/default/files.js.map +1 -1
- package/dist/base/data-types/default/numbers.d.ts +15 -0
- package/dist/base/data-types/default/numbers.js +25 -9
- package/dist/base/data-types/default/numbers.js.map +1 -1
- package/dist/base/data-types/default/relations.d.ts +10 -2
- package/dist/base/data-types/default/relations.js +9 -6
- package/dist/base/data-types/default/relations.js.map +1 -1
- package/dist/base/data-types/default/strings.d.ts +25 -0
- package/dist/base/data-types/default/strings.js +25 -0
- package/dist/base/data-types/default/strings.js.map +1 -1
- package/dist/base/data-types/default-types.d.ts +1 -1
- package/dist/base/data-types/field-types.d.ts +10 -0
- package/dist/base/data-types/field-types.js +10 -0
- package/dist/base/data-types/field-types.js.map +1 -1
- package/dist/base/data-types/internal/relation-endpoints.js +1 -0
- package/dist/base/data-types/internal/relation-endpoints.js.map +1 -1
- package/dist/base/data-types/status-type.d.ts +5 -0
- package/dist/base/data-types/status-type.js +5 -0
- package/dist/base/data-types/status-type.js.map +1 -1
- package/dist/base/display.d.ts +6 -6
- package/dist/base/empty-state.d.ts +15 -13
- package/dist/base/empty-state.js +2 -0
- package/dist/base/empty-state.js.map +1 -1
- package/dist/base/form-block-schema.d.ts +50 -50
- package/dist/base/form-schema.d.ts +2 -0
- package/dist/base/form-schema.js +2 -0
- package/dist/base/form-schema.js.map +1 -1
- package/dist/base/grid.d.ts +4 -0
- package/dist/base/grid.js +4 -0
- package/dist/base/grid.js.map +1 -1
- package/dist/base/guard.d.ts +30 -28
- package/dist/base/guard.js +2 -0
- package/dist/base/guard.js.map +1 -1
- package/dist/base/helpers/archive-mode-helpers.d.ts +1 -1
- package/dist/base/helpers/file-refs.js +15 -15
- package/dist/base/helpers/file-refs.js.map +1 -1
- package/dist/base/internal/form-schema-validation.js +7 -1
- package/dist/base/internal/form-schema-validation.js.map +1 -1
- package/dist/base/internal/realtime-topics.js.map +1 -1
- package/dist/base/internal/searchable.d.ts +2 -1
- package/dist/base/internal/searchable.js +1 -3
- package/dist/base/internal/searchable.js.map +1 -1
- package/dist/base/key-value-list.d.ts +21 -19
- package/dist/base/key-value-list.js +2 -0
- package/dist/base/key-value-list.js.map +1 -1
- package/dist/base/kpi-card.d.ts +4 -2
- package/dist/base/kpi-card.js +2 -0
- package/dist/base/kpi-card.js.map +1 -1
- package/dist/base/live-toggle.d.ts +2 -0
- package/dist/base/live-toggle.js +2 -0
- package/dist/base/live-toggle.js.map +1 -1
- package/dist/base/meter.d.ts +23 -21
- package/dist/base/meter.js +2 -0
- package/dist/base/meter.js.map +1 -1
- package/dist/base/nav-card-grid.d.ts +24 -19
- package/dist/base/nav-card-grid.js +5 -0
- package/dist/base/nav-card-grid.js.map +1 -1
- package/dist/base/page-toolbar.d.ts +2 -0
- package/dist/base/page-toolbar.js +2 -0
- package/dist/base/page-toolbar.js.map +1 -1
- package/dist/base/period-selector.d.ts +3 -1
- package/dist/base/period-selector.js +2 -0
- package/dist/base/period-selector.js.map +1 -1
- package/dist/base/placeholder.d.ts +2 -0
- package/dist/base/placeholder.js +2 -0
- package/dist/base/placeholder.js.map +1 -1
- package/dist/base/resource-form-schema.d.ts +2 -2
- package/dist/base/searchable.d.ts +3 -2
- package/dist/base/searchable.js +2 -2
- package/dist/base/searchable.js.map +1 -1
- package/dist/base/section.d.ts +6 -2
- package/dist/base/section.js +4 -0
- package/dist/base/section.js.map +1 -1
- package/dist/base/stack.d.ts +6 -0
- package/dist/base/stack.js +6 -0
- package/dist/base/stack.js.map +1 -1
- package/dist/base/stat-group.d.ts +16 -14
- package/dist/base/stat-group.js +2 -0
- package/dist/base/stat-group.js.map +1 -1
- package/dist/base/tab.d.ts +10 -8
- package/dist/base/tab.js +2 -0
- package/dist/base/tab.js.map +1 -1
- package/dist/base/table-view/column-display.d.ts +1 -1
- package/dist/base/table-view/column-display.js.map +1 -1
- package/dist/base/table-view/data-functions.d.ts +10 -4
- package/dist/base/table-view/data-functions.js.map +1 -1
- package/dist/base/table-view/factory.d.ts +5 -0
- package/dist/base/table-view/factory.js +8 -4
- package/dist/base/table-view/factory.js.map +1 -1
- package/dist/base/table-view/internal/auth.d.ts +17 -3
- package/dist/base/table-view/internal/auth.js +26 -5
- package/dist/base/table-view/internal/auth.js.map +1 -1
- package/dist/base/table-view/internal/column-conditions.d.ts +8 -1
- package/dist/base/table-view/internal/column-conditions.js +25 -13
- package/dist/base/table-view/internal/column-conditions.js.map +1 -1
- package/dist/base/table-view/internal/column-display.d.ts +1 -2
- package/dist/base/table-view/internal/column-display.js +3 -9
- package/dist/base/table-view/internal/column-display.js.map +1 -1
- package/dist/base/table-view/internal/factory-helpers.d.ts +1 -1
- package/dist/base/table-view/internal/factory-helpers.js +17 -14
- package/dist/base/table-view/internal/factory-helpers.js.map +1 -1
- package/dist/base/table-view/internal/factory-lifecycle-created.d.ts +2 -3
- package/dist/base/table-view/internal/factory-lifecycle-created.js +5 -5
- package/dist/base/table-view/internal/factory-lifecycle-created.js.map +1 -1
- package/dist/base/table-view/internal/factory-lifecycle-filter.d.ts +2 -2
- package/dist/base/table-view/internal/factory-private-actions.d.ts +2 -2
- package/dist/base/table-view/internal/files.d.ts +2 -1
- package/dist/base/table-view/internal/files.js +14 -5
- package/dist/base/table-view/internal/files.js.map +1 -1
- package/dist/base/table-view/internal/footer.d.ts +3 -3
- package/dist/base/table-view/internal/footer.js +16 -15
- package/dist/base/table-view/internal/footer.js.map +1 -1
- package/dist/base/table-view/internal/guards.d.ts +27 -5
- package/dist/base/table-view/internal/guards.js +127 -49
- package/dist/base/table-view/internal/guards.js.map +1 -1
- package/dist/base/table-view/internal/meta-helpers.d.ts +1 -1
- package/dist/base/table-view/internal/meta-helpers.js +3 -4
- package/dist/base/table-view/internal/meta-helpers.js.map +1 -1
- package/dist/base/table-view/internal/meta.js +2 -3
- package/dist/base/table-view/internal/meta.js.map +1 -1
- package/dist/base/table-view/internal/option-contract.d.ts +1 -1
- package/dist/base/table-view/internal/option-footer.d.ts +2 -2
- package/dist/base/table-view/internal/option-row-actions.d.ts +1 -1
- package/dist/base/table-view/internal/realtime.d.ts +52 -11
- package/dist/base/table-view/internal/realtime.js +23 -16
- package/dist/base/table-view/internal/realtime.js.map +1 -1
- package/dist/base/table-view/internal/resource-form.d.ts +1 -1
- package/dist/base/table-view/internal/resource-form.js +1 -1
- package/dist/base/table-view/internal/resource-form.js.map +1 -1
- package/dist/base/table-view/internal/row-actions-schema.d.ts +12 -12
- package/dist/base/table-view/internal/row-rule-shape.d.ts +11 -5
- package/dist/base/table-view/internal/row-rule-shape.js +60 -30
- package/dist/base/table-view/internal/row-rule-shape.js.map +1 -1
- package/dist/base/table-view/internal/row-rules.d.ts +28 -1
- package/dist/base/table-view/internal/row-rules.js +8 -19
- package/dist/base/table-view/internal/row-rules.js.map +1 -1
- package/dist/base/table-view/internal/tabs.d.ts +1 -1
- package/dist/base/table-view/internal/tabs.js +1 -4
- package/dist/base/table-view/internal/tabs.js.map +1 -1
- package/dist/base/table-view/internal/validation.d.ts +2 -2
- package/dist/base/table-view/internal/validation.js +7 -7
- package/dist/base/table-view/internal/validation.js.map +1 -1
- package/dist/base/table-view/meta.d.ts +7 -6
- package/dist/base/table-view/meta.js +12 -7
- package/dist/base/table-view/meta.js.map +1 -1
- package/dist/base/table-view/resource-form.d.ts +3 -1
- package/dist/base/table-view/resource-form.js +2 -0
- package/dist/base/table-view/resource-form.js.map +1 -1
- package/dist/base/table-view/routes.d.ts +73 -21
- package/dist/base/table-view/routes.js +13 -10
- package/dist/base/table-view/routes.js.map +1 -1
- package/dist/base/table-view/schema.d.ts +152 -152
- package/dist/base/table-view/source.js +4 -1
- package/dist/base/table-view/source.js.map +1 -1
- package/dist/base/table-view.d.ts +1 -1
- package/dist/base/table-view.js.map +1 -1
- package/dist/base/top-list-card.d.ts +35 -33
- package/dist/base/top-list-card.js +2 -0
- package/dist/base/top-list-card.js.map +1 -1
- package/dist/base/types/error.js +5 -2
- package/dist/base/types/error.js.map +1 -1
- package/dist/base/types/guards.d.ts +4 -4
- package/dist/base/types/json.d.ts +1 -10
- package/dist/base/types/row-action.d.ts +7 -7
- package/dist/component-options/confirm-dialog.d.ts +2 -0
- package/dist/component-options/confirm-dialog.js.map +1 -1
- package/dist/component-options/data-type.d.ts +2 -2
- package/dist/component-options/data-type.js +7 -7
- package/dist/component-options/data-type.js.map +1 -1
- package/dist/component-options/row-action.d.ts +14 -9
- package/dist/component-slots.d.ts +11 -2
- package/dist/component-slots.js +1 -0
- package/dist/component-slots.js.map +1 -1
- package/dist/component.d.ts +7 -8
- package/dist/component.js +17 -25
- package/dist/component.js.map +1 -1
- package/dist/data-controllers/members.d.ts +11 -7
- package/dist/data-controllers/members.js +1 -1
- package/dist/data-controllers/members.js.map +1 -1
- package/dist/data-controllers/roles.d.ts +32 -10
- package/dist/db/internal/exportJobs.model.d.ts +7 -5
- package/dist/db/internal/inviteResolutions.model.d.ts +7 -5
- package/dist/db/internal/tenantLifecycle.model.d.ts +7 -5
- package/dist/db/models/roles.model.d.ts +7 -5
- package/dist/db/models/tenantMembers.model.d.ts +7 -5
- package/dist/db/models/tenants.model.d.ts +7 -5
- package/dist/db/models/user_invites.model.d.ts +7 -5
- package/dist/guards.d.ts +1 -1
- package/dist/hooks.d.ts +1 -2
- package/dist/hooks.js +33 -26
- package/dist/hooks.js.map +1 -1
- package/dist/internal/attachments.d.ts +2 -2
- package/dist/internal/component-builder.js +2 -1
- package/dist/internal/component-builder.js.map +1 -1
- package/dist/internal/component-serialization.d.ts +2 -4
- package/dist/internal/component-serialization.js +4 -3
- package/dist/internal/component-serialization.js.map +1 -1
- package/dist/internal/component-slots.d.ts +19 -2
- package/dist/internal/component-slots.js +35 -12
- package/dist/internal/component-slots.js.map +1 -1
- package/dist/internal/component-watch-filter.d.ts +11 -0
- package/dist/internal/component-watch-filter.js +22 -0
- package/dist/internal/component-watch-filter.js.map +1 -0
- package/dist/internal/component-watches.js +0 -1
- package/dist/internal/component-watches.js.map +1 -1
- package/dist/internal/own-entry.d.ts +7 -0
- package/dist/internal/own-entry.js +13 -0
- package/dist/internal/own-entry.js.map +1 -0
- package/dist/invite-extension-payloads.d.ts +14 -2
- package/dist/invite-extensions/internal/delivery.js +1 -2
- package/dist/invite-extensions/internal/delivery.js.map +1 -1
- package/dist/invite-extensions/internal/edit.d.ts +2 -1
- package/dist/invite-extensions/internal/edit.js +8 -9
- package/dist/invite-extensions/internal/edit.js.map +1 -1
- package/dist/invite-extensions/internal/form-slot.js +8 -15
- package/dist/invite-extensions/internal/form-slot.js.map +1 -1
- package/dist/invite-extensions/internal/payload-slices.d.ts +33 -3
- package/dist/invite-extensions/internal/payload-slices.js +84 -19
- package/dist/invite-extensions/internal/payload-slices.js.map +1 -1
- package/dist/invite-extensions/internal/types.d.ts +10 -1
- package/dist/invite-resolution.js +6 -4
- package/dist/invite-resolution.js.map +1 -1
- package/dist/json.d.ts +10 -0
- package/dist/json.js +3 -0
- package/dist/json.js.map +1 -0
- package/dist/layout-banners.d.ts +5 -2
- package/dist/layout-banners.js.map +1 -1
- package/dist/page/controllers.d.ts +2 -2
- package/dist/page/internal/extension-validation.js +3 -2
- package/dist/page/internal/extension-validation.js.map +1 -1
- package/dist/page/internal/metadata-layout.js +1 -1
- package/dist/page/internal/metadata-layout.js.map +1 -1
- package/dist/page/internal/metadata-publish.js +3 -2
- package/dist/page/internal/metadata-publish.js.map +1 -1
- package/dist/page/internal/metadata-registration.js +5 -4
- package/dist/page/internal/metadata-registration.js.map +1 -1
- package/dist/page/internal/metadata-upload-registration.d.ts +3 -1
- package/dist/page/internal/metadata-upload-registration.js +9 -0
- package/dist/page/internal/metadata-upload-registration.js.map +1 -1
- package/dist/page/internal/metadata-uploads.d.ts +18 -5
- package/dist/page/internal/metadata-uploads.js +7 -8
- package/dist/page/internal/metadata-uploads.js.map +1 -1
- package/dist/page/internal/page-record.d.ts +2 -3
- package/dist/page/internal/page-record.js +19 -14
- package/dist/page/internal/page-record.js.map +1 -1
- package/dist/page/layout-filter.js +17 -6
- package/dist/page/layout-filter.js.map +1 -1
- package/dist/page/metadata.js +5 -7
- package/dist/page/metadata.js.map +1 -1
- package/dist/request-tenant.d.ts +10 -4
- package/dist/request-tenant.js +16 -34
- package/dist/request-tenant.js.map +1 -1
- package/dist/tenant-export.d.ts +2 -1
- package/dist/tenant-lifecycle.d.ts +13 -4
- package/dist/tenant-lifecycle.js +51 -18
- package/dist/tenant-lifecycle.js.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/uploads.js +42 -59
- package/dist/uploads.js.map +1 -1
- package/dist/utils/fire-and-forget.js.map +1 -1
- package/dist/utils/internal/decorated-class.d.ts +14 -0
- package/dist/utils/internal/decorated-class.js +37 -0
- package/dist/utils/internal/decorated-class.js.map +1 -0
- package/dist/utils/internal/type-check.d.ts +9 -0
- package/dist/utils/internal/type-check.js +13 -5
- package/dist/utils/internal/type-check.js.map +1 -1
- package/dist/utils/internal/value-parser.d.ts +1 -1
- package/dist/utils/internal/value-parser.js +22 -9
- package/dist/utils/internal/value-parser.js.map +1 -1
- package/dist/watch-actions.d.ts +1 -1
- package/docs/1.core/1.introduction.md +82 -77
- package/docs/1.core/10.layout-banners.md +57 -45
- package/docs/1.core/11.modules-and-dynamic-menus.md +178 -0
- package/docs/1.core/12.frontend-modules-and-request-helpers.md +155 -0
- package/docs/1.core/13.tenant-data-export.md +120 -0
- package/docs/1.core/2.pages-and-modules.md +140 -367
- package/docs/1.core/3.components.md +21 -22
- package/docs/1.core/4.permissions.md +92 -71
- package/docs/1.core/5.tenancy.md +112 -86
- package/docs/1.core/6.invites.md +90 -65
- package/docs/1.core/7.hooks.md +96 -166
- package/docs/1.core/8.realtime.md +44 -45
- package/docs/1.core/9.quick-actions.md +37 -30
- package/docs/2.auth/1.introduction.md +33 -35
- package/docs/2.auth/2.authentication.md +60 -38
- package/docs/2.auth/3.tokens-and-sessions.md +56 -55
- package/docs/2.auth/4.users.md +79 -51
- package/docs/2.auth/5.external-identities.md +25 -26
- package/docs/3.base/1.introduction.md +64 -60
- package/docs/3.base/10.display-blocks.md +15 -16
- package/docs/3.base/11.page-layouts.md +273 -0
- package/docs/3.base/12.table-row-actions.md +122 -0
- package/docs/3.base/13.table-filters-and-forms.md +104 -0
- package/docs/3.base/14.table-events-and-helpers.md +114 -0
- package/docs/3.base/15.table-sources-and-gates.md +24 -0
- package/docs/3.base/2.types.md +44 -41
- package/docs/3.base/3.forms.md +27 -28
- package/docs/3.base/4.tables.md +34 -367
- package/docs/3.base/5.charts-and-widgets.md +28 -29
- package/docs/3.base/6.layout.md +21 -270
- package/docs/3.base/8.data-types.md +39 -40
- package/docs/3.base/9.helpers.md +36 -38
- package/docs/4.notifications/1.introduction.md +22 -26
- package/docs/4.notifications/2.categories-and-subjects.md +58 -55
- package/docs/4.notifications/3.sending.md +72 -68
- package/docs/5.html-render/1.introduction.md +119 -81
- package/docs/5.html-render/2.built-in-emails.md +71 -51
- package/docs/index.md +18 -29
- package/docs/tsconfig.json +4 -0
- package/package.json +11 -11
- package/docs/images/emails-admin-invite.png +0 -0
- package/docs/images/emails-change-notice.png +0 -0
- package/docs/images/emails-change-verification.png +0 -0
- package/docs/images/emails-digest-logo-dark.png +0 -0
- package/docs/images/emails-digest-logo-light.png +0 -0
- package/docs/images/emails-digest.png +0 -0
- package/docs/images/emails-export-partial.png +0 -0
- package/docs/images/emails-export-ready.png +0 -0
- package/docs/images/emails-reset-password.png +0 -0
- package/docs/images/emails-two-factor.png +0 -0
- package/docs/images/emails-user-validation.png +0 -0
package/docs/1.core/7.hooks.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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`
|
|
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`
|
|
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`
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
`
|
|
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
|
|
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
|
|
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`.
|
|
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
|
|
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
|
|
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
|
|
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<
|
|
202
|
+
): Promise<Awaited<HookHandlerResult<H>>[]>;
|
|
178
203
|
```
|
|
179
204
|
|
|
180
205
|
| | `ExecuteHooks` | `CollectHooks` |
|
|
181
206
|
| --- | --- | --- |
|
|
182
|
-
| Return value | `Promise<void
|
|
183
|
-
| A handler that throws | The rejection
|
|
184
|
-
| Use for |
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
248
|
+
## Initialise against the database
|
|
226
249
|
|
|
227
|
-
`DATABASE_INITIALIZED` behaves like
|
|
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
|
|
230
|
-
- A handler registered while
|
|
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
|
|
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
|
-
|
|
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
|
|
260
|
+
## Enrich a workspace being provisioned
|
|
238
261
|
|
|
239
|
-
A SaaS signup provisions a workspace, and the product
|
|
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
|
|
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
|
|
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
|
|
260
|
-
- Make
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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. A SaaS module such as `dms-saas` fires the hook for each workspace its signup flow creates.
|
|
267
290
|
|
|
268
|
-
|
|
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`.
|
|
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
|
-
|
|
301
|
+
## Contribute to a tenant data export
|
|
372
302
|
|
|
373
|
-
|
|
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
|
|
305
|
+
## Next steps
|
|
376
306
|
|
|
377
|
-
[Realtime](/docs/interfaces/core/realtime) covers pushing an event to an open page
|
|
307
|
+
[Realtime](/docs/interfaces/core/realtime) covers pushing an event to an open page, as opposed to reacting to a platform event.
|