@aglyn/aglyn 1.0.0-beta.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +11 -0
- package/package.json +66 -0
- package/src/index.d.ts +25 -0
- package/src/index.js +26 -0
- package/src/index.js.map +1 -0
- package/src/lib/aglyn.d.ts +31 -0
- package/src/lib/aglyn.js +114 -0
- package/src/lib/aglyn.js.map +1 -0
- package/src/lib/app-utils/abuse-report.d.ts +193 -0
- package/src/lib/app-utils/abuse-report.js +321 -0
- package/src/lib/app-utils/abuse-report.js.map +1 -0
- package/src/lib/app-utils/accept-negotiation.d.ts +129 -0
- package/src/lib/app-utils/accept-negotiation.js +266 -0
- package/src/lib/app-utils/accept-negotiation.js.map +1 -0
- package/src/lib/app-utils/account-emails.d.ts +263 -0
- package/src/lib/app-utils/account-emails.js +264 -0
- package/src/lib/app-utils/account-emails.js.map +1 -0
- package/src/lib/app-utils/actions.d.ts +717 -0
- package/src/lib/app-utils/actions.js +1031 -0
- package/src/lib/app-utils/actions.js.map +1 -0
- package/src/lib/app-utils/activity-presenter.d.ts +152 -0
- package/src/lib/app-utils/activity-presenter.js +326 -0
- package/src/lib/app-utils/activity-presenter.js.map +1 -0
- package/src/lib/app-utils/advertising-tag-mounts.d.ts +133 -0
- package/src/lib/app-utils/advertising-tag-mounts.js +118 -0
- package/src/lib/app-utils/advertising-tag-mounts.js.map +1 -0
- package/src/lib/app-utils/advertising-tags.d.ts +307 -0
- package/src/lib/app-utils/advertising-tags.js +525 -0
- package/src/lib/app-utils/advertising-tags.js.map +1 -0
- package/src/lib/app-utils/agent-api-catalog.d.ts +94 -0
- package/src/lib/app-utils/agent-api-catalog.js +106 -0
- package/src/lib/app-utils/agent-api-catalog.js.map +1 -0
- package/src/lib/app-utils/agent-openapi.d.ts +103 -0
- package/src/lib/app-utils/agent-openapi.js +1128 -0
- package/src/lib/app-utils/agent-openapi.js.map +1 -0
- package/src/lib/app-utils/analytics-beacon.d.ts +154 -0
- package/src/lib/app-utils/analytics-beacon.js +152 -0
- package/src/lib/app-utils/analytics-beacon.js.map +1 -0
- package/src/lib/app-utils/analytics-environment.d.ts +133 -0
- package/src/lib/app-utils/analytics-environment.js +152 -0
- package/src/lib/app-utils/analytics-environment.js.map +1 -0
- package/src/lib/app-utils/analytics-events.d.ts +744 -0
- package/src/lib/app-utils/analytics-events.js +650 -0
- package/src/lib/app-utils/analytics-events.js.map +1 -0
- package/src/lib/app-utils/analytics-link-clicks.d.ts +102 -0
- package/src/lib/app-utils/analytics-link-clicks.js +267 -0
- package/src/lib/app-utils/analytics-link-clicks.js.map +1 -0
- package/src/lib/app-utils/analytics-path-key.d.ts +46 -0
- package/src/lib/app-utils/analytics-path-key.js +46 -0
- package/src/lib/app-utils/analytics-path-key.js.map +1 -0
- package/src/lib/app-utils/api-adapter.d.ts +47 -0
- package/src/lib/app-utils/api-adapter.js +367 -0
- package/src/lib/app-utils/api-adapter.js.map +1 -0
- package/src/lib/app-utils/api-idempotency.d.ts +169 -0
- package/src/lib/app-utils/api-idempotency.js +213 -0
- package/src/lib/app-utils/api-idempotency.js.map +1 -0
- package/src/lib/app-utils/api-plugins.d.ts +210 -0
- package/src/lib/app-utils/api-plugins.js +221 -0
- package/src/lib/app-utils/api-plugins.js.map +1 -0
- package/src/lib/app-utils/assist-credits.d.ts +482 -0
- package/src/lib/app-utils/assist-credits.js +509 -0
- package/src/lib/app-utils/assist-credits.js.map +1 -0
- package/src/lib/app-utils/attribution-attribute.d.ts +34 -0
- package/src/lib/app-utils/attribution-attribute.js +34 -0
- package/src/lib/app-utils/attribution-attribute.js.map +1 -0
- package/src/lib/app-utils/attribution-guard.d.ts +130 -0
- package/src/lib/app-utils/attribution-guard.js +410 -0
- package/src/lib/app-utils/attribution-guard.js.map +1 -0
- package/src/lib/app-utils/author-css.d.ts +123 -0
- package/src/lib/app-utils/author-css.js +275 -0
- package/src/lib/app-utils/author-css.js.map +1 -0
- package/src/lib/app-utils/author-html-markdown.d.ts +31 -0
- package/src/lib/app-utils/author-html-markdown.js +497 -0
- package/src/lib/app-utils/author-html-markdown.js.map +1 -0
- package/src/lib/app-utils/author-html.d.ts +135 -0
- package/src/lib/app-utils/author-html.js +1037 -0
- package/src/lib/app-utils/author-html.js.map +1 -0
- package/src/lib/app-utils/automation-placeholders.d.ts +72 -0
- package/src/lib/app-utils/automation-placeholders.js +207 -0
- package/src/lib/app-utils/automation-placeholders.js.map +1 -0
- package/src/lib/app-utils/bandwidth-cap.d.ts +183 -0
- package/src/lib/app-utils/bandwidth-cap.js +193 -0
- package/src/lib/app-utils/bandwidth-cap.js.map +1 -0
- package/src/lib/app-utils/binding-token-catalog.d.ts +82 -0
- package/src/lib/app-utils/binding-token-catalog.js +293 -0
- package/src/lib/app-utils/binding-token-catalog.js.map +1 -0
- package/src/lib/app-utils/binding-tokens.d.ts +157 -0
- package/src/lib/app-utils/binding-tokens.js +251 -0
- package/src/lib/app-utils/binding-tokens.js.map +1 -0
- package/src/lib/app-utils/breadcrumb-json-ld.d.ts +57 -0
- package/src/lib/app-utils/breadcrumb-json-ld.js +67 -0
- package/src/lib/app-utils/breadcrumb-json-ld.js.map +1 -0
- package/src/lib/app-utils/campaign-attribution.d.ts +117 -0
- package/src/lib/app-utils/campaign-attribution.js +162 -0
- package/src/lib/app-utils/campaign-attribution.js.map +1 -0
- package/src/lib/app-utils/campaign-forwarding.d.ts +220 -0
- package/src/lib/app-utils/campaign-forwarding.js +332 -0
- package/src/lib/app-utils/campaign-forwarding.js.map +1 -0
- package/src/lib/app-utils/campaign-membership.d.ts +109 -0
- package/src/lib/app-utils/campaign-membership.js +176 -0
- package/src/lib/app-utils/campaign-membership.js.map +1 -0
- package/src/lib/app-utils/campaign-touch.d.ts +206 -0
- package/src/lib/app-utils/campaign-touch.js +326 -0
- package/src/lib/app-utils/campaign-touch.js.map +1 -0
- package/src/lib/app-utils/child-contract-compose.d.ts +28 -0
- package/src/lib/app-utils/child-contract-compose.js +140 -0
- package/src/lib/app-utils/child-contract-compose.js.map +1 -0
- package/src/lib/app-utils/child-contract.d.ts +129 -0
- package/src/lib/app-utils/child-contract.js +153 -0
- package/src/lib/app-utils/child-contract.js.map +1 -0
- package/src/lib/app-utils/collection-delete.d.ts +113 -0
- package/src/lib/app-utils/collection-delete.js +120 -0
- package/src/lib/app-utils/collection-delete.js.map +1 -0
- package/src/lib/app-utils/collection-entries.d.ts +833 -0
- package/src/lib/app-utils/collection-entries.js +1285 -0
- package/src/lib/app-utils/collection-entries.js.map +1 -0
- package/src/lib/app-utils/collection-entry-date.d.ts +129 -0
- package/src/lib/app-utils/collection-entry-date.js +183 -0
- package/src/lib/app-utils/collection-entry-date.js.map +1 -0
- package/src/lib/app-utils/collection-kind.d.ts +55 -0
- package/src/lib/app-utils/collection-kind.js +69 -0
- package/src/lib/app-utils/collection-kind.js.map +1 -0
- package/src/lib/app-utils/collection-slug.d.ts +79 -0
- package/src/lib/app-utils/collection-slug.js +70 -0
- package/src/lib/app-utils/collection-slug.js.map +1 -0
- package/src/lib/app-utils/compose-layout-nodes.d.ts +147 -0
- package/src/lib/app-utils/compose-layout-nodes.js +261 -0
- package/src/lib/app-utils/compose-layout-nodes.js.map +1 -0
- package/src/lib/app-utils/compose-layout-props.d.ts +71 -0
- package/src/lib/app-utils/compose-layout-props.js +61 -0
- package/src/lib/app-utils/compose-layout-props.js.map +1 -0
- package/src/lib/app-utils/compose-reusable-components.d.ts +504 -0
- package/src/lib/app-utils/compose-reusable-components.js +1366 -0
- package/src/lib/app-utils/compose-reusable-components.js.map +1 -0
- package/src/lib/app-utils/compress.d.ts +35 -0
- package/src/lib/app-utils/compress.js +38 -0
- package/src/lib/app-utils/compress.js.map +1 -0
- package/src/lib/app-utils/consent-banner-ui.d.ts +226 -0
- package/src/lib/app-utils/consent-banner-ui.js +583 -0
- package/src/lib/app-utils/consent-banner-ui.js.map +1 -0
- package/src/lib/app-utils/consent-groups.d.ts +189 -0
- package/src/lib/app-utils/consent-groups.js +219 -0
- package/src/lib/app-utils/consent-groups.js.map +1 -0
- package/src/lib/app-utils/consent-preferences-dialog.d.ts +58 -0
- package/src/lib/app-utils/consent-preferences-dialog.js +106 -0
- package/src/lib/app-utils/consent-preferences-dialog.js.map +1 -0
- package/src/lib/app-utils/console-record-links.d.ts +163 -0
- package/src/lib/app-utils/console-record-links.js +178 -0
- package/src/lib/app-utils/console-record-links.js.map +1 -0
- package/src/lib/app-utils/console-routes.d.ts +658 -0
- package/src/lib/app-utils/console-routes.js +489 -0
- package/src/lib/app-utils/console-routes.js.map +1 -0
- package/src/lib/app-utils/console-widget-slot-context.d.ts +46 -0
- package/src/lib/app-utils/console-widget-slot-context.js +23 -0
- package/src/lib/app-utils/console-widget-slot-context.js.map +1 -0
- package/src/lib/app-utils/contact-custom-fields.d.ts +191 -0
- package/src/lib/app-utils/contact-custom-fields.js +349 -0
- package/src/lib/app-utils/contact-custom-fields.js.map +1 -0
- package/src/lib/app-utils/contact-holder.d.ts +52 -0
- package/src/lib/app-utils/contact-holder.js +98 -0
- package/src/lib/app-utils/contact-holder.js.map +1 -0
- package/src/lib/app-utils/contact-merge.d.ts +136 -0
- package/src/lib/app-utils/contact-merge.js +379 -0
- package/src/lib/app-utils/contact-merge.js.map +1 -0
- package/src/lib/app-utils/contacts.d.ts +554 -0
- package/src/lib/app-utils/contacts.js +401 -0
- package/src/lib/app-utils/contacts.js.map +1 -0
- package/src/lib/app-utils/content-author-profile.d.ts +68 -0
- package/src/lib/app-utils/content-author-profile.js +118 -0
- package/src/lib/app-utils/content-author-profile.js.map +1 -0
- package/src/lib/app-utils/content-authors.d.ts +662 -0
- package/src/lib/app-utils/content-authors.js +771 -0
- package/src/lib/app-utils/content-authors.js.map +1 -0
- package/src/lib/app-utils/content-schema-type.d.ts +81 -0
- package/src/lib/app-utils/content-schema-type.js +87 -0
- package/src/lib/app-utils/content-schema-type.js.map +1 -0
- package/src/lib/app-utils/contexts.d.ts +35 -0
- package/src/lib/app-utils/contexts.js +35 -0
- package/src/lib/app-utils/contexts.js.map +1 -0
- package/src/lib/app-utils/create-resource-uid.d.ts +18 -0
- package/src/lib/app-utils/create-resource-uid.js +23 -0
- package/src/lib/app-utils/create-resource-uid.js.map +1 -0
- package/src/lib/app-utils/crm-booking.d.ts +75 -0
- package/src/lib/app-utils/crm-booking.js +133 -0
- package/src/lib/app-utils/crm-booking.js.map +1 -0
- package/src/lib/app-utils/crm-csv.d.ts +232 -0
- package/src/lib/app-utils/crm-csv.js +432 -0
- package/src/lib/app-utils/crm-csv.js.map +1 -0
- package/src/lib/app-utils/crm-digest.d.ts +179 -0
- package/src/lib/app-utils/crm-digest.js +271 -0
- package/src/lib/app-utils/crm-digest.js.map +1 -0
- package/src/lib/app-utils/crm-email-templates.d.ts +172 -0
- package/src/lib/app-utils/crm-email-templates.js +382 -0
- package/src/lib/app-utils/crm-email-templates.js.map +1 -0
- package/src/lib/app-utils/crm-inbound.d.ts +245 -0
- package/src/lib/app-utils/crm-inbound.js +407 -0
- package/src/lib/app-utils/crm-inbound.js.map +1 -0
- package/src/lib/app-utils/crm-kinds.d.ts +73 -0
- package/src/lib/app-utils/crm-kinds.js +92 -0
- package/src/lib/app-utils/crm-kinds.js.map +1 -0
- package/src/lib/app-utils/crm-next-activity.d.ts +107 -0
- package/src/lib/app-utils/crm-next-activity.js +140 -0
- package/src/lib/app-utils/crm-next-activity.js.map +1 -0
- package/src/lib/app-utils/crm-reports.d.ts +479 -0
- package/src/lib/app-utils/crm-reports.js +680 -0
- package/src/lib/app-utils/crm-reports.js.map +1 -0
- package/src/lib/app-utils/crm-task-reminders.d.ts +129 -0
- package/src/lib/app-utils/crm-task-reminders.js +139 -0
- package/src/lib/app-utils/crm-task-reminders.js.map +1 -0
- package/src/lib/app-utils/crm.d.ts +1546 -0
- package/src/lib/app-utils/crm.js +1814 -0
- package/src/lib/app-utils/crm.js.map +1 -0
- package/src/lib/app-utils/csp-report.d.ts +84 -0
- package/src/lib/app-utils/csp-report.js +177 -0
- package/src/lib/app-utils/csp-report.js.map +1 -0
- package/src/lib/app-utils/csv-import.d.ts +192 -0
- package/src/lib/app-utils/csv-import.js +279 -0
- package/src/lib/app-utils/csv-import.js.map +1 -0
- package/src/lib/app-utils/dataset-csv.d.ts +109 -0
- package/src/lib/app-utils/dataset-csv.js +235 -0
- package/src/lib/app-utils/dataset-csv.js.map +1 -0
- package/src/lib/app-utils/dataset-models.d.ts +194 -0
- package/src/lib/app-utils/dataset-models.js +409 -0
- package/src/lib/app-utils/dataset-models.js.map +1 -0
- package/src/lib/app-utils/dataset-query.d.ts +57 -0
- package/src/lib/app-utils/dataset-query.js +126 -0
- package/src/lib/app-utils/dataset-query.js.map +1 -0
- package/src/lib/app-utils/datasets.d.ts +136 -0
- package/src/lib/app-utils/datasets.js +177 -0
- package/src/lib/app-utils/datasets.js.map +1 -0
- package/src/lib/app-utils/decompress.d.ts +36 -0
- package/src/lib/app-utils/decompress.js +32 -0
- package/src/lib/app-utils/decompress.js.map +1 -0
- package/src/lib/app-utils/dedicated-sending-domain.d.ts +47 -0
- package/src/lib/app-utils/dedicated-sending-domain.js +114 -0
- package/src/lib/app-utils/dedicated-sending-domain.js.map +1 -0
- package/src/lib/app-utils/deferred-image.d.ts +86 -0
- package/src/lib/app-utils/deferred-image.js +83 -0
- package/src/lib/app-utils/deferred-image.js.map +1 -0
- package/src/lib/app-utils/definition-canvas-tree.d.ts +61 -0
- package/src/lib/app-utils/definition-canvas-tree.js +126 -0
- package/src/lib/app-utils/definition-canvas-tree.js.map +1 -0
- package/src/lib/app-utils/deployment-shape.d.ts +167 -0
- package/src/lib/app-utils/deployment-shape.js +238 -0
- package/src/lib/app-utils/deployment-shape.js.map +1 -0
- package/src/lib/app-utils/detect-template-placeholders.d.ts +30 -0
- package/src/lib/app-utils/detect-template-placeholders.js +53 -0
- package/src/lib/app-utils/detect-template-placeholders.js.map +1 -0
- package/src/lib/app-utils/dmca-counter-notice.d.ts +185 -0
- package/src/lib/app-utils/dmca-counter-notice.js +277 -0
- package/src/lib/app-utils/dmca-counter-notice.js.map +1 -0
- package/src/lib/app-utils/docs-deflection.d.ts +156 -0
- package/src/lib/app-utils/docs-deflection.js +547 -0
- package/src/lib/app-utils/docs-deflection.js.map +1 -0
- package/src/lib/app-utils/docs-help.d.ts +57 -0
- package/src/lib/app-utils/docs-help.generated.d.ts +303 -0
- package/src/lib/app-utils/docs-help.generated.js +867 -0
- package/src/lib/app-utils/docs-help.generated.js.map +1 -0
- package/src/lib/app-utils/docs-help.js +46 -0
- package/src/lib/app-utils/docs-help.js.map +1 -0
- package/src/lib/app-utils/docs-index.generated.d.ts +29 -0
- package/src/lib/app-utils/docs-index.generated.js +8388 -0
- package/src/lib/app-utils/docs-index.generated.js.map +1 -0
- package/src/lib/app-utils/docs-retrieval.d.ts +67 -0
- package/src/lib/app-utils/docs-retrieval.js +188 -0
- package/src/lib/app-utils/docs-retrieval.js.map +1 -0
- package/src/lib/app-utils/document-landmark.d.ts +50 -0
- package/src/lib/app-utils/document-landmark.js +100 -0
- package/src/lib/app-utils/document-landmark.js.map +1 -0
- package/src/lib/app-utils/duplicate-resource.d.ts +101 -0
- package/src/lib/app-utils/duplicate-resource.js +206 -0
- package/src/lib/app-utils/duplicate-resource.js.map +1 -0
- package/src/lib/app-utils/dynamic-list-rule.d.ts +471 -0
- package/src/lib/app-utils/dynamic-list-rule.js +914 -0
- package/src/lib/app-utils/dynamic-list-rule.js.map +1 -0
- package/src/lib/app-utils/element-animation.d.ts +200 -0
- package/src/lib/app-utils/element-animation.js +233 -0
- package/src/lib/app-utils/element-animation.js.map +1 -0
- package/src/lib/app-utils/element-hidden-style.d.ts +46 -0
- package/src/lib/app-utils/element-hidden-style.js +48 -0
- package/src/lib/app-utils/element-hidden-style.js.map +1 -0
- package/src/lib/app-utils/element-ui.d.ts +243 -0
- package/src/lib/app-utils/element-ui.js +459 -0
- package/src/lib/app-utils/element-ui.js.map +1 -0
- package/src/lib/app-utils/email-starter-policy.d.ts +160 -0
- package/src/lib/app-utils/email-starter-policy.js +327 -0
- package/src/lib/app-utils/email-starter-policy.js.map +1 -0
- package/src/lib/app-utils/email-topics.d.ts +310 -0
- package/src/lib/app-utils/email-topics.js +336 -0
- package/src/lib/app-utils/email-topics.js.map +1 -0
- package/src/lib/app-utils/enabled-plugins-context.d.ts +100 -0
- package/src/lib/app-utils/enabled-plugins-context.js +113 -0
- package/src/lib/app-utils/enabled-plugins-context.js.map +1 -0
- package/src/lib/app-utils/ensure-canvas-root.d.ts +38 -0
- package/src/lib/app-utils/ensure-canvas-root.js +61 -0
- package/src/lib/app-utils/ensure-canvas-root.js.map +1 -0
- package/src/lib/app-utils/entity-picker-context.d.ts +199 -0
- package/src/lib/app-utils/entity-picker-context.js +93 -0
- package/src/lib/app-utils/entity-picker-context.js.map +1 -0
- package/src/lib/app-utils/error-beacon.d.ts +107 -0
- package/src/lib/app-utils/error-beacon.js +296 -0
- package/src/lib/app-utils/error-beacon.js.map +1 -0
- package/src/lib/app-utils/expand-repeatables.d.ts +172 -0
- package/src/lib/app-utils/expand-repeatables.js +299 -0
- package/src/lib/app-utils/expand-repeatables.js.map +1 -0
- package/src/lib/app-utils/foreign-script.d.ts +82 -0
- package/src/lib/app-utils/foreign-script.js +102 -0
- package/src/lib/app-utils/foreign-script.js.map +1 -0
- package/src/lib/app-utils/form-abuse-ceiling.d.ts +146 -0
- package/src/lib/app-utils/form-abuse-ceiling.js +158 -0
- package/src/lib/app-utils/form-abuse-ceiling.js.map +1 -0
- package/src/lib/app-utils/form-contract.d.ts +198 -0
- package/src/lib/app-utils/form-contract.js +282 -0
- package/src/lib/app-utils/form-contract.js.map +1 -0
- package/src/lib/app-utils/form-dataset-binding.d.ts +50 -0
- package/src/lib/app-utils/form-dataset-binding.js +74 -0
- package/src/lib/app-utils/form-dataset-binding.js.map +1 -0
- package/src/lib/app-utils/forms.d.ts +727 -0
- package/src/lib/app-utils/forms.js +777 -0
- package/src/lib/app-utils/forms.js.map +1 -0
- package/src/lib/app-utils/functions.d.ts +188 -0
- package/src/lib/app-utils/functions.js +529 -0
- package/src/lib/app-utils/functions.js.map +1 -0
- package/src/lib/app-utils/guarded-version-save.d.ts +91 -0
- package/src/lib/app-utils/guarded-version-save.js +131 -0
- package/src/lib/app-utils/guarded-version-save.js.map +1 -0
- package/src/lib/app-utils/health-report.d.ts +1766 -0
- package/src/lib/app-utils/health-report.js +2072 -0
- package/src/lib/app-utils/health-report.js.map +1 -0
- package/src/lib/app-utils/host-naming.d.ts +135 -0
- package/src/lib/app-utils/host-naming.js +188 -0
- package/src/lib/app-utils/host-naming.js.map +1 -0
- package/src/lib/app-utils/host-permissions.d.ts +76 -0
- package/src/lib/app-utils/host-permissions.js +119 -0
- package/src/lib/app-utils/host-permissions.js.map +1 -0
- package/src/lib/app-utils/host-tokens.d.ts +228 -0
- package/src/lib/app-utils/host-tokens.js +304 -0
- package/src/lib/app-utils/host-tokens.js.map +1 -0
- package/src/lib/app-utils/idp-profile.d.ts +83 -0
- package/src/lib/app-utils/idp-profile.js +268 -0
- package/src/lib/app-utils/idp-profile.js.map +1 -0
- package/src/lib/app-utils/image-upload-types.d.ts +49 -0
- package/src/lib/app-utils/image-upload-types.js +86 -0
- package/src/lib/app-utils/image-upload-types.js.map +1 -0
- package/src/lib/app-utils/index.d.ts +18 -0
- package/src/lib/app-utils/index.js +23 -0
- package/src/lib/app-utils/index.js.map +1 -0
- package/src/lib/app-utils/internal-traffic.d.ts +197 -0
- package/src/lib/app-utils/internal-traffic.js +288 -0
- package/src/lib/app-utils/internal-traffic.js.map +1 -0
- package/src/lib/app-utils/lineal-order.d.ts +62 -0
- package/src/lib/app-utils/lineal-order.js +65 -0
- package/src/lib/app-utils/lineal-order.js.map +1 -0
- package/src/lib/app-utils/link-references.d.ts +36 -0
- package/src/lib/app-utils/link-references.js +89 -0
- package/src/lib/app-utils/link-references.js.map +1 -0
- package/src/lib/app-utils/link-target-search-context.d.ts +123 -0
- package/src/lib/app-utils/link-target-search-context.js +221 -0
- package/src/lib/app-utils/link-target-search-context.js.map +1 -0
- package/src/lib/app-utils/list-assignment-policy.d.ts +169 -0
- package/src/lib/app-utils/list-assignment-policy.js +170 -0
- package/src/lib/app-utils/list-assignment-policy.js.map +1 -0
- package/src/lib/app-utils/list-import.d.ts +158 -0
- package/src/lib/app-utils/list-import.js +332 -0
- package/src/lib/app-utils/list-import.js.map +1 -0
- package/src/lib/app-utils/llms-txt.d.ts +104 -0
- package/src/lib/app-utils/llms-txt.js +144 -0
- package/src/lib/app-utils/llms-txt.js.map +1 -0
- package/src/lib/app-utils/local-storage-budget.d.ts +75 -0
- package/src/lib/app-utils/local-storage-budget.js +153 -0
- package/src/lib/app-utils/local-storage-budget.js.map +1 -0
- package/src/lib/app-utils/lockdown.d.ts +698 -0
- package/src/lib/app-utils/lockdown.js +929 -0
- package/src/lib/app-utils/lockdown.js.map +1 -0
- package/src/lib/app-utils/margin-utilization.d.ts +267 -0
- package/src/lib/app-utils/margin-utilization.js +351 -0
- package/src/lib/app-utils/margin-utilization.js.map +1 -0
- package/src/lib/app-utils/markdown-lite.d.ts +297 -0
- package/src/lib/app-utils/markdown-lite.js +705 -0
- package/src/lib/app-utils/markdown-lite.js.map +1 -0
- package/src/lib/app-utils/marketing-consent.d.ts +704 -0
- package/src/lib/app-utils/marketing-consent.js +783 -0
- package/src/lib/app-utils/marketing-consent.js.map +1 -0
- package/src/lib/app-utils/marketplace-listing-visibility.d.ts +120 -0
- package/src/lib/app-utils/marketplace-listing-visibility.js +139 -0
- package/src/lib/app-utils/marketplace-listing-visibility.js.map +1 -0
- package/src/lib/app-utils/marketplace-overrides.d.ts +271 -0
- package/src/lib/app-utils/marketplace-overrides.js +462 -0
- package/src/lib/app-utils/marketplace-overrides.js.map +1 -0
- package/src/lib/app-utils/marketplace-provenance.d.ts +150 -0
- package/src/lib/app-utils/marketplace-provenance.js +136 -0
- package/src/lib/app-utils/marketplace-provenance.js.map +1 -0
- package/src/lib/app-utils/marketplace-theme.d.ts +237 -0
- package/src/lib/app-utils/marketplace-theme.js +525 -0
- package/src/lib/app-utils/marketplace-theme.js.map +1 -0
- package/src/lib/app-utils/marketplace-update-state.d.ts +119 -0
- package/src/lib/app-utils/marketplace-update-state.js +132 -0
- package/src/lib/app-utils/marketplace-update-state.js.map +1 -0
- package/src/lib/app-utils/marketplace-verification.d.ts +95 -0
- package/src/lib/app-utils/marketplace-verification.js +81 -0
- package/src/lib/app-utils/marketplace-verification.js.map +1 -0
- package/src/lib/app-utils/measure-node-map.d.ts +76 -0
- package/src/lib/app-utils/measure-node-map.js +111 -0
- package/src/lib/app-utils/measure-node-map.js.map +1 -0
- package/src/lib/app-utils/media-alt.d.ts +55 -0
- package/src/lib/app-utils/media-alt.js +58 -0
- package/src/lib/app-utils/media-alt.js.map +1 -0
- package/src/lib/app-utils/media-asset-facts.d.ts +125 -0
- package/src/lib/app-utils/media-asset-facts.js +467 -0
- package/src/lib/app-utils/media-asset-facts.js.map +1 -0
- package/src/lib/app-utils/media-cdn-scope.d.ts +65 -0
- package/src/lib/app-utils/media-cdn-scope.js +75 -0
- package/src/lib/app-utils/media-cdn-scope.js.map +1 -0
- package/src/lib/app-utils/media-folders.d.ts +161 -0
- package/src/lib/app-utils/media-folders.js +230 -0
- package/src/lib/app-utils/media-folders.js.map +1 -0
- package/src/lib/app-utils/media-metadata.d.ts +277 -0
- package/src/lib/app-utils/media-metadata.js +388 -0
- package/src/lib/app-utils/media-metadata.js.map +1 -0
- package/src/lib/app-utils/media-picker-context.d.ts +93 -0
- package/src/lib/app-utils/media-picker-context.js +26 -0
- package/src/lib/app-utils/media-picker-context.js.map +1 -0
- package/src/lib/app-utils/media-quarantine.d.ts +247 -0
- package/src/lib/app-utils/media-quarantine.js +323 -0
- package/src/lib/app-utils/media-quarantine.js.map +1 -0
- package/src/lib/app-utils/media-ref.d.ts +661 -0
- package/src/lib/app-utils/media-ref.js +1055 -0
- package/src/lib/app-utils/media-ref.js.map +1 -0
- package/src/lib/app-utils/media-takedown-reach.d.ts +126 -0
- package/src/lib/app-utils/media-takedown-reach.js +143 -0
- package/src/lib/app-utils/media-takedown-reach.js.map +1 -0
- package/src/lib/app-utils/member-email-aliases.d.ts +180 -0
- package/src/lib/app-utils/member-email-aliases.js +217 -0
- package/src/lib/app-utils/member-email-aliases.js.map +1 -0
- package/src/lib/app-utils/merge-node-sx.d.ts +47 -0
- package/src/lib/app-utils/merge-node-sx.js +123 -0
- package/src/lib/app-utils/merge-node-sx.js.map +1 -0
- package/src/lib/app-utils/name-match.d.ts +155 -0
- package/src/lib/app-utils/name-match.js +175 -0
- package/src/lib/app-utils/name-match.js.map +1 -0
- package/src/lib/app-utils/name-search.d.ts +117 -0
- package/src/lib/app-utils/name-search.js +145 -0
- package/src/lib/app-utils/name-search.js.map +1 -0
- package/src/lib/app-utils/node-capabilities.d.ts +85 -0
- package/src/lib/app-utils/node-capabilities.js +88 -0
- package/src/lib/app-utils/node-capabilities.js.map +1 -0
- package/src/lib/app-utils/node-definition-sanitizer.d.ts +164 -0
- package/src/lib/app-utils/node-definition-sanitizer.js +402 -0
- package/src/lib/app-utils/node-definition-sanitizer.js.map +1 -0
- package/src/lib/app-utils/node-identity.d.ts +47 -0
- package/src/lib/app-utils/node-identity.js +50 -0
- package/src/lib/app-utils/node-identity.js.map +1 -0
- package/src/lib/app-utils/node-interactions.d.ts +102 -0
- package/src/lib/app-utils/node-interactions.js +192 -0
- package/src/lib/app-utils/node-interactions.js.map +1 -0
- package/src/lib/app-utils/node-url-policy.d.ts +54 -0
- package/src/lib/app-utils/node-url-policy.js +72 -0
- package/src/lib/app-utils/node-url-policy.js.map +1 -0
- package/src/lib/app-utils/notifications.d.ts +71 -0
- package/src/lib/app-utils/notifications.js +101 -0
- package/src/lib/app-utils/notifications.js.map +1 -0
- package/src/lib/app-utils/onboarding-deep-link.d.ts +120 -0
- package/src/lib/app-utils/onboarding-deep-link.js +142 -0
- package/src/lib/app-utils/onboarding-deep-link.js.map +1 -0
- package/src/lib/app-utils/operator-identity.d.ts +207 -0
- package/src/lib/app-utils/operator-identity.js +186 -0
- package/src/lib/app-utils/operator-identity.js.map +1 -0
- package/src/lib/app-utils/org-billing-doc.d.ts +231 -0
- package/src/lib/app-utils/org-billing-doc.js +238 -0
- package/src/lib/app-utils/org-billing-doc.js.map +1 -0
- package/src/lib/app-utils/org-override-reason.d.ts +100 -0
- package/src/lib/app-utils/org-override-reason.js +116 -0
- package/src/lib/app-utils/org-override-reason.js.map +1 -0
- package/src/lib/app-utils/org-permissions.d.ts +194 -0
- package/src/lib/app-utils/org-permissions.js +357 -0
- package/src/lib/app-utils/org-permissions.js.map +1 -0
- package/src/lib/app-utils/org-roles.d.ts +72 -0
- package/src/lib/app-utils/org-roles.js +131 -0
- package/src/lib/app-utils/org-roles.js.map +1 -0
- package/src/lib/app-utils/organizations.d.ts +407 -0
- package/src/lib/app-utils/organizations.js +511 -0
- package/src/lib/app-utils/organizations.js.map +1 -0
- package/src/lib/app-utils/page-header-actions-context.d.ts +83 -0
- package/src/lib/app-utils/page-header-actions-context.js +58 -0
- package/src/lib/app-utils/page-header-actions-context.js.map +1 -0
- package/src/lib/app-utils/page-header-record-context.d.ts +80 -0
- package/src/lib/app-utils/page-header-record-context.js +64 -0
- package/src/lib/app-utils/page-header-record-context.js.map +1 -0
- package/src/lib/app-utils/page-markdown.d.ts +103 -0
- package/src/lib/app-utils/page-markdown.js +385 -0
- package/src/lib/app-utils/page-markdown.js.map +1 -0
- package/src/lib/app-utils/paid-media-source.d.ts +90 -0
- package/src/lib/app-utils/paid-media-source.js +184 -0
- package/src/lib/app-utils/paid-media-source.js.map +1 -0
- package/src/lib/app-utils/palette-sx.d.ts +34 -0
- package/src/lib/app-utils/palette-sx.js +150 -0
- package/src/lib/app-utils/palette-sx.js.map +1 -0
- package/src/lib/app-utils/password-policy.d.ts +47 -0
- package/src/lib/app-utils/password-policy.js +46 -0
- package/src/lib/app-utils/password-policy.js.map +1 -0
- package/src/lib/app-utils/payments-configured.d.ts +84 -0
- package/src/lib/app-utils/payments-configured.js +88 -0
- package/src/lib/app-utils/payments-configured.js.map +1 -0
- package/src/lib/app-utils/person-erasure.d.ts +126 -0
- package/src/lib/app-utils/person-erasure.js +121 -0
- package/src/lib/app-utils/person-erasure.js.map +1 -0
- package/src/lib/app-utils/person-key.d.ts +32 -0
- package/src/lib/app-utils/person-key.js +62 -0
- package/src/lib/app-utils/person-key.js.map +1 -0
- package/src/lib/app-utils/plan-entitlements.d.ts +2756 -0
- package/src/lib/app-utils/plan-entitlements.js +4085 -0
- package/src/lib/app-utils/plan-entitlements.js.map +1 -0
- package/src/lib/app-utils/platform-ad-conversions.d.ts +122 -0
- package/src/lib/app-utils/platform-ad-conversions.js +174 -0
- package/src/lib/app-utils/platform-ad-conversions.js.map +1 -0
- package/src/lib/app-utils/platform-advertising-tags.d.ts +139 -0
- package/src/lib/app-utils/platform-advertising-tags.js +203 -0
- package/src/lib/app-utils/platform-advertising-tags.js.map +1 -0
- package/src/lib/app-utils/platform-brand.d.ts +151 -0
- package/src/lib/app-utils/platform-brand.js +175 -0
- package/src/lib/app-utils/platform-brand.js.map +1 -0
- package/src/lib/app-utils/platform-consent-default.d.ts +142 -0
- package/src/lib/app-utils/platform-consent-default.js +295 -0
- package/src/lib/app-utils/platform-consent-default.js.map +1 -0
- package/src/lib/app-utils/platform-marketing-consent.d.ts +151 -0
- package/src/lib/app-utils/platform-marketing-consent.js +182 -0
- package/src/lib/app-utils/platform-marketing-consent.js.map +1 -0
- package/src/lib/app-utils/platform-marketing-host.d.ts +33 -0
- package/src/lib/app-utils/platform-marketing-host.js +97 -0
- package/src/lib/app-utils/platform-marketing-host.js.map +1 -0
- package/src/lib/app-utils/platform-visitor-consent.d.ts +252 -0
- package/src/lib/app-utils/platform-visitor-consent.js +533 -0
- package/src/lib/app-utils/platform-visitor-consent.js.map +1 -0
- package/src/lib/app-utils/plugin-api-cross-origin.d.ts +61 -0
- package/src/lib/app-utils/plugin-api-cross-origin.js +202 -0
- package/src/lib/app-utils/plugin-api-cross-origin.js.map +1 -0
- package/src/lib/app-utils/plugin-api-rate-limit.d.ts +192 -0
- package/src/lib/app-utils/plugin-api-rate-limit.js +270 -0
- package/src/lib/app-utils/plugin-api-rate-limit.js.map +1 -0
- package/src/lib/app-utils/plugin-artifact-path.d.ts +32 -0
- package/src/lib/app-utils/plugin-artifact-path.js +33 -0
- package/src/lib/app-utils/plugin-artifact-path.js.map +1 -0
- package/src/lib/app-utils/plugin-bundle-checks.d.ts +148 -0
- package/src/lib/app-utils/plugin-bundle-checks.js +989 -0
- package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -0
- package/src/lib/app-utils/plugin-manifest.d.ts +516 -0
- package/src/lib/app-utils/plugin-manifest.js +687 -0
- package/src/lib/app-utils/plugin-manifest.js.map +1 -0
- package/src/lib/app-utils/published-legal-pages.d.ts +71 -0
- package/src/lib/app-utils/published-legal-pages.js +90 -0
- package/src/lib/app-utils/published-legal-pages.js.map +1 -0
- package/src/lib/app-utils/publisher-agreement.d.ts +254 -0
- package/src/lib/app-utils/publisher-agreement.js +274 -0
- package/src/lib/app-utils/publisher-agreement.js.map +1 -0
- package/src/lib/app-utils/publisher-attestation.d.ts +99 -0
- package/src/lib/app-utils/publisher-attestation.js +158 -0
- package/src/lib/app-utils/publisher-attestation.js.map +1 -0
- package/src/lib/app-utils/recipient-email-render.d.ts +216 -0
- package/src/lib/app-utils/recipient-email-render.js +221 -0
- package/src/lib/app-utils/recipient-email-render.js.map +1 -0
- package/src/lib/app-utils/redispatch-caught-error.d.ts +38 -0
- package/src/lib/app-utils/redispatch-caught-error.js +46 -0
- package/src/lib/app-utils/redispatch-caught-error.js.map +1 -0
- package/src/lib/app-utils/registering-plugin.d.ts +19 -0
- package/src/lib/app-utils/registering-plugin.js +38 -0
- package/src/lib/app-utils/registering-plugin.js.map +1 -0
- package/src/lib/app-utils/release-flags.d.ts +283 -0
- package/src/lib/app-utils/release-flags.js +518 -0
- package/src/lib/app-utils/release-flags.js.map +1 -0
- package/src/lib/app-utils/repair-canvas-nodes.d.ts +130 -0
- package/src/lib/app-utils/repair-canvas-nodes.js +298 -0
- package/src/lib/app-utils/repair-canvas-nodes.js.map +1 -0
- package/src/lib/app-utils/repeat-infringer.d.ts +166 -0
- package/src/lib/app-utils/repeat-infringer.js +188 -0
- package/src/lib/app-utils/repeat-infringer.js.map +1 -0
- package/src/lib/app-utils/repeat-sources.d.ts +115 -0
- package/src/lib/app-utils/repeat-sources.js +86 -0
- package/src/lib/app-utils/repeat-sources.js.map +1 -0
- package/src/lib/app-utils/request-geo.d.ts +114 -0
- package/src/lib/app-utils/request-geo.js +247 -0
- package/src/lib/app-utils/request-geo.js.map +1 -0
- package/src/lib/app-utils/request-ip.d.ts +213 -0
- package/src/lib/app-utils/request-ip.js +344 -0
- package/src/lib/app-utils/request-ip.js.map +1 -0
- package/src/lib/app-utils/resolve-named-tokens.d.ts +26 -0
- package/src/lib/app-utils/resolve-named-tokens.js +59 -0
- package/src/lib/app-utils/resolve-named-tokens.js.map +1 -0
- package/src/lib/app-utils/responsive-typography-sx.d.ts +77 -0
- package/src/lib/app-utils/responsive-typography-sx.js +122 -0
- package/src/lib/app-utils/responsive-typography-sx.js.map +1 -0
- package/src/lib/app-utils/reusable-component-keys.d.ts +135 -0
- package/src/lib/app-utils/reusable-component-keys.js +140 -0
- package/src/lib/app-utils/reusable-component-keys.js.map +1 -0
- package/src/lib/app-utils/reusable-prop-values.d.ts +199 -0
- package/src/lib/app-utils/reusable-prop-values.js +494 -0
- package/src/lib/app-utils/reusable-prop-values.js.map +1 -0
- package/src/lib/app-utils/safe-json-ld.d.ts +29 -0
- package/src/lib/app-utils/safe-json-ld.js +37 -0
- package/src/lib/app-utils/safe-json-ld.js.map +1 -0
- package/src/lib/app-utils/sanitize-svg.d.ts +55 -0
- package/src/lib/app-utils/sanitize-svg.js +460 -0
- package/src/lib/app-utils/sanitize-svg.js.map +1 -0
- package/src/lib/app-utils/scheme-sx.d.ts +84 -0
- package/src/lib/app-utils/scheme-sx.js +148 -0
- package/src/lib/app-utils/scheme-sx.js.map +1 -0
- package/src/lib/app-utils/scope-tokens.d.ts +249 -0
- package/src/lib/app-utils/scope-tokens.js +331 -0
- package/src/lib/app-utils/scope-tokens.js.map +1 -0
- package/src/lib/app-utils/screen-link-context-value.d.ts +57 -0
- package/src/lib/app-utils/screen-link-context-value.js +43 -0
- package/src/lib/app-utils/screen-link-context-value.js.map +1 -0
- package/src/lib/app-utils/screen-link-context.d.ts +185 -0
- package/src/lib/app-utils/screen-link-context.js +304 -0
- package/src/lib/app-utils/screen-link-context.js.map +1 -0
- package/src/lib/app-utils/screen-link-value.d.ts +269 -0
- package/src/lib/app-utils/screen-link-value.js +384 -0
- package/src/lib/app-utils/screen-link-value.js.map +1 -0
- package/src/lib/app-utils/screen-route.d.ts +669 -0
- package/src/lib/app-utils/screen-route.js +751 -0
- package/src/lib/app-utils/screen-route.js.map +1 -0
- package/src/lib/app-utils/screen-seo-fields.d.ts +37 -0
- package/src/lib/app-utils/screen-seo-fields.js +42 -0
- package/src/lib/app-utils/screen-seo-fields.js.map +1 -0
- package/src/lib/app-utils/search-indexing.d.ts +162 -0
- package/src/lib/app-utils/search-indexing.js +207 -0
- package/src/lib/app-utils/search-indexing.js.map +1 -0
- package/src/lib/app-utils/seo-listing-fields.d.ts +70 -0
- package/src/lib/app-utils/seo-listing-fields.js +75 -0
- package/src/lib/app-utils/seo-listing-fields.js.map +1 -0
- package/src/lib/app-utils/seo-locale.d.ts +119 -0
- package/src/lib/app-utils/seo-locale.js +133 -0
- package/src/lib/app-utils/seo-locale.js.map +1 -0
- package/src/lib/app-utils/seo-title-variables.d.ts +149 -0
- package/src/lib/app-utils/seo-title-variables.js +190 -0
- package/src/lib/app-utils/seo-title-variables.js.map +1 -0
- package/src/lib/app-utils/seo-title.d.ts +70 -0
- package/src/lib/app-utils/seo-title.js +117 -0
- package/src/lib/app-utils/seo-title.js.map +1 -0
- package/src/lib/app-utils/server.d.ts +174 -0
- package/src/lib/app-utils/server.js +314 -0
- package/src/lib/app-utils/server.js.map +1 -0
- package/src/lib/app-utils/site-context.d.ts +106 -0
- package/src/lib/app-utils/site-context.js +100 -0
- package/src/lib/app-utils/site-context.js.map +1 -0
- package/src/lib/app-utils/sitemap.d.ts +117 -0
- package/src/lib/app-utils/sitemap.js +183 -0
- package/src/lib/app-utils/sitemap.js.map +1 -0
- package/src/lib/app-utils/social-image.d.ts +112 -0
- package/src/lib/app-utils/social-image.js +191 -0
- package/src/lib/app-utils/social-image.js.map +1 -0
- package/src/lib/app-utils/starter-templates.d.ts +99 -0
- package/src/lib/app-utils/starter-templates.js +579 -0
- package/src/lib/app-utils/starter-templates.js.map +1 -0
- package/src/lib/app-utils/stored-nodes.d.ts +120 -0
- package/src/lib/app-utils/stored-nodes.js +174 -0
- package/src/lib/app-utils/stored-nodes.js.map +1 -0
- package/src/lib/app-utils/strip-undefined.d.ts +59 -0
- package/src/lib/app-utils/strip-undefined.js +95 -0
- package/src/lib/app-utils/strip-undefined.js.map +1 -0
- package/src/lib/app-utils/stripe-deployment-mode.d.ts +47 -0
- package/src/lib/app-utils/stripe-deployment-mode.js +70 -0
- package/src/lib/app-utils/stripe-deployment-mode.js.map +1 -0
- package/src/lib/app-utils/support-tiers.d.ts +108 -0
- package/src/lib/app-utils/support-tiers.js +201 -0
- package/src/lib/app-utils/support-tiers.js.map +1 -0
- package/src/lib/app-utils/timezone-geo-hint.d.ts +36 -0
- package/src/lib/app-utils/timezone-geo-hint.js +180 -0
- package/src/lib/app-utils/timezone-geo-hint.js.map +1 -0
- package/src/lib/app-utils/upload-cors.d.ts +155 -0
- package/src/lib/app-utils/upload-cors.js +254 -0
- package/src/lib/app-utils/upload-cors.js.map +1 -0
- package/src/lib/app-utils/upload-inspection.d.ts +159 -0
- package/src/lib/app-utils/upload-inspection.js +539 -0
- package/src/lib/app-utils/upload-inspection.js.map +1 -0
- package/src/lib/app-utils/url-slug.d.ts +40 -0
- package/src/lib/app-utils/url-slug.js +42 -0
- package/src/lib/app-utils/url-slug.js.map +1 -0
- package/src/lib/app-utils/usage-budget.d.ts +429 -0
- package/src/lib/app-utils/usage-budget.js +421 -0
- package/src/lib/app-utils/usage-budget.js.map +1 -0
- package/src/lib/app-utils/variables.d.ts +96 -0
- package/src/lib/app-utils/variables.js +233 -0
- package/src/lib/app-utils/variables.js.map +1 -0
- package/src/lib/app-utils/video-asset-facts.d.ts +61 -0
- package/src/lib/app-utils/video-asset-facts.js +70 -0
- package/src/lib/app-utils/video-asset-facts.js.map +1 -0
- package/src/lib/app-utils/video-object.d.ts +89 -0
- package/src/lib/app-utils/video-object.js +245 -0
- package/src/lib/app-utils/video-object.js.map +1 -0
- package/src/lib/app-utils/visitor-consent.d.ts +757 -0
- package/src/lib/app-utils/visitor-consent.js +981 -0
- package/src/lib/app-utils/visitor-consent.js.map +1 -0
- package/src/lib/app-utils/visitor-record-ceiling.d.ts +223 -0
- package/src/lib/app-utils/visitor-record-ceiling.js +215 -0
- package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -0
- package/src/lib/app-utils/web-vitals-rum.d.ts +53 -0
- package/src/lib/app-utils/web-vitals-rum.js +278 -0
- package/src/lib/app-utils/web-vitals-rum.js.map +1 -0
- package/src/lib/app-utils/webhook-delivery.d.ts +324 -0
- package/src/lib/app-utils/webhook-delivery.js +413 -0
- package/src/lib/app-utils/webhook-delivery.js.map +1 -0
- package/src/lib/app-utils/where-used.d.ts +57 -0
- package/src/lib/app-utils/where-used.js +59 -0
- package/src/lib/app-utils/where-used.js.map +1 -0
- package/src/lib/app-utils/wistia-embed.d.ts +86 -0
- package/src/lib/app-utils/wistia-embed.js +119 -0
- package/src/lib/app-utils/wistia-embed.js.map +1 -0
- package/src/lib/app-utils/workflows.d.ts +136 -0
- package/src/lib/app-utils/workflows.js +346 -0
- package/src/lib/app-utils/workflows.js.map +1 -0
- package/src/lib/canvas-manager/canvas-manager-tree.d.ts +109 -0
- package/src/lib/canvas-manager/canvas-manager-tree.js +139 -0
- package/src/lib/canvas-manager/canvas-manager-tree.js.map +1 -0
- package/src/lib/canvas-manager/canvas-manager.d.ts +549 -0
- package/src/lib/canvas-manager/canvas-manager.js +1311 -0
- package/src/lib/canvas-manager/canvas-manager.js.map +1 -0
- package/src/lib/canvas-manager/index.d.ts +20 -0
- package/src/lib/canvas-manager/index.js +21 -0
- package/src/lib/canvas-manager/index.js.map +1 -0
- package/src/lib/canvas-manager/stores/index.d.ts +17 -0
- package/src/lib/canvas-manager/stores/index.js +18 -0
- package/src/lib/canvas-manager/stores/index.js.map +1 -0
- package/src/lib/canvas-manager/stores/node.store.d.ts +30 -0
- package/src/lib/canvas-manager/stores/node.store.js +45 -0
- package/src/lib/canvas-manager/stores/node.store.js.map +1 -0
- package/src/lib/components-manager/components-manager.d.ts +56 -0
- package/src/lib/components-manager/components-manager.js +191 -0
- package/src/lib/components-manager/components-manager.js.map +1 -0
- package/src/lib/components-manager/index.d.ts +18 -0
- package/src/lib/components-manager/index.js +19 -0
- package/src/lib/components-manager/index.js.map +1 -0
- package/src/lib/emit-manager/emit-manager.d.ts +56 -0
- package/src/lib/emit-manager/emit-manager.js +60 -0
- package/src/lib/emit-manager/emit-manager.js.map +1 -0
- package/src/lib/emit-manager/index.d.ts +18 -0
- package/src/lib/emit-manager/index.js +19 -0
- package/src/lib/emit-manager/index.js.map +1 -0
- package/src/lib/foundation/constants/_internal.d.ts +18 -0
- package/src/lib/foundation/constants/_internal.js +62 -0
- package/src/lib/foundation/constants/_internal.js.map +1 -0
- package/src/lib/foundation/constants/app.d.ts +23 -0
- package/src/lib/foundation/constants/app.js +25 -0
- package/src/lib/foundation/constants/app.js.map +1 -0
- package/src/lib/foundation/constants/canvas.d.ts +18 -0
- package/src/lib/foundation/constants/canvas.js +18 -0
- package/src/lib/foundation/constants/canvas.js.map +1 -0
- package/src/lib/foundation/constants/components.d.ts +54 -0
- package/src/lib/foundation/constants/components.js +61 -0
- package/src/lib/foundation/constants/components.js.map +1 -0
- package/src/lib/foundation/constants/shared.d.ts +50 -0
- package/src/lib/foundation/constants/shared.js +49 -0
- package/src/lib/foundation/constants/shared.js.map +1 -0
- package/src/lib/foundation/constants/symbol.d.ts +63 -0
- package/src/lib/foundation/constants/symbol.js +75 -0
- package/src/lib/foundation/constants/symbol.js.map +1 -0
- package/src/lib/foundation/definitions/components.types.d.ts +372 -0
- package/src/lib/foundation/definitions/components.types.js +180 -0
- package/src/lib/foundation/definitions/components.types.js.map +1 -0
- package/src/lib/foundation/definitions/contact.types.d.ts +73 -0
- package/src/lib/foundation/definitions/contact.types.js +107 -0
- package/src/lib/foundation/definitions/contact.types.js.map +1 -0
- package/src/lib/foundation/definitions/host-content-collections.d.ts +137 -0
- package/src/lib/foundation/definitions/host-content-collections.js +219 -0
- package/src/lib/foundation/definitions/host-content-collections.js.map +1 -0
- package/src/lib/foundation/definitions/org-billing.types.d.ts +1361 -0
- package/src/lib/foundation/definitions/org-billing.types.js +77 -0
- package/src/lib/foundation/definitions/org-billing.types.js.map +1 -0
- package/src/lib/foundation/definitions/organization.types.d.ts +307 -0
- package/src/lib/foundation/definitions/organization.types.js +42 -0
- package/src/lib/foundation/definitions/organization.types.js.map +1 -0
- package/src/lib/foundation/definitions/platform.types.d.ts +1326 -0
- package/src/lib/foundation/definitions/platform.types.js +124 -0
- package/src/lib/foundation/definitions/platform.types.js.map +1 -0
- package/src/lib/foundation/definitions/property-kinds.d.ts +189 -0
- package/src/lib/foundation/definitions/property-kinds.js +565 -0
- package/src/lib/foundation/definitions/property-kinds.js.map +1 -0
- package/src/lib/foundation/definitions/shared.d.ts +23 -0
- package/src/lib/foundation/definitions/shared.js +18 -0
- package/src/lib/foundation/definitions/shared.js.map +1 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +249 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.js +499 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -0
- package/src/lib/foundation/index.d.ts +32 -0
- package/src/lib/foundation/index.js +67 -0
- package/src/lib/foundation/index.js.map +1 -0
- package/src/lib/lifecycle.d.ts +24 -0
- package/src/lib/lifecycle.js +39 -0
- package/src/lib/lifecycle.js.map +1 -0
- package/src/lib/mst/common.d.ts +17 -0
- package/src/lib/mst/common.js +23 -0
- package/src/lib/mst/common.js.map +1 -0
- package/src/lib/mst/index.d.ts +19 -0
- package/src/lib/mst/index.js +20 -0
- package/src/lib/mst/index.js.map +1 -0
- package/src/lib/mst/json.d.ts +22 -0
- package/src/lib/mst/json.js +23 -0
- package/src/lib/mst/json.js.map +1 -0
- package/src/lib/mst/types.d.ts +20 -0
- package/src/lib/mst/types.js +18 -0
- package/src/lib/mst/types.js.map +1 -0
- package/src/lib/plugin-manager/billing-webhook-hooks.d.ts +77 -0
- package/src/lib/plugin-manager/billing-webhook-hooks.js +55 -0
- package/src/lib/plugin-manager/billing-webhook-hooks.js.map +1 -0
- package/src/lib/plugin-manager/commerce-zone-props.d.ts +172 -0
- package/src/lib/plugin-manager/commerce-zone-props.js +31 -0
- package/src/lib/plugin-manager/commerce-zone-props.js.map +1 -0
- package/src/lib/plugin-manager/custom-fields.d.ts +60 -0
- package/src/lib/plugin-manager/custom-fields.js +39 -0
- package/src/lib/plugin-manager/custom-fields.js.map +1 -0
- package/src/lib/plugin-manager/editor-sessions.d.ts +109 -0
- package/src/lib/plugin-manager/editor-sessions.js +137 -0
- package/src/lib/plugin-manager/editor-sessions.js.map +1 -0
- package/src/lib/plugin-manager/enabled-plugins.d.ts +438 -0
- package/src/lib/plugin-manager/enabled-plugins.js +403 -0
- package/src/lib/plugin-manager/enabled-plugins.js.map +1 -0
- package/src/lib/plugin-manager/feature-plugins.d.ts +1314 -0
- package/src/lib/plugin-manager/feature-plugins.js +609 -0
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -0
- package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +11 -0
- package/src/lib/plugin-manager/first-party-plugins.generated.js +149 -0
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -0
- package/src/lib/plugin-manager/index.d.ts +42 -0
- package/src/lib/plugin-manager/index.js +46 -0
- package/src/lib/plugin-manager/index.js.map +1 -0
- package/src/lib/plugin-manager/install-presets.d.ts +93 -0
- package/src/lib/plugin-manager/install-presets.js +86 -0
- package/src/lib/plugin-manager/install-presets.js.map +1 -0
- package/src/lib/plugin-manager/media-delivery-provider.d.ts +124 -0
- package/src/lib/plugin-manager/media-delivery-provider.js +53 -0
- package/src/lib/plugin-manager/media-delivery-provider.js.map +1 -0
- package/src/lib/plugin-manager/order-fulfilment.d.ts +142 -0
- package/src/lib/plugin-manager/order-fulfilment.js +104 -0
- package/src/lib/plugin-manager/order-fulfilment.js.map +1 -0
- package/src/lib/plugin-manager/plugin-activity-actions.d.ts +122 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js +140 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -0
- package/src/lib/plugin-manager/plugin-bridge.d.ts +98 -0
- package/src/lib/plugin-manager/plugin-bridge.js +154 -0
- package/src/lib/plugin-manager/plugin-bridge.js.map +1 -0
- package/src/lib/plugin-manager/plugin-config.d.ts +148 -0
- package/src/lib/plugin-manager/plugin-config.js +228 -0
- package/src/lib/plugin-manager/plugin-config.js.map +1 -0
- package/src/lib/plugin-manager/plugin-console-crons.d.ts +139 -0
- package/src/lib/plugin-manager/plugin-console-crons.js +177 -0
- package/src/lib/plugin-manager/plugin-console-crons.js.map +1 -0
- package/src/lib/plugin-manager/plugin-contact-capture.d.ts +220 -0
- package/src/lib/plugin-manager/plugin-contact-capture.js +89 -0
- package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -0
- package/src/lib/plugin-manager/plugin-contributions.d.ts +244 -0
- package/src/lib/plugin-manager/plugin-contributions.js +283 -0
- package/src/lib/plugin-manager/plugin-contributions.js.map +1 -0
- package/src/lib/plugin-manager/plugin-entitlement-keys.d.ts +169 -0
- package/src/lib/plugin-manager/plugin-entitlement-keys.js +84 -0
- package/src/lib/plugin-manager/plugin-entitlement-keys.js.map +1 -0
- package/src/lib/plugin-manager/plugin-entitlements.d.ts +181 -0
- package/src/lib/plugin-manager/plugin-entitlements.js +188 -0
- package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -0
- package/src/lib/plugin-manager/plugin-events.d.ts +145 -0
- package/src/lib/plugin-manager/plugin-events.js +79 -0
- package/src/lib/plugin-manager/plugin-events.js.map +1 -0
- package/src/lib/plugin-manager/plugin-figures.d.ts +251 -0
- package/src/lib/plugin-manager/plugin-figures.js +252 -0
- package/src/lib/plugin-manager/plugin-figures.js.map +1 -0
- package/src/lib/plugin-manager/plugin-host-collections.d.ts +146 -0
- package/src/lib/plugin-manager/plugin-host-collections.js +116 -0
- package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -0
- package/src/lib/plugin-manager/plugin-jobs.d.ts +130 -0
- package/src/lib/plugin-manager/plugin-jobs.js +182 -0
- package/src/lib/plugin-manager/plugin-jobs.js.map +1 -0
- package/src/lib/plugin-manager/plugin-loader.d.ts +86 -0
- package/src/lib/plugin-manager/plugin-loader.js +223 -0
- package/src/lib/plugin-manager/plugin-loader.js.map +1 -0
- package/src/lib/plugin-manager/plugin-manager.d.ts +95 -0
- package/src/lib/plugin-manager/plugin-manager.js +343 -0
- package/src/lib/plugin-manager/plugin-manager.js.map +1 -0
- package/src/lib/plugin-manager/plugin-metered-lines.d.ts +97 -0
- package/src/lib/plugin-manager/plugin-metered-lines.js +123 -0
- package/src/lib/plugin-manager/plugin-metered-lines.js.map +1 -0
- package/src/lib/plugin-manager/plugin-org-erasure.d.ts +52 -0
- package/src/lib/plugin-manager/plugin-org-erasure.js +100 -0
- package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -0
- package/src/lib/plugin-manager/plugin-permissions.d.ts +69 -0
- package/src/lib/plugin-manager/plugin-permissions.js +77 -0
- package/src/lib/plugin-manager/plugin-permissions.js.map +1 -0
- package/src/lib/plugin-manager/plugin-person-erasure.d.ts +58 -0
- package/src/lib/plugin-manager/plugin-person-erasure.js +109 -0
- package/src/lib/plugin-manager/plugin-person-erasure.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-cards.d.ts +99 -0
- package/src/lib/plugin-manager/plugin-record-cards.js +59 -0
- package/src/lib/plugin-manager/plugin-record-cards.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-facts.d.ts +111 -0
- package/src/lib/plugin-manager/plugin-record-facts.js +51 -0
- package/src/lib/plugin-manager/plugin-record-facts.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-routes.d.ts +129 -0
- package/src/lib/plugin-manager/plugin-record-routes.js +93 -0
- package/src/lib/plugin-manager/plugin-record-routes.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-timeline.d.ts +147 -0
- package/src/lib/plugin-manager/plugin-record-timeline.js +39 -0
- package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -0
- package/src/lib/plugin-manager/plugin-resource-drafts.d.ts +137 -0
- package/src/lib/plugin-manager/plugin-resource-drafts.js +51 -0
- package/src/lib/plugin-manager/plugin-resource-drafts.js.map +1 -0
- package/src/lib/plugin-manager/plugin-services.d.ts +94 -0
- package/src/lib/plugin-manager/plugin-services.js +153 -0
- package/src/lib/plugin-manager/plugin-services.js.map +1 -0
- package/src/lib/plugin-manager/plugin-styles-ui.d.ts +71 -0
- package/src/lib/plugin-manager/plugin-styles-ui.js +80 -0
- package/src/lib/plugin-manager/plugin-styles-ui.js.map +1 -0
- package/src/lib/plugin-manager/plugin-styles.d.ts +176 -0
- package/src/lib/plugin-manager/plugin-styles.js +186 -0
- package/src/lib/plugin-manager/plugin-styles.js.map +1 -0
- package/src/lib/plugin-manager/plugin-subprocessors.d.ts +135 -0
- package/src/lib/plugin-manager/plugin-subprocessors.js +129 -0
- package/src/lib/plugin-manager/plugin-subprocessors.js.map +1 -0
- package/src/lib/plugin-manager/plugin-tax-profile.d.ts +84 -0
- package/src/lib/plugin-manager/plugin-tax-profile.js +39 -0
- package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -0
- package/src/lib/plugin-manager/plugin-user-erasure.d.ts +53 -0
- package/src/lib/plugin-manager/plugin-user-erasure.js +84 -0
- package/src/lib/plugin-manager/plugin-user-erasure.js.map +1 -0
- package/src/lib/plugin-manager/plugin-zones.d.ts +121 -0
- package/src/lib/plugin-manager/plugin-zones.js +74 -0
- package/src/lib/plugin-manager/plugin-zones.js.map +1 -0
- package/src/lib/plugin-manager/realm-plugins.d.ts +112 -0
- package/src/lib/plugin-manager/realm-plugins.js +196 -0
- package/src/lib/plugin-manager/realm-plugins.js.map +1 -0
- package/src/lib/plugin-manager/realm-server.d.ts +54 -0
- package/src/lib/plugin-manager/realm-server.js +88 -0
- package/src/lib/plugin-manager/realm-server.js.map +1 -0
- package/src/lib/plugin-manager/record-zone-props.d.ts +126 -0
- package/src/lib/plugin-manager/record-zone-props.js +31 -0
- package/src/lib/plugin-manager/record-zone-props.js.map +1 -0
- package/src/lib/plugin-manager/site-page-hooks.d.ts +102 -0
- package/src/lib/plugin-manager/site-page-hooks.js +114 -0
- package/src/lib/plugin-manager/site-page-hooks.js.map +1 -0
- package/src/lib/plugin-manager/site-runtime.d.ts +57 -0
- package/src/lib/plugin-manager/site-runtime.js +25 -0
- package/src/lib/plugin-manager/site-runtime.js.map +1 -0
- package/src/lib/plugin-manager/usage-alert-contributors.d.ts +115 -0
- package/src/lib/plugin-manager/usage-alert-contributors.js +106 -0
- package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -0
- package/src/lib/types/ast.d.ts +199 -0
- package/src/lib/types/ast.js +20 -0
- package/src/lib/types/ast.js.map +1 -0
- package/src/lib/types/index.d.ts +20 -0
- package/src/lib/types/index.js +21 -0
- package/src/lib/types/index.js.map +1 -0
- package/src/lib/types/nodes.d.ts +608 -0
- package/src/lib/types/nodes.js +28 -0
- package/src/lib/types/nodes.js.map +1 -0
- package/src/lib/types/routing.d.ts +17 -0
- package/src/lib/types/routing.js +18 -0
- package/src/lib/types/routing.js.map +1 -0
- package/src/lib/types/screen.d.ts +23 -0
- package/src/lib/types/screen.js +18 -0
- package/src/lib/types/screen.js.map +1 -0
- package/src/lib/utils.d.ts +25 -0
- package/src/lib/utils.js +42 -0
- package/src/lib/utils.js.map +1 -0
- package/src/server.d.ts +37 -0
- package/src/server.js +55 -0
- package/src/server.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/crm-reports.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\n/**\n * CRM reporting arithmetic (AGL-2604).\n *\n * The Reports section and the dashboard glance card read Firestore\n * aggregates and bounded windows and then have to turn them into a period, a\n * set of weekly bars, a funnel and a pipeline total. Every one of those is a\n * decision about numbers that a card could get subtly wrong in its own JSX —\n * a bucket boundary that is closed on both ends counts a midnight contact\n * twice, a funnel that divides by the step before reads 100% where nobody\n * arrived, a forecast that weights a closed deal forecasts a sale twice. So\n * they live here, pure and specified, and the cards only draw.\n *\n * Pure data module: no Firestore, no React, no clock — every function that\n * needs \"now\" is handed it.\n */\n\nimport type { ContactFacet, ContactSource } from './contacts'\nimport {\n CONTACT_LIFECYCLE_STAGES,\n CONTACT_LIFECYCLE_STAGE_LABELS,\n CRM_ACTIVITY_KINDS,\n CRM_LEAD_STATUSES,\n type ContactLifecycleStage,\n type CrmActivity,\n type CrmActivityKind,\n type CrmDeal,\n type CrmDealStage,\n type CrmLeadFields,\n type CrmLeadStatus,\n type CrmPipeline,\n type CrmTask,\n crmLeadStatus,\n dealStageById,\n isCrmActivityKind,\n isPipelineArchived,\n weightedDealAmountCents,\n} from './crm'\n\nconst DAY_MS = 24 * 60 * 60 * 1000\nconst WEEK_MS = 7 * DAY_MS\n\n/**\n * The periods a report can be read over.\n *\n * A fixed list rather than a free date range because every period here is\n * also a READ COST: the contact trend runs one count aggregate per week of\n * the period, and the closed-deal chart reads the deals closed within it. A\n * picker that offered \"all time\" would offer a read nobody has sized.\n */\nexport const CRM_REPORT_PERIODS = ['7d', '30d', '90d', 'month'] as const\n\nexport type CrmReportPeriod = (typeof CRM_REPORT_PERIODS)[number]\n\nexport const CRM_REPORT_PERIOD_LABELS: Record<CrmReportPeriod, string> = {\n '7d': 'Last 7 days',\n '30d': 'Last 30 days',\n '90d': 'Last 90 days',\n month: 'This month',\n}\n\n/**\n * A period and the period before it, as half-open millisecond ranges\n * `[from, to)`.\n *\n * The previous period is what a delta is measured against, so it is always\n * the same LENGTH as the current one and ends exactly where the current one\n * begins: \"this month\" against \"last month\", thirty days against the thirty\n * before them. A gap or an overlap between the two would make the comparison\n * a comparison of different things.\n */\nexport interface CrmReportRange {\n from: number\n to: number\n previousFrom: number\n previousTo: number\n}\n\n/**\n * The range a period names at a given moment.\n *\n * `to` is the moment itself, so a period is always \"up to now\" and a report\n * read twice in one sitting shows the same figures with the newer minutes\n * added. \"This month\" starts on the first of the LOCAL month, because that\n * is the month the reader sees on their calendar; the day periods count back\n * from the moment rather than from midnight, so \"last 7 days\" is a full\n * seven days rather than six and a fraction.\n */\nexport function crmReportRange(\n period: CrmReportPeriod,\n nowMs: number,\n): CrmReportRange {\n if (period === 'month') {\n const now = new Date(nowMs)\n const from = new Date(now.getFullYear(), now.getMonth(), 1).getTime()\n const previousFrom = new Date(\n now.getFullYear(),\n now.getMonth() - 1,\n 1,\n ).getTime()\n return { from, to: nowMs, previousFrom, previousTo: from }\n }\n const days = period === '7d' ? 7 : period === '30d' ? 30 : 90\n const from = nowMs - days * DAY_MS\n return {\n from,\n to: nowMs,\n previousFrom: from - days * DAY_MS,\n previousTo: from,\n }\n}\n\n/** One week of a period, `[start, end)`; the last week of a period may be short. */\nexport interface WeekBucketBounds {\n start: number\n end: number\n}\n\n/**\n * The weeks a range divides into: seven-day strides from `from`, the last\n * clipped at `to`.\n *\n * Strides from the START of the period rather than calendar weeks, so that\n * \"last 7 days\" is one bar and \"last 30 days\" is four full weeks and two\n * days — the period the reader picked, in the reader's units — instead of\n * whatever fragment of a Monday-to-Sunday week the period happened to start\n * inside. An empty or inverted range has no weeks.\n */\nexport function weekBuckets(from: number, to: number): WeekBucketBounds[] {\n const buckets: WeekBucketBounds[] = []\n if (!(to > from)) return buckets\n for (let start = from; start < to; start += WEEK_MS) {\n buckets.push({ start, end: Math.min(start + WEEK_MS, to) })\n }\n return buckets\n}\n\nexport interface WeekBucket<T> extends WeekBucketBounds {\n items: T[]\n}\n\n/**\n * The items of a range, grouped into its weeks.\n *\n * Half-open on every boundary, so an item at exactly midnight between two\n * weeks is in the later one and only the later one, and an item at `to` is\n * outside the period the way an item at `from` is inside it. Items with no\n * usable time are dropped rather than placed anywhere — an undated deal is\n * not a deal closed this week. Every week is returned, empty ones included,\n * because a chart wants a bar per week and a gap where a week had nothing is\n * a chart with the wrong number of bars.\n */\nexport function bucketByWeek<T>(\n items: readonly T[],\n atMs: (item: T) => number | null | undefined,\n from: number,\n to: number,\n): WeekBucket<T>[] {\n const buckets: WeekBucket<T>[] = weekBuckets(from, to).map((bounds) => ({\n ...bounds,\n items: [] as T[],\n }))\n if (!buckets.length) return buckets\n for (const item of items) {\n const at = atMs(item)\n if (typeof at !== 'number' || !Number.isFinite(at)) continue\n if (at < from || at >= to) continue\n const index = Math.min(\n buckets.length - 1,\n Math.floor((at - from) / WEEK_MS),\n )\n buckets[index].items.push(item)\n }\n return buckets\n}\n\n/** One step of the lifecycle funnel. */\nexport interface LifecycleFunnelStep {\n stage: ContactLifecycleStage\n label: string\n /** People AT this stage. */\n count: number\n /** People at this stage or any later one — everyone who got at least this far. */\n reached: number\n /**\n * `reached` over the previous step's `reached`, 0–1, or `null` on the first\n * step and wherever nobody reached the step before.\n */\n conversion: number | null\n}\n\nexport interface LifecycleFunnel {\n /** The progression, in `CONTACT_LIFECYCLE_STAGES` order, `other` left out. */\n steps: LifecycleFunnelStep[]\n /** People at the `other` stage, which is beside the funnel rather than in it. */\n other: number\n /** Every counted person, `other` included. */\n total: number\n}\n\n/**\n * The lifecycle funnel a set of per-stage counts describes.\n *\n * A funnel is read CUMULATIVELY: a customer was once a lead, so the \"lead\"\n * step is everyone who is a lead now or has moved past it, not only the\n * people currently sitting there. That is what makes the steps narrow as\n * they go down and what makes a step-to-step conversion mean something.\n * `other` is a stage a merchant chose for a person the funnel does not\n * describe, so it is reported beside the funnel and never folded into a\n * step. A conversion where the previous step was empty is `null` rather than\n * 0 or 100 — there is nothing to divide by, and either number would be read\n * as a measurement.\n */\nexport function funnelFromStages(\n counts: Partial<Record<ContactLifecycleStage, number>>,\n): LifecycleFunnel {\n const countOf = (stage: ContactLifecycleStage): number => {\n const value = Number(counts[stage] ?? 0)\n return Number.isFinite(value) && value > 0 ? Math.round(value) : 0\n }\n const progression = CONTACT_LIFECYCLE_STAGES.filter(\n (stage) => stage !== 'other',\n )\n const steps: LifecycleFunnelStep[] = []\n let reachedAfter = 0\n for (let index = progression.length - 1; index >= 0; index -= 1) {\n const stage = progression[index]\n const count = countOf(stage)\n const reached = count + reachedAfter\n steps.unshift({\n stage,\n label: CONTACT_LIFECYCLE_STAGE_LABELS[stage],\n count,\n reached,\n conversion: null,\n })\n reachedAfter = reached\n }\n for (let index = 1; index < steps.length; index += 1) {\n const previous = steps[index - 1].reached\n steps[index].conversion = previous > 0 ? steps[index].reached / previous : null\n }\n const other = countOf('other')\n return {\n steps,\n other,\n total: steps.reduce((sum, step) => sum + step.count, 0) + other,\n }\n}\n\n/** The open deals sitting in one stage of a pipeline. */\nexport interface PipelineStageTotal {\n stage: CrmDealStage\n count: number\n amountCents: number\n weightedCents: number\n}\n\nexport interface PipelineTotals {\n /** Open deals, wherever they sit. */\n count: number\n amountCents: number\n /** The forecast: each open deal at its stage's odds. */\n weightedCents: number\n /** The pipeline's open stages in order, every one present even when empty. */\n stages: PipelineStageTotal[]\n /**\n * Open deals in a stage the pipeline does not have — retired, renamed, or\n * a closed-kind stage the status disagrees with. Counted in the totals,\n * worth nothing to the forecast, and reported so a merchant can find them.\n */\n unplaced: { count: number; amountCents: number }\n}\n\ntype PipelineDeal = Pick<CrmDeal, 'status' | 'stageId' | 'amountCents'>\n\n/**\n * What a pipeline holds, by stage.\n *\n * Only OPEN deals: a won deal is revenue and a lost one is history, and a\n * pipeline report that counted either would show a stage full of deals that\n * cannot move. Only the pipeline's open-kind stages are rows, for the same\n * reason from the other side — a \"Won\" column in a pipeline chart is the\n * closed-deals report drawn in the wrong place. The weighting is\n * `weightedDealAmountCents`, applied here rather than restated.\n */\nexport function pipelineTotals(\n deals: readonly PipelineDeal[],\n pipeline: Pick<CrmPipeline, 'stages'> | null | undefined,\n): PipelineTotals {\n const stages = [...(pipeline?.stages ?? [])]\n .filter((stage) => stage.kind === 'open')\n .sort((a, b) => a.order - b.order)\n const rows = new Map<string, PipelineStageTotal>(\n stages.map((stage) => [\n stage.id,\n { stage, count: 0, amountCents: 0, weightedCents: 0 },\n ]),\n )\n const totals: PipelineTotals = {\n count: 0,\n amountCents: 0,\n weightedCents: 0,\n stages: stages.map((stage) => rows.get(stage.id) as PipelineStageTotal),\n unplaced: { count: 0, amountCents: 0 },\n }\n for (const deal of deals) {\n if (deal.status !== 'open') continue\n const amount = Math.max(0, Math.round(Number(deal.amountCents ?? 0) || 0))\n totals.count += 1\n totals.amountCents += amount\n const row = rows.get(deal.stageId)\n if (!row) {\n totals.unplaced.count += 1\n totals.unplaced.amountCents += amount\n continue\n }\n const weighted = weightedDealAmountCents(deal, row.stage)\n row.count += 1\n row.amountCents += amount\n row.weightedCents += weighted\n totals.weightedCents += weighted\n }\n return totals\n}\n\n/** How many months ahead the close-month forecast looks. */\nexport const FORECAST_MONTHS = 6\n\n/** One calendar month of the forecast, `[start, end)` in local time. */\nexport interface CloseMonthBucket {\n /** `2026-09` — stable across locales, what a row is keyed by. */\n key: string\n start: number\n end: number\n}\n\n/**\n * The next `months` calendar months from the one `nowMs` falls in, each\n * `[first of the month, first of the next)` on the reader's calendar.\n *\n * The current month is the first bucket, whole: a deal expected to close\n * on the 3rd when today is the 20th is late, not next month's, and a\n * forecast that started at \"now\" would drop it between the rows. Local\n * months rather than UTC, because `expectedCloseAtMs` is stored at local\n * noon of the day picked (`dateInputMs`) and a UTC boundary would put a\n * deal picked for the 1st into the month before in half the world.\n */\nexport function closeMonthBuckets(\n nowMs: number,\n months = FORECAST_MONTHS,\n): CloseMonthBucket[] {\n const now = new Date(nowMs)\n const buckets: CloseMonthBucket[] = []\n for (let offset = 0; offset < months; offset += 1) {\n const start = new Date(now.getFullYear(), now.getMonth() + offset, 1)\n const end = new Date(now.getFullYear(), now.getMonth() + offset + 1, 1)\n buckets.push({\n key: `${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, '0')}`,\n start: start.getTime(),\n end: end.getTime(),\n })\n }\n return buckets\n}\n\n/** One pipeline's open deals expected to close in one month. */\nexport interface ForecastCell {\n count: number\n amountCents: number\n weightedCents: number\n}\n\nexport interface PipelineForecast {\n pipelineId: string\n name: string\n /** One cell per bucket, in `buckets` order, every one present even when empty. */\n months: ForecastCell[]\n /** Open deals with no expected close — their own row, never a guess. */\n undated: ForecastCell\n /** Expected before the first bucket — overdue to close. */\n overdue: ForecastCell\n /** Expected past the last bucket. */\n later: ForecastCell\n total: ForecastCell\n}\n\nexport interface CloseMonthForecast {\n buckets: CloseMonthBucket[]\n pipelines: PipelineForecast[]\n /** Every pipeline's cells added together, in `buckets` order. */\n months: ForecastCell[]\n undated: ForecastCell\n overdue: ForecastCell\n later: ForecastCell\n total: ForecastCell\n}\n\ntype ForecastDeal = Pick<\n CrmDeal,\n 'status' | 'pipelineId' | 'stageId' | 'amountCents' | 'expectedCloseAtMs'\n>\n\nconst emptyCell = (): ForecastCell => ({ count: 0, amountCents: 0, weightedCents: 0 })\n\nfunction addToCell(cell: ForecastCell, amountCents: number, weightedCents: number) {\n cell.count += 1\n cell.amountCents += amountCents\n cell.weightedCents += weightedCents\n}\n\n/**\n * The open pipeline laid out by the month each deal is expected to close\n * (AGL-2620): per pipeline, one cell per month for the next\n * {@link FORECAST_MONTHS}, at face value and weighted by the deal's stage.\n *\n * Only open deals, for the reason `pipelineTotals` gives — a closed deal\n * has closed. A deal with no `expectedCloseAtMs` is its own row rather than\n * being dropped or dated \"now\": a forecast that quietly omitted undated\n * deals would understate the pipeline by exactly the deals nobody has\n * scheduled, which is the number a sales lead most wants to see. A deal\n * dated before the first month is overdue and a deal dated past the last\n * is \"later\"; both are reported so the rows add up to the pipeline.\n *\n * Every ACTIVE pipeline is a row even with nothing open — an empty\n * pipeline is a fact about the forecast too — while an archived one is a\n * row only if a deal still sits open in it. A deal in a pipeline the list\n * does not carry at all is grouped under its id with no name, so the\n * total still counts it; its stage cannot be resolved, so it weighs\n * nothing, as everywhere else.\n */\nexport function forecastByCloseMonth(\n deals: readonly ForecastDeal[],\n pipelines: ReadonlyArray<Pick<CrmPipeline, 'name' | 'stages' | 'archivedAt'> & { $id: string }>,\n nowMs: number,\n months = FORECAST_MONTHS,\n): CloseMonthForecast {\n const buckets = closeMonthBuckets(nowMs, months)\n const byId = new Map<string, PipelineForecast>()\n const pipelineRow = (pipelineId: string): PipelineForecast => {\n let row = byId.get(pipelineId)\n if (!row) {\n const pipeline = pipelines.find((entry) => entry.$id === pipelineId)\n row = {\n pipelineId,\n name: pipeline?.name ?? '',\n months: buckets.map(emptyCell),\n undated: emptyCell(),\n overdue: emptyCell(),\n later: emptyCell(),\n total: emptyCell(),\n }\n byId.set(pipelineId, row)\n }\n return row\n }\n for (const pipeline of pipelines) {\n if (!isPipelineArchived(pipeline)) pipelineRow(pipeline.$id)\n }\n\n const totals: CloseMonthForecast = {\n buckets,\n pipelines: [],\n months: buckets.map(emptyCell),\n undated: emptyCell(),\n overdue: emptyCell(),\n later: emptyCell(),\n total: emptyCell(),\n }\n const first = buckets[0]?.start ?? nowMs\n const last = buckets[buckets.length - 1]?.end ?? nowMs\n for (const deal of deals) {\n if (deal.status !== 'open') continue\n const pipelineId = String(deal.pipelineId ?? '')\n const row = pipelineRow(pipelineId)\n const pipeline = pipelines.find((entry) => entry.$id === pipelineId)\n const amount = Math.max(0, Math.round(Number(deal.amountCents ?? 0) || 0))\n const weighted = weightedDealAmountCents(deal, dealStageById(pipeline, deal.stageId))\n const at = deal.expectedCloseAtMs\n const cells: Array<[ForecastCell, ForecastCell]> = [[row.total, totals.total]]\n if (typeof at !== 'number' || !Number.isFinite(at)) {\n cells.push([row.undated, totals.undated])\n } else if (at < first) {\n cells.push([row.overdue, totals.overdue])\n } else if (at >= last) {\n cells.push([row.later, totals.later])\n } else {\n const index = buckets.findIndex((bucket) => at >= bucket.start && at < bucket.end)\n cells.push([row.months[index], totals.months[index]])\n }\n for (const [own, all] of cells) {\n addToCell(own, amount, weighted)\n addToCell(all, amount, weighted)\n }\n }\n totals.pipelines = [...byId.values()]\n return totals\n}\n\n/**\n * How many of a list share each key, most common first.\n *\n * Ties break on the key so two renders of the same data draw the same order.\n * An item with no key is not \"a key called empty\": a contact whose facet\n * names no source is left out of a by-source chart rather than drawn as a\n * bar labeled with nothing.\n */\nexport function tally<T>(\n items: readonly T[],\n keyOf: (item: T) => string | null | undefined,\n): Array<{ key: string; count: number }> {\n const counts = new Map<string, number>()\n for (const item of items) {\n const key = keyOf(item)\n if (!key) continue\n counts.set(key, (counts.get(key) ?? 0) + 1)\n }\n return [...counts.entries()]\n .map(([key, count]) => ({ key, count }))\n .sort((a, b) => b.count - a.count || a.key.localeCompare(b.key))\n}\n\n/**\n * Percentage change against the previous period, to one decimal, or `null`\n * when the previous period had nothing.\n *\n * `null` rather than a number because every number is wrong: \"+100%\" reads\n * as growth, \"+0%\" reads as flat, and a first contact has no growth rate.\n * The tile that renders this draws nothing for `null`, which is the honest\n * rendering of \"there is no baseline\".\n */\nexport function deltaPercent(current: number, previous: number): number | null {\n if (!(previous > 0)) return null\n return Math.round(((current - previous) / previous) * 1000) / 10\n}\n\n/**\n * The currency a set of deals is in, and whether they are actually in more\n * than one.\n *\n * A pipeline total is a sum, and a sum of dollars and euros is a number\n * with no unit. The reports still show that number — a merchant with one\n * stray euro deal wants the pipeline figure, not a refusal — but they say\n * it is mixed. The most common currency names the total; `usd` is what a\n * deal with no currency means, per the model.\n */\nexport function currencyOfDeals(\n deals: ReadonlyArray<Pick<CrmDeal, 'currency'>>,\n): { currency: string; mixed: boolean } {\n const ranked = tally(deals, (deal) =>\n String(deal.currency || 'usd').toLowerCase(),\n )\n return { currency: ranked[0]?.key ?? 'usd', mixed: ranked.length > 1 }\n}\n\n/**\n * The local calendar day a moment falls in, `[start, end)`.\n *\n * The same day `taskDueState` decides \"today\" on, so a count of tasks due\n * today and the list's \"today\" chip agree about the same task at 11 p.m.\n */\nexport function localDayBounds(nowMs: number): { start: number; end: number } {\n const now = new Date(nowMs)\n const start = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)\n return { start: start.getTime(), end: end.getTime() }\n}\n\n/*==========================================\n * ACTIVITY BY TEAMMATE (AGL-2624)\n *=========================================*/\n\n/** One teammate's row of the activity leaderboard. */\nexport interface ActivityLeaderboardRow {\n /**\n * The account uid, or `''` for work no person did: an activity an\n * automation logged, a task ticked off with nobody assigned. One row\n * rather than a row per automation, because the leaderboard ranks the\n * team, and \"nobody\" is the honest name for the rest.\n */\n uid: string\n /**\n * The name the newest of this uid's activities was signed with, or `null`\n * when none carried one — a uid that only completed tasks, say. The\n * activity's own `byName` because a scoped reader cannot resolve a\n * colleague's uid any other way; see `CrmActivity.byName`.\n */\n name: string | null\n /** Activities logged, by kind, every kind present even at zero. */\n kinds: Record<CrmActivityKind, number>\n /** Every activity logged, whatever its kind. */\n activities: number\n /** Tasks ticked off. */\n tasksDone: number\n}\n\n/** An activity as stored — `kind` is whatever was written, not only what the model names. */\ntype LeaderboardActivity = Pick<CrmActivity, 'byUid' | 'atMs' | 'byName'> & {\n kind: string\n}\ntype LeaderboardTask = Pick<CrmTask, 'assigneeUid' | 'completedByUid'>\n\n/**\n * Who did what: the activities and the completed tasks of a period,\n * grouped by the person who did them, busiest first.\n *\n * An activity is credited to whoever logged it. A task is credited to\n * whoever COMPLETED it when the route stamped that, and to its assignee\n * otherwise: the leaderboard measures work done, and a manager closing out\n * a departed teammate's list did that work. A kind the model does not\n * name counts as `other` rather than vanishing — the activity happened.\n *\n * Busiest means activities plus tasks; ties break on activities, then on\n * the uid, so two renders of one window draw one order.\n */\nexport function activityLeaderboard(\n activities: readonly LeaderboardActivity[],\n tasksDone: readonly LeaderboardTask[],\n): ActivityLeaderboardRow[] {\n const rows = new Map<string, ActivityLeaderboardRow>()\n /** When each row's name was signed, so a newer signature replaces an older one. */\n const namedAtMs = new Map<string, number>()\n const rowFor = (uid: string) => {\n let row = rows.get(uid)\n if (!row) {\n row = {\n uid,\n name: null,\n kinds: Object.fromEntries(\n CRM_ACTIVITY_KINDS.map((kind) => [kind, 0]),\n ) as Record<CrmActivityKind, number>,\n activities: 0,\n tasksDone: 0,\n }\n rows.set(uid, row)\n }\n return row\n }\n for (const activity of activities) {\n const row = rowFor(String(activity.byUid ?? ''))\n const kind = isCrmActivityKind(activity.kind) ? activity.kind : 'other'\n row.kinds[kind] += 1\n row.activities += 1\n const name = String(activity.byName ?? '').trim()\n const atMs = Number(activity.atMs)\n if (\n name &&\n Number.isFinite(atMs) &&\n atMs > (namedAtMs.get(row.uid) ?? Number.NEGATIVE_INFINITY)\n ) {\n row.name = name\n namedAtMs.set(row.uid, atMs)\n }\n }\n for (const task of tasksDone) {\n rowFor(String(task.completedByUid || task.assigneeUid || '')).tasksDone += 1\n }\n return [...rows.values()].sort(\n (a, b) =>\n b.activities + b.tasksDone - (a.activities + a.tasksDone) ||\n b.activities - a.activities ||\n a.uid.localeCompare(b.uid),\n )\n}\n\n/*==========================================\n * LEAD FUNNEL (AGL-2624)\n *=========================================*/\n\n/** The key a lead unqualified with no reason is tallied under. */\nexport const LEAD_NO_REASON_KEY = '$none'\nexport const LEAD_NO_REASON_LABEL = 'No reason given'\n\nexport interface LeadFunnelReason {\n /** The reason, case-folded and whitespace-collapsed, or {@link LEAD_NO_REASON_KEY}. */\n key: string\n /** The reason as it was first written. */\n label: string\n count: number\n}\n\nexport interface LeadFunnel {\n /** Every lead handed in. */\n total: number\n /** Leads at each status now, every status present. An absent status is `new`. */\n byStatus: Record<CrmLeadStatus, number>\n /** `new` and `working` — the leads still needing somebody. */\n open: number\n /** Qualified over every lead, 0–1, or `null` when there were none. */\n qualifiedRate: number | null\n /** Unqualified over every lead, 0–1, or `null` when there were none. */\n unqualifiedRate: number | null\n /** Why leads were unqualified, most common first. */\n reasons: LeadFunnelReason[]\n}\n\n/**\n * Where a set of leads stands, and why the ones that were closed without\n * converting were closed.\n *\n * A status breakdown rather than a cumulative funnel, because a lead's\n * history is not on the document — only where it is now — and a lead can\n * be qualified straight from `new` without ever having been `working`. A\n * step that claimed \"reached working\" would be inventing a past. The two\n * rates are over EVERY lead handed in, so a period in which half the leads\n * are still untouched reads as half unconverted, which is what it is.\n *\n * Reasons are free text — the unqualify dialog requires one but does not\n * offer a list — so `Too small` and `too small` are one reason and the\n * label is whichever spelling came first. An unqualified lead written past\n * the dialog with no reason is counted under its own key rather than\n * dropped: the lead was closed, and \"no reason given\" is the count a\n * manager wants to see.\n */\nexport function leadFunnel(\n leads: ReadonlyArray<Pick<CrmLeadFields, 'status' | 'unqualifiedReason'>>,\n): LeadFunnel {\n const byStatus = Object.fromEntries(\n CRM_LEAD_STATUSES.map((status) => [status, 0]),\n ) as Record<CrmLeadStatus, number>\n const reasons = new Map<string, LeadFunnelReason>()\n for (const lead of leads) {\n const status = crmLeadStatus(lead)\n byStatus[status] += 1\n if (status !== 'unqualified') continue\n const label = String(lead.unqualifiedReason ?? '').replace(/\\s+/g, ' ').trim()\n const key = label ? label.toLowerCase() : LEAD_NO_REASON_KEY\n const row = reasons.get(key) ?? {\n key,\n label: label || LEAD_NO_REASON_LABEL,\n count: 0,\n }\n row.count += 1\n reasons.set(key, row)\n }\n const total = leads.length\n return {\n total,\n byStatus,\n open: byStatus.new + byStatus.working,\n qualifiedRate: total > 0 ? byStatus.qualified / total : null,\n unqualifiedRate: total > 0 ? byStatus.unqualified / total : null,\n reasons: [...reasons.values()].sort(\n (a, b) => b.count - a.count || a.label.localeCompare(b.label),\n ),\n }\n}\n\n/**\n * The leads still needing somebody, from two server counts: every lead,\n * less the ones closed one way or the other.\n *\n * Subtraction rather than a count of the open statuses because a lead\n * nobody has touched carries NO status field — see `crmLeadStatus` — and\n * Firestore cannot select a document by a field's absence. The closed\n * statuses are always written, so they can be counted; what remains is\n * open. Clamped at zero for the moment between the two counts in which a\n * lead was closed.\n */\nexport function openLeadsFromCounts(total: number, closed: number): number {\n return Math.max(0, Math.round(Number(total) || 0) - Math.round(Number(closed) || 0))\n}\n\n/*==========================================\n * CONVERSION BY SOURCE (AGL-2624)\n *=========================================*/\n\n/**\n * The stages that mean a person has bought, or better.\n *\n * `customer` and `evangelist` only. `other` sits after `customer` in the\n * stage list so that no capture can overwrite it, but it names a step the\n * business chose and the report cannot know whether that step is past a\n * purchase; counting it would credit a source with a sale nobody recorded.\n */\nconst CUSTOMER_STAGES: ReadonlySet<ContactLifecycleStage> = new Set([\n 'customer',\n 'evangelist',\n])\n\ntype ConversionFacet = Pick<ContactFacet, 'sources' | 'lifecycleStage' | 'ordersCount'>\n\n/**\n * Whether this holder's view of a person says they have bought: a stage\n * at `customer` or past it, or an order on the books whatever the stage\n * says — an order path that never advanced the stage still sold something.\n */\nexport function contactIsCustomer(\n facet: Pick<ContactFacet, 'lifecycleStage' | 'ordersCount'>,\n): boolean {\n if (facet.lifecycleStage && CUSTOMER_STAGES.has(facet.lifecycleStage)) return true\n const orders = Number(facet.ordersCount ?? 0)\n return Number.isFinite(orders) && orders > 0\n}\n\nexport interface SourceConversionRow {\n source: ContactSource\n /** People this holder captured through the source. */\n captured: number\n /** Of those, the ones who are customers now. */\n customers: number\n /** `customers / captured`, 0–1. */\n rate: number\n}\n\nexport interface SourceConversion {\n /** One row per source anybody came through, most captured first. */\n rows: SourceConversionRow[]\n /** People with no source under this holder — no door to credit. */\n unsourced: number\n /** Every counted person who is a customer, once each. */\n customers: number\n /** Every person handed in. */\n total: number\n}\n\n/**\n * Which capture surfaces turn into customers.\n *\n * Read through one holder's facet, like every by-source figure: a source\n * is which of THIS business's doors the person came through. A person\n * captured two ways counts under both — the question is \"how well does\n * each door convert\", and a person who came through two is evidence for\n * both — so the rows can add to more than `total`, and `customers` counts\n * each person once for the tile above the table. Rows rank by captured,\n * not by rate, because a door with one visitor who bought is not the best\n * door.\n */\nexport function conversionBySource(\n facets: readonly ConversionFacet[],\n): SourceConversion {\n const rows = new Map<ContactSource, SourceConversionRow>()\n let unsourced = 0\n let customers = 0\n for (const facet of facets) {\n const isCustomer = contactIsCustomer(facet)\n if (isCustomer) customers += 1\n const sources = (Object.keys(facet.sources ?? {}) as ContactSource[]).filter(\n (source) => facet.sources?.[source],\n )\n if (!sources.length) {\n unsourced += 1\n continue\n }\n for (const source of sources) {\n const row = rows.get(source) ?? { source, captured: 0, customers: 0, rate: 0 }\n row.captured += 1\n if (isCustomer) row.customers += 1\n rows.set(source, row)\n }\n }\n return {\n rows: [...rows.values()]\n .map((row) => ({ ...row, rate: row.customers / row.captured }))\n .sort(\n (a, b) =>\n b.captured - a.captured ||\n b.customers - a.customers ||\n a.source.localeCompare(b.source),\n ),\n unsourced,\n customers,\n total: facets.length,\n }\n}\n\n/*==========================================\n * WON AND LOST, BY OWNER (AGL-2662)\n *=========================================*/\n\n/** A closed deal as the by-owner tally reads it. */\ntype OwnerOutcomeDeal = Pick<CrmDeal, 'ownerUid' | 'amountCents'>\n\n/** The key an unowned deal is tallied under — see {@link OwnerOutcomeRow.uid}. */\nexport const OWNER_UNASSIGNED_KEY = ''\n\nexport interface OwnerOutcomeRow {\n /**\n * The owner's account uid, or `''` for a deal nobody owns.\n *\n * One row for the unowned rather than none: a pipeline where a third of\n * what closed had no owner is a fact about the team, and dropping those\n * deals would make the rows add to less than the period's totals without\n * saying why.\n */\n uid: string\n won: number\n lost: number\n /** Value of what this owner won, in the deals' own minor units. */\n wonAmountCents: number\n /** Value of what they lost — the size of the miss, not a negative. */\n lostAmountCents: number\n /** Everything that closed for this owner, won or lost. */\n closed: number\n /**\n * Won over closed, 0–1, or `null` when nothing of theirs closed.\n *\n * Null rather than zero because the two read differently: an owner who\n * closed nothing has no rate, and rendering that as 0% would rank a\n * person who was not in the period below one who lost every deal.\n */\n winRate: number | null\n}\n\n/**\n * Who closed what, in the period's window.\n *\n * The by-owner reading of the same two sets the Won and lost card already\n * holds — everything that closed won, and everything that closed lost — so\n * this adds no read. Credited to `ownerUid`, the field the board and the\n * bulk bar both write, and never to the creator: a deal reassigned mid-cycle\n * belongs to whoever carried it at the close, which is what \"by owner\"\n * is asked to mean when a manager reads it.\n *\n * Ranked by how much CLOSED rather than by win rate, for the reason\n * `conversionBySource` ranks by captured: an owner who closed one deal and\n * won it is not the best closer on the team. Ties break on wins, then on\n * the uid, so two renders of one window draw one order.\n */\nexport function wonLostByOwner(\n won: readonly OwnerOutcomeDeal[],\n lost: readonly OwnerOutcomeDeal[],\n): OwnerOutcomeRow[] {\n const rows = new Map<string, OwnerOutcomeRow>()\n const rowFor = (uid: string) => {\n let row = rows.get(uid)\n if (!row) {\n row = {\n uid,\n won: 0,\n lost: 0,\n wonAmountCents: 0,\n lostAmountCents: 0,\n closed: 0,\n winRate: null,\n }\n rows.set(uid, row)\n }\n return row\n }\n /** A missing or negative amount counts as nothing, never as a debit. */\n const cents = (deal: OwnerOutcomeDeal) =>\n Math.max(0, Number(deal.amountCents ?? 0) || 0)\n for (const deal of won) {\n const row = rowFor(String(deal.ownerUid ?? OWNER_UNASSIGNED_KEY))\n row.won += 1\n row.closed += 1\n row.wonAmountCents += cents(deal)\n }\n for (const deal of lost) {\n const row = rowFor(String(deal.ownerUid ?? OWNER_UNASSIGNED_KEY))\n row.lost += 1\n row.closed += 1\n row.lostAmountCents += cents(deal)\n }\n return [...rows.values()]\n .map((row) => ({\n ...row,\n winRate: row.closed ? row.won / row.closed : null,\n }))\n .sort(\n (a, b) => b.closed - a.closed || b.won - a.won || a.uid.localeCompare(b.uid),\n )\n}\n"],"names":["CONTACT_LIFECYCLE_STAGES","CONTACT_LIFECYCLE_STAGE_LABELS","CRM_ACTIVITY_KINDS","CRM_LEAD_STATUSES","crmLeadStatus","dealStageById","isCrmActivityKind","isPipelineArchived","weightedDealAmountCents","DAY_MS","WEEK_MS","CRM_REPORT_PERIODS","CRM_REPORT_PERIOD_LABELS","month","crmReportRange","period","nowMs","now","Date","from","getFullYear","getMonth","getTime","previousFrom","to","previousTo","days","weekBuckets","buckets","start","push","end","Math","min","bucketByWeek","items","atMs","map","bounds","length","item","at","Number","isFinite","index","floor","funnelFromStages","counts","countOf","stage","value","round","progression","filter","steps","reachedAfter","count","reached","unshift","label","conversion","previous","other","total","reduce","sum","step","pipelineTotals","deals","pipeline","stages","kind","sort","a","b","order","rows","Map","id","amountCents","weightedCents","totals","get","unplaced","deal","status","amount","max","row","stageId","weighted","FORECAST_MONTHS","closeMonthBuckets","months","offset","key","String","padStart","emptyCell","addToCell","cell","forecastByCloseMonth","pipelines","byId","pipelineRow","pipelineId","find","entry","$id","name","undated","overdue","later","set","first","last","expectedCloseAtMs","cells","findIndex","bucket","own","all","values","tally","keyOf","entries","localeCompare","deltaPercent","current","currencyOfDeals","ranked","currency","toLowerCase","mixed","localDayBounds","getDate","activityLeaderboard","activities","tasksDone","namedAtMs","rowFor","uid","kinds","Object","fromEntries","activity","byUid","byName","trim","NEGATIVE_INFINITY","task","completedByUid","assigneeUid","LEAD_NO_REASON_KEY","LEAD_NO_REASON_LABEL","leadFunnel","leads","byStatus","reasons","lead","unqualifiedReason","replace","open","new","working","qualifiedRate","qualified","unqualifiedRate","unqualified","openLeadsFromCounts","closed","CUSTOMER_STAGES","Set","contactIsCustomer","facet","lifecycleStage","has","orders","ordersCount","conversionBySource","facets","unsourced","customers","isCustomer","sources","keys","source","captured","rate","OWNER_UNASSIGNED_KEY","wonLostByOwner","won","lost","wonAmountCents","lostAmountCents","winRate","cents","ownerUid"],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* A task's reminder at its own time (AGL-2659).
|
|
19
|
+
*
|
|
20
|
+
* The daily digest says once a morning what is late and what is due today;
|
|
21
|
+
* nothing said "the 3:00 call is at 3:00". A task now carries `remindAtMs`
|
|
22
|
+
* — the due time unless a person moved it, `null` for none — and an hourly
|
|
23
|
+
* route reads every open task whose reminder has come due and tells the
|
|
24
|
+
* assignee once, in the console and by mail. Every judgment here is pure so
|
|
25
|
+
* the three writers of the field (the task routes, the REST resource, the
|
|
26
|
+
* drawer) and the route that reads it agree on what a reminder is.
|
|
27
|
+
*
|
|
28
|
+
* ## Which reminder a save leaves
|
|
29
|
+
*
|
|
30
|
+
* A caller that SAYS what the reminder is gets what it said. A caller that
|
|
31
|
+
* says nothing gets the rule: a new task's reminder is its due time, and an
|
|
32
|
+
* edit that moves the due date moves a reminder that still sat on the old
|
|
33
|
+
* due time — the reminder was never a choice of its own, so it follows —
|
|
34
|
+
* while a reminder somebody set to a time of their own stays where they put
|
|
35
|
+
* it. The one thing an edit never does is invent a reminder for a task that
|
|
36
|
+
* has none.
|
|
37
|
+
*/
|
|
38
|
+
import type { CrmTask } from './crm';
|
|
39
|
+
/**
|
|
40
|
+
* The most due reminders one org's sweep reads per run.
|
|
41
|
+
*
|
|
42
|
+
* NEWEST FIRST, which is the whole reason a ceiling is safe here. A sent
|
|
43
|
+
* reminder keeps its `remindAtMs` — the drawer shows when it fired — so the
|
|
44
|
+
* range "reminders up to now" holds every reminder an open task ever had,
|
|
45
|
+
* and a window of the OLDEST five hundred would fill with ones already sent
|
|
46
|
+
* and never reach a new one. The newest five hundred always begin with what
|
|
47
|
+
* fell due since the last run; the sent ones sit behind them.
|
|
48
|
+
*/
|
|
49
|
+
export declare const CRM_TASK_REMINDER_CEILING = 500;
|
|
50
|
+
/** The two fields the rule reads off a stored task, or off nothing on create. */
|
|
51
|
+
export interface CrmTaskReminderState {
|
|
52
|
+
dueAtMs: number | null;
|
|
53
|
+
remindAtMs: number | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The reminder a save leaves on the task — see the module comment.
|
|
57
|
+
*
|
|
58
|
+
* `remindAtMs` is what the caller SAID: a time, `null` for "no reminder",
|
|
59
|
+
* `undefined` for nothing said. `previous` is the stored task, or `null`
|
|
60
|
+
* when the task is being created. A stored task from before reminders
|
|
61
|
+
* existed carries no `remindAtMs` at all; it reads as `null` here — no
|
|
62
|
+
* reminder — rather than as one that follows the due date, because a task
|
|
63
|
+
* nobody ever set a reminder on should not start reminding the day its
|
|
64
|
+
* title is corrected.
|
|
65
|
+
*/
|
|
66
|
+
export declare function crmTaskReminderAfterEdit(input: {
|
|
67
|
+
dueAtMs: number | null;
|
|
68
|
+
remindAtMs?: number | null;
|
|
69
|
+
previous: CrmTaskReminderState | null;
|
|
70
|
+
}): number | null;
|
|
71
|
+
/** Whether a task's reminder is still owed: set, and not yet handled. */
|
|
72
|
+
export declare function crmTaskReminderPending(task: Pick<CrmTask, 'remindAtMs' | 'reminderSentAtMs'>): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Whether the runner owes this task a reminder now: open, pending, and the
|
|
75
|
+
* reminder's time has come. The query reads the first and third off the
|
|
76
|
+
* index; the second is a fact about a field's absence, which a query cannot
|
|
77
|
+
* select on, so the runner asks it here over the page it read.
|
|
78
|
+
*/
|
|
79
|
+
export declare function crmTaskReminderDue(task: Pick<CrmTask, 'status' | 'remindAtMs' | 'reminderSentAtMs'>, nowMs: number): boolean;
|
|
80
|
+
/** A due reminder as the runner carries it: the fields a line and a link need. */
|
|
81
|
+
export interface CrmReminderTask extends Pick<CrmTask, 'title' | 'kind' | 'contactId' | 'companyId' | 'dealId'> {
|
|
82
|
+
id: string;
|
|
83
|
+
dueAtMs: number | null;
|
|
84
|
+
remindAtMs: number;
|
|
85
|
+
assigneeUid: string;
|
|
86
|
+
/** The site the task was created on, or `''` for the organization's own. */
|
|
87
|
+
hostId: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The console path a reminder opens: the record the task is for, in the
|
|
91
|
+
* hub, or the tasks list when it is for nobody in particular — the same
|
|
92
|
+
* page the assignment notification opens, in the `/{hostDocId}/rest` shape
|
|
93
|
+
* every host notification uses and the `/org/rest` shape the console
|
|
94
|
+
* rewrites onto the organization's own hub for a task with no site.
|
|
95
|
+
*/
|
|
96
|
+
export declare function crmTaskReminderLink(hostId: string | null, task: Pick<CrmTask, 'contactId' | 'companyId' | 'dealId'>): string;
|
|
97
|
+
/** `Wed, Sep 9, 3:00 PM` in a named zone — the words a reminder says a time with. */
|
|
98
|
+
export declare function crmTaskReminderWhen(ms: number, timeZone: string): string;
|
|
99
|
+
/**
|
|
100
|
+
* The one line the console notification is: the title and when the task is
|
|
101
|
+
* due. A reminder set on a task with no due date says only the title —
|
|
102
|
+
* the person chose the time; the task itself has none.
|
|
103
|
+
*/
|
|
104
|
+
export declare function composeCrmTaskReminderBody(task: Pick<CrmReminderTask, 'title' | 'dueAtMs'>, timeZone: string): string;
|
|
105
|
+
/**
|
|
106
|
+
* One mail per member per run, so five tasks due at nine o'clock are one
|
|
107
|
+
* message and not five. The subject names the task when there is one.
|
|
108
|
+
*/
|
|
109
|
+
export declare function composeCrmTaskReminderSubject(tasks: ReadonlyArray<Pick<CrmReminderTask, 'title'>>): string;
|
|
110
|
+
/** What the reminder email needs from the route besides the tasks. */
|
|
111
|
+
export interface CrmTaskReminderEmailInput {
|
|
112
|
+
tasks: readonly CrmReminderTask[];
|
|
113
|
+
timeZone: string;
|
|
114
|
+
/** The product name the mail reads as — the org's brand when white-labeled. */
|
|
115
|
+
productName: string;
|
|
116
|
+
/** The console page a task opens on, absolute. */
|
|
117
|
+
taskUrl: (task: CrmReminderTask) => string;
|
|
118
|
+
/** Account settings → Notifications, where the category can be muted. */
|
|
119
|
+
settingsUrl: string;
|
|
120
|
+
/** The brand's support line, already prefixed, or empty. */
|
|
121
|
+
supportLine?: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The plain-text email. Text rather than a designed template for the
|
|
125
|
+
* reason the digest gives: the body is a list, and `sendEmail` synthesizes
|
|
126
|
+
* the HTML part that makes its links live. Soonest due first, each task on
|
|
127
|
+
* its own line with its link under it, and the way out last.
|
|
128
|
+
*/
|
|
129
|
+
export declare function composeCrmTaskReminderEmailText(input: CrmTaskReminderEmailInput): string;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* A task's reminder at its own time (AGL-2659).
|
|
18
|
+
*
|
|
19
|
+
* The daily digest says once a morning what is late and what is due today;
|
|
20
|
+
* nothing said "the 3:00 call is at 3:00". A task now carries `remindAtMs`
|
|
21
|
+
* — the due time unless a person moved it, `null` for none — and an hourly
|
|
22
|
+
* route reads every open task whose reminder has come due and tells the
|
|
23
|
+
* assignee once, in the console and by mail. Every judgment here is pure so
|
|
24
|
+
* the three writers of the field (the task routes, the REST resource, the
|
|
25
|
+
* drawer) and the route that reads it agree on what a reminder is.
|
|
26
|
+
*
|
|
27
|
+
* ## Which reminder a save leaves
|
|
28
|
+
*
|
|
29
|
+
* A caller that SAYS what the reminder is gets what it said. A caller that
|
|
30
|
+
* says nothing gets the rule: a new task's reminder is its due time, and an
|
|
31
|
+
* edit that moves the due date moves a reminder that still sat on the old
|
|
32
|
+
* due time — the reminder was never a choice of its own, so it follows —
|
|
33
|
+
* while a reminder somebody set to a time of their own stays where they put
|
|
34
|
+
* it. The one thing an edit never does is invent a reminder for a task that
|
|
35
|
+
* has none.
|
|
36
|
+
*/ /**
|
|
37
|
+
* The most due reminders one org's sweep reads per run.
|
|
38
|
+
*
|
|
39
|
+
* NEWEST FIRST, which is the whole reason a ceiling is safe here. A sent
|
|
40
|
+
* reminder keeps its `remindAtMs` — the drawer shows when it fired — so the
|
|
41
|
+
* range "reminders up to now" holds every reminder an open task ever had,
|
|
42
|
+
* and a window of the OLDEST five hundred would fill with ones already sent
|
|
43
|
+
* and never reach a new one. The newest five hundred always begin with what
|
|
44
|
+
* fell due since the last run; the sent ones sit behind them.
|
|
45
|
+
*/ export const CRM_TASK_REMINDER_CEILING = 500;
|
|
46
|
+
/**
|
|
47
|
+
* The reminder a save leaves on the task — see the module comment.
|
|
48
|
+
*
|
|
49
|
+
* `remindAtMs` is what the caller SAID: a time, `null` for "no reminder",
|
|
50
|
+
* `undefined` for nothing said. `previous` is the stored task, or `null`
|
|
51
|
+
* when the task is being created. A stored task from before reminders
|
|
52
|
+
* existed carries no `remindAtMs` at all; it reads as `null` here — no
|
|
53
|
+
* reminder — rather than as one that follows the due date, because a task
|
|
54
|
+
* nobody ever set a reminder on should not start reminding the day its
|
|
55
|
+
* title is corrected.
|
|
56
|
+
*/ export function crmTaskReminderAfterEdit(input) {
|
|
57
|
+
var _previous_remindAtMs;
|
|
58
|
+
const { dueAtMs, remindAtMs, previous } = input;
|
|
59
|
+
if (remindAtMs !== undefined) return remindAtMs;
|
|
60
|
+
if (!previous) return dueAtMs;
|
|
61
|
+
const followed = typeof previous.remindAtMs === 'number' && previous.remindAtMs === previous.dueAtMs;
|
|
62
|
+
if (followed && dueAtMs !== previous.dueAtMs) return dueAtMs;
|
|
63
|
+
return (_previous_remindAtMs = previous.remindAtMs) != null ? _previous_remindAtMs : null;
|
|
64
|
+
}
|
|
65
|
+
/** Whether a task's reminder is still owed: set, and not yet handled. */ export function crmTaskReminderPending(task) {
|
|
66
|
+
return typeof task.remindAtMs === 'number' && task.reminderSentAtMs === undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Whether the runner owes this task a reminder now: open, pending, and the
|
|
70
|
+
* reminder's time has come. The query reads the first and third off the
|
|
71
|
+
* index; the second is a fact about a field's absence, which a query cannot
|
|
72
|
+
* select on, so the runner asks it here over the page it read.
|
|
73
|
+
*/ export function crmTaskReminderDue(task, nowMs) {
|
|
74
|
+
return task.status === 'open' && crmTaskReminderPending(task) && task.remindAtMs <= nowMs;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The console path a reminder opens: the record the task is for, in the
|
|
78
|
+
* hub, or the tasks list when it is for nobody in particular — the same
|
|
79
|
+
* page the assignment notification opens, in the `/{hostDocId}/rest` shape
|
|
80
|
+
* every host notification uses and the `/org/rest` shape the console
|
|
81
|
+
* rewrites onto the organization's own hub for a task with no site.
|
|
82
|
+
*/ export function crmTaskReminderLink(hostId, task) {
|
|
83
|
+
const base = hostId ? `/${hostId}/crm` : '/org/crm';
|
|
84
|
+
if (task.contactId) return `${base}/contacts/${encodeURIComponent(task.contactId)}`;
|
|
85
|
+
if (task.dealId) return `${base}/deals/${encodeURIComponent(task.dealId)}`;
|
|
86
|
+
if (task.companyId) return `${base}/companies/${encodeURIComponent(task.companyId)}`;
|
|
87
|
+
return `${base}/tasks`;
|
|
88
|
+
}
|
|
89
|
+
/** `Wed, Sep 9, 3:00 PM` in a named zone — the words a reminder says a time with. */ export function crmTaskReminderWhen(ms, timeZone) {
|
|
90
|
+
return new Date(ms).toLocaleString('en-US', {
|
|
91
|
+
timeZone,
|
|
92
|
+
weekday: 'short',
|
|
93
|
+
month: 'short',
|
|
94
|
+
day: 'numeric',
|
|
95
|
+
hour: 'numeric',
|
|
96
|
+
minute: '2-digit'
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The one line the console notification is: the title and when the task is
|
|
101
|
+
* due. A reminder set on a task with no due date says only the title —
|
|
102
|
+
* the person chose the time; the task itself has none.
|
|
103
|
+
*/ export function composeCrmTaskReminderBody(task, timeZone) {
|
|
104
|
+
return typeof task.dueAtMs === 'number' ? `${task.title} · due ${crmTaskReminderWhen(task.dueAtMs, timeZone)}` : task.title;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* One mail per member per run, so five tasks due at nine o'clock are one
|
|
108
|
+
* message and not five. The subject names the task when there is one.
|
|
109
|
+
*/ export function composeCrmTaskReminderSubject(tasks) {
|
|
110
|
+
if (tasks.length === 1) return `Reminder: ${tasks[0].title}`;
|
|
111
|
+
return `Reminder: ${tasks.length} tasks are due`;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The plain-text email. Text rather than a designed template for the
|
|
115
|
+
* reason the digest gives: the body is a list, and `sendEmail` synthesizes
|
|
116
|
+
* the HTML part that makes its links live. Soonest due first, each task on
|
|
117
|
+
* its own line with its link under it, and the way out last.
|
|
118
|
+
*/ export function composeCrmTaskReminderEmailText(input) {
|
|
119
|
+
var _input_supportLine;
|
|
120
|
+
const { tasks, timeZone, productName } = input;
|
|
121
|
+
const ordered = [
|
|
122
|
+
...tasks
|
|
123
|
+
].sort((a, b)=>{
|
|
124
|
+
var _a_dueAtMs, _b_dueAtMs;
|
|
125
|
+
return ((_a_dueAtMs = a.dueAtMs) != null ? _a_dueAtMs : a.remindAtMs) - ((_b_dueAtMs = b.dueAtMs) != null ? _b_dueAtMs : b.remindAtMs);
|
|
126
|
+
});
|
|
127
|
+
const lines = [
|
|
128
|
+
ordered.length === 1 ? `A reminder from ${productName}: this task is due.` : `A reminder from ${productName}: ${ordered.length} tasks are due.`,
|
|
129
|
+
''
|
|
130
|
+
];
|
|
131
|
+
for (const task of ordered){
|
|
132
|
+
lines.push(`- ${composeCrmTaskReminderBody(task, timeZone)}`);
|
|
133
|
+
lines.push(` ${input.taskUrl(task)}`);
|
|
134
|
+
}
|
|
135
|
+
lines.push('', 'You get task reminders because the Forms & bookings category is on in ' + `your notification settings: ${input.settingsUrl}`);
|
|
136
|
+
return lines.join('\n') + ((_input_supportLine = input.supportLine) != null ? _input_supportLine : '');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=crm-task-reminders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/crm-task-reminders.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\n/**\n * A task's reminder at its own time (AGL-2659).\n *\n * The daily digest says once a morning what is late and what is due today;\n * nothing said \"the 3:00 call is at 3:00\". A task now carries `remindAtMs`\n * — the due time unless a person moved it, `null` for none — and an hourly\n * route reads every open task whose reminder has come due and tells the\n * assignee once, in the console and by mail. Every judgment here is pure so\n * the three writers of the field (the task routes, the REST resource, the\n * drawer) and the route that reads it agree on what a reminder is.\n *\n * ## Which reminder a save leaves\n *\n * A caller that SAYS what the reminder is gets what it said. A caller that\n * says nothing gets the rule: a new task's reminder is its due time, and an\n * edit that moves the due date moves a reminder that still sat on the old\n * due time — the reminder was never a choice of its own, so it follows —\n * while a reminder somebody set to a time of their own stays where they put\n * it. The one thing an edit never does is invent a reminder for a task that\n * has none.\n */\n\nimport type { CrmTask } from './crm'\n\n/**\n * The most due reminders one org's sweep reads per run.\n *\n * NEWEST FIRST, which is the whole reason a ceiling is safe here. A sent\n * reminder keeps its `remindAtMs` — the drawer shows when it fired — so the\n * range \"reminders up to now\" holds every reminder an open task ever had,\n * and a window of the OLDEST five hundred would fill with ones already sent\n * and never reach a new one. The newest five hundred always begin with what\n * fell due since the last run; the sent ones sit behind them.\n */\nexport const CRM_TASK_REMINDER_CEILING = 500\n\n/** The two fields the rule reads off a stored task, or off nothing on create. */\nexport interface CrmTaskReminderState {\n dueAtMs: number | null\n remindAtMs: number | null\n}\n\n/**\n * The reminder a save leaves on the task — see the module comment.\n *\n * `remindAtMs` is what the caller SAID: a time, `null` for \"no reminder\",\n * `undefined` for nothing said. `previous` is the stored task, or `null`\n * when the task is being created. A stored task from before reminders\n * existed carries no `remindAtMs` at all; it reads as `null` here — no\n * reminder — rather than as one that follows the due date, because a task\n * nobody ever set a reminder on should not start reminding the day its\n * title is corrected.\n */\nexport function crmTaskReminderAfterEdit(input: {\n dueAtMs: number | null\n remindAtMs?: number | null\n previous: CrmTaskReminderState | null\n}): number | null {\n const { dueAtMs, remindAtMs, previous } = input\n if (remindAtMs !== undefined) return remindAtMs\n if (!previous) return dueAtMs\n const followed =\n typeof previous.remindAtMs === 'number' && previous.remindAtMs === previous.dueAtMs\n if (followed && dueAtMs !== previous.dueAtMs) return dueAtMs\n return previous.remindAtMs ?? null\n}\n\n/** Whether a task's reminder is still owed: set, and not yet handled. */\nexport function crmTaskReminderPending(\n task: Pick<CrmTask, 'remindAtMs' | 'reminderSentAtMs'>,\n): boolean {\n return typeof task.remindAtMs === 'number' && task.reminderSentAtMs === undefined\n}\n\n/**\n * Whether the runner owes this task a reminder now: open, pending, and the\n * reminder's time has come. The query reads the first and third off the\n * index; the second is a fact about a field's absence, which a query cannot\n * select on, so the runner asks it here over the page it read.\n */\nexport function crmTaskReminderDue(\n task: Pick<CrmTask, 'status' | 'remindAtMs' | 'reminderSentAtMs'>,\n nowMs: number,\n): boolean {\n return (\n task.status === 'open' &&\n crmTaskReminderPending(task) &&\n (task.remindAtMs as number) <= nowMs\n )\n}\n\n/** A due reminder as the runner carries it: the fields a line and a link need. */\nexport interface CrmReminderTask\n extends Pick<CrmTask, 'title' | 'kind' | 'contactId' | 'companyId' | 'dealId'> {\n id: string\n dueAtMs: number | null\n remindAtMs: number\n assigneeUid: string\n /** The site the task was created on, or `''` for the organization's own. */\n hostId: string\n}\n\n/**\n * The console path a reminder opens: the record the task is for, in the\n * hub, or the tasks list when it is for nobody in particular — the same\n * page the assignment notification opens, in the `/{hostDocId}/rest` shape\n * every host notification uses and the `/org/rest` shape the console\n * rewrites onto the organization's own hub for a task with no site.\n */\nexport function crmTaskReminderLink(\n hostId: string | null,\n task: Pick<CrmTask, 'contactId' | 'companyId' | 'dealId'>,\n): string {\n const base = hostId ? `/${hostId}/crm` : '/org/crm'\n if (task.contactId) return `${base}/contacts/${encodeURIComponent(task.contactId)}`\n if (task.dealId) return `${base}/deals/${encodeURIComponent(task.dealId)}`\n if (task.companyId) return `${base}/companies/${encodeURIComponent(task.companyId)}`\n return `${base}/tasks`\n}\n\n/** `Wed, Sep 9, 3:00 PM` in a named zone — the words a reminder says a time with. */\nexport function crmTaskReminderWhen(ms: number, timeZone: string): string {\n return new Date(ms).toLocaleString('en-US', {\n timeZone,\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\n/**\n * The one line the console notification is: the title and when the task is\n * due. A reminder set on a task with no due date says only the title —\n * the person chose the time; the task itself has none.\n */\nexport function composeCrmTaskReminderBody(\n task: Pick<CrmReminderTask, 'title' | 'dueAtMs'>,\n timeZone: string,\n): string {\n return typeof task.dueAtMs === 'number'\n ? `${task.title} · due ${crmTaskReminderWhen(task.dueAtMs, timeZone)}`\n : task.title\n}\n\n/**\n * One mail per member per run, so five tasks due at nine o'clock are one\n * message and not five. The subject names the task when there is one.\n */\nexport function composeCrmTaskReminderSubject(\n tasks: ReadonlyArray<Pick<CrmReminderTask, 'title'>>,\n): string {\n if (tasks.length === 1) return `Reminder: ${tasks[0].title}`\n return `Reminder: ${tasks.length} tasks are due`\n}\n\n/** What the reminder email needs from the route besides the tasks. */\nexport interface CrmTaskReminderEmailInput {\n tasks: readonly CrmReminderTask[]\n timeZone: string\n /** The product name the mail reads as — the org's brand when white-labeled. */\n productName: string\n /** The console page a task opens on, absolute. */\n taskUrl: (task: CrmReminderTask) => string\n /** Account settings → Notifications, where the category can be muted. */\n settingsUrl: string\n /** The brand's support line, already prefixed, or empty. */\n supportLine?: string\n}\n\n/**\n * The plain-text email. Text rather than a designed template for the\n * reason the digest gives: the body is a list, and `sendEmail` synthesizes\n * the HTML part that makes its links live. Soonest due first, each task on\n * its own line with its link under it, and the way out last.\n */\nexport function composeCrmTaskReminderEmailText(input: CrmTaskReminderEmailInput): string {\n const { tasks, timeZone, productName } = input\n const ordered = [...tasks].sort(\n (a, b) => (a.dueAtMs ?? a.remindAtMs) - (b.dueAtMs ?? b.remindAtMs),\n )\n const lines: string[] = [\n ordered.length === 1\n ? `A reminder from ${productName}: this task is due.`\n : `A reminder from ${productName}: ${ordered.length} tasks are due.`,\n '',\n ]\n for (const task of ordered) {\n lines.push(`- ${composeCrmTaskReminderBody(task, timeZone)}`)\n lines.push(` ${input.taskUrl(task)}`)\n }\n lines.push(\n '',\n 'You get task reminders because the Forms & bookings category is on in ' +\n `your notification settings: ${input.settingsUrl}`,\n )\n return lines.join('\\n') + (input.supportLine ?? '')\n}\n"],"names":["CRM_TASK_REMINDER_CEILING","crmTaskReminderAfterEdit","input","previous","dueAtMs","remindAtMs","undefined","followed","crmTaskReminderPending","task","reminderSentAtMs","crmTaskReminderDue","nowMs","status","crmTaskReminderLink","hostId","base","contactId","encodeURIComponent","dealId","companyId","crmTaskReminderWhen","ms","timeZone","Date","toLocaleString","weekday","month","day","hour","minute","composeCrmTaskReminderBody","title","composeCrmTaskReminderSubject","tasks","length","composeCrmTaskReminderEmailText","productName","ordered","sort","a","b","lines","push","taskUrl","settingsUrl","join","supportLine"],"mappings":"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"}
|