@aglyn/aglyn 1.0.0-beta.218 → 1.0.0-beta.219
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/package.json +11 -11
- package/src/lib/app-utils/actions.d.ts +25 -374
- package/src/lib/app-utils/actions.js +101 -561
- package/src/lib/app-utils/actions.js.map +1 -1
- package/src/lib/app-utils/activity-presenter.d.ts +3 -3
- package/src/lib/app-utils/activity-presenter.js +36 -52
- package/src/lib/app-utils/activity-presenter.js.map +1 -1
- package/src/lib/app-utils/analytics-events.d.ts +1 -1
- package/src/lib/app-utils/analytics-events.js.map +1 -1
- package/src/lib/app-utils/analytics-provider.d.ts +182 -0
- package/src/lib/app-utils/analytics-provider.js +133 -0
- package/src/lib/app-utils/analytics-provider.js.map +1 -0
- package/src/lib/app-utils/api-plugins.d.ts +46 -0
- package/src/lib/app-utils/api-plugins.js +19 -0
- package/src/lib/app-utils/api-plugins.js.map +1 -1
- package/src/lib/app-utils/{marketplace-overrides.d.ts → artifact-overrides.d.ts} +9 -6
- package/src/lib/app-utils/{marketplace-overrides.js → artifact-overrides.js} +10 -7
- package/src/lib/app-utils/artifact-overrides.js.map +1 -0
- package/src/lib/app-utils/{marketplace-provenance.d.ts → artifact-provenance.d.ts} +12 -10
- package/src/lib/app-utils/{marketplace-provenance.js → artifact-provenance.js} +9 -7
- package/src/lib/app-utils/artifact-provenance.js.map +1 -0
- package/src/lib/app-utils/assist-credits.js +2 -1
- package/src/lib/app-utils/assist-credits.js.map +1 -1
- package/src/lib/app-utils/binding-token-catalog.d.ts +5 -13
- package/src/lib/app-utils/binding-token-catalog.js +17 -41
- package/src/lib/app-utils/binding-token-catalog.js.map +1 -1
- package/src/lib/app-utils/brand-support-line.d.ts +36 -0
- package/src/lib/app-utils/brand-support-line.js +38 -0
- package/src/lib/app-utils/brand-support-line.js.map +1 -0
- package/src/lib/app-utils/campaign-touch.d.ts +5 -5
- package/src/lib/app-utils/campaign-touch.js +8 -8
- package/src/lib/app-utils/campaign-touch.js.map +1 -1
- package/src/lib/app-utils/collection-entries.d.ts +2 -2
- package/src/lib/app-utils/collection-entries.js.map +1 -1
- package/src/lib/app-utils/consent-group-change.d.ts +4 -0
- package/src/lib/app-utils/consent-group-change.js.map +1 -1
- package/src/lib/app-utils/console-record-links.d.ts +10 -89
- package/src/lib/app-utils/console-record-links.js +3 -107
- package/src/lib/app-utils/console-record-links.js.map +1 -1
- package/src/lib/app-utils/console-routes.d.ts +16 -7
- package/src/lib/app-utils/console-routes.js +9 -3
- package/src/lib/app-utils/console-routes.js.map +1 -1
- package/src/lib/app-utils/contacts.d.ts +23 -1
- package/src/lib/app-utils/contacts.js +21 -5
- package/src/lib/app-utils/contacts.js.map +1 -1
- package/src/lib/app-utils/cross-pool-desync.d.ts +39 -0
- package/src/lib/app-utils/cross-pool-desync.js +47 -0
- package/src/lib/app-utils/cross-pool-desync.js.map +1 -0
- package/src/lib/app-utils/csv-upload.d.ts +29 -0
- package/src/lib/app-utils/csv-upload.js +29 -0
- package/src/lib/app-utils/csv-upload.js.map +1 -0
- package/src/lib/app-utils/dataset-models.d.ts +5 -5
- package/src/lib/app-utils/dataset-models.js +19 -2
- package/src/lib/app-utils/dataset-models.js.map +1 -1
- package/src/lib/app-utils/datasets.d.ts +65 -11
- package/src/lib/app-utils/datasets.js +74 -11
- package/src/lib/app-utils/datasets.js.map +1 -1
- package/src/lib/app-utils/docs-deflection.d.ts +6 -6
- package/src/lib/app-utils/docs-deflection.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +12 -6
- package/src/lib/app-utils/docs-help.generated.js +21 -6
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.d.ts +2 -2
- package/src/lib/app-utils/docs-index.generated.js +514 -318
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/docs-retrieval.d.ts +3 -3
- package/src/lib/app-utils/docs-retrieval.js +14 -10
- package/src/lib/app-utils/docs-retrieval.js.map +1 -1
- package/src/lib/app-utils/duplicate-resource.d.ts +4 -1
- package/src/lib/app-utils/duplicate-resource.js +7 -4
- package/src/lib/app-utils/duplicate-resource.js.map +1 -1
- package/src/lib/app-utils/dynamic-list-rule.d.ts +1 -1
- package/src/lib/app-utils/dynamic-list-rule.js.map +1 -1
- package/src/lib/app-utils/element-ui.d.ts +1 -1
- package/src/lib/app-utils/element-ui.js +1 -1
- package/src/lib/app-utils/element-ui.js.map +1 -1
- package/src/lib/app-utils/email-starter-policy.js +3 -3
- package/src/lib/app-utils/email-starter-policy.js.map +1 -1
- package/src/lib/app-utils/entity-picker-context.d.ts +10 -5
- package/src/lib/app-utils/entity-picker-context.js.map +1 -1
- package/src/lib/app-utils/error-beacon.d.ts +75 -6
- package/src/lib/app-utils/error-beacon.js +160 -21
- package/src/lib/app-utils/error-beacon.js.map +1 -1
- package/src/lib/app-utils/expand-repeatables.d.ts +10 -10
- package/src/lib/app-utils/expand-repeatables.js +10 -10
- package/src/lib/app-utils/expand-repeatables.js.map +1 -1
- package/src/lib/app-utils/host-events.d.ts +82 -0
- package/src/lib/app-utils/host-events.js +62 -0
- package/src/lib/app-utils/host-events.js.map +1 -0
- package/src/lib/app-utils/margin-utilization.d.ts +4 -69
- package/src/lib/app-utils/margin-utilization.js +82 -69
- package/src/lib/app-utils/margin-utilization.js.map +1 -1
- package/src/lib/app-utils/node-definition-sanitizer.d.ts +16 -16
- package/src/lib/app-utils/node-definition-sanitizer.js +24 -20
- package/src/lib/app-utils/node-definition-sanitizer.js.map +1 -1
- package/src/lib/app-utils/node-interactions.d.ts +7 -7
- package/src/lib/app-utils/node-interactions.js.map +1 -1
- package/src/lib/app-utils/notifications.d.ts +78 -47
- package/src/lib/app-utils/notifications.js +63 -44
- package/src/lib/app-utils/notifications.js.map +1 -1
- package/src/lib/app-utils/payments-configured.d.ts +8 -21
- package/src/lib/app-utils/payments-configured.js +8 -23
- package/src/lib/app-utils/payments-configured.js.map +1 -1
- package/src/lib/app-utils/plan-entitlements.d.ts +43 -128
- package/src/lib/app-utils/plan-entitlements.js +145 -304
- package/src/lib/app-utils/plan-entitlements.js.map +1 -1
- package/src/lib/app-utils/platform-brand.d.ts +10 -0
- package/src/lib/app-utils/platform-brand.js +14 -3
- package/src/lib/app-utils/platform-brand.js.map +1 -1
- package/src/lib/app-utils/plugin-api-rate-limit.d.ts +7 -1
- package/src/lib/app-utils/plugin-api-rate-limit.js +11 -3
- package/src/lib/app-utils/plugin-api-rate-limit.js.map +1 -1
- package/src/lib/app-utils/plugin-bundle-checks.js +2 -2
- package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -1
- package/src/lib/app-utils/plugin-host-events.generated.d.ts +12 -0
- package/src/lib/app-utils/plugin-host-events.generated.js +169 -0
- package/src/lib/app-utils/plugin-host-events.generated.js.map +1 -0
- package/src/lib/app-utils/plugin-release-flags.generated.d.ts +12 -0
- package/src/lib/app-utils/plugin-release-flags.generated.js +106 -0
- package/src/lib/app-utils/plugin-release-flags.generated.js.map +1 -0
- package/src/lib/app-utils/realm-host-surface.generated.js +2 -0
- package/src/lib/app-utils/realm-host-surface.generated.js.map +1 -1
- package/src/lib/app-utils/release-flags.d.ts +14 -5
- package/src/lib/app-utils/release-flags.js +21 -125
- package/src/lib/app-utils/release-flags.js.map +1 -1
- package/src/lib/app-utils/repeat-sources.d.ts +9 -0
- package/src/lib/app-utils/repeat-sources.js.map +1 -1
- package/src/lib/app-utils/retained-refusals.d.ts +73 -0
- package/src/lib/app-utils/retained-refusals.js +186 -0
- package/src/lib/app-utils/retained-refusals.js.map +1 -0
- package/src/lib/app-utils/scope-tokens.d.ts +27 -0
- package/src/lib/app-utils/scope-tokens.js +61 -0
- package/src/lib/app-utils/scope-tokens.js.map +1 -1
- package/src/lib/app-utils/screen-link-context.js +3 -3
- package/src/lib/app-utils/screen-link-context.js.map +1 -1
- package/src/lib/app-utils/screen-route.js +1 -1
- package/src/lib/app-utils/screen-route.js.map +1 -1
- package/src/lib/app-utils/seo-audit.d.ts +132 -0
- package/src/lib/app-utils/seo-audit.js +246 -0
- package/src/lib/app-utils/seo-audit.js.map +1 -0
- package/src/lib/app-utils/seo-keywords.d.ts +48 -0
- package/src/lib/app-utils/seo-keywords.js +60 -0
- package/src/lib/app-utils/seo-keywords.js.map +1 -0
- package/src/lib/app-utils/seo-page-facts.d.ts +66 -0
- package/src/lib/app-utils/seo-page-facts.js +155 -0
- package/src/lib/app-utils/seo-page-facts.js.map +1 -0
- package/src/lib/app-utils/seo-site-scan.d.ts +126 -0
- package/src/lib/app-utils/seo-site-scan.js +162 -0
- package/src/lib/app-utils/seo-site-scan.js.map +1 -0
- package/src/lib/app-utils/seo-title-variables.js +1 -1
- package/src/lib/app-utils/seo-title-variables.js.map +1 -1
- package/src/lib/app-utils/server.d.ts +6 -9
- package/src/lib/app-utils/server.js +17 -27
- package/src/lib/app-utils/server.js.map +1 -1
- package/src/lib/app-utils/site-interactions.d.ts +501 -0
- package/src/lib/app-utils/site-interactions.js +554 -0
- package/src/lib/app-utils/site-interactions.js.map +1 -0
- package/src/lib/app-utils/{marketplace-theme.d.ts → site-theme.d.ts} +13 -2
- package/src/lib/app-utils/{marketplace-theme.js → site-theme.js} +2 -2
- package/src/lib/app-utils/site-theme.js.map +1 -0
- package/src/lib/app-utils/sitemap.d.ts +4 -3
- package/src/lib/app-utils/sitemap.js +9 -7
- package/src/lib/app-utils/sitemap.js.map +1 -1
- package/src/lib/app-utils/stale-build-error.d.ts +51 -0
- package/src/lib/app-utils/stale-build-error.js +100 -1
- package/src/lib/app-utils/stale-build-error.js.map +1 -1
- package/src/lib/app-utils/tax-jurisdiction-figures.d.ts +118 -0
- package/src/lib/app-utils/tax-jurisdiction-figures.js +123 -0
- package/src/lib/app-utils/tax-jurisdiction-figures.js.map +1 -0
- package/src/lib/app-utils/theme-library.d.ts +10 -3
- package/src/lib/app-utils/theme-library.js +13 -4
- package/src/lib/app-utils/theme-library.js.map +1 -1
- package/src/lib/app-utils/{campaign-attribution.d.ts → utm-attribution.d.ts} +11 -6
- package/src/lib/app-utils/{campaign-attribution.js → utm-attribution.js} +13 -8
- package/src/lib/app-utils/utm-attribution.js.map +1 -0
- package/src/lib/app-utils/{campaign-forwarding.d.ts → utm-forwarding.d.ts} +14 -14
- package/src/lib/app-utils/{campaign-forwarding.js → utm-forwarding.js} +26 -26
- package/src/lib/app-utils/utm-forwarding.js.map +1 -0
- package/src/lib/app-utils/video-object.js +6 -5
- package/src/lib/app-utils/video-object.js.map +1 -1
- package/src/lib/app-utils/visitor-consent.d.ts +41 -164
- package/src/lib/app-utils/visitor-consent.js +66 -277
- package/src/lib/app-utils/visitor-consent.js.map +1 -1
- package/src/lib/app-utils/web-vitals-rum.d.ts +7 -7
- package/src/lib/app-utils/web-vitals-rum.js +31 -63
- package/src/lib/app-utils/web-vitals-rum.js.map +1 -1
- package/src/lib/app-utils/where-used.d.ts +11 -6
- package/src/lib/app-utils/where-used.js +3 -8
- package/src/lib/app-utils/where-used.js.map +1 -1
- package/src/lib/foundation/definitions/components.types.d.ts +1 -1
- package/src/lib/foundation/definitions/components.types.js +1 -1
- package/src/lib/foundation/definitions/components.types.js.map +1 -1
- package/src/lib/foundation/definitions/org-billing.types.d.ts +9 -135
- package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
- package/src/lib/foundation/definitions/organization.types.d.ts +1 -3
- package/src/lib/foundation/definitions/organization.types.js.map +1 -1
- package/src/lib/foundation/definitions/platform.types.d.ts +11 -5
- package/src/lib/foundation/definitions/platform.types.js.map +1 -1
- package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +16 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.js +20 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -1
- package/src/lib/foundation/index.d.ts +1 -1
- package/src/lib/foundation/index.js.map +1 -1
- package/src/lib/plugin-manager/besigner-documents.d.ts +96 -0
- package/src/lib/plugin-manager/besigner-documents.js +61 -0
- package/src/lib/plugin-manager/besigner-documents.js.map +1 -0
- package/src/lib/plugin-manager/computed-variables.d.ts +71 -0
- package/src/lib/plugin-manager/computed-variables.js +74 -0
- package/src/lib/plugin-manager/computed-variables.js.map +1 -0
- package/src/lib/plugin-manager/custom-fields.d.ts +29 -2
- package/src/lib/plugin-manager/custom-fields.js +47 -1
- package/src/lib/plugin-manager/custom-fields.js.map +1 -1
- package/src/lib/plugin-manager/feature-plugins.d.ts +292 -20
- package/src/lib/plugin-manager/feature-plugins.js +105 -17
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +123 -0
- package/src/lib/plugin-manager/first-party-plugins.generated.js +784 -10
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/plugin-activity-actions.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js +25 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-contributions.d.ts +1 -1
- package/src/lib/plugin-manager/plugin-contributions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-declarations-repair.d.ts +36 -0
- package/src/lib/plugin-manager/plugin-declarations-repair.js +64 -0
- package/src/lib/plugin-manager/plugin-declarations-repair.js.map +1 -0
- package/src/lib/plugin-manager/plugin-dependents.d.ts +101 -0
- package/src/lib/plugin-manager/plugin-dependents.js +75 -0
- package/src/lib/plugin-manager/plugin-dependents.js.map +1 -0
- package/src/lib/plugin-manager/plugin-distribution.d.ts +55 -0
- package/src/lib/plugin-manager/plugin-distribution.js +45 -0
- package/src/lib/plugin-manager/plugin-distribution.js.map +1 -0
- package/src/lib/plugin-manager/plugin-host-collections.d.ts +17 -0
- package/src/lib/plugin-manager/plugin-host-collections.js +6 -0
- package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
- package/src/lib/plugin-manager/plugin-host-resources.d.ts +133 -0
- package/src/lib/plugin-manager/plugin-host-resources.js +52 -0
- package/src/lib/plugin-manager/plugin-host-resources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-org-erasure.d.ts +28 -2
- package/src/lib/plugin-manager/plugin-org-erasure.js +61 -2
- package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -1
- package/src/lib/plugin-manager/plugin-plan-entitlements.d.ts +123 -0
- package/src/lib/plugin-manager/plugin-plan-entitlements.js +101 -0
- package/src/lib/plugin-manager/plugin-plan-entitlements.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-index.d.ts +95 -0
- package/src/lib/plugin-manager/plugin-record-index.js +51 -0
- package/src/lib/plugin-manager/plugin-record-index.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-written.d.ts +58 -0
- package/src/lib/plugin-manager/plugin-record-written.js +42 -0
- package/src/lib/plugin-manager/plugin-record-written.js.map +1 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.d.ts +213 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.js +194 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-bundle.d.ts +145 -0
- package/src/lib/plugin-manager/plugin-site-bundle.js +78 -0
- package/src/lib/plugin-manager/plugin-site-bundle.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-cache.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-site-cache.js.map +1 -1
- package/src/lib/plugin-manager/plugin-site-export.d.ts +81 -0
- package/src/lib/plugin-manager/plugin-site-export.js +40 -0
- package/src/lib/plugin-manager/plugin-site-export.js.map +1 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.d.ts +88 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.js +29 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.js.map +1 -0
- package/src/lib/plugin-manager/plugin-tax-profile.d.ts +20 -4
- package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -1
- package/src/lib/plugin-manager/plugin-tax-return-sources.d.ts +255 -0
- package/src/lib/plugin-manager/plugin-tax-return-sources.js +191 -0
- package/src/lib/plugin-manager/plugin-tax-return-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-template-sources.d.ts +75 -0
- package/src/lib/plugin-manager/plugin-template-sources.js +37 -0
- package/src/lib/plugin-manager/plugin-template-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-usage-axes.d.ts +107 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js +107 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -0
- package/src/lib/plugin-manager/plugin-zones.d.ts +4 -5
- package/src/lib/plugin-manager/plugin-zones.js.map +1 -1
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js +2 -0
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -1
- package/src/lib/plugin-manager/record-captured-contact.d.ts +1 -14
- package/src/lib/plugin-manager/record-captured-contact.js +10 -32
- package/src/lib/plugin-manager/record-captured-contact.js.map +1 -1
- package/src/lib/plugin-manager/repeat-rows.d.ts +124 -0
- package/src/lib/plugin-manager/repeat-rows.js +108 -0
- package/src/lib/plugin-manager/repeat-rows.js.map +1 -0
- package/src/lib/plugin-manager/submission-record-target.d.ts +144 -0
- package/src/lib/plugin-manager/submission-record-target.js +125 -0
- package/src/lib/plugin-manager/submission-record-target.js.map +1 -0
- package/src/lib/plugin-manager/video-embed-provider.d.ts +108 -0
- package/src/lib/plugin-manager/video-embed-provider.js +165 -0
- package/src/lib/plugin-manager/video-embed-provider.js.map +1 -0
- package/src/lib/types/nodes.d.ts +2 -2
- package/src/lib/types/nodes.js.map +1 -1
- package/src/lib/app-utils/campaign-attribution.js.map +0 -1
- package/src/lib/app-utils/campaign-forwarding.js.map +0 -1
- package/src/lib/app-utils/contact-holder.d.ts +0 -68
- package/src/lib/app-utils/contact-holder.js +0 -129
- package/src/lib/app-utils/contact-holder.js.map +0 -1
- package/src/lib/app-utils/contact-merge.d.ts +0 -136
- package/src/lib/app-utils/contact-merge.js +0 -412
- package/src/lib/app-utils/contact-merge.js.map +0 -1
- package/src/lib/app-utils/crm-csv.d.ts +0 -239
- package/src/lib/app-utils/crm-csv.js +0 -467
- package/src/lib/app-utils/crm-csv.js.map +0 -1
- package/src/lib/app-utils/crm-digest.d.ts +0 -185
- package/src/lib/app-utils/crm-digest.js +0 -272
- package/src/lib/app-utils/crm-digest.js.map +0 -1
- package/src/lib/app-utils/form-dataset-binding.d.ts +0 -50
- package/src/lib/app-utils/form-dataset-binding.js +0 -74
- package/src/lib/app-utils/form-dataset-binding.js.map +0 -1
- package/src/lib/app-utils/list-import.d.ts +0 -158
- package/src/lib/app-utils/list-import.js +0 -332
- package/src/lib/app-utils/list-import.js.map +0 -1
- package/src/lib/app-utils/marketplace-listing-visibility.d.ts +0 -122
- package/src/lib/app-utils/marketplace-listing-visibility.js +0 -147
- package/src/lib/app-utils/marketplace-listing-visibility.js.map +0 -1
- package/src/lib/app-utils/marketplace-overrides.js.map +0 -1
- package/src/lib/app-utils/marketplace-provenance.js.map +0 -1
- package/src/lib/app-utils/marketplace-theme.js.map +0 -1
- package/src/lib/app-utils/marketplace-update-state.d.ts +0 -119
- package/src/lib/app-utils/marketplace-update-state.js +0 -132
- package/src/lib/app-utils/marketplace-update-state.js.map +0 -1
- package/src/lib/app-utils/publisher-agreement.d.ts +0 -289
- package/src/lib/app-utils/publisher-agreement.js +0 -321
- package/src/lib/app-utils/publisher-agreement.js.map +0 -1
- package/src/lib/app-utils/wistia-embed.d.ts +0 -104
- package/src/lib/app-utils/wistia-embed.js +0 -138
- package/src/lib/app-utils/wistia-embed.js.map +0 -1
- package/src/lib/app-utils/workflows.d.ts +0 -136
- package/src/lib/app-utils/workflows.js +0 -346
- package/src/lib/app-utils/workflows.js.map +0 -1
- package/src/lib/plugin-manager/commerce-zone-props.d.ts +0 -172
- package/src/lib/plugin-manager/commerce-zone-props.js +0 -31
- package/src/lib/plugin-manager/commerce-zone-props.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/actions.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n resolveAuthoredEventName,\n sanitizeEventParams,\n} from './analytics-events'\nimport { type AuthorHtmlRemoval, sanitizeAuthorHtml } from './author-html'\nimport {\n type ContactLifecycleStage,\n CRM_TASK_MAX_DUE_DAYS,\n type CrmActivityKind,\n type CrmTaskKind,\n isContactLifecycleStage,\n isCrmActivityKind,\n isCrmTaskKind,\n} from './crm-kinds'\nimport { HOST_EVENT_TYPES } from './workflows'\n\n/**\n * Actions builder (AGL-148): HubSpot-style event → action automation on\n * top of the AGL-128 event triggers. An action listens for a host event\n * (built-in or a custom name fired by another action), optionally filters\n * on the payload, and runs an ordered step list. Pure types + validation\n * here; the executor lives server-side where the I/O is (tenant utils).\n */\n\n/**\n * Client-side site events (AGL-256) the tenant page runtime emits:\n * element interactions, scroll behavior, dwell time, and exit intent.\n * Trigger config (selector/threshold/path) rides the action's trigger.\n */\nexport const SITE_EVENT_TYPES = [\n 'scrollDepth',\n 'scrollToElement',\n 'elementClick',\n 'elementVisible',\n // Hover choreography (AGL-562): delegated enter/leave on the trigger\n // selector — the nav-menu/drawer show-hide building blocks.\n 'elementHoverEnter',\n 'elementHoverLeave',\n 'exitIntent',\n 'timeOnPage',\n 'pageVisit',\n] as const\n\n/** Site events that watch a specific element and need a selector. */\nexport const ELEMENT_SCOPED_SITE_EVENTS = [\n 'scrollToElement',\n 'elementClick',\n 'elementVisible',\n 'elementHoverEnter',\n 'elementHoverLeave',\n] as const\n\nexport type SiteEventType = (typeof SITE_EVENT_TYPES)[number]\n\nexport function isSiteEventType(event: string): event is SiteEventType {\n return SITE_EVENT_TYPES.includes(event as SiteEventType)\n}\n\n/** Structured condition operators (AGL-557). */\nexport const TRIGGER_CONDITION_OPS = [\n 'equals',\n 'contains',\n 'notEmpty',\n] as const\n\nexport type TriggerConditionOp = (typeof TRIGGER_CONDITION_OPS)[number]\n\n/**\n * Structured per-action condition over the event payload (AGL-557): the\n * no-code sibling of the free-text `filter` expression, e.g. \"only run\n * when the `subscribe` field is not empty\". Evaluated against the same\n * scope as the filter (event name + payload fields); the action is\n * skipped when unmet.\n */\nexport interface HostActionTriggerCondition {\n /** Payload field the condition reads (a submitted form field name). */\n field: string\n op: TriggerConditionOp\n /** Comparison value for `equals`/`contains`; unused by `notEmpty`. */\n value?: string\n}\n\n/**\n * Evaluates a structured trigger condition (AGL-557). String-coerces the\n * payload value; `equals`/`contains` compare trimmed + case-insensitive\n * so checkbox values like \"Yes\" match an author-typed \"yes\". A missing\n * field never matches (and is \"empty\" for `notEmpty`); an absent or\n * field-less condition always passes, mirroring the filter's default.\n */\nexport function evaluateTriggerCondition(\n condition: HostActionTriggerCondition | undefined | null,\n payload: Record<string, unknown>,\n): boolean {\n const field = condition?.field?.trim()\n if (!condition || !field) return true\n const raw = (payload ?? {})[field]\n const actual = (raw == null ? '' : String(raw)).trim().toLowerCase()\n if (condition.op === 'notEmpty') return actual.length > 0\n const expected = String(condition.value ?? '')\n .trim()\n .toLowerCase()\n if (condition.op === 'equals') return actual === expected\n if (condition.op === 'contains') {\n return expected.length > 0 && actual.includes(expected)\n }\n return false\n}\n\n/** How chained trigger conditions combine (AGL-565). */\nexport const TRIGGER_COMBINATORS = ['and', 'or'] as const\n\nexport type TriggerCombinator = (typeof TRIGGER_COMBINATORS)[number]\n\n/** Cap on chained conditions per trigger (AGL-565). */\nexport const ACTION_MAX_CONDITIONS = 5\n\n/**\n * Normalizes a trigger's condition clauses to a list (AGL-565): the\n * `conditions` array wins when present; a legacy single `condition`\n * (AGL-557) becomes a one-element list. Read-time only — persisted docs\n * are never migrated, so pre-565 single-condition docs keep working\n * unchanged.\n */\nexport function normalizeTriggerConditions(\n trigger:\n | Pick<HostActionTrigger, 'condition' | 'conditions'>\n | undefined\n | null,\n): HostActionTriggerCondition[] {\n const conditions = trigger?.conditions\n if (Array.isArray(conditions)) return conditions.filter(Boolean)\n return trigger?.condition ? [trigger.condition] : []\n}\n\n/**\n * Evaluates a trigger's condition list with its combinator (AGL-565):\n * `and` (the default) requires every condition to pass, `or` any one.\n * Per-condition semantics are unchanged from AGL-557\n * (`evaluateTriggerCondition`), and an empty list always passes — which\n * covers every pre-565 doc without conditions.\n */\nexport function evaluateTriggerConditions(\n trigger:\n | Pick<HostActionTrigger, 'condition' | 'conditions' | 'combinator'>\n | undefined\n | null,\n payload: Record<string, unknown>,\n): boolean {\n const conditions = normalizeTriggerConditions(trigger)\n if (!conditions.length) return true\n return trigger?.combinator === 'or'\n ? conditions.some((condition) =>\n evaluateTriggerCondition(condition, payload),\n )\n : conditions.every((condition) =>\n evaluateTriggerCondition(condition, payload),\n )\n}\n\nexport interface HostActionTrigger {\n /**\n * Event the action enrolls on: a HOST_EVENT_TYPE (server-emitted), a\n * SITE_EVENT_TYPE (client-emitted, AGL-256), or a custom name fired by\n * another action's `customEvent` step.\n */\n event: string\n /** Optional expression over the payload; runs only when truthy. */\n filter?: string\n /** Optional structured payload condition (AGL-557); null clears it. */\n condition?: HostActionTriggerCondition | null\n /**\n * Chained payload conditions (AGL-565), combined with `combinator`.\n * When present this list wins over the legacy single `condition`\n * (see `normalizeTriggerConditions`); null clears it.\n */\n conditions?: HostActionTriggerCondition[] | null\n /** How the chained conditions combine (AGL-565); default `and`. */\n combinator?: TriggerCombinator | null\n /** CSS selector for element-scoped site events (click/visible/scroll-to). */\n selector?: string\n /** Percent 0-100 (scrollDepth) or seconds (timeOnPage). */\n threshold?: number\n /** Path pattern the site event listens on (overlay glob rules); empty = all. */\n pathPattern?: string\n /**\n * Fire at most once per visitor (AGL-266, localStorage-keyed) instead\n * of once per pageview. Site events only.\n */\n oncePerVisitor?: boolean\n /**\n * Fire at most once per browser session (AGL-274,\n * sessionStorage-keyed). Site events only.\n */\n oncePerSession?: boolean\n /**\n * Minimum minutes between fires for the same visitor (AGL-274,\n * localStorage timestamp). Site events only; ignored when\n * `oncePerVisitor` is set.\n */\n cooldownMinutes?: number\n /**\n * Fire on EVERY occurrence instead of once per pageview (AGL-562) —\n * required for repeatable UI choreography (menu toggles, drawer\n * open/close, hover show/hide). Site events only; the explicit\n * per-session/visitor/cooldown caps above still win when set.\n */\n everyTime?: boolean\n}\n\n/**\n * A per-STEP condition, and the difference between an automation and a flow.\n *\n * `HostActionTrigger.conditions` gates the whole action: every step runs or\n * none does. That is enough for \"when a form is submitted, do these three\n * things\" and not enough for anything with a shape — \"wait three days, then,\n * only if they have not ordered, send the reminder\" needs the condition to\n * belong to the step rather than to the run.\n *\n * Same clause type and the same combinator as the trigger's, evaluated\n * against the same scope, so an author learns one condition editor and the\n * two can never disagree about what `contains` means.\n */\nexport interface HostActionStepGuard {\n conditions: HostActionTriggerCondition[]\n /** How the clauses combine; default `and`, as on the trigger. */\n combinator?: TriggerCombinator | null\n}\n\n/**\n * The scope key a resumed flow carries to say the wait ended on the CLOCK\n * rather than on the event it was watching for.\n *\n * This is how a `waitForEvent` gets its timeout branch without a nested step\n * list: the flow resumes either way, and the step after it carries a `when`\n * naming this field. Underscored because it shares a namespace with the\n * event payload's own fields, which are merchant-authored form field names.\n */\nexport const FLOW_TIMED_OUT_FIELD = '_waitTimedOut'\n\n/**\n * Evaluates a step's own guard, with the trigger's semantics.\n *\n * An absent or clause-less guard passes, exactly as an absent trigger\n * condition does — so every step written before guards existed keeps running.\n */\nexport function evaluateStepGuard(\n guard: HostActionStepGuard | undefined | null,\n scope: Record<string, unknown>,\n): boolean {\n const conditions = (guard?.conditions ?? []).filter(Boolean)\n if (!conditions.length) return true\n return guard?.combinator === 'or'\n ? conditions.some((condition) => evaluateTriggerCondition(condition, scope))\n : conditions.every((condition) => evaluateTriggerCondition(condition, scope))\n}\n\nexport type HostActionStep = (\n // Entity references carry a doc id (AGL-261, rename-safe) with the name\n // kept as a display hint; pre-AGL-261 docs have only the name and the\n // executor resolves either.\n | { type: 'runWorkflow'; workflowId?: string; workflowName?: string }\n | {\n type: 'siteAlert'\n message: string\n severity?: 'info' | 'success' | 'warning' | 'error'\n }\n | { type: 'customEvent'; eventName: string }\n | { type: 'datasetAppend'; datasetId?: string; datasetName?: string }\n | { type: 'webhookPost'; webhookId?: string; webhookName?: string }\n // Client-side UI steps (AGL-257): run in the visitor's page by the\n // tenant automations engine; the server executor skips them.\n | { type: 'showOverlay'; overlayId?: string; overlayName?: string }\n | { type: 'stickyNav'; selector?: string }\n // Selectors accept CSS or a node id via [data-node-id=\"…\"] — the\n // besigner element picker emits the latter (AGL-314/319).\n | { type: 'addClass'; selector: string; className: string }\n | { type: 'removeClass'; selector: string; className: string }\n | { type: 'toggleClass'; selector: string; className: string }\n // Element show/hide choreography (AGL-562): sugar over the shared\n // hidden class (see element-ui.ts) so authors never type class names.\n // The besigner target picker emits the node's stable data-aglyn\n // selector; any CSS selector works.\n // Element visibility (AGL-562) with menu-grade choreography (AGL-589):\n // `delayMs` defers the change and a later visibility step on the same\n // selector cancels the pending one (the hover grace period), and\n // `dismissOn` self-dismisses a shown element on Escape / outside click.\n | {\n type: 'showElement'\n selector: string\n delayMs?: number\n dismissOn?: ElementDismissOption[]\n }\n | { type: 'hideElement'; selector: string; delayMs?: number }\n | {\n type: 'toggleElement'\n selector: string\n delayMs?: number\n dismissOn?: ElementDismissOption[]\n }\n // Drawer commands (AGL-562): delivered to muiDrawer instances over the\n // window event bus keyed by node id; an empty target addresses the\n // page's first drawer.\n // Attribute steps (AGL-2546): the semantics half of a hand-built\n // disclosure. `showElement` opens the panel; these are what let it say so.\n //\n // `selector`, deliberately, and NOT a new targeting field. Two mechanisms\n // key off that exact name: `regraftStepSelectors` rewrites it to the\n // grafted `cmp__{instance}__{id}` when an interaction is authored inside a\n // reusable component, and the runtime wraps it in `expandLeafSelector`.\n // The menu steps took `menuNodeId` instead, get neither, and compensate at\n // run time — one concept should not have three targeting models.\n //\n // Two verbs rather than one with an empty value: `\"\"` does not mean\n // \"absent\" uniformly. `hidden=\"\"` is true, `aria-expanded=\"\"` is invalid\n // and discarded, `data-x=\"\"` is a real value an attribute selector still\n // matches. Overloading it would make the step's meaning depend on which\n // attribute the author picked.\n | { type: 'setAttribute'; selector: string; name: string; value: string }\n | { type: 'removeAttribute'; selector: string; name: string }\n // Steps that act on ONE element (AGL-2867): the first rendered match of\n // `selector`, which is spelled `selector` for the reason the attribute\n // steps give above. `scrollTo` rather than `scrollToElement`, because that\n // name is already a TRIGGER — \"the visitor scrolled to the element\" — and\n // one word meaning both directions would be read wrong by every reader of a\n // stored document.\n | {\n type: 'scrollTo'\n selector: string\n /** Absent means `smooth`. A visitor who asks for reduced motion is always scrolled instantly. */\n behavior?: ScrollToBehavior\n /** Pixels left above the target, so a sticky header does not cover it. Absent means 0. */\n offsetPx?: number\n }\n // Presses a Video element's poster from outside it: the same press, so\n // the lightbox opens and plays, or the film plays in place of the poster.\n | { type: 'playVideo'; selector: string }\n | { type: 'openDrawer'; drawerNodeId?: string }\n | { type: 'closeDrawer'; drawerNodeId?: string }\n | { type: 'toggleDrawer'; drawerNodeId?: string }\n // Menu commands (AGL-568): the drawer pattern for Dropdown/Mega Menu\n // elements — delivered over their own window event bus keyed by node\n // id; an empty target addresses the page's first menu. Hover-triggered\n // opens carry a hover flag so the menu closes on pointer leave.\n | { type: 'openMenu'; menuNodeId?: string }\n | { type: 'closeMenu'; menuNodeId?: string }\n | { type: 'toggleMenu'; menuNodeId?: string }\n | { type: 'showHtml'; html: string }\n | { type: 'runJs'; code: string }\n // Screen targets are rename-safe (AGL-339): the tenant resolves the\n // screen id against the host routing map at run time; `url` is the\n // external/manual fallback.\n | { type: 'redirect'; url?: string; screenId?: string }\n | {\n type: 'trackGaEvent'\n eventName: string\n params?: Record<string, string>\n }\n // Server-side steps (AGL-257).\n | {\n type: 'sendEmail'\n subject: string\n body: string\n toField?: string\n /**\n * The stream this message belongs to, so a recipient who left it is not\n * mailed. Absent on every step authored before topics reached the\n * actions editor, which the executor resolves to the default topic.\n */\n topicId?: string\n }\n | { type: 'notifyAdmins'; title: string; body?: string }\n | { type: 'enrollList'; listId?: string; listName?: string }\n | { type: 'updateDataset'; datasetId?: string; datasetName?: string }\n | { type: 'assignCampaign'; campaignId?: string; campaignName?: string }\n /*\n * THE THREE FLOW STEPS.\n *\n * `wait` is the one that matters: without a durable delay an automation is\n * always trigger → immediate actions, so no welcome series, win-back or\n * post-purchase follow-up can exist at all. The other two are what make a\n * delay useful — something to end the flow early, and a wait that ends on\n * an event instead of on the clock.\n *\n * All three are SERVER steps. A delay outlives the page view that started\n * it by days, so the browser that fired the trigger is long gone by the time\n * the flow continues; `hostActionStepsForClient` truncates the client's copy\n * of the step list at the first of these for that reason.\n */\n | {\n type: 'wait'\n /** Whole minutes to hold before the next step. */\n delayMinutes: number\n }\n | {\n type: 'waitForEvent'\n /** The host or custom event that resumes this person's flow. */\n eventName: string\n /**\n * Whole minutes after which the flow continues anyway, with\n * {@link FLOW_TIMED_OUT_FIELD} true in scope. There is always a\n * deadline: a wait with no timeout is an enrollment that lives forever.\n */\n timeoutMinutes: number\n }\n /** Ends the enrollment here. Paired with a `when`, this is the exit branch. */\n | { type: 'exitFlow' }\n /*\n * THE CRM STEPS (AGL-2605).\n *\n * Server steps, every one, because each writes a record only the Admin\n * SDK may write on a visitor's behalf. Each acts on ONE person — the\n * contact the event names, resolved by `contactId` when the payload\n * carries one and by `email` otherwise — and each writes inside the\n * site's own facet or stamps the site's own scope, for the reason\n * `assignCampaign` gives: a contact row is shared by every site in the\n * org, and a stage or a tag is one holder's business record. A step whose\n * event names nobody the site can see does nothing and says so in the run.\n *\n * None of them carries a doc-id reference for the reference audit to\n * check: a stage and a kind are fixed vocabularies, a tag is free text,\n * and an owner is a member who is resolved at run time.\n */\n | { type: 'setContactStage'; lifecycleStage: ContactLifecycleStage }\n | { type: 'addContactTag'; tag: string }\n /**\n * The owner by uid when a member id was typed or a picker wrote the step,\n * by email when an address was typed; the executor resolves either against\n * the org's roster, and only the roster (AGL-2614).\n * Or `roundRobin`, and the owner is the next member of the pool the CRM's\n * settings keep (AGL-2618) — a step that names nobody and hands the\n * choice to the rotation, so a stage change can spread its follow-ups\n * across a team rather than pile them on one rep. The two are exclusive:\n * a rotation step carries no member, and the validator refuses one that\n * names both.\n */\n | {\n type: 'assignContactOwner'\n ownerUid?: string\n ownerEmail?: string\n roundRobin?: boolean\n }\n | {\n type: 'createCrmTask'\n title: string\n kind: CrmTaskKind\n /** Days from the run to the due date; `0` is due today. */\n dueInDays: number\n /**\n * Who gets it — by uid or by an address, both resolved against the\n * roster the way the owner step's are. Neither named, the task goes\n * to the contact's owner, then to nobody.\n */\n assigneeUid?: string\n assigneeEmail?: string\n }\n | { type: 'logCrmActivity'; kind: CrmActivityKind; body: string }\n) & {\n /** See {@link HostActionStepGuard}. Absent means the step always runs. */\n when?: HostActionStepGuard | null\n}\n\n/** Steps the tenant page runtime executes client-side (AGL-257). */\nexport const CLIENT_ACTION_STEP_TYPES: ReadonlySet<HostActionStepType> =\n new Set([\n 'showOverlay',\n 'stickyNav',\n 'addClass',\n 'removeClass',\n 'toggleClass',\n 'showElement',\n 'hideElement',\n 'toggleElement',\n 'openDrawer',\n 'closeDrawer',\n 'toggleDrawer',\n 'openMenu',\n 'closeMenu',\n 'toggleMenu',\n 'setAttribute',\n 'removeAttribute',\n 'scrollTo',\n 'playVideo',\n 'showHtml',\n 'runJs',\n 'redirect',\n 'trackGaEvent',\n 'siteAlert',\n ] as const)\n\nexport function isClientActionStep(step: HostActionStep): boolean {\n return CLIENT_ACTION_STEP_TYPES.has(step.type)\n}\n\n/**\n * The steps that suspend a run and continue it later, from a job beat.\n *\n * Named as a set rather than checked inline because three surfaces have to\n * agree on it: the executor stops here and writes an enrollment, the client\n * payload is truncated here, and the validator refuses a flow that waits\n * without a person to wait for.\n */\nexport const FLOW_SUSPENDING_STEP_TYPES: ReadonlySet<HostActionStepType> =\n new Set(['wait', 'waitForEvent'] as const)\n\nexport function isFlowSuspendingStep(step: HostActionStep): boolean {\n return FLOW_SUSPENDING_STEP_TYPES.has(step.type)\n}\n\n/**\n * The steps that act on the CRM (AGL-2605) — named as a set because the\n * executor dispatches all five to one module and the docs list them as one\n * group, and a step added to the union but not here would be a server step\n * the executor silently skipped.\n */\nexport const CRM_ACTION_STEP_TYPES: ReadonlySet<HostActionStepType> = new Set([\n 'setContactStage',\n 'addContactTag',\n 'assignContactOwner',\n 'createCrmTask',\n 'logCrmActivity',\n] as const)\n\n/** The CRM steps, as the type the executor narrows to. */\nexport type CrmActionStep = Extract<\n HostActionStep,\n {\n type:\n | 'setContactStage'\n | 'addContactTag'\n | 'assignContactOwner'\n | 'createCrmTask'\n | 'logCrmActivity'\n }\n>\n\nexport function isCrmActionStep(step: HostActionStep): step is CrmActionStep {\n return CRM_ACTION_STEP_TYPES.has(step.type)\n}\n\n/** Longest tag an automation may write — the console's own tag field cap. */\nexport const CONTACT_TAG_MAX_LENGTH = 60\n\n/*\n * THE RECIPES (AGL-2626).\n *\n * A recipe is a ready-to-edit action — a trigger, its conditions and an\n * ordered step list — built from the vocabulary the editor already offers\n * and handed to the editor as a draft. Nothing is written until the person\n * saves, and every field is theirs to change first: the recipe decides\n * where the editor starts, not what the site runs.\n *\n * Defined here, beside the step catalog, rather than in the builder, so the\n * docs page that lists the recipes and the menu that offers them read ONE\n * list, and so a step the catalog renames or retires breaks a recipe at\n * compile time rather than in a menu nobody tests.\n *\n * Definitions only. Three of the four are complete as written. `tagByForm`\n * needs a form, and a form belongs to a site (`hosts/{hostId}/forms`), so\n * the picker that supplies it is the builder's business and host-scoped by\n * nature. That is the seam an org-level mount keeps: the recipes are the\n * same at either level, and only the form picker knows where it is.\n */\nexport const CRM_ACTION_RECIPE_IDS = [\n 'welcomeNewLead',\n 'followUpWonDeal',\n 'reengageStaleLead',\n 'tagByForm',\n] as const\n\nexport type CrmActionRecipeId = (typeof CRM_ACTION_RECIPE_IDS)[number]\n\n/** What a recipe is handed before it builds — today only the form `tagByForm` reads. */\nexport interface CrmActionRecipeInput {\n form?: { id: string; name: string }\n}\n\nexport interface CrmActionRecipe {\n id: CrmActionRecipeId\n /** How the recipe reads in the menu; also the action's starting name. */\n title: string\n /** One sentence under the title. */\n description: string\n /**\n * What must be picked before the recipe can be built. A recipe that needs\n * nothing opens the editor at once; one that needs a form opens a picker\n * first. Built WITHOUT its pick, such a recipe yields an action the\n * validator refuses — a condition with no value — rather than one that\n * saves and then silently matches nothing.\n */\n needs?: 'form'\n /**\n * A fresh action each call, because the draft is edited in place. The\n * action carries `recipe: id` — the provenance is the builder's to stamp,\n * so a writer that saves what it was handed cannot forget it.\n */\n build: (input?: CrmActionRecipeInput) => HostAction\n}\n\n/**\n * How long the stale-lead recipe holds before it decides a lead has gone\n * quiet. A week: long enough that a rep who is working the lead has had a\n * chance to move its stage, short enough that a lead nobody touched is\n * still warm when the reminder lands.\n */\nexport const STALE_LEAD_WAIT_MINUTES = 7 * 24 * 60\n\n/**\n * The tag a form's captures get: the form's own name, cut to the tag cap.\n * The name is what the person calls the form, so it is the tag they would\n * have typed; the editor is open to change it before anything is saved.\n */\nexport function crmRecipeTagForForm(formName: string): string {\n return formName.trim().slice(0, CONTACT_TAG_MAX_LENGTH)\n}\n\nexport const CRM_ACTION_RECIPES: readonly CrmActionRecipe[] = [\n {\n id: 'welcomeNewLead',\n title: 'Welcome a new lead',\n description:\n 'When a form makes a new contact: rotate in an owner, book a call for ' +\n 'tomorrow, send a thank-you, and tag them website.',\n /*\n * The owner first, because the task that follows names no assignee and\n * so goes to whoever owns the contact when it is created — the member\n * the rotation just chose. Round robin rather than a named member: a\n * recipe cannot know who is on the team, and the pool under CRM →\n * Settings is the one place that does. On a workspace with no pool the\n * step fails and the run continues, so the call, the email and the tag\n * still land and the run history says who was not assigned.\n *\n * The email comes before any wait, which makes it an immediate reply to\n * what the visitor just did — transactional, sent from the org's own\n * identity, to the address the event carries.\n */\n build: () => ({\n recipe: 'welcomeNewLead',\n name: 'Welcome a new lead',\n trigger: {\n event: 'contactCreated',\n conditions: [{ field: 'source', op: 'equals', value: 'form' }],\n combinator: 'and',\n },\n steps: [\n { type: 'assignContactOwner', roundRobin: true },\n {\n type: 'createCrmTask',\n title: 'Call the new lead',\n kind: 'call',\n dueInDays: 1,\n },\n {\n type: 'sendEmail',\n subject: 'Thanks for getting in touch',\n body:\n 'Thanks for reaching out — we have your message, and someone ' +\n 'from our team will be in touch within a day.',\n },\n { type: 'addContactTag', tag: 'website' },\n ],\n enabled: true,\n }),\n },\n {\n id: 'followUpWonDeal',\n title: 'Follow up a won deal',\n description:\n 'When a deal is won — which makes the contact a Customer on its own — ' +\n 'book a check-in call a week out.',\n /*\n * No stage step (AGL-2641): the win itself floors the contact at\n * `customer` before `dealWon` is announced, so a step setting the stage\n * here would at best repeat the write and at worst move an evangelist\n * back to customer — a SET, which is what the step is, and not the\n * floor the win applies. The recipe books the follow-up and nothing\n * else.\n */\n build: () => ({\n recipe: 'followUpWonDeal',\n name: 'Follow up a won deal',\n trigger: { event: 'dealWon' },\n steps: [\n {\n type: 'createCrmTask',\n title: 'Check in with the new customer',\n kind: 'call',\n dueInDays: 7,\n },\n ],\n enabled: true,\n }),\n },\n {\n id: 'reengageStaleLead',\n title: 'Re-engage a stale lead',\n description:\n 'When a contact becomes a lead: wait a week, and if their stage has ' +\n 'not moved, book a call to bring them back.',\n /*\n * \"Unless the stage moved on\" is the wait's own event: the flow watches\n * for the next stage change on this person and gives up after a week.\n * Resumed by the clock, the run carries `_waitTimedOut`, and the task\n * step's guard reads it; resumed by the event, the field is absent and\n * the task is skipped — the lead was worked, and nobody is told to\n * re-engage somebody who just moved to Sales qualified.\n */\n build: () => ({\n recipe: 'reengageStaleLead',\n name: 'Re-engage a stale lead',\n trigger: {\n event: 'contactStageChanged',\n conditions: [{ field: 'lifecycleStage', op: 'equals', value: 'lead' }],\n combinator: 'and',\n },\n steps: [\n {\n type: 'waitForEvent',\n eventName: 'contactStageChanged',\n timeoutMinutes: STALE_LEAD_WAIT_MINUTES,\n },\n {\n type: 'createCrmTask',\n title: 'Re-engage a lead that has gone quiet',\n kind: 'call',\n dueInDays: 1,\n when: {\n conditions: [{ field: FLOW_TIMED_OUT_FIELD, op: 'notEmpty' }],\n },\n },\n ],\n enabled: true,\n }),\n },\n {\n id: 'tagByForm',\n title: 'Tag by form',\n description:\n 'When a form you pick makes a new contact: tag them with the form’s ' +\n 'name.',\n needs: 'form',\n build: (input) => {\n const form = input?.form\n return {\n recipe: 'tagByForm',\n name: form ? `Tag ${form.name.trim()} submissions` : 'Tag by form',\n trigger: {\n event: 'contactCreated',\n conditions: [{ field: 'formId', op: 'equals', value: form?.id ?? '' }],\n combinator: 'and',\n },\n steps: [\n { type: 'addContactTag', tag: form ? crmRecipeTagForForm(form.name) : '' },\n ],\n enabled: true,\n }\n },\n },\n]\n\n/** The recipe with this id, or null for a string that names none. */\nexport function crmActionRecipe(id: unknown): CrmActionRecipe | null {\n return CRM_ACTION_RECIPES.find((recipe) => recipe.id === id) ?? null\n}\n\n/**\n * The recipe a STORED action came from, read off its document (AGL-2639).\n *\n * Three answers, and the third is the one that matters. A known id: the\n * action was installed from, or begun as, that recipe. `null`: the action\n * was begun blank, or from a recipe this build no longer knows — the\n * editor wrote the field and said \"no recipe\". `undefined`: the document\n * carries no `recipe` field at all, which is every action saved before the\n * stamp existed. Such an action may well have started from a recipe — the\n * menu opened the editor prefilled long before anything recorded it — so a\n * reader that needs to know whether a site has a recipe treats `undefined`\n * as UNKNOWN, never as absent, and never writes a `null` over it.\n */\nexport function hostActionRecipeId(\n action: { recipe?: unknown } | null | undefined,\n): CrmActionRecipeId | null | undefined {\n const stamp = action?.recipe\n if (stamp === undefined) return undefined\n return crmActionRecipe(stamp)?.id ?? null\n}\n\n/**\n * A HostAction as the document at `hosts/{hostId}/actions/{id}` holds it.\n *\n * Every optional trigger key is written OUT — a boolean cap as `false`, a\n * cleared list as `null` — because the editor saves with a merge-set, and\n * a merge keeps whatever key the payload omits: a frequency cap switched\n * off, left out of the payload, would stay on. The legacy single\n * `condition` is always nulled; `conditions` has been canonical since the\n * list shape arrived and a document that carried both would have the\n * reader pick. Everything else is the action, unchanged.\n *\n * `recipe` rides along only when the action SAYS something about it: an\n * id or `null`. An action that carries no stamp (an older document, edited\n * and saved again) keeps carrying none — see {@link hostActionRecipeId}\n * for why an absent stamp must not become a `null` one.\n */\nexport interface HostActionDocument extends HostAction {\n trigger: HostActionTrigger & {\n oncePerVisitor: boolean\n oncePerSession: boolean\n cooldownMinutes: number | null\n everyTime: boolean\n condition: null\n conditions: HostActionTriggerCondition[] | null\n combinator: TriggerCombinator | null\n }\n enabled: boolean\n}\n\nexport function hostActionDocument(action: HostAction): HostActionDocument {\n const { recipe, ...rest } = action\n const trigger = action.trigger\n return {\n ...rest,\n trigger: {\n ...trigger,\n oncePerVisitor: trigger.oncePerVisitor === true,\n oncePerSession: trigger.oncePerSession === true,\n cooldownMinutes:\n Number(trigger.cooldownMinutes) >= 1 ? Number(trigger.cooldownMinutes) : null,\n everyTime: trigger.everyTime === true,\n condition: null,\n conditions: trigger.conditions ?? null,\n combinator: trigger.combinator ?? null,\n },\n enabled: action.enabled !== false,\n ...(recipe !== undefined ? { recipe } : {}),\n }\n}\n\n/**\n * The step list as the visitor's browser may see it.\n *\n * A client step AFTER a wait must never reach the page. The client engine\n * runs its slice of the list immediately, so shipping the whole list would\n * make \"wait three days, then show the popup\" show the popup at once — the\n * delay would appear to work on the server, be ignored in the browser, and\n * the two halves of one authored flow would disagree about when it happened.\n *\n * Truncating rather than filtering: everything past the first wait belongs to\n * a run that has not happened yet, whichever side would have executed it.\n */\nexport function hostActionStepsForClient(\n steps: readonly HostActionStep[] | undefined | null,\n): HostActionStep[] {\n const list = steps ?? []\n const suspendAt = list.findIndex(isFlowSuspendingStep)\n return [...(suspendAt < 0 ? list : list.slice(0, suspendAt))]\n}\n\n/**\n * Basic presentational interactions (AGL-577): the subset of client steps\n * that are pure DOM choreography — menu/drawer open-close, element\n * show/hide, class toggles, sticky nav, navigation, and client-only\n * alerts. They carry NO server cost and touch NO data, so they run on\n * every plan (the `interactions` feature, on by default everywhere).\n *\n * The powerful client steps stay behind the `actions` entitlement:\n * `showOverlay` (marketing overlays), `showHtml` (arbitrary HTML\n * injection), and `trackGaEvent` (analytics). `runJs` keeps its own\n * higher `webhooks` (Business) gate. Server steps\n * (sendEmail/notifyAdmins/enrollList/updateDataset/assignCampaign) are\n * re-checked against `actions` server-side in `runEventActions`.\n */\nexport const BASIC_CLIENT_ACTION_STEP_TYPES: ReadonlySet<HostActionStepType> =\n new Set([\n 'stickyNav',\n 'addClass',\n 'removeClass',\n 'toggleClass',\n 'showElement',\n 'hideElement',\n 'toggleElement',\n 'openDrawer',\n 'closeDrawer',\n 'toggleDrawer',\n 'openMenu',\n 'closeMenu',\n 'toggleMenu',\n 'setAttribute',\n 'removeAttribute',\n // Moving the page and pressing a player the author placed: no data, no\n // server, and nothing a Video element's own poster does not already do\n // on every plan.\n 'scrollTo',\n 'playVideo',\n 'redirect',\n 'siteAlert',\n ] as const)\n\n/** A client step available on all plans (no `actions` entitlement). */\nexport function isBasicClientActionStep(step: HostActionStep): boolean {\n return BASIC_CLIENT_ACTION_STEP_TYPES.has(step.type)\n}\n\n/**\n * Whether a client step may run for the given entitlement tier (AGL-577).\n * This is the single source of truth the page enricher uses to trim the\n * client-automation payload per plan:\n *\n * - Basic presentational steps → always (every plan).\n * - `runJs` → `allowJs` (Business `webhooks` tier).\n * - Remaining advanced client steps (overlay / showHtml / analytics) →\n * `actionsEntitled` (`actions` tier).\n *\n * Server steps are not client steps and always return false here; they\n * are dispatched and re-authorized server-side.\n */\nexport function isClientStepEntitled(\n step: HostActionStep,\n entitlements: { actionsEntitled: boolean; allowJs: boolean },\n): boolean {\n if (!isClientActionStep(step)) return false\n if (isBasicClientActionStep(step)) return true\n if (step.type === 'runJs') return entitlements.allowJs\n return entitlements.actionsEntitled\n}\n\nexport type HostActionStepType = HostActionStep['type']\n\n/** `hosts/{hostId}/actions/{id}` doc. */\nexport interface HostAction {\n name: string\n trigger: HostActionTrigger\n steps: HostActionStep[]\n /** Disabled actions never run; new actions default enabled. */\n enabled?: boolean\n /**\n * The recipe this action was installed from or begun as (AGL-2639), or\n * `null` for one begun blank. Absent on a document from before the stamp\n * existed — read it through {@link hostActionRecipeId}, which keeps the\n * three cases apart.\n */\n recipe?: CrmActionRecipeId | null\n}\n\nexport const ACTION_MAX_STEPS = 10\n/** Custom-event chaining depth cap (mirrors CROSS_MAX_DEPTH). */\nexport const ACTION_MAX_EVENT_DEPTH = 3\n\n/**\n * Cap on the parameters one `trackGaEvent` step may carry (AGL-1587).\n *\n * Not a storage concern. These pairs are the least trustworthy analytics\n * input on the platform — both halves are typed by a site author and land in\n * that site's own GA4 property — and a bounded list is one an author can read\n * back in full before publishing, which is the only review a parameter ever\n * gets. GA4 caps custom parameters per event as well and drops the overflow\n * without saying so, so an unbounded editor would let an author configure\n * parameters that are never delivered and never explained. Ten, the same\n * ceiling {@link ACTION_MAX_STEPS} puts on the step list itself.\n */\nexport const ACTION_MAX_EVENT_PARAMS = 10\n\n/**\n * Longest analytics parameter NAME GA4 accepts; it drops a longer one on\n * arrival, so a name over this is silence rather than a truncated dimension.\n * The value half is capped by `ANALYTICS_PARAM_MAX_LENGTH`, which truncates\n * instead of dropping.\n */\nexport const ACTION_EVENT_PARAM_NAME_MAX_LENGTH = 40\n\n/** Self-dismiss triggers for shown elements (AGL-589). */\nexport const ELEMENT_DISMISS_OPTIONS = ['escape', 'outsideClick'] as const\nexport type ElementDismissOption = (typeof ELEMENT_DISMISS_OPTIONS)[number]\n/** Visibility grace-delay ceiling — enough for hover travel, no dead UIs. */\nexport const ELEMENT_VISIBILITY_MAX_DELAY_MS = 5000\n\n/**\n * How a `scrollTo` step moves the page. Stored in documents; never rename a\n * value. `instant` rather than the DOM's `auto`, because `auto` defers to the\n * site's CSS `scroll-behavior` — a site that sets `smooth` there would animate\n * a scroll the author asked to be instant.\n */\nexport const SCROLL_TO_BEHAVIORS = ['smooth', 'instant'] as const\nexport type ScrollToBehavior = (typeof SCROLL_TO_BEHAVIORS)[number]\n\n/**\n * The largest offset a `scrollTo` step may leave above its target.\n *\n * The offset exists to clear a sticky header, which is tens of pixels and\n * rarely more than a few hundred with an announcement bar stacked on it. A\n * thousand leaves room for any header while still refusing the extra digit\n * that would park the target off the bottom of the screen.\n */\nexport const SCROLL_TO_MAX_OFFSET_PX = 1000\n\n/** Custom event names: short, no collision with built-ins. */\nexport const CUSTOM_EVENT_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]{1,39}$/\n\n/**\n * The shortest and longest a flow may wait.\n *\n * The floor is a minute because the resume beat runs on a minute, so anything\n * under one is a delay the scheduler cannot honor and would only read as\n * imprecision. The ceiling is ninety days: long enough for the win-back that\n * is the longest sequence anybody writes, and short enough that an enrollment\n * is not an unbounded lease on a document. A person waiting inside a flow is\n * storage the merchant is not looking at, and a wait measured in years is\n * indistinguishable from one nobody will ever collect.\n */\nexport const FLOW_WAIT_MIN_MINUTES = 1\nexport const FLOW_WAIT_MAX_MINUTES = 90 * 24 * 60\n\n/**\n * The attribute names an interaction may write (AGL-2546).\n *\n * An unrestricted attribute setter is a self-XSS vector authored through the\n * interactions UI: it reaches `href`, `src`, `onclick`, `formaction` and\n * `style`, and on a site with collaborators the author and the victim need\n * not be the same person. `aria-*` and `data-*` carry the entire\n * accessibility use case and none of that exposure.\n *\n * Deliberately NOT solved by an entitlement gate. `runJs` is Business-gated\n * because it is dangerous at any tier; this has to work on every plan,\n * because a screen-reader user's access to a menu is not a paid feature.\n */\nexport const INTERACTION_ATTRIBUTE_NAME_PATTERN = /^(?:aria|data)-[a-z][a-z0-9-]*$/\n\n/**\n * Whether an interaction is allowed to write this attribute name.\n *\n * Case-insensitive because HTML attribute names are, and a rejected name is\n * a no-op rather than an error: a step that cannot run must not break the\n * steps after it in the same automation.\n */\nexport function isInteractionAttributeAllowed(name: unknown): boolean {\n return (\n typeof name === 'string' &&\n INTERACTION_ATTRIBUTE_NAME_PATTERN.test(name.trim().toLowerCase())\n )\n}\n\nexport const HOST_ACTION_STEP_LABELS: Record<HostActionStepType, string> = {\n runWorkflow: 'Run a workflow',\n siteAlert: 'Show a site alert',\n customEvent: 'Fire a custom event',\n datasetAppend: 'Write to a dataset',\n webhookPost: 'Send a webhook (Business)',\n showOverlay: 'Show a popup or bar',\n stickyNav: 'Make navigation sticky',\n addClass: 'Add a CSS class',\n toggleClass: 'Toggle a CSS class',\n removeClass: 'Remove a CSS class',\n showElement: 'Show an element',\n hideElement: 'Hide an element',\n toggleElement: 'Show/hide an element',\n openDrawer: 'Open a drawer',\n closeDrawer: 'Close a drawer',\n toggleDrawer: 'Open/close a drawer',\n openMenu: 'Open a menu',\n closeMenu: 'Close a menu',\n toggleMenu: 'Open/close a menu',\n setAttribute: 'Set an ARIA or data attribute',\n removeAttribute: 'Remove an ARIA or data attribute',\n scrollTo: 'Scroll to element',\n playVideo: 'Play a video',\n showHtml: 'Show custom HTML',\n runJs: 'Run custom JS (Business)',\n redirect: 'Redirect the visitor',\n trackGaEvent: 'Track an analytics event',\n sendEmail: 'Send an email',\n notifyAdmins: 'Notify site admins',\n enrollList: 'Enroll in a list',\n updateDataset: 'Update a dataset record',\n assignCampaign: 'Assign to a campaign',\n wait: 'Wait',\n waitForEvent: 'Wait for something to happen',\n exitFlow: 'End the flow here',\n setContactStage: 'Set the contact’s lifecycle stage',\n addContactTag: 'Tag the contact',\n assignContactOwner: 'Assign the contact an owner',\n createCrmTask: 'Create a CRM task',\n logCrmActivity: 'Log a CRM activity',\n}\n\n/**\n * Past-tense phrases for a run summary (AGL-2171).\n *\n * `HOST_ACTION_STEP_LABELS` above names what a step WILL do, in the\n * imperative, for a `Do` select — \"Send an email\". A run history is the\n * opposite tense and a different audience: it says what already happened,\n * on one line, several steps at a time. `/product/workflows` advertises\n * exactly that shape — `Sent email · saved to Leads · webhook 200`.\n *\n * Two maps rather than one derived from the other, because \"Send a webhook\n * (Business)\" cannot be mechanically turned into \"webhook\" — the plan\n * suffix is part of the picker's label and has no business in a log line.\n * Only the steps a SERVER run can perform appear here; the client-side\n * steps run in the visitor's browser and never reach the activity write.\n */\nexport const HOST_ACTION_STEP_OUTCOMES: Partial<\n Record<HostActionStepType, string>\n> = {\n runWorkflow: 'ran workflow',\n siteAlert: 'showed an alert',\n customEvent: 'fired an event',\n datasetAppend: 'saved to dataset',\n updateDataset: 'updated dataset',\n webhookPost: 'webhook',\n sendEmail: 'sent email',\n notifyAdmins: 'notified admins',\n enrollList: 'enrolled in list',\n assignCampaign: 'assigned to campaign',\n wait: 'waiting',\n waitForEvent: 'waiting for',\n exitFlow: 'ended the flow',\n setContactStage: 'set stage',\n addContactTag: 'tagged',\n assignContactOwner: 'assigned owner',\n createCrmTask: 'created task',\n logCrmActivity: 'logged activity',\n}\n\n/**\n * One step's line in a run summary, with the detail that makes it useful.\n *\n * `saved to Leads` beats `saved to dataset`, and `webhook 200` beats\n * `webhook` — the status code is the entire reason anyone opens a run\n * history after a webhook, and it was being discarded on the line it\n * arrived.\n */\nexport function describeStepOutcome(\n type: HostActionStepType,\n detail?: string,\n): string {\n const base = HOST_ACTION_STEP_OUTCOMES[type] ?? String(type)\n const trimmed = detail?.trim()\n if (!trimmed) return base\n if (type === 'datasetAppend' || type === 'updateDataset') {\n // `saved to dataset` + `Leads` reads as `saved to Leads`, not\n // `saved to dataset Leads`.\n return `${base.replace(/ dataset$/, '')} ${trimmed}`\n }\n return `${base} ${trimmed}`\n}\n\n/** A whole number of minutes inside the wait band. */\nexport function isFlowWaitMinutes(value: unknown): boolean {\n return (\n Number.isInteger(value) &&\n (value as number) >= FLOW_WAIT_MIN_MINUTES &&\n (value as number) <= FLOW_WAIT_MAX_MINUTES\n )\n}\n\n/** True for a custom (non-built-in) event name an action may fire. */\nexport function isCustomEventName(event: string): boolean {\n return (\n !HOST_EVENT_TYPES.includes(event as any) &&\n CUSTOM_EVENT_PATTERN.test(event)\n )\n}\n\n/**\n * Validates an action doc shape; returns a human-readable error or null.\n * Server and console share this so bad steps never persist or run.\n */\nexport function validateHostAction(action: HostAction): string | null {\n if (!action.name?.trim()) return 'Name the action'\n // A stamp is provenance, and provenance naming a recipe that does not\n // exist is a document nothing can read back; `null` and absent both pass.\n if (action.recipe != null && !crmActionRecipe(action.recipe)) {\n return 'Unknown recipe'\n }\n const event = action.trigger?.event?.trim() ?? ''\n if (!event) return 'Pick a trigger event'\n if (\n !HOST_EVENT_TYPES.includes(event as any) &&\n !isSiteEventType(event) &&\n !isCustomEventName(event)\n ) {\n return 'Custom event names are 2–40 letters, digits, dashes'\n }\n // Site-event trigger config (AGL-256; hover events AGL-562).\n if (\n (ELEMENT_SCOPED_SITE_EVENTS as readonly string[]).includes(event) &&\n !action.trigger?.selector?.trim()\n ) {\n return 'This trigger needs a CSS selector'\n }\n if (\n ['scrollDepth', 'timeOnPage'].includes(event) &&\n !(Number(action.trigger?.threshold) > 0)\n ) {\n return event === 'scrollDepth'\n ? 'Set the scroll percentage (1–100)'\n : 'Set the seconds on page'\n }\n // Frequency caps (AGL-274).\n if (\n action.trigger?.cooldownMinutes != null &&\n !(Number(action.trigger.cooldownMinutes) >= 1)\n ) {\n return 'Cooldown must be at least 1 minute'\n }\n // Structured payload conditions (AGL-557; chained AGL-565).\n const combinator = action.trigger?.combinator\n if (combinator != null && !TRIGGER_COMBINATORS.includes(combinator)) {\n return 'Combine conditions with AND or OR'\n }\n const conditions = normalizeTriggerConditions(action.trigger)\n if (conditions.length > ACTION_MAX_CONDITIONS) {\n return `Conditions are capped at ${ACTION_MAX_CONDITIONS}`\n }\n for (const [index, condition] of conditions.entries()) {\n // Single-condition messages stay exactly as AGL-557 shipped them;\n // the row marker only appears once there are rows to tell apart.\n const where = conditions.length > 1 ? ` (condition ${index + 1})` : ''\n if (!TRIGGER_CONDITION_OPS.includes(condition.op)) {\n return `Pick a condition operator${where}`\n }\n if (!condition.field?.trim()) {\n return `Name the field the condition checks${where}`\n }\n if (condition.op !== 'notEmpty' && !condition.value?.trim()) {\n return `Enter the value the condition compares against${where}`\n }\n }\n const steps = action.steps ?? []\n if (!steps.length) return 'Add at least one step'\n if (steps.length > ACTION_MAX_STEPS) {\n return `Actions are capped at ${ACTION_MAX_STEPS} steps`\n }\n for (const [index, step] of steps.entries()) {\n const label = `Step ${index + 1}`\n // A step's own guard, checked with the same messages the trigger's\n // clauses get — one condition editor, one set of complaints about it.\n const guardClauses = (step.when?.conditions ?? []).filter(Boolean)\n if (guardClauses.length > ACTION_MAX_CONDITIONS) {\n return `${label}: conditions are capped at ${ACTION_MAX_CONDITIONS}`\n }\n if (\n step.when?.combinator != null &&\n !TRIGGER_COMBINATORS.includes(step.when.combinator)\n ) {\n return `${label}: combine conditions with AND or OR`\n }\n for (const clause of guardClauses) {\n if (!TRIGGER_CONDITION_OPS.includes(clause.op)) {\n return `${label}: pick a condition operator`\n }\n if (!clause.field?.trim()) {\n return `${label}: name the field the condition checks`\n }\n if (clause.op !== 'notEmpty' && !clause.value?.trim()) {\n return `${label}: enter the value the condition compares against`\n }\n }\n if (step.type === 'wait' && !isFlowWaitMinutes(step.delayMinutes)) {\n return `${label}: wait between ${FLOW_WAIT_MIN_MINUTES} minute and ${FLOW_WAIT_MAX_MINUTES} minutes`\n }\n if (step.type === 'waitForEvent') {\n const waited = step.eventName?.trim() ?? ''\n if (\n !waited ||\n (!HOST_EVENT_TYPES.includes(waited as any) && !isCustomEventName(waited))\n ) {\n return `${label}: pick the event to wait for`\n }\n if (!isFlowWaitMinutes(step.timeoutMinutes)) {\n return `${label}: give up after ${FLOW_WAIT_MIN_MINUTES}–${FLOW_WAIT_MAX_MINUTES} minutes`\n }\n }\n if (\n step.type === 'runWorkflow' &&\n !step.workflowId?.trim() &&\n !step.workflowName?.trim()\n ) {\n return `${label}: pick a workflow`\n }\n if (step.type === 'siteAlert' && !step.message?.trim()) {\n return `${label}: enter the alert message`\n }\n if (step.type === 'customEvent') {\n if (!isCustomEventName(step.eventName?.trim() ?? '')) {\n return `${label}: custom event names are 2–40 letters, digits, dashes`\n }\n }\n if (\n step.type === 'datasetAppend' &&\n !step.datasetId?.trim() &&\n !step.datasetName?.trim()\n ) {\n return `${label}: pick a dataset`\n }\n if (\n step.type === 'webhookPost' &&\n !step.webhookId?.trim() &&\n !step.webhookName?.trim()\n ) {\n return `${label}: pick a webhook`\n }\n // Client + server steps (AGL-257).\n if (\n step.type === 'addClass' ||\n step.type === 'removeClass' ||\n step.type === 'toggleClass'\n ) {\n if (!step.selector?.trim()) return `${label}: enter a CSS selector`\n if (!step.className?.trim()) return `${label}: enter the class name`\n }\n // Element show/hide steps (AGL-562) always target a selector; drawer\n // (AGL-562) and menu (AGL-568) commands may omit the target (the\n // page's first drawer/menu answers).\n if (\n (step.type === 'showElement' ||\n step.type === 'hideElement' ||\n step.type === 'toggleElement') &&\n !step.selector?.trim()\n ) {\n return `${label}: pick the element to show or hide`\n }\n if (\n step.type === 'showElement' ||\n step.type === 'hideElement' ||\n step.type === 'toggleElement'\n ) {\n // Choreography options (AGL-589).\n const delay = (step as { delayMs?: unknown }).delayMs\n if (\n delay != null &&\n !(\n Number.isInteger(delay) &&\n (delay as number) >= 0 &&\n (delay as number) <= ELEMENT_VISIBILITY_MAX_DELAY_MS\n )\n ) {\n return `${label}: delay must be 0–${ELEMENT_VISIBILITY_MAX_DELAY_MS}ms`\n }\n const dismiss = (step as { dismissOn?: unknown }).dismissOn\n if (\n dismiss != null &&\n !(\n Array.isArray(dismiss) &&\n dismiss.every((option) =>\n (ELEMENT_DISMISS_OPTIONS as readonly string[]).includes(\n option as string,\n ),\n )\n )\n ) {\n return `${label}: dismiss options are escape and outsideClick`\n }\n }\n // One-target steps (AGL-2867). The runtime does nothing for a target it\n // cannot find, so a missing one is refused here, where it can be fixed.\n if (step.type === 'scrollTo') {\n if (!step.selector?.trim()) {\n return `${label}: pick the element to scroll to`\n }\n const behavior = (step as { behavior?: unknown }).behavior\n if (\n behavior != null &&\n !(SCROLL_TO_BEHAVIORS as readonly unknown[]).includes(behavior)\n ) {\n return `${label}: scroll smoothly or instantly`\n }\n const offset = (step as { offsetPx?: unknown }).offsetPx\n if (\n offset != null &&\n !(\n Number.isInteger(offset) &&\n (offset as number) >= 0 &&\n (offset as number) <= SCROLL_TO_MAX_OFFSET_PX\n )\n ) {\n return `${label}: the offset must be 0–${SCROLL_TO_MAX_OFFSET_PX}px`\n }\n }\n if (step.type === 'playVideo' && !step.selector?.trim()) {\n return `${label}: pick the video to play`\n }\n if (step.type === 'showHtml') {\n if (!step.html?.trim()) return `${label}: enter the HTML`\n // AGL-2486: the author-facing half of the runtime sanitizer, exactly as\n // the reserved analytics name below is the author-facing half of the\n // runtime refusal. `site-runtime.tsx` runs this HTML through\n // `sanitizeAuthorHtml` for a VISITOR, where the only thing it can do\n // with markup it refuses is drop it and say nothing — so an author who\n // pastes an embed snippet gets a step that runs, reports success, and\n // shows nothing. The refusal is reported HERE, where the person who\n // can fix it is looking, and it is the SAME function that will run at\n // render rather than a second description of its rules.\n const removals: AuthorHtmlRemoval[] = []\n const safe = sanitizeAuthorHtml(step.html, removals)\n if (!safe.trim()) {\n return `${label}: none of this HTML can be shown on a published page — ${removals[0]?.message ?? 'it is removed by the sanitizer.'}`\n }\n if (removals.length) {\n return `${label}: ${removals[0].message}${removals.length > 1 ? ` (+${removals.length - 1} more)` : ''}`\n }\n }\n if (step.type === 'runJs' && !step.code?.trim()) {\n return `${label}: enter the JavaScript`\n }\n if (step.type === 'redirect' && !step.url?.trim() && !step.screenId) {\n return `${label}: pick a screen or enter the destination URL`\n }\n if (step.type === 'trackGaEvent') {\n if (!step.eventName?.trim()) {\n return `${label}: name the analytics event`\n }\n // AGL-1587: the author-facing half of the runtime refusal. The runtime\n // can only drop a bad name silently — it is executing for a VISITOR —\n // so the name is checked HERE, where the person who can fix it is.\n const resolved = resolveAuthoredEventName(step.eventName)\n if (resolved.reason === 'reserved') {\n return `${label}: \"${step.eventName.trim()}\" is a reserved analytics event name — pick another`\n }\n if (!resolved.name) {\n return `${label}: the analytics event name must start with a letter`\n }\n const params = Object.entries(step.params ?? {})\n if (params.length > ACTION_MAX_EVENT_PARAMS) {\n return `${label}: analytics parameters are capped at ${ACTION_MAX_EVENT_PARAMS}`\n }\n for (const [key, value] of params) {\n if (!key.trim()) return `${label}: name every analytics parameter`\n if (key.trim().length > ACTION_EVENT_PARAM_NAME_MAX_LENGTH) {\n return `${label}: the \"${key.trim().slice(0, 16)}…\" parameter name is over ${ACTION_EVENT_PARAM_NAME_MAX_LENGTH} characters, which GA4 drops`\n }\n if (!String(value ?? '').trim()) {\n return `${label}: enter a value for the \"${key.trim()}\" parameter`\n }\n }\n // The author-facing half of the runtime sanitizer, the same shape the\n // reserved name above and the `showHtml` check take. `trackAuthoredEvent`\n // strips a parameter that can carry personal data and says nothing about\n // it, because it is executing for a VISITOR — so an author who binds a\n // form field into a parameter gets a step that runs, reports success,\n // and delivers an event missing the dimension it was created for. The\n // strip is reported HERE, by running the REAL `sanitizeEventParams`\n // rather than a second description of its rules, so the two can never\n // disagree about which parameter survives.\n const kept = sanitizeEventParams(step.params ?? undefined)\n const stripped = params.find(([key]) => !(key in kept))\n if (stripped) {\n return `${label}: the \"${stripped[0]}\" parameter is never sent — an analytics parameter must not carry a visitor's own details`\n }\n }\n if (step.type === 'sendEmail') {\n if (!step.subject?.trim()) return `${label}: enter the subject`\n if (!step.body?.trim()) return `${label}: enter the email body`\n }\n if (step.type === 'notifyAdmins' && !step.title?.trim()) {\n return `${label}: enter the notification title`\n }\n if (\n step.type === 'showOverlay' &&\n !step.overlayId?.trim() &&\n !step.overlayName?.trim()\n ) {\n return `${label}: pick an overlay`\n }\n if (\n step.type === 'enrollList' &&\n !step.listId?.trim() &&\n !step.listName?.trim()\n ) {\n return `${label}: pick a list`\n }\n if (\n step.type === 'updateDataset' &&\n !step.datasetId?.trim() &&\n !step.datasetName?.trim()\n ) {\n return `${label}: pick a dataset`\n }\n if (\n step.type === 'assignCampaign' &&\n !step.campaignId?.trim() &&\n !step.campaignName?.trim()\n ) {\n return `${label}: pick a campaign`\n }\n // CRM steps (AGL-2605). A stage or a kind outside its vocabulary is\n // refused here rather than stored: the executor treats the value as\n // trusted and would write it into a facet every stage report counts.\n if (\n step.type === 'setContactStage' &&\n !isContactLifecycleStage(step.lifecycleStage)\n ) {\n return `${label}: pick a lifecycle stage`\n }\n if (step.type === 'addContactTag') {\n const tag = step.tag?.trim() ?? ''\n if (!tag) return `${label}: enter the tag`\n if (tag.length > CONTACT_TAG_MAX_LENGTH) {\n return `${label}: tags are at most ${CONTACT_TAG_MAX_LENGTH} characters`\n }\n }\n if (step.type === 'assignContactOwner') {\n const named = Boolean(step.ownerUid?.trim() || step.ownerEmail?.trim())\n if (step.roundRobin === true && named) {\n return `${label}: pick round robin or a member, not both`\n }\n if (\n step.roundRobin !== true &&\n !step.ownerUid?.trim() &&\n !step.ownerEmail?.trim().includes('@')\n ) {\n return `${label}: enter the owner’s email address`\n }\n }\n if (step.type === 'createCrmTask') {\n if (!step.title?.trim()) return `${label}: give the task a title`\n if (!isCrmTaskKind(step.kind)) return `${label}: pick the kind of task`\n if (\n !Number.isInteger(step.dueInDays) ||\n step.dueInDays < 0 ||\n step.dueInDays > CRM_TASK_MAX_DUE_DAYS\n ) {\n return `${label}: due in 0–${CRM_TASK_MAX_DUE_DAYS} days`\n }\n // Optional — but an address that is not one would resolve to nobody at\n // run time, and the task would land unassigned with no word why.\n const assigneeEmail = step.assigneeEmail?.trim() ?? ''\n if (assigneeEmail && !assigneeEmail.includes('@')) {\n return `${label}: enter the assignee’s email address`\n }\n }\n if (step.type === 'logCrmActivity') {\n if (!isCrmActivityKind(step.kind)) {\n return `${label}: pick the kind of activity`\n }\n if (!step.body?.trim()) return `${label}: write what happened`\n }\n }\n return null\n}\n\n/** Alert produced by a `siteAlert` step, surfaced to the emitting client. */\nexport interface HostActionAlert {\n message: string\n severity: 'info' | 'success' | 'warning' | 'error'\n}\n\n/**\n * Webhooks (AGL-149) at `hosts/{hostId}/webhooks/{id}`. Outbound entries\n * are targets a `webhookPost` action step delivers to (HMAC-signed);\n * inbound entries mint `/api/hooks/{hostId}/{hookId}` endpoints that run\n * a workflow with the posted JSON in scope. Business tier (`webhooks`\n * flag); Pro can be enabled per-tenant via entitlement overrides.\n */\nexport interface HostWebhook {\n name: string\n direction: 'outbound' | 'inbound'\n /** Outbound delivery URL (https only; checked again at send time). */\n url?: string\n /** Shared secret: signs outbound bodies, verifies inbound callers. */\n secret?: string\n /** Inbound: workflow (by name) enrolled with the payload in scope. */\n workflowName?: string\n enabled?: boolean\n}\n\nexport const WEBHOOK_MAX_PER_HOST = 5\n\n/**\n * How many LIVE action documents one host may hold (AGL-2266).\n *\n * `hosts/{hostId}/actions` was in NO exclusion list in\n * `cloud/firebase-firestore.rules`, so the host catch-all's `allow create`\n * granted it to any editor, client-direct, on any plan — and the import\n * route's own table said so in as many words: *\"no `RESOURCES` entry and no\n * quota key anywhere; all three creators write the document client-direct.\"*\n * A free org could therefore mint unbounded Firestore documents from the\n * browser against a $0 subscription. Unbounded infrastructure, not a bypass of\n * anything we sell, which is `WEBHOOK_MAX_PER_HOST`'s (AGL-1360) and\n * `NON_PAGE_SCREEN_MAX_PER_HOST`'s (AGL-1399) shape exactly: a flat PLATFORM\n * cap with no `OrgEntitlements` key, no variation by plan, and nothing on the\n * price list to explain.\n *\n * ## Not gated on the `actions` entitlement, deliberately\n *\n * `actions` is a Pro feature and `interactions` is free, and BOTH write this\n * collection — the interaction builder and the besigner's preset wiring create\n * the same document type the Pro actions card does. Gating creation on the\n * paid flag would take element interactions away from every free and starter\n * site, which is a pricing change wearing a cap's clothes. The runtime already\n * checks the entitlement where it decides what may RUN\n * (`run-event-actions.ts`), which is the right place for it.\n *\n * ## Why 500\n *\n * Sized to the busiest plausible site rather than to today's data. An action\n * is authored per interactive element, so a heavy marketing site with dropdown\n * choreography on every section reaches tens; presets wire a handful at a\n * time. 500 is far past anything authoring produces and well short of what a\n * script in a loop wants, which is the only property a flat cap needs. Live\n * documents only — the interactions provider soft-deletes with `deletedAt`, so\n * counting tombstones would be AGL-1173's bug one collection over, where\n * removing an interaction never frees its slot.\n */\nexport const ACTIONS_MAX_PER_HOST = 500\n/** Outbound URLs must be public https — first-line SSRF guard. */\nexport const WEBHOOK_URL_PATTERN =\n /^https:\\/\\/(?!localhost)(?!127\\.)(?!0\\.)(?!10\\.)(?!172\\.(1[6-9]|2\\d|3[01])\\.)(?!192\\.168\\.)(?!169\\.254\\.)[^\\s]+$/i\n"],"names":["resolveAuthoredEventName","sanitizeEventParams","sanitizeAuthorHtml","CRM_TASK_MAX_DUE_DAYS","isContactLifecycleStage","isCrmActivityKind","isCrmTaskKind","HOST_EVENT_TYPES","SITE_EVENT_TYPES","ELEMENT_SCOPED_SITE_EVENTS","isSiteEventType","event","includes","TRIGGER_CONDITION_OPS","evaluateTriggerCondition","condition","payload","field","trim","raw","actual","String","toLowerCase","op","length","expected","value","TRIGGER_COMBINATORS","ACTION_MAX_CONDITIONS","normalizeTriggerConditions","trigger","conditions","Array","isArray","filter","Boolean","evaluateTriggerConditions","combinator","some","every","FLOW_TIMED_OUT_FIELD","evaluateStepGuard","guard","scope","CLIENT_ACTION_STEP_TYPES","Set","isClientActionStep","step","has","type","FLOW_SUSPENDING_STEP_TYPES","isFlowSuspendingStep","CRM_ACTION_STEP_TYPES","isCrmActionStep","CONTACT_TAG_MAX_LENGTH","CRM_ACTION_RECIPE_IDS","STALE_LEAD_WAIT_MINUTES","crmRecipeTagForForm","formName","slice","CRM_ACTION_RECIPES","id","title","description","build","recipe","name","steps","roundRobin","kind","dueInDays","subject","body","tag","enabled","eventName","timeoutMinutes","when","needs","input","form","crmActionRecipe","find","hostActionRecipeId","action","stamp","undefined","hostActionDocument","rest","oncePerVisitor","oncePerSession","cooldownMinutes","Number","everyTime","hostActionStepsForClient","list","suspendAt","findIndex","BASIC_CLIENT_ACTION_STEP_TYPES","isBasicClientActionStep","isClientStepEntitled","entitlements","allowJs","actionsEntitled","ACTION_MAX_STEPS","ACTION_MAX_EVENT_DEPTH","ACTION_MAX_EVENT_PARAMS","ACTION_EVENT_PARAM_NAME_MAX_LENGTH","ELEMENT_DISMISS_OPTIONS","ELEMENT_VISIBILITY_MAX_DELAY_MS","SCROLL_TO_BEHAVIORS","SCROLL_TO_MAX_OFFSET_PX","CUSTOM_EVENT_PATTERN","FLOW_WAIT_MIN_MINUTES","FLOW_WAIT_MAX_MINUTES","INTERACTION_ATTRIBUTE_NAME_PATTERN","isInteractionAttributeAllowed","test","HOST_ACTION_STEP_LABELS","runWorkflow","siteAlert","customEvent","datasetAppend","webhookPost","showOverlay","stickyNav","addClass","toggleClass","removeClass","showElement","hideElement","toggleElement","openDrawer","closeDrawer","toggleDrawer","openMenu","closeMenu","toggleMenu","setAttribute","removeAttribute","scrollTo","playVideo","showHtml","runJs","redirect","trackGaEvent","sendEmail","notifyAdmins","enrollList","updateDataset","assignCampaign","wait","waitForEvent","exitFlow","setContactStage","addContactTag","assignContactOwner","createCrmTask","logCrmActivity","HOST_ACTION_STEP_OUTCOMES","describeStepOutcome","detail","base","trimmed","replace","isFlowWaitMinutes","isInteger","isCustomEventName","validateHostAction","selector","threshold","index","entries","where","label","guardClauses","clause","delayMinutes","waited","workflowId","workflowName","message","datasetId","datasetName","webhookId","webhookName","className","delay","delayMs","dismiss","dismissOn","option","behavior","offset","offsetPx","html","removals","safe","code","url","screenId","resolved","reason","params","Object","key","kept","stripped","overlayId","overlayName","listId","listName","campaignId","campaignName","lifecycleStage","named","ownerUid","ownerEmail","assigneeEmail","WEBHOOK_MAX_PER_HOST","ACTIONS_MAX_PER_HOST","WEBHOOK_URL_PATTERN"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/actions.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n type ContactLifecycleStage,\n CRM_TASK_MAX_DUE_DAYS,\n type CrmActivityKind,\n type CrmTaskKind,\n isContactLifecycleStage,\n isCrmActivityKind,\n isCrmTaskKind,\n} from './crm-kinds'\nimport {\n type ClientInteractionStep,\n CUSTOM_EVENT_PATTERN,\n type InteractionCondition,\n type InteractionStepGuard,\n type InteractionTrigger,\n type SiteAlert,\n type SiteInteraction,\n type TriggerCombinator,\n validateInteraction,\n} from './site-interactions'\nimport { HOST_EVENT_TYPES } from './host-events'\n\n/**\n * Actions builder (AGL-148): HubSpot-style event → action automation on\n * top of the AGL-128 event triggers. An action listens for a host event\n * (built-in or a custom name fired by another action), optionally filters\n * on the payload, and runs an ordered step list. Pure types + validation\n * here; the executor lives in the workflows plugin, where the I/O is.\n *\n * An action is a SITE INTERACTION (`site-interactions.ts`) with server steps\n * added: its trigger, its conditions, its client steps and its storage are\n * the platform's, and are re-exported here for the readers that take both\n * from this module. What is declared below is the automation's own — the\n * server steps, flows, recipes and webhooks.\n */\nexport * from './site-interactions'\n\n/** The automation's names for the platform's interaction shapes. */\nexport type HostActionTriggerCondition = InteractionCondition\nexport type HostActionTrigger = InteractionTrigger\nexport type HostActionStepGuard = InteractionStepGuard\n/** Alert produced by a `siteAlert` step, surfaced to the emitting client. */\nexport type HostActionAlert = SiteAlert\n\n/**\n * The scope key a resumed flow carries to say the wait ended on the CLOCK\n * rather than on the event it was watching for.\n *\n * This is how a `waitForEvent` gets its timeout branch without a nested step\n * list: the flow resumes either way, and the step after it carries a `when`\n * naming this field. Underscored because it shares a namespace with the\n * event payload's own fields, which are merchant-authored form field names.\n */\nexport const FLOW_TIMED_OUT_FIELD = '_waitTimedOut'\n\n/** The steps an automation adds to the platform's client steps: the server's, run by the workflows plugin. */\ntype ServerActionStep = (\n // Entity references carry a doc id (AGL-261, rename-safe) with the name\n // kept as a display hint; pre-AGL-261 docs have only the name and the\n // executor resolves either.\n | { type: 'runWorkflow'; workflowId?: string; workflowName?: string }\n | { type: 'customEvent'; eventName: string }\n | { type: 'datasetAppend'; datasetId?: string; datasetName?: string }\n | { type: 'webhookPost'; webhookId?: string; webhookName?: string }\n // Server-side steps (AGL-257).\n | {\n type: 'sendEmail'\n subject: string\n body: string\n toField?: string\n /**\n * The stream this message belongs to, so a recipient who left it is not\n * mailed. Absent on every step authored before topics reached the\n * actions editor, which the executor resolves to the default topic.\n */\n topicId?: string\n }\n | { type: 'notifyAdmins'; title: string; body?: string }\n | { type: 'enrollList'; listId?: string; listName?: string }\n | { type: 'updateDataset'; datasetId?: string; datasetName?: string }\n | { type: 'assignCampaign'; campaignId?: string; campaignName?: string }\n /*\n * THE THREE FLOW STEPS.\n *\n * `wait` is the one that matters: without a durable delay an automation is\n * always trigger → immediate actions, so no welcome series, win-back or\n * post-purchase follow-up can exist at all. The other two are what make a\n * delay useful — something to end the flow early, and a wait that ends on\n * an event instead of on the clock.\n *\n * All three are SERVER steps. A delay outlives the page view that started\n * it by days, so the browser that fired the trigger is long gone by the time\n * the flow continues; `hostActionStepsForClient` truncates the client's copy\n * of the step list at the first of these for that reason.\n */\n | {\n type: 'wait'\n /** Whole minutes to hold before the next step. */\n delayMinutes: number\n }\n | {\n type: 'waitForEvent'\n /** The host or custom event that resumes this person's flow. */\n eventName: string\n /**\n * Whole minutes after which the flow continues anyway, with\n * {@link FLOW_TIMED_OUT_FIELD} true in scope. There is always a\n * deadline: a wait with no timeout is an enrollment that lives forever.\n */\n timeoutMinutes: number\n }\n /** Ends the enrollment here. Paired with a `when`, this is the exit branch. */\n | { type: 'exitFlow' }\n /*\n * THE CRM STEPS (AGL-2605).\n *\n * Server steps, every one, because each writes a record only the Admin\n * SDK may write on a visitor's behalf. Each acts on ONE person — the\n * contact the event names, resolved by `contactId` when the payload\n * carries one and by `email` otherwise — and each writes inside the\n * site's own facet or stamps the site's own scope, for the reason\n * `assignCampaign` gives: a contact row is shared by every site in the\n * org, and a stage or a tag is one holder's business record. A step whose\n * event names nobody the site can see does nothing and says so in the run.\n *\n * None of them carries a doc-id reference for the reference audit to\n * check: a stage and a kind are fixed vocabularies, a tag is free text,\n * and an owner is a member who is resolved at run time.\n */\n | { type: 'setContactStage'; lifecycleStage: ContactLifecycleStage }\n | { type: 'addContactTag'; tag: string }\n /**\n * The owner by uid when a member id was typed or a picker wrote the step,\n * by email when an address was typed; the executor resolves either against\n * the org's roster, and only the roster (AGL-2614).\n * Or `roundRobin`, and the owner is the next member of the pool the CRM's\n * settings keep (AGL-2618) — a step that names nobody and hands the\n * choice to the rotation, so a stage change can spread its follow-ups\n * across a team rather than pile them on one rep. The two are exclusive:\n * a rotation step carries no member, and the validator refuses one that\n * names both.\n */\n | {\n type: 'assignContactOwner'\n ownerUid?: string\n ownerEmail?: string\n roundRobin?: boolean\n }\n | {\n type: 'createCrmTask'\n title: string\n kind: CrmTaskKind\n /** Days from the run to the due date; `0` is due today. */\n dueInDays: number\n /**\n * Who gets it — by uid or by an address, both resolved against the\n * roster the way the owner step's are. Neither named, the task goes\n * to the contact's owner, then to nobody.\n */\n assigneeUid?: string\n assigneeEmail?: string\n }\n | { type: 'logCrmActivity'; kind: CrmActivityKind; body: string }\n) & {\n /** See {@link InteractionStepGuard}. Absent means the step always runs. */\n when?: InteractionStepGuard | null\n}\n\n/** A step of an automation: one of the platform's client steps, or one of its server steps. */\nexport type HostActionStep = ClientInteractionStep | ServerActionStep\n\n/**\n * The steps that suspend a run and continue it later, from a job beat.\n *\n * Named as a set rather than checked inline because three surfaces have to\n * agree on it: the executor stops here and writes an enrollment, the client\n * payload is truncated here, and the validator refuses a flow that waits\n * without a person to wait for.\n */\nexport const FLOW_SUSPENDING_STEP_TYPES: ReadonlySet<HostActionStepType> =\n new Set(['wait', 'waitForEvent'] as const)\n\nexport function isFlowSuspendingStep(step: HostActionStep): boolean {\n return FLOW_SUSPENDING_STEP_TYPES.has(step.type)\n}\n\n/**\n * The steps that act on the CRM (AGL-2605) — named as a set because the\n * executor dispatches all five to one module and the docs list them as one\n * group, and a step added to the union but not here would be a server step\n * the executor silently skipped.\n */\nexport const CRM_ACTION_STEP_TYPES: ReadonlySet<HostActionStepType> = new Set([\n 'setContactStage',\n 'addContactTag',\n 'assignContactOwner',\n 'createCrmTask',\n 'logCrmActivity',\n] as const)\n\n/** The CRM steps, as the type the executor narrows to. */\nexport type CrmActionStep = Extract<\n HostActionStep,\n {\n type:\n | 'setContactStage'\n | 'addContactTag'\n | 'assignContactOwner'\n | 'createCrmTask'\n | 'logCrmActivity'\n }\n>\n\nexport function isCrmActionStep(step: HostActionStep): step is CrmActionStep {\n return CRM_ACTION_STEP_TYPES.has(step.type)\n}\n\n/** Longest tag an automation may write — the console's own tag field cap. */\nexport const CONTACT_TAG_MAX_LENGTH = 60\n\n/*\n * THE RECIPES (AGL-2626).\n *\n * A recipe is a ready-to-edit action — a trigger, its conditions and an\n * ordered step list — built from the vocabulary the editor already offers\n * and handed to the editor as a draft. Nothing is written until the person\n * saves, and every field is theirs to change first: the recipe decides\n * where the editor starts, not what the site runs.\n *\n * Defined here, beside the step catalog, rather than in the builder, so the\n * docs page that lists the recipes and the menu that offers them read ONE\n * list, and so a step the catalog renames or retires breaks a recipe at\n * compile time rather than in a menu nobody tests.\n *\n * Definitions only. Three of the four are complete as written. `tagByForm`\n * needs a form, and a form belongs to a site (`hosts/{hostId}/forms`), so\n * the picker that supplies it is the builder's business and host-scoped by\n * nature. That is the seam an org-level mount keeps: the recipes are the\n * same at either level, and only the form picker knows where it is.\n */\nexport const CRM_ACTION_RECIPE_IDS = [\n 'welcomeNewLead',\n 'followUpWonDeal',\n 'reengageStaleLead',\n 'tagByForm',\n] as const\n\nexport type CrmActionRecipeId = (typeof CRM_ACTION_RECIPE_IDS)[number]\n\n/** What a recipe is handed before it builds — today only the form `tagByForm` reads. */\nexport interface CrmActionRecipeInput {\n form?: { id: string; name: string }\n}\n\nexport interface CrmActionRecipe {\n id: CrmActionRecipeId\n /** How the recipe reads in the menu; also the action's starting name. */\n title: string\n /** One sentence under the title. */\n description: string\n /**\n * What must be picked before the recipe can be built. A recipe that needs\n * nothing opens the editor at once; one that needs a form opens a picker\n * first. Built WITHOUT its pick, such a recipe yields an action the\n * validator refuses — a condition with no value — rather than one that\n * saves and then silently matches nothing.\n */\n needs?: 'form'\n /**\n * A fresh action each call, because the draft is edited in place. The\n * action carries `recipe: id` — the provenance is the builder's to stamp,\n * so a writer that saves what it was handed cannot forget it.\n */\n build: (input?: CrmActionRecipeInput) => HostAction\n}\n\n/**\n * How long the stale-lead recipe holds before it decides a lead has gone\n * quiet. A week: long enough that a rep who is working the lead has had a\n * chance to move its stage, short enough that a lead nobody touched is\n * still warm when the reminder lands.\n */\nexport const STALE_LEAD_WAIT_MINUTES = 7 * 24 * 60\n\n/**\n * The tag a form's captures get: the form's own name, cut to the tag cap.\n * The name is what the person calls the form, so it is the tag they would\n * have typed; the editor is open to change it before anything is saved.\n */\nexport function crmRecipeTagForForm(formName: string): string {\n return formName.trim().slice(0, CONTACT_TAG_MAX_LENGTH)\n}\n\nexport const CRM_ACTION_RECIPES: readonly CrmActionRecipe[] = [\n {\n id: 'welcomeNewLead',\n title: 'Welcome a new lead',\n description:\n 'When a form makes a new contact: rotate in an owner, book a call for ' +\n 'tomorrow, send a thank-you, and tag them website.',\n /*\n * The owner first, because the task that follows names no assignee and\n * so goes to whoever owns the contact when it is created — the member\n * the rotation just chose. Round robin rather than a named member: a\n * recipe cannot know who is on the team, and the pool under CRM →\n * Settings is the one place that does. On a workspace with no pool the\n * step fails and the run continues, so the call, the email and the tag\n * still land and the run history says who was not assigned.\n *\n * The email comes before any wait, which makes it an immediate reply to\n * what the visitor just did — transactional, sent from the org's own\n * identity, to the address the event carries.\n */\n build: () => ({\n recipe: 'welcomeNewLead',\n name: 'Welcome a new lead',\n trigger: {\n event: 'contactCreated',\n conditions: [{ field: 'source', op: 'equals', value: 'form' }],\n combinator: 'and',\n },\n steps: [\n { type: 'assignContactOwner', roundRobin: true },\n {\n type: 'createCrmTask',\n title: 'Call the new lead',\n kind: 'call',\n dueInDays: 1,\n },\n {\n type: 'sendEmail',\n subject: 'Thanks for getting in touch',\n body:\n 'Thanks for reaching out — we have your message, and someone ' +\n 'from our team will be in touch within a day.',\n },\n { type: 'addContactTag', tag: 'website' },\n ],\n enabled: true,\n }),\n },\n {\n id: 'followUpWonDeal',\n title: 'Follow up a won deal',\n description:\n 'When a deal is won — which makes the contact a Customer on its own — ' +\n 'book a check-in call a week out.',\n /*\n * No stage step (AGL-2641): the win itself floors the contact at\n * `customer` before `dealWon` is announced, so a step setting the stage\n * here would at best repeat the write and at worst move an evangelist\n * back to customer — a SET, which is what the step is, and not the\n * floor the win applies. The recipe books the follow-up and nothing\n * else.\n */\n build: () => ({\n recipe: 'followUpWonDeal',\n name: 'Follow up a won deal',\n trigger: { event: 'dealWon' },\n steps: [\n {\n type: 'createCrmTask',\n title: 'Check in with the new customer',\n kind: 'call',\n dueInDays: 7,\n },\n ],\n enabled: true,\n }),\n },\n {\n id: 'reengageStaleLead',\n title: 'Re-engage a stale lead',\n description:\n 'When a contact becomes a lead: wait a week, and if their stage has ' +\n 'not moved, book a call to bring them back.',\n /*\n * \"Unless the stage moved on\" is the wait's own event: the flow watches\n * for the next stage change on this person and gives up after a week.\n * Resumed by the clock, the run carries `_waitTimedOut`, and the task\n * step's guard reads it; resumed by the event, the field is absent and\n * the task is skipped — the lead was worked, and nobody is told to\n * re-engage somebody who just moved to Sales qualified.\n */\n build: () => ({\n recipe: 'reengageStaleLead',\n name: 'Re-engage a stale lead',\n trigger: {\n event: 'contactStageChanged',\n conditions: [{ field: 'lifecycleStage', op: 'equals', value: 'lead' }],\n combinator: 'and',\n },\n steps: [\n {\n type: 'waitForEvent',\n eventName: 'contactStageChanged',\n timeoutMinutes: STALE_LEAD_WAIT_MINUTES,\n },\n {\n type: 'createCrmTask',\n title: 'Re-engage a lead that has gone quiet',\n kind: 'call',\n dueInDays: 1,\n when: {\n conditions: [{ field: FLOW_TIMED_OUT_FIELD, op: 'notEmpty' }],\n },\n },\n ],\n enabled: true,\n }),\n },\n {\n id: 'tagByForm',\n title: 'Tag by form',\n description:\n 'When a form you pick makes a new contact: tag them with the form’s ' +\n 'name.',\n needs: 'form',\n build: (input) => {\n const form = input?.form\n return {\n recipe: 'tagByForm',\n name: form ? `Tag ${form.name.trim()} submissions` : 'Tag by form',\n trigger: {\n event: 'contactCreated',\n conditions: [{ field: 'formId', op: 'equals', value: form?.id ?? '' }],\n combinator: 'and',\n },\n steps: [\n { type: 'addContactTag', tag: form ? crmRecipeTagForForm(form.name) : '' },\n ],\n enabled: true,\n }\n },\n },\n]\n\n/** The recipe with this id, or null for a string that names none. */\nexport function crmActionRecipe(id: unknown): CrmActionRecipe | null {\n return CRM_ACTION_RECIPES.find((recipe) => recipe.id === id) ?? null\n}\n\n/**\n * The recipe a STORED action came from, read off its document (AGL-2639).\n *\n * Three answers, and the third is the one that matters. A known id: the\n * action was installed from, or begun as, that recipe. `null`: the action\n * was begun blank, or from a recipe this build no longer knows — the\n * editor wrote the field and said \"no recipe\". `undefined`: the document\n * carries no `recipe` field at all, which is every action saved before the\n * stamp existed. Such an action may well have started from a recipe — the\n * menu opened the editor prefilled long before anything recorded it — so a\n * reader that needs to know whether a site has a recipe treats `undefined`\n * as UNKNOWN, never as absent, and never writes a `null` over it.\n */\nexport function hostActionRecipeId(\n action: { recipe?: unknown } | null | undefined,\n): CrmActionRecipeId | null | undefined {\n const stamp = action?.recipe\n if (stamp === undefined) return undefined\n return crmActionRecipe(stamp)?.id ?? null\n}\n\n/**\n * A HostAction as the document at `hosts/{hostId}/actions/{id}` holds it.\n *\n * Every optional trigger key is written OUT — a boolean cap as `false`, a\n * cleared list as `null` — because the editor saves with a merge-set, and\n * a merge keeps whatever key the payload omits: a frequency cap switched\n * off, left out of the payload, would stay on. The legacy single\n * `condition` is always nulled; `conditions` has been canonical since the\n * list shape arrived and a document that carried both would have the\n * reader pick. Everything else is the action, unchanged.\n *\n * `recipe` rides along only when the action SAYS something about it: an\n * id or `null`. An action that carries no stamp (an older document, edited\n * and saved again) keeps carrying none — see {@link hostActionRecipeId}\n * for why an absent stamp must not become a `null` one.\n */\nexport interface HostActionDocument extends HostAction {\n trigger: HostActionTrigger & {\n oncePerVisitor: boolean\n oncePerSession: boolean\n cooldownMinutes: number | null\n everyTime: boolean\n condition: null\n conditions: HostActionTriggerCondition[] | null\n combinator: TriggerCombinator | null\n }\n enabled: boolean\n}\n\nexport function hostActionDocument(action: HostAction): HostActionDocument {\n const { recipe, ...rest } = action\n const trigger = action.trigger\n return {\n ...rest,\n trigger: {\n ...trigger,\n oncePerVisitor: trigger.oncePerVisitor === true,\n oncePerSession: trigger.oncePerSession === true,\n cooldownMinutes:\n Number(trigger.cooldownMinutes) >= 1 ? Number(trigger.cooldownMinutes) : null,\n everyTime: trigger.everyTime === true,\n condition: null,\n conditions: trigger.conditions ?? null,\n combinator: trigger.combinator ?? null,\n },\n enabled: action.enabled !== false,\n ...(recipe !== undefined ? { recipe } : {}),\n }\n}\n\n/**\n * The step list as the visitor's browser may see it.\n *\n * A client step AFTER a wait must never reach the page. The client engine\n * runs its slice of the list immediately, so shipping the whole list would\n * make \"wait three days, then show the popup\" show the popup at once — the\n * delay would appear to work on the server, be ignored in the browser, and\n * the two halves of one authored flow would disagree about when it happened.\n *\n * Truncating rather than filtering: everything past the first wait belongs to\n * a run that has not happened yet, whichever side would have executed it.\n */\nexport function hostActionStepsForClient(\n steps: readonly HostActionStep[] | undefined | null,\n): HostActionStep[] {\n const list = steps ?? []\n const suspendAt = list.findIndex(isFlowSuspendingStep)\n return [...(suspendAt < 0 ? list : list.slice(0, suspendAt))]\n}\n\nexport type HostActionStepType = HostActionStep['type']\n\n/**\n * `hosts/{hostId}/actions/{id}` doc: a site interaction whose steps may be\n * the automation's server steps as well as the platform's client steps.\n */\nexport interface HostAction extends SiteInteraction<HostActionStep> {\n /**\n * The recipe this action was installed from or begun as (AGL-2639), or\n * `null` for one begun blank. Absent on a document from before the stamp\n * existed — read it through {@link hostActionRecipeId}, which keeps the\n * three cases apart.\n */\n recipe?: CrmActionRecipeId | null\n}\n\n/** Custom-event chaining depth cap (mirrors CROSS_MAX_DEPTH). */\nexport const ACTION_MAX_EVENT_DEPTH = 3\n\n/**\n * The shortest and longest a flow may wait.\n *\n * The floor is a minute because the resume beat runs on a minute, so anything\n * under one is a delay the scheduler cannot honor and would only read as\n * imprecision. The ceiling is ninety days: long enough for the win-back that\n * is the longest sequence anybody writes, and short enough that an enrollment\n * is not an unbounded lease on a document. A person waiting inside a flow is\n * storage the merchant is not looking at, and a wait measured in years is\n * indistinguishable from one nobody will ever collect.\n */\nexport const FLOW_WAIT_MIN_MINUTES = 1\nexport const FLOW_WAIT_MAX_MINUTES = 90 * 24 * 60\n\nexport const HOST_ACTION_STEP_LABELS: Record<HostActionStepType, string> = {\n runWorkflow: 'Run a workflow',\n siteAlert: 'Show a site alert',\n customEvent: 'Fire a custom event',\n datasetAppend: 'Write to a dataset',\n webhookPost: 'Send a webhook (Business)',\n showOverlay: 'Show a popup or bar',\n stickyNav: 'Make navigation sticky',\n addClass: 'Add a CSS class',\n toggleClass: 'Toggle a CSS class',\n removeClass: 'Remove a CSS class',\n showElement: 'Show an element',\n hideElement: 'Hide an element',\n toggleElement: 'Show/hide an element',\n openDrawer: 'Open a drawer',\n closeDrawer: 'Close a drawer',\n toggleDrawer: 'Open/close a drawer',\n openMenu: 'Open a menu',\n closeMenu: 'Close a menu',\n toggleMenu: 'Open/close a menu',\n setAttribute: 'Set an ARIA or data attribute',\n removeAttribute: 'Remove an ARIA or data attribute',\n scrollTo: 'Scroll to element',\n playVideo: 'Play a video',\n showHtml: 'Show custom HTML',\n runJs: 'Run custom JS (Business)',\n redirect: 'Redirect the visitor',\n trackGaEvent: 'Track an analytics event',\n sendEmail: 'Send an email',\n notifyAdmins: 'Notify site admins',\n enrollList: 'Enroll in a list',\n updateDataset: 'Update a dataset record',\n assignCampaign: 'Assign to a campaign',\n wait: 'Wait',\n waitForEvent: 'Wait for something to happen',\n exitFlow: 'End the flow here',\n setContactStage: 'Set the contact’s lifecycle stage',\n addContactTag: 'Tag the contact',\n assignContactOwner: 'Assign the contact an owner',\n createCrmTask: 'Create a CRM task',\n logCrmActivity: 'Log a CRM activity',\n}\n\n/**\n * Past-tense phrases for a run summary (AGL-2171).\n *\n * `HOST_ACTION_STEP_LABELS` above names what a step WILL do, in the\n * imperative, for a `Do` select — \"Send an email\". A run history is the\n * opposite tense and a different audience: it says what already happened,\n * on one line, several steps at a time. `/product/workflows` advertises\n * exactly that shape — `Sent email · saved to Leads · webhook 200`.\n *\n * Two maps rather than one derived from the other, because \"Send a webhook\n * (Business)\" cannot be mechanically turned into \"webhook\" — the plan\n * suffix is part of the picker's label and has no business in a log line.\n * Only the steps a SERVER run can perform appear here; the client-side\n * steps run in the visitor's browser and never reach the activity write.\n */\nexport const HOST_ACTION_STEP_OUTCOMES: Partial<\n Record<HostActionStepType, string>\n> = {\n runWorkflow: 'ran workflow',\n siteAlert: 'showed an alert',\n customEvent: 'fired an event',\n datasetAppend: 'saved to dataset',\n updateDataset: 'updated dataset',\n webhookPost: 'webhook',\n sendEmail: 'sent email',\n notifyAdmins: 'notified admins',\n enrollList: 'enrolled in list',\n assignCampaign: 'assigned to campaign',\n wait: 'waiting',\n waitForEvent: 'waiting for',\n exitFlow: 'ended the flow',\n setContactStage: 'set stage',\n addContactTag: 'tagged',\n assignContactOwner: 'assigned owner',\n createCrmTask: 'created task',\n logCrmActivity: 'logged activity',\n}\n\n/**\n * One step's line in a run summary, with the detail that makes it useful.\n *\n * `saved to Leads` beats `saved to dataset`, and `webhook 200` beats\n * `webhook` — the status code is the entire reason anyone opens a run\n * history after a webhook, and it was being discarded on the line it\n * arrived.\n */\nexport function describeStepOutcome(\n type: HostActionStepType,\n detail?: string,\n): string {\n const base = HOST_ACTION_STEP_OUTCOMES[type] ?? String(type)\n const trimmed = detail?.trim()\n if (!trimmed) return base\n if (type === 'datasetAppend' || type === 'updateDataset') {\n // `saved to dataset` + `Leads` reads as `saved to Leads`, not\n // `saved to dataset Leads`.\n return `${base.replace(/ dataset$/, '')} ${trimmed}`\n }\n return `${base} ${trimmed}`\n}\n\n/** A whole number of minutes inside the wait band. */\nexport function isFlowWaitMinutes(value: unknown): boolean {\n return (\n Number.isInteger(value) &&\n (value as number) >= FLOW_WAIT_MIN_MINUTES &&\n (value as number) <= FLOW_WAIT_MAX_MINUTES\n )\n}\n\n/** True for a custom (non-built-in) event name an action may fire. */\nexport function isCustomEventName(event: string): boolean {\n return (\n !HOST_EVENT_TYPES.includes(event as any) &&\n CUSTOM_EVENT_PATTERN.test(event)\n )\n}\n\n/**\n * Validates an action doc shape; returns a human-readable error or null.\n * Server and console share this so bad steps never persist or run.\n */\nexport function validateHostAction(action: HostAction): string | null {\n if (!action.name?.trim()) return 'Name the action'\n // A stamp is provenance, and provenance naming a recipe that does not\n // exist is a document nothing can read back; `null` and absent both pass.\n if (action.recipe != null && !crmActionRecipe(action.recipe)) {\n return 'Unknown recipe'\n }\n // The trigger, the step guards, the client steps and every declared step's\n // pick are the platform's to check; the server steps are this module's.\n return validateInteraction(action, {\n validateStep: (step, label) => serverStepProblem(step as HostActionStep, label),\n })\n}\n\n/**\n * A server step's own complaint, or null. A `runWorkflow` step's pick is\n * checked with every other declared step's (`interactionSteps` in\n * `plugins.config.json`), by `validateInteraction`.\n */\nfunction serverStepProblem(step: HostActionStep, label: string): string | null {\n if (step.type === 'wait' && !isFlowWaitMinutes(step.delayMinutes)) {\n return `${label}: wait between ${FLOW_WAIT_MIN_MINUTES} minute and ${FLOW_WAIT_MAX_MINUTES} minutes`\n }\n if (step.type === 'waitForEvent') {\n const waited = step.eventName?.trim() ?? ''\n if (\n !waited ||\n (!HOST_EVENT_TYPES.includes(waited as any) && !isCustomEventName(waited))\n ) {\n return `${label}: pick the event to wait for`\n }\n if (!isFlowWaitMinutes(step.timeoutMinutes)) {\n return `${label}: give up after ${FLOW_WAIT_MIN_MINUTES}–${FLOW_WAIT_MAX_MINUTES} minutes`\n }\n }\n if (step.type === 'customEvent') {\n if (!isCustomEventName(step.eventName?.trim() ?? '')) {\n return `${label}: custom event names are 2–40 letters, digits, dashes`\n }\n }\n if (\n step.type === 'datasetAppend' &&\n !step.datasetId?.trim() &&\n !step.datasetName?.trim()\n ) {\n return `${label}: pick a dataset`\n }\n if (\n step.type === 'webhookPost' &&\n !step.webhookId?.trim() &&\n !step.webhookName?.trim()\n ) {\n return `${label}: pick a webhook`\n }\n if (step.type === 'sendEmail') {\n if (!step.subject?.trim()) return `${label}: enter the subject`\n if (!step.body?.trim()) return `${label}: enter the email body`\n }\n if (step.type === 'notifyAdmins' && !step.title?.trim()) {\n return `${label}: enter the notification title`\n }\n if (\n step.type === 'enrollList' &&\n !step.listId?.trim() &&\n !step.listName?.trim()\n ) {\n return `${label}: pick a list`\n }\n if (\n step.type === 'updateDataset' &&\n !step.datasetId?.trim() &&\n !step.datasetName?.trim()\n ) {\n return `${label}: pick a dataset`\n }\n if (\n step.type === 'assignCampaign' &&\n !step.campaignId?.trim() &&\n !step.campaignName?.trim()\n ) {\n return `${label}: pick a campaign`\n }\n // CRM steps (AGL-2605). A stage or a kind outside its vocabulary is\n // refused here rather than stored: the executor treats the value as\n // trusted and would write it into a facet every stage report counts.\n if (\n step.type === 'setContactStage' &&\n !isContactLifecycleStage(step.lifecycleStage)\n ) {\n return `${label}: pick a lifecycle stage`\n }\n if (step.type === 'addContactTag') {\n const tag = step.tag?.trim() ?? ''\n if (!tag) return `${label}: enter the tag`\n if (tag.length > CONTACT_TAG_MAX_LENGTH) {\n return `${label}: tags are at most ${CONTACT_TAG_MAX_LENGTH} characters`\n }\n }\n if (step.type === 'assignContactOwner') {\n const named = Boolean(step.ownerUid?.trim() || step.ownerEmail?.trim())\n if (step.roundRobin === true && named) {\n return `${label}: pick round robin or a member, not both`\n }\n if (\n step.roundRobin !== true &&\n !step.ownerUid?.trim() &&\n !step.ownerEmail?.trim().includes('@')\n ) {\n return `${label}: enter the owner’s email address`\n }\n }\n if (step.type === 'createCrmTask') {\n if (!step.title?.trim()) return `${label}: give the task a title`\n if (!isCrmTaskKind(step.kind)) return `${label}: pick the kind of task`\n if (\n !Number.isInteger(step.dueInDays) ||\n step.dueInDays < 0 ||\n step.dueInDays > CRM_TASK_MAX_DUE_DAYS\n ) {\n return `${label}: due in 0–${CRM_TASK_MAX_DUE_DAYS} days`\n }\n // Optional — but an address that is not one would resolve to nobody at\n // run time, and the task would land unassigned with no word why.\n const assigneeEmail = step.assigneeEmail?.trim() ?? ''\n if (assigneeEmail && !assigneeEmail.includes('@')) {\n return `${label}: enter the assignee’s email address`\n }\n }\n if (step.type === 'logCrmActivity') {\n if (!isCrmActivityKind(step.kind)) {\n return `${label}: pick the kind of activity`\n }\n if (!step.body?.trim()) return `${label}: write what happened`\n }\n return null\n}\n\n/**\n * Webhooks (AGL-149) at `hosts/{hostId}/webhooks/{id}`. Outbound entries\n * are targets a `webhookPost` action step delivers to (HMAC-signed);\n * inbound entries mint `/api/hooks/{hostId}/{hookId}` endpoints that run\n * a workflow with the posted JSON in scope. Business tier (`webhooks`\n * flag); Pro can be enabled per-tenant via entitlement overrides.\n */\nexport interface HostWebhook {\n name: string\n direction: 'outbound' | 'inbound'\n /** Outbound delivery URL (https only; checked again at send time). */\n url?: string\n /** Shared secret: signs outbound bodies, verifies inbound callers. */\n secret?: string\n /** Inbound: workflow (by name) enrolled with the payload in scope. */\n workflowName?: string\n enabled?: boolean\n}\n\nexport const WEBHOOK_MAX_PER_HOST = 5\n\n/** Outbound URLs must be public https — first-line SSRF guard. */\nexport const WEBHOOK_URL_PATTERN =\n /^https:\\/\\/(?!localhost)(?!127\\.)(?!0\\.)(?!10\\.)(?!172\\.(1[6-9]|2\\d|3[01])\\.)(?!192\\.168\\.)(?!169\\.254\\.)[^\\s]+$/i\n\n"],"names":["CRM_TASK_MAX_DUE_DAYS","isContactLifecycleStage","isCrmActivityKind","isCrmTaskKind","CUSTOM_EVENT_PATTERN","validateInteraction","HOST_EVENT_TYPES","FLOW_TIMED_OUT_FIELD","FLOW_SUSPENDING_STEP_TYPES","Set","isFlowSuspendingStep","step","has","type","CRM_ACTION_STEP_TYPES","isCrmActionStep","CONTACT_TAG_MAX_LENGTH","CRM_ACTION_RECIPE_IDS","STALE_LEAD_WAIT_MINUTES","crmRecipeTagForForm","formName","trim","slice","CRM_ACTION_RECIPES","id","title","description","build","recipe","name","trigger","event","conditions","field","op","value","combinator","steps","roundRobin","kind","dueInDays","subject","body","tag","enabled","eventName","timeoutMinutes","when","needs","input","form","crmActionRecipe","find","hostActionRecipeId","action","stamp","undefined","hostActionDocument","rest","oncePerVisitor","oncePerSession","cooldownMinutes","Number","everyTime","condition","hostActionStepsForClient","list","suspendAt","findIndex","ACTION_MAX_EVENT_DEPTH","FLOW_WAIT_MIN_MINUTES","FLOW_WAIT_MAX_MINUTES","HOST_ACTION_STEP_LABELS","runWorkflow","siteAlert","customEvent","datasetAppend","webhookPost","showOverlay","stickyNav","addClass","toggleClass","removeClass","showElement","hideElement","toggleElement","openDrawer","closeDrawer","toggleDrawer","openMenu","closeMenu","toggleMenu","setAttribute","removeAttribute","scrollTo","playVideo","showHtml","runJs","redirect","trackGaEvent","sendEmail","notifyAdmins","enrollList","updateDataset","assignCampaign","wait","waitForEvent","exitFlow","setContactStage","addContactTag","assignContactOwner","createCrmTask","logCrmActivity","HOST_ACTION_STEP_OUTCOMES","describeStepOutcome","detail","base","String","trimmed","replace","isFlowWaitMinutes","isInteger","isCustomEventName","includes","test","validateHostAction","validateStep","label","serverStepProblem","delayMinutes","waited","datasetId","datasetName","webhookId","webhookName","listId","listName","campaignId","campaignName","lifecycleStage","length","named","Boolean","ownerUid","ownerEmail","assigneeEmail","WEBHOOK_MAX_PER_HOST","WEBHOOK_URL_PATTERN"],"mappings":"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"}
|
|
@@ -173,9 +173,9 @@ export interface ActionRunEntryLike extends ActivityEntryLike {
|
|
|
173
173
|
* `formSubmission` → `Form submitted`.
|
|
174
174
|
*
|
|
175
175
|
* The console rendered the raw camelCase identifier in the trigger select
|
|
176
|
-
* and in every run row. The words are
|
|
177
|
-
* — one map, so the picker an author chose from and the
|
|
178
|
-
* afterwards cannot name the same event differently.
|
|
176
|
+
* and in every run row. The words are each event's declared label
|
|
177
|
+
* (`host-events.ts`) — one map, so the picker an author chose from and the
|
|
178
|
+
* run row they read afterwards cannot name the same event differently.
|
|
179
179
|
*/
|
|
180
180
|
export declare function actionTriggerLabel(event: string | undefined): string;
|
|
181
181
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
1
|
/**
|
|
3
2
|
* @license
|
|
4
3
|
* Copyright 2026 Aglyn LLC
|
|
@@ -21,18 +20,18 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
21
20
|
// way — one dropped the target entirely, one printed the raw Firestore doc
|
|
22
21
|
// id when no name was recorded. This module is the single place that turns a
|
|
23
22
|
// stored entry into something a human reads, and into a deep link to the
|
|
24
|
-
// thing that changed. It is deliberately
|
|
25
|
-
// route
|
|
26
|
-
// `@aglyn/aglyn/app-utils/activity-presenter`.
|
|
23
|
+
// thing that changed. It is deliberately light (the string route table and
|
|
24
|
+
// the record-route registry) so plugin libs and the console app can both
|
|
25
|
+
// import it via `@aglyn/aglyn/app-utils/activity-presenter`.
|
|
27
26
|
import { Route, buildRoute } from "./console-routes.js";
|
|
28
|
-
import {
|
|
27
|
+
import { pluginRecordHref, pluginRecordListHref } from "../plugin-manager/plugin-record-routes.js";
|
|
29
28
|
import { PLATFORM_BRAND_NAME } from "./platform-brand.js";
|
|
30
|
-
import { hostEventLabel } from "./
|
|
29
|
+
import { hostEventLabel } from "./host-events.js";
|
|
31
30
|
import { duplicateActivityActionLabel } from "./duplicate-resource.js";
|
|
32
|
-
import { pluginActivityActionLabel, pluginActivityGroupForAction, pluginActivityTargetNoun } from "../plugin-manager/plugin-activity-actions.js";
|
|
31
|
+
import { pluginActivityActionLabel, pluginActivityGroupForAction, pluginActivityTargetLabel, pluginActivityTargetNoun } from "../plugin-manager/plugin-activity-actions.js";
|
|
33
32
|
/** Human, singular noun for a target type. Falls back to the raw type. */ const TYPE_LABELS = {
|
|
34
33
|
host: 'Site',
|
|
35
|
-
screen: '
|
|
34
|
+
screen: 'Page',
|
|
36
35
|
layout: 'Layout',
|
|
37
36
|
theme: 'Theme',
|
|
38
37
|
media: 'Media',
|
|
@@ -46,11 +45,8 @@ import { pluginActivityActionLabel, pluginActivityGroupForAction, pluginActivity
|
|
|
46
45
|
org: 'Organization',
|
|
47
46
|
invite: 'Invitation',
|
|
48
47
|
subscription: 'Subscription',
|
|
49
|
-
// A custom role's
|
|
48
|
+
// A custom role's permission flipped (AGL-2929).
|
|
50
49
|
role: 'Role',
|
|
51
|
-
// A generation job (AGL-2904): the org feed names the job, and each of its
|
|
52
|
-
// outputs names the resource it produced.
|
|
53
|
-
aiJob: 'AI generation',
|
|
54
50
|
// CRM records (AGL-2622): a person added, a company or a deal created or
|
|
55
51
|
// deleted, a lead converted — logged into the feed of the site whose
|
|
56
52
|
// console did it, and linked to the record the way a screen is.
|
|
@@ -59,22 +55,24 @@ import { pluginActivityActionLabel, pluginActivityGroupForAction, pluginActivity
|
|
|
59
55
|
company: 'Company',
|
|
60
56
|
deal: 'Deal'
|
|
61
57
|
};
|
|
62
|
-
/** The activity target types that are CRM records, for the link switch. */ const CRM_TARGET_KINDS = new Set(Object.keys(CRM_RECORD_SECTIONS));
|
|
63
58
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
* A plugin's record, where its owner publishes an address for the kind
|
|
60
|
+
* (`plugin-record-routes`): its own page, or the list it lives on when the
|
|
61
|
+
* entry named no id — a deleted record still has a list to land on.
|
|
62
|
+
* `undefined` for a kind no plugin publishes here.
|
|
63
|
+
*/ function pluginRecordActivityHref(type, id, context) {
|
|
64
|
+
var _ref;
|
|
65
|
+
if (!type) return undefined;
|
|
66
|
+
return (_ref = id ? pluginRecordHref(type, context, id) : pluginRecordListHref(type, context)) != null ? _ref : undefined;
|
|
67
|
+
}
|
|
70
68
|
/**
|
|
71
69
|
* Human noun for a target type, e.g. `'screen'` → `'Screen'`. A plugin's
|
|
72
70
|
* own resource is stored as `pluginId:noun` (AGL-2978), and a reader sees
|
|
73
71
|
* the noun: `acme-mail:mailbox` → `Mailbox`.
|
|
74
72
|
*/ export function activityTypeLabel(type) {
|
|
75
|
-
var _pluginActivityTargetNoun;
|
|
73
|
+
var _TYPE_LABELS_type, _pluginActivityTargetNoun;
|
|
76
74
|
if (!type) return 'Item';
|
|
77
|
-
const known = TYPE_LABELS[type];
|
|
75
|
+
const known = (_TYPE_LABELS_type = TYPE_LABELS[type]) != null ? _TYPE_LABELS_type : pluginActivityTargetLabel(type);
|
|
78
76
|
if (known) return known;
|
|
79
77
|
const noun = (_pluginActivityTargetNoun = pluginActivityTargetNoun(type)) != null ? _pluginActivityTargetNoun : type;
|
|
80
78
|
return noun.charAt(0).toUpperCase() + noun.slice(1);
|
|
@@ -276,21 +274,12 @@ import { pluginActivityActionLabel, pluginActivityGroupForAction, pluginActivity
|
|
|
276
274
|
host
|
|
277
275
|
});
|
|
278
276
|
default:
|
|
279
|
-
// A
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return id ? crmRecordHref({
|
|
286
|
-
orgSlug,
|
|
287
|
-
host
|
|
288
|
-
}, kind, id) : crmSectionHref({
|
|
289
|
-
orgSlug,
|
|
290
|
-
host
|
|
291
|
-
}, CRM_RECORD_SECTIONS[kind]);
|
|
292
|
-
}
|
|
293
|
-
return undefined;
|
|
277
|
+
// A plugin's record — a contact, a deal, an order — at the address
|
|
278
|
+
// its owner publishes, which the route table does not hold.
|
|
279
|
+
return pluginRecordActivityHref(type, id, {
|
|
280
|
+
orgSlug,
|
|
281
|
+
host
|
|
282
|
+
});
|
|
294
283
|
}
|
|
295
284
|
}
|
|
296
285
|
// Org-scoped entries.
|
|
@@ -311,28 +300,23 @@ import { pluginActivityActionLabel, pluginActivityGroupForAction, pluginActivity
|
|
|
311
300
|
orgSlug
|
|
312
301
|
});
|
|
313
302
|
default:
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const hub = `${buildRoute(Route.ORG_CRM, {
|
|
323
|
-
orgSlug
|
|
324
|
-
})}/${section}`;
|
|
325
|
-
return id && type !== 'lead' && type !== 'task' ? `${hub}/${encodeURIComponent(id)}` : hub;
|
|
326
|
-
}
|
|
303
|
+
// A plugin's record acted on at the ORGANIZATION level (AGL-2634) — a
|
|
304
|
+
// deal moved from the org board, a merge, a bulk bar's action — at the
|
|
305
|
+
// organization address its owner publishes; a bulk line names no
|
|
306
|
+
// record and lands on the list.
|
|
307
|
+
return pluginRecordActivityHref(type, id, {
|
|
308
|
+
orgSlug,
|
|
309
|
+
host: null
|
|
310
|
+
});
|
|
327
311
|
}
|
|
328
312
|
}
|
|
329
313
|
/**
|
|
330
314
|
* `formSubmission` → `Form submitted`.
|
|
331
315
|
*
|
|
332
316
|
* The console rendered the raw camelCase identifier in the trigger select
|
|
333
|
-
* and in every run row. The words are
|
|
334
|
-
* — one map, so the picker an author chose from and the
|
|
335
|
-
* afterwards cannot name the same event differently.
|
|
317
|
+
* and in every run row. The words are each event's declared label
|
|
318
|
+
* (`host-events.ts`) — one map, so the picker an author chose from and the
|
|
319
|
+
* run row they read afterwards cannot name the same event differently.
|
|
336
320
|
*/ export function actionTriggerLabel(event) {
|
|
337
321
|
return hostEventLabel(event);
|
|
338
322
|
}
|