@aglyn/aglyn 1.0.0-beta.218 → 1.0.0-beta.220
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -12
- package/src/lib/app-utils/actions.d.ts +26 -433
- package/src/lib/app-utils/actions.js +108 -633
- package/src/lib/app-utils/actions.js.map +1 -1
- package/src/lib/app-utils/activity-presenter.d.ts +3 -3
- package/src/lib/app-utils/activity-presenter.js +36 -52
- package/src/lib/app-utils/activity-presenter.js.map +1 -1
- package/src/lib/app-utils/analytics-events.d.ts +1 -1
- package/src/lib/app-utils/analytics-events.js.map +1 -1
- package/src/lib/app-utils/analytics-provider.d.ts +182 -0
- package/src/lib/app-utils/analytics-provider.js +133 -0
- package/src/lib/app-utils/analytics-provider.js.map +1 -0
- package/src/lib/app-utils/api-plugins.d.ts +46 -0
- package/src/lib/app-utils/api-plugins.js +19 -0
- package/src/lib/app-utils/api-plugins.js.map +1 -1
- package/src/lib/app-utils/{marketplace-overrides.d.ts → artifact-overrides.d.ts} +9 -6
- package/src/lib/app-utils/{marketplace-overrides.js → artifact-overrides.js} +10 -7
- package/src/lib/app-utils/artifact-overrides.js.map +1 -0
- package/src/lib/app-utils/{marketplace-provenance.d.ts → artifact-provenance.d.ts} +12 -10
- package/src/lib/app-utils/{marketplace-provenance.js → artifact-provenance.js} +9 -7
- package/src/lib/app-utils/artifact-provenance.js.map +1 -0
- package/src/lib/app-utils/binding-token-catalog.d.ts +5 -13
- package/src/lib/app-utils/binding-token-catalog.js +17 -41
- package/src/lib/app-utils/binding-token-catalog.js.map +1 -1
- package/src/lib/app-utils/brand-support-line.d.ts +36 -0
- package/src/lib/app-utils/brand-support-line.js +38 -0
- package/src/lib/app-utils/brand-support-line.js.map +1 -0
- package/src/lib/app-utils/collection-entries.d.ts +2 -2
- package/src/lib/app-utils/collection-entries.js.map +1 -1
- package/src/lib/app-utils/consent-group-change.d.ts +4 -0
- package/src/lib/app-utils/consent-group-change.js.map +1 -1
- package/src/lib/app-utils/consent-groups.d.ts +1 -1
- package/src/lib/app-utils/consent-groups.js.map +1 -1
- package/src/lib/app-utils/console-routes.d.ts +15 -42
- package/src/lib/app-utils/console-routes.js +10 -39
- package/src/lib/app-utils/console-routes.js.map +1 -1
- package/src/lib/app-utils/contacts.d.ts +24 -2
- package/src/lib/app-utils/contacts.js +21 -5
- package/src/lib/app-utils/contacts.js.map +1 -1
- package/src/lib/app-utils/container-membership.d.ts +93 -0
- package/src/lib/app-utils/container-membership.js +165 -0
- package/src/lib/app-utils/container-membership.js.map +1 -0
- package/src/lib/app-utils/crm-task-reminders.d.ts +23 -6
- package/src/lib/app-utils/crm-task-reminders.js +32 -11
- package/src/lib/app-utils/crm-task-reminders.js.map +1 -1
- package/src/lib/app-utils/crm.d.ts +24 -4
- package/src/lib/app-utils/crm.js +23 -0
- package/src/lib/app-utils/crm.js.map +1 -1
- package/src/lib/app-utils/cross-pool-desync.d.ts +39 -0
- package/src/lib/app-utils/cross-pool-desync.js +47 -0
- package/src/lib/app-utils/cross-pool-desync.js.map +1 -0
- package/src/lib/app-utils/csv-upload.d.ts +29 -0
- package/src/lib/app-utils/csv-upload.js +29 -0
- package/src/lib/app-utils/csv-upload.js.map +1 -0
- package/src/lib/app-utils/{dataset-csv.d.ts → csv.d.ts} +10 -43
- package/src/lib/app-utils/{dataset-csv.js → csv.js} +10 -81
- package/src/lib/app-utils/csv.js.map +1 -0
- package/src/lib/app-utils/dataset-models.d.ts +5 -5
- package/src/lib/app-utils/dataset-models.js +19 -2
- package/src/lib/app-utils/dataset-models.js.map +1 -1
- package/src/lib/app-utils/datasets.d.ts +65 -30
- package/src/lib/app-utils/datasets.js +74 -24
- package/src/lib/app-utils/datasets.js.map +1 -1
- package/src/lib/app-utils/docs-deflection.d.ts +6 -6
- package/src/lib/app-utils/docs-deflection.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +14 -8
- package/src/lib/app-utils/docs-help.generated.js +23 -6
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.d.ts +2 -2
- package/src/lib/app-utils/docs-index.generated.js +570 -346
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/docs-retrieval.d.ts +3 -3
- package/src/lib/app-utils/docs-retrieval.js +14 -10
- package/src/lib/app-utils/docs-retrieval.js.map +1 -1
- package/src/lib/app-utils/duplicate-resource.d.ts +4 -1
- package/src/lib/app-utils/duplicate-resource.js +7 -4
- package/src/lib/app-utils/duplicate-resource.js.map +1 -1
- package/src/lib/app-utils/dynamic-list-rule.d.ts +1 -1
- package/src/lib/app-utils/dynamic-list-rule.js +3 -3
- package/src/lib/app-utils/dynamic-list-rule.js.map +1 -1
- package/src/lib/app-utils/element-ui.d.ts +1 -1
- package/src/lib/app-utils/element-ui.js +1 -1
- package/src/lib/app-utils/element-ui.js.map +1 -1
- package/src/lib/app-utils/email-starter-policy.js +3 -3
- package/src/lib/app-utils/email-starter-policy.js.map +1 -1
- package/src/lib/app-utils/email-text.d.ts +80 -0
- package/src/lib/app-utils/email-text.js +189 -0
- package/src/lib/app-utils/email-text.js.map +1 -0
- package/src/lib/app-utils/{list-assignment-policy.d.ts → enrollment-basis.d.ts} +4 -0
- package/src/lib/app-utils/{list-assignment-policy.js → enrollment-basis.js} +5 -1
- package/src/lib/app-utils/enrollment-basis.js.map +1 -0
- package/src/lib/app-utils/entity-picker-context.d.ts +10 -5
- package/src/lib/app-utils/entity-picker-context.js.map +1 -1
- package/src/lib/app-utils/error-beacon.d.ts +75 -6
- package/src/lib/app-utils/error-beacon.js +160 -21
- package/src/lib/app-utils/error-beacon.js.map +1 -1
- package/src/lib/app-utils/expand-repeatables.d.ts +10 -10
- package/src/lib/app-utils/expand-repeatables.js +14 -14
- package/src/lib/app-utils/expand-repeatables.js.map +1 -1
- package/src/lib/app-utils/forms.js +2 -2
- package/src/lib/app-utils/forms.js.map +1 -1
- package/src/lib/app-utils/health-report.d.ts +2 -3
- package/src/lib/app-utils/health-report.js +8 -6
- package/src/lib/app-utils/health-report.js.map +1 -1
- package/src/lib/app-utils/host-events.d.ts +82 -0
- package/src/lib/app-utils/host-events.js +62 -0
- package/src/lib/app-utils/host-events.js.map +1 -0
- package/src/lib/app-utils/inbound-mail-domain.d.ts +27 -0
- package/src/lib/app-utils/inbound-mail-domain.js +31 -0
- package/src/lib/app-utils/inbound-mail-domain.js.map +1 -0
- package/src/lib/app-utils/lockdown.d.ts +11 -0
- package/src/lib/app-utils/lockdown.js +33 -3
- package/src/lib/app-utils/lockdown.js.map +1 -1
- package/src/lib/app-utils/margin-utilization.d.ts +4 -69
- package/src/lib/app-utils/margin-utilization.js +82 -69
- package/src/lib/app-utils/margin-utilization.js.map +1 -1
- package/src/lib/app-utils/member-email-aliases.d.ts +25 -0
- package/src/lib/app-utils/member-email-aliases.js +29 -0
- package/src/lib/app-utils/member-email-aliases.js.map +1 -1
- package/src/lib/app-utils/node-definition-sanitizer.d.ts +16 -16
- package/src/lib/app-utils/node-definition-sanitizer.js +24 -20
- package/src/lib/app-utils/node-definition-sanitizer.js.map +1 -1
- package/src/lib/app-utils/node-interactions.d.ts +7 -7
- package/src/lib/app-utils/node-interactions.js.map +1 -1
- package/src/lib/app-utils/notifications.d.ts +147 -47
- package/src/lib/app-utils/notifications.js +182 -44
- package/src/lib/app-utils/notifications.js.map +1 -1
- package/src/lib/app-utils/operator-alerts.d.ts +15 -1
- package/src/lib/app-utils/operator-alerts.js +47 -10
- package/src/lib/app-utils/operator-alerts.js.map +1 -1
- package/src/lib/app-utils/payments-configured.d.ts +8 -21
- package/src/lib/app-utils/payments-configured.js +8 -23
- package/src/lib/app-utils/payments-configured.js.map +1 -1
- package/src/lib/app-utils/plan-entitlements.d.ts +207 -306
- package/src/lib/app-utils/plan-entitlements.js +345 -528
- package/src/lib/app-utils/plan-entitlements.js.map +1 -1
- package/src/lib/app-utils/platform-brand.d.ts +10 -0
- package/src/lib/app-utils/platform-brand.js +14 -3
- package/src/lib/app-utils/platform-brand.js.map +1 -1
- package/src/lib/app-utils/platform-marketing-consent.d.ts +56 -2
- package/src/lib/app-utils/platform-marketing-consent.js +6 -5
- package/src/lib/app-utils/platform-marketing-consent.js.map +1 -1
- package/src/lib/app-utils/plugin-api-rate-limit.d.ts +7 -1
- package/src/lib/app-utils/plugin-api-rate-limit.js +11 -3
- package/src/lib/app-utils/plugin-api-rate-limit.js.map +1 -1
- package/src/lib/app-utils/plugin-bundle-checks.js +2 -2
- package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -1
- package/src/lib/app-utils/plugin-host-events.generated.d.ts +12 -0
- package/src/lib/app-utils/plugin-host-events.generated.js +169 -0
- package/src/lib/app-utils/plugin-host-events.generated.js.map +1 -0
- package/src/lib/app-utils/plugin-release-flags.generated.d.ts +12 -0
- package/src/lib/app-utils/plugin-release-flags.generated.js +106 -0
- package/src/lib/app-utils/plugin-release-flags.generated.js.map +1 -0
- package/src/lib/app-utils/plugin-starter-templates.generated.d.ts +12 -0
- package/src/lib/app-utils/plugin-starter-templates.generated.js +711 -0
- package/src/lib/app-utils/plugin-starter-templates.generated.js.map +1 -0
- package/src/lib/app-utils/realm-host-surface.generated.js +2 -0
- package/src/lib/app-utils/realm-host-surface.generated.js.map +1 -1
- package/src/lib/app-utils/release-flags.d.ts +14 -5
- package/src/lib/app-utils/release-flags.js +21 -125
- package/src/lib/app-utils/release-flags.js.map +1 -1
- package/src/lib/app-utils/repeat-query.d.ts +56 -0
- package/src/lib/app-utils/{dataset-query.js → repeat-query.js} +16 -16
- package/src/lib/app-utils/repeat-query.js.map +1 -0
- package/src/lib/app-utils/repeat-sources.d.ts +9 -0
- package/src/lib/app-utils/repeat-sources.js.map +1 -1
- package/src/lib/app-utils/retained-refusals.d.ts +73 -0
- package/src/lib/app-utils/retained-refusals.js +186 -0
- package/src/lib/app-utils/retained-refusals.js.map +1 -0
- package/src/lib/app-utils/scope-tokens.d.ts +27 -0
- package/src/lib/app-utils/scope-tokens.js +61 -0
- package/src/lib/app-utils/scope-tokens.js.map +1 -1
- package/src/lib/app-utils/screen-link-context.js +3 -3
- package/src/lib/app-utils/screen-link-context.js.map +1 -1
- package/src/lib/app-utils/screen-route.js +1 -1
- package/src/lib/app-utils/screen-route.js.map +1 -1
- package/src/lib/app-utils/seo-audit.d.ts +132 -0
- package/src/lib/app-utils/seo-audit.js +246 -0
- package/src/lib/app-utils/seo-audit.js.map +1 -0
- package/src/lib/app-utils/seo-keywords.d.ts +48 -0
- package/src/lib/app-utils/seo-keywords.js +60 -0
- package/src/lib/app-utils/seo-keywords.js.map +1 -0
- package/src/lib/app-utils/seo-page-facts.d.ts +66 -0
- package/src/lib/app-utils/seo-page-facts.js +155 -0
- package/src/lib/app-utils/seo-page-facts.js.map +1 -0
- package/src/lib/app-utils/seo-site-scan.d.ts +126 -0
- package/src/lib/app-utils/seo-site-scan.js +162 -0
- package/src/lib/app-utils/seo-site-scan.js.map +1 -0
- package/src/lib/app-utils/seo-title-variables.js +1 -1
- package/src/lib/app-utils/seo-title-variables.js.map +1 -1
- package/src/lib/app-utils/server.d.ts +13 -18
- package/src/lib/app-utils/server.js +33 -51
- package/src/lib/app-utils/server.js.map +1 -1
- package/src/lib/app-utils/site-interactions.d.ts +501 -0
- package/src/lib/app-utils/site-interactions.js +554 -0
- package/src/lib/app-utils/site-interactions.js.map +1 -0
- package/src/lib/app-utils/{marketplace-theme.d.ts → site-theme.d.ts} +13 -2
- package/src/lib/app-utils/{marketplace-theme.js → site-theme.js} +2 -2
- package/src/lib/app-utils/site-theme.js.map +1 -0
- package/src/lib/app-utils/sitemap.d.ts +4 -3
- package/src/lib/app-utils/sitemap.js +9 -7
- package/src/lib/app-utils/sitemap.js.map +1 -1
- package/src/lib/app-utils/stale-build-error.d.ts +51 -0
- package/src/lib/app-utils/stale-build-error.js +100 -1
- package/src/lib/app-utils/stale-build-error.js.map +1 -1
- package/src/lib/app-utils/starter-template-nodes.d.ts +137 -0
- package/src/lib/app-utils/starter-template-nodes.js +129 -0
- package/src/lib/app-utils/starter-template-nodes.js.map +1 -0
- package/src/lib/app-utils/starter-templates.d.ts +11 -34
- package/src/lib/app-utils/starter-templates.js +19 -230
- package/src/lib/app-utils/starter-templates.js.map +1 -1
- package/src/lib/app-utils/{email-topics.d.ts → subscription-topics.d.ts} +64 -48
- package/src/lib/app-utils/{email-topics.js → subscription-topics.js} +39 -109
- package/src/lib/app-utils/subscription-topics.js.map +1 -0
- package/src/lib/app-utils/tax-jurisdiction-figures.d.ts +118 -0
- package/src/lib/app-utils/tax-jurisdiction-figures.js +123 -0
- package/src/lib/app-utils/tax-jurisdiction-figures.js.map +1 -0
- package/src/lib/app-utils/theme-library.d.ts +10 -3
- package/src/lib/app-utils/theme-library.js +13 -4
- package/src/lib/app-utils/theme-library.js.map +1 -1
- package/src/lib/app-utils/usage-budget.d.ts +73 -188
- package/src/lib/app-utils/usage-budget.js +58 -194
- package/src/lib/app-utils/usage-budget.js.map +1 -1
- package/src/lib/app-utils/{campaign-attribution.d.ts → utm-attribution.d.ts} +11 -6
- package/src/lib/app-utils/{campaign-attribution.js → utm-attribution.js} +13 -8
- package/src/lib/app-utils/utm-attribution.js.map +1 -0
- package/src/lib/app-utils/{campaign-forwarding.d.ts → utm-forwarding.d.ts} +14 -14
- package/src/lib/app-utils/{campaign-forwarding.js → utm-forwarding.js} +26 -26
- package/src/lib/app-utils/utm-forwarding.js.map +1 -0
- package/src/lib/app-utils/{campaign-touch.d.ts → utm-touch.d.ts} +25 -17
- package/src/lib/app-utils/{campaign-touch.js → utm-touch.js} +38 -30
- package/src/lib/app-utils/utm-touch.js.map +1 -0
- package/src/lib/app-utils/variables.d.ts +9 -9
- package/src/lib/app-utils/variables.js.map +1 -1
- package/src/lib/app-utils/video-object.js +6 -5
- package/src/lib/app-utils/video-object.js.map +1 -1
- package/src/lib/app-utils/visitor-consent.d.ts +41 -164
- package/src/lib/app-utils/visitor-consent.js +66 -277
- package/src/lib/app-utils/visitor-consent.js.map +1 -1
- package/src/lib/app-utils/visitor-record-ceiling.d.ts +32 -0
- package/src/lib/app-utils/visitor-record-ceiling.js +39 -0
- package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -1
- package/src/lib/app-utils/web-vitals-rum.d.ts +7 -7
- package/src/lib/app-utils/web-vitals-rum.js +31 -63
- package/src/lib/app-utils/web-vitals-rum.js.map +1 -1
- package/src/lib/app-utils/where-used.d.ts +11 -6
- package/src/lib/app-utils/where-used.js +3 -8
- package/src/lib/app-utils/where-used.js.map +1 -1
- package/src/lib/foundation/definitions/components.types.d.ts +1 -1
- package/src/lib/foundation/definitions/components.types.js +1 -1
- package/src/lib/foundation/definitions/components.types.js.map +1 -1
- package/src/lib/foundation/definitions/org-billing.types.d.ts +11 -137
- package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
- package/src/lib/foundation/definitions/organization.types.d.ts +1 -3
- package/src/lib/foundation/definitions/organization.types.js.map +1 -1
- package/src/lib/foundation/definitions/platform.types.d.ts +11 -5
- package/src/lib/foundation/definitions/platform.types.js.map +1 -1
- package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +16 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.js +20 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -1
- package/src/lib/foundation/index.d.ts +1 -1
- package/src/lib/foundation/index.js.map +1 -1
- package/src/lib/plugin-manager/besigner-documents.d.ts +96 -0
- package/src/lib/plugin-manager/besigner-documents.js +61 -0
- package/src/lib/plugin-manager/besigner-documents.js.map +1 -0
- package/src/lib/plugin-manager/computed-variables.d.ts +71 -0
- package/src/lib/plugin-manager/computed-variables.js +74 -0
- package/src/lib/plugin-manager/computed-variables.js.map +1 -0
- package/src/lib/plugin-manager/custom-fields.d.ts +29 -2
- package/src/lib/plugin-manager/custom-fields.js +47 -1
- package/src/lib/plugin-manager/custom-fields.js.map +1 -1
- package/src/lib/plugin-manager/feature-plugins.d.ts +372 -99
- package/src/lib/plugin-manager/feature-plugins.js +126 -51
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +134 -0
- package/src/lib/plugin-manager/first-party-plugins.generated.js +840 -10
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/index.d.ts +0 -1
- package/src/lib/plugin-manager/index.js +0 -1
- package/src/lib/plugin-manager/index.js.map +1 -1
- package/src/lib/plugin-manager/plugin-activity-actions.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js +25 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-contact-capture.d.ts +9 -4
- package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
- package/src/lib/plugin-manager/plugin-containers.d.ts +68 -0
- package/src/lib/plugin-manager/plugin-containers.generated.d.ts +10 -0
- package/src/lib/plugin-manager/plugin-containers.generated.js +20 -0
- package/src/lib/plugin-manager/plugin-containers.generated.js.map +1 -0
- package/src/lib/plugin-manager/plugin-containers.js +27 -0
- package/src/lib/plugin-manager/plugin-containers.js.map +1 -0
- package/src/lib/plugin-manager/plugin-contributions.d.ts +1 -1
- package/src/lib/plugin-manager/plugin-contributions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-declarations-repair.d.ts +36 -0
- package/src/lib/plugin-manager/plugin-declarations-repair.js +64 -0
- package/src/lib/plugin-manager/plugin-declarations-repair.js.map +1 -0
- package/src/lib/plugin-manager/plugin-dependents.d.ts +101 -0
- package/src/lib/plugin-manager/plugin-dependents.js +75 -0
- package/src/lib/plugin-manager/plugin-dependents.js.map +1 -0
- package/src/lib/plugin-manager/plugin-distribution.d.ts +55 -0
- package/src/lib/plugin-manager/plugin-distribution.js +45 -0
- package/src/lib/plugin-manager/plugin-distribution.js.map +1 -0
- package/src/lib/plugin-manager/plugin-entitlements.d.ts +8 -0
- package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -1
- package/src/lib/plugin-manager/plugin-host-collections.d.ts +17 -0
- package/src/lib/plugin-manager/plugin-host-collections.js +6 -0
- package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
- package/src/lib/plugin-manager/plugin-host-resources.d.ts +133 -0
- package/src/lib/plugin-manager/plugin-host-resources.js +52 -0
- package/src/lib/plugin-manager/plugin-host-resources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-membership-detach.js +1 -1
- package/src/lib/plugin-manager/plugin-membership-detach.js.map +1 -1
- package/src/lib/plugin-manager/plugin-org-erasure.d.ts +28 -2
- package/src/lib/plugin-manager/plugin-org-erasure.js +61 -2
- package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -1
- package/src/lib/plugin-manager/plugin-plan-entitlements.d.ts +123 -0
- package/src/lib/plugin-manager/plugin-plan-entitlements.js +101 -0
- package/src/lib/plugin-manager/plugin-plan-entitlements.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-email-state.d.ts +69 -0
- package/src/lib/plugin-manager/plugin-record-email-state.js +32 -0
- package/src/lib/plugin-manager/plugin-record-email-state.js.map +1 -1
- package/src/lib/plugin-manager/plugin-record-index.d.ts +95 -0
- package/src/lib/plugin-manager/plugin-record-index.js +51 -0
- package/src/lib/plugin-manager/plugin-record-index.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-lists.d.ts +115 -0
- package/src/lib/plugin-manager/plugin-record-lists.js +75 -0
- package/src/lib/plugin-manager/plugin-record-lists.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-timeline.d.ts +42 -8
- package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -1
- package/src/lib/plugin-manager/plugin-record-written.d.ts +58 -0
- package/src/lib/plugin-manager/plugin-record-written.js +42 -0
- package/src/lib/plugin-manager/plugin-record-written.js.map +1 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.d.ts +213 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.js +194 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-beacons.d.ts +71 -0
- package/src/lib/plugin-manager/plugin-site-beacons.js +54 -0
- package/src/lib/plugin-manager/plugin-site-beacons.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-bundle.d.ts +145 -0
- package/src/lib/plugin-manager/plugin-site-bundle.js +78 -0
- package/src/lib/plugin-manager/plugin-site-bundle.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-cache.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-site-cache.js.map +1 -1
- package/src/lib/plugin-manager/plugin-site-export.d.ts +81 -0
- package/src/lib/plugin-manager/plugin-site-export.js +40 -0
- package/src/lib/plugin-manager/plugin-site-export.js.map +1 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.d.ts +88 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.js +29 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.js.map +1 -0
- package/src/lib/plugin-manager/plugin-subscription-topics.generated.d.ts +11 -0
- package/src/lib/plugin-manager/plugin-subscription-topics.generated.js +41 -0
- package/src/lib/plugin-manager/plugin-subscription-topics.generated.js.map +1 -0
- package/src/lib/plugin-manager/plugin-tax-profile.d.ts +20 -4
- package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -1
- package/src/lib/plugin-manager/plugin-tax-return-sources.d.ts +255 -0
- package/src/lib/plugin-manager/plugin-tax-return-sources.js +191 -0
- package/src/lib/plugin-manager/plugin-tax-return-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-template-sources.d.ts +75 -0
- package/src/lib/plugin-manager/plugin-template-sources.js +37 -0
- package/src/lib/plugin-manager/plugin-template-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-usage-axes.d.ts +319 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js +186 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -0
- package/src/lib/plugin-manager/plugin-usage-meters.d.ts +108 -0
- package/src/lib/plugin-manager/plugin-usage-meters.js +105 -0
- package/src/lib/plugin-manager/plugin-usage-meters.js.map +1 -0
- package/src/lib/plugin-manager/plugin-zones.d.ts +4 -5
- package/src/lib/plugin-manager/plugin-zones.js.map +1 -1
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js +2 -0
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -1
- package/src/lib/plugin-manager/record-captured-contact.d.ts +1 -14
- package/src/lib/plugin-manager/record-captured-contact.js +10 -32
- package/src/lib/plugin-manager/record-captured-contact.js.map +1 -1
- package/src/lib/plugin-manager/repeat-rows.d.ts +124 -0
- package/src/lib/plugin-manager/repeat-rows.js +108 -0
- package/src/lib/plugin-manager/repeat-rows.js.map +1 -0
- package/src/lib/plugin-manager/submission-record-target.d.ts +144 -0
- package/src/lib/plugin-manager/submission-record-target.js +125 -0
- package/src/lib/plugin-manager/submission-record-target.js.map +1 -0
- package/src/lib/plugin-manager/usage-alert-contributors.d.ts +76 -6
- package/src/lib/plugin-manager/usage-alert-contributors.js +23 -10
- package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -1
- package/src/lib/plugin-manager/video-embed-provider.d.ts +108 -0
- package/src/lib/plugin-manager/video-embed-provider.js +165 -0
- package/src/lib/plugin-manager/video-embed-provider.js.map +1 -0
- package/src/lib/types/nodes.d.ts +2 -2
- package/src/lib/types/nodes.js.map +1 -1
- package/src/lib/app-utils/assist-credits.d.ts +0 -482
- package/src/lib/app-utils/assist-credits.js +0 -509
- package/src/lib/app-utils/assist-credits.js.map +0 -1
- package/src/lib/app-utils/campaign-attribution.js.map +0 -1
- package/src/lib/app-utils/campaign-forwarding.js.map +0 -1
- package/src/lib/app-utils/campaign-membership.d.ts +0 -121
- package/src/lib/app-utils/campaign-membership.js +0 -189
- package/src/lib/app-utils/campaign-membership.js.map +0 -1
- package/src/lib/app-utils/campaign-touch.js.map +0 -1
- package/src/lib/app-utils/console-record-links.d.ts +0 -163
- package/src/lib/app-utils/console-record-links.js +0 -180
- package/src/lib/app-utils/console-record-links.js.map +0 -1
- package/src/lib/app-utils/contact-holder.d.ts +0 -68
- package/src/lib/app-utils/contact-holder.js +0 -129
- package/src/lib/app-utils/contact-holder.js.map +0 -1
- package/src/lib/app-utils/contact-merge.d.ts +0 -136
- package/src/lib/app-utils/contact-merge.js +0 -412
- package/src/lib/app-utils/contact-merge.js.map +0 -1
- package/src/lib/app-utils/crm-booking.d.ts +0 -75
- package/src/lib/app-utils/crm-booking.js +0 -133
- package/src/lib/app-utils/crm-booking.js.map +0 -1
- package/src/lib/app-utils/crm-csv.d.ts +0 -239
- package/src/lib/app-utils/crm-csv.js +0 -467
- package/src/lib/app-utils/crm-csv.js.map +0 -1
- package/src/lib/app-utils/crm-digest.d.ts +0 -185
- package/src/lib/app-utils/crm-digest.js +0 -272
- package/src/lib/app-utils/crm-digest.js.map +0 -1
- package/src/lib/app-utils/crm-inbound.d.ts +0 -245
- package/src/lib/app-utils/crm-inbound.js +0 -407
- package/src/lib/app-utils/crm-inbound.js.map +0 -1
- package/src/lib/app-utils/crm-reports.d.ts +0 -479
- package/src/lib/app-utils/crm-reports.js +0 -680
- package/src/lib/app-utils/crm-reports.js.map +0 -1
- package/src/lib/app-utils/dataset-csv.js.map +0 -1
- package/src/lib/app-utils/dataset-query.d.ts +0 -57
- package/src/lib/app-utils/dataset-query.js.map +0 -1
- package/src/lib/app-utils/email-topics.js.map +0 -1
- package/src/lib/app-utils/form-dataset-binding.d.ts +0 -50
- package/src/lib/app-utils/form-dataset-binding.js +0 -74
- package/src/lib/app-utils/form-dataset-binding.js.map +0 -1
- package/src/lib/app-utils/list-assignment-policy.js.map +0 -1
- package/src/lib/app-utils/list-import.d.ts +0 -158
- package/src/lib/app-utils/list-import.js +0 -332
- package/src/lib/app-utils/list-import.js.map +0 -1
- package/src/lib/app-utils/marketplace-listing-visibility.d.ts +0 -122
- package/src/lib/app-utils/marketplace-listing-visibility.js +0 -147
- package/src/lib/app-utils/marketplace-listing-visibility.js.map +0 -1
- package/src/lib/app-utils/marketplace-overrides.js.map +0 -1
- package/src/lib/app-utils/marketplace-provenance.js.map +0 -1
- package/src/lib/app-utils/marketplace-theme.js.map +0 -1
- package/src/lib/app-utils/marketplace-update-state.d.ts +0 -119
- package/src/lib/app-utils/marketplace-update-state.js +0 -132
- package/src/lib/app-utils/marketplace-update-state.js.map +0 -1
- package/src/lib/app-utils/publisher-agreement.d.ts +0 -289
- package/src/lib/app-utils/publisher-agreement.js +0 -321
- package/src/lib/app-utils/publisher-agreement.js.map +0 -1
- package/src/lib/app-utils/wistia-embed.d.ts +0 -104
- package/src/lib/app-utils/wistia-embed.js +0 -138
- package/src/lib/app-utils/wistia-embed.js.map +0 -1
- package/src/lib/app-utils/workflows.d.ts +0 -136
- package/src/lib/app-utils/workflows.js +0 -346
- package/src/lib/app-utils/workflows.js.map +0 -1
- package/src/lib/plugin-manager/commerce-zone-props.d.ts +0 -172
- package/src/lib/plugin-manager/commerce-zone-props.js +0 -31
- package/src/lib/plugin-manager/commerce-zone-props.js.map +0 -1
- package/src/lib/plugin-manager/plugin-metered-lines.d.ts +0 -97
- package/src/lib/plugin-manager/plugin-metered-lines.js +0 -123
- package/src/lib/plugin-manager/plugin-metered-lines.js.map +0 -1
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ /**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* index constraints entirely, at the
|
|
23
|
-
* beyond the fetch bound are never considered. The
|
|
24
|
-
* never silently unbounded.
|
|
25
|
-
*/ export const
|
|
17
|
+
* A repeat's query (AGL-181): the filter, sort and limit an element's
|
|
18
|
+
* `repeatFilter` / `repeatSort` / `repeatLimit` apply to the rows it repeats
|
|
19
|
+
* over, whatever source supplied them — the canvas preview and the published
|
|
20
|
+
* page's expansion run the same one. Deliberately evaluated IN MEMORY over
|
|
21
|
+
* the already-bounded reads (`REPEAT_MAX_RECORDS`): this sidesteps
|
|
22
|
+
* inequality-filter and composite-index constraints entirely, at the
|
|
23
|
+
* documented cost that rows beyond the fetch bound are never considered. The
|
|
24
|
+
* bound is explicit, never silently unbounded.
|
|
25
|
+
*/ export const REPEAT_QUERY_OPS = [
|
|
26
26
|
'==',
|
|
27
27
|
'!=',
|
|
28
28
|
'>',
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* Parses the authoring shorthand `field op value` (e.g. `price <= 20`,
|
|
36
36
|
* `tier == plus`, `tags contains red`). Null for unparseable input so
|
|
37
37
|
* callers fail open (no filter) rather than filtering wrongly.
|
|
38
|
-
*/ export function
|
|
38
|
+
*/ export function parseRepeatFilter(input) {
|
|
39
39
|
const match = String(input != null ? input : '').trim().match(/^([A-Za-z][A-Za-z0-9_]*)\s*(==|!=|>=|<=|>|<|contains)\s+(.+)$/);
|
|
40
40
|
if (!match) return null;
|
|
41
41
|
return {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
value: match[3].trim()
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
/** Parses `field` or `field desc` into an orderBy config. */ export function
|
|
47
|
+
/** Parses `field` or `field desc` into an orderBy config. */ export function parseRepeatSort(input) {
|
|
48
48
|
var _match_;
|
|
49
49
|
const match = String(input != null ? input : '').trim().match(/^([A-Za-z][A-Za-z0-9_]*)(?:\s+(asc|desc))?$/i);
|
|
50
50
|
if (!match) return null;
|
|
@@ -92,10 +92,10 @@ function matches(stored, where) {
|
|
|
92
92
|
return false;
|
|
93
93
|
}
|
|
94
94
|
/**
|
|
95
|
-
* Applies where/orderBy/limit to in-memory rows.
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*/ export function
|
|
95
|
+
* Applies where/orderBy/limit to in-memory rows. A clause compares the
|
|
96
|
+
* stored value whatever field it names, so rows with no declared field
|
|
97
|
+
* model filter too; timestamps compare numerically (epoch millis storage).
|
|
98
|
+
*/ export function applyRepeatQuery(rows, query) {
|
|
99
99
|
var _query_where;
|
|
100
100
|
let result = rows;
|
|
101
101
|
for (const where of (_query_where = query.where) != null ? _query_where : []){
|
|
@@ -123,4 +123,4 @@ function matches(stored, where) {
|
|
|
123
123
|
return result;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
//# sourceMappingURL=
|
|
126
|
+
//# sourceMappingURL=repeat-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/repeat-query.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 repeat's query (AGL-181): the filter, sort and limit an element's\n * `repeatFilter` / `repeatSort` / `repeatLimit` apply to the rows it repeats\n * over, whatever source supplied them — the canvas preview and the published\n * page's expansion run the same one. Deliberately evaluated IN MEMORY over\n * the already-bounded reads (`REPEAT_MAX_RECORDS`): this sidesteps\n * inequality-filter and composite-index constraints entirely, at the\n * documented cost that rows beyond the fetch bound are never considered. The\n * bound is explicit, never silently unbounded.\n */\n\nexport type RepeatQueryOp =\n | '=='\n | '!='\n | '>'\n | '>='\n | '<'\n | '<='\n | 'contains'\n\nexport const REPEAT_QUERY_OPS: RepeatQueryOp[] = [\n '==',\n '!=',\n '>',\n '>=',\n '<',\n '<=',\n 'contains',\n]\n\nexport interface RepeatQueryWhere {\n fieldId: string\n op: RepeatQueryOp\n /** Literal, compared with type-aware coercion against stored values. */\n value: string\n}\n\nexport interface RepeatQuery {\n where?: RepeatQueryWhere[]\n orderBy?: { fieldId: string; direction?: 'asc' | 'desc' }\n limit?: number\n}\n\n/**\n * Parses the authoring shorthand `field op value` (e.g. `price <= 20`,\n * `tier == plus`, `tags contains red`). Null for unparseable input so\n * callers fail open (no filter) rather than filtering wrongly.\n */\nexport function parseRepeatFilter(input: string): RepeatQueryWhere | null {\n const match = String(input ?? '')\n .trim()\n .match(/^([A-Za-z][A-Za-z0-9_]*)\\s*(==|!=|>=|<=|>|<|contains)\\s+(.+)$/)\n if (!match) return null\n return {\n fieldId: match[1],\n op: match[2] as RepeatQueryOp,\n value: match[3].trim(),\n }\n}\n\n/** Parses `field` or `field desc` into an orderBy config. */\nexport function parseRepeatSort(\n input: string,\n): RepeatQuery['orderBy'] | null {\n const match = String(input ?? '')\n .trim()\n .match(/^([A-Za-z][A-Za-z0-9_]*)(?:\\s+(asc|desc))?$/i)\n if (!match) return null\n return {\n fieldId: match[1],\n direction: match[2]?.toLowerCase() === 'desc' ? 'desc' : 'asc',\n }\n}\n\nconst comparable = (value: unknown): number | string | null => {\n if (typeof value === 'number') return value\n if (typeof value === 'boolean') return value ? 1 : 0\n if (typeof value === 'string') {\n const numeric = Number(value)\n return Number.isFinite(numeric) && value.trim() !== '' ? numeric : value\n }\n return null\n}\n\nfunction matches(stored: unknown, where: RepeatQueryWhere): boolean {\n const { op, value } = where\n if (op === 'contains') {\n if (Array.isArray(stored)) {\n return stored.some(\n (entry) => String(entry).toLowerCase() === value.toLowerCase(),\n )\n }\n return String(stored ?? '')\n .toLowerCase()\n .includes(value.toLowerCase())\n }\n if (op === '==' || op === '!=') {\n const equal =\n typeof stored === 'boolean'\n ? String(stored) === value.toLowerCase()\n : String(stored ?? '') === value\n return op === '==' ? equal : !equal\n }\n const left = comparable(stored)\n const right = comparable(value)\n if (left == null || right == null || typeof left !== typeof right) {\n return false\n }\n switch (op) {\n case '>':\n return left > right\n case '>=':\n return left >= right\n case '<':\n return left < right\n case '<=':\n return left <= right\n }\n return false\n}\n\n/**\n * Applies where/orderBy/limit to in-memory rows. A clause compares the\n * stored value whatever field it names, so rows with no declared field\n * model filter too; timestamps compare numerically (epoch millis storage).\n */\nexport function applyRepeatQuery<Row extends Record<string, unknown>>(\n rows: Row[],\n query: RepeatQuery,\n): Row[] {\n let result = rows\n for (const where of query.where ?? []) {\n result = result.filter((row) => matches(row[where.fieldId], where))\n }\n if (query.orderBy) {\n const { fieldId, direction } = query.orderBy\n const sign = direction === 'desc' ? -1 : 1\n result = [...result].sort((a, b) => {\n const left = comparable(a[fieldId])\n const right = comparable(b[fieldId])\n if (left == null) return 1\n if (right == null) return -1\n if (typeof left === 'number' && typeof right === 'number') {\n return (left - right) * sign\n }\n return String(left).localeCompare(String(right)) * sign\n })\n }\n if (query.limit != null && query.limit > 0) {\n result = result.slice(0, query.limit)\n }\n return result\n}\n"],"names":["REPEAT_QUERY_OPS","parseRepeatFilter","input","match","String","trim","fieldId","op","value","parseRepeatSort","direction","toLowerCase","comparable","numeric","Number","isFinite","matches","stored","where","Array","isArray","some","entry","includes","equal","left","right","applyRepeatQuery","rows","query","result","filter","row","orderBy","sign","sort","a","b","localeCompare","limit","slice"],"mappings":"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"}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import type { AglynAttributeSchema } from '../foundation/definitions/components.types';
|
|
18
|
+
import type { EntityPickerKind } from './entity-picker-context';
|
|
18
19
|
import type { RepeatableDataset } from './expand-repeatables';
|
|
19
20
|
/**
|
|
20
21
|
* Where a repeat's rows come from, for the editor (AGL-3111).
|
|
@@ -78,6 +79,14 @@ export interface RepeatSource {
|
|
|
78
79
|
* component attribute is. The panel names the field after `keyProp`.
|
|
79
80
|
*/
|
|
80
81
|
keyAttribute: Omit<AglynAttributeSchema, 'name'>;
|
|
82
|
+
/**
|
|
83
|
+
* The entity picker kind a key names, when a key is an entity the picker
|
|
84
|
+
* lists. With it, the insert-token menu inside the repeat names the entity
|
|
85
|
+
* through the picker and offers `{{item.<field>}}` for each field the
|
|
86
|
+
* entity declares (`EntityPickerContextValue.entityFields`). Without it the
|
|
87
|
+
* menu offers no item tokens and authors type them.
|
|
88
|
+
*/
|
|
89
|
+
entityKind?: EntityPickerKind;
|
|
81
90
|
/**
|
|
82
91
|
* Reads one key's rows for the besigner's canvas preview and badge. A React
|
|
83
92
|
* hook: the editor mounts one reader per key and calls it unconditionally,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/repeat-sources.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynAttributeSchema } from '../foundation/definitions/components.types'\nimport type { RepeatableDataset } from './expand-repeatables'\n\n/**\n * Where a repeat's rows come from, for the editor (AGL-3111).\n *\n * Any element can repeat, and the platform decides what a repeat MEANS — the\n * scope, the copies, the bounds (`expand-repeatables.ts`). What it does not\n * decide is what an element repeats OVER. That belongs to the plugin that owns\n * the rows, which registers a source here from the registration function its\n * loader calls by name. Core names no source.\n *\n * Reached by path, never through a barrel: nothing on a published page asks\n * where rows come from, so nothing here is in a published page's download.\n */\n\n/**\n * What a source answers for one key.\n *\n * - `loading` — no answer yet.\n * - `missing` — the key names nothing this site may use. The published page\n * renders the element once, as written.\n * - `error` — the read failed. Nothing is known about the rows.\n * - `ready` — the rows, read and bounded exactly as the published page's\n * composition reads them.\n */\nexport type RepeatRowsAnswer =\n | { status: 'loading' }\n | { status: 'missing' }\n | { status: 'error' }\n | {\n status: 'ready'\n /** The name authors know the entry by, for the canvas badge. */\n label: string\n /**\n * The rows under every key the expansion looks them up by: the key as\n * the node stores it, the entry's id, and each entry a one-hop reference\n * in the rows reads (`{{item.ref.field}}`).\n */\n rowsByKey: Readonly<Record<string, RepeatableDataset>>\n }\n\n/** One read a source is asked for. */\nexport interface RepeatRowsRequest {\n /** The site the canvas edits. A source answers only what it may show. */\n hostId: string\n /** The key exactly as the node stores it. */\n key: string\n}\n\n/** One kind of thing an element can repeat over. */\nexport interface RepeatSource {\n /** Stable id, e.g. the plugin's name for the kind. */\n id: string\n /** What the Attributes panel calls the kind. */\n label: string\n /**\n * The node prop the chosen key persists under. Persisted in documents, so a\n * source never renames it.\n */\n keyProp: string\n /**\n * The attribute that picks a key, drawn by the Attributes panel as any\n * component attribute is. The panel names the field after `keyProp`.\n */\n keyAttribute: Omit<AglynAttributeSchema, 'name'>\n /**\n * Reads one key's rows for the besigner's canvas preview and badge. A React\n * hook: the editor mounts one reader per key and calls it unconditionally,\n * so its identity must not change after registration.\n */\n useRows(request: RepeatRowsRequest): RepeatRowsAnswer\n}\n\nconst sources = new Map<string, RepeatSource>()\nconst listeners = new Set<() => void>()\nlet version = 0\n/**\n * Replaced only when the registered set changes, never rebuilt per call: it is\n * the snapshot `useSyncExternalStore` compares, and a fresh array every time\n * is a render loop rather than a wasted allocation.\n */\nlet snapshot: readonly RepeatSource[] = []\n\nconst notify = () => {\n version += 1\n snapshot = [...sources.values()]\n for (const listener of [...listeners]) listener()\n}\n\n/**\n * Registers a source, replacing any registered under its id. Returns the\n * unregister, which removes the source only if it is still the one registered.\n *\n * Call it from a registration function the plugin loader calls BY NAME, never\n * from a module's top level: a bundler deletes a module imported only for its\n * side effect when its package says it has none (AGL-3025).\n */\nexport function registerRepeatSource(source: RepeatSource): () => void {\n if (sources.get(source.id) !== source) {\n sources.set(source.id, source)\n notify()\n }\n return () => {\n if (sources.get(source.id) !== source) return\n sources.delete(source.id)\n notify()\n }\n}\n\n/**\n * Every registered source, in registration order. The SAME array until the\n * registered set changes, so it can be read as a store snapshot.\n */\nexport function listRepeatSources(): readonly RepeatSource[] {\n return snapshot\n}\n\n/** The source registered under `id`. */\nexport function getRepeatSource(id: string): RepeatSource | undefined {\n return sources.get(id)\n}\n\n/** Called after the registered set changes. Returns the unsubscribe. */\nexport function subscribeRepeatSources(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** Moves on every change — the snapshot `useSyncExternalStore` reads. */\nexport function getRepeatSourcesVersion(): number {\n return version\n}\n\n/**\n * The registered source a node repeats over, and the key it names, trimmed.\n * `undefined` for a node that repeats over nothing a registered source owns.\n */\nexport function repeatSourceOf(\n node: unknown,\n): { source: RepeatSource; key: string } | undefined {\n const props = (node as { props?: Record<string, unknown> } | undefined)?.props\n if (!props) return undefined\n for (const source of sources.values()) {\n const value = props[source.keyProp]\n const key = typeof value === 'string' ? value.trim() : ''\n if (key) return { source, key }\n }\n return undefined\n}\n"],"names":["sources","Map","listeners","Set","version","snapshot","notify","values","listener","registerRepeatSource","source","get","id","set","delete","listRepeatSources","getRepeatSource","subscribeRepeatSources","add","getRepeatSourcesVersion","repeatSourceOf","node","props","undefined","value","keyProp","key","trim"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/repeat-sources.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynAttributeSchema } from '../foundation/definitions/components.types'\nimport type { EntityPickerKind } from './entity-picker-context'\nimport type { RepeatableDataset } from './expand-repeatables'\n\n/**\n * Where a repeat's rows come from, for the editor (AGL-3111).\n *\n * Any element can repeat, and the platform decides what a repeat MEANS — the\n * scope, the copies, the bounds (`expand-repeatables.ts`). What it does not\n * decide is what an element repeats OVER. That belongs to the plugin that owns\n * the rows, which registers a source here from the registration function its\n * loader calls by name. Core names no source.\n *\n * Reached by path, never through a barrel: nothing on a published page asks\n * where rows come from, so nothing here is in a published page's download.\n */\n\n/**\n * What a source answers for one key.\n *\n * - `loading` — no answer yet.\n * - `missing` — the key names nothing this site may use. The published page\n * renders the element once, as written.\n * - `error` — the read failed. Nothing is known about the rows.\n * - `ready` — the rows, read and bounded exactly as the published page's\n * composition reads them.\n */\nexport type RepeatRowsAnswer =\n | { status: 'loading' }\n | { status: 'missing' }\n | { status: 'error' }\n | {\n status: 'ready'\n /** The name authors know the entry by, for the canvas badge. */\n label: string\n /**\n * The rows under every key the expansion looks them up by: the key as\n * the node stores it, the entry's id, and each entry a one-hop reference\n * in the rows reads (`{{item.ref.field}}`).\n */\n rowsByKey: Readonly<Record<string, RepeatableDataset>>\n }\n\n/** One read a source is asked for. */\nexport interface RepeatRowsRequest {\n /** The site the canvas edits. A source answers only what it may show. */\n hostId: string\n /** The key exactly as the node stores it. */\n key: string\n}\n\n/** One kind of thing an element can repeat over. */\nexport interface RepeatSource {\n /** Stable id, e.g. the plugin's name for the kind. */\n id: string\n /** What the Attributes panel calls the kind. */\n label: string\n /**\n * The node prop the chosen key persists under. Persisted in documents, so a\n * source never renames it.\n */\n keyProp: string\n /**\n * The attribute that picks a key, drawn by the Attributes panel as any\n * component attribute is. The panel names the field after `keyProp`.\n */\n keyAttribute: Omit<AglynAttributeSchema, 'name'>\n /**\n * The entity picker kind a key names, when a key is an entity the picker\n * lists. With it, the insert-token menu inside the repeat names the entity\n * through the picker and offers `{{item.<field>}}` for each field the\n * entity declares (`EntityPickerContextValue.entityFields`). Without it the\n * menu offers no item tokens and authors type them.\n */\n entityKind?: EntityPickerKind\n /**\n * Reads one key's rows for the besigner's canvas preview and badge. A React\n * hook: the editor mounts one reader per key and calls it unconditionally,\n * so its identity must not change after registration.\n */\n useRows(request: RepeatRowsRequest): RepeatRowsAnswer\n}\n\nconst sources = new Map<string, RepeatSource>()\nconst listeners = new Set<() => void>()\nlet version = 0\n/**\n * Replaced only when the registered set changes, never rebuilt per call: it is\n * the snapshot `useSyncExternalStore` compares, and a fresh array every time\n * is a render loop rather than a wasted allocation.\n */\nlet snapshot: readonly RepeatSource[] = []\n\nconst notify = () => {\n version += 1\n snapshot = [...sources.values()]\n for (const listener of [...listeners]) listener()\n}\n\n/**\n * Registers a source, replacing any registered under its id. Returns the\n * unregister, which removes the source only if it is still the one registered.\n *\n * Call it from a registration function the plugin loader calls BY NAME, never\n * from a module's top level: a bundler deletes a module imported only for its\n * side effect when its package says it has none (AGL-3025).\n */\nexport function registerRepeatSource(source: RepeatSource): () => void {\n if (sources.get(source.id) !== source) {\n sources.set(source.id, source)\n notify()\n }\n return () => {\n if (sources.get(source.id) !== source) return\n sources.delete(source.id)\n notify()\n }\n}\n\n/**\n * Every registered source, in registration order. The SAME array until the\n * registered set changes, so it can be read as a store snapshot.\n */\nexport function listRepeatSources(): readonly RepeatSource[] {\n return snapshot\n}\n\n/** The source registered under `id`. */\nexport function getRepeatSource(id: string): RepeatSource | undefined {\n return sources.get(id)\n}\n\n/** Called after the registered set changes. Returns the unsubscribe. */\nexport function subscribeRepeatSources(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** Moves on every change — the snapshot `useSyncExternalStore` reads. */\nexport function getRepeatSourcesVersion(): number {\n return version\n}\n\n/**\n * The registered source a node repeats over, and the key it names, trimmed.\n * `undefined` for a node that repeats over nothing a registered source owns.\n */\nexport function repeatSourceOf(\n node: unknown,\n): { source: RepeatSource; key: string } | undefined {\n const props = (node as { props?: Record<string, unknown> } | undefined)?.props\n if (!props) return undefined\n for (const source of sources.values()) {\n const value = props[source.keyProp]\n const key = typeof value === 'string' ? value.trim() : ''\n if (key) return { source, key }\n }\n return undefined\n}\n"],"names":["sources","Map","listeners","Set","version","snapshot","notify","values","listener","registerRepeatSource","source","get","id","set","delete","listRepeatSources","getRepeatSource","subscribeRepeatSources","add","getRepeatSourcesVersion","repeatSourceOf","node","props","undefined","value","keyProp","key","trim"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAqFD,MAAMA,UAAU,IAAIC;AACpB,MAAMC,YAAY,IAAIC;AACtB,IAAIC,UAAU;AACd;;;;CAIC,GACD,IAAIC,WAAoC,EAAE;AAE1C,MAAMC,SAAS;IACbF,WAAW;IACXC,WAAW;WAAIL,QAAQO,MAAM;KAAG;IAChC,KAAK,MAAMC,YAAY;WAAIN;KAAU,CAAEM;AACzC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,qBAAqBC,MAAoB;IACvD,IAAIV,QAAQW,GAAG,CAACD,OAAOE,EAAE,MAAMF,QAAQ;QACrCV,QAAQa,GAAG,CAACH,OAAOE,EAAE,EAAEF;QACvBJ;IACF;IACA,OAAO;QACL,IAAIN,QAAQW,GAAG,CAACD,OAAOE,EAAE,MAAMF,QAAQ;QACvCV,QAAQc,MAAM,CAACJ,OAAOE,EAAE;QACxBN;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,SAASS;IACd,OAAOV;AACT;AAEA,sCAAsC,GACtC,OAAO,SAASW,gBAAgBJ,EAAU;IACxC,OAAOZ,QAAQW,GAAG,CAACC;AACrB;AAEA,sEAAsE,GACtE,OAAO,SAASK,uBAAuBT,QAAoB;IACzDN,UAAUgB,GAAG,CAACV;IACd,OAAO;QACLN,UAAUY,MAAM,CAACN;IACnB;AACF;AAEA,uEAAuE,GACvE,OAAO,SAASW;IACd,OAAOf;AACT;AAEA;;;CAGC,GACD,OAAO,SAASgB,eACdC,IAAa;IAEb,MAAMC,QAASD,wBAAD,AAACA,KAA0DC,KAAK;IAC9E,IAAI,CAACA,OAAO,OAAOC;IACnB,KAAK,MAAMb,UAAUV,QAAQO,MAAM,GAAI;QACrC,MAAMiB,QAAQF,KAAK,CAACZ,OAAOe,OAAO,CAAC;QACnC,MAAMC,MAAM,OAAOF,UAAU,WAAWA,MAAMG,IAAI,KAAK;QACvD,IAAID,KAAK,OAAO;YAAEhB;YAAQgB;QAAI;IAChC;IACA,OAAOH;AACT"}
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
/** `orgs/{orgId}/retainedRefusals/{personKey}` — server-only, closed to every client. */
|
|
18
|
+
export declare const RETAINED_REFUSALS_COLLECTION = "retainedRefusals";
|
|
19
|
+
/** When a refusal was retained, epoch ms: on each entry and on the document. */
|
|
20
|
+
export declare const RETAINED_AT_FIELD = "retainedAtMs";
|
|
21
|
+
/** The record an entry was retained from, on each entry. */
|
|
22
|
+
export declare const RETAINED_FROM_RECORD_FIELD = "retainedFromContactId";
|
|
23
|
+
/** Every refusal one person's record holds. */
|
|
24
|
+
export interface RetainedRefusals {
|
|
25
|
+
/** Each site's refusal entry, keyed by the site, as the record stored it. */
|
|
26
|
+
byHost: Record<string, Record<string, unknown>>;
|
|
27
|
+
/** The top-level refusal, which names no site and is honored against every one. */
|
|
28
|
+
unscoped: boolean;
|
|
29
|
+
}
|
|
30
|
+
type Doc = Record<string, unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* Every refusal `record` holds, or `null` when it holds none.
|
|
33
|
+
*
|
|
34
|
+
* Every SITE, not the sites of one group: a record can carry a refusal for a
|
|
35
|
+
* site that let it go earlier, or one a consent group change carried onto
|
|
36
|
+
* it, and each is still somebody's "no". Reads a retained document as well
|
|
37
|
+
* as a person record, since the two share their field names.
|
|
38
|
+
*/
|
|
39
|
+
export declare function refusalsOf(record: Doc | null | undefined): RetainedRefusals | null;
|
|
40
|
+
/**
|
|
41
|
+
* `record` with what was retained for the same person laid over it — the
|
|
42
|
+
* record a reader of the stored basis should read.
|
|
43
|
+
*
|
|
44
|
+
* Per retained site, the record's entry stands only when it is a GRANT whose
|
|
45
|
+
* date is strictly after the refusal's (the person's own date, or when it was
|
|
46
|
+
* retained where they gave none). Anything else — no entry, a refusal of its
|
|
47
|
+
* own, an older or undated grant — reads as the retained refusal. The
|
|
48
|
+
* unscoped refusal is laid on only where the record gives no top-level
|
|
49
|
+
* answer of its own.
|
|
50
|
+
*
|
|
51
|
+
* With nothing retained the record comes back as it was, `null` included, so
|
|
52
|
+
* a person with no retained refusal reads exactly as they did before this
|
|
53
|
+
* store existed.
|
|
54
|
+
*/
|
|
55
|
+
export declare function withRetainedRefusals(record: Doc | null | undefined, retained: RetainedRefusals | null | undefined): Doc | null;
|
|
56
|
+
/**
|
|
57
|
+
* The entries one retention writes: each refusal stamped with when it was
|
|
58
|
+
* retained and from which record, keyed by site.
|
|
59
|
+
*/
|
|
60
|
+
export declare function retainedEntries(retained: RetainedRefusals, recordId: string, nowMs: number): Record<string, Doc>;
|
|
61
|
+
/**
|
|
62
|
+
* One retained document's share of a consent group change's carry `to ← from`
|
|
63
|
+
* — the dotted-path patch that gives `to` the refusal `from` holds, or `null`
|
|
64
|
+
* when `from` holds none or `to` already has an entry.
|
|
65
|
+
*
|
|
66
|
+
* Never overwrites: every entry in this store is a refusal, so an entry on
|
|
67
|
+
* `to` is already the answer, and a re-run after a crash writes nothing.
|
|
68
|
+
*/
|
|
69
|
+
export declare function retainedRefusalCarry(retained: Doc | null | undefined, carry: {
|
|
70
|
+
toHostId: string;
|
|
71
|
+
fromHostId: string;
|
|
72
|
+
}, changeId: string): Doc | null;
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ /**
|
|
18
|
+
* RETAINED REFUSALS — a person's "no" outlives the record it was written on
|
|
19
|
+
* (AGL-3338).
|
|
20
|
+
*
|
|
21
|
+
* ## Where a refusal lives, and why that is not enough
|
|
22
|
+
*
|
|
23
|
+
* A person's refusal of marketing email is stored on the record an operator
|
|
24
|
+
* keeps about them: an entry under {@link MARKETING_CONSENT_BY_HOST_FIELD}
|
|
25
|
+
* for the site they refused, or the top-level field for a refusal that names
|
|
26
|
+
* no site. The list gate reads it there. An operator adding people to a list
|
|
27
|
+
* by hand or by import states that they have permission to email them, and a
|
|
28
|
+
* stored refusal is the one fact that attestation cannot overrule: the
|
|
29
|
+
* person said no, and no operator's word turns that into a yes.
|
|
30
|
+
*
|
|
31
|
+
* The record, though, is the operator's, and the operator may delete it. A
|
|
32
|
+
* deleted record is an address with no record at all, which reads as
|
|
33
|
+
* `unrecorded` — nothing known either way — and the attestation is accepted.
|
|
34
|
+
* Deleting somebody from a CRM would then be the way to mail a person who
|
|
35
|
+
* refused, which is the one outcome the refusal exists to prevent.
|
|
36
|
+
*
|
|
37
|
+
* So a refusal is not the record's to lose. When a holder lets a shared
|
|
38
|
+
* record go, its refusal entries stay on the document for the holders that
|
|
39
|
+
* remain. When the last holder lets go, every refusal the document holds —
|
|
40
|
+
* every site's, not only the deleting holder's, and the unscoped one — is
|
|
41
|
+
* copied into {@link RETAINED_REFUSALS_COLLECTION} in the same transaction
|
|
42
|
+
* that deletes it, and the list gate reads that store beside the records.
|
|
43
|
+
*
|
|
44
|
+
* ## No address
|
|
45
|
+
*
|
|
46
|
+
* One document per person, keyed by `personKey` — the sha256 of the
|
|
47
|
+
* normalized address — and holding no address, as a suppression row keyed
|
|
48
|
+
* the same way holds none. The store answers one question about an address
|
|
49
|
+
* somebody already has in hand, "did this person refuse", and it has no
|
|
50
|
+
* business being a list of the people who did.
|
|
51
|
+
*
|
|
52
|
+
* ## The same field names as the record
|
|
53
|
+
*
|
|
54
|
+
* An entry keeps the record's own names, so a record merged with what was
|
|
55
|
+
* retained ({@link withRetainedRefusals}) is read by `readMarketingBasis`
|
|
56
|
+
* like any other — a second set of names would be a second parser for the
|
|
57
|
+
* two to disagree through. Each entry adds when it was retained and from
|
|
58
|
+
* which record.
|
|
59
|
+
*
|
|
60
|
+
* ## A newer grant wins, and nothing else does
|
|
61
|
+
*
|
|
62
|
+
* The person may come back. Signing up again on a form that asks writes a
|
|
63
|
+
* grant, and a grant given after the refusal is the person changing their
|
|
64
|
+
* mind — the merge lets it stand. A grant that is not strictly newer, a grant
|
|
65
|
+
* with no date, and every entry that is not a grant read as the refusal
|
|
66
|
+
* still: withholding mail is recoverable, and sending it is not.
|
|
67
|
+
*
|
|
68
|
+
* ## Carried like the refusals it came from
|
|
69
|
+
*
|
|
70
|
+
* The list gate reads a refusal across the CURRENT consent group, so two
|
|
71
|
+
* sites separating would stop one seeing the other's retained refusals. A
|
|
72
|
+
* consent group change therefore carries this store as it carries the site
|
|
73
|
+
* suppression lists and a record's own refusals: {@link retainedRefusalCarry}
|
|
74
|
+
* decides one document, and the executor pages the store.
|
|
75
|
+
*/ import { MARKETING_CONSENT_BY_HOST_FIELD, MARKETING_CONSENT_FIELD } from "./marketing-consent.js";
|
|
76
|
+
/** `orgs/{orgId}/retainedRefusals/{personKey}` — server-only, closed to every client. */ export const RETAINED_REFUSALS_COLLECTION = 'retainedRefusals';
|
|
77
|
+
/** When a refusal was retained, epoch ms: on each entry and on the document. */ export const RETAINED_AT_FIELD = 'retainedAtMs';
|
|
78
|
+
/** The record an entry was retained from, on each entry. */ export const RETAINED_FROM_RECORD_FIELD = 'retainedFromContactId';
|
|
79
|
+
/** When the person gave the answer an entry records, as every writer stamps it. */ const GIVEN_AT_FIELD = 'marketingConsentAtMs';
|
|
80
|
+
function mapOf(value) {
|
|
81
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
82
|
+
}
|
|
83
|
+
/** Epoch millis off a number, a `Date` or either SDK's `Timestamp`, else `null`. */ function millisOf(value) {
|
|
84
|
+
if (typeof value === 'number') return Number.isFinite(value) ? value : null;
|
|
85
|
+
if (value instanceof Date) return value.getTime();
|
|
86
|
+
const toMillis = value == null ? void 0 : value.toMillis;
|
|
87
|
+
if (typeof toMillis === 'function') {
|
|
88
|
+
const millis = Number(toMillis.call(value));
|
|
89
|
+
return Number.isFinite(millis) ? millis : null;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Every refusal `record` holds, or `null` when it holds none.
|
|
95
|
+
*
|
|
96
|
+
* Every SITE, not the sites of one group: a record can carry a refusal for a
|
|
97
|
+
* site that let it go earlier, or one a consent group change carried onto
|
|
98
|
+
* it, and each is still somebody's "no". Reads a retained document as well
|
|
99
|
+
* as a person record, since the two share their field names.
|
|
100
|
+
*/ export function refusalsOf(record) {
|
|
101
|
+
var _mapOf;
|
|
102
|
+
if (!record) return null;
|
|
103
|
+
const byHost = {};
|
|
104
|
+
for (const [hostId, value] of Object.entries((_mapOf = mapOf(record[MARKETING_CONSENT_BY_HOST_FIELD])) != null ? _mapOf : {})){
|
|
105
|
+
const entry = mapOf(value);
|
|
106
|
+
if (hostId && (entry == null ? void 0 : entry[MARKETING_CONSENT_FIELD]) === false) byHost[hostId] = _extends({}, entry);
|
|
107
|
+
}
|
|
108
|
+
const unscoped = record[MARKETING_CONSENT_FIELD] === false;
|
|
109
|
+
return unscoped || Object.keys(byHost).length ? {
|
|
110
|
+
byHost,
|
|
111
|
+
unscoped
|
|
112
|
+
} : null;
|
|
113
|
+
}
|
|
114
|
+
/** When a retained refusal counts as given: the person's own date, else the retention's. */ function refusedAtMs(refusal) {
|
|
115
|
+
var _millisOf;
|
|
116
|
+
return (_millisOf = millisOf(refusal[GIVEN_AT_FIELD])) != null ? _millisOf : millisOf(refusal[RETAINED_AT_FIELD]);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* `record` with what was retained for the same person laid over it — the
|
|
120
|
+
* record a reader of the stored basis should read.
|
|
121
|
+
*
|
|
122
|
+
* Per retained site, the record's entry stands only when it is a GRANT whose
|
|
123
|
+
* date is strictly after the refusal's (the person's own date, or when it was
|
|
124
|
+
* retained where they gave none). Anything else — no entry, a refusal of its
|
|
125
|
+
* own, an older or undated grant — reads as the retained refusal. The
|
|
126
|
+
* unscoped refusal is laid on only where the record gives no top-level
|
|
127
|
+
* answer of its own.
|
|
128
|
+
*
|
|
129
|
+
* With nothing retained the record comes back as it was, `null` included, so
|
|
130
|
+
* a person with no retained refusal reads exactly as they did before this
|
|
131
|
+
* store existed.
|
|
132
|
+
*/ export function withRetainedRefusals(record, retained) {
|
|
133
|
+
var _ref, _mapOf;
|
|
134
|
+
const hosts = Object.entries((_ref = retained == null ? void 0 : retained.byHost) != null ? _ref : {});
|
|
135
|
+
if (!retained || !hosts.length && !retained.unscoped) return record != null ? record : null;
|
|
136
|
+
const base = record != null ? record : {};
|
|
137
|
+
const byHost = _extends({}, (_mapOf = mapOf(base[MARKETING_CONSENT_BY_HOST_FIELD])) != null ? _mapOf : {});
|
|
138
|
+
for (const [hostId, refusal] of hosts){
|
|
139
|
+
const entry = mapOf(byHost[hostId]);
|
|
140
|
+
const grantedAt = (entry == null ? void 0 : entry[MARKETING_CONSENT_FIELD]) === true ? millisOf(entry[GIVEN_AT_FIELD]) : null;
|
|
141
|
+
const refusedAt = refusedAtMs(refusal);
|
|
142
|
+
const newerGrant = grantedAt !== null && refusedAt !== null && grantedAt > refusedAt;
|
|
143
|
+
if (!newerGrant) byHost[hostId] = refusal;
|
|
144
|
+
}
|
|
145
|
+
const merged = _extends({}, base, {
|
|
146
|
+
[MARKETING_CONSENT_BY_HOST_FIELD]: byHost
|
|
147
|
+
});
|
|
148
|
+
if (retained.unscoped && typeof base[MARKETING_CONSENT_FIELD] !== 'boolean') {
|
|
149
|
+
merged[MARKETING_CONSENT_FIELD] = false;
|
|
150
|
+
}
|
|
151
|
+
return merged;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The entries one retention writes: each refusal stamped with when it was
|
|
155
|
+
* retained and from which record, keyed by site.
|
|
156
|
+
*/ export function retainedEntries(retained, recordId, nowMs) {
|
|
157
|
+
return Object.fromEntries(Object.entries(retained.byHost).map(([hostId, refusal])=>[
|
|
158
|
+
hostId,
|
|
159
|
+
_extends({}, refusal, {
|
|
160
|
+
[RETAINED_AT_FIELD]: nowMs,
|
|
161
|
+
[RETAINED_FROM_RECORD_FIELD]: recordId
|
|
162
|
+
})
|
|
163
|
+
]));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* One retained document's share of a consent group change's carry `to ← from`
|
|
167
|
+
* — the dotted-path patch that gives `to` the refusal `from` holds, or `null`
|
|
168
|
+
* when `from` holds none or `to` already has an entry.
|
|
169
|
+
*
|
|
170
|
+
* Never overwrites: every entry in this store is a refusal, so an entry on
|
|
171
|
+
* `to` is already the answer, and a re-run after a crash writes nothing.
|
|
172
|
+
*/ export function retainedRefusalCarry(retained, carry, changeId) {
|
|
173
|
+
var _mapOf;
|
|
174
|
+
const byHost = (_mapOf = mapOf(retained == null ? void 0 : retained[MARKETING_CONSENT_BY_HOST_FIELD])) != null ? _mapOf : {};
|
|
175
|
+
const source = mapOf(byHost[carry.fromHostId]);
|
|
176
|
+
if ((source == null ? void 0 : source[MARKETING_CONSENT_FIELD]) !== false) return null;
|
|
177
|
+
if (byHost[carry.toHostId] !== undefined) return null;
|
|
178
|
+
return {
|
|
179
|
+
[`${MARKETING_CONSENT_BY_HOST_FIELD}.${carry.toHostId}`]: _extends({}, source, {
|
|
180
|
+
carriedFromHostId: carry.fromHostId,
|
|
181
|
+
carriedByChangeId: changeId
|
|
182
|
+
})
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//# sourceMappingURL=retained-refusals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/retained-refusals.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 * RETAINED REFUSALS — a person's \"no\" outlives the record it was written on\n * (AGL-3338).\n *\n * ## Where a refusal lives, and why that is not enough\n *\n * A person's refusal of marketing email is stored on the record an operator\n * keeps about them: an entry under {@link MARKETING_CONSENT_BY_HOST_FIELD}\n * for the site they refused, or the top-level field for a refusal that names\n * no site. The list gate reads it there. An operator adding people to a list\n * by hand or by import states that they have permission to email them, and a\n * stored refusal is the one fact that attestation cannot overrule: the\n * person said no, and no operator's word turns that into a yes.\n *\n * The record, though, is the operator's, and the operator may delete it. A\n * deleted record is an address with no record at all, which reads as\n * `unrecorded` — nothing known either way — and the attestation is accepted.\n * Deleting somebody from a CRM would then be the way to mail a person who\n * refused, which is the one outcome the refusal exists to prevent.\n *\n * So a refusal is not the record's to lose. When a holder lets a shared\n * record go, its refusal entries stay on the document for the holders that\n * remain. When the last holder lets go, every refusal the document holds —\n * every site's, not only the deleting holder's, and the unscoped one — is\n * copied into {@link RETAINED_REFUSALS_COLLECTION} in the same transaction\n * that deletes it, and the list gate reads that store beside the records.\n *\n * ## No address\n *\n * One document per person, keyed by `personKey` — the sha256 of the\n * normalized address — and holding no address, as a suppression row keyed\n * the same way holds none. The store answers one question about an address\n * somebody already has in hand, \"did this person refuse\", and it has no\n * business being a list of the people who did.\n *\n * ## The same field names as the record\n *\n * An entry keeps the record's own names, so a record merged with what was\n * retained ({@link withRetainedRefusals}) is read by `readMarketingBasis`\n * like any other — a second set of names would be a second parser for the\n * two to disagree through. Each entry adds when it was retained and from\n * which record.\n *\n * ## A newer grant wins, and nothing else does\n *\n * The person may come back. Signing up again on a form that asks writes a\n * grant, and a grant given after the refusal is the person changing their\n * mind — the merge lets it stand. A grant that is not strictly newer, a grant\n * with no date, and every entry that is not a grant read as the refusal\n * still: withholding mail is recoverable, and sending it is not.\n *\n * ## Carried like the refusals it came from\n *\n * The list gate reads a refusal across the CURRENT consent group, so two\n * sites separating would stop one seeing the other's retained refusals. A\n * consent group change therefore carries this store as it carries the site\n * suppression lists and a record's own refusals: {@link retainedRefusalCarry}\n * decides one document, and the executor pages the store.\n */\n\nimport {\n MARKETING_CONSENT_BY_HOST_FIELD,\n MARKETING_CONSENT_FIELD,\n} from './marketing-consent'\n\n/** `orgs/{orgId}/retainedRefusals/{personKey}` — server-only, closed to every client. */\nexport const RETAINED_REFUSALS_COLLECTION = 'retainedRefusals'\n\n/** When a refusal was retained, epoch ms: on each entry and on the document. */\nexport const RETAINED_AT_FIELD = 'retainedAtMs'\n\n/** The record an entry was retained from, on each entry. */\nexport const RETAINED_FROM_RECORD_FIELD = 'retainedFromContactId'\n\n/** When the person gave the answer an entry records, as every writer stamps it. */\nconst GIVEN_AT_FIELD = 'marketingConsentAtMs'\n\n/** Every refusal one person's record holds. */\nexport interface RetainedRefusals {\n /** Each site's refusal entry, keyed by the site, as the record stored it. */\n byHost: Record<string, Record<string, unknown>>\n /** The top-level refusal, which names no site and is honored against every one. */\n unscoped: boolean\n}\n\ntype Doc = Record<string, unknown>\n\nfunction mapOf(value: unknown): Doc | null {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Doc) : null\n}\n\n/** Epoch millis off a number, a `Date` or either SDK's `Timestamp`, else `null`. */\nfunction millisOf(value: unknown): number | null {\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (value instanceof Date) return value.getTime()\n const toMillis = (value as { toMillis?: unknown } | null)?.toMillis\n if (typeof toMillis === 'function') {\n const millis = Number(toMillis.call(value))\n return Number.isFinite(millis) ? millis : null\n }\n return null\n}\n\n/**\n * Every refusal `record` holds, or `null` when it holds none.\n *\n * Every SITE, not the sites of one group: a record can carry a refusal for a\n * site that let it go earlier, or one a consent group change carried onto\n * it, and each is still somebody's \"no\". Reads a retained document as well\n * as a person record, since the two share their field names.\n */\nexport function refusalsOf(record: Doc | null | undefined): RetainedRefusals | null {\n if (!record) return null\n const byHost: Record<string, Doc> = {}\n for (const [hostId, value] of Object.entries(mapOf(record[MARKETING_CONSENT_BY_HOST_FIELD]) ?? {})) {\n const entry = mapOf(value)\n if (hostId && entry?.[MARKETING_CONSENT_FIELD] === false) byHost[hostId] = { ...entry }\n }\n const unscoped = record[MARKETING_CONSENT_FIELD] === false\n return unscoped || Object.keys(byHost).length ? { byHost, unscoped } : null\n}\n\n/** When a retained refusal counts as given: the person's own date, else the retention's. */\nfunction refusedAtMs(refusal: Doc): number | null {\n return millisOf(refusal[GIVEN_AT_FIELD]) ?? millisOf(refusal[RETAINED_AT_FIELD])\n}\n\n/**\n * `record` with what was retained for the same person laid over it — the\n * record a reader of the stored basis should read.\n *\n * Per retained site, the record's entry stands only when it is a GRANT whose\n * date is strictly after the refusal's (the person's own date, or when it was\n * retained where they gave none). Anything else — no entry, a refusal of its\n * own, an older or undated grant — reads as the retained refusal. The\n * unscoped refusal is laid on only where the record gives no top-level\n * answer of its own.\n *\n * With nothing retained the record comes back as it was, `null` included, so\n * a person with no retained refusal reads exactly as they did before this\n * store existed.\n */\nexport function withRetainedRefusals(\n record: Doc | null | undefined,\n retained: RetainedRefusals | null | undefined,\n): Doc | null {\n const hosts = Object.entries(retained?.byHost ?? {})\n if (!retained || (!hosts.length && !retained.unscoped)) return record ?? null\n const base = record ?? {}\n const byHost: Doc = { ...(mapOf(base[MARKETING_CONSENT_BY_HOST_FIELD]) ?? {}) }\n for (const [hostId, refusal] of hosts) {\n const entry = mapOf(byHost[hostId])\n const grantedAt = entry?.[MARKETING_CONSENT_FIELD] === true ? millisOf(entry[GIVEN_AT_FIELD]) : null\n const refusedAt = refusedAtMs(refusal)\n const newerGrant = grantedAt !== null && refusedAt !== null && grantedAt > refusedAt\n if (!newerGrant) byHost[hostId] = refusal\n }\n const merged: Doc = { ...base, [MARKETING_CONSENT_BY_HOST_FIELD]: byHost }\n if (retained.unscoped && typeof base[MARKETING_CONSENT_FIELD] !== 'boolean') {\n merged[MARKETING_CONSENT_FIELD] = false\n }\n return merged\n}\n\n/**\n * The entries one retention writes: each refusal stamped with when it was\n * retained and from which record, keyed by site.\n */\nexport function retainedEntries(\n retained: RetainedRefusals,\n recordId: string,\n nowMs: number,\n): Record<string, Doc> {\n return Object.fromEntries(\n Object.entries(retained.byHost).map(([hostId, refusal]) => [\n hostId,\n { ...refusal, [RETAINED_AT_FIELD]: nowMs, [RETAINED_FROM_RECORD_FIELD]: recordId },\n ]),\n )\n}\n\n/**\n * One retained document's share of a consent group change's carry `to ← from`\n * — the dotted-path patch that gives `to` the refusal `from` holds, or `null`\n * when `from` holds none or `to` already has an entry.\n *\n * Never overwrites: every entry in this store is a refusal, so an entry on\n * `to` is already the answer, and a re-run after a crash writes nothing.\n */\nexport function retainedRefusalCarry(\n retained: Doc | null | undefined,\n carry: { toHostId: string; fromHostId: string },\n changeId: string,\n): Doc | null {\n const byHost = mapOf(retained?.[MARKETING_CONSENT_BY_HOST_FIELD]) ?? {}\n const source = mapOf(byHost[carry.fromHostId])\n if (source?.[MARKETING_CONSENT_FIELD] !== false) return null\n if (byHost[carry.toHostId] !== undefined) return null\n return {\n [`${MARKETING_CONSENT_BY_HOST_FIELD}.${carry.toHostId}`]: {\n ...source,\n carriedFromHostId: carry.fromHostId,\n carriedByChangeId: changeId,\n },\n }\n}\n"],"names":["MARKETING_CONSENT_BY_HOST_FIELD","MARKETING_CONSENT_FIELD","RETAINED_REFUSALS_COLLECTION","RETAINED_AT_FIELD","RETAINED_FROM_RECORD_FIELD","GIVEN_AT_FIELD","mapOf","value","Array","isArray","millisOf","Number","isFinite","Date","getTime","toMillis","millis","call","refusalsOf","record","byHost","hostId","Object","entries","entry","unscoped","keys","length","refusedAtMs","refusal","withRetainedRefusals","retained","hosts","base","grantedAt","refusedAt","newerGrant","merged","retainedEntries","recordId","nowMs","fromEntries","map","retainedRefusalCarry","carry","changeId","source","fromHostId","toHostId","undefined","carriedFromHostId","carriedByChangeId"],"mappings":"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"}
|
|
@@ -247,3 +247,30 @@ export declare function scopeCovers(target: readonly string[] | undefined | null
|
|
|
247
247
|
* org CDN URL. That keeps it safe to apply blindly at every pick site.
|
|
248
248
|
*/
|
|
249
249
|
export declare function hostQualifiedCdnPath(cdnPath: string | undefined | null, visibleTo: readonly string[] | undefined | null, hostId: string | undefined | null): string | undefined;
|
|
250
|
+
/** The map a scoped document records its visibility grants under. */
|
|
251
|
+
export declare const SCOPE_GRANTS_FIELD = "sharing";
|
|
252
|
+
/** The tokens a grant ADDED to `visibleTo`: seen through, never held. */
|
|
253
|
+
export declare function grantedScopeTokens(doc: Readonly<Record<string, unknown>> | null | undefined): string[];
|
|
254
|
+
/**
|
|
255
|
+
* The tokens that HOLD a document: `visibleTo` without the ones a grant
|
|
256
|
+
* added. Missing `visibleTo` is held by nobody, as it is seen by nobody.
|
|
257
|
+
*
|
|
258
|
+
* A site that created the document (`hostId`) or captured the person on it
|
|
259
|
+
* (`capturedByHostIds`) holds its token whatever the grants say: a record
|
|
260
|
+
* shared with a site that then captures the person itself is that site's
|
|
261
|
+
* own from the capture on, before anything re-evaluates the grants.
|
|
262
|
+
*/
|
|
263
|
+
export declare function heldScopeTokens(doc: Readonly<Record<string, unknown>> | null | undefined): string[];
|
|
264
|
+
/**
|
|
265
|
+
* Whether a site HOLDS a document rather than only seeing it through a
|
|
266
|
+
* grant. The question a marketing audience, a list rule, a sequence and a
|
|
267
|
+
* holder count ask; {@link visibleToHost} is the one a reader asks.
|
|
268
|
+
*/
|
|
269
|
+
export declare function heldByHost(doc: Readonly<Record<string, unknown>> | null | undefined, hostId: string): boolean;
|
|
270
|
+
/**
|
|
271
|
+
* Whether a site sees a document ONLY through a grant: it carries grants and
|
|
272
|
+
* the site does not hold it. The filter an audience sweep applies to what
|
|
273
|
+
* its scoped query returned — a document with no grants answers `false`, so
|
|
274
|
+
* every read that predates sharing keeps exactly the rows it had.
|
|
275
|
+
*/
|
|
276
|
+
export declare function seenOnlyThroughGrant(doc: Readonly<Record<string, unknown>> | null | undefined, hostId: string): boolean;
|
|
@@ -327,5 +327,66 @@ export function isScopeToken(value) {
|
|
|
327
327
|
...tail
|
|
328
328
|
].join('/');
|
|
329
329
|
}
|
|
330
|
+
/*==========================================
|
|
331
|
+
* SEEN, AND HELD (AGL-3336).
|
|
332
|
+
*
|
|
333
|
+
* `visibleTo` answers one question, "who may see this", because it is the
|
|
334
|
+
* one field both enforcement layers can evaluate. A visibility GRANT — an
|
|
335
|
+
* org manager sharing a record with another site, by hand or by a rule —
|
|
336
|
+
* has to widen exactly that field, or the other site's lists, which are
|
|
337
|
+
* real `array-contains-any` queries, could never return the record.
|
|
338
|
+
*
|
|
339
|
+
* But a site a record was shared with does not HOLD it. It did not capture
|
|
340
|
+
* the person, it holds no consent from them, and letting go of the record
|
|
341
|
+
* is not its decision. So a document that carries a grant records which of
|
|
342
|
+
* its tokens the grant added, under `sharing.added`, and every question
|
|
343
|
+
* about holding — who may delete it, whose audience it joins, which site's
|
|
344
|
+
* list it enters — reads {@link heldScopeTokens} instead of `visibleTo`.
|
|
345
|
+
*
|
|
346
|
+
* A document without the field is held by everything that sees it, which
|
|
347
|
+
* is every document written before grants existed.
|
|
348
|
+
*=========================================*/ /** The map a scoped document records its visibility grants under. */ export const SCOPE_GRANTS_FIELD = 'sharing';
|
|
349
|
+
/** The tokens a grant ADDED to `visibleTo`: seen through, never held. */ export function grantedScopeTokens(doc) {
|
|
350
|
+
const grants = (doc != null ? doc : {})[SCOPE_GRANTS_FIELD];
|
|
351
|
+
if (!grants || typeof grants !== 'object' || Array.isArray(grants)) return [];
|
|
352
|
+
const added = grants['added'];
|
|
353
|
+
return Array.isArray(added) ? added.filter((token)=>typeof token === 'string') : [];
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* The tokens that HOLD a document: `visibleTo` without the ones a grant
|
|
357
|
+
* added. Missing `visibleTo` is held by nobody, as it is seen by nobody.
|
|
358
|
+
*
|
|
359
|
+
* A site that created the document (`hostId`) or captured the person on it
|
|
360
|
+
* (`capturedByHostIds`) holds its token whatever the grants say: a record
|
|
361
|
+
* shared with a site that then captures the person itself is that site's
|
|
362
|
+
* own from the capture on, before anything re-evaluates the grants.
|
|
363
|
+
*/ export function heldScopeTokens(doc) {
|
|
364
|
+
const visibleTo = (doc != null ? doc : {})['visibleTo'];
|
|
365
|
+
if (!Array.isArray(visibleTo)) return [];
|
|
366
|
+
const granted = new Set(grantedScopeTokens(doc));
|
|
367
|
+
const capturing = new Set();
|
|
368
|
+
const hostId = (doc != null ? doc : {})['hostId'];
|
|
369
|
+
if (typeof hostId === 'string' && hostId) capturing.add(hostScopeToken(hostId));
|
|
370
|
+
const captured = (doc != null ? doc : {})['capturedByHostIds'];
|
|
371
|
+
if (Array.isArray(captured)) {
|
|
372
|
+
for (const id of captured)if (typeof id === 'string' && id) capturing.add(hostScopeToken(id));
|
|
373
|
+
}
|
|
374
|
+
return visibleTo.filter((token)=>typeof token === 'string' && (!granted.has(token) || capturing.has(token)));
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Whether a site HOLDS a document rather than only seeing it through a
|
|
378
|
+
* grant. The question a marketing audience, a list rule, a sequence and a
|
|
379
|
+
* holder count ask; {@link visibleToHost} is the one a reader asks.
|
|
380
|
+
*/ export function heldByHost(doc, hostId) {
|
|
381
|
+
return visibleToHost(heldScopeTokens(doc), hostId);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Whether a site sees a document ONLY through a grant: it carries grants and
|
|
385
|
+
* the site does not hold it. The filter an audience sweep applies to what
|
|
386
|
+
* its scoped query returned — a document with no grants answers `false`, so
|
|
387
|
+
* every read that predates sharing keeps exactly the rows it had.
|
|
388
|
+
*/ export function seenOnlyThroughGrant(doc, hostId) {
|
|
389
|
+
return grantedScopeTokens(doc).length > 0 && !heldByHost(doc, hostId);
|
|
390
|
+
}
|
|
330
391
|
|
|
331
392
|
//# sourceMappingURL=scope-tokens.js.map
|